buffer_tests.rs

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