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