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_serialization(cx: &mut gpui::MutableAppContext) {
 814    let mut now = Instant::now();
 815
 816    let buffer1 = cx.add_model(|cx| {
 817        let mut buffer = Buffer::new(0, "abc", cx);
 818        buffer.edit([(3..3, "D")], cx);
 819
 820        now += Duration::from_secs(1);
 821        buffer.start_transaction_at(now);
 822        buffer.edit([(4..4, "E")], cx);
 823        buffer.end_transaction_at(now, cx);
 824        assert_eq!(buffer.text(), "abcDE");
 825
 826        buffer.undo(cx);
 827        assert_eq!(buffer.text(), "abcD");
 828
 829        buffer.edit([(4..4, "F")], cx);
 830        assert_eq!(buffer.text(), "abcDF");
 831        buffer
 832    });
 833    assert_eq!(buffer1.read(cx).text(), "abcDF");
 834
 835    let message = buffer1.read(cx).to_proto();
 836    let buffer2 = cx.add_model(|cx| Buffer::from_proto(1, message, None, cx).unwrap());
 837    assert_eq!(buffer2.read(cx).text(), "abcDF");
 838}
 839
 840#[gpui::test(iterations = 100)]
 841fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) {
 842    let min_peers = env::var("MIN_PEERS")
 843        .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
 844        .unwrap_or(1);
 845    let max_peers = env::var("MAX_PEERS")
 846        .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
 847        .unwrap_or(5);
 848    let operations = env::var("OPERATIONS")
 849        .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
 850        .unwrap_or(10);
 851
 852    let base_text_len = rng.gen_range(0..10);
 853    let base_text = RandomCharIter::new(&mut rng)
 854        .take(base_text_len)
 855        .collect::<String>();
 856    let mut replica_ids = Vec::new();
 857    let mut buffers = Vec::new();
 858    let network = Rc::new(RefCell::new(Network::new(rng.clone())));
 859    let base_buffer = cx.add_model(|cx| Buffer::new(0, base_text.as_str(), cx));
 860
 861    for i in 0..rng.gen_range(min_peers..=max_peers) {
 862        let buffer = cx.add_model(|cx| {
 863            let mut buffer =
 864                Buffer::from_proto(i as ReplicaId, base_buffer.read(cx).to_proto(), None, cx)
 865                    .unwrap();
 866            buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
 867            let network = network.clone();
 868            cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
 869                if let Event::Operation(op) = event {
 870                    network
 871                        .borrow_mut()
 872                        .broadcast(buffer.replica_id(), vec![proto::serialize_operation(&op)]);
 873                }
 874            })
 875            .detach();
 876            buffer
 877        });
 878        buffers.push(buffer);
 879        replica_ids.push(i as ReplicaId);
 880        network.borrow_mut().add_peer(i as ReplicaId);
 881        log::info!("Adding initial peer with replica id {}", i);
 882    }
 883
 884    log::info!("initial text: {:?}", base_text);
 885
 886    let mut now = Instant::now();
 887    let mut mutation_count = operations;
 888    let mut next_diagnostic_id = 0;
 889    let mut active_selections = BTreeMap::default();
 890    loop {
 891        let replica_index = rng.gen_range(0..replica_ids.len());
 892        let replica_id = replica_ids[replica_index];
 893        let buffer = &mut buffers[replica_index];
 894        let mut new_buffer = None;
 895        match rng.gen_range(0..100) {
 896            0..=29 if mutation_count != 0 => {
 897                buffer.update(cx, |buffer, cx| {
 898                    buffer.start_transaction_at(now);
 899                    buffer.randomly_edit(&mut rng, 5, cx);
 900                    buffer.end_transaction_at(now, cx);
 901                    log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
 902                });
 903                mutation_count -= 1;
 904            }
 905            30..=39 if mutation_count != 0 => {
 906                buffer.update(cx, |buffer, cx| {
 907                    let mut selections = Vec::new();
 908                    for id in 0..rng.gen_range(1..=5) {
 909                        let range = buffer.random_byte_range(0, &mut rng);
 910                        selections.push(Selection {
 911                            id,
 912                            start: buffer.anchor_before(range.start),
 913                            end: buffer.anchor_before(range.end),
 914                            reversed: false,
 915                            goal: SelectionGoal::None,
 916                        });
 917                    }
 918                    let selections: Arc<[Selection<Anchor>]> = selections.into();
 919                    log::info!(
 920                        "peer {} setting active selections: {:?}",
 921                        replica_id,
 922                        selections
 923                    );
 924                    active_selections.insert(replica_id, selections.clone());
 925                    buffer.set_active_selections(selections, false, cx);
 926                });
 927                mutation_count -= 1;
 928            }
 929            40..=49 if mutation_count != 0 && replica_id == 0 => {
 930                let entry_count = rng.gen_range(1..=5);
 931                buffer.update(cx, |buffer, cx| {
 932                    let diagnostics = DiagnosticSet::new(
 933                        (0..entry_count).map(|_| {
 934                            let range = buffer.random_byte_range(0, &mut rng);
 935                            let range = range.to_point_utf16(buffer);
 936                            DiagnosticEntry {
 937                                range,
 938                                diagnostic: Diagnostic {
 939                                    message: post_inc(&mut next_diagnostic_id).to_string(),
 940                                    ..Default::default()
 941                                },
 942                            }
 943                        }),
 944                        buffer,
 945                    );
 946                    log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
 947                    buffer.update_diagnostics(diagnostics, cx);
 948                });
 949                mutation_count -= 1;
 950            }
 951            50..=59 if replica_ids.len() < max_peers => {
 952                let old_buffer = buffer.read(cx).to_proto();
 953                let new_replica_id = (0..=replica_ids.len() as ReplicaId)
 954                    .filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
 955                    .choose(&mut rng)
 956                    .unwrap();
 957                log::info!(
 958                    "Adding new replica {} (replicating from {})",
 959                    new_replica_id,
 960                    replica_id
 961                );
 962                new_buffer = Some(cx.add_model(|cx| {
 963                    let mut new_buffer =
 964                        Buffer::from_proto(new_replica_id, old_buffer, None, cx).unwrap();
 965                    log::info!(
 966                        "New replica {} text: {:?}",
 967                        new_buffer.replica_id(),
 968                        new_buffer.text()
 969                    );
 970                    new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
 971                    let network = network.clone();
 972                    cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
 973                        if let Event::Operation(op) = event {
 974                            network.borrow_mut().broadcast(
 975                                buffer.replica_id(),
 976                                vec![proto::serialize_operation(&op)],
 977                            );
 978                        }
 979                    })
 980                    .detach();
 981                    new_buffer
 982                }));
 983                network.borrow_mut().replicate(replica_id, new_replica_id);
 984
 985                if new_replica_id as usize == replica_ids.len() {
 986                    replica_ids.push(new_replica_id);
 987                } else {
 988                    let new_buffer = new_buffer.take().unwrap();
 989                    while network.borrow().has_unreceived(new_replica_id) {
 990                        let ops = network
 991                            .borrow_mut()
 992                            .receive(new_replica_id)
 993                            .into_iter()
 994                            .map(|op| proto::deserialize_operation(op).unwrap());
 995                        if ops.len() > 0 {
 996                            log::info!(
 997                                "peer {} (version: {:?}) applying {} ops from the network. {:?}",
 998                                new_replica_id,
 999                                buffer.read(cx).version(),
1000                                ops.len(),
1001                                ops
1002                            );
1003                            new_buffer.update(cx, |new_buffer, cx| {
1004                                new_buffer.apply_ops(ops, cx).unwrap();
1005                            });
1006                        }
1007                    }
1008                    buffers[new_replica_id as usize] = new_buffer;
1009                }
1010            }
1011            60..=69 if mutation_count != 0 => {
1012                buffer.update(cx, |buffer, cx| {
1013                    buffer.randomly_undo_redo(&mut rng, cx);
1014                    log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1015                });
1016                mutation_count -= 1;
1017            }
1018            _ if network.borrow().has_unreceived(replica_id) => {
1019                let ops = network
1020                    .borrow_mut()
1021                    .receive(replica_id)
1022                    .into_iter()
1023                    .map(|op| proto::deserialize_operation(op).unwrap());
1024                if ops.len() > 0 {
1025                    log::info!(
1026                        "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1027                        replica_id,
1028                        buffer.read(cx).version(),
1029                        ops.len(),
1030                        ops
1031                    );
1032                    buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
1033                }
1034            }
1035            _ => {}
1036        }
1037
1038        now += Duration::from_millis(rng.gen_range(0..=200));
1039        buffers.extend(new_buffer);
1040
1041        for buffer in &buffers {
1042            buffer.read(cx).check_invariants();
1043        }
1044
1045        if mutation_count == 0 && network.borrow().is_idle() {
1046            break;
1047        }
1048    }
1049
1050    let first_buffer = buffers[0].read(cx).snapshot();
1051    for buffer in &buffers[1..] {
1052        let buffer = buffer.read(cx).snapshot();
1053        assert_eq!(
1054            buffer.version(),
1055            first_buffer.version(),
1056            "Replica {} version != Replica 0 version",
1057            buffer.replica_id()
1058        );
1059        assert_eq!(
1060            buffer.text(),
1061            first_buffer.text(),
1062            "Replica {} text != Replica 0 text",
1063            buffer.replica_id()
1064        );
1065        assert_eq!(
1066            buffer
1067                .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
1068                .collect::<Vec<_>>(),
1069            first_buffer
1070                .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
1071                .collect::<Vec<_>>(),
1072            "Replica {} diagnostics != Replica 0 diagnostics",
1073            buffer.replica_id()
1074        );
1075    }
1076
1077    for buffer in &buffers {
1078        let buffer = buffer.read(cx).snapshot();
1079        let actual_remote_selections = buffer
1080            .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
1081            .map(|(replica_id, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
1082            .collect::<Vec<_>>();
1083        let expected_remote_selections = active_selections
1084            .iter()
1085            .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
1086            .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
1087            .collect::<Vec<_>>();
1088        assert_eq!(
1089            actual_remote_selections,
1090            expected_remote_selections,
1091            "Replica {} remote selections != expected selections",
1092            buffer.replica_id()
1093        );
1094    }
1095}
1096
1097#[test]
1098fn test_contiguous_ranges() {
1099    assert_eq!(
1100        contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
1101        &[1..4, 5..7, 9..13]
1102    );
1103
1104    // Respects the `max_len` parameter
1105    assert_eq!(
1106        contiguous_ranges(
1107            [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
1108            3
1109        )
1110        .collect::<Vec<_>>(),
1111        &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
1112    );
1113}
1114
1115impl Buffer {
1116    pub fn enclosing_bracket_point_ranges<T: ToOffset>(
1117        &self,
1118        range: Range<T>,
1119    ) -> Option<(Range<Point>, Range<Point>)> {
1120        self.snapshot()
1121            .enclosing_bracket_ranges(range)
1122            .map(|(start, end)| {
1123                let point_start = start.start.to_point(self)..start.end.to_point(self);
1124                let point_end = end.start.to_point(self)..end.end.to_point(self);
1125                (point_start, point_end)
1126            })
1127    }
1128}
1129
1130fn rust_lang() -> Language {
1131    Language::new(
1132        LanguageConfig {
1133            name: "Rust".into(),
1134            path_suffixes: vec!["rs".to_string()],
1135            ..Default::default()
1136        },
1137        Some(tree_sitter_rust::language()),
1138    )
1139    .with_indents_query(
1140        r#"
1141        (call_expression) @indent
1142        (field_expression) @indent
1143        (_ "(" ")" @end) @indent
1144        (_ "{" "}" @end) @indent
1145        "#,
1146    )
1147    .unwrap()
1148    .with_brackets_query(
1149        r#"
1150        ("{" @open "}" @close)
1151        "#,
1152    )
1153    .unwrap()
1154    .with_outline_query(
1155        r#"
1156        (struct_item
1157            "struct" @context
1158            name: (_) @name) @item
1159        (enum_item
1160            "enum" @context
1161            name: (_) @name) @item
1162        (enum_variant
1163            name: (_) @name) @item
1164        (field_declaration
1165            name: (_) @name) @item
1166        (impl_item
1167            "impl" @context
1168            trait: (_)? @name
1169            "for"? @context
1170            type: (_) @name) @item
1171        (function_item
1172            "fn" @context
1173            name: (_) @name) @item
1174        (mod_item
1175            "mod" @context
1176            name: (_) @name) @item
1177        "#,
1178    )
1179    .unwrap()
1180}
1181
1182fn json_lang() -> Language {
1183    Language::new(
1184        LanguageConfig {
1185            name: "Json".into(),
1186            path_suffixes: vec!["js".to_string()],
1187            ..Default::default()
1188        },
1189        Some(tree_sitter_json::language()),
1190    )
1191}
1192
1193fn get_tree_sexp(buffer: &ModelHandle<Buffer>, cx: &gpui::TestAppContext) -> String {
1194    buffer.read_with(cx, |buffer, _| {
1195        buffer.syntax_tree().unwrap().root_node().to_sexp()
1196    })
1197}
1198
1199fn empty(point: Point) -> Range<Point> {
1200    point..point
1201}