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