syntax_map_tests.rs

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