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