buffer_tests.rs

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