1use super::*;
2use crate::{LanguageConfig, LanguageMatcher};
3use gpui::AppContext;
4use rand::rngs::StdRng;
5use std::{env, ops::Range, sync::Arc};
6use text::{Buffer, BufferId};
7use tree_sitter::Node;
8use unindent::Unindent as _;
9use util::test::marked_text_ranges;
10
11#[test]
12fn test_splice_included_ranges() {
13 let ranges = vec![ts_range(20..30), ts_range(50..60), ts_range(80..90)];
14
15 let (new_ranges, change) = splice_included_ranges(
16 ranges.clone(),
17 &[54..56, 58..68],
18 &[ts_range(50..54), ts_range(59..67)],
19 );
20 assert_eq!(
21 new_ranges,
22 &[
23 ts_range(20..30),
24 ts_range(50..54),
25 ts_range(59..67),
26 ts_range(80..90),
27 ]
28 );
29 assert_eq!(change, 1..3);
30
31 let (new_ranges, change) = splice_included_ranges(ranges.clone(), &[70..71, 91..100], &[]);
32 assert_eq!(
33 new_ranges,
34 &[ts_range(20..30), ts_range(50..60), ts_range(80..90)]
35 );
36 assert_eq!(change, 2..3);
37
38 let (new_ranges, change) =
39 splice_included_ranges(ranges.clone(), &[], &[ts_range(0..2), ts_range(70..75)]);
40 assert_eq!(
41 new_ranges,
42 &[
43 ts_range(0..2),
44 ts_range(20..30),
45 ts_range(50..60),
46 ts_range(70..75),
47 ts_range(80..90)
48 ]
49 );
50 assert_eq!(change, 0..4);
51
52 let (new_ranges, change) =
53 splice_included_ranges(ranges.clone(), &[30..50], &[ts_range(25..55)]);
54 assert_eq!(new_ranges, &[ts_range(25..55), ts_range(80..90)]);
55 assert_eq!(change, 0..1);
56
57 // does not create overlapping ranges
58 let (new_ranges, change) =
59 splice_included_ranges(ranges.clone(), &[0..18], &[ts_range(20..32)]);
60 assert_eq!(
61 new_ranges,
62 &[ts_range(20..32), ts_range(50..60), ts_range(80..90)]
63 );
64 assert_eq!(change, 0..1);
65
66 fn ts_range(range: Range<usize>) -> tree_sitter::Range {
67 tree_sitter::Range {
68 start_byte: range.start,
69 start_point: tree_sitter::Point {
70 row: 0,
71 column: range.start,
72 },
73 end_byte: range.end,
74 end_point: tree_sitter::Point {
75 row: 0,
76 column: range.end,
77 },
78 }
79 }
80}
81
82#[gpui::test]
83fn test_syntax_map_layers_for_range(cx: &mut AppContext) {
84 let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
85 let language = Arc::new(rust_lang());
86 registry.add(language.clone());
87
88 let mut buffer = Buffer::new(
89 0,
90 BufferId::new(1).unwrap(),
91 r#"
92 fn a() {
93 assert_eq!(
94 b(vec![C {}]),
95 vec![d.e],
96 );
97 println!("{}", f(|_| true));
98 }
99 "#
100 .unindent(),
101 );
102
103 let mut syntax_map = SyntaxMap::new();
104 syntax_map.set_language_registry(registry.clone());
105 syntax_map.reparse(language.clone(), &buffer);
106
107 assert_layers_for_range(
108 &syntax_map,
109 &buffer,
110 Point::new(2, 0)..Point::new(2, 0),
111 &[
112 "...(function_item ... (block (expression_statement (macro_invocation...",
113 "...(tuple_expression (call_expression ... arguments: (arguments (macro_invocation...",
114 ],
115 );
116 assert_layers_for_range(
117 &syntax_map,
118 &buffer,
119 Point::new(2, 14)..Point::new(2, 16),
120 &[
121 "...(function_item ...",
122 "...(tuple_expression (call_expression ... arguments: (arguments (macro_invocation...",
123 "...(array_expression (struct_expression ...",
124 ],
125 );
126 assert_layers_for_range(
127 &syntax_map,
128 &buffer,
129 Point::new(3, 14)..Point::new(3, 16),
130 &[
131 "...(function_item ...",
132 "...(tuple_expression (call_expression ... arguments: (arguments (macro_invocation...",
133 "...(array_expression (field_expression ...",
134 ],
135 );
136 assert_layers_for_range(
137 &syntax_map,
138 &buffer,
139 Point::new(5, 12)..Point::new(5, 16),
140 &[
141 "...(function_item ...",
142 "...(call_expression ... (arguments (closure_expression ...",
143 ],
144 );
145
146 // Replace a vec! macro invocation with a plain slice, removing a syntactic layer.
147 let macro_name_range = range_for_text(&buffer, "vec!");
148 buffer.edit([(macro_name_range, "&")]);
149 syntax_map.interpolate(&buffer);
150 syntax_map.reparse(language.clone(), &buffer);
151
152 assert_layers_for_range(
153 &syntax_map,
154 &buffer,
155 Point::new(2, 14)..Point::new(2, 16),
156 &[
157 "...(function_item ...",
158 "...(tuple_expression (call_expression ... arguments: (arguments (reference_expression value: (array_expression...",
159 ],
160 );
161
162 // Put the vec! macro back, adding back the syntactic layer.
163 buffer.undo();
164 syntax_map.interpolate(&buffer);
165 syntax_map.reparse(language.clone(), &buffer);
166
167 assert_layers_for_range(
168 &syntax_map,
169 &buffer,
170 Point::new(2, 14)..Point::new(2, 16),
171 &[
172 "...(function_item ...",
173 "...(tuple_expression (call_expression ... arguments: (arguments (macro_invocation...",
174 "...(array_expression (struct_expression ...",
175 ],
176 );
177}
178
179#[gpui::test]
180fn test_dynamic_language_injection(cx: &mut AppContext) {
181 let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
182 let markdown = Arc::new(markdown_lang());
183 let markdown_inline = Arc::new(markdown_inline_lang());
184 registry.add(markdown.clone());
185 registry.add(markdown_inline.clone());
186 registry.add(Arc::new(rust_lang()));
187 registry.add(Arc::new(ruby_lang()));
188
189 let mut buffer = Buffer::new(
190 0,
191 BufferId::new(1).unwrap(),
192 r#"
193 This is a code block:
194
195 ```rs
196 fn foo() {}
197 ```
198 "#
199 .unindent(),
200 );
201
202 let mut syntax_map = SyntaxMap::new();
203 syntax_map.set_language_registry(registry.clone());
204 syntax_map.reparse(markdown.clone(), &buffer);
205 syntax_map.reparse(markdown_inline.clone(), &buffer);
206 assert_layers_for_range(
207 &syntax_map,
208 &buffer,
209 Point::new(3, 0)..Point::new(3, 0),
210 &[
211 "(document (section (paragraph (inline)) (fenced_code_block (fenced_code_block_delimiter) (info_string (language)) (block_continuation) (code_fence_content (block_continuation)) (fenced_code_block_delimiter))))",
212 "(inline (code_span (code_span_delimiter) (code_span_delimiter)))",
213 "...(function_item name: (identifier) parameters: (parameters) body: (block)...",
214 ],
215 );
216
217 // Replace Rust with Ruby in code block.
218 let macro_name_range = range_for_text(&buffer, "rs");
219 buffer.edit([(macro_name_range, "ruby")]);
220 syntax_map.interpolate(&buffer);
221 syntax_map.reparse(markdown.clone(), &buffer);
222 syntax_map.reparse(markdown_inline.clone(), &buffer);
223 assert_layers_for_range(
224 &syntax_map,
225 &buffer,
226 Point::new(3, 0)..Point::new(3, 0),
227 &[
228 "(document (section (paragraph (inline)) (fenced_code_block (fenced_code_block_delimiter) (info_string (language)) (block_continuation) (code_fence_content (block_continuation)) (fenced_code_block_delimiter))))",
229 "(inline (code_span (code_span_delimiter) (code_span_delimiter)))",
230 "...(call method: (identifier) arguments: (argument_list (call method: (identifier) arguments: (argument_list) block: (block)...",
231 ],
232 );
233
234 // Replace Ruby with a language that hasn't been loaded yet.
235 let macro_name_range = range_for_text(&buffer, "ruby");
236 buffer.edit([(macro_name_range, "html")]);
237 syntax_map.interpolate(&buffer);
238 syntax_map.reparse(markdown.clone(), &buffer);
239 syntax_map.reparse(markdown_inline.clone(), &buffer);
240 assert_layers_for_range(
241 &syntax_map,
242 &buffer,
243 Point::new(3, 0)..Point::new(3, 0),
244 &[
245 "(document (section (paragraph (inline)) (fenced_code_block (fenced_code_block_delimiter) (info_string (language)) (block_continuation) (code_fence_content (block_continuation)) (fenced_code_block_delimiter))))",
246 "(inline (code_span (code_span_delimiter) (code_span_delimiter)))",
247 ],
248 );
249 assert!(syntax_map.contains_unknown_injections());
250
251 registry.add(Arc::new(html_lang()));
252 syntax_map.reparse(markdown.clone(), &buffer);
253 syntax_map.reparse(markdown_inline.clone(), &buffer);
254 assert_layers_for_range(
255 &syntax_map,
256 &buffer,
257 Point::new(3, 0)..Point::new(3, 0),
258 &[
259 "(document (section (paragraph (inline)) (fenced_code_block (fenced_code_block_delimiter) (info_string (language)) (block_continuation) (code_fence_content (block_continuation)) (fenced_code_block_delimiter))))",
260 "(inline (code_span (code_span_delimiter) (code_span_delimiter)))",
261 "(document (text))",
262 ],
263 );
264 assert!(!syntax_map.contains_unknown_injections());
265}
266
267#[gpui::test]
268fn test_typing_multiple_new_injections(cx: &mut AppContext) {
269 let (buffer, syntax_map) = test_edit_sequence(
270 "Rust",
271 &[
272 "fn a() { test_macro }",
273 "fn a() { test_macro«!» }",
274 "fn a() { test_macro!«()» }",
275 "fn a() { test_macro!(«b») }",
276 "fn a() { test_macro!(b«.») }",
277 "fn a() { test_macro!(b.«c») }",
278 "fn a() { test_macro!(b.c«()») }",
279 "fn a() { test_macro!(b.c(«vec»)) }",
280 "fn a() { test_macro!(b.c(vec«!»)) }",
281 "fn a() { test_macro!(b.c(vec!«[]»)) }",
282 "fn a() { test_macro!(b.c(vec![«d»])) }",
283 "fn a() { test_macro!(b.c(vec![d«.»])) }",
284 "fn a() { test_macro!(b.c(vec![d.«e»])) }",
285 ],
286 cx,
287 );
288
289 assert_capture_ranges(
290 &syntax_map,
291 &buffer,
292 &["field"],
293 "fn a() { test_macro!(b.«c»(vec![d.«e»])) }",
294 );
295}
296
297#[gpui::test]
298fn test_pasting_new_injection_line_between_others(cx: &mut AppContext) {
299 let (buffer, syntax_map) = test_edit_sequence(
300 "Rust",
301 &[
302 "
303 fn a() {
304 b!(B {});
305 c!(C {});
306 d!(D {});
307 e!(E {});
308 f!(F {});
309 g!(G {});
310 }
311 ",
312 "
313 fn a() {
314 b!(B {});
315 c!(C {});
316 d!(D {});
317 « h!(H {});
318 » e!(E {});
319 f!(F {});
320 g!(G {});
321 }
322 ",
323 ],
324 cx,
325 );
326
327 assert_capture_ranges(
328 &syntax_map,
329 &buffer,
330 &["struct"],
331 "
332 fn a() {
333 b!(«B {}»);
334 c!(«C {}»);
335 d!(«D {}»);
336 h!(«H {}»);
337 e!(«E {}»);
338 f!(«F {}»);
339 g!(«G {}»);
340 }
341 ",
342 );
343}
344
345#[gpui::test]
346fn test_joining_injections_with_child_injections(cx: &mut AppContext) {
347 let (buffer, syntax_map) = test_edit_sequence(
348 "Rust",
349 &[
350 "
351 fn a() {
352 b!(
353 c![one.two.three],
354 d![four.five.six],
355 );
356 e!(
357 f![seven.eight],
358 );
359 }
360 ",
361 "
362 fn a() {
363 b!(
364 c![one.two.three],
365 d![four.five.six],
366 ˇ f![seven.eight],
367 );
368 }
369 ",
370 ],
371 cx,
372 );
373
374 assert_capture_ranges(
375 &syntax_map,
376 &buffer,
377 &["field"],
378 "
379 fn a() {
380 b!(
381 c![one.«two».«three»],
382 d![four.«five».«six»],
383 f![seven.«eight»],
384 );
385 }
386 ",
387 );
388}
389
390#[gpui::test]
391fn test_editing_edges_of_injection(cx: &mut AppContext) {
392 test_edit_sequence(
393 "Rust",
394 &[
395 "
396 fn a() {
397 b!(c!())
398 }
399 ",
400 "
401 fn a() {
402 «d»!(c!())
403 }
404 ",
405 "
406 fn a() {
407 «e»d!(c!())
408 }
409 ",
410 "
411 fn a() {
412 ed!«[»c!()«]»
413 }
414 ",
415 ],
416 cx,
417 );
418}
419
420#[gpui::test]
421fn test_edits_preceding_and_intersecting_injection(cx: &mut AppContext) {
422 test_edit_sequence(
423 "Rust",
424 &[
425 //
426 "const aaaaaaaaaaaa: B = c!(d(e.f));",
427 "const aˇa: B = c!(d(eˇ));",
428 ],
429 cx,
430 );
431}
432
433#[gpui::test]
434fn test_non_local_changes_create_injections(cx: &mut AppContext) {
435 test_edit_sequence(
436 "Rust",
437 &[
438 "
439 // a! {
440 static B: C = d;
441 // }
442 ",
443 "
444 ˇa! {
445 static B: C = d;
446 ˇ}
447 ",
448 ],
449 cx,
450 );
451}
452
453#[gpui::test]
454fn test_creating_many_injections_in_one_edit(cx: &mut AppContext) {
455 test_edit_sequence(
456 "Rust",
457 &[
458 "
459 fn a() {
460 one(Two::three(3));
461 four(Five::six(6));
462 seven(Eight::nine(9));
463 }
464 ",
465 "
466 fn a() {
467 one«!»(Two::three(3));
468 four«!»(Five::six(6));
469 seven«!»(Eight::nine(9));
470 }
471 ",
472 "
473 fn a() {
474 one!(Two::three«!»(3));
475 four!(Five::six«!»(6));
476 seven!(Eight::nine«!»(9));
477 }
478 ",
479 ],
480 cx,
481 );
482}
483
484#[gpui::test]
485fn test_editing_across_injection_boundary(cx: &mut AppContext) {
486 test_edit_sequence(
487 "Rust",
488 &[
489 "
490 fn one() {
491 two();
492 three!(
493 three.four,
494 five.six,
495 );
496 }
497 ",
498 "
499 fn one() {
500 two();
501 th«irty_five![»
502 three.four,
503 five.six,
504 « seven.eight,
505 ];»
506 }
507 ",
508 ],
509 cx,
510 );
511}
512
513#[gpui::test]
514fn test_removing_injection_by_replacing_across_boundary(cx: &mut AppContext) {
515 test_edit_sequence(
516 "Rust",
517 &[
518 "
519 fn one() {
520 two!(
521 three.four,
522 );
523 }
524 ",
525 "
526 fn one() {
527 t«en
528 .eleven(
529 twelve,
530 »
531 three.four,
532 );
533 }
534 ",
535 ],
536 cx,
537 );
538}
539
540#[gpui::test]
541fn test_combined_injections_simple(cx: &mut AppContext) {
542 let (buffer, syntax_map) = test_edit_sequence(
543 "ERB",
544 &[
545 "
546 <body>
547 <% if @one %>
548 <div class=one>
549 <% else %>
550 <div class=two>
551 <% end %>
552 </div>
553 </body>
554 ",
555 "
556 <body>
557 <% if @one %>
558 <div class=one>
559 ˇ else ˇ
560 <div class=two>
561 <% end %>
562 </div>
563 </body>
564 ",
565 "
566 <body>
567 <% if @one «;» end %>
568 </div>
569 </body>
570 ",
571 ],
572 cx,
573 );
574
575 assert_capture_ranges(
576 &syntax_map,
577 &buffer,
578 &["tag", "ivar"],
579 "
580 <«body»>
581 <% if «@one» ; end %>
582 </«div»>
583 </«body»>
584 ",
585 );
586}
587
588#[gpui::test]
589fn test_combined_injections_empty_ranges(cx: &mut AppContext) {
590 test_edit_sequence(
591 "ERB",
592 &[
593 "
594 <% if @one %>
595 <% else %>
596 <% end %>
597 ",
598 "
599 <% if @one %>
600 ˇ<% end %>
601 ",
602 ],
603 cx,
604 );
605}
606
607#[gpui::test]
608fn test_combined_injections_edit_edges_of_ranges(cx: &mut AppContext) {
609 let (buffer, syntax_map) = test_edit_sequence(
610 "ERB",
611 &[
612 "
613 <%= one @two %>
614 <%= three @four %>
615 ",
616 "
617 <%= one @two %ˇ
618 <%= three @four %>
619 ",
620 "
621 <%= one @two %«>»
622 <%= three @four %>
623 ",
624 ],
625 cx,
626 );
627
628 assert_capture_ranges(
629 &syntax_map,
630 &buffer,
631 &["tag", "ivar"],
632 "
633 <%= one «@two» %>
634 <%= three «@four» %>
635 ",
636 );
637}
638
639#[gpui::test]
640fn test_combined_injections_splitting_some_injections(cx: &mut AppContext) {
641 let (_buffer, _syntax_map) = test_edit_sequence(
642 "ERB",
643 &[
644 r#"
645 <%A if b(:c) %>
646 d
647 <% end %>
648 eee
649 <% f %>
650 "#,
651 r#"
652 <%« AAAAAAA %>
653 hhhhhhh
654 <%=» if b(:c) %>
655 d
656 <% end %>
657 eee
658 <% f %>
659 "#,
660 ],
661 cx,
662 );
663}
664
665#[gpui::test]
666fn test_combined_injections_editing_after_last_injection(cx: &mut AppContext) {
667 test_edit_sequence(
668 "ERB",
669 &[
670 r#"
671 <% foo %>
672 <div></div>
673 <% bar %>
674 "#,
675 r#"
676 <% foo %>
677 <div></div>
678 <% bar %>«
679 more text»
680 "#,
681 ],
682 cx,
683 );
684}
685
686#[gpui::test]
687fn test_combined_injections_inside_injections(cx: &mut AppContext) {
688 let (buffer, syntax_map) = test_edit_sequence(
689 "Markdown",
690 &[
691 r#"
692 here is
693 some
694 ERB code:
695
696 ```erb
697 <ul>
698 <% people.each do |person| %>
699 <li><%= person.name %></li>
700 <li><%= person.age %></li>
701 <% end %>
702 </ul>
703 ```
704 "#,
705 r#"
706 here is
707 some
708 ERB code:
709
710 ```erb
711 <ul>
712 <% people«2».each do |person| %>
713 <li><%= person.name %></li>
714 <li><%= person.age %></li>
715 <% end %>
716 </ul>
717 ```
718 "#,
719 // Inserting a comment character inside one code directive
720 // does not cause the other code directive to become a comment,
721 // because newlines are included in between each injection range.
722 r#"
723 here is
724 some
725 ERB code:
726
727 ```erb
728 <ul>
729 <% people2.each do |person| %>
730 <li><%= «# »person.name %></li>
731 <li><%= person.age %></li>
732 <% end %>
733 </ul>
734 ```
735 "#,
736 ],
737 cx,
738 );
739
740 // Check that the code directive below the ruby comment is
741 // not parsed as a comment.
742 assert_capture_ranges(
743 &syntax_map,
744 &buffer,
745 &["method"],
746 "
747 here is
748 some
749 ERB code:
750
751 ```erb
752 <ul>
753 <% people2.«each» do |person| %>
754 <li><%= # person.name %></li>
755 <li><%= person.«age» %></li>
756 <% end %>
757 </ul>
758 ```
759 ",
760 );
761}
762
763#[gpui::test]
764fn test_empty_combined_injections_inside_injections(cx: &mut AppContext) {
765 let (buffer, syntax_map) = test_edit_sequence(
766 "Markdown",
767 &[r#"
768 ```erb
769 hello
770 ```
771
772 goodbye
773 "#],
774 cx,
775 );
776
777 assert_layers_for_range(
778 &syntax_map,
779 &buffer,
780 Point::new(0, 0)..Point::new(5, 0),
781 &[
782 "(document (section (fenced_code_block (fenced_code_block_delimiter) (info_string (language)) (block_continuation) (code_fence_content (block_continuation)) (fenced_code_block_delimiter)) (paragraph (inline))))",
783 "(template...",
784 "(document (text))",
785 // The ruby syntax tree should be empty, since there are
786 // no interpolations in the ERB template.
787 "(program)",
788 ],
789 );
790}
791
792#[gpui::test(iterations = 50)]
793fn test_random_syntax_map_edits_rust_macros(rng: StdRng, cx: &mut AppContext) {
794 let text = r#"
795 fn test_something() {
796 let vec = vec![5, 1, 3, 8];
797 assert_eq!(
798 vec
799 .into_iter()
800 .map(|i| i * 2)
801 .collect::<Vec<usize>>(),
802 vec![
803 5 * 2, 1 * 2, 3 * 2, 8 * 2
804 ],
805 );
806 }
807 "#
808 .unindent()
809 .repeat(2);
810
811 let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
812 let language = Arc::new(rust_lang());
813 registry.add(language.clone());
814
815 test_random_edits(text, registry, language, rng);
816}
817
818#[gpui::test(iterations = 50)]
819fn test_random_syntax_map_edits_with_erb(rng: StdRng, cx: &mut AppContext) {
820 let text = r#"
821 <div id="main">
822 <% if one?(:two) %>
823 <p class="three" four>
824 <%= yield :five %>
825 </p>
826 <% elsif Six.seven(8) %>
827 <p id="three" four>
828 <%= yield :five %>
829 </p>
830 <% else %>
831 <span>Ok</span>
832 <% end %>
833 </div>
834 "#
835 .unindent()
836 .repeat(5);
837
838 let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
839 let language = Arc::new(erb_lang());
840 registry.add(language.clone());
841 registry.add(Arc::new(ruby_lang()));
842 registry.add(Arc::new(html_lang()));
843
844 test_random_edits(text, registry, language, rng);
845}
846
847#[gpui::test(iterations = 50)]
848fn test_random_syntax_map_edits_with_heex(rng: StdRng, cx: &mut AppContext) {
849 let text = r#"
850 defmodule TheModule do
851 def the_method(assigns) do
852 ~H"""
853 <%= if @empty do %>
854 <div class="h-4"></div>
855 <% else %>
856 <div class="max-w-2xl w-full animate-pulse">
857 <div class="flex-1 space-y-4">
858 <div class={[@bg_class, "h-4 rounded-lg w-3/4"]}></div>
859 <div class={[@bg_class, "h-4 rounded-lg"]}></div>
860 <div class={[@bg_class, "h-4 rounded-lg w-5/6"]}></div>
861 </div>
862 </div>
863 <% end %>
864 """
865 end
866 end
867 "#
868 .unindent()
869 .repeat(3);
870
871 let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
872 let language = Arc::new(elixir_lang());
873 registry.add(language.clone());
874 registry.add(Arc::new(heex_lang()));
875 registry.add(Arc::new(html_lang()));
876
877 test_random_edits(text, registry, language, rng);
878}
879
880fn test_random_edits(
881 text: String,
882 registry: Arc<LanguageRegistry>,
883 language: Arc<Language>,
884 mut rng: StdRng,
885) {
886 let operations = env::var("OPERATIONS")
887 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
888 .unwrap_or(10);
889
890 let mut buffer = Buffer::new(0, BufferId::new(1).unwrap(), text);
891
892 let mut syntax_map = SyntaxMap::new();
893 syntax_map.set_language_registry(registry.clone());
894 syntax_map.reparse(language.clone(), &buffer);
895
896 let mut reference_syntax_map = SyntaxMap::new();
897 reference_syntax_map.set_language_registry(registry.clone());
898
899 log::info!("initial text:\n{}", buffer.text());
900
901 for _ in 0..operations {
902 let prev_buffer = buffer.snapshot();
903 let prev_syntax_map = syntax_map.snapshot();
904
905 buffer.randomly_edit(&mut rng, 3);
906 log::info!("text:\n{}", buffer.text());
907
908 syntax_map.interpolate(&buffer);
909 check_interpolation(&prev_syntax_map, &syntax_map, &prev_buffer, &buffer);
910
911 syntax_map.reparse(language.clone(), &buffer);
912
913 reference_syntax_map.clear();
914 reference_syntax_map.reparse(language.clone(), &buffer);
915 }
916
917 for i in 0..operations {
918 let i = operations - i - 1;
919 buffer.undo();
920 log::info!("undoing operation {}", i);
921 log::info!("text:\n{}", buffer.text());
922
923 syntax_map.interpolate(&buffer);
924 syntax_map.reparse(language.clone(), &buffer);
925
926 reference_syntax_map.clear();
927 reference_syntax_map.reparse(language.clone(), &buffer);
928 assert_eq!(
929 syntax_map.layers(&buffer).len(),
930 reference_syntax_map.layers(&buffer).len(),
931 "wrong number of layers after undoing edit {i}"
932 );
933 }
934
935 let layers = syntax_map.layers(&buffer);
936 let reference_layers = reference_syntax_map.layers(&buffer);
937 for (edited_layer, reference_layer) in layers.into_iter().zip(reference_layers.into_iter()) {
938 assert_eq!(
939 edited_layer.node().to_sexp(),
940 reference_layer.node().to_sexp()
941 );
942 assert_eq!(edited_layer.node().range(), reference_layer.node().range());
943 }
944}
945
946fn check_interpolation(
947 old_syntax_map: &SyntaxSnapshot,
948 new_syntax_map: &SyntaxSnapshot,
949 old_buffer: &BufferSnapshot,
950 new_buffer: &BufferSnapshot,
951) {
952 let edits = new_buffer
953 .edits_since::<usize>(&old_buffer.version())
954 .collect::<Vec<_>>();
955
956 for (old_layer, new_layer) in old_syntax_map
957 .layers
958 .iter()
959 .zip(new_syntax_map.layers.iter())
960 {
961 assert_eq!(old_layer.range, new_layer.range);
962 let Some(old_tree) = old_layer.content.tree() else {
963 continue;
964 };
965 let Some(new_tree) = new_layer.content.tree() else {
966 continue;
967 };
968 let old_start_byte = old_layer.range.start.to_offset(old_buffer);
969 let new_start_byte = new_layer.range.start.to_offset(new_buffer);
970 let old_start_point = old_layer.range.start.to_point(old_buffer).to_ts_point();
971 let new_start_point = new_layer.range.start.to_point(new_buffer).to_ts_point();
972 let old_node = old_tree.root_node_with_offset(old_start_byte, old_start_point);
973 let new_node = new_tree.root_node_with_offset(new_start_byte, new_start_point);
974 check_node_edits(
975 old_layer.depth,
976 &old_layer.range,
977 old_node,
978 new_node,
979 old_buffer,
980 new_buffer,
981 &edits,
982 );
983 }
984
985 fn check_node_edits(
986 depth: usize,
987 range: &Range<Anchor>,
988 old_node: Node,
989 new_node: Node,
990 old_buffer: &BufferSnapshot,
991 new_buffer: &BufferSnapshot,
992 edits: &[text::Edit<usize>],
993 ) {
994 assert_eq!(old_node.kind(), new_node.kind());
995
996 let old_range = old_node.byte_range();
997 let new_range = new_node.byte_range();
998
999 let is_edited = edits
1000 .iter()
1001 .any(|edit| edit.new.start < new_range.end && edit.new.end > new_range.start);
1002 if is_edited {
1003 assert!(
1004 new_node.has_changes(),
1005 concat!(
1006 "failed to mark node as edited.\n",
1007 "layer depth: {}, old layer range: {:?}, new layer range: {:?},\n",
1008 "node kind: {}, old node range: {:?}, new node range: {:?}",
1009 ),
1010 depth,
1011 range.to_offset(old_buffer),
1012 range.to_offset(new_buffer),
1013 new_node.kind(),
1014 old_range,
1015 new_range,
1016 );
1017 }
1018
1019 if !new_node.has_changes() {
1020 assert_eq!(
1021 old_buffer
1022 .text_for_range(old_range.clone())
1023 .collect::<String>(),
1024 new_buffer
1025 .text_for_range(new_range.clone())
1026 .collect::<String>(),
1027 concat!(
1028 "mismatched text for node\n",
1029 "layer depth: {}, old layer range: {:?}, new layer range: {:?},\n",
1030 "node kind: {}, old node range:{:?}, new node range:{:?}",
1031 ),
1032 depth,
1033 range.to_offset(old_buffer),
1034 range.to_offset(new_buffer),
1035 new_node.kind(),
1036 old_range,
1037 new_range,
1038 );
1039 }
1040
1041 for i in 0..new_node.child_count() {
1042 check_node_edits(
1043 depth,
1044 range,
1045 old_node.child(i).unwrap(),
1046 new_node.child(i).unwrap(),
1047 old_buffer,
1048 new_buffer,
1049 edits,
1050 )
1051 }
1052 }
1053}
1054
1055fn test_edit_sequence(
1056 language_name: &str,
1057 steps: &[&str],
1058 cx: &mut AppContext,
1059) -> (Buffer, SyntaxMap) {
1060 let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
1061 registry.add(Arc::new(elixir_lang()));
1062 registry.add(Arc::new(heex_lang()));
1063 registry.add(Arc::new(rust_lang()));
1064 registry.add(Arc::new(ruby_lang()));
1065 registry.add(Arc::new(html_lang()));
1066 registry.add(Arc::new(erb_lang()));
1067 registry.add(Arc::new(markdown_lang()));
1068 registry.add(Arc::new(markdown_inline_lang()));
1069
1070 let language = registry
1071 .language_for_name(language_name)
1072 .now_or_never()
1073 .unwrap()
1074 .unwrap();
1075 let mut buffer = Buffer::new(0, BufferId::new(1).unwrap(), Default::default());
1076
1077 let mut mutated_syntax_map = SyntaxMap::new();
1078 mutated_syntax_map.set_language_registry(registry.clone());
1079 mutated_syntax_map.reparse(language.clone(), &buffer);
1080
1081 for (i, marked_string) in steps.into_iter().enumerate() {
1082 let marked_string = marked_string.unindent();
1083 log::info!("incremental parse {i}: {marked_string:?}");
1084 buffer.edit_via_marked_text(&marked_string);
1085
1086 // Reparse the syntax map
1087 mutated_syntax_map.interpolate(&buffer);
1088 mutated_syntax_map.reparse(language.clone(), &buffer);
1089
1090 // Create a second syntax map from scratch
1091 log::info!("fresh parse {i}: {marked_string:?}");
1092 let mut reference_syntax_map = SyntaxMap::new();
1093 reference_syntax_map.set_language_registry(registry.clone());
1094 reference_syntax_map.reparse(language.clone(), &buffer);
1095
1096 // Compare the mutated syntax map to the new syntax map
1097 let mutated_layers = mutated_syntax_map.layers(&buffer);
1098 let reference_layers = reference_syntax_map.layers(&buffer);
1099 assert_eq!(
1100 mutated_layers.len(),
1101 reference_layers.len(),
1102 "wrong number of layers at step {i}"
1103 );
1104 for (edited_layer, reference_layer) in
1105 mutated_layers.into_iter().zip(reference_layers.into_iter())
1106 {
1107 assert_eq!(
1108 edited_layer.node().to_sexp(),
1109 reference_layer.node().to_sexp(),
1110 "different layer at step {i}"
1111 );
1112 assert_eq!(
1113 edited_layer.node().range(),
1114 reference_layer.node().range(),
1115 "different layer at step {i}"
1116 );
1117 }
1118 }
1119
1120 (buffer, mutated_syntax_map)
1121}
1122
1123fn html_lang() -> Language {
1124 Language::new(
1125 LanguageConfig {
1126 name: "HTML".into(),
1127 matcher: LanguageMatcher {
1128 path_suffixes: vec!["html".to_string()],
1129 ..Default::default()
1130 },
1131 ..Default::default()
1132 },
1133 Some(tree_sitter_html::language()),
1134 )
1135 .with_highlights_query(
1136 r#"
1137 (tag_name) @tag
1138 (erroneous_end_tag_name) @tag
1139 (attribute_name) @property
1140 "#,
1141 )
1142 .unwrap()
1143}
1144
1145fn ruby_lang() -> Language {
1146 Language::new(
1147 LanguageConfig {
1148 name: "Ruby".into(),
1149 matcher: LanguageMatcher {
1150 path_suffixes: vec!["rb".to_string()],
1151 ..Default::default()
1152 },
1153 ..Default::default()
1154 },
1155 Some(tree_sitter_ruby::language()),
1156 )
1157 .with_highlights_query(
1158 r#"
1159 ["if" "do" "else" "end"] @keyword
1160 (instance_variable) @ivar
1161 (call method: (identifier) @method)
1162 "#,
1163 )
1164 .unwrap()
1165}
1166
1167fn erb_lang() -> Language {
1168 Language::new(
1169 LanguageConfig {
1170 name: "ERB".into(),
1171 matcher: LanguageMatcher {
1172 path_suffixes: vec!["erb".to_string()],
1173 ..Default::default()
1174 },
1175 ..Default::default()
1176 },
1177 Some(tree_sitter_embedded_template::language()),
1178 )
1179 .with_highlights_query(
1180 r#"
1181 ["<%" "%>"] @keyword
1182 "#,
1183 )
1184 .unwrap()
1185 .with_injection_query(
1186 r#"
1187 (
1188 (code) @content
1189 (#set! "language" "ruby")
1190 (#set! "combined")
1191 )
1192
1193 (
1194 (content) @content
1195 (#set! "language" "html")
1196 (#set! "combined")
1197 )
1198 "#,
1199 )
1200 .unwrap()
1201}
1202
1203fn rust_lang() -> Language {
1204 Language::new(
1205 LanguageConfig {
1206 name: "Rust".into(),
1207 matcher: LanguageMatcher {
1208 path_suffixes: vec!["rs".to_string()],
1209 ..Default::default()
1210 },
1211 ..Default::default()
1212 },
1213 Some(tree_sitter_rust::language()),
1214 )
1215 .with_highlights_query(
1216 r#"
1217 (field_identifier) @field
1218 (struct_expression) @struct
1219 "#,
1220 )
1221 .unwrap()
1222 .with_injection_query(
1223 r#"
1224 (macro_invocation
1225 (token_tree) @content
1226 (#set! "language" "rust"))
1227 "#,
1228 )
1229 .unwrap()
1230}
1231
1232fn markdown_lang() -> Language {
1233 Language::new(
1234 LanguageConfig {
1235 name: "Markdown".into(),
1236 matcher: LanguageMatcher {
1237 path_suffixes: vec!["md".into()],
1238 ..Default::default()
1239 },
1240 ..Default::default()
1241 },
1242 Some(tree_sitter_md::language()),
1243 )
1244 .with_injection_query(
1245 r#"
1246 (fenced_code_block
1247 (info_string
1248 (language) @language)
1249 (code_fence_content) @content)
1250 "#,
1251 )
1252 .unwrap()
1253}
1254
1255fn markdown_inline_lang() -> Language {
1256 Language::new(
1257 LanguageConfig {
1258 name: "Markdown-Inline".into(),
1259 ..LanguageConfig::default()
1260 },
1261 Some(tree_sitter_md::inline_language()),
1262 )
1263}
1264
1265fn elixir_lang() -> Language {
1266 Language::new(
1267 LanguageConfig {
1268 name: "Elixir".into(),
1269 matcher: LanguageMatcher {
1270 path_suffixes: vec!["ex".into()],
1271 ..Default::default()
1272 },
1273 ..Default::default()
1274 },
1275 Some(tree_sitter_elixir::language()),
1276 )
1277 .with_highlights_query(
1278 r#"
1279
1280 "#,
1281 )
1282 .unwrap()
1283}
1284
1285fn heex_lang() -> Language {
1286 Language::new(
1287 LanguageConfig {
1288 name: "HEEx".into(),
1289 matcher: LanguageMatcher {
1290 path_suffixes: vec!["heex".into()],
1291 ..Default::default()
1292 },
1293 ..Default::default()
1294 },
1295 Some(tree_sitter_heex::language()),
1296 )
1297 .with_injection_query(
1298 r#"
1299 (
1300 (directive
1301 [
1302 (partial_expression_value)
1303 (expression_value)
1304 (ending_expression_value)
1305 ] @content)
1306 (#set! language "elixir")
1307 (#set! combined)
1308 )
1309
1310 ((expression (expression_value) @content)
1311 (#set! language "elixir"))
1312 "#,
1313 )
1314 .unwrap()
1315}
1316
1317fn range_for_text(buffer: &Buffer, text: &str) -> Range<usize> {
1318 let start = buffer.as_rope().to_string().find(text).unwrap();
1319 start..start + text.len()
1320}
1321
1322#[track_caller]
1323fn assert_layers_for_range(
1324 syntax_map: &SyntaxMap,
1325 buffer: &BufferSnapshot,
1326 range: Range<Point>,
1327 expected_layers: &[&str],
1328) {
1329 let layers = syntax_map
1330 .layers_for_range(range, &buffer)
1331 .collect::<Vec<_>>();
1332 assert_eq!(
1333 layers.len(),
1334 expected_layers.len(),
1335 "wrong number of layers"
1336 );
1337 for (i, (layer, expected_s_exp)) in layers.iter().zip(expected_layers.iter()).enumerate() {
1338 let actual_s_exp = layer.node().to_sexp();
1339 assert!(
1340 string_contains_sequence(
1341 &actual_s_exp,
1342 &expected_s_exp.split("...").collect::<Vec<_>>()
1343 ),
1344 "layer {i}:\n\nexpected: {expected_s_exp}\nactual: {actual_s_exp}",
1345 );
1346 }
1347}
1348
1349fn assert_capture_ranges(
1350 syntax_map: &SyntaxMap,
1351 buffer: &BufferSnapshot,
1352 highlight_query_capture_names: &[&str],
1353 marked_string: &str,
1354) {
1355 let mut actual_ranges = Vec::<Range<usize>>::new();
1356 let captures = syntax_map.captures(0..buffer.len(), buffer, |grammar| {
1357 grammar.highlights_query.as_ref()
1358 });
1359 let queries = captures
1360 .grammars()
1361 .iter()
1362 .map(|grammar| grammar.highlights_query.as_ref().unwrap())
1363 .collect::<Vec<_>>();
1364 for capture in captures {
1365 let name = &queries[capture.grammar_index].capture_names()[capture.index as usize];
1366 if highlight_query_capture_names.contains(&name) {
1367 actual_ranges.push(capture.node.byte_range());
1368 }
1369 }
1370
1371 let (text, expected_ranges) = marked_text_ranges(&marked_string.unindent(), false);
1372 assert_eq!(text, buffer.text());
1373 assert_eq!(actual_ranges, expected_ranges);
1374}
1375
1376pub fn string_contains_sequence(text: &str, parts: &[&str]) -> bool {
1377 let mut last_part_end = 0;
1378 for part in parts {
1379 if let Some(start_ix) = text[last_part_end..].find(part) {
1380 last_part_end = start_ix + part.len();
1381 } else {
1382 return false;
1383 }
1384 }
1385 true
1386}