buffer_tests.rs

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