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