syntax_map_tests.rs

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