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