buffer_tests.rs

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