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