syntax_map_tests.rs

   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}