buffer_tests.rs

   1use super::*;
   2use crate::Buffer;
   3use clock::ReplicaId;
   4use collections::BTreeMap;
   5use futures::FutureExt as _;
   6use gpui::{App, AppContext as _, BorrowAppContext, Entity};
   7use gpui::{HighlightStyle, TestAppContext};
   8use indoc::indoc;
   9use pretty_assertions::assert_eq;
  10use proto::deserialize_operation;
  11use rand::prelude::*;
  12use regex::RegexBuilder;
  13use settings::SettingsStore;
  14use settings::{AllLanguageSettingsContent, LanguageSettingsContent};
  15use std::collections::BTreeSet;
  16use std::fmt::Write as _;
  17use std::{
  18    env,
  19    ops::Range,
  20    sync::LazyLock,
  21    time::{Duration, Instant},
  22};
  23use syntax_map::TreeSitterOptions;
  24use text::network::Network;
  25use text::{BufferId, LineEnding};
  26use text::{Point, ToPoint};
  27use theme::ActiveTheme;
  28use unindent::Unindent as _;
  29use util::rel_path::rel_path;
  30use util::test::marked_text_offsets;
  31use util::{RandomCharIter, assert_set_eq, post_inc, test::marked_text_ranges};
  32
  33pub static TRAILING_WHITESPACE_REGEX: LazyLock<regex::Regex> = LazyLock::new(|| {
  34    RegexBuilder::new(r"[ \t]+$")
  35        .multi_line(true)
  36        .build()
  37        .expect("Failed to create TRAILING_WHITESPACE_REGEX")
  38});
  39
  40#[cfg(test)]
  41#[ctor::ctor]
  42fn init_logger() {
  43    zlog::init_test();
  44}
  45
  46#[gpui::test]
  47fn test_line_endings(cx: &mut gpui::App) {
  48    init_settings(cx, |_| {});
  49
  50    cx.new(|cx| {
  51        let mut buffer = Buffer::local("one\r\ntwo\rthree", cx).with_language(rust_lang(), cx);
  52        assert_eq!(buffer.text(), "one\ntwo\nthree");
  53        assert_eq!(buffer.line_ending(), LineEnding::Windows);
  54
  55        buffer.check_invariants();
  56        buffer.edit(
  57            [(buffer.len()..buffer.len(), "\r\nfour")],
  58            Some(AutoindentMode::EachLine),
  59            cx,
  60        );
  61        buffer.edit([(0..0, "zero\r\n")], None, cx);
  62        assert_eq!(buffer.text(), "zero\none\ntwo\nthree\nfour");
  63        assert_eq!(buffer.line_ending(), LineEnding::Windows);
  64        buffer.check_invariants();
  65
  66        buffer
  67    });
  68}
  69
  70#[gpui::test]
  71fn test_set_line_ending(cx: &mut TestAppContext) {
  72    let base = cx.new(|cx| Buffer::local("one\ntwo\nthree\n", cx));
  73    let base_replica = cx.new(|cx| {
  74        Buffer::from_proto(
  75            ReplicaId::new(1),
  76            Capability::ReadWrite,
  77            base.read(cx).to_proto(cx),
  78            None,
  79        )
  80        .unwrap()
  81    });
  82    base.update(cx, |_buffer, cx| {
  83        cx.subscribe(&base_replica, |this, _, event, cx| {
  84            if let BufferEvent::Operation {
  85                operation,
  86                is_local: true,
  87            } = event
  88            {
  89                this.apply_ops([operation.clone()], cx);
  90            }
  91        })
  92        .detach();
  93    });
  94    base_replica.update(cx, |_buffer, cx| {
  95        cx.subscribe(&base, |this, _, event, cx| {
  96            if let BufferEvent::Operation {
  97                operation,
  98                is_local: true,
  99            } = event
 100            {
 101                this.apply_ops([operation.clone()], cx);
 102            }
 103        })
 104        .detach();
 105    });
 106
 107    // Base
 108    base_replica.read_with(cx, |buffer, _| {
 109        assert_eq!(buffer.line_ending(), LineEnding::Unix);
 110    });
 111    base.update(cx, |buffer, cx| {
 112        assert_eq!(buffer.line_ending(), LineEnding::Unix);
 113        buffer.set_line_ending(LineEnding::Windows, cx);
 114        assert_eq!(buffer.line_ending(), LineEnding::Windows);
 115    });
 116    base_replica.read_with(cx, |buffer, _| {
 117        assert_eq!(buffer.line_ending(), LineEnding::Windows);
 118    });
 119    base.update(cx, |buffer, cx| {
 120        buffer.set_line_ending(LineEnding::Unix, cx);
 121        assert_eq!(buffer.line_ending(), LineEnding::Unix);
 122    });
 123    base_replica.read_with(cx, |buffer, _| {
 124        assert_eq!(buffer.line_ending(), LineEnding::Unix);
 125    });
 126
 127    // Replica
 128    base.read_with(cx, |buffer, _| {
 129        assert_eq!(buffer.line_ending(), LineEnding::Unix);
 130    });
 131    base_replica.update(cx, |buffer, cx| {
 132        assert_eq!(buffer.line_ending(), LineEnding::Unix);
 133        buffer.set_line_ending(LineEnding::Windows, cx);
 134        assert_eq!(buffer.line_ending(), LineEnding::Windows);
 135    });
 136    base.read_with(cx, |buffer, _| {
 137        assert_eq!(buffer.line_ending(), LineEnding::Windows);
 138    });
 139    base_replica.update(cx, |buffer, cx| {
 140        buffer.set_line_ending(LineEnding::Unix, cx);
 141        assert_eq!(buffer.line_ending(), LineEnding::Unix);
 142    });
 143    base.read_with(cx, |buffer, _| {
 144        assert_eq!(buffer.line_ending(), LineEnding::Unix);
 145    });
 146}
 147
 148#[gpui::test]
 149fn test_select_language(cx: &mut App) {
 150    init_settings(cx, |_| {});
 151
 152    let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
 153    registry.add(Arc::new(Language::new(
 154        LanguageConfig {
 155            name: LanguageName::new_static("Rust"),
 156            matcher: LanguageMatcher {
 157                path_suffixes: vec!["rs".to_string()],
 158                ..Default::default()
 159            },
 160            ..Default::default()
 161        },
 162        Some(tree_sitter_rust::LANGUAGE.into()),
 163    )));
 164    registry.add(Arc::new(Language::new(
 165        LanguageConfig {
 166            name: "Rust with longer extension".into(),
 167            matcher: LanguageMatcher {
 168                path_suffixes: vec!["longer.rs".to_string()],
 169                ..Default::default()
 170            },
 171            ..Default::default()
 172        },
 173        Some(tree_sitter_rust::LANGUAGE.into()),
 174    )));
 175    registry.add(Arc::new(Language::new(
 176        LanguageConfig {
 177            name: LanguageName::new_static("Make"),
 178            matcher: LanguageMatcher {
 179                path_suffixes: vec!["Makefile".to_string(), "mk".to_string()],
 180                ..Default::default()
 181            },
 182            ..Default::default()
 183        },
 184        Some(tree_sitter_rust::LANGUAGE.into()),
 185    )));
 186
 187    // matching file extension
 188    assert_eq!(
 189        registry
 190            .language_for_file(&file("src/lib.rs"), None, cx)
 191            .map(|l| l.name()),
 192        Some("Rust".into())
 193    );
 194    assert_eq!(
 195        registry
 196            .language_for_file(&file("src/lib.mk"), None, cx)
 197            .map(|l| l.name()),
 198        Some("Make".into())
 199    );
 200
 201    // matching longer, compound extension, part of which could also match another lang
 202    assert_eq!(
 203        registry
 204            .language_for_file(&file("src/lib.longer.rs"), None, cx)
 205            .map(|l| l.name()),
 206        Some("Rust with longer extension".into())
 207    );
 208
 209    // matching filename
 210    assert_eq!(
 211        registry
 212            .language_for_file(&file("src/Makefile"), None, cx)
 213            .map(|l| l.name()),
 214        Some("Make".into())
 215    );
 216
 217    // matching suffix that is not the full file extension or filename
 218    assert_eq!(
 219        registry
 220            .language_for_file(&file("zed/cars"), None, cx)
 221            .map(|l| l.name()),
 222        None
 223    );
 224    assert_eq!(
 225        registry
 226            .language_for_file(&file("zed/a.cars"), None, cx)
 227            .map(|l| l.name()),
 228        None
 229    );
 230    assert_eq!(
 231        registry
 232            .language_for_file(&file("zed/sumk"), None, cx)
 233            .map(|l| l.name()),
 234        None
 235    );
 236}
 237
 238#[gpui::test(iterations = 10)]
 239async fn test_first_line_pattern(cx: &mut TestAppContext) {
 240    cx.update(|cx| init_settings(cx, |_| {}));
 241
 242    let languages = LanguageRegistry::test(cx.executor());
 243    let languages = Arc::new(languages);
 244
 245    languages.register_test_language(LanguageConfig {
 246        name: "JavaScript".into(),
 247        matcher: LanguageMatcher {
 248            path_suffixes: vec!["js".into()],
 249            first_line_pattern: Some(Regex::new(r"\bnode\b").unwrap()),
 250        },
 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_large_block(cx: &mut App) {
1375    // Build a buffer with an impl block large enough that the distance between
1376    // `{` and `}` exceeds MAX_BYTES_TO_QUERY (16 KB). Each comment line is
1377    // ~24 bytes, so ~700 lines push us past the limit.
1378    let comment_line_count = 1000;
1379    let mut source = String::from("impl Foo {\n");
1380    for i in 0..comment_line_count {
1381        writeln!(source, "    // line {i:04}  padding").unwrap();
1382    }
1383    source.push_str("}\n");
1384
1385    let buffer = cx.new(|cx| Buffer::local(source.clone(), cx).with_language(rust_lang(), cx));
1386    let snapshot = buffer.update(cx, |buffer, _cx| buffer.snapshot());
1387
1388    let open_brace = source.find('{').unwrap();
1389    let close_brace = source.rfind('}').unwrap();
1390
1391    // Cursor right after the opening brace — should find the enclosing pair.
1392    let cursor = open_brace + 1;
1393    let pairs = snapshot
1394        .enclosing_bracket_ranges(cursor..cursor)
1395        .map(|pair| (pair.open_range, pair.close_range))
1396        .collect::<Vec<_>>();
1397    assert_eq!(
1398        pairs,
1399        vec![(open_brace..open_brace + 1, close_brace..close_brace + 1)],
1400        "enclosing_bracket_ranges should find the bracket pair even when \
1401         open and close are in different row-chunks"
1402    );
1403
1404    // Cursor at the opening brace itself.
1405    let pairs = snapshot
1406        .enclosing_bracket_ranges(open_brace..open_brace)
1407        .map(|pair| (pair.open_range, pair.close_range))
1408        .collect::<Vec<_>>();
1409    assert_eq!(
1410        pairs,
1411        vec![(open_brace..open_brace + 1, close_brace..close_brace + 1)],
1412        "cursor at the opening brace should also find the pair"
1413    );
1414
1415    // Cursor somewhere in the middle of the block.
1416    let middle = source.len() / 2;
1417    let pairs = snapshot
1418        .enclosing_bracket_ranges(middle..middle)
1419        .map(|pair| (pair.open_range, pair.close_range))
1420        .collect::<Vec<_>>();
1421    assert_eq!(
1422        pairs,
1423        vec![(open_brace..open_brace + 1, close_brace..close_brace + 1)],
1424        "cursor in the middle of a large block should find the enclosing pair"
1425    );
1426}
1427
1428#[gpui::test]
1429fn test_enclosing_bracket_ranges_where_brackets_are_not_outermost_children(cx: &mut App) {
1430    let mut assert = |selection_text, bracket_pair_texts| {
1431        assert_bracket_pairs(
1432            selection_text,
1433            bracket_pair_texts,
1434            Arc::new(javascript_lang()),
1435            cx,
1436        )
1437    };
1438
1439    assert(
1440        indoc! {"
1441        for (const a in b)ˇ {
1442            // a comment that's longer than the for-loop header
1443        }"},
1444        vec![indoc! {"
1445        for «(»const a in b«)» {
1446            // a comment that's longer than the for-loop header
1447        }"}],
1448    );
1449
1450    // Regression test: even though the parent node of the parentheses (the for loop) does
1451    // intersect the given range, the parentheses themselves do not contain the range, so
1452    // they should not be returned. Only the curly braces contain the range.
1453    assert(
1454        indoc! {"
1455        for (const a in b) {ˇ
1456            // a comment that's longer than the for-loop header
1457        }"},
1458        vec![indoc! {"
1459        for (const a in b) «{»
1460            // a comment that's longer than the for-loop header
1461        «}»"}],
1462    );
1463}
1464
1465#[gpui::test]
1466fn test_range_for_syntax_ancestor(cx: &mut App) {
1467    cx.new(|cx| {
1468        let text = "fn a() { b(|c| {}) }";
1469        let buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
1470        let snapshot = buffer.snapshot();
1471
1472        assert_eq!(
1473            snapshot
1474                .syntax_ancestor(empty_range_at(text, "|"))
1475                .unwrap()
1476                .byte_range(),
1477            range_of(text, "|")
1478        );
1479        assert_eq!(
1480            snapshot
1481                .syntax_ancestor(range_of(text, "|"))
1482                .unwrap()
1483                .byte_range(),
1484            range_of(text, "|c|")
1485        );
1486        assert_eq!(
1487            snapshot
1488                .syntax_ancestor(range_of(text, "|c|"))
1489                .unwrap()
1490                .byte_range(),
1491            range_of(text, "|c| {}")
1492        );
1493        assert_eq!(
1494            snapshot
1495                .syntax_ancestor(range_of(text, "|c| {}"))
1496                .unwrap()
1497                .byte_range(),
1498            range_of(text, "(|c| {})")
1499        );
1500
1501        buffer
1502    });
1503
1504    fn empty_range_at(text: &str, part: &str) -> Range<usize> {
1505        let start = text.find(part).unwrap();
1506        start..start
1507    }
1508
1509    fn range_of(text: &str, part: &str) -> Range<usize> {
1510        let start = text.find(part).unwrap();
1511        start..start + part.len()
1512    }
1513}
1514
1515#[gpui::test]
1516fn test_autoindent_with_soft_tabs(cx: &mut App) {
1517    init_settings(cx, |_| {});
1518
1519    cx.new(|cx| {
1520        let text = "fn a() {}";
1521        let mut buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
1522
1523        buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
1524        assert_eq!(buffer.text(), "fn a() {\n    \n}");
1525
1526        buffer.edit(
1527            [(Point::new(1, 4)..Point::new(1, 4), "b()\n")],
1528            Some(AutoindentMode::EachLine),
1529            cx,
1530        );
1531        assert_eq!(buffer.text(), "fn a() {\n    b()\n    \n}");
1532
1533        // Create a field expression on a new line, causing that line
1534        // to be indented.
1535        buffer.edit(
1536            [(Point::new(2, 4)..Point::new(2, 4), ".c")],
1537            Some(AutoindentMode::EachLine),
1538            cx,
1539        );
1540        assert_eq!(buffer.text(), "fn a() {\n    b()\n        .c\n}");
1541
1542        // Remove the dot so that the line is no longer a field expression,
1543        // causing the line to be outdented.
1544        buffer.edit(
1545            [(Point::new(2, 8)..Point::new(2, 9), "")],
1546            Some(AutoindentMode::EachLine),
1547            cx,
1548        );
1549        assert_eq!(buffer.text(), "fn a() {\n    b()\n    c\n}");
1550
1551        buffer
1552    });
1553}
1554
1555#[gpui::test]
1556fn test_autoindent_with_hard_tabs(cx: &mut App) {
1557    init_settings(cx, |settings| {
1558        settings.defaults.hard_tabs = Some(true);
1559    });
1560
1561    cx.new(|cx| {
1562        let text = "fn a() {}";
1563        let mut buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
1564
1565        buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
1566        assert_eq!(buffer.text(), "fn a() {\n\t\n}");
1567
1568        buffer.edit(
1569            [(Point::new(1, 1)..Point::new(1, 1), "b()\n")],
1570            Some(AutoindentMode::EachLine),
1571            cx,
1572        );
1573        assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\n}");
1574
1575        // Create a field expression on a new line, causing that line
1576        // to be indented.
1577        buffer.edit(
1578            [(Point::new(2, 1)..Point::new(2, 1), ".c")],
1579            Some(AutoindentMode::EachLine),
1580            cx,
1581        );
1582        assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\t.c\n}");
1583
1584        // Remove the dot so that the line is no longer a field expression,
1585        // causing the line to be outdented.
1586        buffer.edit(
1587            [(Point::new(2, 2)..Point::new(2, 3), "")],
1588            Some(AutoindentMode::EachLine),
1589            cx,
1590        );
1591        assert_eq!(buffer.text(), "fn a() {\n\tb()\n\tc\n}");
1592
1593        buffer
1594    });
1595}
1596
1597#[gpui::test]
1598fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut App) {
1599    init_settings(cx, |_| {});
1600
1601    cx.new(|cx| {
1602        let mut buffer = Buffer::local(
1603            "
1604            fn a() {
1605            c;
1606            d;
1607            }
1608            "
1609            .unindent(),
1610            cx,
1611        )
1612        .with_language(rust_lang(), cx);
1613
1614        // Lines 2 and 3 don't match the indentation suggestion. When editing these lines,
1615        // their indentation is not adjusted.
1616        buffer.edit_via_marked_text(
1617            &"
1618            fn a() {
1619            c«()»;
1620            d«()»;
1621            }
1622            "
1623            .unindent(),
1624            Some(AutoindentMode::EachLine),
1625            cx,
1626        );
1627        assert_eq!(
1628            buffer.text(),
1629            "
1630            fn a() {
1631            c();
1632            d();
1633            }
1634            "
1635            .unindent()
1636        );
1637
1638        // When appending new content after these lines, the indentation is based on the
1639        // preceding lines' actual indentation.
1640        buffer.edit_via_marked_text(
1641            &"
1642            fn a() {
16431644            .f
1645            .g()»;
16461647            .f
1648            .g()»;
1649            }
1650            "
1651            .unindent(),
1652            Some(AutoindentMode::EachLine),
1653            cx,
1654        );
1655        assert_eq!(
1656            buffer.text(),
1657            "
1658            fn a() {
1659            c
1660                .f
1661                .g();
1662            d
1663                .f
1664                .g();
1665            }
1666            "
1667            .unindent()
1668        );
1669
1670        // Insert a newline after the open brace. It is auto-indented
1671        buffer.edit_via_marked_text(
1672            &"
1673            fn a() {«
1674            »
1675            c
1676                .f
1677                .g();
1678            d
1679                .f
1680                .g();
1681            }
1682            "
1683            .unindent(),
1684            Some(AutoindentMode::EachLine),
1685            cx,
1686        );
1687        assert_eq!(
1688            buffer.text(),
1689            "
1690            fn a() {
1691                ˇ
1692            c
1693                .f
1694                .g();
1695            d
1696                .f
1697                .g();
1698            }
1699            "
1700            .unindent()
1701            .replace("ˇ", "")
1702        );
1703
1704        // Manually outdent the line. It stays outdented.
1705        buffer.edit_via_marked_text(
1706            &"
1707            fn a() {
1708            «»
1709            c
1710                .f
1711                .g();
1712            d
1713                .f
1714                .g();
1715            }
1716            "
1717            .unindent(),
1718            Some(AutoindentMode::EachLine),
1719            cx,
1720        );
1721        assert_eq!(
1722            buffer.text(),
1723            "
1724            fn a() {
1725
1726            c
1727                .f
1728                .g();
1729            d
1730                .f
1731                .g();
1732            }
1733            "
1734            .unindent()
1735        );
1736
1737        buffer
1738    });
1739
1740    cx.new(|cx| {
1741        eprintln!("second buffer: {:?}", cx.entity_id());
1742
1743        let mut buffer = Buffer::local(
1744            "
1745            fn a() {
1746                b();
1747                |
1748            "
1749            .replace('|', "") // marker to preserve trailing whitespace
1750            .unindent(),
1751            cx,
1752        )
1753        .with_language(rust_lang(), cx);
1754
1755        // Insert a closing brace. It is outdented.
1756        buffer.edit_via_marked_text(
1757            &"
1758            fn a() {
1759                b();
1760                «}»
1761            "
1762            .unindent(),
1763            Some(AutoindentMode::EachLine),
1764            cx,
1765        );
1766        assert_eq!(
1767            buffer.text(),
1768            "
1769            fn a() {
1770                b();
1771            }
1772            "
1773            .unindent()
1774        );
1775
1776        // Manually edit the leading whitespace. The edit is preserved.
1777        buffer.edit_via_marked_text(
1778            &"
1779            fn a() {
1780                b();
1781            «    »}
1782            "
1783            .unindent(),
1784            Some(AutoindentMode::EachLine),
1785            cx,
1786        );
1787        assert_eq!(
1788            buffer.text(),
1789            "
1790            fn a() {
1791                b();
1792                }
1793            "
1794            .unindent()
1795        );
1796        buffer
1797    });
1798
1799    eprintln!("DONE");
1800}
1801
1802#[gpui::test]
1803fn test_autoindent_does_not_adjust_lines_within_newly_created_errors(cx: &mut App) {
1804    init_settings(cx, |_| {});
1805
1806    cx.new(|cx| {
1807        let mut buffer = Buffer::local(
1808            "
1809            fn a() {
1810                i
1811            }
1812            "
1813            .unindent(),
1814            cx,
1815        )
1816        .with_language(rust_lang(), cx);
1817
1818        // Regression test: line does not get outdented due to syntax error
1819        buffer.edit_via_marked_text(
1820            &"
1821            fn a() {
1822                i«f let Some(x) = y»
1823            }
1824            "
1825            .unindent(),
1826            Some(AutoindentMode::EachLine),
1827            cx,
1828        );
1829        assert_eq!(
1830            buffer.text(),
1831            "
1832            fn a() {
1833                if let Some(x) = y
1834            }
1835            "
1836            .unindent()
1837        );
1838
1839        buffer.edit_via_marked_text(
1840            &"
1841            fn a() {
1842                if let Some(x) = y« {»
1843            }
1844            "
1845            .unindent(),
1846            Some(AutoindentMode::EachLine),
1847            cx,
1848        );
1849        assert_eq!(
1850            buffer.text(),
1851            "
1852            fn a() {
1853                if let Some(x) = y {
1854            }
1855            "
1856            .unindent()
1857        );
1858
1859        buffer
1860    });
1861}
1862
1863#[gpui::test]
1864fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut App) {
1865    init_settings(cx, |_| {});
1866
1867    cx.new(|cx| {
1868        let mut buffer = Buffer::local(
1869            "
1870            fn a() {}
1871            "
1872            .unindent(),
1873            cx,
1874        )
1875        .with_language(rust_lang(), cx);
1876
1877        buffer.edit_via_marked_text(
1878            &"
1879            fn a(«
1880            b») {}
1881            "
1882            .unindent(),
1883            Some(AutoindentMode::EachLine),
1884            cx,
1885        );
1886        assert_eq!(
1887            buffer.text(),
1888            "
1889            fn a(
1890                b) {}
1891            "
1892            .unindent()
1893        );
1894
1895        // The indentation suggestion changed because `@end` node (a close paren)
1896        // is now at the beginning of the line.
1897        buffer.edit_via_marked_text(
1898            &"
1899            fn a(
1900                ˇ) {}
1901            "
1902            .unindent(),
1903            Some(AutoindentMode::EachLine),
1904            cx,
1905        );
1906        assert_eq!(
1907            buffer.text(),
1908            "
1909                fn a(
1910                ) {}
1911            "
1912            .unindent()
1913        );
1914
1915        buffer
1916    });
1917}
1918
1919#[gpui::test]
1920fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut App) {
1921    init_settings(cx, |_| {});
1922
1923    cx.new(|cx| {
1924        let text = "a\nb";
1925        let mut buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
1926        buffer.edit(
1927            [(0..1, "\n"), (2..3, "\n")],
1928            Some(AutoindentMode::EachLine),
1929            cx,
1930        );
1931        assert_eq!(buffer.text(), "\n\n\n");
1932        buffer
1933    });
1934}
1935
1936#[gpui::test]
1937fn test_autoindent_multi_line_insertion(cx: &mut App) {
1938    init_settings(cx, |_| {});
1939
1940    cx.new(|cx| {
1941        let text = "
1942            const a: usize = 1;
1943            fn b() {
1944                if c {
1945                    let d = 2;
1946                }
1947            }
1948        "
1949        .unindent();
1950
1951        let mut buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
1952        buffer.edit(
1953            [(Point::new(3, 0)..Point::new(3, 0), "e(\n    f()\n);\n")],
1954            Some(AutoindentMode::EachLine),
1955            cx,
1956        );
1957        assert_eq!(
1958            buffer.text(),
1959            "
1960                const a: usize = 1;
1961                fn b() {
1962                    if c {
1963                        e(
1964                            f()
1965                        );
1966                        let d = 2;
1967                    }
1968                }
1969            "
1970            .unindent()
1971        );
1972
1973        buffer
1974    });
1975}
1976
1977#[gpui::test]
1978fn test_autoindent_block_mode(cx: &mut App) {
1979    init_settings(cx, |_| {});
1980
1981    cx.new(|cx| {
1982        let text = r#"
1983            fn a() {
1984                b();
1985            }
1986        "#
1987        .unindent();
1988        let mut buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
1989
1990        // When this text was copied, both of the quotation marks were at the same
1991        // indent level, but the indentation of the first line was not included in
1992        // the copied text. This information is retained in the
1993        // 'original_indent_columns' vector.
1994        let original_indent_columns = vec![Some(4)];
1995        let inserted_text = r#"
1996            "
1997                  c
1998                    d
1999                      e
2000                "
2001        "#
2002        .unindent();
2003
2004        // Insert the block at column zero. The entire block is indented
2005        // so that the first line matches the previous line's indentation.
2006        buffer.edit(
2007            [(Point::new(2, 0)..Point::new(2, 0), inserted_text.clone())],
2008            Some(AutoindentMode::Block {
2009                original_indent_columns: original_indent_columns.clone(),
2010            }),
2011            cx,
2012        );
2013        assert_eq!(
2014            buffer.text(),
2015            r#"
2016            fn a() {
2017                b();
2018                "
2019                  c
2020                    d
2021                      e
2022                "
2023            }
2024            "#
2025            .unindent()
2026        );
2027
2028        // Grouping is disabled in tests, so we need 2 undos
2029        buffer.undo(cx); // Undo the auto-indent
2030        buffer.undo(cx); // Undo the original edit
2031
2032        // Insert the block at a deeper indent level. The entire block is outdented.
2033        buffer.edit([(Point::new(2, 0)..Point::new(2, 0), "        ")], None, cx);
2034        buffer.edit(
2035            [(Point::new(2, 8)..Point::new(2, 8), inserted_text)],
2036            Some(AutoindentMode::Block {
2037                original_indent_columns,
2038            }),
2039            cx,
2040        );
2041        assert_eq!(
2042            buffer.text(),
2043            r#"
2044            fn a() {
2045                b();
2046                "
2047                  c
2048                    d
2049                      e
2050                "
2051            }
2052            "#
2053            .unindent()
2054        );
2055
2056        buffer
2057    });
2058}
2059
2060#[gpui::test]
2061fn test_autoindent_block_mode_with_newline(cx: &mut App) {
2062    init_settings(cx, |_| {});
2063
2064    cx.new(|cx| {
2065        let text = r#"
2066            fn a() {
2067                b();
2068            }
2069        "#
2070        .unindent();
2071        let mut buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
2072
2073        // First line contains just '\n', it's indentation is stored in "original_indent_columns"
2074        let original_indent_columns = vec![Some(4)];
2075        let inserted_text = r#"
2076
2077                c();
2078                    d();
2079                        e();
2080        "#
2081        .unindent();
2082        buffer.edit(
2083            [(Point::new(2, 0)..Point::new(2, 0), inserted_text)],
2084            Some(AutoindentMode::Block {
2085                original_indent_columns,
2086            }),
2087            cx,
2088        );
2089
2090        // While making edit, we ignore first line as it only contains '\n'
2091        // hence second line indent is used to calculate delta
2092        assert_eq!(
2093            buffer.text(),
2094            r#"
2095            fn a() {
2096                b();
2097
2098                c();
2099                    d();
2100                        e();
2101            }
2102            "#
2103            .unindent()
2104        );
2105
2106        buffer
2107    });
2108}
2109
2110#[gpui::test]
2111fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut App) {
2112    init_settings(cx, |_| {});
2113
2114    cx.new(|cx| {
2115        let text = r#"
2116            fn a() {
2117                if b() {
2118
2119                }
2120            }
2121        "#
2122        .unindent();
2123        let mut buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
2124
2125        // The original indent columns are not known, so this text is
2126        // auto-indented in a block as if the first line was copied in
2127        // its entirety.
2128        let original_indent_columns = Vec::new();
2129        let inserted_text = "    c\n        .d()\n        .e();";
2130
2131        // Insert the block at column zero. The entire block is indented
2132        // so that the first line matches the previous line's indentation.
2133        buffer.edit(
2134            [(Point::new(2, 0)..Point::new(2, 0), inserted_text)],
2135            Some(AutoindentMode::Block {
2136                original_indent_columns,
2137            }),
2138            cx,
2139        );
2140        assert_eq!(
2141            buffer.text(),
2142            r#"
2143            fn a() {
2144                if b() {
2145                    c
2146                        .d()
2147                        .e();
2148                }
2149            }
2150            "#
2151            .unindent()
2152        );
2153
2154        // Grouping is disabled in tests, so we need 2 undos
2155        buffer.undo(cx); // Undo the auto-indent
2156        buffer.undo(cx); // Undo the original edit
2157
2158        // Insert the block at a deeper indent level. The entire block is outdented.
2159        buffer.edit(
2160            [(Point::new(2, 0)..Point::new(2, 0), " ".repeat(12))],
2161            None,
2162            cx,
2163        );
2164        buffer.edit(
2165            [(Point::new(2, 12)..Point::new(2, 12), inserted_text)],
2166            Some(AutoindentMode::Block {
2167                original_indent_columns: Vec::new(),
2168            }),
2169            cx,
2170        );
2171        assert_eq!(
2172            buffer.text(),
2173            r#"
2174            fn a() {
2175                if b() {
2176                    c
2177                        .d()
2178                        .e();
2179                }
2180            }
2181            "#
2182            .unindent()
2183        );
2184
2185        buffer
2186    });
2187}
2188
2189#[gpui::test]
2190fn test_autoindent_block_mode_multiple_adjacent_ranges(cx: &mut App) {
2191    init_settings(cx, |_| {});
2192
2193    cx.new(|cx| {
2194        let (text, ranges_to_replace) = marked_text_ranges(
2195            &"
2196            mod numbers {
2197                «fn one() {
2198                    1
2199                }
2200            »
2201                «fn two() {
2202                    2
2203                }
2204            »
2205                «fn three() {
2206                    3
2207                }
2208            »}
2209            "
2210            .unindent(),
2211            false,
2212        );
2213
2214        let mut buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
2215
2216        buffer.edit(
2217            [
2218                (ranges_to_replace[0].clone(), "fn one() {\n    101\n}\n"),
2219                (ranges_to_replace[1].clone(), "fn two() {\n    102\n}\n"),
2220                (ranges_to_replace[2].clone(), "fn three() {\n    103\n}\n"),
2221            ],
2222            Some(AutoindentMode::Block {
2223                original_indent_columns: vec![Some(0), Some(0), Some(0)],
2224            }),
2225            cx,
2226        );
2227
2228        assert_eq!(
2229            buffer.text(),
2230            "
2231            mod numbers {
2232                fn one() {
2233                    101
2234                }
2235
2236                fn two() {
2237                    102
2238                }
2239
2240                fn three() {
2241                    103
2242                }
2243            }
2244            "
2245            .unindent()
2246        );
2247
2248        buffer
2249    });
2250}
2251
2252#[gpui::test]
2253fn test_autoindent_language_without_indents_query(cx: &mut App) {
2254    init_settings(cx, |_| {});
2255
2256    cx.new(|cx| {
2257        let text = "
2258            * one
2259                - a
2260                - b
2261            * two
2262        "
2263        .unindent();
2264
2265        let mut buffer = Buffer::local(text, cx).with_language(
2266            Arc::new(Language::new(
2267                LanguageConfig {
2268                    name: "Markdown".into(),
2269                    auto_indent_using_last_non_empty_line: false,
2270                    ..Default::default()
2271                },
2272                Some(tree_sitter_json::LANGUAGE.into()),
2273            )),
2274            cx,
2275        );
2276        buffer.edit(
2277            [(Point::new(3, 0)..Point::new(3, 0), "\n")],
2278            Some(AutoindentMode::EachLine),
2279            cx,
2280        );
2281        assert_eq!(
2282            buffer.text(),
2283            "
2284            * one
2285                - a
2286                - b
2287
2288            * two
2289            "
2290            .unindent()
2291        );
2292        buffer
2293    });
2294}
2295
2296#[gpui::test]
2297fn test_autoindent_with_injected_languages(cx: &mut App) {
2298    init_settings(cx, |settings| {
2299        settings.languages.0.extend([
2300            (
2301                "HTML".into(),
2302                LanguageSettingsContent {
2303                    tab_size: Some(2.try_into().unwrap()),
2304                    ..Default::default()
2305                },
2306            ),
2307            (
2308                "JavaScript".into(),
2309                LanguageSettingsContent {
2310                    tab_size: Some(8.try_into().unwrap()),
2311                    ..Default::default()
2312                },
2313            ),
2314        ])
2315    });
2316
2317    let html_language = Arc::new(html_lang());
2318
2319    let javascript_language = Arc::new(javascript_lang());
2320
2321    let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
2322    language_registry.add(html_language.clone());
2323    language_registry.add(javascript_language);
2324
2325    cx.new(|cx| {
2326        let (text, ranges) = marked_text_ranges(
2327            &"
2328                <div>ˇ
2329                </div>
2330                <script>
2331                    init({ˇ
2332                    })
2333                </script>
2334                <span>ˇ
2335                </span>
2336            "
2337            .unindent(),
2338            false,
2339        );
2340
2341        let mut buffer = Buffer::local(text, cx);
2342        buffer.set_language_registry(language_registry);
2343        buffer.set_language(Some(html_language), cx);
2344        buffer.edit(
2345            ranges.into_iter().map(|range| (range, "\na")),
2346            Some(AutoindentMode::EachLine),
2347            cx,
2348        );
2349        assert_eq!(
2350            buffer.text(),
2351            "
2352                <div>
2353                  a
2354                </div>
2355                <script>
2356                    init({
2357                            a
2358                    })
2359                </script>
2360                <span>
2361                  a
2362                </span>
2363            "
2364            .unindent()
2365        );
2366        buffer
2367    });
2368}
2369
2370#[gpui::test]
2371fn test_autoindent_query_with_outdent_captures(cx: &mut App) {
2372    init_settings(cx, |settings| {
2373        settings.defaults.tab_size = Some(2.try_into().unwrap());
2374    });
2375
2376    cx.new(|cx| {
2377        let mut buffer = Buffer::local("", cx).with_language(Arc::new(ruby_lang()), cx);
2378
2379        let text = r#"
2380            class C
2381            def a(b, c)
2382            puts b
2383            puts c
2384            rescue
2385            puts "errored"
2386            exit 1
2387            end
2388            end
2389        "#
2390        .unindent();
2391
2392        buffer.edit([(0..0, text)], Some(AutoindentMode::EachLine), cx);
2393
2394        assert_eq!(
2395            buffer.text(),
2396            r#"
2397                class C
2398                  def a(b, c)
2399                    puts b
2400                    puts c
2401                  rescue
2402                    puts "errored"
2403                    exit 1
2404                  end
2405                end
2406            "#
2407            .unindent()
2408        );
2409
2410        buffer
2411    });
2412}
2413
2414#[gpui::test]
2415async fn test_async_autoindents_preserve_preview(cx: &mut TestAppContext) {
2416    cx.update(|cx| init_settings(cx, |_| {}));
2417
2418    // First we insert some newlines to request an auto-indent (asynchronously).
2419    // Then we request that a preview tab be preserved for the new version, even though it's edited.
2420    let buffer = cx.new(|cx| {
2421        let text = "fn a() {}";
2422        let mut buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
2423
2424        // This causes autoindent to be async.
2425        buffer.set_sync_parse_timeout(None);
2426
2427        buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
2428        buffer.refresh_preview();
2429
2430        // Synchronously, we haven't auto-indented and we're still preserving the preview.
2431        assert_eq!(buffer.text(), "fn a() {\n\n}");
2432        assert!(buffer.preserve_preview());
2433        buffer
2434    });
2435
2436    // Now let the autoindent finish
2437    cx.executor().run_until_parked();
2438
2439    // The auto-indent applied, but didn't dismiss our preview
2440    buffer.update(cx, |buffer, cx| {
2441        assert_eq!(buffer.text(), "fn a() {\n    \n}");
2442        assert!(buffer.preserve_preview());
2443
2444        // Edit inserting another line. It will autoindent async.
2445        // Then refresh the preview version.
2446        buffer.edit(
2447            [(Point::new(1, 4)..Point::new(1, 4), "\n")],
2448            Some(AutoindentMode::EachLine),
2449            cx,
2450        );
2451        buffer.refresh_preview();
2452        assert_eq!(buffer.text(), "fn a() {\n    \n\n}");
2453        assert!(buffer.preserve_preview());
2454
2455        // Then perform another edit, this time without refreshing the preview version.
2456        buffer.edit([(Point::new(1, 4)..Point::new(1, 4), "x")], None, cx);
2457        // This causes the preview to not be preserved.
2458        assert!(!buffer.preserve_preview());
2459    });
2460
2461    // Let the async autoindent from the first edit finish.
2462    cx.executor().run_until_parked();
2463
2464    // The autoindent applies, but it shouldn't restore the preview status because we had an edit in the meantime.
2465    buffer.update(cx, |buffer, _| {
2466        assert_eq!(buffer.text(), "fn a() {\n    x\n    \n}");
2467        assert!(!buffer.preserve_preview());
2468    });
2469}
2470
2471#[gpui::test]
2472fn test_insert_empty_line(cx: &mut App) {
2473    init_settings(cx, |_| {});
2474
2475    // Insert empty line at the beginning, requesting an empty line above
2476    cx.new(|cx| {
2477        let mut buffer = Buffer::local("abc\ndef\nghi", cx);
2478        let point = buffer.insert_empty_line(Point::new(0, 0), true, false, cx);
2479        assert_eq!(buffer.text(), "\nabc\ndef\nghi");
2480        assert_eq!(point, Point::new(0, 0));
2481        buffer
2482    });
2483
2484    // Insert empty line at the beginning, requesting an empty line above and below
2485    cx.new(|cx| {
2486        let mut buffer = Buffer::local("abc\ndef\nghi", cx);
2487        let point = buffer.insert_empty_line(Point::new(0, 0), true, true, cx);
2488        assert_eq!(buffer.text(), "\n\nabc\ndef\nghi");
2489        assert_eq!(point, Point::new(0, 0));
2490        buffer
2491    });
2492
2493    // Insert empty line at the start of a line, requesting empty lines above and below
2494    cx.new(|cx| {
2495        let mut buffer = Buffer::local("abc\ndef\nghi", cx);
2496        let point = buffer.insert_empty_line(Point::new(2, 0), true, true, cx);
2497        assert_eq!(buffer.text(), "abc\ndef\n\n\n\nghi");
2498        assert_eq!(point, Point::new(3, 0));
2499        buffer
2500    });
2501
2502    // Insert empty line in the middle of a line, requesting empty lines above and below
2503    cx.new(|cx| {
2504        let mut buffer = Buffer::local("abc\ndefghi\njkl", cx);
2505        let point = buffer.insert_empty_line(Point::new(1, 3), true, true, cx);
2506        assert_eq!(buffer.text(), "abc\ndef\n\n\n\nghi\njkl");
2507        assert_eq!(point, Point::new(3, 0));
2508        buffer
2509    });
2510
2511    // Insert empty line in the middle of a line, requesting empty line above only
2512    cx.new(|cx| {
2513        let mut buffer = Buffer::local("abc\ndefghi\njkl", cx);
2514        let point = buffer.insert_empty_line(Point::new(1, 3), true, false, cx);
2515        assert_eq!(buffer.text(), "abc\ndef\n\n\nghi\njkl");
2516        assert_eq!(point, Point::new(3, 0));
2517        buffer
2518    });
2519
2520    // Insert empty line in the middle of a line, requesting empty line below only
2521    cx.new(|cx| {
2522        let mut buffer = Buffer::local("abc\ndefghi\njkl", cx);
2523        let point = buffer.insert_empty_line(Point::new(1, 3), false, true, cx);
2524        assert_eq!(buffer.text(), "abc\ndef\n\n\nghi\njkl");
2525        assert_eq!(point, Point::new(2, 0));
2526        buffer
2527    });
2528
2529    // Insert empty line at the end, requesting empty lines above and below
2530    cx.new(|cx| {
2531        let mut buffer = Buffer::local("abc\ndef\nghi", cx);
2532        let point = buffer.insert_empty_line(Point::new(2, 3), true, true, cx);
2533        assert_eq!(buffer.text(), "abc\ndef\nghi\n\n\n");
2534        assert_eq!(point, Point::new(4, 0));
2535        buffer
2536    });
2537
2538    // Insert empty line at the end, requesting empty line above only
2539    cx.new(|cx| {
2540        let mut buffer = Buffer::local("abc\ndef\nghi", cx);
2541        let point = buffer.insert_empty_line(Point::new(2, 3), true, false, cx);
2542        assert_eq!(buffer.text(), "abc\ndef\nghi\n\n");
2543        assert_eq!(point, Point::new(4, 0));
2544        buffer
2545    });
2546
2547    // Insert empty line at the end, requesting empty line below only
2548    cx.new(|cx| {
2549        let mut buffer = Buffer::local("abc\ndef\nghi", cx);
2550        let point = buffer.insert_empty_line(Point::new(2, 3), false, true, cx);
2551        assert_eq!(buffer.text(), "abc\ndef\nghi\n\n");
2552        assert_eq!(point, Point::new(3, 0));
2553        buffer
2554    });
2555}
2556
2557#[gpui::test]
2558fn test_language_scope_at_with_javascript(cx: &mut App) {
2559    init_settings(cx, |_| {});
2560
2561    cx.new(|cx| {
2562        let language = Language::new(
2563            LanguageConfig {
2564                name: "JavaScript".into(),
2565                line_comments: vec!["// ".into()],
2566                block_comment: Some(BlockCommentConfig {
2567                    start: "/*".into(),
2568                    end: "*/".into(),
2569                    prefix: "* ".into(),
2570                    tab_size: 1,
2571                }),
2572                brackets: BracketPairConfig {
2573                    pairs: vec![
2574                        BracketPair {
2575                            start: "{".into(),
2576                            end: "}".into(),
2577                            close: true,
2578                            surround: true,
2579                            newline: false,
2580                        },
2581                        BracketPair {
2582                            start: "'".into(),
2583                            end: "'".into(),
2584                            close: true,
2585                            surround: true,
2586                            newline: false,
2587                        },
2588                    ],
2589                    disabled_scopes_by_bracket_ix: vec![
2590                        Vec::new(),                              //
2591                        vec!["string".into(), "comment".into()], // single quotes disabled
2592                    ],
2593                },
2594                overrides: [(
2595                    "element".into(),
2596                    LanguageConfigOverride {
2597                        line_comments: Override::Remove { remove: true },
2598                        block_comment: Override::Set(BlockCommentConfig {
2599                            start: "{/*".into(),
2600                            prefix: "".into(),
2601                            end: "*/}".into(),
2602                            tab_size: 0,
2603                        }),
2604                        ..Default::default()
2605                    },
2606                )]
2607                .into_iter()
2608                .collect(),
2609                ..Default::default()
2610            },
2611            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
2612        )
2613        .with_override_query(
2614            r#"
2615                (jsx_element) @element
2616                (string) @string
2617                (comment) @comment.inclusive
2618                [
2619                    (jsx_opening_element)
2620                    (jsx_closing_element)
2621                    (jsx_expression)
2622                ] @default
2623            "#,
2624        )
2625        .unwrap();
2626
2627        let text = r#"
2628            a["b"] = <C d="e">
2629                <F></F>
2630                { g() }
2631            </C>; // a comment
2632        "#
2633        .unindent();
2634
2635        let buffer = Buffer::local(&text, cx).with_language(Arc::new(language), cx);
2636        let snapshot = buffer.snapshot();
2637
2638        let config = snapshot.language_scope_at(0).unwrap();
2639        assert_eq!(config.line_comment_prefixes(), &[Arc::from("// ")]);
2640        assert_eq!(
2641            config.block_comment(),
2642            Some(&BlockCommentConfig {
2643                start: "/*".into(),
2644                prefix: "* ".into(),
2645                end: "*/".into(),
2646                tab_size: 1,
2647            })
2648        );
2649
2650        // Both bracket pairs are enabled
2651        assert_eq!(
2652            config.brackets().map(|e| e.1).collect::<Vec<_>>(),
2653            &[true, true]
2654        );
2655
2656        let comment_config = snapshot
2657            .language_scope_at(text.find("comment").unwrap() + "comment".len())
2658            .unwrap();
2659        assert_eq!(
2660            comment_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
2661            &[true, false]
2662        );
2663
2664        let string_config = snapshot
2665            .language_scope_at(text.find("b\"").unwrap())
2666            .unwrap();
2667        assert_eq!(string_config.line_comment_prefixes(), &[Arc::from("// ")]);
2668        assert_eq!(
2669            string_config.block_comment(),
2670            Some(&BlockCommentConfig {
2671                start: "/*".into(),
2672                prefix: "* ".into(),
2673                end: "*/".into(),
2674                tab_size: 1,
2675            })
2676        );
2677        // Second bracket pair is disabled
2678        assert_eq!(
2679            string_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
2680            &[true, false]
2681        );
2682
2683        // In between JSX tags: use the `element` override.
2684        let element_config = snapshot
2685            .language_scope_at(text.find("<F>").unwrap())
2686            .unwrap();
2687        // TODO nested blocks after newlines are captured with all whitespaces
2688        // https://github.com/tree-sitter/tree-sitter-typescript/issues/306
2689        // assert_eq!(element_config.line_comment_prefixes(), &[]);
2690        // assert_eq!(
2691        //     element_config.block_comment_delimiters(),
2692        //     Some((&"{/*".into(), &"*/}".into()))
2693        // );
2694        assert_eq!(
2695            element_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
2696            &[true, true]
2697        );
2698
2699        // Within a JSX tag: use the default config.
2700        let tag_config = snapshot
2701            .language_scope_at(text.find(" d=").unwrap() + 1)
2702            .unwrap();
2703        assert_eq!(tag_config.line_comment_prefixes(), &[Arc::from("// ")]);
2704        assert_eq!(
2705            tag_config.block_comment(),
2706            Some(&BlockCommentConfig {
2707                start: "/*".into(),
2708                prefix: "* ".into(),
2709                end: "*/".into(),
2710                tab_size: 1,
2711            })
2712        );
2713        assert_eq!(
2714            tag_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
2715            &[true, true]
2716        );
2717
2718        // In a JSX expression: use the default config.
2719        let expression_in_element_config = snapshot
2720            .language_scope_at(text.find('{').unwrap() + 1)
2721            .unwrap();
2722        assert_eq!(
2723            expression_in_element_config.line_comment_prefixes(),
2724            &[Arc::from("// ")]
2725        );
2726        assert_eq!(
2727            expression_in_element_config.block_comment(),
2728            Some(&BlockCommentConfig {
2729                start: "/*".into(),
2730                prefix: "* ".into(),
2731                end: "*/".into(),
2732                tab_size: 1,
2733            })
2734        );
2735        assert_eq!(
2736            expression_in_element_config
2737                .brackets()
2738                .map(|e| e.1)
2739                .collect::<Vec<_>>(),
2740            &[true, true]
2741        );
2742
2743        buffer
2744    });
2745}
2746
2747#[gpui::test]
2748fn test_language_scope_at_with_rust(cx: &mut App) {
2749    init_settings(cx, |_| {});
2750
2751    cx.new(|cx| {
2752        let language = Language::new(
2753            LanguageConfig {
2754                name: "Rust".into(),
2755                brackets: BracketPairConfig {
2756                    pairs: vec![
2757                        BracketPair {
2758                            start: "{".into(),
2759                            end: "}".into(),
2760                            close: true,
2761                            surround: true,
2762                            newline: false,
2763                        },
2764                        BracketPair {
2765                            start: "'".into(),
2766                            end: "'".into(),
2767                            close: true,
2768                            surround: true,
2769                            newline: false,
2770                        },
2771                    ],
2772                    disabled_scopes_by_bracket_ix: vec![
2773                        Vec::new(), //
2774                        vec!["string".into()],
2775                    ],
2776                },
2777                ..Default::default()
2778            },
2779            Some(tree_sitter_rust::LANGUAGE.into()),
2780        )
2781        .with_override_query(
2782            r#"
2783                (string_literal) @string
2784            "#,
2785        )
2786        .unwrap();
2787
2788        let text = r#"
2789            const S: &'static str = "hello";
2790        "#
2791        .unindent();
2792
2793        let buffer = Buffer::local(text.clone(), cx).with_language(Arc::new(language), cx);
2794        let snapshot = buffer.snapshot();
2795
2796        // By default, all brackets are enabled
2797        let config = snapshot.language_scope_at(0).unwrap();
2798        assert_eq!(
2799            config.brackets().map(|e| e.1).collect::<Vec<_>>(),
2800            &[true, true]
2801        );
2802
2803        // Within a string, the quotation brackets are disabled.
2804        let string_config = snapshot
2805            .language_scope_at(text.find("ello").unwrap())
2806            .unwrap();
2807        assert_eq!(
2808            string_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
2809            &[true, false]
2810        );
2811
2812        buffer
2813    });
2814}
2815
2816#[gpui::test]
2817fn test_language_scope_at_with_combined_injections(cx: &mut App) {
2818    init_settings(cx, |_| {});
2819
2820    cx.new(|cx| {
2821        let text = r#"
2822            <ol>
2823            <% people.each do |person| %>
2824                <li>
2825                    <%= person.name %>
2826                </li>
2827            <% end %>
2828            </ol>
2829        "#
2830        .unindent();
2831
2832        let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
2833        language_registry.add(Arc::new(ruby_lang()));
2834        language_registry.add(Arc::new(html_lang()));
2835        language_registry.add(Arc::new(erb_lang()));
2836
2837        let mut buffer = Buffer::local(text, cx);
2838        buffer.set_language_registry(language_registry.clone());
2839        let language = language_registry
2840            .language_for_name("HTML+ERB")
2841            .now_or_never()
2842            .and_then(Result::ok);
2843        buffer.set_language(language, cx);
2844
2845        let snapshot = buffer.snapshot();
2846        let html_config = snapshot.language_scope_at(Point::new(2, 4)).unwrap();
2847        assert_eq!(html_config.line_comment_prefixes(), &[]);
2848        assert_eq!(
2849            html_config.block_comment(),
2850            Some(&BlockCommentConfig {
2851                start: "<!--".into(),
2852                end: "-->".into(),
2853                prefix: "".into(),
2854                tab_size: 0,
2855            })
2856        );
2857
2858        let ruby_config = snapshot.language_scope_at(Point::new(3, 12)).unwrap();
2859        assert_eq!(ruby_config.line_comment_prefixes(), &[Arc::from("# ")]);
2860        assert_eq!(ruby_config.block_comment(), None);
2861
2862        buffer
2863    });
2864}
2865
2866#[gpui::test]
2867fn test_language_at_with_hidden_languages(cx: &mut App) {
2868    init_settings(cx, |_| {});
2869
2870    cx.new(|cx| {
2871        let text = r#"
2872            this is an *emphasized* word.
2873        "#
2874        .unindent();
2875
2876        let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
2877        language_registry.add(markdown_lang());
2878        language_registry.add(Arc::new(markdown_inline_lang()));
2879
2880        let mut buffer = Buffer::local(text, cx);
2881        buffer.set_language_registry(language_registry.clone());
2882        buffer.set_language(
2883            language_registry
2884                .language_for_name("Markdown")
2885                .now_or_never()
2886                .unwrap()
2887                .ok(),
2888            cx,
2889        );
2890
2891        let snapshot = buffer.snapshot();
2892
2893        for point in [Point::new(0, 4), Point::new(0, 16)] {
2894            let config = snapshot.language_scope_at(point).unwrap();
2895            assert_eq!(config.language_name(), "Markdown");
2896
2897            let language = snapshot.language_at(point).unwrap();
2898            assert_eq!(language.name().as_ref(), "Markdown");
2899        }
2900
2901        buffer
2902    });
2903}
2904
2905#[gpui::test]
2906fn test_language_at_for_markdown_code_block(cx: &mut App) {
2907    init_settings(cx, |_| {});
2908
2909    cx.new(|cx| {
2910        let text = r#"
2911            ```rs
2912            let a = 2;
2913            // let b = 3;
2914            ```
2915        "#
2916        .unindent();
2917
2918        let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
2919        language_registry.add(markdown_lang());
2920        language_registry.add(Arc::new(markdown_inline_lang()));
2921        language_registry.add(rust_lang());
2922
2923        let mut buffer = Buffer::local(text, cx);
2924        buffer.set_language_registry(language_registry.clone());
2925        buffer.set_language(
2926            language_registry
2927                .language_for_name("Markdown")
2928                .now_or_never()
2929                .unwrap()
2930                .ok(),
2931            cx,
2932        );
2933
2934        let snapshot = buffer.snapshot();
2935
2936        // Test points in the code line
2937        for point in [Point::new(1, 4), Point::new(1, 6)] {
2938            let config = snapshot.language_scope_at(point).unwrap();
2939            assert_eq!(config.language_name(), "Rust");
2940
2941            let language = snapshot.language_at(point).unwrap();
2942            assert_eq!(language.name().as_ref(), "Rust");
2943        }
2944
2945        // Test points in the comment line to verify it's still detected as Rust
2946        for point in [Point::new(2, 4), Point::new(2, 6)] {
2947            let config = snapshot.language_scope_at(point).unwrap();
2948            assert_eq!(config.language_name(), "Rust");
2949
2950            let language = snapshot.language_at(point).unwrap();
2951            assert_eq!(language.name().as_ref(), "Rust");
2952        }
2953
2954        buffer
2955    });
2956}
2957
2958#[gpui::test]
2959fn test_syntax_layer_at_for_combined_injections(cx: &mut App) {
2960    init_settings(cx, |_| {});
2961
2962    cx.new(|cx| {
2963        // ERB template with HTML and Ruby content
2964        let text = r#"
2965<div>Hello</div>
2966<%= link_to "Click", url %>
2967<p>World</p>
2968        "#
2969        .unindent();
2970
2971        let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
2972        language_registry.add(Arc::new(erb_lang()));
2973        language_registry.add(Arc::new(html_lang()));
2974        language_registry.add(Arc::new(ruby_lang()));
2975
2976        let mut buffer = Buffer::local(text, cx);
2977        buffer.set_language_registry(language_registry.clone());
2978        let language = language_registry
2979            .language_for_name("HTML+ERB")
2980            .now_or_never()
2981            .and_then(Result::ok);
2982        buffer.set_language(language, cx);
2983
2984        let snapshot = buffer.snapshot();
2985
2986        // Test language_at for HTML content (line 0: "<div>Hello</div>")
2987        let html_point = Point::new(0, 4);
2988        let language = snapshot.language_at(html_point).unwrap();
2989        assert_eq!(
2990            language.name().as_ref(),
2991            "HTML",
2992            "Expected HTML at {:?}, got {}",
2993            html_point,
2994            language.name()
2995        );
2996
2997        // Test language_at for Ruby code (line 1: "<%= link_to ... %>")
2998        let ruby_point = Point::new(1, 6);
2999        let language = snapshot.language_at(ruby_point).unwrap();
3000        assert_eq!(
3001            language.name().as_ref(),
3002            "Ruby",
3003            "Expected Ruby at {:?}, got {}",
3004            ruby_point,
3005            language.name()
3006        );
3007
3008        // Test language_at for HTML after Ruby (line 2: "<p>World</p>")
3009        let html_after_ruby = Point::new(2, 2);
3010        let language = snapshot.language_at(html_after_ruby).unwrap();
3011        assert_eq!(
3012            language.name().as_ref(),
3013            "HTML",
3014            "Expected HTML at {:?}, got {}",
3015            html_after_ruby,
3016            language.name()
3017        );
3018
3019        buffer
3020    });
3021}
3022
3023#[gpui::test]
3024fn test_languages_at_for_combined_injections(cx: &mut App) {
3025    init_settings(cx, |_| {});
3026
3027    cx.new(|cx| {
3028        // ERB template with HTML and Ruby content
3029        let text = r#"
3030<div>Hello</div>
3031<%= yield %>
3032<p>World</p>
3033        "#
3034        .unindent();
3035
3036        let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
3037        language_registry.add(Arc::new(erb_lang()));
3038        language_registry.add(Arc::new(html_lang()));
3039        language_registry.add(Arc::new(ruby_lang()));
3040
3041        let mut buffer = Buffer::local(text, cx);
3042        buffer.set_language_registry(language_registry.clone());
3043        buffer.set_language(
3044            language_registry
3045                .language_for_name("HTML+ERB")
3046                .now_or_never()
3047                .unwrap()
3048                .ok(),
3049            cx,
3050        );
3051
3052        // Test languages_at for HTML content - should NOT include Ruby
3053        let html_point = Point::new(0, 4);
3054        let languages = buffer.languages_at(html_point);
3055        let language_names: Vec<_> = languages.iter().map(|language| language.name()).collect();
3056        assert!(
3057            language_names
3058                .iter()
3059                .any(|language_name| language_name.as_ref() == "HTML"),
3060            "Expected HTML in languages at {:?}, got {:?}",
3061            html_point,
3062            language_names
3063        );
3064        assert!(
3065            !language_names
3066                .iter()
3067                .any(|language_name| language_name.as_ref() == "Ruby"),
3068            "Did not expect Ruby in languages at {:?}, got {:?}",
3069            html_point,
3070            language_names
3071        );
3072
3073        // Test languages_at for Ruby code - should NOT include HTML
3074        let ruby_point = Point::new(1, 6);
3075        let languages = buffer.languages_at(ruby_point);
3076        let language_names: Vec<_> = languages.iter().map(|language| language.name()).collect();
3077        assert!(
3078            language_names
3079                .iter()
3080                .any(|language_name| language_name.as_ref() == "Ruby"),
3081            "Expected Ruby in languages at {:?}, got {:?}",
3082            ruby_point,
3083            language_names
3084        );
3085        assert!(
3086            !language_names
3087                .iter()
3088                .any(|language_name| language_name.as_ref() == "HTML"),
3089            "Did not expect HTML in languages at {:?}, got {:?}",
3090            ruby_point,
3091            language_names
3092        );
3093
3094        buffer
3095    });
3096}
3097
3098#[gpui::test]
3099fn test_serialization(cx: &mut gpui::App) {
3100    let mut now = Instant::now();
3101
3102    let buffer1 = cx.new(|cx| {
3103        let mut buffer = Buffer::local("abc", cx);
3104        buffer.edit([(3..3, "D")], None, cx);
3105
3106        now += Duration::from_secs(1);
3107        buffer.start_transaction_at(now);
3108        buffer.edit([(4..4, "E")], None, cx);
3109        buffer.end_transaction_at(now, cx);
3110        assert_eq!(buffer.text(), "abcDE");
3111
3112        buffer.undo(cx);
3113        assert_eq!(buffer.text(), "abcD");
3114
3115        buffer.edit([(4..4, "F")], None, cx);
3116        assert_eq!(buffer.text(), "abcDF");
3117        buffer
3118    });
3119    assert_eq!(buffer1.read(cx).text(), "abcDF");
3120
3121    let state = buffer1.read(cx).to_proto(cx);
3122    let ops = cx
3123        .foreground_executor()
3124        .block_on(buffer1.read(cx).serialize_ops(None, cx));
3125    let buffer2 = cx.new(|cx| {
3126        let mut buffer =
3127            Buffer::from_proto(ReplicaId::new(1), Capability::ReadWrite, state, None).unwrap();
3128        buffer.apply_ops(
3129            ops.into_iter()
3130                .map(|op| proto::deserialize_operation(op).unwrap()),
3131            cx,
3132        );
3133        buffer
3134    });
3135    assert_eq!(buffer2.read(cx).text(), "abcDF");
3136}
3137
3138#[gpui::test]
3139fn test_branch_and_merge(cx: &mut TestAppContext) {
3140    cx.update(|cx| init_settings(cx, |_| {}));
3141
3142    let base = cx.new(|cx| Buffer::local("one\ntwo\nthree\n", cx));
3143
3144    // Create a remote replica of the base buffer.
3145    let base_replica = cx.new(|cx| {
3146        Buffer::from_proto(
3147            ReplicaId::new(1),
3148            Capability::ReadWrite,
3149            base.read(cx).to_proto(cx),
3150            None,
3151        )
3152        .unwrap()
3153    });
3154    base.update(cx, |_buffer, cx| {
3155        cx.subscribe(&base_replica, |this, _, event, cx| {
3156            if let BufferEvent::Operation {
3157                operation,
3158                is_local: true,
3159            } = event
3160            {
3161                this.apply_ops([operation.clone()], cx);
3162            }
3163        })
3164        .detach();
3165    });
3166
3167    // Create a branch, which initially has the same state as the base buffer.
3168    let branch = base.update(cx, |buffer, cx| buffer.branch(cx));
3169    branch.read_with(cx, |buffer, _| {
3170        assert_eq!(buffer.text(), "one\ntwo\nthree\n");
3171    });
3172
3173    // Edits to the branch are not applied to the base.
3174    branch.update(cx, |buffer, cx| {
3175        buffer.edit(
3176            [
3177                (Point::new(1, 0)..Point::new(1, 0), "1.5\n"),
3178                (Point::new(2, 0)..Point::new(2, 5), "THREE"),
3179            ],
3180            None,
3181            cx,
3182        )
3183    });
3184    branch.read_with(cx, |buffer, cx| {
3185        assert_eq!(base.read(cx).text(), "one\ntwo\nthree\n");
3186        assert_eq!(buffer.text(), "one\n1.5\ntwo\nTHREE\n");
3187    });
3188
3189    // Convert from branch buffer ranges to the corresponding ranges in the
3190    // base buffer.
3191    branch.read_with(cx, |buffer, cx| {
3192        assert_eq!(
3193            buffer.range_to_version(4..7, &base.read(cx).version()),
3194            4..4
3195        );
3196        assert_eq!(
3197            buffer.range_to_version(2..9, &base.read(cx).version()),
3198            2..5
3199        );
3200    });
3201
3202    // Edits to the base are applied to the branch.
3203    base.update(cx, |buffer, cx| {
3204        buffer.edit([(Point::new(0, 0)..Point::new(0, 0), "ZERO\n")], None, cx)
3205    });
3206    branch.read_with(cx, |buffer, cx| {
3207        assert_eq!(base.read(cx).text(), "ZERO\none\ntwo\nthree\n");
3208        assert_eq!(buffer.text(), "ZERO\none\n1.5\ntwo\nTHREE\n");
3209    });
3210
3211    // Edits to any replica of the base are applied to the branch.
3212    base_replica.update(cx, |buffer, cx| {
3213        buffer.edit([(Point::new(2, 0)..Point::new(2, 0), "2.5\n")], None, cx)
3214    });
3215    branch.read_with(cx, |buffer, cx| {
3216        assert_eq!(base.read(cx).text(), "ZERO\none\ntwo\n2.5\nthree\n");
3217        assert_eq!(buffer.text(), "ZERO\none\n1.5\ntwo\n2.5\nTHREE\n");
3218    });
3219
3220    // Merging the branch applies all of its changes to the base.
3221    branch.update(cx, |buffer, cx| {
3222        buffer.merge_into_base(Vec::new(), cx);
3223    });
3224
3225    branch.update(cx, |buffer, cx| {
3226        assert_eq!(base.read(cx).text(), "ZERO\none\n1.5\ntwo\n2.5\nTHREE\n");
3227        assert_eq!(buffer.text(), "ZERO\none\n1.5\ntwo\n2.5\nTHREE\n");
3228    });
3229}
3230
3231#[gpui::test]
3232fn test_merge_into_base(cx: &mut TestAppContext) {
3233    cx.update(|cx| init_settings(cx, |_| {}));
3234
3235    let base = cx.new(|cx| Buffer::local("abcdefghijk", cx));
3236    let branch = base.update(cx, |buffer, cx| buffer.branch(cx));
3237
3238    // Make 3 edits, merge one into the base.
3239    branch.update(cx, |branch, cx| {
3240        branch.edit([(0..3, "ABC"), (7..9, "HI"), (11..11, "LMN")], None, cx);
3241        branch.merge_into_base(vec![5..8], cx);
3242    });
3243
3244    branch.read_with(cx, |branch, _| assert_eq!(branch.text(), "ABCdefgHIjkLMN"));
3245    base.read_with(cx, |base, _| assert_eq!(base.text(), "abcdefgHIjk"));
3246
3247    // Undo the one already-merged edit. Merge that into the base.
3248    branch.update(cx, |branch, cx| {
3249        branch.edit([(7..9, "hi")], None, cx);
3250        branch.merge_into_base(vec![5..8], cx);
3251    });
3252    base.read_with(cx, |base, _| assert_eq!(base.text(), "abcdefghijk"));
3253
3254    // Merge an insertion into the base.
3255    branch.update(cx, |branch, cx| {
3256        branch.merge_into_base(vec![11..11], cx);
3257    });
3258
3259    branch.read_with(cx, |branch, _| assert_eq!(branch.text(), "ABCdefghijkLMN"));
3260    base.read_with(cx, |base, _| assert_eq!(base.text(), "abcdefghijkLMN"));
3261
3262    // Deleted the inserted text and merge that into the base.
3263    branch.update(cx, |branch, cx| {
3264        branch.edit([(11..14, "")], None, cx);
3265        branch.merge_into_base(vec![10..11], cx);
3266    });
3267
3268    base.read_with(cx, |base, _| assert_eq!(base.text(), "abcdefghijk"));
3269}
3270
3271#[gpui::test]
3272fn test_undo_after_merge_into_base(cx: &mut TestAppContext) {
3273    cx.update(|cx| init_settings(cx, |_| {}));
3274
3275    let base = cx.new(|cx| Buffer::local("abcdefghijk", cx));
3276    let branch = base.update(cx, |buffer, cx| buffer.branch(cx));
3277
3278    // Make 2 edits, merge one into the base.
3279    branch.update(cx, |branch, cx| {
3280        branch.edit([(0..3, "ABC"), (7..9, "HI")], None, cx);
3281        branch.merge_into_base(vec![7..7], cx);
3282    });
3283    base.read_with(cx, |base, _| assert_eq!(base.text(), "abcdefgHIjk"));
3284    branch.read_with(cx, |branch, _| assert_eq!(branch.text(), "ABCdefgHIjk"));
3285
3286    // Undo the merge in the base buffer.
3287    base.update(cx, |base, cx| {
3288        base.undo(cx);
3289    });
3290    base.read_with(cx, |base, _| assert_eq!(base.text(), "abcdefghijk"));
3291    branch.read_with(cx, |branch, _| assert_eq!(branch.text(), "ABCdefgHIjk"));
3292
3293    // Merge that operation into the base again.
3294    branch.update(cx, |branch, cx| {
3295        branch.merge_into_base(vec![7..7], cx);
3296    });
3297    base.read_with(cx, |base, _| assert_eq!(base.text(), "abcdefgHIjk"));
3298    branch.read_with(cx, |branch, _| assert_eq!(branch.text(), "ABCdefgHIjk"));
3299}
3300
3301#[gpui::test]
3302async fn test_preview_edits(cx: &mut TestAppContext) {
3303    cx.update(|cx| {
3304        init_settings(cx, |_| {});
3305        theme::init(theme::LoadThemes::JustBase, cx);
3306    });
3307
3308    let insertion_style = HighlightStyle {
3309        background_color: Some(cx.read(|cx| cx.theme().status().created_background)),
3310        ..Default::default()
3311    };
3312    let deletion_style = HighlightStyle {
3313        background_color: Some(cx.read(|cx| cx.theme().status().deleted_background)),
3314        ..Default::default()
3315    };
3316
3317    // no edits
3318    assert_preview_edits(
3319        indoc! {"
3320        fn test_empty() -> bool {
3321            false
3322        }"
3323        },
3324        vec![],
3325        true,
3326        cx,
3327        |hl| {
3328            assert!(hl.text.is_empty());
3329            assert!(hl.highlights.is_empty());
3330        },
3331    )
3332    .await;
3333
3334    // only insertions
3335    assert_preview_edits(
3336        indoc! {"
3337        fn calculate_area(: f64) -> f64 {
3338            std::f64::consts::PI * .powi(2)
3339        }"
3340        },
3341        vec![
3342            (Point::new(0, 18)..Point::new(0, 18), "radius"),
3343            (Point::new(1, 27)..Point::new(1, 27), "radius"),
3344        ],
3345        true,
3346        cx,
3347        |hl| {
3348            assert_eq!(
3349                hl.text,
3350                indoc! {"
3351                fn calculate_area(radius: f64) -> f64 {
3352                    std::f64::consts::PI * radius.powi(2)"
3353                }
3354            );
3355
3356            assert_eq!(hl.highlights.len(), 2);
3357            assert_eq!(hl.highlights[0], ((18..24), insertion_style));
3358            assert_eq!(hl.highlights[1], ((67..73), insertion_style));
3359        },
3360    )
3361    .await;
3362
3363    // insertions & deletions
3364    assert_preview_edits(
3365        indoc! {"
3366        struct Person {
3367            first_name: String,
3368        }
3369
3370        impl Person {
3371            fn first_name(&self) -> &String {
3372                &self.first_name
3373            }
3374        }"
3375        },
3376        vec![
3377            (Point::new(1, 4)..Point::new(1, 9), "last"),
3378            (Point::new(5, 7)..Point::new(5, 12), "last"),
3379            (Point::new(6, 14)..Point::new(6, 19), "last"),
3380        ],
3381        true,
3382        cx,
3383        |hl| {
3384            assert_eq!(
3385                hl.text,
3386                indoc! {"
3387                        firstlast_name: String,
3388                    }
3389
3390                    impl Person {
3391                        fn firstlast_name(&self) -> &String {
3392                            &self.firstlast_name"
3393                }
3394            );
3395
3396            assert_eq!(hl.highlights.len(), 6);
3397            assert_eq!(hl.highlights[0], ((4..9), deletion_style));
3398            assert_eq!(hl.highlights[1], ((9..13), insertion_style));
3399            assert_eq!(hl.highlights[2], ((52..57), deletion_style));
3400            assert_eq!(hl.highlights[3], ((57..61), insertion_style));
3401            assert_eq!(hl.highlights[4], ((101..106), deletion_style));
3402            assert_eq!(hl.highlights[5], ((106..110), insertion_style));
3403        },
3404    )
3405    .await;
3406
3407    async fn assert_preview_edits(
3408        text: &str,
3409        edits: Vec<(Range<Point>, &str)>,
3410        include_deletions: bool,
3411        cx: &mut TestAppContext,
3412        assert_fn: impl Fn(HighlightedText),
3413    ) {
3414        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(rust_lang(), cx));
3415        let edits = buffer.read_with(cx, |buffer, _| {
3416            edits
3417                .into_iter()
3418                .map(|(range, text)| {
3419                    (
3420                        buffer.anchor_before(range.start)..buffer.anchor_after(range.end),
3421                        text.into(),
3422                    )
3423                })
3424                .collect::<Arc<[_]>>()
3425        });
3426        let edit_preview = buffer
3427            .read_with(cx, |buffer, cx| buffer.preview_edits(edits.clone(), cx))
3428            .await;
3429        let highlighted_edits = cx.read(|cx| {
3430            edit_preview.highlight_edits(&buffer.read(cx).snapshot(), &edits, include_deletions, cx)
3431        });
3432        assert_fn(highlighted_edits);
3433    }
3434}
3435
3436#[gpui::test(iterations = 100)]
3437fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
3438    let min_peers = env::var("MIN_PEERS")
3439        .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
3440        .unwrap_or(1);
3441    let max_peers = env::var("MAX_PEERS")
3442        .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
3443        .unwrap_or(5);
3444    let operations = env::var("OPERATIONS")
3445        .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
3446        .unwrap_or(10);
3447
3448    let base_text_len = rng.random_range(0..10);
3449    let base_text = RandomCharIter::new(&mut rng)
3450        .take(base_text_len)
3451        .collect::<String>();
3452    let mut replica_ids = Vec::new();
3453    let mut buffers = Vec::new();
3454    let network = Arc::new(Mutex::new(Network::new(rng.clone())));
3455    let base_buffer = cx.new(|cx| Buffer::local(base_text.as_str(), cx));
3456
3457    for i in 0..rng.random_range(min_peers..=max_peers) {
3458        let buffer = cx.new(|cx| {
3459            let state = base_buffer.read(cx).to_proto(cx);
3460            let ops = cx
3461                .foreground_executor()
3462                .block_on(base_buffer.read(cx).serialize_ops(None, cx));
3463            let mut buffer =
3464                Buffer::from_proto(ReplicaId::new(i as u16), Capability::ReadWrite, state, None)
3465                    .unwrap();
3466            buffer.apply_ops(
3467                ops.into_iter()
3468                    .map(|op| proto::deserialize_operation(op).unwrap()),
3469                cx,
3470            );
3471            buffer.set_group_interval(Duration::from_millis(rng.random_range(0..=200)));
3472            let network = network.clone();
3473            cx.subscribe(&cx.entity(), move |buffer, _, event, _| {
3474                if let BufferEvent::Operation {
3475                    operation,
3476                    is_local: true,
3477                } = event
3478                {
3479                    network.lock().broadcast(
3480                        buffer.replica_id(),
3481                        vec![proto::serialize_operation(operation)],
3482                    );
3483                }
3484            })
3485            .detach();
3486            buffer
3487        });
3488
3489        buffers.push(buffer);
3490        replica_ids.push(ReplicaId::new(i as u16));
3491        network.lock().add_peer(ReplicaId::new(i as u16));
3492        log::info!("Adding initial peer with replica id {:?}", replica_ids[i]);
3493    }
3494
3495    log::info!("initial text: {:?}", base_text);
3496
3497    let mut now = Instant::now();
3498    let mut mutation_count = operations;
3499    let mut next_diagnostic_id = 0;
3500    let mut active_selections = BTreeMap::default();
3501    loop {
3502        let replica_index = rng.random_range(0..replica_ids.len());
3503        let replica_id = replica_ids[replica_index];
3504        let buffer = &mut buffers[replica_index];
3505        let mut new_buffer = None;
3506        match rng.random_range(0..100) {
3507            0..=29 if mutation_count != 0 => {
3508                buffer.update(cx, |buffer, cx| {
3509                    buffer.start_transaction_at(now);
3510                    buffer.randomly_edit(&mut rng, 5, cx);
3511                    buffer.end_transaction_at(now, cx);
3512                    log::info!("buffer {:?} text: {:?}", buffer.replica_id(), buffer.text());
3513                });
3514                mutation_count -= 1;
3515            }
3516            30..=39 if mutation_count != 0 => {
3517                buffer.update(cx, |buffer, cx| {
3518                    if rng.random_bool(0.2) {
3519                        log::info!("peer {:?} clearing active selections", replica_id);
3520                        active_selections.remove(&replica_id);
3521                        buffer.remove_active_selections(cx);
3522                    } else {
3523                        let mut selections = Vec::new();
3524                        for id in 0..rng.random_range(1..=5) {
3525                            let range = buffer.random_byte_range(0, &mut rng);
3526                            selections.push(Selection {
3527                                id,
3528                                start: buffer.anchor_before(range.start),
3529                                end: buffer.anchor_before(range.end),
3530                                reversed: false,
3531                                goal: SelectionGoal::None,
3532                            });
3533                        }
3534                        let selections: Arc<[Selection<Anchor>]> = selections.into();
3535                        log::info!(
3536                            "peer {:?} setting active selections: {:?}",
3537                            replica_id,
3538                            selections
3539                        );
3540                        active_selections.insert(replica_id, selections.clone());
3541                        buffer.set_active_selections(selections, false, Default::default(), cx);
3542                    }
3543                });
3544                mutation_count -= 1;
3545            }
3546            40..=49 if mutation_count != 0 && replica_id == ReplicaId::REMOTE_SERVER => {
3547                let entry_count = rng.random_range(1..=5);
3548                buffer.update(cx, |buffer, cx| {
3549                    let diagnostics = DiagnosticSet::new(
3550                        (0..entry_count).map(|_| {
3551                            let range = buffer.random_byte_range(0, &mut rng);
3552                            let range = range.to_point_utf16(buffer);
3553                            let range = range.start..range.end;
3554                            DiagnosticEntry {
3555                                range,
3556                                diagnostic: Diagnostic {
3557                                    message: post_inc(&mut next_diagnostic_id).to_string(),
3558                                    ..Default::default()
3559                                },
3560                            }
3561                        }),
3562                        buffer,
3563                    );
3564                    log::info!(
3565                        "peer {:?} setting diagnostics: {:?}",
3566                        replica_id,
3567                        diagnostics
3568                    );
3569                    buffer.update_diagnostics(LanguageServerId(0), diagnostics, cx);
3570                });
3571                mutation_count -= 1;
3572            }
3573            50..=59 if replica_ids.len() < max_peers => {
3574                let old_buffer_state = buffer.read(cx).to_proto(cx);
3575                let old_buffer_ops = cx
3576                    .foreground_executor()
3577                    .block_on(buffer.read(cx).serialize_ops(None, cx));
3578                let new_replica_id = (0..=replica_ids.len() as u16)
3579                    .map(ReplicaId::new)
3580                    .filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
3581                    .choose(&mut rng)
3582                    .unwrap();
3583                log::info!(
3584                    "Adding new replica {:?} (replicating from {:?})",
3585                    new_replica_id,
3586                    replica_id
3587                );
3588                new_buffer = Some(cx.new(|cx| {
3589                    let mut new_buffer = Buffer::from_proto(
3590                        new_replica_id,
3591                        Capability::ReadWrite,
3592                        old_buffer_state,
3593                        None,
3594                    )
3595                    .unwrap();
3596                    new_buffer.apply_ops(
3597                        old_buffer_ops
3598                            .into_iter()
3599                            .map(|op| deserialize_operation(op).unwrap()),
3600                        cx,
3601                    );
3602                    log::info!(
3603                        "New replica {:?} text: {:?}",
3604                        new_buffer.replica_id(),
3605                        new_buffer.text()
3606                    );
3607                    new_buffer.set_group_interval(Duration::from_millis(rng.random_range(0..=200)));
3608                    let network = network.clone();
3609                    cx.subscribe(&cx.entity(), move |buffer, _, event, _| {
3610                        if let BufferEvent::Operation {
3611                            operation,
3612                            is_local: true,
3613                        } = event
3614                        {
3615                            network.lock().broadcast(
3616                                buffer.replica_id(),
3617                                vec![proto::serialize_operation(operation)],
3618                            );
3619                        }
3620                    })
3621                    .detach();
3622                    new_buffer
3623                }));
3624                network.lock().replicate(replica_id, new_replica_id);
3625
3626                if new_replica_id.as_u16() as usize == replica_ids.len() {
3627                    replica_ids.push(new_replica_id);
3628                } else {
3629                    let new_buffer = new_buffer.take().unwrap();
3630                    while network.lock().has_unreceived(new_replica_id) {
3631                        let ops = network
3632                            .lock()
3633                            .receive(new_replica_id)
3634                            .into_iter()
3635                            .map(|op| proto::deserialize_operation(op).unwrap());
3636                        if ops.len() > 0 {
3637                            log::info!(
3638                                "peer {:?} (version: {:?}) applying {} ops from the network. {:?}",
3639                                new_replica_id,
3640                                buffer.read(cx).version(),
3641                                ops.len(),
3642                                ops
3643                            );
3644                            new_buffer.update(cx, |new_buffer, cx| {
3645                                new_buffer.apply_ops(ops, cx);
3646                            });
3647                        }
3648                    }
3649                    buffers[new_replica_id.as_u16() as usize] = new_buffer;
3650                }
3651            }
3652            60..=69 if mutation_count != 0 => {
3653                buffer.update(cx, |buffer, cx| {
3654                    buffer.randomly_undo_redo(&mut rng, cx);
3655                    log::info!("buffer {:?} text: {:?}", buffer.replica_id(), buffer.text());
3656                });
3657                mutation_count -= 1;
3658            }
3659            _ if network.lock().has_unreceived(replica_id) => {
3660                let ops = network
3661                    .lock()
3662                    .receive(replica_id)
3663                    .into_iter()
3664                    .map(|op| proto::deserialize_operation(op).unwrap());
3665                if ops.len() > 0 {
3666                    log::info!(
3667                        "peer {:?} (version: {:?}) applying {} ops from the network. {:?}",
3668                        replica_id,
3669                        buffer.read(cx).version(),
3670                        ops.len(),
3671                        ops
3672                    );
3673                    buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx));
3674                }
3675            }
3676            _ => {}
3677        }
3678
3679        now += Duration::from_millis(rng.random_range(0..=200));
3680        buffers.extend(new_buffer);
3681
3682        for buffer in &buffers {
3683            buffer.read(cx).check_invariants();
3684        }
3685
3686        if mutation_count == 0 && network.lock().is_idle() {
3687            break;
3688        }
3689    }
3690
3691    let first_buffer = buffers[0].read(cx).snapshot();
3692    for buffer in &buffers[1..] {
3693        let buffer = buffer.read(cx).snapshot();
3694        assert_eq!(
3695            buffer.version(),
3696            first_buffer.version(),
3697            "Replica {:?} version != Replica 0 version",
3698            buffer.replica_id()
3699        );
3700        assert_eq!(
3701            buffer.text(),
3702            first_buffer.text(),
3703            "Replica {:?} text != Replica 0 text",
3704            buffer.replica_id()
3705        );
3706        assert_eq!(
3707            buffer
3708                .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
3709                .collect::<Vec<_>>(),
3710            first_buffer
3711                .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
3712                .collect::<Vec<_>>(),
3713            "Replica {:?} diagnostics != Replica 0 diagnostics",
3714            buffer.replica_id()
3715        );
3716    }
3717
3718    for buffer in &buffers {
3719        let buffer = buffer.read(cx).snapshot();
3720        let actual_remote_selections = buffer
3721            .selections_in_range(Anchor::min_max_range_for_buffer(buffer.remote_id()), false)
3722            .map(|(replica_id, _, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
3723            .collect::<Vec<_>>();
3724        let expected_remote_selections = active_selections
3725            .iter()
3726            .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
3727            .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
3728            .collect::<Vec<_>>();
3729        assert_eq!(
3730            actual_remote_selections,
3731            expected_remote_selections,
3732            "Replica {:?} remote selections != expected selections",
3733            buffer.replica_id()
3734        );
3735    }
3736}
3737
3738#[test]
3739fn test_contiguous_ranges() {
3740    assert_eq!(
3741        contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
3742        &[1..4, 5..7, 9..13]
3743    );
3744
3745    // Respects the `max_len` parameter
3746    assert_eq!(
3747        contiguous_ranges(
3748            [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
3749            3
3750        )
3751        .collect::<Vec<_>>(),
3752        &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
3753    );
3754}
3755
3756#[gpui::test]
3757fn test_insertion_after_deletion(cx: &mut gpui::App) {
3758    let buffer = cx.new(|cx| Buffer::local("struct Foo {\n    \n}", cx));
3759    buffer.update(cx, |buffer, cx| {
3760        let mut anchor = buffer.anchor_after(17);
3761        buffer.edit([(12..18, "")], None, cx);
3762        let snapshot = buffer.snapshot();
3763        assert_eq!(snapshot.text(), "struct Foo {}");
3764        if !anchor.is_valid(&snapshot) {
3765            anchor = snapshot.anchor_after(snapshot.offset_for_anchor(&anchor));
3766        }
3767        buffer.edit([(anchor..anchor, "\n")], None, cx);
3768        buffer.edit([(anchor..anchor, "field1:")], None, cx);
3769        buffer.edit([(anchor..anchor, " i32,")], None, cx);
3770        let snapshot = buffer.snapshot();
3771        assert_eq!(snapshot.text(), "struct Foo {\nfield1: i32,}");
3772    })
3773}
3774
3775#[gpui::test(iterations = 500)]
3776fn test_trailing_whitespace_ranges(mut rng: StdRng) {
3777    // Generate a random multi-line string containing
3778    // some lines with trailing whitespace.
3779    let mut text = String::new();
3780    for _ in 0..rng.random_range(0..16) {
3781        for _ in 0..rng.random_range(0..36) {
3782            text.push(match rng.random_range(0..10) {
3783                0..=1 => ' ',
3784                3 => '\t',
3785                _ => rng.random_range('a'..='z'),
3786            });
3787        }
3788        text.push('\n');
3789    }
3790
3791    match rng.random_range(0..10) {
3792        // sometimes remove the last newline
3793        0..=1 => drop(text.pop()), //
3794
3795        // sometimes add extra newlines
3796        2..=3 => text.push_str(&"\n".repeat(rng.random_range(1..5))),
3797        _ => {}
3798    }
3799
3800    let rope = Rope::from(text.as_str());
3801    let actual_ranges = trailing_whitespace_ranges(&rope);
3802    let expected_ranges = TRAILING_WHITESPACE_REGEX
3803        .find_iter(&text)
3804        .map(|m| m.range())
3805        .collect::<Vec<_>>();
3806    assert_eq!(
3807        actual_ranges,
3808        expected_ranges,
3809        "wrong ranges for text lines:\n{:?}",
3810        text.split('\n').collect::<Vec<_>>()
3811    );
3812}
3813
3814#[gpui::test]
3815fn test_words_in_range(cx: &mut gpui::App) {
3816    init_settings(cx, |_| {});
3817
3818    // The first line are words excluded from the results with heuristics, we do not expect them in the test assertions.
3819    let contents = r#"
38200_isize 123 3.4 4  
3821let word=öäpple.bar你 Öäpple word2-öÄpPlE-Pizza-word ÖÄPPLE word
3822    "#;
3823
3824    let buffer = cx.new(|cx| {
3825        let buffer = Buffer::local(contents, cx).with_language(rust_lang(), cx);
3826        assert_eq!(buffer.text(), contents);
3827        buffer.check_invariants();
3828        buffer
3829    });
3830
3831    buffer.update(cx, |buffer, _| {
3832        let snapshot = buffer.snapshot();
3833        assert_eq!(
3834            BTreeSet::from_iter(["Pizza".to_string()]),
3835            snapshot
3836                .words_in_range(WordsQuery {
3837                    fuzzy_contents: Some("piz"),
3838                    skip_digits: true,
3839                    range: 0..snapshot.len(),
3840                })
3841                .into_keys()
3842                .collect::<BTreeSet<_>>()
3843        );
3844        assert_eq!(
3845            BTreeSet::from_iter([
3846                "öäpple".to_string(),
3847                "Öäpple".to_string(),
3848                "öÄpPlE".to_string(),
3849                "ÖÄPPLE".to_string(),
3850            ]),
3851            snapshot
3852                .words_in_range(WordsQuery {
3853                    fuzzy_contents: Some("öp"),
3854                    skip_digits: true,
3855                    range: 0..snapshot.len(),
3856                })
3857                .into_keys()
3858                .collect::<BTreeSet<_>>()
3859        );
3860        assert_eq!(
3861            BTreeSet::from_iter([
3862                "öÄpPlE".to_string(),
3863                "Öäpple".to_string(),
3864                "ÖÄPPLE".to_string(),
3865                "öäpple".to_string(),
3866            ]),
3867            snapshot
3868                .words_in_range(WordsQuery {
3869                    fuzzy_contents: Some("öÄ"),
3870                    skip_digits: true,
3871                    range: 0..snapshot.len(),
3872                })
3873                .into_keys()
3874                .collect::<BTreeSet<_>>()
3875        );
3876        assert_eq!(
3877            BTreeSet::default(),
3878            snapshot
3879                .words_in_range(WordsQuery {
3880                    fuzzy_contents: Some("öÄ好"),
3881                    skip_digits: true,
3882                    range: 0..snapshot.len(),
3883                })
3884                .into_keys()
3885                .collect::<BTreeSet<_>>()
3886        );
3887        assert_eq!(
3888            BTreeSet::from_iter(["bar你".to_string(),]),
3889            snapshot
3890                .words_in_range(WordsQuery {
3891                    fuzzy_contents: Some(""),
3892                    skip_digits: true,
3893                    range: 0..snapshot.len(),
3894                })
3895                .into_keys()
3896                .collect::<BTreeSet<_>>()
3897        );
3898        assert_eq!(
3899            BTreeSet::default(),
3900            snapshot
3901                .words_in_range(WordsQuery {
3902                    fuzzy_contents: Some(""),
3903                    skip_digits: true,
3904                    range: 0..snapshot.len(),
3905                },)
3906                .into_keys()
3907                .collect::<BTreeSet<_>>()
3908        );
3909        assert_eq!(
3910            BTreeSet::from_iter([
3911                "bar你".to_string(),
3912                "öÄpPlE".to_string(),
3913                "Öäpple".to_string(),
3914                "ÖÄPPLE".to_string(),
3915                "öäpple".to_string(),
3916                "let".to_string(),
3917                "Pizza".to_string(),
3918                "word".to_string(),
3919                "word2".to_string(),
3920            ]),
3921            snapshot
3922                .words_in_range(WordsQuery {
3923                    fuzzy_contents: None,
3924                    skip_digits: true,
3925                    range: 0..snapshot.len(),
3926                })
3927                .into_keys()
3928                .collect::<BTreeSet<_>>()
3929        );
3930        assert_eq!(
3931            BTreeSet::from_iter([
3932                "0_isize".to_string(),
3933                "123".to_string(),
3934                "3".to_string(),
3935                "4".to_string(),
3936                "bar你".to_string(),
3937                "öÄpPlE".to_string(),
3938                "Öäpple".to_string(),
3939                "ÖÄPPLE".to_string(),
3940                "öäpple".to_string(),
3941                "let".to_string(),
3942                "Pizza".to_string(),
3943                "word".to_string(),
3944                "word2".to_string(),
3945            ]),
3946            snapshot
3947                .words_in_range(WordsQuery {
3948                    fuzzy_contents: None,
3949                    skip_digits: false,
3950                    range: 0..snapshot.len(),
3951                })
3952                .into_keys()
3953                .collect::<BTreeSet<_>>()
3954        );
3955    });
3956}
3957
3958fn ruby_lang() -> Language {
3959    Language::new(
3960        LanguageConfig {
3961            name: "Ruby".into(),
3962            matcher: LanguageMatcher {
3963                path_suffixes: vec!["rb".to_string()],
3964                ..Default::default()
3965            },
3966            line_comments: vec!["# ".into()],
3967            ..Default::default()
3968        },
3969        Some(tree_sitter_ruby::LANGUAGE.into()),
3970    )
3971    .with_indents_query(
3972        r#"
3973            (class "end" @end) @indent
3974            (method "end" @end) @indent
3975            (rescue) @outdent
3976            (then) @indent
3977        "#,
3978    )
3979    .unwrap()
3980}
3981
3982fn html_lang() -> Language {
3983    Language::new(
3984        LanguageConfig {
3985            name: LanguageName::new_static("HTML"),
3986            block_comment: Some(BlockCommentConfig {
3987                start: "<!--".into(),
3988                prefix: "".into(),
3989                end: "-->".into(),
3990                tab_size: 0,
3991            }),
3992            ..Default::default()
3993        },
3994        Some(tree_sitter_html::LANGUAGE.into()),
3995    )
3996    .with_indents_query(
3997        "
3998        (element
3999          (start_tag) @start
4000          (end_tag)? @end) @indent
4001        ",
4002    )
4003    .unwrap()
4004    .with_injection_query(
4005        r#"
4006        (script_element
4007            (raw_text) @injection.content
4008            (#set! injection.language "javascript"))
4009        "#,
4010    )
4011    .unwrap()
4012}
4013
4014fn erb_lang() -> Language {
4015    Language::new(
4016        LanguageConfig {
4017            name: "HTML+ERB".into(),
4018            matcher: LanguageMatcher {
4019                path_suffixes: vec!["erb".to_string()],
4020                ..Default::default()
4021            },
4022            block_comment: Some(BlockCommentConfig {
4023                start: "<%#".into(),
4024                prefix: "".into(),
4025                end: "%>".into(),
4026                tab_size: 0,
4027            }),
4028            ..Default::default()
4029        },
4030        Some(tree_sitter_embedded_template::LANGUAGE.into()),
4031    )
4032    .with_injection_query(
4033        r#"
4034            (
4035                (code) @content
4036                (#set! "language" "ruby")
4037                (#set! "combined")
4038            )
4039
4040            (
4041                (content) @content
4042                (#set! "language" "html")
4043                (#set! "combined")
4044            )
4045        "#,
4046    )
4047    .unwrap()
4048}
4049
4050fn json_lang() -> Language {
4051    Language::new(
4052        LanguageConfig {
4053            name: "Json".into(),
4054            matcher: LanguageMatcher {
4055                path_suffixes: vec!["js".to_string()],
4056                ..Default::default()
4057            },
4058            ..Default::default()
4059        },
4060        Some(tree_sitter_json::LANGUAGE.into()),
4061    )
4062}
4063
4064fn javascript_lang() -> Language {
4065    Language::new(
4066        LanguageConfig {
4067            name: "JavaScript".into(),
4068            ..Default::default()
4069        },
4070        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
4071    )
4072    .with_brackets_query(
4073        r#"
4074        ("{" @open "}" @close)
4075        ("(" @open ")" @close)
4076        "#,
4077    )
4078    .unwrap()
4079    .with_indents_query(
4080        r#"
4081        (object "}" @end) @indent
4082        "#,
4083    )
4084    .unwrap()
4085}
4086
4087pub fn markdown_inline_lang() -> Language {
4088    Language::new(
4089        LanguageConfig {
4090            name: "Markdown-Inline".into(),
4091            hidden: true,
4092            ..LanguageConfig::default()
4093        },
4094        Some(tree_sitter_md::INLINE_LANGUAGE.into()),
4095    )
4096    .with_highlights_query("(emphasis) @emphasis")
4097    .unwrap()
4098}
4099
4100fn get_tree_sexp(buffer: &Entity<Buffer>, cx: &mut gpui::TestAppContext) -> String {
4101    buffer.update(cx, |buffer, _| {
4102        let snapshot = buffer.snapshot();
4103        let layers = snapshot.syntax.layers(buffer.as_text_snapshot());
4104        layers[0].node().to_sexp()
4105    })
4106}
4107
4108// Assert that the enclosing bracket ranges around the selection match the pairs indicated by the marked text in `range_markers`
4109#[track_caller]
4110fn assert_bracket_pairs(
4111    selection_text: &'static str,
4112    bracket_pair_texts: Vec<&'static str>,
4113    language: Arc<Language>,
4114    cx: &mut App,
4115) {
4116    let (expected_text, selection_ranges) = marked_text_ranges(selection_text, false);
4117    let buffer = cx.new(|cx| Buffer::local(expected_text.clone(), cx).with_language(language, cx));
4118    let buffer = buffer.update(cx, |buffer, _cx| buffer.snapshot());
4119
4120    let selection_range = selection_ranges[0].clone();
4121
4122    let bracket_pairs = bracket_pair_texts
4123        .into_iter()
4124        .map(|pair_text| {
4125            let (bracket_text, ranges) = marked_text_ranges(pair_text, false);
4126            assert_eq!(bracket_text, expected_text);
4127            (ranges[0].clone(), ranges[1].clone())
4128        })
4129        .collect::<Vec<_>>();
4130
4131    assert_set_eq!(
4132        buffer
4133            .bracket_ranges(selection_range)
4134            .map(|pair| (pair.open_range, pair.close_range))
4135            .collect::<Vec<_>>(),
4136        bracket_pairs
4137    );
4138}
4139
4140fn init_settings(cx: &mut App, f: fn(&mut AllLanguageSettingsContent)) {
4141    let settings_store = SettingsStore::test(cx);
4142    cx.set_global(settings_store);
4143    cx.update_global::<SettingsStore, _>(|settings, cx| {
4144        settings.update_user_settings(cx, |content| f(&mut content.project.all_languages));
4145    });
4146}
4147
4148#[gpui::test(iterations = 100)]
4149fn test_random_chunk_bitmaps(cx: &mut App, mut rng: StdRng) {
4150    use util::RandomCharIter;
4151
4152    // Generate random text
4153    let len = rng.random_range(0..10000);
4154    let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
4155
4156    let buffer = cx.new(|cx| Buffer::local(text, cx));
4157    let snapshot = buffer.read(cx).snapshot();
4158
4159    // Get all chunks and verify their bitmaps
4160    let chunks = snapshot.chunks(0..snapshot.len(), false);
4161
4162    for chunk in chunks {
4163        let chunk_text = chunk.text;
4164        let chars_bitmap = chunk.chars;
4165        let tabs_bitmap = chunk.tabs;
4166
4167        // Check empty chunks have empty bitmaps
4168        if chunk_text.is_empty() {
4169            assert_eq!(
4170                chars_bitmap, 0,
4171                "Empty chunk should have empty chars bitmap"
4172            );
4173            assert_eq!(tabs_bitmap, 0, "Empty chunk should have empty tabs bitmap");
4174            continue;
4175        }
4176
4177        // Verify that chunk text doesn't exceed 128 bytes
4178        assert!(
4179            chunk_text.len() <= 128,
4180            "Chunk text length {} exceeds 128 bytes",
4181            chunk_text.len()
4182        );
4183
4184        // Verify chars bitmap
4185        let char_indices = chunk_text
4186            .char_indices()
4187            .map(|(i, _)| i)
4188            .collect::<Vec<_>>();
4189
4190        for byte_idx in 0..chunk_text.len() {
4191            let should_have_bit = char_indices.contains(&byte_idx);
4192            let has_bit = chars_bitmap & (1 << byte_idx) != 0;
4193
4194            if has_bit != should_have_bit {
4195                eprintln!("Chunk text bytes: {:?}", chunk_text.as_bytes());
4196                eprintln!("Char indices: {:?}", char_indices);
4197                eprintln!("Chars bitmap: {:#b}", chars_bitmap);
4198            }
4199
4200            assert_eq!(
4201                has_bit, should_have_bit,
4202                "Chars bitmap mismatch at byte index {} in chunk {:?}. Expected bit: {}, Got bit: {}",
4203                byte_idx, chunk_text, should_have_bit, has_bit
4204            );
4205        }
4206
4207        // Verify tabs bitmap
4208        for (byte_idx, byte) in chunk_text.bytes().enumerate() {
4209            let is_tab = byte == b'\t';
4210            let has_bit = tabs_bitmap & (1 << byte_idx) != 0;
4211
4212            if has_bit != is_tab {
4213                eprintln!("Chunk text bytes: {:?}", chunk_text.as_bytes());
4214                eprintln!("Tabs bitmap: {:#b}", tabs_bitmap);
4215                assert_eq!(
4216                    has_bit, is_tab,
4217                    "Tabs bitmap mismatch at byte index {} in chunk {:?}. Byte: {:?}, Expected bit: {}, Got bit: {}",
4218                    byte_idx, chunk_text, byte as char, is_tab, has_bit
4219                );
4220            }
4221        }
4222    }
4223}