buffer_tests.rs

   1use super::*;
   2use crate::language_settings::{
   3    AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent,
   4};
   5use crate::Buffer;
   6use clock::ReplicaId;
   7use collections::BTreeMap;
   8use futures::FutureExt as _;
   9use gpui::{AppContext, BorrowAppContext, Model};
  10use gpui::{Context, TestAppContext};
  11use indoc::indoc;
  12use proto::deserialize_operation;
  13use rand::prelude::*;
  14use regex::RegexBuilder;
  15use settings::SettingsStore;
  16use std::{
  17    env,
  18    ops::Range,
  19    sync::LazyLock,
  20    time::{Duration, Instant},
  21};
  22use text::network::Network;
  23use text::{BufferId, LineEnding, LineIndent};
  24use text::{Point, ToPoint};
  25use unindent::Unindent as _;
  26use util::{assert_set_eq, post_inc, test::marked_text_ranges, RandomCharIter};
  27
  28pub static TRAILING_WHITESPACE_REGEX: LazyLock<regex::Regex> = LazyLock::new(|| {
  29    RegexBuilder::new(r"[ \t]+$")
  30        .multi_line(true)
  31        .build()
  32        .expect("Failed to create TRAILING_WHITESPACE_REGEX")
  33});
  34
  35#[cfg(test)]
  36#[ctor::ctor]
  37fn init_logger() {
  38    if std::env::var("RUST_LOG").is_ok() {
  39        env_logger::init();
  40    }
  41}
  42
  43#[gpui::test]
  44fn test_line_endings(cx: &mut gpui::AppContext) {
  45    init_settings(cx, |_| {});
  46
  47    cx.new_model(|cx| {
  48        let mut buffer =
  49            Buffer::local("one\r\ntwo\rthree", cx).with_language(Arc::new(rust_lang()), cx);
  50        assert_eq!(buffer.text(), "one\ntwo\nthree");
  51        assert_eq!(buffer.line_ending(), LineEnding::Windows);
  52
  53        buffer.check_invariants();
  54        buffer.edit(
  55            [(buffer.len()..buffer.len(), "\r\nfour")],
  56            Some(AutoindentMode::EachLine),
  57            cx,
  58        );
  59        buffer.edit([(0..0, "zero\r\n")], None, cx);
  60        assert_eq!(buffer.text(), "zero\none\ntwo\nthree\nfour");
  61        assert_eq!(buffer.line_ending(), LineEnding::Windows);
  62        buffer.check_invariants();
  63
  64        buffer
  65    });
  66}
  67
  68#[gpui::test]
  69fn test_select_language(cx: &mut AppContext) {
  70    init_settings(cx, |_| {});
  71
  72    let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
  73    registry.add(Arc::new(Language::new(
  74        LanguageConfig {
  75            name: LanguageName::new("Rust"),
  76            matcher: LanguageMatcher {
  77                path_suffixes: vec!["rs".to_string()],
  78                ..Default::default()
  79            },
  80            ..Default::default()
  81        },
  82        Some(tree_sitter_rust::language()),
  83    )));
  84    registry.add(Arc::new(Language::new(
  85        LanguageConfig {
  86            name: LanguageName::new("Make"),
  87            matcher: LanguageMatcher {
  88                path_suffixes: vec!["Makefile".to_string(), "mk".to_string()],
  89                ..Default::default()
  90            },
  91            ..Default::default()
  92        },
  93        Some(tree_sitter_rust::language()),
  94    )));
  95
  96    // matching file extension
  97    assert_eq!(
  98        registry
  99            .language_for_file(&file("src/lib.rs"), None, cx)
 100            .map(|l| l.name()),
 101        Some("Rust".into())
 102    );
 103    assert_eq!(
 104        registry
 105            .language_for_file(&file("src/lib.mk"), None, cx)
 106            .map(|l| l.name()),
 107        Some("Make".into())
 108    );
 109
 110    // matching filename
 111    assert_eq!(
 112        registry
 113            .language_for_file(&file("src/Makefile"), None, cx)
 114            .map(|l| l.name()),
 115        Some("Make".into())
 116    );
 117
 118    // matching suffix that is not the full file extension or filename
 119    assert_eq!(
 120        registry
 121            .language_for_file(&file("zed/cars"), None, cx)
 122            .map(|l| l.name()),
 123        None
 124    );
 125    assert_eq!(
 126        registry
 127            .language_for_file(&file("zed/a.cars"), None, cx)
 128            .map(|l| l.name()),
 129        None
 130    );
 131    assert_eq!(
 132        registry
 133            .language_for_file(&file("zed/sumk"), None, cx)
 134            .map(|l| l.name()),
 135        None
 136    );
 137}
 138
 139#[gpui::test(iterations = 10)]
 140async fn test_first_line_pattern(cx: &mut TestAppContext) {
 141    cx.update(|cx| init_settings(cx, |_| {}));
 142
 143    let languages = LanguageRegistry::test(cx.executor());
 144    let languages = Arc::new(languages);
 145
 146    languages.register_test_language(LanguageConfig {
 147        name: "JavaScript".into(),
 148        matcher: LanguageMatcher {
 149            path_suffixes: vec!["js".into()],
 150            first_line_pattern: Some(Regex::new(r"\bnode\b").unwrap()),
 151        },
 152        ..Default::default()
 153    });
 154
 155    assert!(cx
 156        .read(|cx| languages.language_for_file(&file("the/script"), None, cx))
 157        .is_none());
 158    assert!(cx
 159        .read(|cx| languages.language_for_file(&file("the/script"), Some(&"nothing".into()), cx))
 160        .is_none());
 161
 162    assert_eq!(
 163        cx.read(|cx| languages.language_for_file(
 164            &file("the/script"),
 165            Some(&"#!/bin/env node".into()),
 166            cx
 167        ))
 168        .unwrap()
 169        .name(),
 170        "JavaScript".into()
 171    );
 172}
 173
 174#[gpui::test]
 175async fn test_language_for_file_with_custom_file_types(cx: &mut TestAppContext) {
 176    cx.update(|cx| {
 177        init_settings(cx, |settings| {
 178            settings.file_types.extend([
 179                ("TypeScript".into(), vec!["js".into()]),
 180                ("C++".into(), vec!["c".into()]),
 181                (
 182                    "Dockerfile".into(),
 183                    vec!["Dockerfile".into(), "Dockerfile.*".into()],
 184                ),
 185            ]);
 186        })
 187    });
 188
 189    let languages = Arc::new(LanguageRegistry::test(cx.executor()));
 190
 191    for config in [
 192        LanguageConfig {
 193            name: "JavaScript".into(),
 194            matcher: LanguageMatcher {
 195                path_suffixes: vec!["js".to_string()],
 196                ..Default::default()
 197            },
 198            ..Default::default()
 199        },
 200        LanguageConfig {
 201            name: "TypeScript".into(),
 202            matcher: LanguageMatcher {
 203                path_suffixes: vec!["js".to_string()],
 204                ..Default::default()
 205            },
 206            ..Default::default()
 207        },
 208        LanguageConfig {
 209            name: "C++".into(),
 210            matcher: LanguageMatcher {
 211                path_suffixes: vec!["cpp".to_string()],
 212                ..Default::default()
 213            },
 214            ..Default::default()
 215        },
 216        LanguageConfig {
 217            name: "C".into(),
 218            matcher: LanguageMatcher {
 219                path_suffixes: vec!["c".to_string()],
 220                ..Default::default()
 221            },
 222            ..Default::default()
 223        },
 224        LanguageConfig {
 225            name: "Dockerfile".into(),
 226            matcher: LanguageMatcher {
 227                path_suffixes: vec!["Dockerfile".to_string()],
 228                ..Default::default()
 229            },
 230            ..Default::default()
 231        },
 232    ] {
 233        languages.add(Arc::new(Language::new(config, None)));
 234    }
 235
 236    let language = cx
 237        .read(|cx| languages.language_for_file(&file("foo.js"), None, cx))
 238        .unwrap();
 239    assert_eq!(language.name(), "TypeScript".into());
 240    let language = cx
 241        .read(|cx| languages.language_for_file(&file("foo.c"), None, cx))
 242        .unwrap();
 243    assert_eq!(language.name(), "C++".into());
 244    let language = cx
 245        .read(|cx| languages.language_for_file(&file("Dockerfile.dev"), None, cx))
 246        .unwrap();
 247    assert_eq!(language.name(), "Dockerfile".into());
 248}
 249
 250fn file(path: &str) -> Arc<dyn File> {
 251    Arc::new(TestFile {
 252        path: Path::new(path).into(),
 253        root_name: "zed".into(),
 254    })
 255}
 256
 257#[gpui::test]
 258fn test_edit_events(cx: &mut gpui::AppContext) {
 259    let mut now = Instant::now();
 260    let buffer_1_events = Arc::new(Mutex::new(Vec::new()));
 261    let buffer_2_events = Arc::new(Mutex::new(Vec::new()));
 262
 263    let buffer1 = cx.new_model(|cx| Buffer::local("abcdef", cx));
 264    let buffer2 = cx.new_model(|cx| {
 265        Buffer::remote(
 266            BufferId::from(cx.entity_id().as_non_zero_u64()),
 267            1,
 268            Capability::ReadWrite,
 269            "abcdef",
 270        )
 271    });
 272    let buffer1_ops = Arc::new(Mutex::new(Vec::new()));
 273    buffer1.update(cx, {
 274        let buffer1_ops = buffer1_ops.clone();
 275        |buffer, cx| {
 276            let buffer_1_events = buffer_1_events.clone();
 277            cx.subscribe(&buffer1, move |_, _, event, _| match event.clone() {
 278                Event::Operation(op) => buffer1_ops.lock().push(op),
 279                event => buffer_1_events.lock().push(event),
 280            })
 281            .detach();
 282            let buffer_2_events = buffer_2_events.clone();
 283            cx.subscribe(&buffer2, move |_, _, event, _| {
 284                buffer_2_events.lock().push(event.clone())
 285            })
 286            .detach();
 287
 288            // An edit emits an edited event, followed by a dirty changed event,
 289            // since the buffer was previously in a clean state.
 290            buffer.edit([(2..4, "XYZ")], None, cx);
 291
 292            // An empty transaction does not emit any events.
 293            buffer.start_transaction();
 294            buffer.end_transaction(cx);
 295
 296            // A transaction containing two edits emits one edited event.
 297            now += Duration::from_secs(1);
 298            buffer.start_transaction_at(now);
 299            buffer.edit([(5..5, "u")], None, cx);
 300            buffer.edit([(6..6, "w")], None, cx);
 301            buffer.end_transaction_at(now, cx);
 302
 303            // Undoing a transaction emits one edited event.
 304            buffer.undo(cx);
 305        }
 306    });
 307
 308    // Incorporating a set of remote ops emits a single edited event,
 309    // followed by a dirty changed event.
 310    buffer2.update(cx, |buffer, cx| {
 311        buffer.apply_ops(buffer1_ops.lock().drain(..), cx).unwrap();
 312    });
 313    assert_eq!(
 314        mem::take(&mut *buffer_1_events.lock()),
 315        vec![
 316            Event::Edited,
 317            Event::DirtyChanged,
 318            Event::Edited,
 319            Event::Edited,
 320        ]
 321    );
 322    assert_eq!(
 323        mem::take(&mut *buffer_2_events.lock()),
 324        vec![Event::Edited, Event::DirtyChanged]
 325    );
 326
 327    buffer1.update(cx, |buffer, cx| {
 328        // Undoing the first transaction emits edited event, followed by a
 329        // dirty changed event, since the buffer is again in a clean state.
 330        buffer.undo(cx);
 331    });
 332    // Incorporating the remote ops again emits a single edited event,
 333    // followed by a dirty changed event.
 334    buffer2.update(cx, |buffer, cx| {
 335        buffer.apply_ops(buffer1_ops.lock().drain(..), cx).unwrap();
 336    });
 337    assert_eq!(
 338        mem::take(&mut *buffer_1_events.lock()),
 339        vec![Event::Edited, Event::DirtyChanged,]
 340    );
 341    assert_eq!(
 342        mem::take(&mut *buffer_2_events.lock()),
 343        vec![Event::Edited, Event::DirtyChanged]
 344    );
 345}
 346
 347#[gpui::test]
 348async fn test_apply_diff(cx: &mut TestAppContext) {
 349    let text = "a\nbb\nccc\ndddd\neeeee\nffffff\n";
 350    let buffer = cx.new_model(|cx| Buffer::local(text, cx));
 351    let anchor = buffer.update(cx, |buffer, _| buffer.anchor_before(Point::new(3, 3)));
 352
 353    let text = "a\nccc\ndddd\nffffff\n";
 354    let diff = buffer.update(cx, |b, cx| b.diff(text.into(), cx)).await;
 355    buffer.update(cx, |buffer, cx| {
 356        buffer.apply_diff(diff, cx).unwrap();
 357        assert_eq!(buffer.text(), text);
 358        assert_eq!(anchor.to_point(buffer), Point::new(2, 3));
 359    });
 360
 361    let text = "a\n1\n\nccc\ndd2dd\nffffff\n";
 362    let diff = buffer.update(cx, |b, cx| b.diff(text.into(), cx)).await;
 363    buffer.update(cx, |buffer, cx| {
 364        buffer.apply_diff(diff, cx).unwrap();
 365        assert_eq!(buffer.text(), text);
 366        assert_eq!(anchor.to_point(buffer), Point::new(4, 4));
 367    });
 368}
 369
 370#[gpui::test(iterations = 10)]
 371async fn test_normalize_whitespace(cx: &mut gpui::TestAppContext) {
 372    let text = [
 373        "zero",     //
 374        "one  ",    // 2 trailing spaces
 375        "two",      //
 376        "three   ", // 3 trailing spaces
 377        "four",     //
 378        "five    ", // 4 trailing spaces
 379    ]
 380    .join("\n");
 381
 382    let buffer = cx.new_model(|cx| Buffer::local(text, cx));
 383
 384    // Spawn a task to format the buffer's whitespace.
 385    // Pause so that the foratting task starts running.
 386    let format = buffer.update(cx, |buffer, cx| buffer.remove_trailing_whitespace(cx));
 387    smol::future::yield_now().await;
 388
 389    // Edit the buffer while the normalization task is running.
 390    let version_before_edit = buffer.update(cx, |buffer, _| buffer.version());
 391    buffer.update(cx, |buffer, cx| {
 392        buffer.edit(
 393            [
 394                (Point::new(0, 1)..Point::new(0, 1), "EE"),
 395                (Point::new(3, 5)..Point::new(3, 5), "EEE"),
 396            ],
 397            None,
 398            cx,
 399        );
 400    });
 401
 402    let format_diff = format.await;
 403    buffer.update(cx, |buffer, cx| {
 404        let version_before_format = format_diff.base_version.clone();
 405        buffer.apply_diff(format_diff, cx);
 406
 407        // The outcome depends on the order of concurrent tasks.
 408        //
 409        // If the edit occurred while searching for trailing whitespace ranges,
 410        // then the trailing whitespace region touched by the edit is left intact.
 411        if version_before_format == version_before_edit {
 412            assert_eq!(
 413                buffer.text(),
 414                [
 415                    "zEEero",      //
 416                    "one",         //
 417                    "two",         //
 418                    "threeEEE   ", //
 419                    "four",        //
 420                    "five",        //
 421                ]
 422                .join("\n")
 423            );
 424        }
 425        // Otherwise, all trailing whitespace is removed.
 426        else {
 427            assert_eq!(
 428                buffer.text(),
 429                [
 430                    "zEEero",   //
 431                    "one",      //
 432                    "two",      //
 433                    "threeEEE", //
 434                    "four",     //
 435                    "five",     //
 436                ]
 437                .join("\n")
 438            );
 439        }
 440    });
 441}
 442
 443#[gpui::test]
 444async fn test_reparse(cx: &mut gpui::TestAppContext) {
 445    let text = "fn a() {}";
 446    let buffer =
 447        cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
 448
 449    // Wait for the initial text to parse
 450    cx.executor().run_until_parked();
 451    assert!(!buffer.update(cx, |buffer, _| buffer.is_parsing()));
 452    assert_eq!(
 453        get_tree_sexp(&buffer, cx),
 454        concat!(
 455            "(source_file (function_item name: (identifier) ",
 456            "parameters: (parameters) ",
 457            "body: (block)))"
 458        )
 459    );
 460
 461    buffer.update(cx, |buffer, _| {
 462        buffer.set_sync_parse_timeout(Duration::ZERO)
 463    });
 464
 465    // Perform some edits (add parameter and variable reference)
 466    // Parsing doesn't begin until the transaction is complete
 467    buffer.update(cx, |buf, cx| {
 468        buf.start_transaction();
 469
 470        let offset = buf.text().find(')').unwrap();
 471        buf.edit([(offset..offset, "b: C")], None, cx);
 472        assert!(!buf.is_parsing());
 473
 474        let offset = buf.text().find('}').unwrap();
 475        buf.edit([(offset..offset, " d; ")], None, cx);
 476        assert!(!buf.is_parsing());
 477
 478        buf.end_transaction(cx);
 479        assert_eq!(buf.text(), "fn a(b: C) { d; }");
 480        assert!(buf.is_parsing());
 481    });
 482    cx.executor().run_until_parked();
 483    assert!(!buffer.update(cx, |buffer, _| buffer.is_parsing()));
 484    assert_eq!(
 485        get_tree_sexp(&buffer, cx),
 486        concat!(
 487            "(source_file (function_item name: (identifier) ",
 488            "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
 489            "body: (block (expression_statement (identifier)))))"
 490        )
 491    );
 492
 493    // Perform a series of edits without waiting for the current parse to complete:
 494    // * turn identifier into a field expression
 495    // * turn field expression into a method call
 496    // * add a turbofish to the method call
 497    buffer.update(cx, |buf, cx| {
 498        let offset = buf.text().find(';').unwrap();
 499        buf.edit([(offset..offset, ".e")], None, cx);
 500        assert_eq!(buf.text(), "fn a(b: C) { d.e; }");
 501        assert!(buf.is_parsing());
 502    });
 503    buffer.update(cx, |buf, cx| {
 504        let offset = buf.text().find(';').unwrap();
 505        buf.edit([(offset..offset, "(f)")], None, cx);
 506        assert_eq!(buf.text(), "fn a(b: C) { d.e(f); }");
 507        assert!(buf.is_parsing());
 508    });
 509    buffer.update(cx, |buf, cx| {
 510        let offset = buf.text().find("(f)").unwrap();
 511        buf.edit([(offset..offset, "::<G>")], None, cx);
 512        assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
 513        assert!(buf.is_parsing());
 514    });
 515    cx.executor().run_until_parked();
 516    assert_eq!(
 517        get_tree_sexp(&buffer, cx),
 518        concat!(
 519            "(source_file (function_item name: (identifier) ",
 520            "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
 521            "body: (block (expression_statement (call_expression ",
 522            "function: (generic_function ",
 523            "function: (field_expression value: (identifier) field: (field_identifier)) ",
 524            "type_arguments: (type_arguments (type_identifier))) ",
 525            "arguments: (arguments (identifier)))))))",
 526        )
 527    );
 528
 529    buffer.update(cx, |buf, cx| {
 530        buf.undo(cx);
 531        buf.undo(cx);
 532        buf.undo(cx);
 533        buf.undo(cx);
 534        assert_eq!(buf.text(), "fn a() {}");
 535        assert!(buf.is_parsing());
 536    });
 537
 538    cx.executor().run_until_parked();
 539    assert_eq!(
 540        get_tree_sexp(&buffer, cx),
 541        concat!(
 542            "(source_file (function_item name: (identifier) ",
 543            "parameters: (parameters) ",
 544            "body: (block)))"
 545        )
 546    );
 547
 548    buffer.update(cx, |buf, cx| {
 549        buf.redo(cx);
 550        buf.redo(cx);
 551        buf.redo(cx);
 552        buf.redo(cx);
 553        assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
 554        assert!(buf.is_parsing());
 555    });
 556    cx.executor().run_until_parked();
 557    assert_eq!(
 558        get_tree_sexp(&buffer, cx),
 559        concat!(
 560            "(source_file (function_item name: (identifier) ",
 561            "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
 562            "body: (block (expression_statement (call_expression ",
 563            "function: (generic_function ",
 564            "function: (field_expression value: (identifier) field: (field_identifier)) ",
 565            "type_arguments: (type_arguments (type_identifier))) ",
 566            "arguments: (arguments (identifier)))))))",
 567        )
 568    );
 569}
 570
 571#[gpui::test]
 572async fn test_resetting_language(cx: &mut gpui::TestAppContext) {
 573    let buffer = cx.new_model(|cx| {
 574        let mut buffer = Buffer::local("{}", cx).with_language(Arc::new(rust_lang()), cx);
 575        buffer.set_sync_parse_timeout(Duration::ZERO);
 576        buffer
 577    });
 578
 579    // Wait for the initial text to parse
 580    cx.executor().run_until_parked();
 581    assert_eq!(
 582        get_tree_sexp(&buffer, cx),
 583        "(source_file (expression_statement (block)))"
 584    );
 585
 586    buffer.update(cx, |buffer, cx| {
 587        buffer.set_language(Some(Arc::new(json_lang())), cx)
 588    });
 589    cx.executor().run_until_parked();
 590    assert_eq!(get_tree_sexp(&buffer, cx), "(document (object))");
 591}
 592
 593#[gpui::test]
 594async fn test_outline(cx: &mut gpui::TestAppContext) {
 595    let text = r#"
 596        struct Person {
 597            name: String,
 598            age: usize,
 599        }
 600
 601        mod module {
 602            enum LoginState {
 603                LoggedOut,
 604                LoggingOn,
 605                LoggedIn {
 606                    person: Person,
 607                    time: Instant,
 608                }
 609            }
 610        }
 611
 612        impl Eq for Person {}
 613
 614        impl Drop for Person {
 615            fn drop(&mut self) {
 616                println!("bye");
 617            }
 618        }
 619    "#
 620    .unindent();
 621
 622    let buffer =
 623        cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
 624    let outline = buffer
 625        .update(cx, |buffer, _| buffer.snapshot().outline(None))
 626        .unwrap();
 627
 628    assert_eq!(
 629        outline
 630            .items
 631            .iter()
 632            .map(|item| (item.text.as_str(), item.depth))
 633            .collect::<Vec<_>>(),
 634        &[
 635            ("struct Person", 0),
 636            ("name", 1),
 637            ("age", 1),
 638            ("mod module", 0),
 639            ("enum LoginState", 1),
 640            ("LoggedOut", 2),
 641            ("LoggingOn", 2),
 642            ("LoggedIn", 2),
 643            ("person", 3),
 644            ("time", 3),
 645            ("impl Eq for Person", 0),
 646            ("impl Drop for Person", 0),
 647            ("fn drop", 1),
 648        ]
 649    );
 650
 651    // Without space, we only match on names
 652    assert_eq!(
 653        search(&outline, "oon", cx).await,
 654        &[
 655            ("mod module", vec![]),                    // included as the parent of a match
 656            ("enum LoginState", vec![]),               // included as the parent of a match
 657            ("LoggingOn", vec![1, 7, 8]),              // matches
 658            ("impl Drop for Person", vec![7, 18, 19]), // matches in two disjoint names
 659        ]
 660    );
 661
 662    assert_eq!(
 663        search(&outline, "dp p", cx).await,
 664        &[
 665            ("impl Drop for Person", vec![5, 8, 9, 14]),
 666            ("fn drop", vec![]),
 667        ]
 668    );
 669    assert_eq!(
 670        search(&outline, "dpn", cx).await,
 671        &[("impl Drop for Person", vec![5, 14, 19])]
 672    );
 673    assert_eq!(
 674        search(&outline, "impl ", cx).await,
 675        &[
 676            ("impl Eq for Person", vec![0, 1, 2, 3, 4]),
 677            ("impl Drop for Person", vec![0, 1, 2, 3, 4]),
 678            ("fn drop", vec![]),
 679        ]
 680    );
 681
 682    async fn search<'a>(
 683        outline: &'a Outline<Anchor>,
 684        query: &'a str,
 685        cx: &'a gpui::TestAppContext,
 686    ) -> Vec<(&'a str, Vec<usize>)> {
 687        let matches = cx
 688            .update(|cx| outline.search(query, cx.background_executor().clone()))
 689            .await;
 690        matches
 691            .into_iter()
 692            .map(|mat| (outline.items[mat.candidate_id].text.as_str(), mat.positions))
 693            .collect::<Vec<_>>()
 694    }
 695}
 696
 697#[gpui::test]
 698async fn test_outline_nodes_with_newlines(cx: &mut gpui::TestAppContext) {
 699    let text = r#"
 700        impl A for B<
 701            C
 702        > {
 703        };
 704    "#
 705    .unindent();
 706
 707    let buffer =
 708        cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
 709    let outline = buffer
 710        .update(cx, |buffer, _| buffer.snapshot().outline(None))
 711        .unwrap();
 712
 713    assert_eq!(
 714        outline
 715            .items
 716            .iter()
 717            .map(|item| (item.text.as_str(), item.depth))
 718            .collect::<Vec<_>>(),
 719        &[("impl A for B<", 0)]
 720    );
 721}
 722
 723#[gpui::test]
 724async fn test_outline_with_extra_context(cx: &mut gpui::TestAppContext) {
 725    let language = javascript_lang()
 726        .with_outline_query(
 727            r#"
 728            (function_declaration
 729                "function" @context
 730                name: (_) @name
 731                parameters: (formal_parameters
 732                    "(" @context.extra
 733                    ")" @context.extra)) @item
 734            "#,
 735        )
 736        .unwrap();
 737
 738    let text = r#"
 739        function a() {}
 740        function b(c) {}
 741    "#
 742    .unindent();
 743
 744    let buffer = cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(language), cx));
 745    let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
 746
 747    // extra context nodes are included in the outline.
 748    let outline = snapshot.outline(None).unwrap();
 749    assert_eq!(
 750        outline
 751            .items
 752            .iter()
 753            .map(|item| (item.text.as_str(), item.depth))
 754            .collect::<Vec<_>>(),
 755        &[("function a()", 0), ("function b( )", 0),]
 756    );
 757
 758    // extra context nodes do not appear in breadcrumbs.
 759    let symbols = snapshot.symbols_containing(3, None).unwrap();
 760    assert_eq!(
 761        symbols
 762            .iter()
 763            .map(|item| (item.text.as_str(), item.depth))
 764            .collect::<Vec<_>>(),
 765        &[("function a", 0)]
 766    );
 767}
 768
 769#[gpui::test]
 770fn test_outline_annotations(cx: &mut AppContext) {
 771    // Add this new test case
 772    let text = r#"
 773        /// This is a doc comment
 774        /// that spans multiple lines
 775        fn annotated_function() {
 776            // This is not an annotation
 777        }
 778
 779        // This is a single-line annotation
 780        fn another_function() {}
 781
 782        fn unannotated_function() {}
 783
 784        // This comment is not an annotation
 785
 786        fn function_after_blank_line() {}
 787    "#
 788    .unindent();
 789
 790    let buffer =
 791        cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
 792    let outline = buffer
 793        .update(cx, |buffer, _| buffer.snapshot().outline(None))
 794        .unwrap();
 795
 796    assert_eq!(
 797        outline
 798            .items
 799            .into_iter()
 800            .map(|item| (
 801                item.text,
 802                item.depth,
 803                item.annotation_range
 804                    .map(|range| { buffer.read(cx).text_for_range(range).collect::<String>() })
 805            ))
 806            .collect::<Vec<_>>(),
 807        &[
 808            (
 809                "fn annotated_function".to_string(),
 810                0,
 811                Some("/// This is a doc comment\n/// that spans multiple lines".to_string())
 812            ),
 813            (
 814                "fn another_function".to_string(),
 815                0,
 816                Some("// This is a single-line annotation".to_string())
 817            ),
 818            ("fn unannotated_function".to_string(), 0, None),
 819            ("fn function_after_blank_line".to_string(), 0, None),
 820        ]
 821    );
 822}
 823
 824#[gpui::test]
 825async fn test_symbols_containing(cx: &mut gpui::TestAppContext) {
 826    let text = r#"
 827        impl Person {
 828            fn one() {
 829                1
 830            }
 831
 832            fn two() {
 833                2
 834            }fn three() {
 835                3
 836            }
 837        }
 838    "#
 839    .unindent();
 840
 841    let buffer =
 842        cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
 843    let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
 844
 845    // point is at the start of an item
 846    assert_eq!(
 847        symbols_containing(Point::new(1, 4), &snapshot),
 848        vec![
 849            (
 850                "impl Person".to_string(),
 851                Point::new(0, 0)..Point::new(10, 1)
 852            ),
 853            ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
 854        ]
 855    );
 856
 857    // point is in the middle of an item
 858    assert_eq!(
 859        symbols_containing(Point::new(2, 8), &snapshot),
 860        vec![
 861            (
 862                "impl Person".to_string(),
 863                Point::new(0, 0)..Point::new(10, 1)
 864            ),
 865            ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
 866        ]
 867    );
 868
 869    // point is at the end of an item
 870    assert_eq!(
 871        symbols_containing(Point::new(3, 5), &snapshot),
 872        vec![
 873            (
 874                "impl Person".to_string(),
 875                Point::new(0, 0)..Point::new(10, 1)
 876            ),
 877            ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
 878        ]
 879    );
 880
 881    // point is in between two adjacent items
 882    assert_eq!(
 883        symbols_containing(Point::new(7, 5), &snapshot),
 884        vec![
 885            (
 886                "impl Person".to_string(),
 887                Point::new(0, 0)..Point::new(10, 1)
 888            ),
 889            ("fn two".to_string(), Point::new(5, 4)..Point::new(7, 5))
 890        ]
 891    );
 892
 893    fn symbols_containing(
 894        position: Point,
 895        snapshot: &BufferSnapshot,
 896    ) -> Vec<(String, Range<Point>)> {
 897        snapshot
 898            .symbols_containing(position, None)
 899            .unwrap()
 900            .into_iter()
 901            .map(|item| {
 902                (
 903                    item.text,
 904                    item.range.start.to_point(snapshot)..item.range.end.to_point(snapshot),
 905                )
 906            })
 907            .collect()
 908    }
 909}
 910
 911#[gpui::test]
 912fn test_enclosing_bracket_ranges(cx: &mut AppContext) {
 913    let mut assert = |selection_text, range_markers| {
 914        assert_bracket_pairs(selection_text, range_markers, rust_lang(), cx)
 915    };
 916
 917    assert(
 918        indoc! {"
 919            mod x {
 920                moˇd y {
 921
 922                }
 923            }
 924            let foo = 1;"},
 925        vec![indoc! {"
 926            mod x «{»
 927                mod y {
 928
 929                }
 930            «}»
 931            let foo = 1;"}],
 932    );
 933
 934    assert(
 935        indoc! {"
 936            mod x {
 937                mod y ˇ{
 938
 939                }
 940            }
 941            let foo = 1;"},
 942        vec![
 943            indoc! {"
 944                mod x «{»
 945                    mod y {
 946
 947                    }
 948                «}»
 949                let foo = 1;"},
 950            indoc! {"
 951                mod x {
 952                    mod y «{»
 953
 954                    «}»
 955                }
 956                let foo = 1;"},
 957        ],
 958    );
 959
 960    assert(
 961        indoc! {"
 962            mod x {
 963                mod y {
 964
 965 966            }
 967            let foo = 1;"},
 968        vec![
 969            indoc! {"
 970                mod x «{»
 971                    mod y {
 972
 973                    }
 974                «}»
 975                let foo = 1;"},
 976            indoc! {"
 977                mod x {
 978                    mod y «{»
 979
 980                    «}»
 981                }
 982                let foo = 1;"},
 983        ],
 984    );
 985
 986    assert(
 987        indoc! {"
 988            mod x {
 989                mod y {
 990
 991                }
 992            ˇ}
 993            let foo = 1;"},
 994        vec![indoc! {"
 995            mod x «{»
 996                mod y {
 997
 998                }
 999            «}»
1000            let foo = 1;"}],
1001    );
1002
1003    assert(
1004        indoc! {"
1005            mod x {
1006                mod y {
1007
1008                }
1009            }
1010            let fˇoo = 1;"},
1011        vec![],
1012    );
1013
1014    // Regression test: avoid crash when querying at the end of the buffer.
1015    assert(
1016        indoc! {"
1017            mod x {
1018                mod y {
1019
1020                }
1021            }
1022            let foo = 1;ˇ"},
1023        vec![],
1024    );
1025}
1026
1027#[gpui::test]
1028fn test_enclosing_bracket_ranges_where_brackets_are_not_outermost_children(cx: &mut AppContext) {
1029    let mut assert = |selection_text, bracket_pair_texts| {
1030        assert_bracket_pairs(selection_text, bracket_pair_texts, javascript_lang(), cx)
1031    };
1032
1033    assert(
1034        indoc! {"
1035        for (const a in b)ˇ {
1036            // a comment that's longer than the for-loop header
1037        }"},
1038        vec![indoc! {"
1039        for «(»const a in b«)» {
1040            // a comment that's longer than the for-loop header
1041        }"}],
1042    );
1043
1044    // Regression test: even though the parent node of the parentheses (the for loop) does
1045    // intersect the given range, the parentheses themselves do not contain the range, so
1046    // they should not be returned. Only the curly braces contain the range.
1047    assert(
1048        indoc! {"
1049        for (const a in b) {ˇ
1050            // a comment that's longer than the for-loop header
1051        }"},
1052        vec![indoc! {"
1053        for (const a in b) «{»
1054            // a comment that's longer than the for-loop header
1055        «}»"}],
1056    );
1057}
1058
1059#[gpui::test]
1060fn test_range_for_syntax_ancestor(cx: &mut AppContext) {
1061    cx.new_model(|cx| {
1062        let text = "fn a() { b(|c| {}) }";
1063        let buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
1064        let snapshot = buffer.snapshot();
1065
1066        assert_eq!(
1067            snapshot.range_for_syntax_ancestor(empty_range_at(text, "|")),
1068            Some(range_of(text, "|"))
1069        );
1070        assert_eq!(
1071            snapshot.range_for_syntax_ancestor(range_of(text, "|")),
1072            Some(range_of(text, "|c|"))
1073        );
1074        assert_eq!(
1075            snapshot.range_for_syntax_ancestor(range_of(text, "|c|")),
1076            Some(range_of(text, "|c| {}"))
1077        );
1078        assert_eq!(
1079            snapshot.range_for_syntax_ancestor(range_of(text, "|c| {}")),
1080            Some(range_of(text, "(|c| {})"))
1081        );
1082
1083        buffer
1084    });
1085
1086    fn empty_range_at(text: &str, part: &str) -> Range<usize> {
1087        let start = text.find(part).unwrap();
1088        start..start
1089    }
1090
1091    fn range_of(text: &str, part: &str) -> Range<usize> {
1092        let start = text.find(part).unwrap();
1093        start..start + part.len()
1094    }
1095}
1096
1097#[gpui::test]
1098fn test_autoindent_with_soft_tabs(cx: &mut AppContext) {
1099    init_settings(cx, |_| {});
1100
1101    cx.new_model(|cx| {
1102        let text = "fn a() {}";
1103        let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
1104
1105        buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
1106        assert_eq!(buffer.text(), "fn a() {\n    \n}");
1107
1108        buffer.edit(
1109            [(Point::new(1, 4)..Point::new(1, 4), "b()\n")],
1110            Some(AutoindentMode::EachLine),
1111            cx,
1112        );
1113        assert_eq!(buffer.text(), "fn a() {\n    b()\n    \n}");
1114
1115        // Create a field expression on a new line, causing that line
1116        // to be indented.
1117        buffer.edit(
1118            [(Point::new(2, 4)..Point::new(2, 4), ".c")],
1119            Some(AutoindentMode::EachLine),
1120            cx,
1121        );
1122        assert_eq!(buffer.text(), "fn a() {\n    b()\n        .c\n}");
1123
1124        // Remove the dot so that the line is no longer a field expression,
1125        // causing the line to be outdented.
1126        buffer.edit(
1127            [(Point::new(2, 8)..Point::new(2, 9), "")],
1128            Some(AutoindentMode::EachLine),
1129            cx,
1130        );
1131        assert_eq!(buffer.text(), "fn a() {\n    b()\n    c\n}");
1132
1133        buffer
1134    });
1135}
1136
1137#[gpui::test]
1138fn test_autoindent_with_hard_tabs(cx: &mut AppContext) {
1139    init_settings(cx, |settings| {
1140        settings.defaults.hard_tabs = Some(true);
1141    });
1142
1143    cx.new_model(|cx| {
1144        let text = "fn a() {}";
1145        let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
1146
1147        buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
1148        assert_eq!(buffer.text(), "fn a() {\n\t\n}");
1149
1150        buffer.edit(
1151            [(Point::new(1, 1)..Point::new(1, 1), "b()\n")],
1152            Some(AutoindentMode::EachLine),
1153            cx,
1154        );
1155        assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\n}");
1156
1157        // Create a field expression on a new line, causing that line
1158        // to be indented.
1159        buffer.edit(
1160            [(Point::new(2, 1)..Point::new(2, 1), ".c")],
1161            Some(AutoindentMode::EachLine),
1162            cx,
1163        );
1164        assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\t.c\n}");
1165
1166        // Remove the dot so that the line is no longer a field expression,
1167        // causing the line to be outdented.
1168        buffer.edit(
1169            [(Point::new(2, 2)..Point::new(2, 3), "")],
1170            Some(AutoindentMode::EachLine),
1171            cx,
1172        );
1173        assert_eq!(buffer.text(), "fn a() {\n\tb()\n\tc\n}");
1174
1175        buffer
1176    });
1177}
1178
1179#[gpui::test]
1180fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut AppContext) {
1181    init_settings(cx, |_| {});
1182
1183    cx.new_model(|cx| {
1184        let mut buffer = Buffer::local(
1185            "
1186            fn a() {
1187            c;
1188            d;
1189            }
1190            "
1191            .unindent(),
1192            cx,
1193        )
1194        .with_language(Arc::new(rust_lang()), cx);
1195
1196        // Lines 2 and 3 don't match the indentation suggestion. When editing these lines,
1197        // their indentation is not adjusted.
1198        buffer.edit_via_marked_text(
1199            &"
1200            fn a() {
1201            c«()»;
1202            d«()»;
1203            }
1204            "
1205            .unindent(),
1206            Some(AutoindentMode::EachLine),
1207            cx,
1208        );
1209        assert_eq!(
1210            buffer.text(),
1211            "
1212            fn a() {
1213            c();
1214            d();
1215            }
1216            "
1217            .unindent()
1218        );
1219
1220        // When appending new content after these lines, the indentation is based on the
1221        // preceding lines' actual indentation.
1222        buffer.edit_via_marked_text(
1223            &"
1224            fn a() {
12251226            .f
1227            .g()»;
12281229            .f
1230            .g()»;
1231            }
1232            "
1233            .unindent(),
1234            Some(AutoindentMode::EachLine),
1235            cx,
1236        );
1237
1238        assert_eq!(
1239            buffer.text(),
1240            "
1241            fn a() {
1242            c
1243                .f
1244                .g();
1245            d
1246                .f
1247                .g();
1248            }
1249            "
1250            .unindent()
1251        );
1252        buffer
1253    });
1254
1255    cx.new_model(|cx| {
1256        eprintln!("second buffer: {:?}", cx.entity_id());
1257
1258        let mut buffer = Buffer::local(
1259            "
1260            fn a() {
1261                b();
1262                |
1263            "
1264            .replace('|', "") // marker to preserve trailing whitespace
1265            .unindent(),
1266            cx,
1267        )
1268        .with_language(Arc::new(rust_lang()), cx);
1269
1270        // Insert a closing brace. It is outdented.
1271        buffer.edit_via_marked_text(
1272            &"
1273            fn a() {
1274                b();
1275                «}»
1276            "
1277            .unindent(),
1278            Some(AutoindentMode::EachLine),
1279            cx,
1280        );
1281        assert_eq!(
1282            buffer.text(),
1283            "
1284            fn a() {
1285                b();
1286            }
1287            "
1288            .unindent()
1289        );
1290
1291        // Manually edit the leading whitespace. The edit is preserved.
1292        buffer.edit_via_marked_text(
1293            &"
1294            fn a() {
1295                b();
1296            «    »}
1297            "
1298            .unindent(),
1299            Some(AutoindentMode::EachLine),
1300            cx,
1301        );
1302        assert_eq!(
1303            buffer.text(),
1304            "
1305            fn a() {
1306                b();
1307                }
1308            "
1309            .unindent()
1310        );
1311        buffer
1312    });
1313
1314    eprintln!("DONE");
1315}
1316
1317#[gpui::test]
1318fn test_autoindent_does_not_adjust_lines_within_newly_created_errors(cx: &mut AppContext) {
1319    init_settings(cx, |_| {});
1320
1321    cx.new_model(|cx| {
1322        let mut buffer = Buffer::local(
1323            "
1324            fn a() {
1325                i
1326            }
1327            "
1328            .unindent(),
1329            cx,
1330        )
1331        .with_language(Arc::new(rust_lang()), cx);
1332
1333        // Regression test: line does not get outdented due to syntax error
1334        buffer.edit_via_marked_text(
1335            &"
1336            fn a() {
1337                i«f let Some(x) = y»
1338            }
1339            "
1340            .unindent(),
1341            Some(AutoindentMode::EachLine),
1342            cx,
1343        );
1344        assert_eq!(
1345            buffer.text(),
1346            "
1347            fn a() {
1348                if let Some(x) = y
1349            }
1350            "
1351            .unindent()
1352        );
1353
1354        buffer.edit_via_marked_text(
1355            &"
1356            fn a() {
1357                if let Some(x) = y« {»
1358            }
1359            "
1360            .unindent(),
1361            Some(AutoindentMode::EachLine),
1362            cx,
1363        );
1364        assert_eq!(
1365            buffer.text(),
1366            "
1367            fn a() {
1368                if let Some(x) = y {
1369            }
1370            "
1371            .unindent()
1372        );
1373
1374        buffer
1375    });
1376}
1377
1378#[gpui::test]
1379fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut AppContext) {
1380    init_settings(cx, |_| {});
1381
1382    cx.new_model(|cx| {
1383        let mut buffer = Buffer::local(
1384            "
1385            fn a() {}
1386            "
1387            .unindent(),
1388            cx,
1389        )
1390        .with_language(Arc::new(rust_lang()), cx);
1391
1392        buffer.edit_via_marked_text(
1393            &"
1394            fn a(«
1395            b») {}
1396            "
1397            .unindent(),
1398            Some(AutoindentMode::EachLine),
1399            cx,
1400        );
1401        assert_eq!(
1402            buffer.text(),
1403            "
1404            fn a(
1405                b) {}
1406            "
1407            .unindent()
1408        );
1409
1410        // The indentation suggestion changed because `@end` node (a close paren)
1411        // is now at the beginning of the line.
1412        buffer.edit_via_marked_text(
1413            &"
1414            fn a(
1415                ˇ) {}
1416            "
1417            .unindent(),
1418            Some(AutoindentMode::EachLine),
1419            cx,
1420        );
1421        assert_eq!(
1422            buffer.text(),
1423            "
1424                fn a(
1425                ) {}
1426            "
1427            .unindent()
1428        );
1429
1430        buffer
1431    });
1432}
1433
1434#[gpui::test]
1435fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut AppContext) {
1436    init_settings(cx, |_| {});
1437
1438    cx.new_model(|cx| {
1439        let text = "a\nb";
1440        let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
1441        buffer.edit(
1442            [(0..1, "\n"), (2..3, "\n")],
1443            Some(AutoindentMode::EachLine),
1444            cx,
1445        );
1446        assert_eq!(buffer.text(), "\n\n\n");
1447        buffer
1448    });
1449}
1450
1451#[gpui::test]
1452fn test_autoindent_multi_line_insertion(cx: &mut AppContext) {
1453    init_settings(cx, |_| {});
1454
1455    cx.new_model(|cx| {
1456        let text = "
1457            const a: usize = 1;
1458            fn b() {
1459                if c {
1460                    let d = 2;
1461                }
1462            }
1463        "
1464        .unindent();
1465
1466        let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
1467        buffer.edit(
1468            [(Point::new(3, 0)..Point::new(3, 0), "e(\n    f()\n);\n")],
1469            Some(AutoindentMode::EachLine),
1470            cx,
1471        );
1472        assert_eq!(
1473            buffer.text(),
1474            "
1475                const a: usize = 1;
1476                fn b() {
1477                    if c {
1478                        e(
1479                            f()
1480                        );
1481                        let d = 2;
1482                    }
1483                }
1484            "
1485            .unindent()
1486        );
1487
1488        buffer
1489    });
1490}
1491
1492#[gpui::test]
1493fn test_autoindent_block_mode(cx: &mut AppContext) {
1494    init_settings(cx, |_| {});
1495
1496    cx.new_model(|cx| {
1497        let text = r#"
1498            fn a() {
1499                b();
1500            }
1501        "#
1502        .unindent();
1503        let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
1504
1505        // When this text was copied, both of the quotation marks were at the same
1506        // indent level, but the indentation of the first line was not included in
1507        // the copied text. This information is retained in the
1508        // 'original_indent_columns' vector.
1509        let original_indent_columns = vec![4];
1510        let inserted_text = r#"
1511            "
1512                  c
1513                    d
1514                      e
1515                "
1516        "#
1517        .unindent();
1518
1519        // Insert the block at column zero. The entire block is indented
1520        // so that the first line matches the previous line's indentation.
1521        buffer.edit(
1522            [(Point::new(2, 0)..Point::new(2, 0), inserted_text.clone())],
1523            Some(AutoindentMode::Block {
1524                original_indent_columns: original_indent_columns.clone(),
1525            }),
1526            cx,
1527        );
1528        assert_eq!(
1529            buffer.text(),
1530            r#"
1531            fn a() {
1532                b();
1533                "
1534                  c
1535                    d
1536                      e
1537                "
1538            }
1539            "#
1540            .unindent()
1541        );
1542
1543        // Grouping is disabled in tests, so we need 2 undos
1544        buffer.undo(cx); // Undo the auto-indent
1545        buffer.undo(cx); // Undo the original edit
1546
1547        // Insert the block at a deeper indent level. The entire block is outdented.
1548        buffer.edit([(Point::new(2, 0)..Point::new(2, 0), "        ")], None, cx);
1549        buffer.edit(
1550            [(Point::new(2, 8)..Point::new(2, 8), inserted_text)],
1551            Some(AutoindentMode::Block {
1552                original_indent_columns: original_indent_columns.clone(),
1553            }),
1554            cx,
1555        );
1556        assert_eq!(
1557            buffer.text(),
1558            r#"
1559            fn a() {
1560                b();
1561                "
1562                  c
1563                    d
1564                      e
1565                "
1566            }
1567            "#
1568            .unindent()
1569        );
1570
1571        buffer
1572    });
1573}
1574
1575#[gpui::test]
1576fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut AppContext) {
1577    init_settings(cx, |_| {});
1578
1579    cx.new_model(|cx| {
1580        let text = r#"
1581            fn a() {
1582                if b() {
1583
1584                }
1585            }
1586        "#
1587        .unindent();
1588        let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
1589
1590        // The original indent columns are not known, so this text is
1591        // auto-indented in a block as if the first line was copied in
1592        // its entirety.
1593        let original_indent_columns = Vec::new();
1594        let inserted_text = "    c\n        .d()\n        .e();";
1595
1596        // Insert the block at column zero. The entire block is indented
1597        // so that the first line matches the previous line's indentation.
1598        buffer.edit(
1599            [(Point::new(2, 0)..Point::new(2, 0), inserted_text)],
1600            Some(AutoindentMode::Block {
1601                original_indent_columns: original_indent_columns.clone(),
1602            }),
1603            cx,
1604        );
1605        assert_eq!(
1606            buffer.text(),
1607            r#"
1608            fn a() {
1609                if b() {
1610                    c
1611                        .d()
1612                        .e();
1613                }
1614            }
1615            "#
1616            .unindent()
1617        );
1618
1619        // Grouping is disabled in tests, so we need 2 undos
1620        buffer.undo(cx); // Undo the auto-indent
1621        buffer.undo(cx); // Undo the original edit
1622
1623        // Insert the block at a deeper indent level. The entire block is outdented.
1624        buffer.edit(
1625            [(Point::new(2, 0)..Point::new(2, 0), " ".repeat(12))],
1626            None,
1627            cx,
1628        );
1629        buffer.edit(
1630            [(Point::new(2, 12)..Point::new(2, 12), inserted_text)],
1631            Some(AutoindentMode::Block {
1632                original_indent_columns: Vec::new(),
1633            }),
1634            cx,
1635        );
1636        assert_eq!(
1637            buffer.text(),
1638            r#"
1639            fn a() {
1640                if b() {
1641                    c
1642                        .d()
1643                        .e();
1644                }
1645            }
1646            "#
1647            .unindent()
1648        );
1649
1650        buffer
1651    });
1652}
1653
1654#[gpui::test]
1655fn test_autoindent_language_without_indents_query(cx: &mut AppContext) {
1656    init_settings(cx, |_| {});
1657
1658    cx.new_model(|cx| {
1659        let text = "
1660            * one
1661                - a
1662                - b
1663            * two
1664        "
1665        .unindent();
1666
1667        let mut buffer = Buffer::local(text, cx).with_language(
1668            Arc::new(Language::new(
1669                LanguageConfig {
1670                    name: "Markdown".into(),
1671                    auto_indent_using_last_non_empty_line: false,
1672                    ..Default::default()
1673                },
1674                Some(tree_sitter_json::language()),
1675            )),
1676            cx,
1677        );
1678        buffer.edit(
1679            [(Point::new(3, 0)..Point::new(3, 0), "\n")],
1680            Some(AutoindentMode::EachLine),
1681            cx,
1682        );
1683        assert_eq!(
1684            buffer.text(),
1685            "
1686            * one
1687                - a
1688                - b
1689
1690            * two
1691            "
1692            .unindent()
1693        );
1694        buffer
1695    });
1696}
1697
1698#[gpui::test]
1699fn test_autoindent_with_injected_languages(cx: &mut AppContext) {
1700    init_settings(cx, |settings| {
1701        settings.languages.extend([
1702            (
1703                "HTML".into(),
1704                LanguageSettingsContent {
1705                    tab_size: Some(2.try_into().unwrap()),
1706                    ..Default::default()
1707                },
1708            ),
1709            (
1710                "JavaScript".into(),
1711                LanguageSettingsContent {
1712                    tab_size: Some(8.try_into().unwrap()),
1713                    ..Default::default()
1714                },
1715            ),
1716        ])
1717    });
1718
1719    let html_language = Arc::new(html_lang());
1720
1721    let javascript_language = Arc::new(javascript_lang());
1722
1723    let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
1724    language_registry.add(html_language.clone());
1725    language_registry.add(javascript_language.clone());
1726
1727    cx.new_model(|cx| {
1728        let (text, ranges) = marked_text_ranges(
1729            &"
1730                <div>ˇ
1731                </div>
1732                <script>
1733                    init({ˇ
1734                    })
1735                </script>
1736                <span>ˇ
1737                </span>
1738            "
1739            .unindent(),
1740            false,
1741        );
1742
1743        let mut buffer = Buffer::local(text, cx);
1744        buffer.set_language_registry(language_registry);
1745        buffer.set_language(Some(html_language), cx);
1746        buffer.edit(
1747            ranges.into_iter().map(|range| (range, "\na")),
1748            Some(AutoindentMode::EachLine),
1749            cx,
1750        );
1751        assert_eq!(
1752            buffer.text(),
1753            "
1754                <div>
1755                  a
1756                </div>
1757                <script>
1758                    init({
1759                            a
1760                    })
1761                </script>
1762                <span>
1763                  a
1764                </span>
1765            "
1766            .unindent()
1767        );
1768        buffer
1769    });
1770}
1771
1772#[gpui::test]
1773fn test_autoindent_query_with_outdent_captures(cx: &mut AppContext) {
1774    init_settings(cx, |settings| {
1775        settings.defaults.tab_size = Some(2.try_into().unwrap());
1776    });
1777
1778    cx.new_model(|cx| {
1779        let mut buffer = Buffer::local("", cx).with_language(Arc::new(ruby_lang()), cx);
1780
1781        let text = r#"
1782            class C
1783            def a(b, c)
1784            puts b
1785            puts c
1786            rescue
1787            puts "errored"
1788            exit 1
1789            end
1790            end
1791        "#
1792        .unindent();
1793
1794        buffer.edit([(0..0, text)], Some(AutoindentMode::EachLine), cx);
1795
1796        assert_eq!(
1797            buffer.text(),
1798            r#"
1799                class C
1800                  def a(b, c)
1801                    puts b
1802                    puts c
1803                  rescue
1804                    puts "errored"
1805                    exit 1
1806                  end
1807                end
1808            "#
1809            .unindent()
1810        );
1811
1812        buffer
1813    });
1814}
1815
1816#[gpui::test]
1817async fn test_async_autoindents_preserve_preview(cx: &mut TestAppContext) {
1818    cx.update(|cx| init_settings(cx, |_| {}));
1819
1820    // First we insert some newlines to request an auto-indent (asynchronously).
1821    // Then we request that a preview tab be preserved for the new version, even though it's edited.
1822    let buffer = cx.new_model(|cx| {
1823        let text = "fn a() {}";
1824        let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
1825
1826        // This causes autoindent to be async.
1827        buffer.set_sync_parse_timeout(Duration::ZERO);
1828
1829        buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
1830        buffer.refresh_preview();
1831
1832        // Synchronously, we haven't auto-indented and we're still preserving the preview.
1833        assert_eq!(buffer.text(), "fn a() {\n\n}");
1834        assert!(buffer.preserve_preview());
1835        buffer
1836    });
1837
1838    // Now let the autoindent finish
1839    cx.executor().run_until_parked();
1840
1841    // The auto-indent applied, but didn't dismiss our preview
1842    buffer.update(cx, |buffer, cx| {
1843        assert_eq!(buffer.text(), "fn a() {\n    \n}");
1844        assert!(buffer.preserve_preview());
1845
1846        // Edit inserting another line. It will autoindent async.
1847        // Then refresh the preview version.
1848        buffer.edit(
1849            [(Point::new(1, 4)..Point::new(1, 4), "\n")],
1850            Some(AutoindentMode::EachLine),
1851            cx,
1852        );
1853        buffer.refresh_preview();
1854        assert_eq!(buffer.text(), "fn a() {\n    \n\n}");
1855        assert!(buffer.preserve_preview());
1856
1857        // Then perform another edit, this time without refreshing the preview version.
1858        buffer.edit([(Point::new(1, 4)..Point::new(1, 4), "x")], None, cx);
1859        // This causes the preview to not be preserved.
1860        assert!(!buffer.preserve_preview());
1861    });
1862
1863    // Let the async autoindent from the first edit finish.
1864    cx.executor().run_until_parked();
1865
1866    // The autoindent applies, but it shouldn't restore the preview status because we had an edit in the meantime.
1867    buffer.update(cx, |buffer, _| {
1868        assert_eq!(buffer.text(), "fn a() {\n    x\n    \n}");
1869        assert!(!buffer.preserve_preview());
1870    });
1871}
1872
1873#[gpui::test]
1874fn test_insert_empty_line(cx: &mut AppContext) {
1875    init_settings(cx, |_| {});
1876
1877    // Insert empty line at the beginning, requesting an empty line above
1878    cx.new_model(|cx| {
1879        let mut buffer = Buffer::local("abc\ndef\nghi", cx);
1880        let point = buffer.insert_empty_line(Point::new(0, 0), true, false, cx);
1881        assert_eq!(buffer.text(), "\nabc\ndef\nghi");
1882        assert_eq!(point, Point::new(0, 0));
1883        buffer
1884    });
1885
1886    // Insert empty line at the beginning, requesting an empty line above and below
1887    cx.new_model(|cx| {
1888        let mut buffer = Buffer::local("abc\ndef\nghi", cx);
1889        let point = buffer.insert_empty_line(Point::new(0, 0), true, true, cx);
1890        assert_eq!(buffer.text(), "\n\nabc\ndef\nghi");
1891        assert_eq!(point, Point::new(0, 0));
1892        buffer
1893    });
1894
1895    // Insert empty line at the start of a line, requesting empty lines above and below
1896    cx.new_model(|cx| {
1897        let mut buffer = Buffer::local("abc\ndef\nghi", cx);
1898        let point = buffer.insert_empty_line(Point::new(2, 0), true, true, cx);
1899        assert_eq!(buffer.text(), "abc\ndef\n\n\n\nghi");
1900        assert_eq!(point, Point::new(3, 0));
1901        buffer
1902    });
1903
1904    // Insert empty line in the middle of a line, requesting empty lines above and below
1905    cx.new_model(|cx| {
1906        let mut buffer = Buffer::local("abc\ndefghi\njkl", cx);
1907        let point = buffer.insert_empty_line(Point::new(1, 3), true, true, cx);
1908        assert_eq!(buffer.text(), "abc\ndef\n\n\n\nghi\njkl");
1909        assert_eq!(point, Point::new(3, 0));
1910        buffer
1911    });
1912
1913    // Insert empty line in the middle of a line, requesting empty line above only
1914    cx.new_model(|cx| {
1915        let mut buffer = Buffer::local("abc\ndefghi\njkl", cx);
1916        let point = buffer.insert_empty_line(Point::new(1, 3), true, false, cx);
1917        assert_eq!(buffer.text(), "abc\ndef\n\n\nghi\njkl");
1918        assert_eq!(point, Point::new(3, 0));
1919        buffer
1920    });
1921
1922    // Insert empty line in the middle of a line, requesting empty line below only
1923    cx.new_model(|cx| {
1924        let mut buffer = Buffer::local("abc\ndefghi\njkl", cx);
1925        let point = buffer.insert_empty_line(Point::new(1, 3), false, true, cx);
1926        assert_eq!(buffer.text(), "abc\ndef\n\n\nghi\njkl");
1927        assert_eq!(point, Point::new(2, 0));
1928        buffer
1929    });
1930
1931    // Insert empty line at the end, requesting empty lines above and below
1932    cx.new_model(|cx| {
1933        let mut buffer = Buffer::local("abc\ndef\nghi", cx);
1934        let point = buffer.insert_empty_line(Point::new(2, 3), true, true, cx);
1935        assert_eq!(buffer.text(), "abc\ndef\nghi\n\n\n");
1936        assert_eq!(point, Point::new(4, 0));
1937        buffer
1938    });
1939
1940    // Insert empty line at the end, requesting empty line above only
1941    cx.new_model(|cx| {
1942        let mut buffer = Buffer::local("abc\ndef\nghi", cx);
1943        let point = buffer.insert_empty_line(Point::new(2, 3), true, false, cx);
1944        assert_eq!(buffer.text(), "abc\ndef\nghi\n\n");
1945        assert_eq!(point, Point::new(4, 0));
1946        buffer
1947    });
1948
1949    // Insert empty line at the end, requesting empty line below only
1950    cx.new_model(|cx| {
1951        let mut buffer = Buffer::local("abc\ndef\nghi", cx);
1952        let point = buffer.insert_empty_line(Point::new(2, 3), false, true, cx);
1953        assert_eq!(buffer.text(), "abc\ndef\nghi\n\n");
1954        assert_eq!(point, Point::new(3, 0));
1955        buffer
1956    });
1957}
1958
1959#[gpui::test]
1960fn test_language_scope_at_with_javascript(cx: &mut AppContext) {
1961    init_settings(cx, |_| {});
1962
1963    cx.new_model(|cx| {
1964        let language = Language::new(
1965            LanguageConfig {
1966                name: "JavaScript".into(),
1967                line_comments: vec!["// ".into()],
1968                brackets: BracketPairConfig {
1969                    pairs: vec![
1970                        BracketPair {
1971                            start: "{".into(),
1972                            end: "}".into(),
1973                            close: true,
1974                            surround: true,
1975                            newline: false,
1976                        },
1977                        BracketPair {
1978                            start: "'".into(),
1979                            end: "'".into(),
1980                            close: true,
1981                            surround: true,
1982                            newline: false,
1983                        },
1984                    ],
1985                    disabled_scopes_by_bracket_ix: vec![
1986                        Vec::new(), //
1987                        vec!["string".into()],
1988                    ],
1989                },
1990                overrides: [(
1991                    "element".into(),
1992                    LanguageConfigOverride {
1993                        line_comments: Override::Remove { remove: true },
1994                        block_comment: Override::Set(("{/*".into(), "*/}".into())),
1995                        ..Default::default()
1996                    },
1997                )]
1998                .into_iter()
1999                .collect(),
2000                ..Default::default()
2001            },
2002            Some(tree_sitter_typescript::language_tsx()),
2003        )
2004        .with_override_query(
2005            r#"
2006                (jsx_element) @element
2007                (string) @string
2008                [
2009                    (jsx_opening_element)
2010                    (jsx_closing_element)
2011                    (jsx_expression)
2012                ] @default
2013            "#,
2014        )
2015        .unwrap();
2016
2017        let text = r#"
2018            a["b"] = <C d="e">
2019                <F></F>
2020                { g() }
2021            </C>;
2022        "#
2023        .unindent();
2024
2025        let buffer = Buffer::local(&text, cx).with_language(Arc::new(language), cx);
2026        let snapshot = buffer.snapshot();
2027
2028        let config = snapshot.language_scope_at(0).unwrap();
2029        assert_eq!(config.line_comment_prefixes(), &[Arc::from("// ")]);
2030        // Both bracket pairs are enabled
2031        assert_eq!(
2032            config.brackets().map(|e| e.1).collect::<Vec<_>>(),
2033            &[true, true]
2034        );
2035
2036        let string_config = snapshot
2037            .language_scope_at(text.find("b\"").unwrap())
2038            .unwrap();
2039        assert_eq!(string_config.line_comment_prefixes(), &[Arc::from("// ")]);
2040        // Second bracket pair is disabled
2041        assert_eq!(
2042            string_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
2043            &[true, false]
2044        );
2045
2046        // In between JSX tags: use the `element` override.
2047        let element_config = snapshot
2048            .language_scope_at(text.find("<F>").unwrap())
2049            .unwrap();
2050        // TODO nested blocks after newlines are captured with all whitespaces
2051        // https://github.com/tree-sitter/tree-sitter-typescript/issues/306
2052        // assert_eq!(element_config.line_comment_prefixes(), &[]);
2053        // assert_eq!(
2054        //     element_config.block_comment_delimiters(),
2055        //     Some((&"{/*".into(), &"*/}".into()))
2056        // );
2057        assert_eq!(
2058            element_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
2059            &[true, true]
2060        );
2061
2062        // Within a JSX tag: use the default config.
2063        let tag_config = snapshot
2064            .language_scope_at(text.find(" d=").unwrap() + 1)
2065            .unwrap();
2066        assert_eq!(tag_config.line_comment_prefixes(), &[Arc::from("// ")]);
2067        assert_eq!(
2068            tag_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
2069            &[true, true]
2070        );
2071
2072        // In a JSX expression: use the default config.
2073        let expression_in_element_config = snapshot
2074            .language_scope_at(text.find('{').unwrap() + 1)
2075            .unwrap();
2076        assert_eq!(
2077            expression_in_element_config.line_comment_prefixes(),
2078            &[Arc::from("// ")]
2079        );
2080        assert_eq!(
2081            expression_in_element_config
2082                .brackets()
2083                .map(|e| e.1)
2084                .collect::<Vec<_>>(),
2085            &[true, true]
2086        );
2087
2088        buffer
2089    });
2090}
2091
2092#[gpui::test]
2093fn test_language_scope_at_with_rust(cx: &mut AppContext) {
2094    init_settings(cx, |_| {});
2095
2096    cx.new_model(|cx| {
2097        let language = Language::new(
2098            LanguageConfig {
2099                name: "Rust".into(),
2100                brackets: BracketPairConfig {
2101                    pairs: vec![
2102                        BracketPair {
2103                            start: "{".into(),
2104                            end: "}".into(),
2105                            close: true,
2106                            surround: true,
2107                            newline: false,
2108                        },
2109                        BracketPair {
2110                            start: "'".into(),
2111                            end: "'".into(),
2112                            close: true,
2113                            surround: true,
2114                            newline: false,
2115                        },
2116                    ],
2117                    disabled_scopes_by_bracket_ix: vec![
2118                        Vec::new(), //
2119                        vec!["string".into()],
2120                    ],
2121                },
2122                ..Default::default()
2123            },
2124            Some(tree_sitter_rust::language()),
2125        )
2126        .with_override_query(
2127            r#"
2128                (string_literal) @string
2129            "#,
2130        )
2131        .unwrap();
2132
2133        let text = r#"
2134            const S: &'static str = "hello";
2135        "#
2136        .unindent();
2137
2138        let buffer = Buffer::local(text.clone(), cx).with_language(Arc::new(language), cx);
2139        let snapshot = buffer.snapshot();
2140
2141        // By default, all brackets are enabled
2142        let config = snapshot.language_scope_at(0).unwrap();
2143        assert_eq!(
2144            config.brackets().map(|e| e.1).collect::<Vec<_>>(),
2145            &[true, true]
2146        );
2147
2148        // Within a string, the quotation brackets are disabled.
2149        let string_config = snapshot
2150            .language_scope_at(text.find("ello").unwrap())
2151            .unwrap();
2152        assert_eq!(
2153            string_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
2154            &[true, false]
2155        );
2156
2157        buffer
2158    });
2159}
2160
2161#[gpui::test]
2162fn test_language_scope_at_with_combined_injections(cx: &mut AppContext) {
2163    init_settings(cx, |_| {});
2164
2165    cx.new_model(|cx| {
2166        let text = r#"
2167            <ol>
2168            <% people.each do |person| %>
2169                <li>
2170                    <%= person.name %>
2171                </li>
2172            <% end %>
2173            </ol>
2174        "#
2175        .unindent();
2176
2177        let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
2178        language_registry.add(Arc::new(ruby_lang()));
2179        language_registry.add(Arc::new(html_lang()));
2180        language_registry.add(Arc::new(erb_lang()));
2181
2182        let mut buffer = Buffer::local(text, cx);
2183        buffer.set_language_registry(language_registry.clone());
2184        buffer.set_language(
2185            language_registry
2186                .language_for_name("ERB")
2187                .now_or_never()
2188                .unwrap()
2189                .ok(),
2190            cx,
2191        );
2192
2193        let snapshot = buffer.snapshot();
2194        let html_config = snapshot.language_scope_at(Point::new(2, 4)).unwrap();
2195        assert_eq!(html_config.line_comment_prefixes(), &[]);
2196        assert_eq!(
2197            html_config.block_comment_delimiters(),
2198            Some((&"<!--".into(), &"-->".into()))
2199        );
2200
2201        let ruby_config = snapshot.language_scope_at(Point::new(3, 12)).unwrap();
2202        assert_eq!(ruby_config.line_comment_prefixes(), &[Arc::from("# ")]);
2203        assert_eq!(ruby_config.block_comment_delimiters(), None);
2204
2205        buffer
2206    });
2207}
2208
2209#[gpui::test]
2210fn test_language_at_with_hidden_languages(cx: &mut AppContext) {
2211    init_settings(cx, |_| {});
2212
2213    cx.new_model(|cx| {
2214        let text = r#"
2215            this is an *emphasized* word.
2216        "#
2217        .unindent();
2218
2219        let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
2220        language_registry.add(Arc::new(markdown_lang()));
2221        language_registry.add(Arc::new(markdown_inline_lang()));
2222
2223        let mut buffer = Buffer::local(text, cx);
2224        buffer.set_language_registry(language_registry.clone());
2225        buffer.set_language(
2226            language_registry
2227                .language_for_name("Markdown")
2228                .now_or_never()
2229                .unwrap()
2230                .ok(),
2231            cx,
2232        );
2233
2234        let snapshot = buffer.snapshot();
2235
2236        for point in [Point::new(0, 4), Point::new(0, 16)] {
2237            let config = snapshot.language_scope_at(point).unwrap();
2238            assert_eq!(config.language_name(), "Markdown".into());
2239
2240            let language = snapshot.language_at(point).unwrap();
2241            assert_eq!(language.name().0.as_ref(), "Markdown");
2242        }
2243
2244        buffer
2245    });
2246}
2247
2248#[gpui::test]
2249fn test_serialization(cx: &mut gpui::AppContext) {
2250    let mut now = Instant::now();
2251
2252    let buffer1 = cx.new_model(|cx| {
2253        let mut buffer = Buffer::local("abc", cx);
2254        buffer.edit([(3..3, "D")], None, cx);
2255
2256        now += Duration::from_secs(1);
2257        buffer.start_transaction_at(now);
2258        buffer.edit([(4..4, "E")], None, cx);
2259        buffer.end_transaction_at(now, cx);
2260        assert_eq!(buffer.text(), "abcDE");
2261
2262        buffer.undo(cx);
2263        assert_eq!(buffer.text(), "abcD");
2264
2265        buffer.edit([(4..4, "F")], None, cx);
2266        assert_eq!(buffer.text(), "abcDF");
2267        buffer
2268    });
2269    assert_eq!(buffer1.read(cx).text(), "abcDF");
2270
2271    let state = buffer1.read(cx).to_proto(cx);
2272    let ops = cx
2273        .background_executor()
2274        .block(buffer1.read(cx).serialize_ops(None, cx));
2275    let buffer2 = cx.new_model(|cx| {
2276        let mut buffer = Buffer::from_proto(1, Capability::ReadWrite, state, None).unwrap();
2277        buffer
2278            .apply_ops(
2279                ops.into_iter()
2280                    .map(|op| proto::deserialize_operation(op).unwrap()),
2281                cx,
2282            )
2283            .unwrap();
2284        buffer
2285    });
2286    assert_eq!(buffer2.read(cx).text(), "abcDF");
2287}
2288
2289#[gpui::test]
2290async fn test_find_matching_indent(cx: &mut TestAppContext) {
2291    cx.update(|cx| init_settings(cx, |_| {}));
2292
2293    async fn enclosing_indent(
2294        text: impl Into<String>,
2295        buffer_row: u32,
2296        cx: &mut TestAppContext,
2297    ) -> Option<(Range<u32>, LineIndent)> {
2298        let buffer = cx.new_model(|cx| Buffer::local(text, cx));
2299        let snapshot = cx.read(|cx| buffer.read(cx).snapshot());
2300        snapshot.enclosing_indent(buffer_row).await
2301    }
2302
2303    assert_eq!(
2304        enclosing_indent(
2305            "
2306        fn b() {
2307            if c {
2308                let d = 2;
2309            }
2310        }"
2311            .unindent(),
2312            1,
2313            cx,
2314        )
2315        .await,
2316        Some((
2317            1..2,
2318            LineIndent {
2319                tabs: 0,
2320                spaces: 4,
2321                line_blank: false,
2322            }
2323        ))
2324    );
2325
2326    assert_eq!(
2327        enclosing_indent(
2328            "
2329        fn b() {
2330            if c {
2331                let d = 2;
2332            }
2333        }"
2334            .unindent(),
2335            2,
2336            cx,
2337        )
2338        .await,
2339        Some((
2340            1..2,
2341            LineIndent {
2342                tabs: 0,
2343                spaces: 4,
2344                line_blank: false,
2345            }
2346        ))
2347    );
2348
2349    assert_eq!(
2350        enclosing_indent(
2351            "
2352        fn b() {
2353            if c {
2354                let d = 2;
2355
2356                let e = 5;
2357            }
2358        }"
2359            .unindent(),
2360            3,
2361            cx,
2362        )
2363        .await,
2364        Some((
2365            1..4,
2366            LineIndent {
2367                tabs: 0,
2368                spaces: 4,
2369                line_blank: false,
2370            }
2371        ))
2372    );
2373}
2374
2375#[gpui::test(iterations = 100)]
2376fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
2377    let min_peers = env::var("MIN_PEERS")
2378        .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
2379        .unwrap_or(1);
2380    let max_peers = env::var("MAX_PEERS")
2381        .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
2382        .unwrap_or(5);
2383    let operations = env::var("OPERATIONS")
2384        .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
2385        .unwrap_or(10);
2386
2387    let base_text_len = rng.gen_range(0..10);
2388    let base_text = RandomCharIter::new(&mut rng)
2389        .take(base_text_len)
2390        .collect::<String>();
2391    let mut replica_ids = Vec::new();
2392    let mut buffers = Vec::new();
2393    let network = Arc::new(Mutex::new(Network::new(rng.clone())));
2394    let base_buffer = cx.new_model(|cx| Buffer::local(base_text.as_str(), cx));
2395
2396    for i in 0..rng.gen_range(min_peers..=max_peers) {
2397        let buffer = cx.new_model(|cx| {
2398            let state = base_buffer.read(cx).to_proto(cx);
2399            let ops = cx
2400                .background_executor()
2401                .block(base_buffer.read(cx).serialize_ops(None, cx));
2402            let mut buffer =
2403                Buffer::from_proto(i as ReplicaId, Capability::ReadWrite, state, None).unwrap();
2404            buffer
2405                .apply_ops(
2406                    ops.into_iter()
2407                        .map(|op| proto::deserialize_operation(op).unwrap()),
2408                    cx,
2409                )
2410                .unwrap();
2411            buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
2412            let network = network.clone();
2413            cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
2414                if let Event::Operation(op) = event {
2415                    network
2416                        .lock()
2417                        .broadcast(buffer.replica_id(), vec![proto::serialize_operation(op)]);
2418                }
2419            })
2420            .detach();
2421            buffer
2422        });
2423
2424        buffers.push(buffer);
2425        replica_ids.push(i as ReplicaId);
2426        network.lock().add_peer(i as ReplicaId);
2427        log::info!("Adding initial peer with replica id {}", i);
2428    }
2429
2430    log::info!("initial text: {:?}", base_text);
2431
2432    let mut now = Instant::now();
2433    let mut mutation_count = operations;
2434    let mut next_diagnostic_id = 0;
2435    let mut active_selections = BTreeMap::default();
2436    loop {
2437        let replica_index = rng.gen_range(0..replica_ids.len());
2438        let replica_id = replica_ids[replica_index];
2439        let buffer = &mut buffers[replica_index];
2440        let mut new_buffer = None;
2441        match rng.gen_range(0..100) {
2442            0..=29 if mutation_count != 0 => {
2443                buffer.update(cx, |buffer, cx| {
2444                    buffer.start_transaction_at(now);
2445                    buffer.randomly_edit(&mut rng, 5, cx);
2446                    buffer.end_transaction_at(now, cx);
2447                    log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
2448                });
2449                mutation_count -= 1;
2450            }
2451            30..=39 if mutation_count != 0 => {
2452                buffer.update(cx, |buffer, cx| {
2453                    if rng.gen_bool(0.2) {
2454                        log::info!("peer {} clearing active selections", replica_id);
2455                        active_selections.remove(&replica_id);
2456                        buffer.remove_active_selections(cx);
2457                    } else {
2458                        let mut selections = Vec::new();
2459                        for id in 0..rng.gen_range(1..=5) {
2460                            let range = buffer.random_byte_range(0, &mut rng);
2461                            selections.push(Selection {
2462                                id,
2463                                start: buffer.anchor_before(range.start),
2464                                end: buffer.anchor_before(range.end),
2465                                reversed: false,
2466                                goal: SelectionGoal::None,
2467                            });
2468                        }
2469                        let selections: Arc<[Selection<Anchor>]> = selections.into();
2470                        log::info!(
2471                            "peer {} setting active selections: {:?}",
2472                            replica_id,
2473                            selections
2474                        );
2475                        active_selections.insert(replica_id, selections.clone());
2476                        buffer.set_active_selections(selections, false, Default::default(), cx);
2477                    }
2478                });
2479                mutation_count -= 1;
2480            }
2481            40..=49 if mutation_count != 0 && replica_id == 0 => {
2482                let entry_count = rng.gen_range(1..=5);
2483                buffer.update(cx, |buffer, cx| {
2484                    let diagnostics = DiagnosticSet::new(
2485                        (0..entry_count).map(|_| {
2486                            let range = buffer.random_byte_range(0, &mut rng);
2487                            let range = range.to_point_utf16(buffer);
2488                            let range = range.start..range.end;
2489                            DiagnosticEntry {
2490                                range,
2491                                diagnostic: Diagnostic {
2492                                    message: post_inc(&mut next_diagnostic_id).to_string(),
2493                                    ..Default::default()
2494                                },
2495                            }
2496                        }),
2497                        buffer,
2498                    );
2499                    log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
2500                    buffer.update_diagnostics(LanguageServerId(0), diagnostics, cx);
2501                });
2502                mutation_count -= 1;
2503            }
2504            50..=59 if replica_ids.len() < max_peers => {
2505                let old_buffer_state = buffer.read(cx).to_proto(cx);
2506                let old_buffer_ops = cx
2507                    .background_executor()
2508                    .block(buffer.read(cx).serialize_ops(None, cx));
2509                let new_replica_id = (0..=replica_ids.len() as ReplicaId)
2510                    .filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
2511                    .choose(&mut rng)
2512                    .unwrap();
2513                log::info!(
2514                    "Adding new replica {} (replicating from {})",
2515                    new_replica_id,
2516                    replica_id
2517                );
2518                new_buffer = Some(cx.new_model(|cx| {
2519                    let mut new_buffer = Buffer::from_proto(
2520                        new_replica_id,
2521                        Capability::ReadWrite,
2522                        old_buffer_state,
2523                        None,
2524                    )
2525                    .unwrap();
2526                    new_buffer
2527                        .apply_ops(
2528                            old_buffer_ops
2529                                .into_iter()
2530                                .map(|op| deserialize_operation(op).unwrap()),
2531                            cx,
2532                        )
2533                        .unwrap();
2534                    log::info!(
2535                        "New replica {} text: {:?}",
2536                        new_buffer.replica_id(),
2537                        new_buffer.text()
2538                    );
2539                    new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
2540                    let network = network.clone();
2541                    cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
2542                        if let Event::Operation(op) = event {
2543                            network.lock().broadcast(
2544                                buffer.replica_id(),
2545                                vec![proto::serialize_operation(op)],
2546                            );
2547                        }
2548                    })
2549                    .detach();
2550                    new_buffer
2551                }));
2552                network.lock().replicate(replica_id, new_replica_id);
2553
2554                if new_replica_id as usize == replica_ids.len() {
2555                    replica_ids.push(new_replica_id);
2556                } else {
2557                    let new_buffer = new_buffer.take().unwrap();
2558                    while network.lock().has_unreceived(new_replica_id) {
2559                        let ops = network
2560                            .lock()
2561                            .receive(new_replica_id)
2562                            .into_iter()
2563                            .map(|op| proto::deserialize_operation(op).unwrap());
2564                        if ops.len() > 0 {
2565                            log::info!(
2566                                "peer {} (version: {:?}) applying {} ops from the network. {:?}",
2567                                new_replica_id,
2568                                buffer.read(cx).version(),
2569                                ops.len(),
2570                                ops
2571                            );
2572                            new_buffer.update(cx, |new_buffer, cx| {
2573                                new_buffer.apply_ops(ops, cx).unwrap();
2574                            });
2575                        }
2576                    }
2577                    buffers[new_replica_id as usize] = new_buffer;
2578                }
2579            }
2580            60..=69 if mutation_count != 0 => {
2581                buffer.update(cx, |buffer, cx| {
2582                    buffer.randomly_undo_redo(&mut rng, cx);
2583                    log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
2584                });
2585                mutation_count -= 1;
2586            }
2587            _ if network.lock().has_unreceived(replica_id) => {
2588                let ops = network
2589                    .lock()
2590                    .receive(replica_id)
2591                    .into_iter()
2592                    .map(|op| proto::deserialize_operation(op).unwrap());
2593                if ops.len() > 0 {
2594                    log::info!(
2595                        "peer {} (version: {:?}) applying {} ops from the network. {:?}",
2596                        replica_id,
2597                        buffer.read(cx).version(),
2598                        ops.len(),
2599                        ops
2600                    );
2601                    buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
2602                }
2603            }
2604            _ => {}
2605        }
2606
2607        now += Duration::from_millis(rng.gen_range(0..=200));
2608        buffers.extend(new_buffer);
2609
2610        for buffer in &buffers {
2611            buffer.read(cx).check_invariants();
2612        }
2613
2614        if mutation_count == 0 && network.lock().is_idle() {
2615            break;
2616        }
2617    }
2618
2619    let first_buffer = buffers[0].read(cx).snapshot();
2620    for buffer in &buffers[1..] {
2621        let buffer = buffer.read(cx).snapshot();
2622        assert_eq!(
2623            buffer.version(),
2624            first_buffer.version(),
2625            "Replica {} version != Replica 0 version",
2626            buffer.replica_id()
2627        );
2628        assert_eq!(
2629            buffer.text(),
2630            first_buffer.text(),
2631            "Replica {} text != Replica 0 text",
2632            buffer.replica_id()
2633        );
2634        assert_eq!(
2635            buffer
2636                .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
2637                .collect::<Vec<_>>(),
2638            first_buffer
2639                .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
2640                .collect::<Vec<_>>(),
2641            "Replica {} diagnostics != Replica 0 diagnostics",
2642            buffer.replica_id()
2643        );
2644    }
2645
2646    for buffer in &buffers {
2647        let buffer = buffer.read(cx).snapshot();
2648        let actual_remote_selections = buffer
2649            .selections_in_range(Anchor::MIN..Anchor::MAX, false)
2650            .map(|(replica_id, _, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
2651            .collect::<Vec<_>>();
2652        let expected_remote_selections = active_selections
2653            .iter()
2654            .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
2655            .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
2656            .collect::<Vec<_>>();
2657        assert_eq!(
2658            actual_remote_selections,
2659            expected_remote_selections,
2660            "Replica {} remote selections != expected selections",
2661            buffer.replica_id()
2662        );
2663    }
2664}
2665
2666#[test]
2667fn test_contiguous_ranges() {
2668    assert_eq!(
2669        contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
2670        &[1..4, 5..7, 9..13]
2671    );
2672
2673    // Respects the `max_len` parameter
2674    assert_eq!(
2675        contiguous_ranges(
2676            [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
2677            3
2678        )
2679        .collect::<Vec<_>>(),
2680        &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
2681    );
2682}
2683
2684#[gpui::test(iterations = 500)]
2685fn test_trailing_whitespace_ranges(mut rng: StdRng) {
2686    // Generate a random multi-line string containing
2687    // some lines with trailing whitespace.
2688    let mut text = String::new();
2689    for _ in 0..rng.gen_range(0..16) {
2690        for _ in 0..rng.gen_range(0..36) {
2691            text.push(match rng.gen_range(0..10) {
2692                0..=1 => ' ',
2693                3 => '\t',
2694                _ => rng.gen_range('a'..='z'),
2695            });
2696        }
2697        text.push('\n');
2698    }
2699
2700    match rng.gen_range(0..10) {
2701        // sometimes remove the last newline
2702        0..=1 => drop(text.pop()), //
2703
2704        // sometimes add extra newlines
2705        2..=3 => text.push_str(&"\n".repeat(rng.gen_range(1..5))),
2706        _ => {}
2707    }
2708
2709    let rope = Rope::from(text.as_str());
2710    let actual_ranges = trailing_whitespace_ranges(&rope);
2711    let expected_ranges = TRAILING_WHITESPACE_REGEX
2712        .find_iter(&text)
2713        .map(|m| m.range())
2714        .collect::<Vec<_>>();
2715    assert_eq!(
2716        actual_ranges,
2717        expected_ranges,
2718        "wrong ranges for text lines:\n{:?}",
2719        text.split('\n').collect::<Vec<_>>()
2720    );
2721}
2722
2723fn ruby_lang() -> Language {
2724    Language::new(
2725        LanguageConfig {
2726            name: "Ruby".into(),
2727            matcher: LanguageMatcher {
2728                path_suffixes: vec!["rb".to_string()],
2729                ..Default::default()
2730            },
2731            line_comments: vec!["# ".into()],
2732            ..Default::default()
2733        },
2734        Some(tree_sitter_ruby::language()),
2735    )
2736    .with_indents_query(
2737        r#"
2738            (class "end" @end) @indent
2739            (method "end" @end) @indent
2740            (rescue) @outdent
2741            (then) @indent
2742        "#,
2743    )
2744    .unwrap()
2745}
2746
2747fn html_lang() -> Language {
2748    Language::new(
2749        LanguageConfig {
2750            name: LanguageName::new("HTML"),
2751            block_comment: Some(("<!--".into(), "-->".into())),
2752            ..Default::default()
2753        },
2754        Some(tree_sitter_html::language()),
2755    )
2756    .with_indents_query(
2757        "
2758        (element
2759          (start_tag) @start
2760          (end_tag)? @end) @indent
2761        ",
2762    )
2763    .unwrap()
2764    .with_injection_query(
2765        r#"
2766        (script_element
2767            (raw_text) @content
2768            (#set! "language" "javascript"))
2769        "#,
2770    )
2771    .unwrap()
2772}
2773
2774fn erb_lang() -> Language {
2775    Language::new(
2776        LanguageConfig {
2777            name: "ERB".into(),
2778            matcher: LanguageMatcher {
2779                path_suffixes: vec!["erb".to_string()],
2780                ..Default::default()
2781            },
2782            block_comment: Some(("<%#".into(), "%>".into())),
2783            ..Default::default()
2784        },
2785        Some(tree_sitter_embedded_template::language()),
2786    )
2787    .with_injection_query(
2788        r#"
2789            (
2790                (code) @content
2791                (#set! "language" "ruby")
2792                (#set! "combined")
2793            )
2794
2795            (
2796                (content) @content
2797                (#set! "language" "html")
2798                (#set! "combined")
2799            )
2800        "#,
2801    )
2802    .unwrap()
2803}
2804
2805fn rust_lang() -> Language {
2806    Language::new(
2807        LanguageConfig {
2808            name: "Rust".into(),
2809            matcher: LanguageMatcher {
2810                path_suffixes: vec!["rs".to_string()],
2811                ..Default::default()
2812            },
2813            ..Default::default()
2814        },
2815        Some(tree_sitter_rust::language()),
2816    )
2817    .with_indents_query(
2818        r#"
2819        (call_expression) @indent
2820        (field_expression) @indent
2821        (_ "(" ")" @end) @indent
2822        (_ "{" "}" @end) @indent
2823        "#,
2824    )
2825    .unwrap()
2826    .with_brackets_query(
2827        r#"
2828        ("{" @open "}" @close)
2829        "#,
2830    )
2831    .unwrap()
2832    .with_outline_query(
2833        r#"
2834        (line_comment) @annotation
2835
2836        (struct_item
2837            "struct" @context
2838            name: (_) @name) @item
2839        (enum_item
2840            "enum" @context
2841            name: (_) @name) @item
2842        (enum_variant
2843            name: (_) @name) @item
2844        (field_declaration
2845            name: (_) @name) @item
2846        (impl_item
2847            "impl" @context
2848            trait: (_)? @name
2849            "for"? @context
2850            type: (_) @name
2851            body: (_ "{" (_)* "}")) @item
2852        (function_item
2853            "fn" @context
2854            name: (_) @name) @item
2855        (mod_item
2856            "mod" @context
2857            name: (_) @name) @item
2858        "#,
2859    )
2860    .unwrap()
2861}
2862
2863fn json_lang() -> Language {
2864    Language::new(
2865        LanguageConfig {
2866            name: "Json".into(),
2867            matcher: LanguageMatcher {
2868                path_suffixes: vec!["js".to_string()],
2869                ..Default::default()
2870            },
2871            ..Default::default()
2872        },
2873        Some(tree_sitter_json::language()),
2874    )
2875}
2876
2877fn javascript_lang() -> Language {
2878    Language::new(
2879        LanguageConfig {
2880            name: "JavaScript".into(),
2881            ..Default::default()
2882        },
2883        Some(tree_sitter_typescript::language_tsx()),
2884    )
2885    .with_brackets_query(
2886        r#"
2887        ("{" @open "}" @close)
2888        ("(" @open ")" @close)
2889        "#,
2890    )
2891    .unwrap()
2892    .with_indents_query(
2893        r#"
2894        (object "}" @end) @indent
2895        "#,
2896    )
2897    .unwrap()
2898}
2899
2900pub fn markdown_lang() -> Language {
2901    Language::new(
2902        LanguageConfig {
2903            name: "Markdown".into(),
2904            matcher: LanguageMatcher {
2905                path_suffixes: vec!["md".into()],
2906                ..Default::default()
2907            },
2908            ..Default::default()
2909        },
2910        Some(tree_sitter_md::language()),
2911    )
2912    .with_injection_query(
2913        r#"
2914            (fenced_code_block
2915                (info_string
2916                    (language) @language)
2917                (code_fence_content) @content)
2918
2919            ((inline) @content
2920                (#set! "language" "markdown-inline"))
2921        "#,
2922    )
2923    .unwrap()
2924}
2925
2926pub fn markdown_inline_lang() -> Language {
2927    Language::new(
2928        LanguageConfig {
2929            name: "Markdown-Inline".into(),
2930            hidden: true,
2931            ..LanguageConfig::default()
2932        },
2933        Some(tree_sitter_md::inline_language()),
2934    )
2935    .with_highlights_query("(emphasis) @emphasis")
2936    .unwrap()
2937}
2938
2939fn get_tree_sexp(buffer: &Model<Buffer>, cx: &mut gpui::TestAppContext) -> String {
2940    buffer.update(cx, |buffer, _| {
2941        let snapshot = buffer.snapshot();
2942        let layers = snapshot.syntax.layers(buffer.as_text_snapshot());
2943        layers[0].node().to_sexp()
2944    })
2945}
2946
2947// Assert that the enclosing bracket ranges around the selection match the pairs indicated by the marked text in `range_markers`
2948fn assert_bracket_pairs(
2949    selection_text: &'static str,
2950    bracket_pair_texts: Vec<&'static str>,
2951    language: Language,
2952    cx: &mut AppContext,
2953) {
2954    let (expected_text, selection_ranges) = marked_text_ranges(selection_text, false);
2955    let buffer = cx.new_model(|cx| {
2956        Buffer::local(expected_text.clone(), cx).with_language(Arc::new(language), cx)
2957    });
2958    let buffer = buffer.update(cx, |buffer, _cx| buffer.snapshot());
2959
2960    let selection_range = selection_ranges[0].clone();
2961
2962    let bracket_pairs = bracket_pair_texts
2963        .into_iter()
2964        .map(|pair_text| {
2965            let (bracket_text, ranges) = marked_text_ranges(pair_text, false);
2966            assert_eq!(bracket_text, expected_text);
2967            (ranges[0].clone(), ranges[1].clone())
2968        })
2969        .collect::<Vec<_>>();
2970
2971    assert_set_eq!(
2972        buffer.bracket_ranges(selection_range).collect::<Vec<_>>(),
2973        bracket_pairs
2974    );
2975}
2976
2977fn init_settings(cx: &mut AppContext, f: fn(&mut AllLanguageSettingsContent)) {
2978    let settings_store = SettingsStore::test(cx);
2979    cx.set_global(settings_store);
2980    crate::init(cx);
2981    cx.update_global::<SettingsStore, _>(|settings, cx| {
2982        settings.update_user_settings::<AllLanguageSettings>(cx, f);
2983    });
2984}