tests.rs

   1use super::*;
   2use clock::ReplicaId;
   3use collections::BTreeMap;
   4use gpui::{ModelHandle, MutableAppContext};
   5use rand::prelude::*;
   6use std::{
   7    cell::RefCell,
   8    env,
   9    ops::Range,
  10    rc::Rc,
  11    time::{Duration, Instant},
  12};
  13use text::network::Network;
  14use unindent::Unindent as _;
  15use util::post_inc;
  16
  17#[cfg(test)]
  18#[ctor::ctor]
  19fn init_logger() {
  20    if std::env::var("RUST_LOG").is_ok() {
  21        env_logger::init();
  22    }
  23}
  24
  25#[gpui::test]
  26fn test_line_endings(cx: &mut gpui::MutableAppContext) {
  27    cx.add_model(|cx| {
  28        let mut buffer =
  29            Buffer::new(0, "one\r\ntwo\rthree", cx).with_language(Arc::new(rust_lang()), cx);
  30        assert_eq!(buffer.text(), "one\ntwo\nthree");
  31        assert_eq!(buffer.line_ending(), LineEnding::Windows);
  32
  33        buffer.check_invariants();
  34        buffer.edit_with_autoindent(
  35            [(buffer.len()..buffer.len(), "\r\nfour")],
  36            IndentSize::spaces(2),
  37            cx,
  38        );
  39        buffer.edit([(0..0, "zero\r\n")], cx);
  40        assert_eq!(buffer.text(), "zero\none\ntwo\nthree\nfour");
  41        assert_eq!(buffer.line_ending(), LineEnding::Windows);
  42        buffer.check_invariants();
  43
  44        buffer
  45    });
  46}
  47
  48#[gpui::test]
  49fn test_select_language() {
  50    let registry = LanguageRegistry::test();
  51    registry.add(Arc::new(Language::new(
  52        LanguageConfig {
  53            name: "Rust".into(),
  54            path_suffixes: vec!["rs".to_string()],
  55            ..Default::default()
  56        },
  57        Some(tree_sitter_rust::language()),
  58    )));
  59    registry.add(Arc::new(Language::new(
  60        LanguageConfig {
  61            name: "Make".into(),
  62            path_suffixes: vec!["Makefile".to_string(), "mk".to_string()],
  63            ..Default::default()
  64        },
  65        Some(tree_sitter_rust::language()),
  66    )));
  67
  68    // matching file extension
  69    assert_eq!(
  70        registry.select_language("zed/lib.rs").map(|l| l.name()),
  71        Some("Rust".into())
  72    );
  73    assert_eq!(
  74        registry.select_language("zed/lib.mk").map(|l| l.name()),
  75        Some("Make".into())
  76    );
  77
  78    // matching filename
  79    assert_eq!(
  80        registry.select_language("zed/Makefile").map(|l| l.name()),
  81        Some("Make".into())
  82    );
  83
  84    // matching suffix that is not the full file extension or filename
  85    assert_eq!(registry.select_language("zed/cars").map(|l| l.name()), None);
  86    assert_eq!(
  87        registry.select_language("zed/a.cars").map(|l| l.name()),
  88        None
  89    );
  90    assert_eq!(registry.select_language("zed/sumk").map(|l| l.name()), None);
  91}
  92
  93#[gpui::test]
  94fn test_edit_events(cx: &mut gpui::MutableAppContext) {
  95    let mut now = Instant::now();
  96    let buffer_1_events = Rc::new(RefCell::new(Vec::new()));
  97    let buffer_2_events = Rc::new(RefCell::new(Vec::new()));
  98
  99    let buffer1 = cx.add_model(|cx| Buffer::new(0, "abcdef", cx));
 100    let buffer2 = cx.add_model(|cx| Buffer::new(1, "abcdef", cx));
 101    let buffer1_ops = Rc::new(RefCell::new(Vec::new()));
 102    buffer1.update(cx, {
 103        let buffer1_ops = buffer1_ops.clone();
 104        |buffer, cx| {
 105            let buffer_1_events = buffer_1_events.clone();
 106            cx.subscribe(&buffer1, move |_, _, event, _| match event.clone() {
 107                Event::Operation(op) => buffer1_ops.borrow_mut().push(op),
 108                event @ _ => buffer_1_events.borrow_mut().push(event),
 109            })
 110            .detach();
 111            let buffer_2_events = buffer_2_events.clone();
 112            cx.subscribe(&buffer2, move |_, _, event, _| {
 113                buffer_2_events.borrow_mut().push(event.clone())
 114            })
 115            .detach();
 116
 117            // An edit emits an edited event, followed by a dirty changed event,
 118            // since the buffer was previously in a clean state.
 119            buffer.edit([(2..4, "XYZ")], cx);
 120
 121            // An empty transaction does not emit any events.
 122            buffer.start_transaction();
 123            buffer.end_transaction(cx);
 124
 125            // A transaction containing two edits emits one edited event.
 126            now += Duration::from_secs(1);
 127            buffer.start_transaction_at(now);
 128            buffer.edit([(5..5, "u")], cx);
 129            buffer.edit([(6..6, "w")], cx);
 130            buffer.end_transaction_at(now, cx);
 131
 132            // Undoing a transaction emits one edited event.
 133            buffer.undo(cx);
 134        }
 135    });
 136
 137    // Incorporating a set of remote ops emits a single edited event,
 138    // followed by a dirty changed event.
 139    buffer2.update(cx, |buffer, cx| {
 140        buffer
 141            .apply_ops(buffer1_ops.borrow_mut().drain(..), cx)
 142            .unwrap();
 143    });
 144    assert_eq!(
 145        mem::take(&mut *buffer_1_events.borrow_mut()),
 146        vec![
 147            Event::Edited,
 148            Event::DirtyChanged,
 149            Event::Edited,
 150            Event::Edited,
 151        ]
 152    );
 153    assert_eq!(
 154        mem::take(&mut *buffer_2_events.borrow_mut()),
 155        vec![Event::Edited, Event::DirtyChanged]
 156    );
 157
 158    buffer1.update(cx, |buffer, cx| {
 159        // Undoing the first transaction emits edited event, followed by a
 160        // dirty changed event, since the buffer is again in a clean state.
 161        buffer.undo(cx);
 162    });
 163    // Incorporating the remote ops again emits a single edited event,
 164    // followed by a dirty changed event.
 165    buffer2.update(cx, |buffer, cx| {
 166        buffer
 167            .apply_ops(buffer1_ops.borrow_mut().drain(..), cx)
 168            .unwrap();
 169    });
 170    assert_eq!(
 171        mem::take(&mut *buffer_1_events.borrow_mut()),
 172        vec![Event::Edited, Event::DirtyChanged,]
 173    );
 174    assert_eq!(
 175        mem::take(&mut *buffer_2_events.borrow_mut()),
 176        vec![Event::Edited, Event::DirtyChanged]
 177    );
 178}
 179
 180#[gpui::test]
 181async fn test_apply_diff(cx: &mut gpui::TestAppContext) {
 182    let text = "a\nbb\nccc\ndddd\neeeee\nffffff\n";
 183    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
 184
 185    let text = "a\nccc\ndddd\nffffff\n";
 186    let diff = buffer.read_with(cx, |b, cx| b.diff(text.into(), cx)).await;
 187    buffer.update(cx, |buffer, cx| {
 188        buffer.apply_diff(diff, cx).unwrap();
 189    });
 190    cx.read(|cx| assert_eq!(buffer.read(cx).text(), text));
 191
 192    let text = "a\n1\n\nccc\ndd2dd\nffffff\n";
 193    let diff = buffer.read_with(cx, |b, cx| b.diff(text.into(), cx)).await;
 194    buffer.update(cx, |buffer, cx| {
 195        buffer.apply_diff(diff, cx).unwrap();
 196    });
 197    cx.read(|cx| assert_eq!(buffer.read(cx).text(), text));
 198}
 199
 200#[gpui::test]
 201async fn test_reparse(cx: &mut gpui::TestAppContext) {
 202    let text = "fn a() {}";
 203    let buffer =
 204        cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
 205
 206    // Wait for the initial text to parse
 207    buffer
 208        .condition(&cx, |buffer, _| !buffer.is_parsing())
 209        .await;
 210    assert_eq!(
 211        get_tree_sexp(&buffer, &cx),
 212        concat!(
 213            "(source_file (function_item name: (identifier) ",
 214            "parameters: (parameters) ",
 215            "body: (block)))"
 216        )
 217    );
 218
 219    buffer.update(cx, |buffer, _| {
 220        buffer.set_sync_parse_timeout(Duration::ZERO)
 221    });
 222
 223    // Perform some edits (add parameter and variable reference)
 224    // Parsing doesn't begin until the transaction is complete
 225    buffer.update(cx, |buf, cx| {
 226        buf.start_transaction();
 227
 228        let offset = buf.text().find(")").unwrap();
 229        buf.edit([(offset..offset, "b: C")], cx);
 230        assert!(!buf.is_parsing());
 231
 232        let offset = buf.text().find("}").unwrap();
 233        buf.edit([(offset..offset, " d; ")], cx);
 234        assert!(!buf.is_parsing());
 235
 236        buf.end_transaction(cx);
 237        assert_eq!(buf.text(), "fn a(b: C) { d; }");
 238        assert!(buf.is_parsing());
 239    });
 240    buffer
 241        .condition(&cx, |buffer, _| !buffer.is_parsing())
 242        .await;
 243    assert_eq!(
 244        get_tree_sexp(&buffer, &cx),
 245        concat!(
 246            "(source_file (function_item name: (identifier) ",
 247            "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
 248            "body: (block (expression_statement (identifier)))))"
 249        )
 250    );
 251
 252    // Perform a series of edits without waiting for the current parse to complete:
 253    // * turn identifier into a field expression
 254    // * turn field expression into a method call
 255    // * add a turbofish to the method call
 256    buffer.update(cx, |buf, cx| {
 257        let offset = buf.text().find(";").unwrap();
 258        buf.edit([(offset..offset, ".e")], cx);
 259        assert_eq!(buf.text(), "fn a(b: C) { d.e; }");
 260        assert!(buf.is_parsing());
 261    });
 262    buffer.update(cx, |buf, cx| {
 263        let offset = buf.text().find(";").unwrap();
 264        buf.edit([(offset..offset, "(f)")], cx);
 265        assert_eq!(buf.text(), "fn a(b: C) { d.e(f); }");
 266        assert!(buf.is_parsing());
 267    });
 268    buffer.update(cx, |buf, cx| {
 269        let offset = buf.text().find("(f)").unwrap();
 270        buf.edit([(offset..offset, "::<G>")], cx);
 271        assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
 272        assert!(buf.is_parsing());
 273    });
 274    buffer
 275        .condition(&cx, |buffer, _| !buffer.is_parsing())
 276        .await;
 277    assert_eq!(
 278        get_tree_sexp(&buffer, &cx),
 279        concat!(
 280            "(source_file (function_item name: (identifier) ",
 281            "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
 282            "body: (block (expression_statement (call_expression ",
 283            "function: (generic_function ",
 284            "function: (field_expression value: (identifier) field: (field_identifier)) ",
 285            "type_arguments: (type_arguments (type_identifier))) ",
 286            "arguments: (arguments (identifier)))))))",
 287        )
 288    );
 289
 290    buffer.update(cx, |buf, cx| {
 291        buf.undo(cx);
 292        assert_eq!(buf.text(), "fn a() {}");
 293        assert!(buf.is_parsing());
 294    });
 295    buffer
 296        .condition(&cx, |buffer, _| !buffer.is_parsing())
 297        .await;
 298    assert_eq!(
 299        get_tree_sexp(&buffer, &cx),
 300        concat!(
 301            "(source_file (function_item name: (identifier) ",
 302            "parameters: (parameters) ",
 303            "body: (block)))"
 304        )
 305    );
 306
 307    buffer.update(cx, |buf, cx| {
 308        buf.redo(cx);
 309        assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
 310        assert!(buf.is_parsing());
 311    });
 312    buffer
 313        .condition(&cx, |buffer, _| !buffer.is_parsing())
 314        .await;
 315    assert_eq!(
 316        get_tree_sexp(&buffer, &cx),
 317        concat!(
 318            "(source_file (function_item name: (identifier) ",
 319            "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
 320            "body: (block (expression_statement (call_expression ",
 321            "function: (generic_function ",
 322            "function: (field_expression value: (identifier) field: (field_identifier)) ",
 323            "type_arguments: (type_arguments (type_identifier))) ",
 324            "arguments: (arguments (identifier)))))))",
 325        )
 326    );
 327}
 328
 329#[gpui::test]
 330async fn test_resetting_language(cx: &mut gpui::TestAppContext) {
 331    let buffer = cx.add_model(|cx| {
 332        let mut buffer = Buffer::new(0, "{}", cx).with_language(Arc::new(rust_lang()), cx);
 333        buffer.set_sync_parse_timeout(Duration::ZERO);
 334        buffer
 335    });
 336
 337    // Wait for the initial text to parse
 338    buffer
 339        .condition(&cx, |buffer, _| !buffer.is_parsing())
 340        .await;
 341    assert_eq!(
 342        get_tree_sexp(&buffer, &cx),
 343        "(source_file (expression_statement (block)))"
 344    );
 345
 346    buffer.update(cx, |buffer, cx| {
 347        buffer.set_language(Some(Arc::new(json_lang())), cx)
 348    });
 349    buffer
 350        .condition(&cx, |buffer, _| !buffer.is_parsing())
 351        .await;
 352    assert_eq!(get_tree_sexp(&buffer, &cx), "(document (object))");
 353}
 354
 355#[gpui::test]
 356async fn test_outline(cx: &mut gpui::TestAppContext) {
 357    let text = r#"
 358        struct Person {
 359            name: String,
 360            age: usize,
 361        }
 362
 363        mod module {
 364            enum LoginState {
 365                LoggedOut,
 366                LoggingOn,
 367                LoggedIn {
 368                    person: Person,
 369                    time: Instant,
 370                }
 371            }
 372        }
 373
 374        impl Eq for Person {}
 375
 376        impl Drop for Person {
 377            fn drop(&mut self) {
 378                println!("bye");
 379            }
 380        }
 381    "#
 382    .unindent();
 383
 384    let buffer =
 385        cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
 386    let outline = buffer
 387        .read_with(cx, |buffer, _| buffer.snapshot().outline(None))
 388        .unwrap();
 389
 390    assert_eq!(
 391        outline
 392            .items
 393            .iter()
 394            .map(|item| (item.text.as_str(), item.depth))
 395            .collect::<Vec<_>>(),
 396        &[
 397            ("struct Person", 0),
 398            ("name", 1),
 399            ("age", 1),
 400            ("mod module", 0),
 401            ("enum LoginState", 1),
 402            ("LoggedOut", 2),
 403            ("LoggingOn", 2),
 404            ("LoggedIn", 2),
 405            ("person", 3),
 406            ("time", 3),
 407            ("impl Eq for Person", 0),
 408            ("impl Drop for Person", 0),
 409            ("fn drop", 1),
 410        ]
 411    );
 412
 413    // Without space, we only match on names
 414    assert_eq!(
 415        search(&outline, "oon", &cx).await,
 416        &[
 417            ("mod module", vec![]),                    // included as the parent of a match
 418            ("enum LoginState", vec![]),               // included as the parent of a match
 419            ("LoggingOn", vec![1, 7, 8]),              // matches
 420            ("impl Drop for Person", vec![7, 18, 19]), // matches in two disjoint names
 421        ]
 422    );
 423
 424    assert_eq!(
 425        search(&outline, "dp p", &cx).await,
 426        &[
 427            ("impl Drop for Person", vec![5, 8, 9, 14]),
 428            ("fn drop", vec![]),
 429        ]
 430    );
 431    assert_eq!(
 432        search(&outline, "dpn", &cx).await,
 433        &[("impl Drop for Person", vec![5, 14, 19])]
 434    );
 435    assert_eq!(
 436        search(&outline, "impl ", &cx).await,
 437        &[
 438            ("impl Eq for Person", vec![0, 1, 2, 3, 4]),
 439            ("impl Drop for Person", vec![0, 1, 2, 3, 4]),
 440            ("fn drop", vec![]),
 441        ]
 442    );
 443
 444    async fn search<'a>(
 445        outline: &'a Outline<Anchor>,
 446        query: &'a str,
 447        cx: &'a gpui::TestAppContext,
 448    ) -> Vec<(&'a str, Vec<usize>)> {
 449        let matches = cx
 450            .read(|cx| outline.search(query, cx.background().clone()))
 451            .await;
 452        matches
 453            .into_iter()
 454            .map(|mat| (outline.items[mat.candidate_id].text.as_str(), mat.positions))
 455            .collect::<Vec<_>>()
 456    }
 457}
 458
 459#[gpui::test]
 460async fn test_symbols_containing(cx: &mut gpui::TestAppContext) {
 461    let text = r#"
 462        impl Person {
 463            fn one() {
 464                1
 465            }
 466
 467            fn two() {
 468                2
 469            }fn three() {
 470                3
 471            }
 472        }
 473    "#
 474    .unindent();
 475
 476    let buffer =
 477        cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
 478    let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot());
 479
 480    // point is at the start of an item
 481    assert_eq!(
 482        symbols_containing(Point::new(1, 4), &snapshot),
 483        vec![
 484            (
 485                "impl Person".to_string(),
 486                Point::new(0, 0)..Point::new(10, 1)
 487            ),
 488            ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
 489        ]
 490    );
 491
 492    // point is in the middle of an item
 493    assert_eq!(
 494        symbols_containing(Point::new(2, 8), &snapshot),
 495        vec![
 496            (
 497                "impl Person".to_string(),
 498                Point::new(0, 0)..Point::new(10, 1)
 499            ),
 500            ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
 501        ]
 502    );
 503
 504    // point is at the end of an item
 505    assert_eq!(
 506        symbols_containing(Point::new(3, 5), &snapshot),
 507        vec![
 508            (
 509                "impl Person".to_string(),
 510                Point::new(0, 0)..Point::new(10, 1)
 511            ),
 512            ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
 513        ]
 514    );
 515
 516    // point is in between two adjacent items
 517    assert_eq!(
 518        symbols_containing(Point::new(7, 5), &snapshot),
 519        vec![
 520            (
 521                "impl Person".to_string(),
 522                Point::new(0, 0)..Point::new(10, 1)
 523            ),
 524            ("fn two".to_string(), Point::new(5, 4)..Point::new(7, 5))
 525        ]
 526    );
 527
 528    fn symbols_containing<'a>(
 529        position: Point,
 530        snapshot: &'a BufferSnapshot,
 531    ) -> Vec<(String, Range<Point>)> {
 532        snapshot
 533            .symbols_containing(position, None)
 534            .unwrap()
 535            .into_iter()
 536            .map(|item| {
 537                (
 538                    item.text,
 539                    item.range.start.to_point(snapshot)..item.range.end.to_point(snapshot),
 540                )
 541            })
 542            .collect()
 543    }
 544}
 545
 546#[gpui::test]
 547fn test_enclosing_bracket_ranges(cx: &mut MutableAppContext) {
 548    let buffer = cx.add_model(|cx| {
 549        let text = "
 550            mod x {
 551                mod y {
 552
 553                }
 554            }
 555        "
 556        .unindent();
 557        Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx)
 558    });
 559    let buffer = buffer.read(cx);
 560    assert_eq!(
 561        buffer.enclosing_bracket_point_ranges(Point::new(1, 6)..Point::new(1, 6)),
 562        Some((
 563            Point::new(0, 6)..Point::new(0, 7),
 564            Point::new(4, 0)..Point::new(4, 1)
 565        ))
 566    );
 567    assert_eq!(
 568        buffer.enclosing_bracket_point_ranges(Point::new(1, 10)..Point::new(1, 10)),
 569        Some((
 570            Point::new(1, 10)..Point::new(1, 11),
 571            Point::new(3, 4)..Point::new(3, 5)
 572        ))
 573    );
 574    assert_eq!(
 575        buffer.enclosing_bracket_point_ranges(Point::new(3, 5)..Point::new(3, 5)),
 576        Some((
 577            Point::new(1, 10)..Point::new(1, 11),
 578            Point::new(3, 4)..Point::new(3, 5)
 579        ))
 580    );
 581}
 582
 583#[gpui::test]
 584fn test_range_for_syntax_ancestor(cx: &mut MutableAppContext) {
 585    cx.add_model(|cx| {
 586        let text = "fn a() { b(|c| {}) }";
 587        let buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
 588        let snapshot = buffer.snapshot();
 589
 590        assert_eq!(
 591            snapshot.range_for_syntax_ancestor(empty_range_at(text, "|")),
 592            Some(range_of(text, "|"))
 593        );
 594        assert_eq!(
 595            snapshot.range_for_syntax_ancestor(range_of(text, "|")),
 596            Some(range_of(text, "|c|"))
 597        );
 598        assert_eq!(
 599            snapshot.range_for_syntax_ancestor(range_of(text, "|c|")),
 600            Some(range_of(text, "|c| {}"))
 601        );
 602        assert_eq!(
 603            snapshot.range_for_syntax_ancestor(range_of(text, "|c| {}")),
 604            Some(range_of(text, "(|c| {})"))
 605        );
 606
 607        buffer
 608    });
 609
 610    fn empty_range_at(text: &str, part: &str) -> Range<usize> {
 611        let start = text.find(part).unwrap();
 612        start..start
 613    }
 614
 615    fn range_of(text: &str, part: &str) -> Range<usize> {
 616        let start = text.find(part).unwrap();
 617        start..start + part.len()
 618    }
 619}
 620
 621#[gpui::test]
 622fn test_autoindent_with_soft_tabs(cx: &mut MutableAppContext) {
 623    cx.add_model(|cx| {
 624        let text = "fn a() {}";
 625        let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
 626
 627        buffer.edit_with_autoindent([(8..8, "\n\n")], IndentSize::spaces(4), cx);
 628        assert_eq!(buffer.text(), "fn a() {\n    \n}");
 629
 630        buffer.edit_with_autoindent(
 631            [(Point::new(1, 4)..Point::new(1, 4), "b()\n")],
 632            IndentSize::spaces(4),
 633            cx,
 634        );
 635        assert_eq!(buffer.text(), "fn a() {\n    b()\n    \n}");
 636
 637        // Create a field expression on a new line, causing that line
 638        // to be indented.
 639        buffer.edit_with_autoindent(
 640            [(Point::new(2, 4)..Point::new(2, 4), ".c")],
 641            IndentSize::spaces(4),
 642            cx,
 643        );
 644        assert_eq!(buffer.text(), "fn a() {\n    b()\n        .c\n}");
 645
 646        // Remove the dot so that the line is no longer a field expression,
 647        // causing the line to be outdented.
 648        buffer.edit_with_autoindent(
 649            [(Point::new(2, 8)..Point::new(2, 9), "")],
 650            IndentSize::spaces(4),
 651            cx,
 652        );
 653        assert_eq!(buffer.text(), "fn a() {\n    b()\n    c\n}");
 654
 655        buffer
 656    });
 657}
 658
 659#[gpui::test]
 660fn test_autoindent_with_hard_tabs(cx: &mut MutableAppContext) {
 661    cx.add_model(|cx| {
 662        let text = "fn a() {}";
 663        let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
 664
 665        buffer.edit_with_autoindent([(8..8, "\n\n")], IndentSize::tab(), cx);
 666        assert_eq!(buffer.text(), "fn a() {\n\t\n}");
 667
 668        buffer.edit_with_autoindent(
 669            [(Point::new(1, 1)..Point::new(1, 1), "b()\n")],
 670            IndentSize::tab(),
 671            cx,
 672        );
 673        assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\n}");
 674
 675        // Create a field expression on a new line, causing that line
 676        // to be indented.
 677        buffer.edit_with_autoindent(
 678            [(Point::new(2, 1)..Point::new(2, 1), ".c")],
 679            IndentSize::tab(),
 680            cx,
 681        );
 682        assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\t.c\n}");
 683
 684        // Remove the dot so that the line is no longer a field expression,
 685        // causing the line to be outdented.
 686        buffer.edit_with_autoindent(
 687            [(Point::new(2, 2)..Point::new(2, 3), "")],
 688            IndentSize::tab(),
 689            cx,
 690        );
 691        assert_eq!(buffer.text(), "fn a() {\n\tb()\n\tc\n}");
 692
 693        buffer
 694    });
 695}
 696
 697#[gpui::test]
 698fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut MutableAppContext) {
 699    cx.add_model(|cx| {
 700        let text = "
 701            fn a() {
 702            c;
 703            d;
 704            }
 705        "
 706        .unindent();
 707
 708        let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
 709
 710        // Lines 2 and 3 don't match the indentation suggestion. When editing these lines,
 711        // their indentation is not adjusted.
 712        buffer.edit_with_autoindent(
 713            [
 714                (empty(Point::new(1, 1)), "()"),
 715                (empty(Point::new(2, 1)), "()"),
 716            ],
 717            IndentSize::spaces(4),
 718            cx,
 719        );
 720        assert_eq!(
 721            buffer.text(),
 722            "
 723            fn a() {
 724            c();
 725            d();
 726            }
 727            "
 728            .unindent()
 729        );
 730
 731        // When appending new content after these lines, the indentation is based on the
 732        // preceding lines' actual indentation.
 733        buffer.edit_with_autoindent(
 734            [
 735                (empty(Point::new(1, 1)), "\n.f\n.g"),
 736                (empty(Point::new(2, 1)), "\n.f\n.g"),
 737            ],
 738            IndentSize::spaces(4),
 739            cx,
 740        );
 741        assert_eq!(
 742            buffer.text(),
 743            "
 744            fn a() {
 745            c
 746                .f
 747                .g();
 748            d
 749                .f
 750                .g();
 751            }
 752            "
 753            .unindent()
 754        );
 755        buffer
 756    });
 757
 758    cx.add_model(|cx| {
 759        let text = "fn a() {\n    {\n        b()?\n    }\n\n    Ok(())\n}";
 760        let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
 761        buffer.edit_with_autoindent(
 762            [(Point::new(3, 4)..Point::new(3, 5), "")],
 763            IndentSize::spaces(4),
 764            cx,
 765        );
 766        assert_eq!(
 767            buffer.text(),
 768            "fn a() {\n    {\n        b()?\n            \n\n    Ok(())\n}"
 769        );
 770
 771        buffer.edit_with_autoindent(
 772            [(Point::new(3, 0)..Point::new(3, 12), "")],
 773            IndentSize::spaces(4),
 774            cx,
 775        );
 776        assert_eq!(
 777            buffer.text(),
 778            "fn a() {\n    {\n        b()?\n\n\n    Ok(())\n}"
 779        );
 780        buffer
 781    });
 782}
 783
 784#[gpui::test]
 785fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut MutableAppContext) {
 786    cx.add_model(|cx| {
 787        let text = "
 788            fn a() {}
 789        "
 790        .unindent();
 791
 792        let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
 793
 794        buffer.edit_with_autoindent([(5..5, "\nb")], IndentSize::spaces(4), cx);
 795        assert_eq!(
 796            buffer.text(),
 797            "
 798                fn a(
 799                    b) {}
 800            "
 801            .unindent()
 802        );
 803
 804        // The indentation suggestion changed because `@end` node (a close paren)
 805        // is now at the beginning of the line.
 806        buffer.edit_with_autoindent(
 807            [(Point::new(1, 4)..Point::new(1, 5), "")],
 808            IndentSize::spaces(4),
 809            cx,
 810        );
 811        assert_eq!(
 812            buffer.text(),
 813            "
 814                fn a(
 815                ) {}
 816            "
 817            .unindent()
 818        );
 819
 820        buffer
 821    });
 822}
 823
 824#[gpui::test]
 825fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut MutableAppContext) {
 826    cx.add_model(|cx| {
 827        let text = "a\nb";
 828        let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
 829        buffer.edit_with_autoindent([(0..1, "\n"), (2..3, "\n")], IndentSize::spaces(4), cx);
 830        assert_eq!(buffer.text(), "\n\n\n");
 831        buffer
 832    });
 833}
 834
 835#[gpui::test]
 836fn test_autoindent_disabled(cx: &mut MutableAppContext) {
 837    cx.add_model(|cx| {
 838        let text = "
 839            * one
 840                - a
 841                - b
 842            * two
 843        "
 844        .unindent();
 845
 846        let mut buffer = Buffer::new(0, text, cx).with_language(
 847            Arc::new(Language::new(
 848                LanguageConfig {
 849                    name: "Markdown".into(),
 850                    ..Default::default()
 851                },
 852                Some(tree_sitter_json::language()),
 853            )),
 854            cx,
 855        );
 856        buffer.edit_with_autoindent(
 857            [(Point::new(3, 0)..Point::new(3, 0), "\n")],
 858            IndentSize::spaces(4),
 859            cx,
 860        );
 861        assert_eq!(
 862            buffer.text(),
 863            "
 864            * one
 865                - a
 866                - b
 867
 868            * two
 869            "
 870            .unindent()
 871        );
 872        buffer
 873    });
 874}
 875
 876#[gpui::test]
 877fn test_serialization(cx: &mut gpui::MutableAppContext) {
 878    let mut now = Instant::now();
 879
 880    let buffer1 = cx.add_model(|cx| {
 881        let mut buffer = Buffer::new(0, "abc", cx);
 882        buffer.edit([(3..3, "D")], cx);
 883
 884        now += Duration::from_secs(1);
 885        buffer.start_transaction_at(now);
 886        buffer.edit([(4..4, "E")], cx);
 887        buffer.end_transaction_at(now, cx);
 888        assert_eq!(buffer.text(), "abcDE");
 889
 890        buffer.undo(cx);
 891        assert_eq!(buffer.text(), "abcD");
 892
 893        buffer.edit([(4..4, "F")], cx);
 894        assert_eq!(buffer.text(), "abcDF");
 895        buffer
 896    });
 897    assert_eq!(buffer1.read(cx).text(), "abcDF");
 898
 899    let message = buffer1.read(cx).to_proto();
 900    let buffer2 = cx.add_model(|cx| Buffer::from_proto(1, message, None, cx).unwrap());
 901    assert_eq!(buffer2.read(cx).text(), "abcDF");
 902}
 903
 904#[gpui::test(iterations = 100)]
 905fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) {
 906    let min_peers = env::var("MIN_PEERS")
 907        .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
 908        .unwrap_or(1);
 909    let max_peers = env::var("MAX_PEERS")
 910        .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
 911        .unwrap_or(5);
 912    let operations = env::var("OPERATIONS")
 913        .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
 914        .unwrap_or(10);
 915
 916    let base_text_len = rng.gen_range(0..10);
 917    let base_text = RandomCharIter::new(&mut rng)
 918        .take(base_text_len)
 919        .collect::<String>();
 920    let mut replica_ids = Vec::new();
 921    let mut buffers = Vec::new();
 922    let network = Rc::new(RefCell::new(Network::new(rng.clone())));
 923    let base_buffer = cx.add_model(|cx| Buffer::new(0, base_text.as_str(), cx));
 924
 925    for i in 0..rng.gen_range(min_peers..=max_peers) {
 926        let buffer = cx.add_model(|cx| {
 927            let mut buffer =
 928                Buffer::from_proto(i as ReplicaId, base_buffer.read(cx).to_proto(), None, cx)
 929                    .unwrap();
 930            buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
 931            let network = network.clone();
 932            cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
 933                if let Event::Operation(op) = event {
 934                    network
 935                        .borrow_mut()
 936                        .broadcast(buffer.replica_id(), vec![proto::serialize_operation(&op)]);
 937                }
 938            })
 939            .detach();
 940            buffer
 941        });
 942        buffers.push(buffer);
 943        replica_ids.push(i as ReplicaId);
 944        network.borrow_mut().add_peer(i as ReplicaId);
 945        log::info!("Adding initial peer with replica id {}", i);
 946    }
 947
 948    log::info!("initial text: {:?}", base_text);
 949
 950    let mut now = Instant::now();
 951    let mut mutation_count = operations;
 952    let mut next_diagnostic_id = 0;
 953    let mut active_selections = BTreeMap::default();
 954    loop {
 955        let replica_index = rng.gen_range(0..replica_ids.len());
 956        let replica_id = replica_ids[replica_index];
 957        let buffer = &mut buffers[replica_index];
 958        let mut new_buffer = None;
 959        match rng.gen_range(0..100) {
 960            0..=29 if mutation_count != 0 => {
 961                buffer.update(cx, |buffer, cx| {
 962                    buffer.start_transaction_at(now);
 963                    buffer.randomly_edit(&mut rng, 5, cx);
 964                    buffer.end_transaction_at(now, cx);
 965                    log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
 966                });
 967                mutation_count -= 1;
 968            }
 969            30..=39 if mutation_count != 0 => {
 970                buffer.update(cx, |buffer, cx| {
 971                    let mut selections = Vec::new();
 972                    for id in 0..rng.gen_range(1..=5) {
 973                        let range = buffer.random_byte_range(0, &mut rng);
 974                        selections.push(Selection {
 975                            id,
 976                            start: buffer.anchor_before(range.start),
 977                            end: buffer.anchor_before(range.end),
 978                            reversed: false,
 979                            goal: SelectionGoal::None,
 980                        });
 981                    }
 982                    let selections: Arc<[Selection<Anchor>]> = selections.into();
 983                    log::info!(
 984                        "peer {} setting active selections: {:?}",
 985                        replica_id,
 986                        selections
 987                    );
 988                    active_selections.insert(replica_id, selections.clone());
 989                    buffer.set_active_selections(selections, false, cx);
 990                });
 991                mutation_count -= 1;
 992            }
 993            40..=49 if mutation_count != 0 && replica_id == 0 => {
 994                let entry_count = rng.gen_range(1..=5);
 995                buffer.update(cx, |buffer, cx| {
 996                    let diagnostics = DiagnosticSet::new(
 997                        (0..entry_count).map(|_| {
 998                            let range = buffer.random_byte_range(0, &mut rng);
 999                            let range = range.to_point_utf16(buffer);
1000                            DiagnosticEntry {
1001                                range,
1002                                diagnostic: Diagnostic {
1003                                    message: post_inc(&mut next_diagnostic_id).to_string(),
1004                                    ..Default::default()
1005                                },
1006                            }
1007                        }),
1008                        buffer,
1009                    );
1010                    log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
1011                    buffer.update_diagnostics(diagnostics, cx);
1012                });
1013                mutation_count -= 1;
1014            }
1015            50..=59 if replica_ids.len() < max_peers => {
1016                let old_buffer = buffer.read(cx).to_proto();
1017                let new_replica_id = (0..=replica_ids.len() as ReplicaId)
1018                    .filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
1019                    .choose(&mut rng)
1020                    .unwrap();
1021                log::info!(
1022                    "Adding new replica {} (replicating from {})",
1023                    new_replica_id,
1024                    replica_id
1025                );
1026                new_buffer = Some(cx.add_model(|cx| {
1027                    let mut new_buffer =
1028                        Buffer::from_proto(new_replica_id, old_buffer, None, cx).unwrap();
1029                    log::info!(
1030                        "New replica {} text: {:?}",
1031                        new_buffer.replica_id(),
1032                        new_buffer.text()
1033                    );
1034                    new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1035                    let network = network.clone();
1036                    cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1037                        if let Event::Operation(op) = event {
1038                            network.borrow_mut().broadcast(
1039                                buffer.replica_id(),
1040                                vec![proto::serialize_operation(&op)],
1041                            );
1042                        }
1043                    })
1044                    .detach();
1045                    new_buffer
1046                }));
1047                network.borrow_mut().replicate(replica_id, new_replica_id);
1048
1049                if new_replica_id as usize == replica_ids.len() {
1050                    replica_ids.push(new_replica_id);
1051                } else {
1052                    let new_buffer = new_buffer.take().unwrap();
1053                    while network.borrow().has_unreceived(new_replica_id) {
1054                        let ops = network
1055                            .borrow_mut()
1056                            .receive(new_replica_id)
1057                            .into_iter()
1058                            .map(|op| proto::deserialize_operation(op).unwrap());
1059                        if ops.len() > 0 {
1060                            log::info!(
1061                                "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1062                                new_replica_id,
1063                                buffer.read(cx).version(),
1064                                ops.len(),
1065                                ops
1066                            );
1067                            new_buffer.update(cx, |new_buffer, cx| {
1068                                new_buffer.apply_ops(ops, cx).unwrap();
1069                            });
1070                        }
1071                    }
1072                    buffers[new_replica_id as usize] = new_buffer;
1073                }
1074            }
1075            60..=69 if mutation_count != 0 => {
1076                buffer.update(cx, |buffer, cx| {
1077                    buffer.randomly_undo_redo(&mut rng, cx);
1078                    log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1079                });
1080                mutation_count -= 1;
1081            }
1082            _ if network.borrow().has_unreceived(replica_id) => {
1083                let ops = network
1084                    .borrow_mut()
1085                    .receive(replica_id)
1086                    .into_iter()
1087                    .map(|op| proto::deserialize_operation(op).unwrap());
1088                if ops.len() > 0 {
1089                    log::info!(
1090                        "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1091                        replica_id,
1092                        buffer.read(cx).version(),
1093                        ops.len(),
1094                        ops
1095                    );
1096                    buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
1097                }
1098            }
1099            _ => {}
1100        }
1101
1102        now += Duration::from_millis(rng.gen_range(0..=200));
1103        buffers.extend(new_buffer);
1104
1105        for buffer in &buffers {
1106            buffer.read(cx).check_invariants();
1107        }
1108
1109        if mutation_count == 0 && network.borrow().is_idle() {
1110            break;
1111        }
1112    }
1113
1114    let first_buffer = buffers[0].read(cx).snapshot();
1115    for buffer in &buffers[1..] {
1116        let buffer = buffer.read(cx).snapshot();
1117        assert_eq!(
1118            buffer.version(),
1119            first_buffer.version(),
1120            "Replica {} version != Replica 0 version",
1121            buffer.replica_id()
1122        );
1123        assert_eq!(
1124            buffer.text(),
1125            first_buffer.text(),
1126            "Replica {} text != Replica 0 text",
1127            buffer.replica_id()
1128        );
1129        assert_eq!(
1130            buffer
1131                .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
1132                .collect::<Vec<_>>(),
1133            first_buffer
1134                .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
1135                .collect::<Vec<_>>(),
1136            "Replica {} diagnostics != Replica 0 diagnostics",
1137            buffer.replica_id()
1138        );
1139    }
1140
1141    for buffer in &buffers {
1142        let buffer = buffer.read(cx).snapshot();
1143        let actual_remote_selections = buffer
1144            .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
1145            .map(|(replica_id, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
1146            .collect::<Vec<_>>();
1147        let expected_remote_selections = active_selections
1148            .iter()
1149            .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
1150            .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
1151            .collect::<Vec<_>>();
1152        assert_eq!(
1153            actual_remote_selections,
1154            expected_remote_selections,
1155            "Replica {} remote selections != expected selections",
1156            buffer.replica_id()
1157        );
1158    }
1159}
1160
1161#[test]
1162fn test_contiguous_ranges() {
1163    assert_eq!(
1164        contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
1165        &[1..4, 5..7, 9..13]
1166    );
1167
1168    // Respects the `max_len` parameter
1169    assert_eq!(
1170        contiguous_ranges(
1171            [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
1172            3
1173        )
1174        .collect::<Vec<_>>(),
1175        &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
1176    );
1177}
1178
1179impl Buffer {
1180    pub fn enclosing_bracket_point_ranges<T: ToOffset>(
1181        &self,
1182        range: Range<T>,
1183    ) -> Option<(Range<Point>, Range<Point>)> {
1184        self.snapshot()
1185            .enclosing_bracket_ranges(range)
1186            .map(|(start, end)| {
1187                let point_start = start.start.to_point(self)..start.end.to_point(self);
1188                let point_end = end.start.to_point(self)..end.end.to_point(self);
1189                (point_start, point_end)
1190            })
1191    }
1192}
1193
1194fn rust_lang() -> Language {
1195    Language::new(
1196        LanguageConfig {
1197            name: "Rust".into(),
1198            path_suffixes: vec!["rs".to_string()],
1199            ..Default::default()
1200        },
1201        Some(tree_sitter_rust::language()),
1202    )
1203    .with_indents_query(
1204        r#"
1205        (call_expression) @indent
1206        (field_expression) @indent
1207        (_ "(" ")" @end) @indent
1208        (_ "{" "}" @end) @indent
1209        "#,
1210    )
1211    .unwrap()
1212    .with_brackets_query(
1213        r#"
1214        ("{" @open "}" @close)
1215        "#,
1216    )
1217    .unwrap()
1218    .with_outline_query(
1219        r#"
1220        (struct_item
1221            "struct" @context
1222            name: (_) @name) @item
1223        (enum_item
1224            "enum" @context
1225            name: (_) @name) @item
1226        (enum_variant
1227            name: (_) @name) @item
1228        (field_declaration
1229            name: (_) @name) @item
1230        (impl_item
1231            "impl" @context
1232            trait: (_)? @name
1233            "for"? @context
1234            type: (_) @name) @item
1235        (function_item
1236            "fn" @context
1237            name: (_) @name) @item
1238        (mod_item
1239            "mod" @context
1240            name: (_) @name) @item
1241        "#,
1242    )
1243    .unwrap()
1244}
1245
1246fn json_lang() -> Language {
1247    Language::new(
1248        LanguageConfig {
1249            name: "Json".into(),
1250            path_suffixes: vec!["js".to_string()],
1251            ..Default::default()
1252        },
1253        Some(tree_sitter_json::language()),
1254    )
1255}
1256
1257fn get_tree_sexp(buffer: &ModelHandle<Buffer>, cx: &gpui::TestAppContext) -> String {
1258    buffer.read_with(cx, |buffer, _| {
1259        buffer.syntax_tree().unwrap().root_node().to_sexp()
1260    })
1261}
1262
1263fn empty(point: Point) -> Range<Point> {
1264    point..point
1265}