buffer_tests.rs

   1use super::*;
   2use crate::Buffer;
   3use clock::ReplicaId;
   4use collections::BTreeMap;
   5use futures::FutureExt as _;
   6use gpui::{App, AppContext as _, BorrowAppContext, Entity};
   7use gpui::{HighlightStyle, TestAppContext};
   8use indoc::indoc;
   9use pretty_assertions::assert_eq;
  10use proto::deserialize_operation;
  11use rand::prelude::*;
  12use regex::RegexBuilder;
  13use settings::SettingsStore;
  14use settings::{AllLanguageSettingsContent, LanguageSettingsContent};
  15use std::collections::BTreeSet;
  16use std::fmt::Write as _;
  17use std::{
  18    env,
  19    ops::Range,
  20    sync::LazyLock,
  21    time::{Duration, Instant},
  22};
  23use syntax_map::TreeSitterOptions;
  24use text::network::Network;
  25use text::{BufferId, LineEnding};
  26use text::{Point, ToPoint};
  27use theme::ActiveTheme;
  28use unindent::Unindent as _;
  29use util::rel_path::rel_path;
  30use util::test::marked_text_offsets;
  31use util::{RandomCharIter, assert_set_eq, post_inc, test::marked_text_ranges};
  32
  33pub static TRAILING_WHITESPACE_REGEX: LazyLock<regex::Regex> = LazyLock::new(|| {
  34    RegexBuilder::new(r"[ \t]+$")
  35        .multi_line(true)
  36        .build()
  37        .expect("Failed to create TRAILING_WHITESPACE_REGEX")
  38});
  39
  40#[cfg(test)]
  41#[ctor::ctor]
  42fn init_logger() {
  43    zlog::init_test();
  44}
  45
  46#[gpui::test]
  47fn test_line_endings(cx: &mut gpui::App) {
  48    init_settings(cx, |_| {});
  49
  50    cx.new(|cx| {
  51        let mut buffer = Buffer::local("one\r\ntwo\rthree", cx).with_language(rust_lang(), cx);
  52        assert_eq!(buffer.text(), "one\ntwo\nthree");
  53        assert_eq!(buffer.line_ending(), LineEnding::Windows);
  54
  55        buffer.check_invariants();
  56        buffer.edit(
  57            [(buffer.len()..buffer.len(), "\r\nfour")],
  58            Some(AutoindentMode::EachLine),
  59            cx,
  60        );
  61        buffer.edit([(0..0, "zero\r\n")], None, cx);
  62        assert_eq!(buffer.text(), "zero\none\ntwo\nthree\nfour");
  63        assert_eq!(buffer.line_ending(), LineEnding::Windows);
  64        buffer.check_invariants();
  65
  66        buffer
  67    });
  68}
  69
  70#[gpui::test]
  71fn test_set_line_ending(cx: &mut TestAppContext) {
  72    let base = cx.new(|cx| Buffer::local("one\ntwo\nthree\n", cx));
  73    let base_replica = cx.new(|cx| {
  74        Buffer::from_proto(
  75            ReplicaId::new(1),
  76            Capability::ReadWrite,
  77            base.read(cx).to_proto(cx),
  78            None,
  79        )
  80        .unwrap()
  81    });
  82    base.update(cx, |_buffer, cx| {
  83        cx.subscribe(&base_replica, |this, _, event, cx| {
  84            if let BufferEvent::Operation {
  85                operation,
  86                is_local: true,
  87            } = event
  88            {
  89                this.apply_ops([operation.clone()], cx);
  90            }
  91        })
  92        .detach();
  93    });
  94    base_replica.update(cx, |_buffer, cx| {
  95        cx.subscribe(&base, |this, _, event, cx| {
  96            if let BufferEvent::Operation {
  97                operation,
  98                is_local: true,
  99            } = event
 100            {
 101                this.apply_ops([operation.clone()], cx);
 102            }
 103        })
 104        .detach();
 105    });
 106
 107    // Base
 108    base_replica.read_with(cx, |buffer, _| {
 109        assert_eq!(buffer.line_ending(), LineEnding::Unix);
 110    });
 111    base.update(cx, |buffer, cx| {
 112        assert_eq!(buffer.line_ending(), LineEnding::Unix);
 113        buffer.set_line_ending(LineEnding::Windows, cx);
 114        assert_eq!(buffer.line_ending(), LineEnding::Windows);
 115    });
 116    base_replica.read_with(cx, |buffer, _| {
 117        assert_eq!(buffer.line_ending(), LineEnding::Windows);
 118    });
 119    base.update(cx, |buffer, cx| {
 120        buffer.set_line_ending(LineEnding::Unix, cx);
 121        assert_eq!(buffer.line_ending(), LineEnding::Unix);
 122    });
 123    base_replica.read_with(cx, |buffer, _| {
 124        assert_eq!(buffer.line_ending(), LineEnding::Unix);
 125    });
 126
 127    // Replica
 128    base.read_with(cx, |buffer, _| {
 129        assert_eq!(buffer.line_ending(), LineEnding::Unix);
 130    });
 131    base_replica.update(cx, |buffer, cx| {
 132        assert_eq!(buffer.line_ending(), LineEnding::Unix);
 133        buffer.set_line_ending(LineEnding::Windows, cx);
 134        assert_eq!(buffer.line_ending(), LineEnding::Windows);
 135    });
 136    base.read_with(cx, |buffer, _| {
 137        assert_eq!(buffer.line_ending(), LineEnding::Windows);
 138    });
 139    base_replica.update(cx, |buffer, cx| {
 140        buffer.set_line_ending(LineEnding::Unix, cx);
 141        assert_eq!(buffer.line_ending(), LineEnding::Unix);
 142    });
 143    base.read_with(cx, |buffer, _| {
 144        assert_eq!(buffer.line_ending(), LineEnding::Unix);
 145    });
 146}
 147
 148#[gpui::test]
 149fn test_select_language(cx: &mut App) {
 150    init_settings(cx, |_| {});
 151
 152    let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
 153    registry.add(Arc::new(Language::new(
 154        LanguageConfig {
 155            name: LanguageName::new_static("Rust"),
 156            matcher: LanguageMatcher {
 157                path_suffixes: vec!["rs".to_string()],
 158                ..Default::default()
 159            },
 160            ..Default::default()
 161        },
 162        Some(tree_sitter_rust::LANGUAGE.into()),
 163    )));
 164    registry.add(Arc::new(Language::new(
 165        LanguageConfig {
 166            name: "Rust with longer extension".into(),
 167            matcher: LanguageMatcher {
 168                path_suffixes: vec!["longer.rs".to_string()],
 169                ..Default::default()
 170            },
 171            ..Default::default()
 172        },
 173        Some(tree_sitter_rust::LANGUAGE.into()),
 174    )));
 175    registry.add(Arc::new(Language::new(
 176        LanguageConfig {
 177            name: LanguageName::new_static("Make"),
 178            matcher: LanguageMatcher {
 179                path_suffixes: vec!["Makefile".to_string(), "mk".to_string()],
 180                ..Default::default()
 181            },
 182            ..Default::default()
 183        },
 184        Some(tree_sitter_rust::LANGUAGE.into()),
 185    )));
 186
 187    // matching file extension
 188    assert_eq!(
 189        registry
 190            .language_for_file(&file("src/lib.rs"), None, cx)
 191            .map(|l| l.name()),
 192        Some("Rust".into())
 193    );
 194    assert_eq!(
 195        registry
 196            .language_for_file(&file("src/lib.mk"), None, cx)
 197            .map(|l| l.name()),
 198        Some("Make".into())
 199    );
 200
 201    // matching longer, compound extension, part of which could also match another lang
 202    assert_eq!(
 203        registry
 204            .language_for_file(&file("src/lib.longer.rs"), None, cx)
 205            .map(|l| l.name()),
 206        Some("Rust with longer extension".into())
 207    );
 208
 209    // matching filename
 210    assert_eq!(
 211        registry
 212            .language_for_file(&file("src/Makefile"), None, cx)
 213            .map(|l| l.name()),
 214        Some("Make".into())
 215    );
 216
 217    // matching suffix that is not the full file extension or filename
 218    assert_eq!(
 219        registry
 220            .language_for_file(&file("zed/cars"), None, cx)
 221            .map(|l| l.name()),
 222        None
 223    );
 224    assert_eq!(
 225        registry
 226            .language_for_file(&file("zed/a.cars"), None, cx)
 227            .map(|l| l.name()),
 228        None
 229    );
 230    assert_eq!(
 231        registry
 232            .language_for_file(&file("zed/sumk"), None, cx)
 233            .map(|l| l.name()),
 234        None
 235    );
 236}
 237
 238#[gpui::test(iterations = 10)]
 239async fn test_first_line_pattern(cx: &mut TestAppContext) {
 240    cx.update(|cx| init_settings(cx, |_| {}));
 241
 242    let languages = LanguageRegistry::test(cx.executor());
 243    let languages = Arc::new(languages);
 244
 245    languages.register_test_language(LanguageConfig {
 246        name: "JavaScript".into(),
 247        matcher: LanguageMatcher {
 248            path_suffixes: vec!["js".into()],
 249            first_line_pattern: Some(Regex::new(r"\bnode\b").unwrap()),
 250            ..LanguageMatcher::default()
 251        },
 252        ..Default::default()
 253    });
 254
 255    assert!(
 256        cx.read(|cx| languages.language_for_file(&file("the/script"), None, cx))
 257            .is_none()
 258    );
 259    assert!(
 260        cx.read(|cx| languages.language_for_file(&file("the/script"), Some(&"nothing".into()), cx))
 261            .is_none()
 262    );
 263
 264    assert_eq!(
 265        cx.read(|cx| languages.language_for_file(
 266            &file("the/script"),
 267            Some(&"#!/bin/env node".into()),
 268            cx
 269        ))
 270        .unwrap()
 271        .name(),
 272        "JavaScript"
 273    );
 274}
 275
 276#[gpui::test]
 277async fn test_language_for_file_with_custom_file_types(cx: &mut TestAppContext) {
 278    cx.update(|cx| {
 279        init_settings(cx, |settings| {
 280            settings.file_types.get_or_insert_default().extend([
 281                ("TypeScript".into(), vec!["js".into()].into()),
 282                (
 283                    "JavaScript".into(),
 284                    vec!["*longer.ts".into(), "ecmascript".into()].into(),
 285                ),
 286                ("C++".into(), vec!["c".into(), "*.dev".into()].into()),
 287                (
 288                    "Dockerfile".into(),
 289                    vec!["Dockerfile".into(), "Dockerfile.*".into()].into(),
 290                ),
 291            ]);
 292        })
 293    });
 294
 295    let languages = Arc::new(LanguageRegistry::test(cx.executor()));
 296
 297    for config in [
 298        LanguageConfig {
 299            name: "JavaScript".into(),
 300            matcher: LanguageMatcher {
 301                path_suffixes: vec!["js".to_string()],
 302                ..Default::default()
 303            },
 304            ..Default::default()
 305        },
 306        LanguageConfig {
 307            name: "TypeScript".into(),
 308            matcher: LanguageMatcher {
 309                path_suffixes: vec!["ts".to_string(), "ts.ecmascript".to_string()],
 310                ..Default::default()
 311            },
 312            ..Default::default()
 313        },
 314        LanguageConfig {
 315            name: "C++".into(),
 316            matcher: LanguageMatcher {
 317                path_suffixes: vec!["cpp".to_string()],
 318                ..Default::default()
 319            },
 320            ..Default::default()
 321        },
 322        LanguageConfig {
 323            name: "C".into(),
 324            matcher: LanguageMatcher {
 325                path_suffixes: vec!["c".to_string()],
 326                ..Default::default()
 327            },
 328            ..Default::default()
 329        },
 330        LanguageConfig {
 331            name: "Dockerfile".into(),
 332            matcher: LanguageMatcher {
 333                path_suffixes: vec!["Dockerfile".to_string()],
 334                ..Default::default()
 335            },
 336            ..Default::default()
 337        },
 338    ] {
 339        languages.add(Arc::new(Language::new(config, None)));
 340    }
 341
 342    // matches system-provided lang extension
 343    let language = cx
 344        .read(|cx| languages.language_for_file(&file("foo.ts"), None, cx))
 345        .unwrap();
 346    assert_eq!(language.name(), "TypeScript");
 347    let language = cx
 348        .read(|cx| languages.language_for_file(&file("foo.ts.ecmascript"), None, cx))
 349        .unwrap();
 350    assert_eq!(language.name(), "TypeScript");
 351    let language = cx
 352        .read(|cx| languages.language_for_file(&file("foo.cpp"), None, cx))
 353        .unwrap();
 354    assert_eq!(language.name(), "C++");
 355
 356    // user configured lang extension, same length as system-provided
 357    let language = cx
 358        .read(|cx| languages.language_for_file(&file("foo.js"), None, cx))
 359        .unwrap();
 360    assert_eq!(language.name(), "TypeScript");
 361    let language = cx
 362        .read(|cx| languages.language_for_file(&file("foo.c"), None, cx))
 363        .unwrap();
 364    assert_eq!(language.name(), "C++");
 365
 366    // user configured lang extension, longer than system-provided
 367    let language = cx
 368        .read(|cx| languages.language_for_file(&file("foo.longer.ts"), None, cx))
 369        .unwrap();
 370    assert_eq!(language.name(), "JavaScript");
 371
 372    // user configured lang extension, shorter than system-provided
 373    let language = cx
 374        .read(|cx| languages.language_for_file(&file("foo.ecmascript"), None, cx))
 375        .unwrap();
 376    assert_eq!(language.name(), "JavaScript");
 377
 378    // user configured glob matches
 379    let language = cx
 380        .read(|cx| languages.language_for_file(&file("c-plus-plus.dev"), None, cx))
 381        .unwrap();
 382    assert_eq!(language.name(), "C++");
 383    // should match Dockerfile.* => Dockerfile, not *.dev => C++
 384    let language = cx
 385        .read(|cx| languages.language_for_file(&file("Dockerfile.dev"), None, cx))
 386        .unwrap();
 387    assert_eq!(language.name(), "Dockerfile");
 388}
 389
 390fn file(path: &str) -> Arc<dyn File> {
 391    Arc::new(TestFile {
 392        path: Arc::from(rel_path(path)),
 393        root_name: "zed".into(),
 394        local_root: None,
 395    })
 396}
 397
 398#[gpui::test]
 399fn test_edit_events(cx: &mut gpui::App) {
 400    let mut now = Instant::now();
 401    let buffer_1_events = Arc::new(Mutex::new(Vec::new()));
 402    let buffer_2_events = Arc::new(Mutex::new(Vec::new()));
 403
 404    let buffer1 = cx.new(|cx| Buffer::local("abcdef", cx));
 405    let buffer2 = cx.new(|cx| {
 406        Buffer::remote(
 407            BufferId::from(cx.entity_id().as_non_zero_u64()),
 408            ReplicaId::new(1),
 409            Capability::ReadWrite,
 410            "abcdef",
 411        )
 412    });
 413    let buffer1_ops = Arc::new(Mutex::new(Vec::new()));
 414    buffer1.update(cx, {
 415        let buffer1_ops = buffer1_ops.clone();
 416        |buffer, cx| {
 417            let buffer_1_events = buffer_1_events.clone();
 418            cx.subscribe(&buffer1, move |_, _, event, _| match event.clone() {
 419                BufferEvent::Operation {
 420                    operation,
 421                    is_local: true,
 422                } => buffer1_ops.lock().push(operation),
 423                event => buffer_1_events.lock().push(event),
 424            })
 425            .detach();
 426            let buffer_2_events = buffer_2_events.clone();
 427            cx.subscribe(&buffer2, move |_, _, event, _| match event.clone() {
 428                BufferEvent::Operation {
 429                    is_local: false, ..
 430                } => {}
 431                event => buffer_2_events.lock().push(event),
 432            })
 433            .detach();
 434
 435            // An edit emits an edited event, followed by a dirty changed event,
 436            // since the buffer was previously in a clean state.
 437            buffer.edit([(2..4, "XYZ")], None, cx);
 438
 439            // An empty transaction does not emit any events.
 440            buffer.start_transaction();
 441            buffer.end_transaction(cx);
 442
 443            // A transaction containing two edits emits one edited event.
 444            now += Duration::from_secs(1);
 445            buffer.start_transaction_at(now);
 446            buffer.edit([(5..5, "u")], None, cx);
 447            buffer.edit([(6..6, "w")], None, cx);
 448            buffer.end_transaction_at(now, cx);
 449
 450            // Undoing a transaction emits one edited event.
 451            buffer.undo(cx);
 452        }
 453    });
 454
 455    // Incorporating a set of remote ops emits a single edited event,
 456    // followed by a dirty changed event.
 457    buffer2.update(cx, |buffer, cx| {
 458        buffer.apply_ops(buffer1_ops.lock().drain(..), cx);
 459    });
 460    assert_eq!(
 461        mem::take(&mut *buffer_1_events.lock()),
 462        vec![
 463            BufferEvent::Edited { is_local: true },
 464            BufferEvent::DirtyChanged,
 465            BufferEvent::Edited { is_local: true },
 466            BufferEvent::Edited { is_local: true },
 467        ]
 468    );
 469    assert_eq!(
 470        mem::take(&mut *buffer_2_events.lock()),
 471        vec![
 472            BufferEvent::Edited { is_local: false },
 473            BufferEvent::DirtyChanged
 474        ]
 475    );
 476
 477    buffer1.update(cx, |buffer, cx| {
 478        // Undoing the first transaction emits edited event, followed by a
 479        // dirty changed event, since the buffer is again in a clean state.
 480        buffer.undo(cx);
 481    });
 482    // Incorporating the remote ops again emits a single edited event,
 483    // followed by a dirty changed event.
 484    buffer2.update(cx, |buffer, cx| {
 485        buffer.apply_ops(buffer1_ops.lock().drain(..), cx);
 486    });
 487    assert_eq!(
 488        mem::take(&mut *buffer_1_events.lock()),
 489        vec![
 490            BufferEvent::Edited { is_local: true },
 491            BufferEvent::DirtyChanged,
 492        ]
 493    );
 494    assert_eq!(
 495        mem::take(&mut *buffer_2_events.lock()),
 496        vec![
 497            BufferEvent::Edited { is_local: false },
 498            BufferEvent::DirtyChanged
 499        ]
 500    );
 501}
 502
 503#[gpui::test]
 504async fn test_apply_diff(cx: &mut TestAppContext) {
 505    let (text, offsets) = marked_text_offsets(
 506        "one two three\nfour fiˇve six\nseven eightˇ nine\nten eleven twelve\n",
 507    );
 508    let buffer = cx.new(|cx| Buffer::local(text, cx));
 509    let anchors = buffer.update(cx, |buffer, _| {
 510        offsets
 511            .iter()
 512            .map(|offset| buffer.anchor_before(offset))
 513            .collect::<Vec<_>>()
 514    });
 515
 516    let (text, offsets) = marked_text_offsets(
 517        "one two three\n{\nfour FIVEˇ six\n}\nseven AND EIGHTˇ nine\nten eleven twelve\n",
 518    );
 519
 520    let diff = buffer.update(cx, |b, cx| b.diff(text.clone(), cx)).await;
 521    buffer.update(cx, |buffer, cx| {
 522        buffer.apply_diff(diff, cx).unwrap();
 523        assert_eq!(buffer.text(), text);
 524        let actual_offsets = anchors
 525            .iter()
 526            .map(|anchor| anchor.to_offset(buffer))
 527            .collect::<Vec<_>>();
 528        assert_eq!(actual_offsets, offsets);
 529    });
 530
 531    let (text, offsets) =
 532        marked_text_offsets("one two three\n{\nˇ}\nseven AND EIGHTEENˇ nine\nten eleven twelve\n");
 533
 534    let diff = buffer.update(cx, |b, cx| b.diff(text.clone(), cx)).await;
 535    buffer.update(cx, |buffer, cx| {
 536        buffer.apply_diff(diff, cx).unwrap();
 537        assert_eq!(buffer.text(), text);
 538        let actual_offsets = anchors
 539            .iter()
 540            .map(|anchor| anchor.to_offset(buffer))
 541            .collect::<Vec<_>>();
 542        assert_eq!(actual_offsets, offsets);
 543    });
 544}
 545
 546#[gpui::test(iterations = 10)]
 547async fn test_normalize_whitespace(cx: &mut gpui::TestAppContext) {
 548    let text = [
 549        "zero",     //
 550        "one  ",    // 2 trailing spaces
 551        "two",      //
 552        "three   ", // 3 trailing spaces
 553        "four",     //
 554        "five    ", // 4 trailing spaces
 555    ]
 556    .join("\n");
 557
 558    let buffer = cx.new(|cx| Buffer::local(text, cx));
 559
 560    // Spawn a task to format the buffer's whitespace.
 561    // Pause so that the formatting task starts running.
 562    let format = buffer.update(cx, |buffer, cx| buffer.remove_trailing_whitespace(cx));
 563    smol::future::yield_now().await;
 564
 565    // Edit the buffer while the normalization task is running.
 566    let version_before_edit = buffer.update(cx, |buffer, _| buffer.version());
 567    buffer.update(cx, |buffer, cx| {
 568        buffer.edit(
 569            [
 570                (Point::new(0, 1)..Point::new(0, 1), "EE"),
 571                (Point::new(3, 5)..Point::new(3, 5), "EEE"),
 572            ],
 573            None,
 574            cx,
 575        );
 576    });
 577
 578    let format_diff = format.await;
 579    buffer.update(cx, |buffer, cx| {
 580        let version_before_format = format_diff.base_version.clone();
 581        buffer.apply_diff(format_diff, cx);
 582
 583        // The outcome depends on the order of concurrent tasks.
 584        //
 585        // If the edit occurred while searching for trailing whitespace ranges,
 586        // then the trailing whitespace region touched by the edit is left intact.
 587        if version_before_format == version_before_edit {
 588            assert_eq!(
 589                buffer.text(),
 590                [
 591                    "zEEero",      //
 592                    "one",         //
 593                    "two",         //
 594                    "threeEEE   ", //
 595                    "four",        //
 596                    "five",        //
 597                ]
 598                .join("\n")
 599            );
 600        }
 601        // Otherwise, all trailing whitespace is removed.
 602        else {
 603            assert_eq!(
 604                buffer.text(),
 605                [
 606                    "zEEero",   //
 607                    "one",      //
 608                    "two",      //
 609                    "threeEEE", //
 610                    "four",     //
 611                    "five",     //
 612                ]
 613                .join("\n")
 614            );
 615        }
 616    });
 617}
 618
 619#[gpui::test]
 620async fn test_reparse(cx: &mut gpui::TestAppContext) {
 621    let text = "fn a() {}";
 622    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(rust_lang(), cx));
 623
 624    // Wait for the initial text to parse
 625    cx.executor().run_until_parked();
 626    assert!(!buffer.update(cx, |buffer, _| buffer.is_parsing()));
 627    assert_eq!(
 628        get_tree_sexp(&buffer, cx),
 629        concat!(
 630            "(source_file (function_item name: (identifier) ",
 631            "parameters: (parameters) ",
 632            "body: (block)))"
 633        )
 634    );
 635
 636    buffer.update(cx, |buffer, _| buffer.set_sync_parse_timeout(None));
 637
 638    // Perform some edits (add parameter and variable reference)
 639    // Parsing doesn't begin until the transaction is complete
 640    buffer.update(cx, |buf, cx| {
 641        buf.start_transaction();
 642
 643        let offset = buf.text().find(')').unwrap();
 644        buf.edit([(offset..offset, "b: C")], None, cx);
 645        assert!(!buf.is_parsing());
 646
 647        let offset = buf.text().find('}').unwrap();
 648        buf.edit([(offset..offset, " d; ")], None, cx);
 649        assert!(!buf.is_parsing());
 650
 651        buf.end_transaction(cx);
 652        assert_eq!(buf.text(), "fn a(b: C) { d; }");
 653        assert!(buf.is_parsing());
 654    });
 655    cx.executor().run_until_parked();
 656    assert!(!buffer.update(cx, |buffer, _| buffer.is_parsing()));
 657    assert_eq!(
 658        get_tree_sexp(&buffer, cx),
 659        concat!(
 660            "(source_file (function_item name: (identifier) ",
 661            "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
 662            "body: (block (expression_statement (identifier)))))"
 663        )
 664    );
 665
 666    // Perform a series of edits without waiting for the current parse to complete:
 667    // * turn identifier into a field expression
 668    // * turn field expression into a method call
 669    // * add a turbofish to the method call
 670    buffer.update(cx, |buf, cx| {
 671        let offset = buf.text().find(';').unwrap();
 672        buf.edit([(offset..offset, ".e")], None, cx);
 673        assert_eq!(buf.text(), "fn a(b: C) { d.e; }");
 674        assert!(buf.is_parsing());
 675    });
 676    buffer.update(cx, |buf, cx| {
 677        let offset = buf.text().find(';').unwrap();
 678        buf.edit([(offset..offset, "(f)")], None, cx);
 679        assert_eq!(buf.text(), "fn a(b: C) { d.e(f); }");
 680        assert!(buf.is_parsing());
 681    });
 682    buffer.update(cx, |buf, cx| {
 683        let offset = buf.text().find("(f)").unwrap();
 684        buf.edit([(offset..offset, "::<G>")], None, cx);
 685        assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
 686        assert!(buf.is_parsing());
 687    });
 688    cx.executor().run_until_parked();
 689    assert_eq!(
 690        get_tree_sexp(&buffer, cx),
 691        concat!(
 692            "(source_file (function_item name: (identifier) ",
 693            "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
 694            "body: (block (expression_statement (call_expression ",
 695            "function: (generic_function ",
 696            "function: (field_expression value: (identifier) field: (field_identifier)) ",
 697            "type_arguments: (type_arguments (type_identifier))) ",
 698            "arguments: (arguments (identifier)))))))",
 699        )
 700    );
 701
 702    buffer.update(cx, |buf, cx| {
 703        buf.undo(cx);
 704        buf.undo(cx);
 705        buf.undo(cx);
 706        buf.undo(cx);
 707        assert_eq!(buf.text(), "fn a() {}");
 708        assert!(buf.is_parsing());
 709    });
 710
 711    cx.executor().run_until_parked();
 712    assert_eq!(
 713        get_tree_sexp(&buffer, cx),
 714        concat!(
 715            "(source_file (function_item name: (identifier) ",
 716            "parameters: (parameters) ",
 717            "body: (block)))"
 718        )
 719    );
 720
 721    buffer.update(cx, |buf, cx| {
 722        buf.redo(cx);
 723        buf.redo(cx);
 724        buf.redo(cx);
 725        buf.redo(cx);
 726        assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
 727        assert!(buf.is_parsing());
 728    });
 729    cx.executor().run_until_parked();
 730    assert_eq!(
 731        get_tree_sexp(&buffer, cx),
 732        concat!(
 733            "(source_file (function_item name: (identifier) ",
 734            "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
 735            "body: (block (expression_statement (call_expression ",
 736            "function: (generic_function ",
 737            "function: (field_expression value: (identifier) field: (field_identifier)) ",
 738            "type_arguments: (type_arguments (type_identifier))) ",
 739            "arguments: (arguments (identifier)))))))",
 740        )
 741    );
 742}
 743
 744#[gpui::test]
 745async fn test_resetting_language(cx: &mut gpui::TestAppContext) {
 746    let buffer = cx.new(|cx| {
 747        let mut buffer = Buffer::local("{}", cx).with_language(rust_lang(), cx);
 748        buffer.set_sync_parse_timeout(None);
 749        buffer
 750    });
 751
 752    // Wait for the initial text to parse
 753    cx.executor().run_until_parked();
 754    assert_eq!(
 755        get_tree_sexp(&buffer, cx),
 756        "(source_file (expression_statement (block)))"
 757    );
 758
 759    buffer.update(cx, |buffer, cx| {
 760        buffer.set_language(Some(Arc::new(json_lang())), cx)
 761    });
 762    cx.executor().run_until_parked();
 763    assert_eq!(get_tree_sexp(&buffer, cx), "(document (object))");
 764}
 765
 766#[gpui::test]
 767async fn test_outline(cx: &mut gpui::TestAppContext) {
 768    let text = r#"
 769        struct Person {
 770            name: String,
 771            age: usize,
 772        }
 773
 774        mod module {
 775            enum LoginState {
 776                LoggedOut,
 777                LoggingOn,
 778                LoggedIn {
 779                    person: Person,
 780                    time: Instant,
 781                }
 782            }
 783        }
 784
 785        impl Eq for Person {}
 786
 787        impl Drop for Person {
 788            fn drop(&mut self) {
 789                println!("bye");
 790            }
 791        }
 792    "#
 793    .unindent();
 794
 795    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(rust_lang(), cx));
 796    let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
 797    let outline = snapshot.outline(None);
 798
 799    assert_eq!(
 800        outline
 801            .items
 802            .iter()
 803            .map(|item| (
 804                item.text.as_str(),
 805                item.depth,
 806                item.to_point(&snapshot).body_range(&snapshot)
 807                    .map(|range| minimize_space(&snapshot.text_for_range(range).collect::<String>()))
 808            ))
 809            .collect::<Vec<_>>(),
 810        &[
 811            ("struct Person", 0, Some("name: String, age: usize,".to_string())),
 812            ("name", 1, None),
 813            ("age", 1, None),
 814            (
 815                "mod module",
 816                0,
 817                Some(
 818                    "enum LoginState { LoggedOut, LoggingOn, LoggedIn { person: Person, time: Instant, } }".to_string()
 819                )
 820            ),
 821            (
 822                "enum LoginState",
 823                1,
 824                Some("LoggedOut, LoggingOn, LoggedIn { person: Person, time: Instant, }".to_string())
 825            ),
 826            ("LoggedOut", 2, None),
 827            ("LoggingOn", 2, None),
 828            ("LoggedIn", 2, Some("person: Person, time: Instant,".to_string())),
 829            ("person", 3, None),
 830            ("time", 3, None),
 831            ("impl Eq for Person", 0, Some("".to_string())),
 832            (
 833                "impl Drop for Person",
 834                0,
 835                Some("fn drop(&mut self) { println!(\"bye\"); }".to_string())
 836            ),
 837            ("fn drop", 1, Some("println!(\"bye\");".to_string())),
 838        ]
 839    );
 840
 841    // Without space, we only match on names
 842    assert_eq!(
 843        search(&outline, "oon", cx).await,
 844        &[
 845            ("mod module", vec![]),                    // included as the parent of a match
 846            ("enum LoginState", vec![]),               // included as the parent of a match
 847            ("LoggingOn", vec![1, 7, 8]),              // matches
 848            ("impl Drop for Person", vec![7, 18, 19]), // matches in two disjoint names
 849        ]
 850    );
 851
 852    assert_eq!(
 853        search(&outline, "dp p", cx).await,
 854        &[
 855            ("impl Drop for Person", vec![5, 8, 9, 14]),
 856            ("fn drop", vec![]),
 857        ]
 858    );
 859    assert_eq!(
 860        search(&outline, "dpn", cx).await,
 861        &[("impl Drop for Person", vec![5, 14, 19])]
 862    );
 863    assert_eq!(
 864        search(&outline, "impl ", cx).await,
 865        &[
 866            ("impl Eq for Person", vec![0, 1, 2, 3, 4]),
 867            ("impl Drop for Person", vec![0, 1, 2, 3, 4]),
 868            ("fn drop", vec![]),
 869        ]
 870    );
 871
 872    fn minimize_space(text: &str) -> String {
 873        static WHITESPACE: LazyLock<Regex> = LazyLock::new(|| Regex::new("[\\n\\s]+").unwrap());
 874        WHITESPACE.replace_all(text, " ").trim().to_string()
 875    }
 876
 877    async fn search<'a>(
 878        outline: &'a Outline<Anchor>,
 879        query: &'a str,
 880        cx: &'a gpui::TestAppContext,
 881    ) -> Vec<(&'a str, Vec<usize>)> {
 882        let matches = cx
 883            .update(|cx| outline.search(query, cx.background_executor().clone()))
 884            .await;
 885        matches
 886            .into_iter()
 887            .map(|mat| (outline.items[mat.candidate_id].text.as_str(), mat.positions))
 888            .collect::<Vec<_>>()
 889    }
 890}
 891
 892#[gpui::test]
 893async fn test_outline_nodes_with_newlines(cx: &mut gpui::TestAppContext) {
 894    let text = r#"
 895        impl A for B<
 896            C
 897        > {
 898        };
 899    "#
 900    .unindent();
 901
 902    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(rust_lang(), cx));
 903    let outline = buffer.update(cx, |buffer, _| buffer.snapshot().outline(None));
 904
 905    assert_eq!(
 906        outline
 907            .items
 908            .iter()
 909            .map(|item| (item.text.as_str(), item.depth))
 910            .collect::<Vec<_>>(),
 911        &[("impl A for B<", 0)]
 912    );
 913}
 914
 915#[gpui::test]
 916async fn test_outline_with_extra_context(cx: &mut gpui::TestAppContext) {
 917    let language = javascript_lang()
 918        .with_outline_query(
 919            r#"
 920            (function_declaration
 921                "function" @context
 922                name: (_) @name
 923                parameters: (formal_parameters
 924                    "(" @context.extra
 925                    ")" @context.extra)) @item
 926            "#,
 927        )
 928        .unwrap();
 929
 930    let text = r#"
 931        function a() {}
 932        function b(c) {}
 933    "#
 934    .unindent();
 935
 936    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(Arc::new(language), cx));
 937    let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
 938
 939    // extra context nodes are included in the outline.
 940    let outline = snapshot.outline(None);
 941    assert_eq!(
 942        outline
 943            .items
 944            .iter()
 945            .map(|item| (item.text.as_str(), item.depth))
 946            .collect::<Vec<_>>(),
 947        &[("function a()", 0), ("function b( )", 0),]
 948    );
 949
 950    // extra context nodes do not appear in breadcrumbs.
 951    let symbols = snapshot.symbols_containing(3, None);
 952    assert_eq!(
 953        symbols
 954            .iter()
 955            .map(|item| (item.text.as_str(), item.depth))
 956            .collect::<Vec<_>>(),
 957        &[("function a", 0)]
 958    );
 959}
 960
 961#[gpui::test]
 962fn test_outline_annotations(cx: &mut App) {
 963    // Add this new test case
 964    let text = r#"
 965        /// This is a doc comment
 966        /// that spans multiple lines
 967        fn annotated_function() {
 968            // This is not an annotation
 969        }
 970
 971        // This is a single-line annotation
 972        fn another_function() {}
 973
 974        fn unannotated_function() {}
 975
 976        // This comment is not an annotation
 977
 978        fn function_after_blank_line() {}
 979    "#
 980    .unindent();
 981
 982    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(rust_lang(), cx));
 983    let outline = buffer.update(cx, |buffer, _| buffer.snapshot().outline(None));
 984
 985    assert_eq!(
 986        outline
 987            .items
 988            .into_iter()
 989            .map(|item| (
 990                item.text,
 991                item.depth,
 992                item.annotation_range
 993                    .map(|range| { buffer.read(cx).text_for_range(range).collect::<String>() })
 994            ))
 995            .collect::<Vec<_>>(),
 996        &[
 997            (
 998                "fn annotated_function".to_string(),
 999                0,
1000                Some("/// This is a doc comment\n/// that spans multiple lines".to_string())
1001            ),
1002            (
1003                "fn another_function".to_string(),
1004                0,
1005                Some("// This is a single-line annotation".to_string())
1006            ),
1007            ("fn unannotated_function".to_string(), 0, None),
1008            ("fn function_after_blank_line".to_string(), 0, None),
1009        ]
1010    );
1011}
1012
1013#[gpui::test]
1014async fn test_symbols_containing(cx: &mut gpui::TestAppContext) {
1015    let text = r#"
1016        impl Person {
1017            fn one() {
1018                1
1019            }
1020
1021            fn two() {
1022                2
1023            }fn three() {
1024                3
1025            }
1026        }
1027    "#
1028    .unindent();
1029
1030    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(rust_lang(), cx));
1031    let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
1032
1033    // point is at the start of an item
1034    assert_eq!(
1035        symbols_containing(Point::new(1, 4), &snapshot),
1036        vec![
1037            (
1038                "impl Person".to_string(),
1039                Point::new(0, 0)..Point::new(10, 1)
1040            ),
1041            ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
1042        ]
1043    );
1044
1045    // point is in the middle of an item
1046    assert_eq!(
1047        symbols_containing(Point::new(2, 8), &snapshot),
1048        vec![
1049            (
1050                "impl Person".to_string(),
1051                Point::new(0, 0)..Point::new(10, 1)
1052            ),
1053            ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
1054        ]
1055    );
1056
1057    // point is at the end of an item
1058    assert_eq!(
1059        symbols_containing(Point::new(3, 5), &snapshot),
1060        vec![
1061            (
1062                "impl Person".to_string(),
1063                Point::new(0, 0)..Point::new(10, 1)
1064            ),
1065            ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
1066        ]
1067    );
1068
1069    // point is in between two adjacent items
1070    assert_eq!(
1071        symbols_containing(Point::new(7, 5), &snapshot),
1072        vec![
1073            (
1074                "impl Person".to_string(),
1075                Point::new(0, 0)..Point::new(10, 1)
1076            ),
1077            ("fn two".to_string(), Point::new(5, 4)..Point::new(7, 5))
1078        ]
1079    );
1080
1081    fn symbols_containing(
1082        position: Point,
1083        snapshot: &BufferSnapshot,
1084    ) -> Vec<(String, Range<Point>)> {
1085        snapshot
1086            .symbols_containing(position, None)
1087            .into_iter()
1088            .map(|item| {
1089                (
1090                    item.text,
1091                    item.range.start.to_point(snapshot)..item.range.end.to_point(snapshot),
1092                )
1093            })
1094            .collect()
1095    }
1096
1097    let (text, offsets) = marked_text_offsets(
1098        &"
1099        // ˇ😅 //
1100        fn test() {
1101        }
1102    "
1103        .unindent(),
1104    );
1105    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(rust_lang(), cx));
1106    let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
1107
1108    // note, it would be nice to actually return the method test in this
1109    // case, but primarily asserting we don't crash because of the multibyte character.
1110    assert_eq!(snapshot.symbols_containing(offsets[0], None), vec![]);
1111}
1112
1113#[gpui::test]
1114fn test_text_objects(cx: &mut App) {
1115    let (text, ranges) = marked_text_ranges(
1116        indoc! {r#"
1117            impl Hello {
1118                fn say() -> u8 { return /* ˇhi */ 1 }
1119            }"#
1120        },
1121        false,
1122    );
1123
1124    let buffer = cx.new(|cx| Buffer::local(text.clone(), cx).with_language(rust_lang(), cx));
1125    let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
1126
1127    let matches = snapshot
1128        .text_object_ranges(ranges[0].clone(), TreeSitterOptions::default())
1129        .map(|(range, text_object)| (&text[range], text_object))
1130        .collect::<Vec<_>>();
1131
1132    assert_eq!(
1133        matches,
1134        &[
1135            ("/* hi */", TextObject::AroundComment),
1136            ("return /* hi */ 1", TextObject::InsideFunction),
1137            (
1138                "fn say() -> u8 { return /* hi */ 1 }",
1139                TextObject::AroundFunction
1140            ),
1141            (
1142                "fn say() -> u8 { return /* hi */ 1 }",
1143                TextObject::InsideClass
1144            ),
1145            (
1146                "impl Hello {\n    fn say() -> u8 { return /* hi */ 1 }\n}",
1147                TextObject::AroundClass
1148            ),
1149        ],
1150    )
1151}
1152
1153#[gpui::test]
1154fn test_text_objects_with_has_parent_predicate(cx: &mut App) {
1155    use std::borrow::Cow;
1156
1157    // Create a language with a custom text_objects query that uses #has-parent?
1158    // This query only matches closure_expression when it's inside a call_expression
1159    let language = Language::new(
1160        LanguageConfig {
1161            name: "Rust".into(),
1162            matcher: LanguageMatcher {
1163                path_suffixes: vec!["rs".to_string()],
1164                ..Default::default()
1165            },
1166            ..Default::default()
1167        },
1168        Some(tree_sitter_rust::LANGUAGE.into()),
1169    )
1170    .with_queries(LanguageQueries {
1171        text_objects: Some(Cow::from(indoc! {r#"
1172            ; Only match closures that are arguments to function calls
1173            (closure_expression) @function.around
1174              (#has-parent? @function.around arguments)
1175        "#})),
1176        ..Default::default()
1177    })
1178    .expect("Could not parse queries");
1179
1180    let (text, ranges) = marked_text_ranges(
1181        indoc! {r#"
1182            fn main() {
1183                let standalone = |x| x + 1;
1184                let result = foo(|y| y * ˇ2);
1185            }"#
1186        },
1187        false,
1188    );
1189
1190    let buffer = cx.new(|cx| Buffer::local(text.clone(), cx).with_language(Arc::new(language), cx));
1191    let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
1192
1193    let matches = snapshot
1194        .text_object_ranges(ranges[0].clone(), TreeSitterOptions::default())
1195        .map(|(range, text_object)| (&text[range], text_object))
1196        .collect::<Vec<_>>();
1197
1198    // Should only match the closure inside foo(), not the standalone closure
1199    assert_eq!(matches, &[("|y| y * 2", TextObject::AroundFunction),]);
1200}
1201
1202#[gpui::test]
1203fn test_text_objects_with_not_has_parent_predicate(cx: &mut App) {
1204    use std::borrow::Cow;
1205
1206    // Create a language with a custom text_objects query that uses #not-has-parent?
1207    // This query only matches closure_expression when it's NOT inside a call_expression
1208    let language = Language::new(
1209        LanguageConfig {
1210            name: "Rust".into(),
1211            matcher: LanguageMatcher {
1212                path_suffixes: vec!["rs".to_string()],
1213                ..Default::default()
1214            },
1215            ..Default::default()
1216        },
1217        Some(tree_sitter_rust::LANGUAGE.into()),
1218    )
1219    .with_queries(LanguageQueries {
1220        text_objects: Some(Cow::from(indoc! {r#"
1221            ; Only match closures that are NOT arguments to function calls
1222            (closure_expression) @function.around
1223              (#not-has-parent? @function.around arguments)
1224        "#})),
1225        ..Default::default()
1226    })
1227    .expect("Could not parse queries");
1228
1229    let (text, ranges) = marked_text_ranges(
1230        indoc! {r#"
1231            fn main() {
1232                let standalone = |x| x +ˇ 1;
1233                let result = foo(|y| y * 2);
1234            }"#
1235        },
1236        false,
1237    );
1238
1239    let buffer = cx.new(|cx| Buffer::local(text.clone(), cx).with_language(Arc::new(language), cx));
1240    let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
1241
1242    let matches = snapshot
1243        .text_object_ranges(ranges[0].clone(), TreeSitterOptions::default())
1244        .map(|(range, text_object)| (&text[range], text_object))
1245        .collect::<Vec<_>>();
1246
1247    // Should only match the standalone closure, not the one inside foo()
1248    assert_eq!(matches, &[("|x| x + 1", TextObject::AroundFunction),]);
1249}
1250
1251#[gpui::test]
1252fn test_enclosing_bracket_ranges(cx: &mut App) {
1253    #[track_caller]
1254    fn assert(selection_text: &'static str, range_markers: Vec<&'static str>, cx: &mut App) {
1255        assert_bracket_pairs(selection_text, range_markers, rust_lang(), cx)
1256    }
1257
1258    assert(
1259        indoc! {"
1260            mod x {
1261                moˇd y {
1262
1263                }
1264            }
1265            let foo = 1;"},
1266        vec![indoc! {"
1267            mod x «{»
1268                mod y {
1269
1270                }
1271            «}»
1272            let foo = 1;"}],
1273        cx,
1274    );
1275
1276    assert(
1277        indoc! {"
1278            mod x {
1279                mod y ˇ{
1280
1281                }
1282            }
1283            let foo = 1;"},
1284        vec![
1285            indoc! {"
1286                mod x «{»
1287                    mod y {
1288
1289                    }
1290                «}»
1291                let foo = 1;"},
1292            indoc! {"
1293                mod x {
1294                    mod y «{»
1295
1296                    «}»
1297                }
1298                let foo = 1;"},
1299        ],
1300        cx,
1301    );
1302
1303    assert(
1304        indoc! {"
1305            mod x {
1306                mod y {
1307
13081309            }
1310            let foo = 1;"},
1311        vec![
1312            indoc! {"
1313                mod x «{»
1314                    mod y {
1315
1316                    }
1317                «}»
1318                let foo = 1;"},
1319            indoc! {"
1320                mod x {
1321                    mod y «{»
1322
1323                    «}»
1324                }
1325                let foo = 1;"},
1326        ],
1327        cx,
1328    );
1329
1330    assert(
1331        indoc! {"
1332            mod x {
1333                mod y {
1334
1335                }
1336            ˇ}
1337            let foo = 1;"},
1338        vec![indoc! {"
1339            mod x «{»
1340                mod y {
1341
1342                }
1343            «}»
1344            let foo = 1;"}],
1345        cx,
1346    );
1347
1348    assert(
1349        indoc! {"
1350            mod x {
1351                mod y {
1352
1353                }
1354            }
1355            let fˇoo = 1;"},
1356        Vec::new(),
1357        cx,
1358    );
1359
1360    // Regression test: avoid crash when querying at the end of the buffer.
1361    assert(
1362        indoc! {"
1363            mod x {
1364                mod y {
1365
1366                }
1367            }
1368            let foo = 1;ˇ"},
1369        Vec::new(),
1370        cx,
1371    );
1372}
1373
1374#[gpui::test]
1375fn test_enclosing_bracket_ranges_large_block(cx: &mut App) {
1376    // Build a buffer with an impl block large enough that the distance between
1377    // `{` and `}` exceeds MAX_BYTES_TO_QUERY (16 KB). Each comment line is
1378    // ~24 bytes, so ~700 lines push us past the limit.
1379    let comment_line_count = 1000;
1380    let mut source = String::from("impl Foo {\n");
1381    for i in 0..comment_line_count {
1382        writeln!(source, "    // line {i:04}  padding").unwrap();
1383    }
1384    source.push_str("}\n");
1385
1386    let buffer = cx.new(|cx| Buffer::local(source.clone(), cx).with_language(rust_lang(), cx));
1387    let snapshot = buffer.update(cx, |buffer, _cx| buffer.snapshot());
1388
1389    let open_brace = source.find('{').unwrap();
1390    let close_brace = source.rfind('}').unwrap();
1391
1392    // Cursor right after the opening brace — should find the enclosing pair.
1393    let cursor = open_brace + 1;
1394    let pairs = snapshot
1395        .enclosing_bracket_ranges(cursor..cursor)
1396        .map(|pair| (pair.open_range, pair.close_range))
1397        .collect::<Vec<_>>();
1398    assert_eq!(
1399        pairs,
1400        vec![(open_brace..open_brace + 1, close_brace..close_brace + 1)],
1401        "enclosing_bracket_ranges should find the bracket pair even when \
1402         open and close are in different row-chunks"
1403    );
1404
1405    // Cursor at the opening brace itself.
1406    let pairs = snapshot
1407        .enclosing_bracket_ranges(open_brace..open_brace)
1408        .map(|pair| (pair.open_range, pair.close_range))
1409        .collect::<Vec<_>>();
1410    assert_eq!(
1411        pairs,
1412        vec![(open_brace..open_brace + 1, close_brace..close_brace + 1)],
1413        "cursor at the opening brace should also find the pair"
1414    );
1415
1416    // Cursor somewhere in the middle of the block.
1417    let middle = source.len() / 2;
1418    let pairs = snapshot
1419        .enclosing_bracket_ranges(middle..middle)
1420        .map(|pair| (pair.open_range, pair.close_range))
1421        .collect::<Vec<_>>();
1422    assert_eq!(
1423        pairs,
1424        vec![(open_brace..open_brace + 1, close_brace..close_brace + 1)],
1425        "cursor in the middle of a large block should find the enclosing pair"
1426    );
1427}
1428
1429#[gpui::test]
1430fn test_bracket_pairs_for_large_enclosing_blocks(cx: &mut App) {
1431    use crate::syntax_map::MAX_BYTES_TO_QUERY;
1432
1433    // Build a source that looks like real code: uses, structs, and other
1434    // items before a large impl block — similar to editor.rs.
1435    let mut source = String::from(
1436        "use std::collections::HashMap;\n\
1437         use std::sync::Arc;\n\
1438         \n\
1439         pub struct Foo {\n\
1440         \x20   field_a: i32,\n\
1441         \x20   field_b: String,\n\
1442         }\n\
1443         \n\
1444         pub struct Bar {\n\
1445         \x20   items: Vec<Foo>,\n\
1446         }\n\
1447         \n",
1448    );
1449    let impl_start = source.len();
1450    source.push_str("impl Foo {\n");
1451    let fn_body = "        let x = 1;\n        let y = 2;\n        x + y\n";
1452    let mut fn_count = 0;
1453    while source.len() < MAX_BYTES_TO_QUERY + 1000 {
1454        writeln!(
1455            source,
1456            "    fn func_{fn_count}() -> i32 {{\n{fn_body}    }}"
1457        )
1458        .unwrap();
1459        fn_count += 1;
1460    }
1461    source.push_str("}\n");
1462
1463    let buffer = cx.new(|cx| Buffer::local(source.clone(), cx).with_language(rust_lang(), cx));
1464    let snapshot = buffer.update(cx, |buffer, _cx| buffer.snapshot());
1465
1466    let open_brace = source[impl_start..].find('{').unwrap() + impl_start;
1467    let close_brace = source.rfind('}').unwrap();
1468
1469    // Query from a viewport near the beginning of the impl block.
1470    let viewport_start = open_brace + 1;
1471    let viewport_end = (viewport_start + 500).min(source.len());
1472    let pairs = snapshot.bracket_pairs_for_large_enclosing_blocks(&(viewport_start..viewport_end));
1473    assert_eq!(
1474        pairs.len(),
1475        1,
1476        "should find exactly one large enclosing bracket pair from top viewport"
1477    );
1478    assert_eq!(pairs[0].open_range, open_brace..open_brace + 1);
1479    assert_eq!(pairs[0].close_range, close_brace..close_brace + 1);
1480    assert_eq!(pairs[0].color_index, Some(0), "outermost block has depth 0");
1481
1482    // Query from a viewport in the middle of the impl block.
1483    let middle = source.len() / 2;
1484    let pairs = snapshot.bracket_pairs_for_large_enclosing_blocks(&(middle..middle + 500));
1485    assert_eq!(
1486        pairs.len(),
1487        1,
1488        "should find exactly one large enclosing bracket pair from middle viewport"
1489    );
1490    assert_eq!(pairs[0].open_range, open_brace..open_brace + 1);
1491    assert_eq!(pairs[0].close_range, close_brace..close_brace + 1);
1492
1493    // Query from a viewport near the end of the impl block.
1494    let near_end = close_brace.saturating_sub(200);
1495    let pairs = snapshot.bracket_pairs_for_large_enclosing_blocks(&(near_end..close_brace + 1));
1496    assert_eq!(
1497        pairs.len(),
1498        1,
1499        "should find exactly one large enclosing bracket pair from bottom viewport"
1500    );
1501    assert_eq!(pairs[0].open_range, open_brace..open_brace + 1);
1502    assert_eq!(pairs[0].close_range, close_brace..close_brace + 1);
1503
1504    // Viewport that extends past the closing brace should still find the pair
1505    // (the viewport may include trailing content after `}`).
1506    let pairs = snapshot.bracket_pairs_for_large_enclosing_blocks(&(near_end..source.len()));
1507    assert_eq!(
1508        pairs.len(),
1509        1,
1510        "should find the pair even when viewport extends past the block"
1511    );
1512    assert_eq!(pairs[0].open_range, open_brace..open_brace + 1);
1513    assert_eq!(pairs[0].close_range, close_brace..close_brace + 1);
1514}
1515
1516#[gpui::test]
1517fn test_bracket_pairs_for_large_block_viewport_before_block(cx: &mut App) {
1518    use crate::syntax_map::MAX_BYTES_TO_QUERY;
1519
1520    // Simulate a viewport that starts a few lines BEFORE `impl Foo {`,
1521    // e.g. the user sees the closing `}` of the previous item and then
1522    // `impl Foo {`.  `goto_first_child_for_byte(range.start)` descends
1523    // into the previous item, so the `impl_item` node is only reachable
1524    // as a sibling — and brackets live on its `declaration_list` child,
1525    // one level deeper.
1526    let preamble = "struct Bar {\n    field: i32,\n}\n\n";
1527    let mut source = String::from(preamble);
1528    let impl_start = source.len();
1529    source.push_str("impl Foo {\n");
1530    let fn_body = "        let x = 1;\n        let y = 2;\n        x + y\n";
1531    let mut fn_count = 0;
1532    while source.len() < impl_start + MAX_BYTES_TO_QUERY + 1000 {
1533        writeln!(
1534            source,
1535            "    fn func_{fn_count}() -> i32 {{\n{fn_body}    }}"
1536        )
1537        .unwrap();
1538        fn_count += 1;
1539    }
1540    source.push_str("}\n");
1541
1542    let buffer = cx.new(|cx| Buffer::local(source.clone(), cx).with_language(rust_lang(), cx));
1543    let snapshot = buffer.update(cx, |buffer, _cx| buffer.snapshot());
1544
1545    let open_brace = source[impl_start..].find('{').unwrap() + impl_start;
1546    let close_brace = source.rfind('}').unwrap();
1547
1548    // Viewport starts inside the preamble (before the impl block).
1549    let viewport_start = preamble.len().saturating_sub(10);
1550    let viewport_end = open_brace + 200;
1551    let pairs = snapshot.bracket_pairs_for_large_enclosing_blocks(&(viewport_start..viewport_end));
1552    assert_eq!(
1553        pairs.len(),
1554        1,
1555        "should find the impl bracket pair even when viewport starts before the block"
1556    );
1557    assert_eq!(pairs[0].open_range, open_brace..open_brace + 1);
1558    assert_eq!(pairs[0].close_range, close_brace..close_brace + 1);
1559}
1560
1561#[gpui::test]
1562fn test_enclosing_bracket_ranges_where_brackets_are_not_outermost_children(cx: &mut App) {
1563    let mut assert = |selection_text, bracket_pair_texts| {
1564        assert_bracket_pairs(
1565            selection_text,
1566            bracket_pair_texts,
1567            Arc::new(javascript_lang()),
1568            cx,
1569        )
1570    };
1571
1572    assert(
1573        indoc! {"
1574        for (const a in b)ˇ {
1575            // a comment that's longer than the for-loop header
1576        }"},
1577        vec![indoc! {"
1578        for «(»const a in b«)» {
1579            // a comment that's longer than the for-loop header
1580        }"}],
1581    );
1582
1583    // Regression test: even though the parent node of the parentheses (the for loop) does
1584    // intersect the given range, the parentheses themselves do not contain the range, so
1585    // they should not be returned. Only the curly braces contain the range.
1586    assert(
1587        indoc! {"
1588        for (const a in b) {ˇ
1589            // a comment that's longer than the for-loop header
1590        }"},
1591        vec![indoc! {"
1592        for (const a in b) «{»
1593            // a comment that's longer than the for-loop header
1594        «}»"}],
1595    );
1596}
1597
1598#[gpui::test]
1599fn test_range_for_syntax_ancestor(cx: &mut App) {
1600    cx.new(|cx| {
1601        let text = "fn a() { b(|c| {}) }";
1602        let buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
1603        let snapshot = buffer.snapshot();
1604
1605        assert_eq!(
1606            snapshot
1607                .syntax_ancestor(empty_range_at(text, "|"))
1608                .unwrap()
1609                .byte_range(),
1610            range_of(text, "|")
1611        );
1612        assert_eq!(
1613            snapshot
1614                .syntax_ancestor(range_of(text, "|"))
1615                .unwrap()
1616                .byte_range(),
1617            range_of(text, "|c|")
1618        );
1619        assert_eq!(
1620            snapshot
1621                .syntax_ancestor(range_of(text, "|c|"))
1622                .unwrap()
1623                .byte_range(),
1624            range_of(text, "|c| {}")
1625        );
1626        assert_eq!(
1627            snapshot
1628                .syntax_ancestor(range_of(text, "|c| {}"))
1629                .unwrap()
1630                .byte_range(),
1631            range_of(text, "(|c| {})")
1632        );
1633
1634        buffer
1635    });
1636
1637    fn empty_range_at(text: &str, part: &str) -> Range<usize> {
1638        let start = text.find(part).unwrap();
1639        start..start
1640    }
1641
1642    fn range_of(text: &str, part: &str) -> Range<usize> {
1643        let start = text.find(part).unwrap();
1644        start..start + part.len()
1645    }
1646}
1647
1648#[gpui::test]
1649fn test_autoindent_with_soft_tabs(cx: &mut App) {
1650    init_settings(cx, |_| {});
1651
1652    cx.new(|cx| {
1653        let text = "fn a() {}";
1654        let mut buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
1655
1656        buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
1657        assert_eq!(buffer.text(), "fn a() {\n    \n}");
1658
1659        buffer.edit(
1660            [(Point::new(1, 4)..Point::new(1, 4), "b()\n")],
1661            Some(AutoindentMode::EachLine),
1662            cx,
1663        );
1664        assert_eq!(buffer.text(), "fn a() {\n    b()\n    \n}");
1665
1666        // Create a field expression on a new line, causing that line
1667        // to be indented.
1668        buffer.edit(
1669            [(Point::new(2, 4)..Point::new(2, 4), ".c")],
1670            Some(AutoindentMode::EachLine),
1671            cx,
1672        );
1673        assert_eq!(buffer.text(), "fn a() {\n    b()\n        .c\n}");
1674
1675        // Remove the dot so that the line is no longer a field expression,
1676        // causing the line to be outdented.
1677        buffer.edit(
1678            [(Point::new(2, 8)..Point::new(2, 9), "")],
1679            Some(AutoindentMode::EachLine),
1680            cx,
1681        );
1682        assert_eq!(buffer.text(), "fn a() {\n    b()\n    c\n}");
1683
1684        buffer
1685    });
1686}
1687
1688#[gpui::test]
1689fn test_autoindent_with_hard_tabs(cx: &mut App) {
1690    init_settings(cx, |settings| {
1691        settings.defaults.hard_tabs = Some(true);
1692    });
1693
1694    cx.new(|cx| {
1695        let text = "fn a() {}";
1696        let mut buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
1697
1698        buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
1699        assert_eq!(buffer.text(), "fn a() {\n\t\n}");
1700
1701        buffer.edit(
1702            [(Point::new(1, 1)..Point::new(1, 1), "b()\n")],
1703            Some(AutoindentMode::EachLine),
1704            cx,
1705        );
1706        assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\n}");
1707
1708        // Create a field expression on a new line, causing that line
1709        // to be indented.
1710        buffer.edit(
1711            [(Point::new(2, 1)..Point::new(2, 1), ".c")],
1712            Some(AutoindentMode::EachLine),
1713            cx,
1714        );
1715        assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\t.c\n}");
1716
1717        // Remove the dot so that the line is no longer a field expression,
1718        // causing the line to be outdented.
1719        buffer.edit(
1720            [(Point::new(2, 2)..Point::new(2, 3), "")],
1721            Some(AutoindentMode::EachLine),
1722            cx,
1723        );
1724        assert_eq!(buffer.text(), "fn a() {\n\tb()\n\tc\n}");
1725
1726        buffer
1727    });
1728}
1729
1730#[gpui::test]
1731fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut App) {
1732    init_settings(cx, |_| {});
1733
1734    cx.new(|cx| {
1735        let mut buffer = Buffer::local(
1736            "
1737            fn a() {
1738            c;
1739            d;
1740            }
1741            "
1742            .unindent(),
1743            cx,
1744        )
1745        .with_language(rust_lang(), cx);
1746
1747        // Lines 2 and 3 don't match the indentation suggestion. When editing these lines,
1748        // their indentation is not adjusted.
1749        buffer.edit_via_marked_text(
1750            &"
1751            fn a() {
1752            c«()»;
1753            d«()»;
1754            }
1755            "
1756            .unindent(),
1757            Some(AutoindentMode::EachLine),
1758            cx,
1759        );
1760        assert_eq!(
1761            buffer.text(),
1762            "
1763            fn a() {
1764            c();
1765            d();
1766            }
1767            "
1768            .unindent()
1769        );
1770
1771        // When appending new content after these lines, the indentation is based on the
1772        // preceding lines' actual indentation.
1773        buffer.edit_via_marked_text(
1774            &"
1775            fn a() {
17761777            .f
1778            .g()»;
17791780            .f
1781            .g()»;
1782            }
1783            "
1784            .unindent(),
1785            Some(AutoindentMode::EachLine),
1786            cx,
1787        );
1788        assert_eq!(
1789            buffer.text(),
1790            "
1791            fn a() {
1792            c
1793                .f
1794                .g();
1795            d
1796                .f
1797                .g();
1798            }
1799            "
1800            .unindent()
1801        );
1802
1803        // Insert a newline after the open brace. It is auto-indented
1804        buffer.edit_via_marked_text(
1805            &"
1806            fn a() {«
1807            »
1808            c
1809                .f
1810                .g();
1811            d
1812                .f
1813                .g();
1814            }
1815            "
1816            .unindent(),
1817            Some(AutoindentMode::EachLine),
1818            cx,
1819        );
1820        assert_eq!(
1821            buffer.text(),
1822            "
1823            fn a() {
1824                ˇ
1825            c
1826                .f
1827                .g();
1828            d
1829                .f
1830                .g();
1831            }
1832            "
1833            .unindent()
1834            .replace("ˇ", "")
1835        );
1836
1837        // Manually outdent the line. It stays outdented.
1838        buffer.edit_via_marked_text(
1839            &"
1840            fn a() {
1841            «»
1842            c
1843                .f
1844                .g();
1845            d
1846                .f
1847                .g();
1848            }
1849            "
1850            .unindent(),
1851            Some(AutoindentMode::EachLine),
1852            cx,
1853        );
1854        assert_eq!(
1855            buffer.text(),
1856            "
1857            fn a() {
1858
1859            c
1860                .f
1861                .g();
1862            d
1863                .f
1864                .g();
1865            }
1866            "
1867            .unindent()
1868        );
1869
1870        buffer
1871    });
1872
1873    cx.new(|cx| {
1874        eprintln!("second buffer: {:?}", cx.entity_id());
1875
1876        let mut buffer = Buffer::local(
1877            "
1878            fn a() {
1879                b();
1880                |
1881            "
1882            .replace('|', "") // marker to preserve trailing whitespace
1883            .unindent(),
1884            cx,
1885        )
1886        .with_language(rust_lang(), cx);
1887
1888        // Insert a closing brace. It is outdented.
1889        buffer.edit_via_marked_text(
1890            &"
1891            fn a() {
1892                b();
1893                «}»
1894            "
1895            .unindent(),
1896            Some(AutoindentMode::EachLine),
1897            cx,
1898        );
1899        assert_eq!(
1900            buffer.text(),
1901            "
1902            fn a() {
1903                b();
1904            }
1905            "
1906            .unindent()
1907        );
1908
1909        // Manually edit the leading whitespace. The edit is preserved.
1910        buffer.edit_via_marked_text(
1911            &"
1912            fn a() {
1913                b();
1914            «    »}
1915            "
1916            .unindent(),
1917            Some(AutoindentMode::EachLine),
1918            cx,
1919        );
1920        assert_eq!(
1921            buffer.text(),
1922            "
1923            fn a() {
1924                b();
1925                }
1926            "
1927            .unindent()
1928        );
1929        buffer
1930    });
1931
1932    eprintln!("DONE");
1933}
1934
1935#[gpui::test]
1936fn test_autoindent_does_not_adjust_lines_within_newly_created_errors(cx: &mut App) {
1937    init_settings(cx, |_| {});
1938
1939    cx.new(|cx| {
1940        let mut buffer = Buffer::local(
1941            "
1942            fn a() {
1943                i
1944            }
1945            "
1946            .unindent(),
1947            cx,
1948        )
1949        .with_language(rust_lang(), cx);
1950
1951        // Regression test: line does not get outdented due to syntax error
1952        buffer.edit_via_marked_text(
1953            &"
1954            fn a() {
1955                i«f let Some(x) = y»
1956            }
1957            "
1958            .unindent(),
1959            Some(AutoindentMode::EachLine),
1960            cx,
1961        );
1962        assert_eq!(
1963            buffer.text(),
1964            "
1965            fn a() {
1966                if let Some(x) = y
1967            }
1968            "
1969            .unindent()
1970        );
1971
1972        buffer.edit_via_marked_text(
1973            &"
1974            fn a() {
1975                if let Some(x) = y« {»
1976            }
1977            "
1978            .unindent(),
1979            Some(AutoindentMode::EachLine),
1980            cx,
1981        );
1982        assert_eq!(
1983            buffer.text(),
1984            "
1985            fn a() {
1986                if let Some(x) = y {
1987            }
1988            "
1989            .unindent()
1990        );
1991
1992        buffer
1993    });
1994}
1995
1996#[gpui::test]
1997fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut App) {
1998    init_settings(cx, |_| {});
1999
2000    cx.new(|cx| {
2001        let mut buffer = Buffer::local(
2002            "
2003            fn a() {}
2004            "
2005            .unindent(),
2006            cx,
2007        )
2008        .with_language(rust_lang(), cx);
2009
2010        buffer.edit_via_marked_text(
2011            &"
2012            fn a(«
2013            b») {}
2014            "
2015            .unindent(),
2016            Some(AutoindentMode::EachLine),
2017            cx,
2018        );
2019        assert_eq!(
2020            buffer.text(),
2021            "
2022            fn a(
2023                b) {}
2024            "
2025            .unindent()
2026        );
2027
2028        // The indentation suggestion changed because `@end` node (a close paren)
2029        // is now at the beginning of the line.
2030        buffer.edit_via_marked_text(
2031            &"
2032            fn a(
2033                ˇ) {}
2034            "
2035            .unindent(),
2036            Some(AutoindentMode::EachLine),
2037            cx,
2038        );
2039        assert_eq!(
2040            buffer.text(),
2041            "
2042                fn a(
2043                ) {}
2044            "
2045            .unindent()
2046        );
2047
2048        buffer
2049    });
2050}
2051
2052#[gpui::test]
2053fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut App) {
2054    init_settings(cx, |_| {});
2055
2056    cx.new(|cx| {
2057        let text = "a\nb";
2058        let mut buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
2059        buffer.edit(
2060            [(0..1, "\n"), (2..3, "\n")],
2061            Some(AutoindentMode::EachLine),
2062            cx,
2063        );
2064        assert_eq!(buffer.text(), "\n\n\n");
2065        buffer
2066    });
2067}
2068
2069#[gpui::test]
2070fn test_autoindent_multi_line_insertion(cx: &mut App) {
2071    init_settings(cx, |_| {});
2072
2073    cx.new(|cx| {
2074        let text = "
2075            const a: usize = 1;
2076            fn b() {
2077                if c {
2078                    let d = 2;
2079                }
2080            }
2081        "
2082        .unindent();
2083
2084        let mut buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
2085        buffer.edit(
2086            [(Point::new(3, 0)..Point::new(3, 0), "e(\n    f()\n);\n")],
2087            Some(AutoindentMode::EachLine),
2088            cx,
2089        );
2090        assert_eq!(
2091            buffer.text(),
2092            "
2093                const a: usize = 1;
2094                fn b() {
2095                    if c {
2096                        e(
2097                            f()
2098                        );
2099                        let d = 2;
2100                    }
2101                }
2102            "
2103            .unindent()
2104        );
2105
2106        buffer
2107    });
2108}
2109
2110#[gpui::test]
2111fn test_autoindent_block_mode(cx: &mut App) {
2112    init_settings(cx, |_| {});
2113
2114    cx.new(|cx| {
2115        let text = r#"
2116            fn a() {
2117                b();
2118            }
2119        "#
2120        .unindent();
2121        let mut buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
2122
2123        // When this text was copied, both of the quotation marks were at the same
2124        // indent level, but the indentation of the first line was not included in
2125        // the copied text. This information is retained in the
2126        // 'original_indent_columns' vector.
2127        let original_indent_columns = vec![Some(4)];
2128        let inserted_text = r#"
2129            "
2130                  c
2131                    d
2132                      e
2133                "
2134        "#
2135        .unindent();
2136
2137        // Insert the block at column zero. The entire block is indented
2138        // so that the first line matches the previous line's indentation.
2139        buffer.edit(
2140            [(Point::new(2, 0)..Point::new(2, 0), inserted_text.clone())],
2141            Some(AutoindentMode::Block {
2142                original_indent_columns: original_indent_columns.clone(),
2143            }),
2144            cx,
2145        );
2146        assert_eq!(
2147            buffer.text(),
2148            r#"
2149            fn a() {
2150                b();
2151                "
2152                  c
2153                    d
2154                      e
2155                "
2156            }
2157            "#
2158            .unindent()
2159        );
2160
2161        // Grouping is disabled in tests, so we need 2 undos
2162        buffer.undo(cx); // Undo the auto-indent
2163        buffer.undo(cx); // Undo the original edit
2164
2165        // Insert the block at a deeper indent level. The entire block is outdented.
2166        buffer.edit([(Point::new(2, 0)..Point::new(2, 0), "        ")], None, cx);
2167        buffer.edit(
2168            [(Point::new(2, 8)..Point::new(2, 8), inserted_text)],
2169            Some(AutoindentMode::Block {
2170                original_indent_columns,
2171            }),
2172            cx,
2173        );
2174        assert_eq!(
2175            buffer.text(),
2176            r#"
2177            fn a() {
2178                b();
2179                "
2180                  c
2181                    d
2182                      e
2183                "
2184            }
2185            "#
2186            .unindent()
2187        );
2188
2189        buffer
2190    });
2191}
2192
2193#[gpui::test]
2194fn test_autoindent_block_mode_with_newline(cx: &mut App) {
2195    init_settings(cx, |_| {});
2196
2197    cx.new(|cx| {
2198        let text = r#"
2199            fn a() {
2200                b();
2201            }
2202        "#
2203        .unindent();
2204        let mut buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
2205
2206        // First line contains just '\n', it's indentation is stored in "original_indent_columns"
2207        let original_indent_columns = vec![Some(4)];
2208        let inserted_text = r#"
2209
2210                c();
2211                    d();
2212                        e();
2213        "#
2214        .unindent();
2215        buffer.edit(
2216            [(Point::new(2, 0)..Point::new(2, 0), inserted_text)],
2217            Some(AutoindentMode::Block {
2218                original_indent_columns,
2219            }),
2220            cx,
2221        );
2222
2223        // While making edit, we ignore first line as it only contains '\n'
2224        // hence second line indent is used to calculate delta
2225        assert_eq!(
2226            buffer.text(),
2227            r#"
2228            fn a() {
2229                b();
2230
2231                c();
2232                    d();
2233                        e();
2234            }
2235            "#
2236            .unindent()
2237        );
2238
2239        buffer
2240    });
2241}
2242
2243#[gpui::test]
2244fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut App) {
2245    init_settings(cx, |_| {});
2246
2247    cx.new(|cx| {
2248        let text = r#"
2249            fn a() {
2250                if b() {
2251
2252                }
2253            }
2254        "#
2255        .unindent();
2256        let mut buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
2257
2258        // The original indent columns are not known, so this text is
2259        // auto-indented in a block as if the first line was copied in
2260        // its entirety.
2261        let original_indent_columns = Vec::new();
2262        let inserted_text = "    c\n        .d()\n        .e();";
2263
2264        // Insert the block at column zero. The entire block is indented
2265        // so that the first line matches the previous line's indentation.
2266        buffer.edit(
2267            [(Point::new(2, 0)..Point::new(2, 0), inserted_text)],
2268            Some(AutoindentMode::Block {
2269                original_indent_columns,
2270            }),
2271            cx,
2272        );
2273        assert_eq!(
2274            buffer.text(),
2275            r#"
2276            fn a() {
2277                if b() {
2278                    c
2279                        .d()
2280                        .e();
2281                }
2282            }
2283            "#
2284            .unindent()
2285        );
2286
2287        // Grouping is disabled in tests, so we need 2 undos
2288        buffer.undo(cx); // Undo the auto-indent
2289        buffer.undo(cx); // Undo the original edit
2290
2291        // Insert the block at a deeper indent level. The entire block is outdented.
2292        buffer.edit(
2293            [(Point::new(2, 0)..Point::new(2, 0), " ".repeat(12))],
2294            None,
2295            cx,
2296        );
2297        buffer.edit(
2298            [(Point::new(2, 12)..Point::new(2, 12), inserted_text)],
2299            Some(AutoindentMode::Block {
2300                original_indent_columns: Vec::new(),
2301            }),
2302            cx,
2303        );
2304        assert_eq!(
2305            buffer.text(),
2306            r#"
2307            fn a() {
2308                if b() {
2309                    c
2310                        .d()
2311                        .e();
2312                }
2313            }
2314            "#
2315            .unindent()
2316        );
2317
2318        buffer
2319    });
2320}
2321
2322#[gpui::test]
2323fn test_autoindent_block_mode_multiple_adjacent_ranges(cx: &mut App) {
2324    init_settings(cx, |_| {});
2325
2326    cx.new(|cx| {
2327        let (text, ranges_to_replace) = marked_text_ranges(
2328            &"
2329            mod numbers {
2330                «fn one() {
2331                    1
2332                }
2333            »
2334                «fn two() {
2335                    2
2336                }
2337            »
2338                «fn three() {
2339                    3
2340                }
2341            »}
2342            "
2343            .unindent(),
2344            false,
2345        );
2346
2347        let mut buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
2348
2349        buffer.edit(
2350            [
2351                (ranges_to_replace[0].clone(), "fn one() {\n    101\n}\n"),
2352                (ranges_to_replace[1].clone(), "fn two() {\n    102\n}\n"),
2353                (ranges_to_replace[2].clone(), "fn three() {\n    103\n}\n"),
2354            ],
2355            Some(AutoindentMode::Block {
2356                original_indent_columns: vec![Some(0), Some(0), Some(0)],
2357            }),
2358            cx,
2359        );
2360
2361        assert_eq!(
2362            buffer.text(),
2363            "
2364            mod numbers {
2365                fn one() {
2366                    101
2367                }
2368
2369                fn two() {
2370                    102
2371                }
2372
2373                fn three() {
2374                    103
2375                }
2376            }
2377            "
2378            .unindent()
2379        );
2380
2381        buffer
2382    });
2383}
2384
2385#[gpui::test]
2386fn test_autoindent_language_without_indents_query(cx: &mut App) {
2387    init_settings(cx, |_| {});
2388
2389    cx.new(|cx| {
2390        let text = "
2391            * one
2392                - a
2393                - b
2394            * two
2395        "
2396        .unindent();
2397
2398        let mut buffer = Buffer::local(text, cx).with_language(
2399            Arc::new(Language::new(
2400                LanguageConfig {
2401                    name: "Markdown".into(),
2402                    auto_indent_using_last_non_empty_line: false,
2403                    ..Default::default()
2404                },
2405                Some(tree_sitter_json::LANGUAGE.into()),
2406            )),
2407            cx,
2408        );
2409        buffer.edit(
2410            [(Point::new(3, 0)..Point::new(3, 0), "\n")],
2411            Some(AutoindentMode::EachLine),
2412            cx,
2413        );
2414        assert_eq!(
2415            buffer.text(),
2416            "
2417            * one
2418                - a
2419                - b
2420
2421            * two
2422            "
2423            .unindent()
2424        );
2425        buffer
2426    });
2427}
2428
2429#[gpui::test]
2430fn test_autoindent_with_injected_languages(cx: &mut App) {
2431    init_settings(cx, |settings| {
2432        settings.languages.0.extend([
2433            (
2434                "HTML".into(),
2435                LanguageSettingsContent {
2436                    tab_size: Some(2.try_into().unwrap()),
2437                    ..Default::default()
2438                },
2439            ),
2440            (
2441                "JavaScript".into(),
2442                LanguageSettingsContent {
2443                    tab_size: Some(8.try_into().unwrap()),
2444                    ..Default::default()
2445                },
2446            ),
2447        ])
2448    });
2449
2450    let html_language = Arc::new(html_lang());
2451
2452    let javascript_language = Arc::new(javascript_lang());
2453
2454    let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
2455    language_registry.add(html_language.clone());
2456    language_registry.add(javascript_language);
2457
2458    cx.new(|cx| {
2459        let (text, ranges) = marked_text_ranges(
2460            &"
2461                <div>ˇ
2462                </div>
2463                <script>
2464                    init({ˇ
2465                    })
2466                </script>
2467                <span>ˇ
2468                </span>
2469            "
2470            .unindent(),
2471            false,
2472        );
2473
2474        let mut buffer = Buffer::local(text, cx);
2475        buffer.set_language_registry(language_registry);
2476        buffer.set_language(Some(html_language), cx);
2477        buffer.edit(
2478            ranges.into_iter().map(|range| (range, "\na")),
2479            Some(AutoindentMode::EachLine),
2480            cx,
2481        );
2482        assert_eq!(
2483            buffer.text(),
2484            "
2485                <div>
2486                  a
2487                </div>
2488                <script>
2489                    init({
2490                            a
2491                    })
2492                </script>
2493                <span>
2494                  a
2495                </span>
2496            "
2497            .unindent()
2498        );
2499        buffer
2500    });
2501}
2502
2503#[gpui::test]
2504fn test_autoindent_query_with_outdent_captures(cx: &mut App) {
2505    init_settings(cx, |settings| {
2506        settings.defaults.tab_size = Some(2.try_into().unwrap());
2507    });
2508
2509    cx.new(|cx| {
2510        let mut buffer = Buffer::local("", cx).with_language(Arc::new(ruby_lang()), cx);
2511
2512        let text = r#"
2513            class C
2514            def a(b, c)
2515            puts b
2516            puts c
2517            rescue
2518            puts "errored"
2519            exit 1
2520            end
2521            end
2522        "#
2523        .unindent();
2524
2525        buffer.edit([(0..0, text)], Some(AutoindentMode::EachLine), cx);
2526
2527        assert_eq!(
2528            buffer.text(),
2529            r#"
2530                class C
2531                  def a(b, c)
2532                    puts b
2533                    puts c
2534                  rescue
2535                    puts "errored"
2536                    exit 1
2537                  end
2538                end
2539            "#
2540            .unindent()
2541        );
2542
2543        buffer
2544    });
2545}
2546
2547#[gpui::test]
2548async fn test_async_autoindents_preserve_preview(cx: &mut TestAppContext) {
2549    cx.update(|cx| init_settings(cx, |_| {}));
2550
2551    // First we insert some newlines to request an auto-indent (asynchronously).
2552    // Then we request that a preview tab be preserved for the new version, even though it's edited.
2553    let buffer = cx.new(|cx| {
2554        let text = "fn a() {}";
2555        let mut buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
2556
2557        // This causes autoindent to be async.
2558        buffer.set_sync_parse_timeout(None);
2559
2560        buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
2561        buffer.refresh_preview();
2562
2563        // Synchronously, we haven't auto-indented and we're still preserving the preview.
2564        assert_eq!(buffer.text(), "fn a() {\n\n}");
2565        assert!(buffer.preserve_preview());
2566        buffer
2567    });
2568
2569    // Now let the autoindent finish
2570    cx.executor().run_until_parked();
2571
2572    // The auto-indent applied, but didn't dismiss our preview
2573    buffer.update(cx, |buffer, cx| {
2574        assert_eq!(buffer.text(), "fn a() {\n    \n}");
2575        assert!(buffer.preserve_preview());
2576
2577        // Edit inserting another line. It will autoindent async.
2578        // Then refresh the preview version.
2579        buffer.edit(
2580            [(Point::new(1, 4)..Point::new(1, 4), "\n")],
2581            Some(AutoindentMode::EachLine),
2582            cx,
2583        );
2584        buffer.refresh_preview();
2585        assert_eq!(buffer.text(), "fn a() {\n    \n\n}");
2586        assert!(buffer.preserve_preview());
2587
2588        // Then perform another edit, this time without refreshing the preview version.
2589        buffer.edit([(Point::new(1, 4)..Point::new(1, 4), "x")], None, cx);
2590        // This causes the preview to not be preserved.
2591        assert!(!buffer.preserve_preview());
2592    });
2593
2594    // Let the async autoindent from the first edit finish.
2595    cx.executor().run_until_parked();
2596
2597    // The autoindent applies, but it shouldn't restore the preview status because we had an edit in the meantime.
2598    buffer.update(cx, |buffer, _| {
2599        assert_eq!(buffer.text(), "fn a() {\n    x\n    \n}");
2600        assert!(!buffer.preserve_preview());
2601    });
2602}
2603
2604#[gpui::test]
2605fn test_insert_empty_line(cx: &mut App) {
2606    init_settings(cx, |_| {});
2607
2608    // Insert empty line at the beginning, requesting an empty line above
2609    cx.new(|cx| {
2610        let mut buffer = Buffer::local("abc\ndef\nghi", cx);
2611        let point = buffer.insert_empty_line(Point::new(0, 0), true, false, cx);
2612        assert_eq!(buffer.text(), "\nabc\ndef\nghi");
2613        assert_eq!(point, Point::new(0, 0));
2614        buffer
2615    });
2616
2617    // Insert empty line at the beginning, requesting an empty line above and below
2618    cx.new(|cx| {
2619        let mut buffer = Buffer::local("abc\ndef\nghi", cx);
2620        let point = buffer.insert_empty_line(Point::new(0, 0), true, true, cx);
2621        assert_eq!(buffer.text(), "\n\nabc\ndef\nghi");
2622        assert_eq!(point, Point::new(0, 0));
2623        buffer
2624    });
2625
2626    // Insert empty line at the start of a line, requesting empty lines above and below
2627    cx.new(|cx| {
2628        let mut buffer = Buffer::local("abc\ndef\nghi", cx);
2629        let point = buffer.insert_empty_line(Point::new(2, 0), true, true, cx);
2630        assert_eq!(buffer.text(), "abc\ndef\n\n\n\nghi");
2631        assert_eq!(point, Point::new(3, 0));
2632        buffer
2633    });
2634
2635    // Insert empty line in the middle of a line, requesting empty lines above and below
2636    cx.new(|cx| {
2637        let mut buffer = Buffer::local("abc\ndefghi\njkl", cx);
2638        let point = buffer.insert_empty_line(Point::new(1, 3), true, true, cx);
2639        assert_eq!(buffer.text(), "abc\ndef\n\n\n\nghi\njkl");
2640        assert_eq!(point, Point::new(3, 0));
2641        buffer
2642    });
2643
2644    // Insert empty line in the middle of a line, requesting empty line above only
2645    cx.new(|cx| {
2646        let mut buffer = Buffer::local("abc\ndefghi\njkl", cx);
2647        let point = buffer.insert_empty_line(Point::new(1, 3), true, false, cx);
2648        assert_eq!(buffer.text(), "abc\ndef\n\n\nghi\njkl");
2649        assert_eq!(point, Point::new(3, 0));
2650        buffer
2651    });
2652
2653    // Insert empty line in the middle of a line, requesting empty line below only
2654    cx.new(|cx| {
2655        let mut buffer = Buffer::local("abc\ndefghi\njkl", cx);
2656        let point = buffer.insert_empty_line(Point::new(1, 3), false, true, cx);
2657        assert_eq!(buffer.text(), "abc\ndef\n\n\nghi\njkl");
2658        assert_eq!(point, Point::new(2, 0));
2659        buffer
2660    });
2661
2662    // Insert empty line at the end, requesting empty lines above and below
2663    cx.new(|cx| {
2664        let mut buffer = Buffer::local("abc\ndef\nghi", cx);
2665        let point = buffer.insert_empty_line(Point::new(2, 3), true, true, cx);
2666        assert_eq!(buffer.text(), "abc\ndef\nghi\n\n\n");
2667        assert_eq!(point, Point::new(4, 0));
2668        buffer
2669    });
2670
2671    // Insert empty line at the end, requesting empty line above only
2672    cx.new(|cx| {
2673        let mut buffer = Buffer::local("abc\ndef\nghi", cx);
2674        let point = buffer.insert_empty_line(Point::new(2, 3), true, false, cx);
2675        assert_eq!(buffer.text(), "abc\ndef\nghi\n\n");
2676        assert_eq!(point, Point::new(4, 0));
2677        buffer
2678    });
2679
2680    // Insert empty line at the end, requesting empty line below only
2681    cx.new(|cx| {
2682        let mut buffer = Buffer::local("abc\ndef\nghi", cx);
2683        let point = buffer.insert_empty_line(Point::new(2, 3), false, true, cx);
2684        assert_eq!(buffer.text(), "abc\ndef\nghi\n\n");
2685        assert_eq!(point, Point::new(3, 0));
2686        buffer
2687    });
2688}
2689
2690#[gpui::test]
2691fn test_language_scope_at_with_javascript(cx: &mut App) {
2692    init_settings(cx, |_| {});
2693
2694    cx.new(|cx| {
2695        let language = Language::new(
2696            LanguageConfig {
2697                name: "JavaScript".into(),
2698                line_comments: vec!["// ".into()],
2699                block_comment: Some(BlockCommentConfig {
2700                    start: "/*".into(),
2701                    end: "*/".into(),
2702                    prefix: "* ".into(),
2703                    tab_size: 1,
2704                }),
2705                brackets: BracketPairConfig {
2706                    pairs: vec![
2707                        BracketPair {
2708                            start: "{".into(),
2709                            end: "}".into(),
2710                            close: true,
2711                            surround: true,
2712                            newline: false,
2713                        },
2714                        BracketPair {
2715                            start: "'".into(),
2716                            end: "'".into(),
2717                            close: true,
2718                            surround: true,
2719                            newline: false,
2720                        },
2721                    ],
2722                    disabled_scopes_by_bracket_ix: vec![
2723                        Vec::new(),                              //
2724                        vec!["string".into(), "comment".into()], // single quotes disabled
2725                    ],
2726                },
2727                overrides: [(
2728                    "element".into(),
2729                    LanguageConfigOverride {
2730                        line_comments: Override::Remove { remove: true },
2731                        block_comment: Override::Set(BlockCommentConfig {
2732                            start: "{/*".into(),
2733                            prefix: "".into(),
2734                            end: "*/}".into(),
2735                            tab_size: 0,
2736                        }),
2737                        ..Default::default()
2738                    },
2739                )]
2740                .into_iter()
2741                .collect(),
2742                ..Default::default()
2743            },
2744            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
2745        )
2746        .with_override_query(
2747            r#"
2748                (jsx_element) @element
2749                (string) @string
2750                (comment) @comment.inclusive
2751                [
2752                    (jsx_opening_element)
2753                    (jsx_closing_element)
2754                    (jsx_expression)
2755                ] @default
2756            "#,
2757        )
2758        .unwrap();
2759
2760        let text = r#"
2761            a["b"] = <C d="e">
2762                <F></F>
2763                { g() }
2764            </C>; // a comment
2765        "#
2766        .unindent();
2767
2768        let buffer = Buffer::local(&text, cx).with_language(Arc::new(language), cx);
2769        let snapshot = buffer.snapshot();
2770
2771        let config = snapshot.language_scope_at(0).unwrap();
2772        assert_eq!(config.line_comment_prefixes(), &[Arc::from("// ")]);
2773        assert_eq!(
2774            config.block_comment(),
2775            Some(&BlockCommentConfig {
2776                start: "/*".into(),
2777                prefix: "* ".into(),
2778                end: "*/".into(),
2779                tab_size: 1,
2780            })
2781        );
2782
2783        // Both bracket pairs are enabled
2784        assert_eq!(
2785            config.brackets().map(|e| e.1).collect::<Vec<_>>(),
2786            &[true, true]
2787        );
2788
2789        let comment_config = snapshot
2790            .language_scope_at(text.find("comment").unwrap() + "comment".len())
2791            .unwrap();
2792        assert_eq!(
2793            comment_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
2794            &[true, false]
2795        );
2796
2797        let string_config = snapshot
2798            .language_scope_at(text.find("b\"").unwrap())
2799            .unwrap();
2800        assert_eq!(string_config.line_comment_prefixes(), &[Arc::from("// ")]);
2801        assert_eq!(
2802            string_config.block_comment(),
2803            Some(&BlockCommentConfig {
2804                start: "/*".into(),
2805                prefix: "* ".into(),
2806                end: "*/".into(),
2807                tab_size: 1,
2808            })
2809        );
2810        // Second bracket pair is disabled
2811        assert_eq!(
2812            string_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
2813            &[true, false]
2814        );
2815
2816        // In between JSX tags: use the `element` override.
2817        let element_config = snapshot
2818            .language_scope_at(text.find("<F>").unwrap())
2819            .unwrap();
2820        // TODO nested blocks after newlines are captured with all whitespaces
2821        // https://github.com/tree-sitter/tree-sitter-typescript/issues/306
2822        // assert_eq!(element_config.line_comment_prefixes(), &[]);
2823        // assert_eq!(
2824        //     element_config.block_comment_delimiters(),
2825        //     Some((&"{/*".into(), &"*/}".into()))
2826        // );
2827        assert_eq!(
2828            element_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
2829            &[true, true]
2830        );
2831
2832        // Within a JSX tag: use the default config.
2833        let tag_config = snapshot
2834            .language_scope_at(text.find(" d=").unwrap() + 1)
2835            .unwrap();
2836        assert_eq!(tag_config.line_comment_prefixes(), &[Arc::from("// ")]);
2837        assert_eq!(
2838            tag_config.block_comment(),
2839            Some(&BlockCommentConfig {
2840                start: "/*".into(),
2841                prefix: "* ".into(),
2842                end: "*/".into(),
2843                tab_size: 1,
2844            })
2845        );
2846        assert_eq!(
2847            tag_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
2848            &[true, true]
2849        );
2850
2851        // In a JSX expression: use the default config.
2852        let expression_in_element_config = snapshot
2853            .language_scope_at(text.find('{').unwrap() + 1)
2854            .unwrap();
2855        assert_eq!(
2856            expression_in_element_config.line_comment_prefixes(),
2857            &[Arc::from("// ")]
2858        );
2859        assert_eq!(
2860            expression_in_element_config.block_comment(),
2861            Some(&BlockCommentConfig {
2862                start: "/*".into(),
2863                prefix: "* ".into(),
2864                end: "*/".into(),
2865                tab_size: 1,
2866            })
2867        );
2868        assert_eq!(
2869            expression_in_element_config
2870                .brackets()
2871                .map(|e| e.1)
2872                .collect::<Vec<_>>(),
2873            &[true, true]
2874        );
2875
2876        buffer
2877    });
2878}
2879
2880#[gpui::test]
2881fn test_language_scope_at_with_rust(cx: &mut App) {
2882    init_settings(cx, |_| {});
2883
2884    cx.new(|cx| {
2885        let language = Language::new(
2886            LanguageConfig {
2887                name: "Rust".into(),
2888                brackets: BracketPairConfig {
2889                    pairs: vec![
2890                        BracketPair {
2891                            start: "{".into(),
2892                            end: "}".into(),
2893                            close: true,
2894                            surround: true,
2895                            newline: false,
2896                        },
2897                        BracketPair {
2898                            start: "'".into(),
2899                            end: "'".into(),
2900                            close: true,
2901                            surround: true,
2902                            newline: false,
2903                        },
2904                    ],
2905                    disabled_scopes_by_bracket_ix: vec![
2906                        Vec::new(), //
2907                        vec!["string".into()],
2908                    ],
2909                },
2910                ..Default::default()
2911            },
2912            Some(tree_sitter_rust::LANGUAGE.into()),
2913        )
2914        .with_override_query(
2915            r#"
2916                (string_literal) @string
2917            "#,
2918        )
2919        .unwrap();
2920
2921        let text = r#"
2922            const S: &'static str = "hello";
2923        "#
2924        .unindent();
2925
2926        let buffer = Buffer::local(text.clone(), cx).with_language(Arc::new(language), cx);
2927        let snapshot = buffer.snapshot();
2928
2929        // By default, all brackets are enabled
2930        let config = snapshot.language_scope_at(0).unwrap();
2931        assert_eq!(
2932            config.brackets().map(|e| e.1).collect::<Vec<_>>(),
2933            &[true, true]
2934        );
2935
2936        // Within a string, the quotation brackets are disabled.
2937        let string_config = snapshot
2938            .language_scope_at(text.find("ello").unwrap())
2939            .unwrap();
2940        assert_eq!(
2941            string_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
2942            &[true, false]
2943        );
2944
2945        buffer
2946    });
2947}
2948
2949#[gpui::test]
2950fn test_language_scope_at_with_combined_injections(cx: &mut App) {
2951    init_settings(cx, |_| {});
2952
2953    cx.new(|cx| {
2954        let text = r#"
2955            <ol>
2956            <% people.each do |person| %>
2957                <li>
2958                    <%= person.name %>
2959                </li>
2960            <% end %>
2961            </ol>
2962        "#
2963        .unindent();
2964
2965        let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
2966        language_registry.add(Arc::new(ruby_lang()));
2967        language_registry.add(Arc::new(html_lang()));
2968        language_registry.add(Arc::new(erb_lang()));
2969
2970        let mut buffer = Buffer::local(text, cx);
2971        buffer.set_language_registry(language_registry.clone());
2972        let language = language_registry
2973            .language_for_name("HTML+ERB")
2974            .now_or_never()
2975            .and_then(Result::ok);
2976        buffer.set_language(language, cx);
2977
2978        let snapshot = buffer.snapshot();
2979        let html_config = snapshot.language_scope_at(Point::new(2, 4)).unwrap();
2980        assert_eq!(html_config.line_comment_prefixes(), &[]);
2981        assert_eq!(
2982            html_config.block_comment(),
2983            Some(&BlockCommentConfig {
2984                start: "<!--".into(),
2985                end: "-->".into(),
2986                prefix: "".into(),
2987                tab_size: 0,
2988            })
2989        );
2990
2991        let ruby_config = snapshot.language_scope_at(Point::new(3, 12)).unwrap();
2992        assert_eq!(ruby_config.line_comment_prefixes(), &[Arc::from("# ")]);
2993        assert_eq!(ruby_config.block_comment(), None);
2994
2995        buffer
2996    });
2997}
2998
2999#[gpui::test]
3000fn test_language_at_with_hidden_languages(cx: &mut App) {
3001    init_settings(cx, |_| {});
3002
3003    cx.new(|cx| {
3004        let text = r#"
3005            this is an *emphasized* word.
3006        "#
3007        .unindent();
3008
3009        let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
3010        language_registry.add(markdown_lang());
3011        language_registry.add(Arc::new(markdown_inline_lang()));
3012
3013        let mut buffer = Buffer::local(text, cx);
3014        buffer.set_language_registry(language_registry.clone());
3015        buffer.set_language(
3016            language_registry
3017                .language_for_name("Markdown")
3018                .now_or_never()
3019                .unwrap()
3020                .ok(),
3021            cx,
3022        );
3023
3024        let snapshot = buffer.snapshot();
3025
3026        for point in [Point::new(0, 4), Point::new(0, 16)] {
3027            let config = snapshot.language_scope_at(point).unwrap();
3028            assert_eq!(config.language_name(), "Markdown");
3029
3030            let language = snapshot.language_at(point).unwrap();
3031            assert_eq!(language.name().as_ref(), "Markdown");
3032        }
3033
3034        buffer
3035    });
3036}
3037
3038#[gpui::test]
3039fn test_language_at_for_markdown_code_block(cx: &mut App) {
3040    init_settings(cx, |_| {});
3041
3042    cx.new(|cx| {
3043        let text = r#"
3044            ```rs
3045            let a = 2;
3046            // let b = 3;
3047            ```
3048        "#
3049        .unindent();
3050
3051        let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
3052        language_registry.add(markdown_lang());
3053        language_registry.add(Arc::new(markdown_inline_lang()));
3054        language_registry.add(rust_lang());
3055
3056        let mut buffer = Buffer::local(text, cx);
3057        buffer.set_language_registry(language_registry.clone());
3058        buffer.set_language(
3059            language_registry
3060                .language_for_name("Markdown")
3061                .now_or_never()
3062                .unwrap()
3063                .ok(),
3064            cx,
3065        );
3066
3067        let snapshot = buffer.snapshot();
3068
3069        // Test points in the code line
3070        for point in [Point::new(1, 4), Point::new(1, 6)] {
3071            let config = snapshot.language_scope_at(point).unwrap();
3072            assert_eq!(config.language_name(), "Rust");
3073
3074            let language = snapshot.language_at(point).unwrap();
3075            assert_eq!(language.name().as_ref(), "Rust");
3076        }
3077
3078        // Test points in the comment line to verify it's still detected as Rust
3079        for point in [Point::new(2, 4), Point::new(2, 6)] {
3080            let config = snapshot.language_scope_at(point).unwrap();
3081            assert_eq!(config.language_name(), "Rust");
3082
3083            let language = snapshot.language_at(point).unwrap();
3084            assert_eq!(language.name().as_ref(), "Rust");
3085        }
3086
3087        buffer
3088    });
3089}
3090
3091#[gpui::test]
3092fn test_syntax_layer_at_for_combined_injections(cx: &mut App) {
3093    init_settings(cx, |_| {});
3094
3095    cx.new(|cx| {
3096        // ERB template with HTML and Ruby content
3097        let text = r#"
3098<div>Hello</div>
3099<%= link_to "Click", url %>
3100<p>World</p>
3101        "#
3102        .unindent();
3103
3104        let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
3105        language_registry.add(Arc::new(erb_lang()));
3106        language_registry.add(Arc::new(html_lang()));
3107        language_registry.add(Arc::new(ruby_lang()));
3108
3109        let mut buffer = Buffer::local(text, cx);
3110        buffer.set_language_registry(language_registry.clone());
3111        let language = language_registry
3112            .language_for_name("HTML+ERB")
3113            .now_or_never()
3114            .and_then(Result::ok);
3115        buffer.set_language(language, cx);
3116
3117        let snapshot = buffer.snapshot();
3118
3119        // Test language_at for HTML content (line 0: "<div>Hello</div>")
3120        let html_point = Point::new(0, 4);
3121        let language = snapshot.language_at(html_point).unwrap();
3122        assert_eq!(
3123            language.name().as_ref(),
3124            "HTML",
3125            "Expected HTML at {:?}, got {}",
3126            html_point,
3127            language.name()
3128        );
3129
3130        // Test language_at for Ruby code (line 1: "<%= link_to ... %>")
3131        let ruby_point = Point::new(1, 6);
3132        let language = snapshot.language_at(ruby_point).unwrap();
3133        assert_eq!(
3134            language.name().as_ref(),
3135            "Ruby",
3136            "Expected Ruby at {:?}, got {}",
3137            ruby_point,
3138            language.name()
3139        );
3140
3141        // Test language_at for HTML after Ruby (line 2: "<p>World</p>")
3142        let html_after_ruby = Point::new(2, 2);
3143        let language = snapshot.language_at(html_after_ruby).unwrap();
3144        assert_eq!(
3145            language.name().as_ref(),
3146            "HTML",
3147            "Expected HTML at {:?}, got {}",
3148            html_after_ruby,
3149            language.name()
3150        );
3151
3152        buffer
3153    });
3154}
3155
3156#[gpui::test]
3157fn test_languages_at_for_combined_injections(cx: &mut App) {
3158    init_settings(cx, |_| {});
3159
3160    cx.new(|cx| {
3161        // ERB template with HTML and Ruby content
3162        let text = r#"
3163<div>Hello</div>
3164<%= yield %>
3165<p>World</p>
3166        "#
3167        .unindent();
3168
3169        let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
3170        language_registry.add(Arc::new(erb_lang()));
3171        language_registry.add(Arc::new(html_lang()));
3172        language_registry.add(Arc::new(ruby_lang()));
3173
3174        let mut buffer = Buffer::local(text, cx);
3175        buffer.set_language_registry(language_registry.clone());
3176        buffer.set_language(
3177            language_registry
3178                .language_for_name("HTML+ERB")
3179                .now_or_never()
3180                .unwrap()
3181                .ok(),
3182            cx,
3183        );
3184
3185        // Test languages_at for HTML content - should NOT include Ruby
3186        let html_point = Point::new(0, 4);
3187        let languages = buffer.languages_at(html_point);
3188        let language_names: Vec<_> = languages.iter().map(|language| language.name()).collect();
3189        assert!(
3190            language_names
3191                .iter()
3192                .any(|language_name| language_name.as_ref() == "HTML"),
3193            "Expected HTML in languages at {:?}, got {:?}",
3194            html_point,
3195            language_names
3196        );
3197        assert!(
3198            !language_names
3199                .iter()
3200                .any(|language_name| language_name.as_ref() == "Ruby"),
3201            "Did not expect Ruby in languages at {:?}, got {:?}",
3202            html_point,
3203            language_names
3204        );
3205
3206        // Test languages_at for Ruby code - should NOT include HTML
3207        let ruby_point = Point::new(1, 6);
3208        let languages = buffer.languages_at(ruby_point);
3209        let language_names: Vec<_> = languages.iter().map(|language| language.name()).collect();
3210        assert!(
3211            language_names
3212                .iter()
3213                .any(|language_name| language_name.as_ref() == "Ruby"),
3214            "Expected Ruby in languages at {:?}, got {:?}",
3215            ruby_point,
3216            language_names
3217        );
3218        assert!(
3219            !language_names
3220                .iter()
3221                .any(|language_name| language_name.as_ref() == "HTML"),
3222            "Did not expect HTML in languages at {:?}, got {:?}",
3223            ruby_point,
3224            language_names
3225        );
3226
3227        buffer
3228    });
3229}
3230
3231#[gpui::test]
3232fn test_serialization(cx: &mut gpui::App) {
3233    let mut now = Instant::now();
3234
3235    let buffer1 = cx.new(|cx| {
3236        let mut buffer = Buffer::local("abc", cx);
3237        buffer.edit([(3..3, "D")], None, cx);
3238
3239        now += Duration::from_secs(1);
3240        buffer.start_transaction_at(now);
3241        buffer.edit([(4..4, "E")], None, cx);
3242        buffer.end_transaction_at(now, cx);
3243        assert_eq!(buffer.text(), "abcDE");
3244
3245        buffer.undo(cx);
3246        assert_eq!(buffer.text(), "abcD");
3247
3248        buffer.edit([(4..4, "F")], None, cx);
3249        assert_eq!(buffer.text(), "abcDF");
3250        buffer
3251    });
3252    assert_eq!(buffer1.read(cx).text(), "abcDF");
3253
3254    let state = buffer1.read(cx).to_proto(cx);
3255    let ops = cx
3256        .foreground_executor()
3257        .block_on(buffer1.read(cx).serialize_ops(None, cx));
3258    let buffer2 = cx.new(|cx| {
3259        let mut buffer =
3260            Buffer::from_proto(ReplicaId::new(1), Capability::ReadWrite, state, None).unwrap();
3261        buffer.apply_ops(
3262            ops.into_iter()
3263                .map(|op| proto::deserialize_operation(op).unwrap()),
3264            cx,
3265        );
3266        buffer
3267    });
3268    assert_eq!(buffer2.read(cx).text(), "abcDF");
3269}
3270
3271#[gpui::test]
3272fn test_branch_and_merge(cx: &mut TestAppContext) {
3273    cx.update(|cx| init_settings(cx, |_| {}));
3274
3275    let base = cx.new(|cx| Buffer::local("one\ntwo\nthree\n", cx));
3276
3277    // Create a remote replica of the base buffer.
3278    let base_replica = cx.new(|cx| {
3279        Buffer::from_proto(
3280            ReplicaId::new(1),
3281            Capability::ReadWrite,
3282            base.read(cx).to_proto(cx),
3283            None,
3284        )
3285        .unwrap()
3286    });
3287    base.update(cx, |_buffer, cx| {
3288        cx.subscribe(&base_replica, |this, _, event, cx| {
3289            if let BufferEvent::Operation {
3290                operation,
3291                is_local: true,
3292            } = event
3293            {
3294                this.apply_ops([operation.clone()], cx);
3295            }
3296        })
3297        .detach();
3298    });
3299
3300    // Create a branch, which initially has the same state as the base buffer.
3301    let branch = base.update(cx, |buffer, cx| buffer.branch(cx));
3302    branch.read_with(cx, |buffer, _| {
3303        assert_eq!(buffer.text(), "one\ntwo\nthree\n");
3304    });
3305
3306    // Edits to the branch are not applied to the base.
3307    branch.update(cx, |buffer, cx| {
3308        buffer.edit(
3309            [
3310                (Point::new(1, 0)..Point::new(1, 0), "1.5\n"),
3311                (Point::new(2, 0)..Point::new(2, 5), "THREE"),
3312            ],
3313            None,
3314            cx,
3315        )
3316    });
3317    branch.read_with(cx, |buffer, cx| {
3318        assert_eq!(base.read(cx).text(), "one\ntwo\nthree\n");
3319        assert_eq!(buffer.text(), "one\n1.5\ntwo\nTHREE\n");
3320    });
3321
3322    // Convert from branch buffer ranges to the corresponding ranges in the
3323    // base buffer.
3324    branch.read_with(cx, |buffer, cx| {
3325        assert_eq!(
3326            buffer.range_to_version(4..7, &base.read(cx).version()),
3327            4..4
3328        );
3329        assert_eq!(
3330            buffer.range_to_version(2..9, &base.read(cx).version()),
3331            2..5
3332        );
3333    });
3334
3335    // Edits to the base are applied to the branch.
3336    base.update(cx, |buffer, cx| {
3337        buffer.edit([(Point::new(0, 0)..Point::new(0, 0), "ZERO\n")], None, cx)
3338    });
3339    branch.read_with(cx, |buffer, cx| {
3340        assert_eq!(base.read(cx).text(), "ZERO\none\ntwo\nthree\n");
3341        assert_eq!(buffer.text(), "ZERO\none\n1.5\ntwo\nTHREE\n");
3342    });
3343
3344    // Edits to any replica of the base are applied to the branch.
3345    base_replica.update(cx, |buffer, cx| {
3346        buffer.edit([(Point::new(2, 0)..Point::new(2, 0), "2.5\n")], None, cx)
3347    });
3348    branch.read_with(cx, |buffer, cx| {
3349        assert_eq!(base.read(cx).text(), "ZERO\none\ntwo\n2.5\nthree\n");
3350        assert_eq!(buffer.text(), "ZERO\none\n1.5\ntwo\n2.5\nTHREE\n");
3351    });
3352
3353    // Merging the branch applies all of its changes to the base.
3354    branch.update(cx, |buffer, cx| {
3355        buffer.merge_into_base(Vec::new(), cx);
3356    });
3357
3358    branch.update(cx, |buffer, cx| {
3359        assert_eq!(base.read(cx).text(), "ZERO\none\n1.5\ntwo\n2.5\nTHREE\n");
3360        assert_eq!(buffer.text(), "ZERO\none\n1.5\ntwo\n2.5\nTHREE\n");
3361    });
3362}
3363
3364#[gpui::test]
3365fn test_merge_into_base(cx: &mut TestAppContext) {
3366    cx.update(|cx| init_settings(cx, |_| {}));
3367
3368    let base = cx.new(|cx| Buffer::local("abcdefghijk", cx));
3369    let branch = base.update(cx, |buffer, cx| buffer.branch(cx));
3370
3371    // Make 3 edits, merge one into the base.
3372    branch.update(cx, |branch, cx| {
3373        branch.edit([(0..3, "ABC"), (7..9, "HI"), (11..11, "LMN")], None, cx);
3374        branch.merge_into_base(vec![5..8], cx);
3375    });
3376
3377    branch.read_with(cx, |branch, _| assert_eq!(branch.text(), "ABCdefgHIjkLMN"));
3378    base.read_with(cx, |base, _| assert_eq!(base.text(), "abcdefgHIjk"));
3379
3380    // Undo the one already-merged edit. Merge that into the base.
3381    branch.update(cx, |branch, cx| {
3382        branch.edit([(7..9, "hi")], None, cx);
3383        branch.merge_into_base(vec![5..8], cx);
3384    });
3385    base.read_with(cx, |base, _| assert_eq!(base.text(), "abcdefghijk"));
3386
3387    // Merge an insertion into the base.
3388    branch.update(cx, |branch, cx| {
3389        branch.merge_into_base(vec![11..11], cx);
3390    });
3391
3392    branch.read_with(cx, |branch, _| assert_eq!(branch.text(), "ABCdefghijkLMN"));
3393    base.read_with(cx, |base, _| assert_eq!(base.text(), "abcdefghijkLMN"));
3394
3395    // Deleted the inserted text and merge that into the base.
3396    branch.update(cx, |branch, cx| {
3397        branch.edit([(11..14, "")], None, cx);
3398        branch.merge_into_base(vec![10..11], cx);
3399    });
3400
3401    base.read_with(cx, |base, _| assert_eq!(base.text(), "abcdefghijk"));
3402}
3403
3404#[gpui::test]
3405fn test_undo_after_merge_into_base(cx: &mut TestAppContext) {
3406    cx.update(|cx| init_settings(cx, |_| {}));
3407
3408    let base = cx.new(|cx| Buffer::local("abcdefghijk", cx));
3409    let branch = base.update(cx, |buffer, cx| buffer.branch(cx));
3410
3411    // Make 2 edits, merge one into the base.
3412    branch.update(cx, |branch, cx| {
3413        branch.edit([(0..3, "ABC"), (7..9, "HI")], None, cx);
3414        branch.merge_into_base(vec![7..7], cx);
3415    });
3416    base.read_with(cx, |base, _| assert_eq!(base.text(), "abcdefgHIjk"));
3417    branch.read_with(cx, |branch, _| assert_eq!(branch.text(), "ABCdefgHIjk"));
3418
3419    // Undo the merge in the base buffer.
3420    base.update(cx, |base, cx| {
3421        base.undo(cx);
3422    });
3423    base.read_with(cx, |base, _| assert_eq!(base.text(), "abcdefghijk"));
3424    branch.read_with(cx, |branch, _| assert_eq!(branch.text(), "ABCdefgHIjk"));
3425
3426    // Merge that operation into the base again.
3427    branch.update(cx, |branch, cx| {
3428        branch.merge_into_base(vec![7..7], cx);
3429    });
3430    base.read_with(cx, |base, _| assert_eq!(base.text(), "abcdefgHIjk"));
3431    branch.read_with(cx, |branch, _| assert_eq!(branch.text(), "ABCdefgHIjk"));
3432}
3433
3434#[gpui::test]
3435async fn test_preview_edits(cx: &mut TestAppContext) {
3436    cx.update(|cx| {
3437        init_settings(cx, |_| {});
3438        theme_settings::init(theme::LoadThemes::JustBase, cx);
3439    });
3440
3441    let insertion_style = HighlightStyle {
3442        background_color: Some(cx.read(|cx| cx.theme().status().created_background)),
3443        ..Default::default()
3444    };
3445    let deletion_style = HighlightStyle {
3446        background_color: Some(cx.read(|cx| cx.theme().status().deleted_background)),
3447        ..Default::default()
3448    };
3449
3450    // no edits
3451    assert_preview_edits(
3452        indoc! {"
3453        fn test_empty() -> bool {
3454            false
3455        }"
3456        },
3457        vec![],
3458        true,
3459        cx,
3460        |hl| {
3461            assert!(hl.text.is_empty());
3462            assert!(hl.highlights.is_empty());
3463        },
3464    )
3465    .await;
3466
3467    // only insertions
3468    assert_preview_edits(
3469        indoc! {"
3470        fn calculate_area(: f64) -> f64 {
3471            std::f64::consts::PI * .powi(2)
3472        }"
3473        },
3474        vec![
3475            (Point::new(0, 18)..Point::new(0, 18), "radius"),
3476            (Point::new(1, 27)..Point::new(1, 27), "radius"),
3477        ],
3478        true,
3479        cx,
3480        |hl| {
3481            assert_eq!(
3482                hl.text,
3483                indoc! {"
3484                fn calculate_area(radius: f64) -> f64 {
3485                    std::f64::consts::PI * radius.powi(2)"
3486                }
3487            );
3488
3489            assert_eq!(hl.highlights.len(), 2);
3490            assert_eq!(hl.highlights[0], ((18..24), insertion_style));
3491            assert_eq!(hl.highlights[1], ((67..73), insertion_style));
3492        },
3493    )
3494    .await;
3495
3496    // insertions & deletions
3497    assert_preview_edits(
3498        indoc! {"
3499        struct Person {
3500            first_name: String,
3501        }
3502
3503        impl Person {
3504            fn first_name(&self) -> &String {
3505                &self.first_name
3506            }
3507        }"
3508        },
3509        vec![
3510            (Point::new(1, 4)..Point::new(1, 9), "last"),
3511            (Point::new(5, 7)..Point::new(5, 12), "last"),
3512            (Point::new(6, 14)..Point::new(6, 19), "last"),
3513        ],
3514        true,
3515        cx,
3516        |hl| {
3517            assert_eq!(
3518                hl.text,
3519                indoc! {"
3520                        firstlast_name: String,
3521                    }
3522
3523                    impl Person {
3524                        fn firstlast_name(&self) -> &String {
3525                            &self.firstlast_name"
3526                }
3527            );
3528
3529            assert_eq!(hl.highlights.len(), 6);
3530            assert_eq!(hl.highlights[0], ((4..9), deletion_style));
3531            assert_eq!(hl.highlights[1], ((9..13), insertion_style));
3532            assert_eq!(hl.highlights[2], ((52..57), deletion_style));
3533            assert_eq!(hl.highlights[3], ((57..61), insertion_style));
3534            assert_eq!(hl.highlights[4], ((101..106), deletion_style));
3535            assert_eq!(hl.highlights[5], ((106..110), insertion_style));
3536        },
3537    )
3538    .await;
3539
3540    async fn assert_preview_edits(
3541        text: &str,
3542        edits: Vec<(Range<Point>, &str)>,
3543        include_deletions: bool,
3544        cx: &mut TestAppContext,
3545        assert_fn: impl Fn(HighlightedText),
3546    ) {
3547        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(rust_lang(), cx));
3548        let edits = buffer.read_with(cx, |buffer, _| {
3549            edits
3550                .into_iter()
3551                .map(|(range, text)| {
3552                    (
3553                        buffer.anchor_before(range.start)..buffer.anchor_after(range.end),
3554                        text.into(),
3555                    )
3556                })
3557                .collect::<Arc<[_]>>()
3558        });
3559        let edit_preview = buffer
3560            .read_with(cx, |buffer, cx| buffer.preview_edits(edits.clone(), cx))
3561            .await;
3562        let highlighted_edits = cx.read(|cx| {
3563            edit_preview.highlight_edits(&buffer.read(cx).snapshot(), &edits, include_deletions, cx)
3564        });
3565        assert_fn(highlighted_edits);
3566    }
3567}
3568
3569#[gpui::test(iterations = 100)]
3570fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
3571    let min_peers = env::var("MIN_PEERS")
3572        .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
3573        .unwrap_or(1);
3574    let max_peers = env::var("MAX_PEERS")
3575        .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
3576        .unwrap_or(5);
3577    let operations = env::var("OPERATIONS")
3578        .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
3579        .unwrap_or(10);
3580
3581    let base_text_len = rng.random_range(0..10);
3582    let base_text = RandomCharIter::new(&mut rng)
3583        .take(base_text_len)
3584        .collect::<String>();
3585    let mut replica_ids = Vec::new();
3586    let mut buffers = Vec::new();
3587    let network = Arc::new(Mutex::new(Network::new(rng.clone())));
3588    let base_buffer = cx.new(|cx| Buffer::local(base_text.as_str(), cx));
3589
3590    for i in 0..rng.random_range(min_peers..=max_peers) {
3591        let buffer = cx.new(|cx| {
3592            let state = base_buffer.read(cx).to_proto(cx);
3593            let ops = cx
3594                .foreground_executor()
3595                .block_on(base_buffer.read(cx).serialize_ops(None, cx));
3596            let mut buffer =
3597                Buffer::from_proto(ReplicaId::new(i as u16), Capability::ReadWrite, state, None)
3598                    .unwrap();
3599            buffer.apply_ops(
3600                ops.into_iter()
3601                    .map(|op| proto::deserialize_operation(op).unwrap()),
3602                cx,
3603            );
3604            buffer.set_group_interval(Duration::from_millis(rng.random_range(0..=200)));
3605            let network = network.clone();
3606            cx.subscribe(&cx.entity(), move |buffer, _, event, _| {
3607                if let BufferEvent::Operation {
3608                    operation,
3609                    is_local: true,
3610                } = event
3611                {
3612                    network.lock().broadcast(
3613                        buffer.replica_id(),
3614                        vec![proto::serialize_operation(operation)],
3615                    );
3616                }
3617            })
3618            .detach();
3619            buffer
3620        });
3621
3622        buffers.push(buffer);
3623        replica_ids.push(ReplicaId::new(i as u16));
3624        network.lock().add_peer(ReplicaId::new(i as u16));
3625        log::info!("Adding initial peer with replica id {:?}", replica_ids[i]);
3626    }
3627
3628    log::info!("initial text: {:?}", base_text);
3629
3630    let mut now = Instant::now();
3631    let mut mutation_count = operations;
3632    let mut next_diagnostic_id = 0;
3633    let mut active_selections = BTreeMap::default();
3634    loop {
3635        let replica_index = rng.random_range(0..replica_ids.len());
3636        let replica_id = replica_ids[replica_index];
3637        let buffer = &mut buffers[replica_index];
3638        let mut new_buffer = None;
3639        match rng.random_range(0..100) {
3640            0..=29 if mutation_count != 0 => {
3641                buffer.update(cx, |buffer, cx| {
3642                    buffer.start_transaction_at(now);
3643                    buffer.randomly_edit(&mut rng, 5, cx);
3644                    buffer.end_transaction_at(now, cx);
3645                    log::info!("buffer {:?} text: {:?}", buffer.replica_id(), buffer.text());
3646                });
3647                mutation_count -= 1;
3648            }
3649            30..=39 if mutation_count != 0 => {
3650                buffer.update(cx, |buffer, cx| {
3651                    if rng.random_bool(0.2) {
3652                        log::info!("peer {:?} clearing active selections", replica_id);
3653                        active_selections.remove(&replica_id);
3654                        buffer.remove_active_selections(cx);
3655                    } else {
3656                        let mut selections = Vec::new();
3657                        for id in 0..rng.random_range(1..=5) {
3658                            let range = buffer.random_byte_range(0, &mut rng);
3659                            selections.push(Selection {
3660                                id,
3661                                start: buffer.anchor_before(range.start),
3662                                end: buffer.anchor_before(range.end),
3663                                reversed: false,
3664                                goal: SelectionGoal::None,
3665                            });
3666                        }
3667                        let selections: Arc<[Selection<Anchor>]> = selections.into();
3668                        log::info!(
3669                            "peer {:?} setting active selections: {:?}",
3670                            replica_id,
3671                            selections
3672                        );
3673                        active_selections.insert(replica_id, selections.clone());
3674                        buffer.set_active_selections(selections, false, Default::default(), cx);
3675                    }
3676                });
3677                mutation_count -= 1;
3678            }
3679            40..=49 if mutation_count != 0 && replica_id == ReplicaId::REMOTE_SERVER => {
3680                let entry_count = rng.random_range(1..=5);
3681                buffer.update(cx, |buffer, cx| {
3682                    let diagnostics = DiagnosticSet::new(
3683                        (0..entry_count).map(|_| {
3684                            let range = buffer.random_byte_range(0, &mut rng);
3685                            let range = range.to_point_utf16(buffer);
3686                            let range = range.start..range.end;
3687                            DiagnosticEntry {
3688                                range,
3689                                diagnostic: Diagnostic {
3690                                    message: post_inc(&mut next_diagnostic_id).to_string(),
3691                                    ..Default::default()
3692                                },
3693                            }
3694                        }),
3695                        buffer,
3696                    );
3697                    log::info!(
3698                        "peer {:?} setting diagnostics: {:?}",
3699                        replica_id,
3700                        diagnostics
3701                    );
3702                    buffer.update_diagnostics(LanguageServerId(0), diagnostics, cx);
3703                });
3704                mutation_count -= 1;
3705            }
3706            50..=59 if replica_ids.len() < max_peers => {
3707                let old_buffer_state = buffer.read(cx).to_proto(cx);
3708                let old_buffer_ops = cx
3709                    .foreground_executor()
3710                    .block_on(buffer.read(cx).serialize_ops(None, cx));
3711                let new_replica_id = (0..=replica_ids.len() as u16)
3712                    .map(ReplicaId::new)
3713                    .filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
3714                    .choose(&mut rng)
3715                    .unwrap();
3716                log::info!(
3717                    "Adding new replica {:?} (replicating from {:?})",
3718                    new_replica_id,
3719                    replica_id
3720                );
3721                new_buffer = Some(cx.new(|cx| {
3722                    let mut new_buffer = Buffer::from_proto(
3723                        new_replica_id,
3724                        Capability::ReadWrite,
3725                        old_buffer_state,
3726                        None,
3727                    )
3728                    .unwrap();
3729                    new_buffer.apply_ops(
3730                        old_buffer_ops
3731                            .into_iter()
3732                            .map(|op| deserialize_operation(op).unwrap()),
3733                        cx,
3734                    );
3735                    log::info!(
3736                        "New replica {:?} text: {:?}",
3737                        new_buffer.replica_id(),
3738                        new_buffer.text()
3739                    );
3740                    new_buffer.set_group_interval(Duration::from_millis(rng.random_range(0..=200)));
3741                    let network = network.clone();
3742                    cx.subscribe(&cx.entity(), move |buffer, _, event, _| {
3743                        if let BufferEvent::Operation {
3744                            operation,
3745                            is_local: true,
3746                        } = event
3747                        {
3748                            network.lock().broadcast(
3749                                buffer.replica_id(),
3750                                vec![proto::serialize_operation(operation)],
3751                            );
3752                        }
3753                    })
3754                    .detach();
3755                    new_buffer
3756                }));
3757                network.lock().replicate(replica_id, new_replica_id);
3758
3759                if new_replica_id.as_u16() as usize == replica_ids.len() {
3760                    replica_ids.push(new_replica_id);
3761                } else {
3762                    let new_buffer = new_buffer.take().unwrap();
3763                    while network.lock().has_unreceived(new_replica_id) {
3764                        let ops = network
3765                            .lock()
3766                            .receive(new_replica_id)
3767                            .into_iter()
3768                            .map(|op| proto::deserialize_operation(op).unwrap());
3769                        if ops.len() > 0 {
3770                            log::info!(
3771                                "peer {:?} (version: {:?}) applying {} ops from the network. {:?}",
3772                                new_replica_id,
3773                                buffer.read(cx).version(),
3774                                ops.len(),
3775                                ops
3776                            );
3777                            new_buffer.update(cx, |new_buffer, cx| {
3778                                new_buffer.apply_ops(ops, cx);
3779                            });
3780                        }
3781                    }
3782                    buffers[new_replica_id.as_u16() as usize] = new_buffer;
3783                }
3784            }
3785            60..=69 if mutation_count != 0 => {
3786                buffer.update(cx, |buffer, cx| {
3787                    buffer.randomly_undo_redo(&mut rng, cx);
3788                    log::info!("buffer {:?} text: {:?}", buffer.replica_id(), buffer.text());
3789                });
3790                mutation_count -= 1;
3791            }
3792            _ if network.lock().has_unreceived(replica_id) => {
3793                let ops = network
3794                    .lock()
3795                    .receive(replica_id)
3796                    .into_iter()
3797                    .map(|op| proto::deserialize_operation(op).unwrap());
3798                if ops.len() > 0 {
3799                    log::info!(
3800                        "peer {:?} (version: {:?}) applying {} ops from the network. {:?}",
3801                        replica_id,
3802                        buffer.read(cx).version(),
3803                        ops.len(),
3804                        ops
3805                    );
3806                    buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx));
3807                }
3808            }
3809            _ => {}
3810        }
3811
3812        now += Duration::from_millis(rng.random_range(0..=200));
3813        buffers.extend(new_buffer);
3814
3815        for buffer in &buffers {
3816            buffer.read(cx).check_invariants();
3817        }
3818
3819        if mutation_count == 0 && network.lock().is_idle() {
3820            break;
3821        }
3822    }
3823
3824    let first_buffer = buffers[0].read(cx).snapshot();
3825    for buffer in &buffers[1..] {
3826        let buffer = buffer.read(cx).snapshot();
3827        assert_eq!(
3828            buffer.version(),
3829            first_buffer.version(),
3830            "Replica {:?} version != Replica 0 version",
3831            buffer.replica_id()
3832        );
3833        assert_eq!(
3834            buffer.text(),
3835            first_buffer.text(),
3836            "Replica {:?} text != Replica 0 text",
3837            buffer.replica_id()
3838        );
3839        assert_eq!(
3840            buffer
3841                .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
3842                .collect::<Vec<_>>(),
3843            first_buffer
3844                .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
3845                .collect::<Vec<_>>(),
3846            "Replica {:?} diagnostics != Replica 0 diagnostics",
3847            buffer.replica_id()
3848        );
3849    }
3850
3851    for buffer in &buffers {
3852        let buffer = buffer.read(cx).snapshot();
3853        let actual_remote_selections = buffer
3854            .selections_in_range(Anchor::min_max_range_for_buffer(buffer.remote_id()), false)
3855            .map(|(replica_id, _, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
3856            .collect::<Vec<_>>();
3857        let expected_remote_selections = active_selections
3858            .iter()
3859            .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
3860            .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
3861            .collect::<Vec<_>>();
3862        assert_eq!(
3863            actual_remote_selections,
3864            expected_remote_selections,
3865            "Replica {:?} remote selections != expected selections",
3866            buffer.replica_id()
3867        );
3868    }
3869}
3870
3871#[test]
3872fn test_contiguous_ranges() {
3873    assert_eq!(
3874        contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
3875        &[1..4, 5..7, 9..13]
3876    );
3877
3878    // Respects the `max_len` parameter
3879    assert_eq!(
3880        contiguous_ranges(
3881            [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
3882            3
3883        )
3884        .collect::<Vec<_>>(),
3885        &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
3886    );
3887}
3888
3889#[gpui::test]
3890fn test_insertion_after_deletion(cx: &mut gpui::App) {
3891    let buffer = cx.new(|cx| Buffer::local("struct Foo {\n    \n}", cx));
3892    buffer.update(cx, |buffer, cx| {
3893        let mut anchor = buffer.anchor_after(17);
3894        buffer.edit([(12..18, "")], None, cx);
3895        let snapshot = buffer.snapshot();
3896        assert_eq!(snapshot.text(), "struct Foo {}");
3897        if !anchor.is_valid(&snapshot) {
3898            anchor = snapshot.anchor_after(snapshot.offset_for_anchor(&anchor));
3899        }
3900        buffer.edit([(anchor..anchor, "\n")], None, cx);
3901        buffer.edit([(anchor..anchor, "field1:")], None, cx);
3902        buffer.edit([(anchor..anchor, " i32,")], None, cx);
3903        let snapshot = buffer.snapshot();
3904        assert_eq!(snapshot.text(), "struct Foo {\nfield1: i32,}");
3905    })
3906}
3907
3908#[gpui::test(iterations = 500)]
3909fn test_trailing_whitespace_ranges(mut rng: StdRng) {
3910    // Generate a random multi-line string containing
3911    // some lines with trailing whitespace.
3912    let mut text = String::new();
3913    for _ in 0..rng.random_range(0..16) {
3914        for _ in 0..rng.random_range(0..36) {
3915            text.push(match rng.random_range(0..10) {
3916                0..=1 => ' ',
3917                3 => '\t',
3918                _ => rng.random_range('a'..='z'),
3919            });
3920        }
3921        text.push('\n');
3922    }
3923
3924    match rng.random_range(0..10) {
3925        // sometimes remove the last newline
3926        0..=1 => drop(text.pop()), //
3927
3928        // sometimes add extra newlines
3929        2..=3 => text.push_str(&"\n".repeat(rng.random_range(1..5))),
3930        _ => {}
3931    }
3932
3933    let rope = Rope::from(text.as_str());
3934    let actual_ranges = trailing_whitespace_ranges(&rope);
3935    let expected_ranges = TRAILING_WHITESPACE_REGEX
3936        .find_iter(&text)
3937        .map(|m| m.range())
3938        .collect::<Vec<_>>();
3939    assert_eq!(
3940        actual_ranges,
3941        expected_ranges,
3942        "wrong ranges for text lines:\n{:?}",
3943        text.split('\n').collect::<Vec<_>>()
3944    );
3945}
3946
3947#[gpui::test]
3948fn test_words_in_range(cx: &mut gpui::App) {
3949    init_settings(cx, |_| {});
3950
3951    // The first line are words excluded from the results with heuristics, we do not expect them in the test assertions.
3952    let contents = r#"
39530_isize 123 3.4 4  
3954let word=öäpple.bar你 Öäpple word2-öÄpPlE-Pizza-word ÖÄPPLE word
3955    "#;
3956
3957    let buffer = cx.new(|cx| {
3958        let buffer = Buffer::local(contents, cx).with_language(rust_lang(), cx);
3959        assert_eq!(buffer.text(), contents);
3960        buffer.check_invariants();
3961        buffer
3962    });
3963
3964    buffer.update(cx, |buffer, _| {
3965        let snapshot = buffer.snapshot();
3966        assert_eq!(
3967            BTreeSet::from_iter(["Pizza".to_string()]),
3968            snapshot
3969                .words_in_range(WordsQuery {
3970                    fuzzy_contents: Some("piz"),
3971                    skip_digits: true,
3972                    range: 0..snapshot.len(),
3973                })
3974                .into_keys()
3975                .collect::<BTreeSet<_>>()
3976        );
3977        assert_eq!(
3978            BTreeSet::from_iter([
3979                "öäpple".to_string(),
3980                "Öäpple".to_string(),
3981                "öÄpPlE".to_string(),
3982                "ÖÄPPLE".to_string(),
3983            ]),
3984            snapshot
3985                .words_in_range(WordsQuery {
3986                    fuzzy_contents: Some("öp"),
3987                    skip_digits: true,
3988                    range: 0..snapshot.len(),
3989                })
3990                .into_keys()
3991                .collect::<BTreeSet<_>>()
3992        );
3993        assert_eq!(
3994            BTreeSet::from_iter([
3995                "öÄpPlE".to_string(),
3996                "Öäpple".to_string(),
3997                "ÖÄPPLE".to_string(),
3998                "öäpple".to_string(),
3999            ]),
4000            snapshot
4001                .words_in_range(WordsQuery {
4002                    fuzzy_contents: Some("öÄ"),
4003                    skip_digits: true,
4004                    range: 0..snapshot.len(),
4005                })
4006                .into_keys()
4007                .collect::<BTreeSet<_>>()
4008        );
4009        assert_eq!(
4010            BTreeSet::default(),
4011            snapshot
4012                .words_in_range(WordsQuery {
4013                    fuzzy_contents: Some("öÄ好"),
4014                    skip_digits: true,
4015                    range: 0..snapshot.len(),
4016                })
4017                .into_keys()
4018                .collect::<BTreeSet<_>>()
4019        );
4020        assert_eq!(
4021            BTreeSet::from_iter(["bar你".to_string(),]),
4022            snapshot
4023                .words_in_range(WordsQuery {
4024                    fuzzy_contents: Some(""),
4025                    skip_digits: true,
4026                    range: 0..snapshot.len(),
4027                })
4028                .into_keys()
4029                .collect::<BTreeSet<_>>()
4030        );
4031        assert_eq!(
4032            BTreeSet::default(),
4033            snapshot
4034                .words_in_range(WordsQuery {
4035                    fuzzy_contents: Some(""),
4036                    skip_digits: true,
4037                    range: 0..snapshot.len(),
4038                },)
4039                .into_keys()
4040                .collect::<BTreeSet<_>>()
4041        );
4042        assert_eq!(
4043            BTreeSet::from_iter([
4044                "bar你".to_string(),
4045                "öÄpPlE".to_string(),
4046                "Öäpple".to_string(),
4047                "ÖÄPPLE".to_string(),
4048                "öäpple".to_string(),
4049                "let".to_string(),
4050                "Pizza".to_string(),
4051                "word".to_string(),
4052                "word2".to_string(),
4053            ]),
4054            snapshot
4055                .words_in_range(WordsQuery {
4056                    fuzzy_contents: None,
4057                    skip_digits: true,
4058                    range: 0..snapshot.len(),
4059                })
4060                .into_keys()
4061                .collect::<BTreeSet<_>>()
4062        );
4063        assert_eq!(
4064            BTreeSet::from_iter([
4065                "0_isize".to_string(),
4066                "123".to_string(),
4067                "3".to_string(),
4068                "4".to_string(),
4069                "bar你".to_string(),
4070                "öÄpPlE".to_string(),
4071                "Öäpple".to_string(),
4072                "ÖÄPPLE".to_string(),
4073                "öäpple".to_string(),
4074                "let".to_string(),
4075                "Pizza".to_string(),
4076                "word".to_string(),
4077                "word2".to_string(),
4078            ]),
4079            snapshot
4080                .words_in_range(WordsQuery {
4081                    fuzzy_contents: None,
4082                    skip_digits: false,
4083                    range: 0..snapshot.len(),
4084                })
4085                .into_keys()
4086                .collect::<BTreeSet<_>>()
4087        );
4088    });
4089}
4090
4091fn ruby_lang() -> Language {
4092    Language::new(
4093        LanguageConfig {
4094            name: "Ruby".into(),
4095            matcher: LanguageMatcher {
4096                path_suffixes: vec!["rb".to_string()],
4097                ..Default::default()
4098            },
4099            line_comments: vec!["# ".into()],
4100            ..Default::default()
4101        },
4102        Some(tree_sitter_ruby::LANGUAGE.into()),
4103    )
4104    .with_indents_query(
4105        r#"
4106            (class "end" @end) @indent
4107            (method "end" @end) @indent
4108            (rescue) @outdent
4109            (then) @indent
4110        "#,
4111    )
4112    .unwrap()
4113}
4114
4115fn html_lang() -> Language {
4116    Language::new(
4117        LanguageConfig {
4118            name: LanguageName::new_static("HTML"),
4119            block_comment: Some(BlockCommentConfig {
4120                start: "<!--".into(),
4121                prefix: "".into(),
4122                end: "-->".into(),
4123                tab_size: 0,
4124            }),
4125            ..Default::default()
4126        },
4127        Some(tree_sitter_html::LANGUAGE.into()),
4128    )
4129    .with_indents_query(
4130        "
4131        (element
4132          (start_tag) @start
4133          (end_tag)? @end) @indent
4134        ",
4135    )
4136    .unwrap()
4137    .with_injection_query(
4138        r#"
4139        (script_element
4140            (raw_text) @injection.content
4141            (#set! injection.language "javascript"))
4142        "#,
4143    )
4144    .unwrap()
4145}
4146
4147fn erb_lang() -> Language {
4148    Language::new(
4149        LanguageConfig {
4150            name: "HTML+ERB".into(),
4151            matcher: LanguageMatcher {
4152                path_suffixes: vec!["erb".to_string()],
4153                ..Default::default()
4154            },
4155            block_comment: Some(BlockCommentConfig {
4156                start: "<%#".into(),
4157                prefix: "".into(),
4158                end: "%>".into(),
4159                tab_size: 0,
4160            }),
4161            ..Default::default()
4162        },
4163        Some(tree_sitter_embedded_template::LANGUAGE.into()),
4164    )
4165    .with_injection_query(
4166        r#"
4167            (
4168                (code) @content
4169                (#set! "language" "ruby")
4170                (#set! "combined")
4171            )
4172
4173            (
4174                (content) @content
4175                (#set! "language" "html")
4176                (#set! "combined")
4177            )
4178        "#,
4179    )
4180    .unwrap()
4181}
4182
4183fn json_lang() -> Language {
4184    Language::new(
4185        LanguageConfig {
4186            name: "Json".into(),
4187            matcher: LanguageMatcher {
4188                path_suffixes: vec!["js".to_string()],
4189                ..Default::default()
4190            },
4191            ..Default::default()
4192        },
4193        Some(tree_sitter_json::LANGUAGE.into()),
4194    )
4195}
4196
4197fn javascript_lang() -> Language {
4198    Language::new(
4199        LanguageConfig {
4200            name: "JavaScript".into(),
4201            ..Default::default()
4202        },
4203        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
4204    )
4205    .with_brackets_query(
4206        r#"
4207        ("{" @open "}" @close)
4208        ("(" @open ")" @close)
4209        "#,
4210    )
4211    .unwrap()
4212    .with_indents_query(
4213        r#"
4214        (object "}" @end) @indent
4215        "#,
4216    )
4217    .unwrap()
4218}
4219
4220pub fn markdown_inline_lang() -> Language {
4221    Language::new(
4222        LanguageConfig {
4223            name: "Markdown-Inline".into(),
4224            hidden: true,
4225            ..LanguageConfig::default()
4226        },
4227        Some(tree_sitter_md::INLINE_LANGUAGE.into()),
4228    )
4229    .with_highlights_query("(emphasis) @emphasis")
4230    .unwrap()
4231}
4232
4233fn get_tree_sexp(buffer: &Entity<Buffer>, cx: &mut gpui::TestAppContext) -> String {
4234    buffer.update(cx, |buffer, _| {
4235        let snapshot = buffer.snapshot();
4236        let layers = snapshot.syntax.layers(buffer.as_text_snapshot());
4237        layers[0].node().to_sexp()
4238    })
4239}
4240
4241// Assert that the enclosing bracket ranges around the selection match the pairs indicated by the marked text in `range_markers`
4242#[track_caller]
4243fn assert_bracket_pairs(
4244    selection_text: &'static str,
4245    bracket_pair_texts: Vec<&'static str>,
4246    language: Arc<Language>,
4247    cx: &mut App,
4248) {
4249    let (expected_text, selection_ranges) = marked_text_ranges(selection_text, false);
4250    let buffer = cx.new(|cx| Buffer::local(expected_text.clone(), cx).with_language(language, cx));
4251    let buffer = buffer.update(cx, |buffer, _cx| buffer.snapshot());
4252
4253    let selection_range = selection_ranges[0].clone();
4254
4255    let bracket_pairs = bracket_pair_texts
4256        .into_iter()
4257        .map(|pair_text| {
4258            let (bracket_text, ranges) = marked_text_ranges(pair_text, false);
4259            assert_eq!(bracket_text, expected_text);
4260            (ranges[0].clone(), ranges[1].clone())
4261        })
4262        .collect::<Vec<_>>();
4263
4264    assert_set_eq!(
4265        buffer
4266            .bracket_ranges(selection_range)
4267            .map(|pair| (pair.open_range, pair.close_range))
4268            .collect::<Vec<_>>(),
4269        bracket_pairs
4270    );
4271}
4272
4273fn init_settings(cx: &mut App, f: fn(&mut AllLanguageSettingsContent)) {
4274    let settings_store = SettingsStore::test(cx);
4275    cx.set_global(settings_store);
4276    cx.update_global::<SettingsStore, _>(|settings, cx| {
4277        settings.update_user_settings(cx, |content| f(&mut content.project.all_languages));
4278    });
4279}
4280
4281#[gpui::test(iterations = 100)]
4282fn test_random_chunk_bitmaps(cx: &mut App, mut rng: StdRng) {
4283    use util::RandomCharIter;
4284
4285    // Generate random text
4286    let len = rng.random_range(0..10000);
4287    let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
4288
4289    let buffer = cx.new(|cx| Buffer::local(text, cx));
4290    let snapshot = buffer.read(cx).snapshot();
4291
4292    // Get all chunks and verify their bitmaps
4293    let chunks = snapshot.chunks(
4294        0..snapshot.len(),
4295        LanguageAwareStyling {
4296            tree_sitter: false,
4297            diagnostics: false,
4298        },
4299    );
4300
4301    for chunk in chunks {
4302        let chunk_text = chunk.text;
4303        let chars_bitmap = chunk.chars;
4304        let tabs_bitmap = chunk.tabs;
4305
4306        // Check empty chunks have empty bitmaps
4307        if chunk_text.is_empty() {
4308            assert_eq!(
4309                chars_bitmap, 0,
4310                "Empty chunk should have empty chars bitmap"
4311            );
4312            assert_eq!(tabs_bitmap, 0, "Empty chunk should have empty tabs bitmap");
4313            continue;
4314        }
4315
4316        // Verify that chunk text doesn't exceed 128 bytes
4317        assert!(
4318            chunk_text.len() <= 128,
4319            "Chunk text length {} exceeds 128 bytes",
4320            chunk_text.len()
4321        );
4322
4323        // Verify chars bitmap
4324        let char_indices = chunk_text
4325            .char_indices()
4326            .map(|(i, _)| i)
4327            .collect::<Vec<_>>();
4328
4329        for byte_idx in 0..chunk_text.len() {
4330            let should_have_bit = char_indices.contains(&byte_idx);
4331            let has_bit = chars_bitmap & (1 << byte_idx) != 0;
4332
4333            if has_bit != should_have_bit {
4334                eprintln!("Chunk text bytes: {:?}", chunk_text.as_bytes());
4335                eprintln!("Char indices: {:?}", char_indices);
4336                eprintln!("Chars bitmap: {:#b}", chars_bitmap);
4337            }
4338
4339            assert_eq!(
4340                has_bit, should_have_bit,
4341                "Chars bitmap mismatch at byte index {} in chunk {:?}. Expected bit: {}, Got bit: {}",
4342                byte_idx, chunk_text, should_have_bit, has_bit
4343            );
4344        }
4345
4346        // Verify tabs bitmap
4347        for (byte_idx, byte) in chunk_text.bytes().enumerate() {
4348            let is_tab = byte == b'\t';
4349            let has_bit = tabs_bitmap & (1 << byte_idx) != 0;
4350
4351            if has_bit != is_tab {
4352                eprintln!("Chunk text bytes: {:?}", chunk_text.as_bytes());
4353                eprintln!("Tabs bitmap: {:#b}", tabs_bitmap);
4354                assert_eq!(
4355                    has_bit, is_tab,
4356                    "Tabs bitmap mismatch at byte index {} in chunk {:?}. Byte: {:?}, Expected bit: {}, Got bit: {}",
4357                    byte_idx, chunk_text, byte as char, is_tab, has_bit
4358                );
4359            }
4360        }
4361    }
4362}