buffer_tests.rs

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