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