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