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