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