editor_tests.rs

   1use super::*;
   2use crate::test::{
   3    assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
   4    editor_test_context::EditorTestContext, select_ranges,
   5};
   6use drag_and_drop::DragAndDrop;
   7use futures::StreamExt;
   8use gpui::{
   9    executor::Deterministic,
  10    geometry::{rect::RectF, vector::vec2f},
  11    platform::{WindowBounds, WindowOptions},
  12    serde_json,
  13};
  14use indoc::indoc;
  15use language::{BracketPairConfig, FakeLspAdapter, LanguageConfig, LanguageRegistry, Point};
  16use parking_lot::Mutex;
  17use project::FakeFs;
  18use settings::EditorSettings;
  19use std::{cell::RefCell, rc::Rc, time::Instant};
  20use unindent::Unindent;
  21use util::{
  22    assert_set_eq,
  23    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
  24};
  25use workspace::{
  26    item::{FollowableItem, ItemHandle},
  27    NavigationEntry, Pane, ViewId,
  28};
  29
  30#[gpui::test]
  31fn test_edit_events(cx: &mut MutableAppContext) {
  32    cx.set_global(Settings::test(cx));
  33    let buffer = cx.add_model(|cx| {
  34        let mut buffer = language::Buffer::new(0, "123456", cx);
  35        buffer.set_group_interval(Duration::from_secs(1));
  36        buffer
  37    });
  38
  39    let events = Rc::new(RefCell::new(Vec::new()));
  40    let (_, editor1) = cx.add_window(Default::default(), {
  41        let events = events.clone();
  42        |cx| {
  43            cx.subscribe(&cx.handle(), move |_, _, event, _| {
  44                if matches!(
  45                    event,
  46                    Event::Edited | Event::BufferEdited | Event::DirtyChanged
  47                ) {
  48                    events.borrow_mut().push(("editor1", event.clone()));
  49                }
  50            })
  51            .detach();
  52            Editor::for_buffer(buffer.clone(), None, cx)
  53        }
  54    });
  55    let (_, editor2) = cx.add_window(Default::default(), {
  56        let events = events.clone();
  57        |cx| {
  58            cx.subscribe(&cx.handle(), move |_, _, event, _| {
  59                if matches!(
  60                    event,
  61                    Event::Edited | Event::BufferEdited | Event::DirtyChanged
  62                ) {
  63                    events.borrow_mut().push(("editor2", event.clone()));
  64                }
  65            })
  66            .detach();
  67            Editor::for_buffer(buffer.clone(), None, cx)
  68        }
  69    });
  70    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  71
  72    // Mutating editor 1 will emit an `Edited` event only for that editor.
  73    editor1.update(cx, |editor, cx| editor.insert("X", cx));
  74    assert_eq!(
  75        mem::take(&mut *events.borrow_mut()),
  76        [
  77            ("editor1", Event::Edited),
  78            ("editor1", Event::BufferEdited),
  79            ("editor2", Event::BufferEdited),
  80            ("editor1", Event::DirtyChanged),
  81            ("editor2", Event::DirtyChanged)
  82        ]
  83    );
  84
  85    // Mutating editor 2 will emit an `Edited` event only for that editor.
  86    editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
  87    assert_eq!(
  88        mem::take(&mut *events.borrow_mut()),
  89        [
  90            ("editor2", Event::Edited),
  91            ("editor1", Event::BufferEdited),
  92            ("editor2", Event::BufferEdited),
  93        ]
  94    );
  95
  96    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  97    editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
  98    assert_eq!(
  99        mem::take(&mut *events.borrow_mut()),
 100        [
 101            ("editor1", Event::Edited),
 102            ("editor1", Event::BufferEdited),
 103            ("editor2", Event::BufferEdited),
 104            ("editor1", Event::DirtyChanged),
 105            ("editor2", Event::DirtyChanged),
 106        ]
 107    );
 108
 109    // Redoing on editor 1 will emit an `Edited` event only for that editor.
 110    editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
 111    assert_eq!(
 112        mem::take(&mut *events.borrow_mut()),
 113        [
 114            ("editor1", Event::Edited),
 115            ("editor1", Event::BufferEdited),
 116            ("editor2", Event::BufferEdited),
 117            ("editor1", Event::DirtyChanged),
 118            ("editor2", Event::DirtyChanged),
 119        ]
 120    );
 121
 122    // Undoing on editor 2 will emit an `Edited` event only for that editor.
 123    editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
 124    assert_eq!(
 125        mem::take(&mut *events.borrow_mut()),
 126        [
 127            ("editor2", Event::Edited),
 128            ("editor1", Event::BufferEdited),
 129            ("editor2", Event::BufferEdited),
 130            ("editor1", Event::DirtyChanged),
 131            ("editor2", Event::DirtyChanged),
 132        ]
 133    );
 134
 135    // Redoing on editor 2 will emit an `Edited` event only for that editor.
 136    editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
 137    assert_eq!(
 138        mem::take(&mut *events.borrow_mut()),
 139        [
 140            ("editor2", Event::Edited),
 141            ("editor1", Event::BufferEdited),
 142            ("editor2", Event::BufferEdited),
 143            ("editor1", Event::DirtyChanged),
 144            ("editor2", Event::DirtyChanged),
 145        ]
 146    );
 147
 148    // No event is emitted when the mutation is a no-op.
 149    editor2.update(cx, |editor, cx| {
 150        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 151
 152        editor.backspace(&Backspace, cx);
 153    });
 154    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
 155}
 156
 157#[gpui::test]
 158fn test_undo_redo_with_selection_restoration(cx: &mut MutableAppContext) {
 159    cx.set_global(Settings::test(cx));
 160    let mut now = Instant::now();
 161    let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
 162    let group_interval = buffer.read(cx).transaction_group_interval();
 163    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
 164    let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
 165
 166    editor.update(cx, |editor, cx| {
 167        editor.start_transaction_at(now, cx);
 168        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
 169
 170        editor.insert("cd", cx);
 171        editor.end_transaction_at(now, cx);
 172        assert_eq!(editor.text(cx), "12cd56");
 173        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
 174
 175        editor.start_transaction_at(now, cx);
 176        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
 177        editor.insert("e", cx);
 178        editor.end_transaction_at(now, cx);
 179        assert_eq!(editor.text(cx), "12cde6");
 180        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
 181
 182        now += group_interval + Duration::from_millis(1);
 183        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
 184
 185        // Simulate an edit in another editor
 186        buffer.update(cx, |buffer, cx| {
 187            buffer.start_transaction_at(now, cx);
 188            buffer.edit([(0..1, "a")], None, cx);
 189            buffer.edit([(1..1, "b")], None, cx);
 190            buffer.end_transaction_at(now, cx);
 191        });
 192
 193        assert_eq!(editor.text(cx), "ab2cde6");
 194        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
 195
 196        // Last transaction happened past the group interval in a different editor.
 197        // Undo it individually and don't restore selections.
 198        editor.undo(&Undo, cx);
 199        assert_eq!(editor.text(cx), "12cde6");
 200        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
 201
 202        // First two transactions happened within the group interval in this editor.
 203        // Undo them together and restore selections.
 204        editor.undo(&Undo, cx);
 205        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
 206        assert_eq!(editor.text(cx), "123456");
 207        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
 208
 209        // Redo the first two transactions together.
 210        editor.redo(&Redo, cx);
 211        assert_eq!(editor.text(cx), "12cde6");
 212        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
 213
 214        // Redo the last transaction on its own.
 215        editor.redo(&Redo, cx);
 216        assert_eq!(editor.text(cx), "ab2cde6");
 217        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
 218
 219        // Test empty transactions.
 220        editor.start_transaction_at(now, cx);
 221        editor.end_transaction_at(now, cx);
 222        editor.undo(&Undo, cx);
 223        assert_eq!(editor.text(cx), "12cde6");
 224    });
 225}
 226
 227#[gpui::test]
 228fn test_ime_composition(cx: &mut MutableAppContext) {
 229    cx.set_global(Settings::test(cx));
 230    let buffer = cx.add_model(|cx| {
 231        let mut buffer = language::Buffer::new(0, "abcde", cx);
 232        // Ensure automatic grouping doesn't occur.
 233        buffer.set_group_interval(Duration::ZERO);
 234        buffer
 235    });
 236
 237    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
 238    cx.add_window(Default::default(), |cx| {
 239        let mut editor = build_editor(buffer.clone(), cx);
 240
 241        // Start a new IME composition.
 242        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
 243        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
 244        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
 245        assert_eq!(editor.text(cx), "äbcde");
 246        assert_eq!(
 247            editor.marked_text_ranges(cx),
 248            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
 249        );
 250
 251        // Finalize IME composition.
 252        editor.replace_text_in_range(None, "ā", cx);
 253        assert_eq!(editor.text(cx), "ābcde");
 254        assert_eq!(editor.marked_text_ranges(cx), None);
 255
 256        // IME composition edits are grouped and are undone/redone at once.
 257        editor.undo(&Default::default(), cx);
 258        assert_eq!(editor.text(cx), "abcde");
 259        assert_eq!(editor.marked_text_ranges(cx), None);
 260        editor.redo(&Default::default(), cx);
 261        assert_eq!(editor.text(cx), "ābcde");
 262        assert_eq!(editor.marked_text_ranges(cx), None);
 263
 264        // Start a new IME composition.
 265        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
 266        assert_eq!(
 267            editor.marked_text_ranges(cx),
 268            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
 269        );
 270
 271        // Undoing during an IME composition cancels it.
 272        editor.undo(&Default::default(), cx);
 273        assert_eq!(editor.text(cx), "ābcde");
 274        assert_eq!(editor.marked_text_ranges(cx), None);
 275
 276        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
 277        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
 278        assert_eq!(editor.text(cx), "ābcdè");
 279        assert_eq!(
 280            editor.marked_text_ranges(cx),
 281            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
 282        );
 283
 284        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
 285        editor.replace_text_in_range(Some(4..999), "ę", cx);
 286        assert_eq!(editor.text(cx), "ābcdę");
 287        assert_eq!(editor.marked_text_ranges(cx), None);
 288
 289        // Start a new IME composition with multiple cursors.
 290        editor.change_selections(None, cx, |s| {
 291            s.select_ranges([
 292                OffsetUtf16(1)..OffsetUtf16(1),
 293                OffsetUtf16(3)..OffsetUtf16(3),
 294                OffsetUtf16(5)..OffsetUtf16(5),
 295            ])
 296        });
 297        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
 298        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
 299        assert_eq!(
 300            editor.marked_text_ranges(cx),
 301            Some(vec![
 302                OffsetUtf16(0)..OffsetUtf16(3),
 303                OffsetUtf16(4)..OffsetUtf16(7),
 304                OffsetUtf16(8)..OffsetUtf16(11)
 305            ])
 306        );
 307
 308        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
 309        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
 310        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
 311        assert_eq!(
 312            editor.marked_text_ranges(cx),
 313            Some(vec![
 314                OffsetUtf16(1)..OffsetUtf16(2),
 315                OffsetUtf16(5)..OffsetUtf16(6),
 316                OffsetUtf16(9)..OffsetUtf16(10)
 317            ])
 318        );
 319
 320        // Finalize IME composition with multiple cursors.
 321        editor.replace_text_in_range(Some(9..10), "2", cx);
 322        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
 323        assert_eq!(editor.marked_text_ranges(cx), None);
 324
 325        editor
 326    });
 327}
 328
 329#[gpui::test]
 330fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) {
 331    cx.set_global(Settings::test(cx));
 332
 333    let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
 334    let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
 335    editor.update(cx, |view, cx| {
 336        view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
 337    });
 338    assert_eq!(
 339        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 340        [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
 341    );
 342
 343    editor.update(cx, |view, cx| {
 344        view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
 345    });
 346
 347    assert_eq!(
 348        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 349        [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
 350    );
 351
 352    editor.update(cx, |view, cx| {
 353        view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
 354    });
 355
 356    assert_eq!(
 357        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 358        [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
 359    );
 360
 361    editor.update(cx, |view, cx| {
 362        view.end_selection(cx);
 363        view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
 364    });
 365
 366    assert_eq!(
 367        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 368        [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
 369    );
 370
 371    editor.update(cx, |view, cx| {
 372        view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
 373        view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx);
 374    });
 375
 376    assert_eq!(
 377        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 378        [
 379            DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
 380            DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
 381        ]
 382    );
 383
 384    editor.update(cx, |view, cx| {
 385        view.end_selection(cx);
 386    });
 387
 388    assert_eq!(
 389        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 390        [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
 391    );
 392}
 393
 394#[gpui::test]
 395fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) {
 396    cx.set_global(Settings::test(cx));
 397    let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
 398    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
 399
 400    view.update(cx, |view, cx| {
 401        view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
 402        assert_eq!(
 403            view.selections.display_ranges(cx),
 404            [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
 405        );
 406    });
 407
 408    view.update(cx, |view, cx| {
 409        view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
 410        assert_eq!(
 411            view.selections.display_ranges(cx),
 412            [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
 413        );
 414    });
 415
 416    view.update(cx, |view, cx| {
 417        view.cancel(&Cancel, cx);
 418        view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
 419        assert_eq!(
 420            view.selections.display_ranges(cx),
 421            [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
 422        );
 423    });
 424}
 425
 426#[gpui::test]
 427fn test_clone(cx: &mut gpui::MutableAppContext) {
 428    let (text, selection_ranges) = marked_text_ranges(
 429        indoc! {"
 430            one
 431            two
 432            threeˇ
 433            four
 434            fiveˇ
 435        "},
 436        true,
 437    );
 438    cx.set_global(Settings::test(cx));
 439    let buffer = MultiBuffer::build_simple(&text, cx);
 440
 441    let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
 442
 443    editor.update(cx, |editor, cx| {
 444        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
 445        editor.fold_ranges(
 446            [
 447                Point::new(1, 0)..Point::new(2, 0),
 448                Point::new(3, 0)..Point::new(4, 0),
 449            ],
 450            true,
 451            cx,
 452        );
 453    });
 454
 455    let (_, cloned_editor) = editor.update(cx, |editor, cx| {
 456        cx.add_window(Default::default(), |cx| editor.clone(cx))
 457    });
 458
 459    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx));
 460    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx));
 461
 462    assert_eq!(
 463        cloned_editor.update(cx, |e, cx| e.display_text(cx)),
 464        editor.update(cx, |e, cx| e.display_text(cx))
 465    );
 466    assert_eq!(
 467        cloned_snapshot
 468            .folds_in_range(0..text.len())
 469            .collect::<Vec<_>>(),
 470        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
 471    );
 472    assert_set_eq!(
 473        cloned_editor.read(cx).selections.ranges::<Point>(cx),
 474        editor.read(cx).selections.ranges(cx)
 475    );
 476    assert_set_eq!(
 477        cloned_editor.update(cx, |e, cx| e.selections.display_ranges(cx)),
 478        editor.update(cx, |e, cx| e.selections.display_ranges(cx))
 479    );
 480}
 481
 482#[gpui::test]
 483fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
 484    cx.set_global(Settings::test(cx));
 485    cx.set_global(DragAndDrop::<Workspace>::default());
 486    use workspace::item::Item;
 487    let (_, pane) = cx.add_window(Default::default(), |cx| Pane::new(None, cx));
 488    let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
 489
 490    cx.add_view(&pane, |cx| {
 491        let mut editor = build_editor(buffer.clone(), cx);
 492        let handle = cx.handle();
 493        editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
 494
 495        fn pop_history(editor: &mut Editor, cx: &mut MutableAppContext) -> Option<NavigationEntry> {
 496            editor.nav_history.as_mut().unwrap().pop_backward(cx)
 497        }
 498
 499        // Move the cursor a small distance.
 500        // Nothing is added to the navigation history.
 501        editor.change_selections(None, cx, |s| {
 502            s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
 503        });
 504        editor.change_selections(None, cx, |s| {
 505            s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
 506        });
 507        assert!(pop_history(&mut editor, cx).is_none());
 508
 509        // Move the cursor a large distance.
 510        // The history can jump back to the previous position.
 511        editor.change_selections(None, cx, |s| {
 512            s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
 513        });
 514        let nav_entry = pop_history(&mut editor, cx).unwrap();
 515        editor.navigate(nav_entry.data.unwrap(), cx);
 516        assert_eq!(nav_entry.item.id(), cx.view_id());
 517        assert_eq!(
 518            editor.selections.display_ranges(cx),
 519            &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
 520        );
 521        assert!(pop_history(&mut editor, cx).is_none());
 522
 523        // Move the cursor a small distance via the mouse.
 524        // Nothing is added to the navigation history.
 525        editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
 526        editor.end_selection(cx);
 527        assert_eq!(
 528            editor.selections.display_ranges(cx),
 529            &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
 530        );
 531        assert!(pop_history(&mut editor, cx).is_none());
 532
 533        // Move the cursor a large distance via the mouse.
 534        // The history can jump back to the previous position.
 535        editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
 536        editor.end_selection(cx);
 537        assert_eq!(
 538            editor.selections.display_ranges(cx),
 539            &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
 540        );
 541        let nav_entry = pop_history(&mut editor, cx).unwrap();
 542        editor.navigate(nav_entry.data.unwrap(), cx);
 543        assert_eq!(nav_entry.item.id(), cx.view_id());
 544        assert_eq!(
 545            editor.selections.display_ranges(cx),
 546            &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
 547        );
 548        assert!(pop_history(&mut editor, cx).is_none());
 549
 550        // Set scroll position to check later
 551        editor.set_scroll_position(Vector2F::new(5.5, 5.5), cx);
 552        let original_scroll_position = editor.scroll_manager.anchor();
 553
 554        // Jump to the end of the document and adjust scroll
 555        editor.move_to_end(&MoveToEnd, cx);
 556        editor.set_scroll_position(Vector2F::new(-2.5, -0.5), cx);
 557        assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
 558
 559        let nav_entry = pop_history(&mut editor, cx).unwrap();
 560        editor.navigate(nav_entry.data.unwrap(), cx);
 561        assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
 562
 563        // Ensure we don't panic when navigation data contains invalid anchors *and* points.
 564        let mut invalid_anchor = editor.scroll_manager.anchor().top_anchor;
 565        invalid_anchor.text_anchor.buffer_id = Some(999);
 566        let invalid_point = Point::new(9999, 0);
 567        editor.navigate(
 568            Box::new(NavigationData {
 569                cursor_anchor: invalid_anchor,
 570                cursor_position: invalid_point,
 571                scroll_anchor: ScrollAnchor {
 572                    top_anchor: invalid_anchor,
 573                    offset: Default::default(),
 574                },
 575                scroll_top_row: invalid_point.row,
 576            }),
 577            cx,
 578        );
 579        assert_eq!(
 580            editor.selections.display_ranges(cx),
 581            &[editor.max_point(cx)..editor.max_point(cx)]
 582        );
 583        assert_eq!(
 584            editor.scroll_position(cx),
 585            vec2f(0., editor.max_point(cx).row() as f32)
 586        );
 587
 588        editor
 589    });
 590}
 591
 592#[gpui::test]
 593fn test_cancel(cx: &mut gpui::MutableAppContext) {
 594    cx.set_global(Settings::test(cx));
 595    let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
 596    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
 597
 598    view.update(cx, |view, cx| {
 599        view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
 600        view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
 601        view.end_selection(cx);
 602
 603        view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
 604        view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx);
 605        view.end_selection(cx);
 606        assert_eq!(
 607            view.selections.display_ranges(cx),
 608            [
 609                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
 610                DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
 611            ]
 612        );
 613    });
 614
 615    view.update(cx, |view, cx| {
 616        view.cancel(&Cancel, cx);
 617        assert_eq!(
 618            view.selections.display_ranges(cx),
 619            [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
 620        );
 621    });
 622
 623    view.update(cx, |view, cx| {
 624        view.cancel(&Cancel, cx);
 625        assert_eq!(
 626            view.selections.display_ranges(cx),
 627            [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
 628        );
 629    });
 630}
 631
 632#[gpui::test]
 633fn test_fold(cx: &mut gpui::MutableAppContext) {
 634    cx.set_global(Settings::test(cx));
 635    let buffer = MultiBuffer::build_simple(
 636        &"
 637            impl Foo {
 638                // Hello!
 639
 640                fn a() {
 641                    1
 642                }
 643
 644                fn b() {
 645                    2
 646                }
 647
 648                fn c() {
 649                    3
 650                }
 651            }
 652        "
 653        .unindent(),
 654        cx,
 655    );
 656    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
 657
 658    view.update(cx, |view, cx| {
 659        view.change_selections(None, cx, |s| {
 660            s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]);
 661        });
 662        view.fold(&Fold, cx);
 663        assert_eq!(
 664            view.display_text(cx),
 665            "
 666                impl Foo {
 667                    // Hello!
 668
 669                    fn a() {
 670                        1
 671                    }
 672
 673                    fn b() {⋯
 674                    }
 675
 676                    fn c() {⋯
 677                    }
 678                }
 679            "
 680            .unindent(),
 681        );
 682
 683        view.fold(&Fold, cx);
 684        assert_eq!(
 685            view.display_text(cx),
 686            "
 687                impl Foo {⋯
 688                }
 689            "
 690            .unindent(),
 691        );
 692
 693        view.unfold_lines(&UnfoldLines, cx);
 694        assert_eq!(
 695            view.display_text(cx),
 696            "
 697                impl Foo {
 698                    // Hello!
 699
 700                    fn a() {
 701                        1
 702                    }
 703
 704                    fn b() {⋯
 705                    }
 706
 707                    fn c() {⋯
 708                    }
 709                }
 710            "
 711            .unindent(),
 712        );
 713
 714        view.unfold_lines(&UnfoldLines, cx);
 715        assert_eq!(view.display_text(cx), buffer.read(cx).read(cx).text());
 716    });
 717}
 718
 719#[gpui::test]
 720fn test_move_cursor(cx: &mut gpui::MutableAppContext) {
 721    cx.set_global(Settings::test(cx));
 722    let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
 723    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
 724
 725    buffer.update(cx, |buffer, cx| {
 726        buffer.edit(
 727            vec![
 728                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 729                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 730            ],
 731            None,
 732            cx,
 733        );
 734    });
 735
 736    view.update(cx, |view, cx| {
 737        assert_eq!(
 738            view.selections.display_ranges(cx),
 739            &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
 740        );
 741
 742        view.move_down(&MoveDown, cx);
 743        assert_eq!(
 744            view.selections.display_ranges(cx),
 745            &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
 746        );
 747
 748        view.move_right(&MoveRight, cx);
 749        assert_eq!(
 750            view.selections.display_ranges(cx),
 751            &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
 752        );
 753
 754        view.move_left(&MoveLeft, cx);
 755        assert_eq!(
 756            view.selections.display_ranges(cx),
 757            &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
 758        );
 759
 760        view.move_up(&MoveUp, cx);
 761        assert_eq!(
 762            view.selections.display_ranges(cx),
 763            &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
 764        );
 765
 766        view.move_to_end(&MoveToEnd, cx);
 767        assert_eq!(
 768            view.selections.display_ranges(cx),
 769            &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
 770        );
 771
 772        view.move_to_beginning(&MoveToBeginning, cx);
 773        assert_eq!(
 774            view.selections.display_ranges(cx),
 775            &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
 776        );
 777
 778        view.change_selections(None, cx, |s| {
 779            s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]);
 780        });
 781        view.select_to_beginning(&SelectToBeginning, cx);
 782        assert_eq!(
 783            view.selections.display_ranges(cx),
 784            &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
 785        );
 786
 787        view.select_to_end(&SelectToEnd, cx);
 788        assert_eq!(
 789            view.selections.display_ranges(cx),
 790            &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
 791        );
 792    });
 793}
 794
 795#[gpui::test]
 796fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) {
 797    cx.set_global(Settings::test(cx));
 798    let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
 799    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
 800
 801    assert_eq!('ⓐ'.len_utf8(), 3);
 802    assert_eq!('α'.len_utf8(), 2);
 803
 804    view.update(cx, |view, cx| {
 805        view.fold_ranges(
 806            vec![
 807                Point::new(0, 6)..Point::new(0, 12),
 808                Point::new(1, 2)..Point::new(1, 4),
 809                Point::new(2, 4)..Point::new(2, 8),
 810            ],
 811            true,
 812            cx,
 813        );
 814        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε\n");
 815
 816        view.move_right(&MoveRight, cx);
 817        assert_eq!(
 818            view.selections.display_ranges(cx),
 819            &[empty_range(0, "".len())]
 820        );
 821        view.move_right(&MoveRight, cx);
 822        assert_eq!(
 823            view.selections.display_ranges(cx),
 824            &[empty_range(0, "ⓐⓑ".len())]
 825        );
 826        view.move_right(&MoveRight, cx);
 827        assert_eq!(
 828            view.selections.display_ranges(cx),
 829            &[empty_range(0, "ⓐⓑ⋯".len())]
 830        );
 831
 832        view.move_down(&MoveDown, cx);
 833        assert_eq!(
 834            view.selections.display_ranges(cx),
 835            &[empty_range(1, "ab⋯".len())]
 836        );
 837        view.move_left(&MoveLeft, cx);
 838        assert_eq!(
 839            view.selections.display_ranges(cx),
 840            &[empty_range(1, "ab".len())]
 841        );
 842        view.move_left(&MoveLeft, cx);
 843        assert_eq!(
 844            view.selections.display_ranges(cx),
 845            &[empty_range(1, "a".len())]
 846        );
 847
 848        view.move_down(&MoveDown, cx);
 849        assert_eq!(
 850            view.selections.display_ranges(cx),
 851            &[empty_range(2, "α".len())]
 852        );
 853        view.move_right(&MoveRight, cx);
 854        assert_eq!(
 855            view.selections.display_ranges(cx),
 856            &[empty_range(2, "αβ".len())]
 857        );
 858        view.move_right(&MoveRight, cx);
 859        assert_eq!(
 860            view.selections.display_ranges(cx),
 861            &[empty_range(2, "αβ⋯".len())]
 862        );
 863        view.move_right(&MoveRight, cx);
 864        assert_eq!(
 865            view.selections.display_ranges(cx),
 866            &[empty_range(2, "αβ⋯ε".len())]
 867        );
 868
 869        view.move_up(&MoveUp, cx);
 870        assert_eq!(
 871            view.selections.display_ranges(cx),
 872            &[empty_range(1, "ab⋯e".len())]
 873        );
 874        view.move_up(&MoveUp, cx);
 875        assert_eq!(
 876            view.selections.display_ranges(cx),
 877            &[empty_range(0, "ⓐⓑ⋯ⓔ".len())]
 878        );
 879        view.move_left(&MoveLeft, cx);
 880        assert_eq!(
 881            view.selections.display_ranges(cx),
 882            &[empty_range(0, "ⓐⓑ⋯".len())]
 883        );
 884        view.move_left(&MoveLeft, cx);
 885        assert_eq!(
 886            view.selections.display_ranges(cx),
 887            &[empty_range(0, "ⓐⓑ".len())]
 888        );
 889        view.move_left(&MoveLeft, cx);
 890        assert_eq!(
 891            view.selections.display_ranges(cx),
 892            &[empty_range(0, "".len())]
 893        );
 894    });
 895}
 896
 897#[gpui::test]
 898fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) {
 899    cx.set_global(Settings::test(cx));
 900    let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 901    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
 902    view.update(cx, |view, cx| {
 903        view.change_selections(None, cx, |s| {
 904            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 905        });
 906        view.move_down(&MoveDown, cx);
 907        assert_eq!(
 908            view.selections.display_ranges(cx),
 909            &[empty_range(1, "abcd".len())]
 910        );
 911
 912        view.move_down(&MoveDown, cx);
 913        assert_eq!(
 914            view.selections.display_ranges(cx),
 915            &[empty_range(2, "αβγ".len())]
 916        );
 917
 918        view.move_down(&MoveDown, cx);
 919        assert_eq!(
 920            view.selections.display_ranges(cx),
 921            &[empty_range(3, "abcd".len())]
 922        );
 923
 924        view.move_down(&MoveDown, cx);
 925        assert_eq!(
 926            view.selections.display_ranges(cx),
 927            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 928        );
 929
 930        view.move_up(&MoveUp, cx);
 931        assert_eq!(
 932            view.selections.display_ranges(cx),
 933            &[empty_range(3, "abcd".len())]
 934        );
 935
 936        view.move_up(&MoveUp, cx);
 937        assert_eq!(
 938            view.selections.display_ranges(cx),
 939            &[empty_range(2, "αβγ".len())]
 940        );
 941    });
 942}
 943
 944#[gpui::test]
 945fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) {
 946    cx.set_global(Settings::test(cx));
 947    let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 948    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
 949    view.update(cx, |view, cx| {
 950        view.change_selections(None, cx, |s| {
 951            s.select_display_ranges([
 952                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
 953                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
 954            ]);
 955        });
 956    });
 957
 958    view.update(cx, |view, cx| {
 959        view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
 960        assert_eq!(
 961            view.selections.display_ranges(cx),
 962            &[
 963                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
 964                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
 965            ]
 966        );
 967    });
 968
 969    view.update(cx, |view, cx| {
 970        view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
 971        assert_eq!(
 972            view.selections.display_ranges(cx),
 973            &[
 974                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
 975                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
 976            ]
 977        );
 978    });
 979
 980    view.update(cx, |view, cx| {
 981        view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
 982        assert_eq!(
 983            view.selections.display_ranges(cx),
 984            &[
 985                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
 986                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
 987            ]
 988        );
 989    });
 990
 991    view.update(cx, |view, cx| {
 992        view.move_to_end_of_line(&MoveToEndOfLine, cx);
 993        assert_eq!(
 994            view.selections.display_ranges(cx),
 995            &[
 996                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
 997                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
 998            ]
 999        );
1000    });
1001
1002    // Moving to the end of line again is a no-op.
1003    view.update(cx, |view, cx| {
1004        view.move_to_end_of_line(&MoveToEndOfLine, cx);
1005        assert_eq!(
1006            view.selections.display_ranges(cx),
1007            &[
1008                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
1009                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
1010            ]
1011        );
1012    });
1013
1014    view.update(cx, |view, cx| {
1015        view.move_left(&MoveLeft, cx);
1016        view.select_to_beginning_of_line(
1017            &SelectToBeginningOfLine {
1018                stop_at_soft_wraps: true,
1019            },
1020            cx,
1021        );
1022        assert_eq!(
1023            view.selections.display_ranges(cx),
1024            &[
1025                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
1026                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
1027            ]
1028        );
1029    });
1030
1031    view.update(cx, |view, cx| {
1032        view.select_to_beginning_of_line(
1033            &SelectToBeginningOfLine {
1034                stop_at_soft_wraps: true,
1035            },
1036            cx,
1037        );
1038        assert_eq!(
1039            view.selections.display_ranges(cx),
1040            &[
1041                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
1042                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
1043            ]
1044        );
1045    });
1046
1047    view.update(cx, |view, cx| {
1048        view.select_to_beginning_of_line(
1049            &SelectToBeginningOfLine {
1050                stop_at_soft_wraps: true,
1051            },
1052            cx,
1053        );
1054        assert_eq!(
1055            view.selections.display_ranges(cx),
1056            &[
1057                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
1058                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
1059            ]
1060        );
1061    });
1062
1063    view.update(cx, |view, cx| {
1064        view.select_to_end_of_line(
1065            &SelectToEndOfLine {
1066                stop_at_soft_wraps: true,
1067            },
1068            cx,
1069        );
1070        assert_eq!(
1071            view.selections.display_ranges(cx),
1072            &[
1073                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
1074                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
1075            ]
1076        );
1077    });
1078
1079    view.update(cx, |view, cx| {
1080        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
1081        assert_eq!(view.display_text(cx), "ab\n  de");
1082        assert_eq!(
1083            view.selections.display_ranges(cx),
1084            &[
1085                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
1086                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
1087            ]
1088        );
1089    });
1090
1091    view.update(cx, |view, cx| {
1092        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
1093        assert_eq!(view.display_text(cx), "\n");
1094        assert_eq!(
1095            view.selections.display_ranges(cx),
1096            &[
1097                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
1098                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
1099            ]
1100        );
1101    });
1102}
1103
1104#[gpui::test]
1105fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) {
1106    cx.set_global(Settings::test(cx));
1107    let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
1108    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
1109    view.update(cx, |view, cx| {
1110        view.change_selections(None, cx, |s| {
1111            s.select_display_ranges([
1112                DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
1113                DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
1114            ])
1115        });
1116
1117        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1118        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
1119
1120        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1121        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
1122
1123        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1124        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
1125
1126        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1127        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
1128
1129        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1130        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
1131
1132        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1133        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
1134
1135        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1136        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
1137
1138        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1139        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
1140
1141        view.move_right(&MoveRight, cx);
1142        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
1143        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
1144
1145        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
1146        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
1147
1148        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
1149        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
1150    });
1151}
1152
1153#[gpui::test]
1154fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
1155    cx.set_global(Settings::test(cx));
1156    let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
1157    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
1158
1159    view.update(cx, |view, cx| {
1160        view.set_wrap_width(Some(140.), cx);
1161        assert_eq!(
1162            view.display_text(cx),
1163            "use one::{\n    two::three::\n    four::five\n};"
1164        );
1165
1166        view.change_selections(None, cx, |s| {
1167            s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]);
1168        });
1169
1170        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1171        assert_eq!(
1172            view.selections.display_ranges(cx),
1173            &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
1174        );
1175
1176        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1177        assert_eq!(
1178            view.selections.display_ranges(cx),
1179            &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
1180        );
1181
1182        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1183        assert_eq!(
1184            view.selections.display_ranges(cx),
1185            &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
1186        );
1187
1188        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1189        assert_eq!(
1190            view.selections.display_ranges(cx),
1191            &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
1192        );
1193
1194        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1195        assert_eq!(
1196            view.selections.display_ranges(cx),
1197            &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
1198        );
1199
1200        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1201        assert_eq!(
1202            view.selections.display_ranges(cx),
1203            &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
1204        );
1205    });
1206}
1207
1208#[gpui::test]
1209async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
1210    let mut cx = EditorTestContext::new(cx);
1211
1212    let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
1213    cx.simulate_window_resize(cx.window_id, vec2f(100., 4. * line_height));
1214
1215    cx.set_state(
1216        &r#"
1217        ˇone
1218        two
1219        threeˇ
1220        four
1221        five
1222        six
1223        seven
1224        eight
1225        nine
1226        ten
1227        "#
1228        .unindent(),
1229    );
1230
1231    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
1232    cx.assert_editor_state(
1233        &r#"
1234        one
1235        two
1236        three
1237        ˇfour
1238        five
1239        sixˇ
1240        seven
1241        eight
1242        nine
1243        ten
1244        "#
1245        .unindent(),
1246    );
1247
1248    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
1249    cx.assert_editor_state(
1250        &r#"
1251        one
1252        two
1253        three
1254        four
1255        five
1256        six
1257        ˇseven
1258        eight
1259        nineˇ
1260        ten
1261        "#
1262        .unindent(),
1263    );
1264
1265    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
1266    cx.assert_editor_state(
1267        &r#"
1268        one
1269        two
1270        three
1271        ˇfour
1272        five
1273        sixˇ
1274        seven
1275        eight
1276        nine
1277        ten
1278        "#
1279        .unindent(),
1280    );
1281
1282    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
1283    cx.assert_editor_state(
1284        &r#"
1285        ˇone
1286        two
1287        threeˇ
1288        four
1289        five
1290        six
1291        seven
1292        eight
1293        nine
1294        ten
1295        "#
1296        .unindent(),
1297    );
1298
1299    // Test select collapsing
1300    cx.update_editor(|editor, cx| {
1301        editor.move_page_down(&MovePageDown::default(), cx);
1302        editor.move_page_down(&MovePageDown::default(), cx);
1303        editor.move_page_down(&MovePageDown::default(), cx);
1304    });
1305    cx.assert_editor_state(
1306        &r#"
1307        one
1308        two
1309        three
1310        four
1311        five
1312        six
1313        seven
1314        eight
1315        nine
1316        ˇten
1317        ˇ"#
1318        .unindent(),
1319    );
1320}
1321
1322#[gpui::test]
1323async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
1324    let mut cx = EditorTestContext::new(cx);
1325    cx.set_state("one «two threeˇ» four");
1326    cx.update_editor(|editor, cx| {
1327        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
1328        assert_eq!(editor.text(cx), " four");
1329    });
1330}
1331
1332#[gpui::test]
1333fn test_delete_to_word_boundary(cx: &mut gpui::MutableAppContext) {
1334    cx.set_global(Settings::test(cx));
1335    let buffer = MultiBuffer::build_simple("one two three four", cx);
1336    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
1337
1338    view.update(cx, |view, cx| {
1339        view.change_selections(None, cx, |s| {
1340            s.select_display_ranges([
1341                // an empty selection - the preceding word fragment is deleted
1342                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
1343                // characters selected - they are deleted
1344                DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
1345            ])
1346        });
1347        view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
1348    });
1349
1350    assert_eq!(buffer.read(cx).read(cx).text(), "e two te four");
1351
1352    view.update(cx, |view, cx| {
1353        view.change_selections(None, cx, |s| {
1354            s.select_display_ranges([
1355                // an empty selection - the following word fragment is deleted
1356                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
1357                // characters selected - they are deleted
1358                DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
1359            ])
1360        });
1361        view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
1362    });
1363
1364    assert_eq!(buffer.read(cx).read(cx).text(), "e t te our");
1365}
1366
1367#[gpui::test]
1368fn test_newline(cx: &mut gpui::MutableAppContext) {
1369    cx.set_global(Settings::test(cx));
1370    let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
1371    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
1372
1373    view.update(cx, |view, cx| {
1374        view.change_selections(None, cx, |s| {
1375            s.select_display_ranges([
1376                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
1377                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
1378                DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
1379            ])
1380        });
1381
1382        view.newline(&Newline, cx);
1383        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
1384    });
1385}
1386
1387#[gpui::test]
1388fn test_newline_with_old_selections(cx: &mut gpui::MutableAppContext) {
1389    cx.set_global(Settings::test(cx));
1390    let buffer = MultiBuffer::build_simple(
1391        "
1392            a
1393            b(
1394                X
1395            )
1396            c(
1397                X
1398            )
1399        "
1400        .unindent()
1401        .as_str(),
1402        cx,
1403    );
1404
1405    let (_, editor) = cx.add_window(Default::default(), |cx| {
1406        let mut editor = build_editor(buffer.clone(), cx);
1407        editor.change_selections(None, cx, |s| {
1408            s.select_ranges([
1409                Point::new(2, 4)..Point::new(2, 5),
1410                Point::new(5, 4)..Point::new(5, 5),
1411            ])
1412        });
1413        editor
1414    });
1415
1416    // Edit the buffer directly, deleting ranges surrounding the editor's selections
1417    buffer.update(cx, |buffer, cx| {
1418        buffer.edit(
1419            [
1420                (Point::new(1, 2)..Point::new(3, 0), ""),
1421                (Point::new(4, 2)..Point::new(6, 0), ""),
1422            ],
1423            None,
1424            cx,
1425        );
1426        assert_eq!(
1427            buffer.read(cx).text(),
1428            "
1429                a
1430                b()
1431                c()
1432            "
1433            .unindent()
1434        );
1435    });
1436
1437    editor.update(cx, |editor, cx| {
1438        assert_eq!(
1439            editor.selections.ranges(cx),
1440            &[
1441                Point::new(1, 2)..Point::new(1, 2),
1442                Point::new(2, 2)..Point::new(2, 2),
1443            ],
1444        );
1445
1446        editor.newline(&Newline, cx);
1447        assert_eq!(
1448            editor.text(cx),
1449            "
1450                a
1451                b(
1452                )
1453                c(
1454                )
1455            "
1456            .unindent()
1457        );
1458
1459        // The selections are moved after the inserted newlines
1460        assert_eq!(
1461            editor.selections.ranges(cx),
1462            &[
1463                Point::new(2, 0)..Point::new(2, 0),
1464                Point::new(4, 0)..Point::new(4, 0),
1465            ],
1466        );
1467    });
1468}
1469
1470#[gpui::test]
1471async fn test_newline_below(cx: &mut gpui::TestAppContext) {
1472    let mut cx = EditorTestContext::new(cx);
1473    cx.update(|cx| {
1474        cx.update_global::<Settings, _, _>(|settings, _| {
1475            settings.editor_overrides.tab_size = Some(NonZeroU32::new(4).unwrap());
1476        });
1477    });
1478
1479    let language = Arc::new(
1480        Language::new(
1481            LanguageConfig::default(),
1482            Some(tree_sitter_rust::language()),
1483        )
1484        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
1485        .unwrap(),
1486    );
1487    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1488
1489    cx.set_state(indoc! {"
1490        const a: ˇA = (
14911492                «const_functionˇ»(ˇ),
1493                so«mˇ»et«hˇ»ing_ˇelse,ˇ
14941495        ˇ);ˇ
1496    "});
1497    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
1498    cx.assert_editor_state(indoc! {"
1499        const a: A = (
1500            ˇ
1501            (
1502                ˇ
1503                const_function(),
1504                ˇ
1505                ˇ
1506                something_else,
1507                ˇ
1508                ˇ
1509                ˇ
1510                ˇ
1511            )
1512            ˇ
1513        );
1514        ˇ
1515        ˇ
1516    "});
1517}
1518
1519#[gpui::test]
1520fn test_insert_with_old_selections(cx: &mut gpui::MutableAppContext) {
1521    cx.set_global(Settings::test(cx));
1522    let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
1523    let (_, editor) = cx.add_window(Default::default(), |cx| {
1524        let mut editor = build_editor(buffer.clone(), cx);
1525        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
1526        editor
1527    });
1528
1529    // Edit the buffer directly, deleting ranges surrounding the editor's selections
1530    buffer.update(cx, |buffer, cx| {
1531        buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
1532        assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
1533    });
1534
1535    editor.update(cx, |editor, cx| {
1536        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
1537
1538        editor.insert("Z", cx);
1539        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
1540
1541        // The selections are moved after the inserted characters
1542        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
1543    });
1544}
1545
1546#[gpui::test]
1547async fn test_tab(cx: &mut gpui::TestAppContext) {
1548    let mut cx = EditorTestContext::new(cx);
1549    cx.update(|cx| {
1550        cx.update_global::<Settings, _, _>(|settings, _| {
1551            settings.editor_overrides.tab_size = Some(NonZeroU32::new(3).unwrap());
1552        });
1553    });
1554    cx.set_state(indoc! {"
1555        ˇabˇc
1556        ˇ🏀ˇ🏀ˇefg
15571558    "});
1559    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1560    cx.assert_editor_state(indoc! {"
1561           ˇab ˇc
1562           ˇ🏀  ˇ🏀  ˇefg
1563        d  ˇ
1564    "});
1565
1566    cx.set_state(indoc! {"
1567        a
1568        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
1569    "});
1570    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1571    cx.assert_editor_state(indoc! {"
1572        a
1573           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
1574    "});
1575}
1576
1577#[gpui::test]
1578async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
1579    let mut cx = EditorTestContext::new(cx);
1580    let language = Arc::new(
1581        Language::new(
1582            LanguageConfig::default(),
1583            Some(tree_sitter_rust::language()),
1584        )
1585        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
1586        .unwrap(),
1587    );
1588    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1589
1590    // cursors that are already at the suggested indent level insert
1591    // a soft tab. cursors that are to the left of the suggested indent
1592    // auto-indent their line.
1593    cx.set_state(indoc! {"
1594        ˇ
1595        const a: B = (
1596            c(
1597                d(
1598        ˇ
1599                )
1600        ˇ
1601        ˇ    )
1602        );
1603    "});
1604    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1605    cx.assert_editor_state(indoc! {"
1606            ˇ
1607        const a: B = (
1608            c(
1609                d(
1610                    ˇ
1611                )
1612                ˇ
1613            ˇ)
1614        );
1615    "});
1616
1617    // handle auto-indent when there are multiple cursors on the same line
1618    cx.set_state(indoc! {"
1619        const a: B = (
1620            c(
1621        ˇ    ˇ
1622        ˇ    )
1623        );
1624    "});
1625    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1626    cx.assert_editor_state(indoc! {"
1627        const a: B = (
1628            c(
1629                ˇ
1630            ˇ)
1631        );
1632    "});
1633}
1634
1635#[gpui::test]
1636async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
1637    let mut cx = EditorTestContext::new(cx);
1638    let language = Arc::new(
1639        Language::new(
1640            LanguageConfig::default(),
1641            Some(tree_sitter_rust::language()),
1642        )
1643        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
1644        .unwrap(),
1645    );
1646    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1647
1648    cx.update(|cx| {
1649        cx.update_global::<Settings, _, _>(|settings, _| {
1650            settings.editor_overrides.tab_size = Some(4.try_into().unwrap());
1651        });
1652    });
1653
1654    cx.set_state(indoc! {"
1655        fn a() {
1656            if b {
1657        \t ˇc
1658            }
1659        }
1660    "});
1661
1662    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1663    cx.assert_editor_state(indoc! {"
1664        fn a() {
1665            if b {
1666                ˇc
1667            }
1668        }
1669    "});
1670}
1671
1672#[gpui::test]
1673async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
1674    let mut cx = EditorTestContext::new(cx);
1675
1676    cx.set_state(indoc! {"
1677          «oneˇ» «twoˇ»
1678        three
1679         four
1680    "});
1681    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1682    cx.assert_editor_state(indoc! {"
1683            «oneˇ» «twoˇ»
1684        three
1685         four
1686    "});
1687
1688    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1689    cx.assert_editor_state(indoc! {"
1690        «oneˇ» «twoˇ»
1691        three
1692         four
1693    "});
1694
1695    // select across line ending
1696    cx.set_state(indoc! {"
1697        one two
1698        t«hree
1699        ˇ» four
1700    "});
1701    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1702    cx.assert_editor_state(indoc! {"
1703        one two
1704            t«hree
1705        ˇ» four
1706    "});
1707
1708    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1709    cx.assert_editor_state(indoc! {"
1710        one two
1711        t«hree
1712        ˇ» four
1713    "});
1714
1715    // Ensure that indenting/outdenting works when the cursor is at column 0.
1716    cx.set_state(indoc! {"
1717        one two
1718        ˇthree
1719            four
1720    "});
1721    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1722    cx.assert_editor_state(indoc! {"
1723        one two
1724            ˇthree
1725            four
1726    "});
1727
1728    cx.set_state(indoc! {"
1729        one two
1730        ˇ    three
1731            four
1732    "});
1733    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1734    cx.assert_editor_state(indoc! {"
1735        one two
1736        ˇthree
1737            four
1738    "});
1739}
1740
1741#[gpui::test]
1742async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
1743    let mut cx = EditorTestContext::new(cx);
1744    cx.update(|cx| {
1745        cx.update_global::<Settings, _, _>(|settings, _| {
1746            settings.editor_overrides.hard_tabs = Some(true);
1747        });
1748    });
1749
1750    // select two ranges on one line
1751    cx.set_state(indoc! {"
1752        «oneˇ» «twoˇ»
1753        three
1754        four
1755    "});
1756    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1757    cx.assert_editor_state(indoc! {"
1758        \t«oneˇ» «twoˇ»
1759        three
1760        four
1761    "});
1762    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1763    cx.assert_editor_state(indoc! {"
1764        \t\t«oneˇ» «twoˇ»
1765        three
1766        four
1767    "});
1768    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1769    cx.assert_editor_state(indoc! {"
1770        \t«oneˇ» «twoˇ»
1771        three
1772        four
1773    "});
1774    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1775    cx.assert_editor_state(indoc! {"
1776        «oneˇ» «twoˇ»
1777        three
1778        four
1779    "});
1780
1781    // select across a line ending
1782    cx.set_state(indoc! {"
1783        one two
1784        t«hree
1785        ˇ»four
1786    "});
1787    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1788    cx.assert_editor_state(indoc! {"
1789        one two
1790        \tt«hree
1791        ˇ»four
1792    "});
1793    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1794    cx.assert_editor_state(indoc! {"
1795        one two
1796        \t\tt«hree
1797        ˇ»four
1798    "});
1799    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1800    cx.assert_editor_state(indoc! {"
1801        one two
1802        \tt«hree
1803        ˇ»four
1804    "});
1805    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1806    cx.assert_editor_state(indoc! {"
1807        one two
1808        t«hree
1809        ˇ»four
1810    "});
1811
1812    // Ensure that indenting/outdenting works when the cursor is at column 0.
1813    cx.set_state(indoc! {"
1814        one two
1815        ˇthree
1816        four
1817    "});
1818    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1819    cx.assert_editor_state(indoc! {"
1820        one two
1821        ˇthree
1822        four
1823    "});
1824    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1825    cx.assert_editor_state(indoc! {"
1826        one two
1827        \tˇthree
1828        four
1829    "});
1830    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1831    cx.assert_editor_state(indoc! {"
1832        one two
1833        ˇthree
1834        four
1835    "});
1836}
1837
1838#[gpui::test]
1839fn test_indent_outdent_with_excerpts(cx: &mut gpui::MutableAppContext) {
1840    cx.set_global(
1841        Settings::test(cx)
1842            .with_language_defaults(
1843                "TOML",
1844                EditorSettings {
1845                    tab_size: Some(2.try_into().unwrap()),
1846                    ..Default::default()
1847                },
1848            )
1849            .with_language_defaults(
1850                "Rust",
1851                EditorSettings {
1852                    tab_size: Some(4.try_into().unwrap()),
1853                    ..Default::default()
1854                },
1855            ),
1856    );
1857    let toml_language = Arc::new(Language::new(
1858        LanguageConfig {
1859            name: "TOML".into(),
1860            ..Default::default()
1861        },
1862        None,
1863    ));
1864    let rust_language = Arc::new(Language::new(
1865        LanguageConfig {
1866            name: "Rust".into(),
1867            ..Default::default()
1868        },
1869        None,
1870    ));
1871
1872    let toml_buffer =
1873        cx.add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx));
1874    let rust_buffer = cx.add_model(|cx| {
1875        Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx)
1876    });
1877    let multibuffer = cx.add_model(|cx| {
1878        let mut multibuffer = MultiBuffer::new(0);
1879        multibuffer.push_excerpts(
1880            toml_buffer.clone(),
1881            [ExcerptRange {
1882                context: Point::new(0, 0)..Point::new(2, 0),
1883                primary: None,
1884            }],
1885            cx,
1886        );
1887        multibuffer.push_excerpts(
1888            rust_buffer.clone(),
1889            [ExcerptRange {
1890                context: Point::new(0, 0)..Point::new(1, 0),
1891                primary: None,
1892            }],
1893            cx,
1894        );
1895        multibuffer
1896    });
1897
1898    cx.add_window(Default::default(), |cx| {
1899        let mut editor = build_editor(multibuffer, cx);
1900
1901        assert_eq!(
1902            editor.text(cx),
1903            indoc! {"
1904                a = 1
1905                b = 2
1906
1907                const c: usize = 3;
1908            "}
1909        );
1910
1911        select_ranges(
1912            &mut editor,
1913            indoc! {"
1914                «aˇ» = 1
1915                b = 2
1916
1917                «const c:ˇ» usize = 3;
1918            "},
1919            cx,
1920        );
1921
1922        editor.tab(&Tab, cx);
1923        assert_text_with_selections(
1924            &mut editor,
1925            indoc! {"
1926                  «aˇ» = 1
1927                b = 2
1928
1929                    «const c:ˇ» usize = 3;
1930            "},
1931            cx,
1932        );
1933        editor.tab_prev(&TabPrev, cx);
1934        assert_text_with_selections(
1935            &mut editor,
1936            indoc! {"
1937                «aˇ» = 1
1938                b = 2
1939
1940                «const c:ˇ» usize = 3;
1941            "},
1942            cx,
1943        );
1944
1945        editor
1946    });
1947}
1948
1949#[gpui::test]
1950async fn test_backspace(cx: &mut gpui::TestAppContext) {
1951    let mut cx = EditorTestContext::new(cx);
1952
1953    // Basic backspace
1954    cx.set_state(indoc! {"
1955        onˇe two three
1956        fou«rˇ» five six
1957        seven «ˇeight nine
1958        »ten
1959    "});
1960    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
1961    cx.assert_editor_state(indoc! {"
1962        oˇe two three
1963        fouˇ five six
1964        seven ˇten
1965    "});
1966
1967    // Test backspace inside and around indents
1968    cx.set_state(indoc! {"
1969        zero
1970            ˇone
1971                ˇtwo
1972            ˇ ˇ ˇ  three
1973        ˇ  ˇ  four
1974    "});
1975    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
1976    cx.assert_editor_state(indoc! {"
1977        zero
1978        ˇone
1979            ˇtwo
1980        ˇ  threeˇ  four
1981    "});
1982
1983    // Test backspace with line_mode set to true
1984    cx.update_editor(|e, _| e.selections.line_mode = true);
1985    cx.set_state(indoc! {"
1986        The ˇquick ˇbrown
1987        fox jumps over
1988        the lazy dog
1989        ˇThe qu«ick bˇ»rown"});
1990    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
1991    cx.assert_editor_state(indoc! {"
1992        ˇfox jumps over
1993        the lazy dogˇ"});
1994}
1995
1996#[gpui::test]
1997async fn test_delete(cx: &mut gpui::TestAppContext) {
1998    let mut cx = EditorTestContext::new(cx);
1999
2000    cx.set_state(indoc! {"
2001        onˇe two three
2002        fou«rˇ» five six
2003        seven «ˇeight nine
2004        »ten
2005    "});
2006    cx.update_editor(|e, cx| e.delete(&Delete, cx));
2007    cx.assert_editor_state(indoc! {"
2008        onˇ two three
2009        fouˇ five six
2010        seven ˇten
2011    "});
2012
2013    // Test backspace with line_mode set to true
2014    cx.update_editor(|e, _| e.selections.line_mode = true);
2015    cx.set_state(indoc! {"
2016        The ˇquick ˇbrown
2017        fox «ˇjum»ps over
2018        the lazy dog
2019        ˇThe qu«ick bˇ»rown"});
2020    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2021    cx.assert_editor_state("ˇthe lazy dogˇ");
2022}
2023
2024#[gpui::test]
2025fn test_delete_line(cx: &mut gpui::MutableAppContext) {
2026    cx.set_global(Settings::test(cx));
2027    let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2028    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2029    view.update(cx, |view, cx| {
2030        view.change_selections(None, cx, |s| {
2031            s.select_display_ranges([
2032                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2033                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
2034                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2035            ])
2036        });
2037        view.delete_line(&DeleteLine, cx);
2038        assert_eq!(view.display_text(cx), "ghi");
2039        assert_eq!(
2040            view.selections.display_ranges(cx),
2041            vec![
2042                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
2043                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
2044            ]
2045        );
2046    });
2047
2048    cx.set_global(Settings::test(cx));
2049    let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2050    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2051    view.update(cx, |view, cx| {
2052        view.change_selections(None, cx, |s| {
2053            s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
2054        });
2055        view.delete_line(&DeleteLine, cx);
2056        assert_eq!(view.display_text(cx), "ghi\n");
2057        assert_eq!(
2058            view.selections.display_ranges(cx),
2059            vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
2060        );
2061    });
2062}
2063
2064#[gpui::test]
2065fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
2066    cx.set_global(Settings::test(cx));
2067    let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2068    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2069    view.update(cx, |view, cx| {
2070        view.change_selections(None, cx, |s| {
2071            s.select_display_ranges([
2072                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2073                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2074                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2075                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2076            ])
2077        });
2078        view.duplicate_line(&DuplicateLine, cx);
2079        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
2080        assert_eq!(
2081            view.selections.display_ranges(cx),
2082            vec![
2083                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
2084                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
2085                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2086                DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
2087            ]
2088        );
2089    });
2090
2091    let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2092    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2093    view.update(cx, |view, cx| {
2094        view.change_selections(None, cx, |s| {
2095            s.select_display_ranges([
2096                DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
2097                DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
2098            ])
2099        });
2100        view.duplicate_line(&DuplicateLine, cx);
2101        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
2102        assert_eq!(
2103            view.selections.display_ranges(cx),
2104            vec![
2105                DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
2106                DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
2107            ]
2108        );
2109    });
2110}
2111
2112#[gpui::test]
2113fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
2114    cx.set_global(Settings::test(cx));
2115    let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
2116    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2117    view.update(cx, |view, cx| {
2118        view.fold_ranges(
2119            vec![
2120                Point::new(0, 2)..Point::new(1, 2),
2121                Point::new(2, 3)..Point::new(4, 1),
2122                Point::new(7, 0)..Point::new(8, 4),
2123            ],
2124            true,
2125            cx,
2126        );
2127        view.change_selections(None, cx, |s| {
2128            s.select_display_ranges([
2129                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2130                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2131                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2132                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
2133            ])
2134        });
2135        assert_eq!(
2136            view.display_text(cx),
2137            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
2138        );
2139
2140        view.move_line_up(&MoveLineUp, cx);
2141        assert_eq!(
2142            view.display_text(cx),
2143            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
2144        );
2145        assert_eq!(
2146            view.selections.display_ranges(cx),
2147            vec![
2148                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2149                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2150                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
2151                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
2152            ]
2153        );
2154    });
2155
2156    view.update(cx, |view, cx| {
2157        view.move_line_down(&MoveLineDown, cx);
2158        assert_eq!(
2159            view.display_text(cx),
2160            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
2161        );
2162        assert_eq!(
2163            view.selections.display_ranges(cx),
2164            vec![
2165                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
2166                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2167                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2168                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
2169            ]
2170        );
2171    });
2172
2173    view.update(cx, |view, cx| {
2174        view.move_line_down(&MoveLineDown, cx);
2175        assert_eq!(
2176            view.display_text(cx),
2177            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
2178        );
2179        assert_eq!(
2180            view.selections.display_ranges(cx),
2181            vec![
2182                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2183                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2184                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2185                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
2186            ]
2187        );
2188    });
2189
2190    view.update(cx, |view, cx| {
2191        view.move_line_up(&MoveLineUp, cx);
2192        assert_eq!(
2193            view.display_text(cx),
2194            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
2195        );
2196        assert_eq!(
2197            view.selections.display_ranges(cx),
2198            vec![
2199                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
2200                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2201                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
2202                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
2203            ]
2204        );
2205    });
2206}
2207
2208#[gpui::test]
2209fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) {
2210    cx.set_global(Settings::test(cx));
2211    let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
2212    let snapshot = buffer.read(cx).snapshot(cx);
2213    let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2214    editor.update(cx, |editor, cx| {
2215        editor.insert_blocks(
2216            [BlockProperties {
2217                style: BlockStyle::Fixed,
2218                position: snapshot.anchor_after(Point::new(2, 0)),
2219                disposition: BlockDisposition::Below,
2220                height: 1,
2221                render: Arc::new(|_| Empty::new().boxed()),
2222            }],
2223            cx,
2224        );
2225        editor.change_selections(None, cx, |s| {
2226            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
2227        });
2228        editor.move_line_down(&MoveLineDown, cx);
2229    });
2230}
2231
2232#[gpui::test]
2233fn test_transpose(cx: &mut gpui::MutableAppContext) {
2234    cx.set_global(Settings::test(cx));
2235
2236    _ = cx
2237        .add_window(Default::default(), |cx| {
2238            let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
2239
2240            editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
2241            editor.transpose(&Default::default(), cx);
2242            assert_eq!(editor.text(cx), "bac");
2243            assert_eq!(editor.selections.ranges(cx), [2..2]);
2244
2245            editor.transpose(&Default::default(), cx);
2246            assert_eq!(editor.text(cx), "bca");
2247            assert_eq!(editor.selections.ranges(cx), [3..3]);
2248
2249            editor.transpose(&Default::default(), cx);
2250            assert_eq!(editor.text(cx), "bac");
2251            assert_eq!(editor.selections.ranges(cx), [3..3]);
2252
2253            editor
2254        })
2255        .1;
2256
2257    _ = cx
2258        .add_window(Default::default(), |cx| {
2259            let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
2260
2261            editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
2262            editor.transpose(&Default::default(), cx);
2263            assert_eq!(editor.text(cx), "acb\nde");
2264            assert_eq!(editor.selections.ranges(cx), [3..3]);
2265
2266            editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
2267            editor.transpose(&Default::default(), cx);
2268            assert_eq!(editor.text(cx), "acbd\ne");
2269            assert_eq!(editor.selections.ranges(cx), [5..5]);
2270
2271            editor.transpose(&Default::default(), cx);
2272            assert_eq!(editor.text(cx), "acbde\n");
2273            assert_eq!(editor.selections.ranges(cx), [6..6]);
2274
2275            editor.transpose(&Default::default(), cx);
2276            assert_eq!(editor.text(cx), "acbd\ne");
2277            assert_eq!(editor.selections.ranges(cx), [6..6]);
2278
2279            editor
2280        })
2281        .1;
2282
2283    _ = cx
2284        .add_window(Default::default(), |cx| {
2285            let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
2286
2287            editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
2288            editor.transpose(&Default::default(), cx);
2289            assert_eq!(editor.text(cx), "bacd\ne");
2290            assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
2291
2292            editor.transpose(&Default::default(), cx);
2293            assert_eq!(editor.text(cx), "bcade\n");
2294            assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
2295
2296            editor.transpose(&Default::default(), cx);
2297            assert_eq!(editor.text(cx), "bcda\ne");
2298            assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
2299
2300            editor.transpose(&Default::default(), cx);
2301            assert_eq!(editor.text(cx), "bcade\n");
2302            assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
2303
2304            editor.transpose(&Default::default(), cx);
2305            assert_eq!(editor.text(cx), "bcaed\n");
2306            assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
2307
2308            editor
2309        })
2310        .1;
2311
2312    _ = cx
2313        .add_window(Default::default(), |cx| {
2314            let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
2315
2316            editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
2317            editor.transpose(&Default::default(), cx);
2318            assert_eq!(editor.text(cx), "🏀🍐✋");
2319            assert_eq!(editor.selections.ranges(cx), [8..8]);
2320
2321            editor.transpose(&Default::default(), cx);
2322            assert_eq!(editor.text(cx), "🏀✋🍐");
2323            assert_eq!(editor.selections.ranges(cx), [11..11]);
2324
2325            editor.transpose(&Default::default(), cx);
2326            assert_eq!(editor.text(cx), "🏀🍐✋");
2327            assert_eq!(editor.selections.ranges(cx), [11..11]);
2328
2329            editor
2330        })
2331        .1;
2332}
2333
2334#[gpui::test]
2335async fn test_clipboard(cx: &mut gpui::TestAppContext) {
2336    let mut cx = EditorTestContext::new(cx);
2337
2338    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
2339    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2340    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
2341
2342    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
2343    cx.set_state("two ˇfour ˇsix ˇ");
2344    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2345    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
2346
2347    // Paste again but with only two cursors. Since the number of cursors doesn't
2348    // match the number of slices in the clipboard, the entire clipboard text
2349    // is pasted at each cursor.
2350    cx.set_state("ˇtwo one✅ four three six five ˇ");
2351    cx.update_editor(|e, cx| {
2352        e.handle_input("( ", cx);
2353        e.paste(&Paste, cx);
2354        e.handle_input(") ", cx);
2355    });
2356    cx.assert_editor_state(indoc! {"
2357        ( one✅ 
2358        three 
2359        five ) ˇtwo one✅ four three six five ( one✅ 
2360        three 
2361        five ) ˇ"});
2362
2363    // Cut with three selections, one of which is full-line.
2364    cx.set_state(indoc! {"
2365        1«2ˇ»3
2366        4ˇ567
2367        «8ˇ»9"});
2368    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2369    cx.assert_editor_state(indoc! {"
2370        1ˇ3
2371        ˇ9"});
2372
2373    // Paste with three selections, noticing how the copied selection that was full-line
2374    // gets inserted before the second cursor.
2375    cx.set_state(indoc! {"
2376        1ˇ3
23772378        «oˇ»ne"});
2379    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2380    cx.assert_editor_state(indoc! {"
2381        12ˇ3
2382        4567
23832384        8ˇne"});
2385
2386    // Copy with a single cursor only, which writes the whole line into the clipboard.
2387    cx.set_state(indoc! {"
2388        The quick brown
2389        fox juˇmps over
2390        the lazy dog"});
2391    cx.update_editor(|e, cx| e.copy(&Copy, cx));
2392    cx.cx.assert_clipboard_content(Some("fox jumps over\n"));
2393
2394    // Paste with three selections, noticing how the copied full-line selection is inserted
2395    // before the empty selections but replaces the selection that is non-empty.
2396    cx.set_state(indoc! {"
2397        Tˇhe quick brown
2398        «foˇ»x jumps over
2399        tˇhe lazy dog"});
2400    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2401    cx.assert_editor_state(indoc! {"
2402        fox jumps over
2403        Tˇhe quick brown
2404        fox jumps over
2405        ˇx jumps over
2406        fox jumps over
2407        tˇhe lazy dog"});
2408}
2409
2410#[gpui::test]
2411async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
2412    let mut cx = EditorTestContext::new(cx);
2413    let language = Arc::new(Language::new(
2414        LanguageConfig::default(),
2415        Some(tree_sitter_rust::language()),
2416    ));
2417    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
2418
2419    // Cut an indented block, without the leading whitespace.
2420    cx.set_state(indoc! {"
2421        const a: B = (
2422            c(),
2423            «d(
2424                e,
2425                f
2426            )ˇ»
2427        );
2428    "});
2429    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2430    cx.assert_editor_state(indoc! {"
2431        const a: B = (
2432            c(),
2433            ˇ
2434        );
2435    "});
2436
2437    // Paste it at the same position.
2438    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2439    cx.assert_editor_state(indoc! {"
2440        const a: B = (
2441            c(),
2442            d(
2443                e,
2444                f
24452446        );
2447    "});
2448
2449    // Paste it at a line with a lower indent level.
2450    cx.set_state(indoc! {"
2451        ˇ
2452        const a: B = (
2453            c(),
2454        );
2455    "});
2456    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2457    cx.assert_editor_state(indoc! {"
2458        d(
2459            e,
2460            f
24612462        const a: B = (
2463            c(),
2464        );
2465    "});
2466
2467    // Cut an indented block, with the leading whitespace.
2468    cx.set_state(indoc! {"
2469        const a: B = (
2470            c(),
2471        «    d(
2472                e,
2473                f
2474            )
2475        ˇ»);
2476    "});
2477    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2478    cx.assert_editor_state(indoc! {"
2479        const a: B = (
2480            c(),
2481        ˇ);
2482    "});
2483
2484    // Paste it at the same position.
2485    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2486    cx.assert_editor_state(indoc! {"
2487        const a: B = (
2488            c(),
2489            d(
2490                e,
2491                f
2492            )
2493        ˇ);
2494    "});
2495
2496    // Paste it at a line with a higher indent level.
2497    cx.set_state(indoc! {"
2498        const a: B = (
2499            c(),
2500            d(
2501                e,
25022503            )
2504        );
2505    "});
2506    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2507    cx.assert_editor_state(indoc! {"
2508        const a: B = (
2509            c(),
2510            d(
2511                e,
2512                f    d(
2513                    e,
2514                    f
2515                )
2516        ˇ
2517            )
2518        );
2519    "});
2520}
2521
2522#[gpui::test]
2523fn test_select_all(cx: &mut gpui::MutableAppContext) {
2524    cx.set_global(Settings::test(cx));
2525    let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
2526    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2527    view.update(cx, |view, cx| {
2528        view.select_all(&SelectAll, cx);
2529        assert_eq!(
2530            view.selections.display_ranges(cx),
2531            &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
2532        );
2533    });
2534}
2535
2536#[gpui::test]
2537fn test_select_line(cx: &mut gpui::MutableAppContext) {
2538    cx.set_global(Settings::test(cx));
2539    let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
2540    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2541    view.update(cx, |view, cx| {
2542        view.change_selections(None, cx, |s| {
2543            s.select_display_ranges([
2544                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2545                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2546                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2547                DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
2548            ])
2549        });
2550        view.select_line(&SelectLine, cx);
2551        assert_eq!(
2552            view.selections.display_ranges(cx),
2553            vec![
2554                DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
2555                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
2556            ]
2557        );
2558    });
2559
2560    view.update(cx, |view, cx| {
2561        view.select_line(&SelectLine, cx);
2562        assert_eq!(
2563            view.selections.display_ranges(cx),
2564            vec![
2565                DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
2566                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
2567            ]
2568        );
2569    });
2570
2571    view.update(cx, |view, cx| {
2572        view.select_line(&SelectLine, cx);
2573        assert_eq!(
2574            view.selections.display_ranges(cx),
2575            vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
2576        );
2577    });
2578}
2579
2580#[gpui::test]
2581fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
2582    cx.set_global(Settings::test(cx));
2583    let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
2584    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2585    view.update(cx, |view, cx| {
2586        view.fold_ranges(
2587            vec![
2588                Point::new(0, 2)..Point::new(1, 2),
2589                Point::new(2, 3)..Point::new(4, 1),
2590                Point::new(7, 0)..Point::new(8, 4),
2591            ],
2592            true,
2593            cx,
2594        );
2595        view.change_selections(None, cx, |s| {
2596            s.select_display_ranges([
2597                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2598                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2599                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2600                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
2601            ])
2602        });
2603        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
2604    });
2605
2606    view.update(cx, |view, cx| {
2607        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
2608        assert_eq!(
2609            view.display_text(cx),
2610            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
2611        );
2612        assert_eq!(
2613            view.selections.display_ranges(cx),
2614            [
2615                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2616                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2617                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
2618                DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
2619            ]
2620        );
2621    });
2622
2623    view.update(cx, |view, cx| {
2624        view.change_selections(None, cx, |s| {
2625            s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
2626        });
2627        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
2628        assert_eq!(
2629            view.display_text(cx),
2630            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
2631        );
2632        assert_eq!(
2633            view.selections.display_ranges(cx),
2634            [
2635                DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
2636                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
2637                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
2638                DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
2639                DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
2640                DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
2641                DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
2642                DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
2643            ]
2644        );
2645    });
2646}
2647
2648#[gpui::test]
2649fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
2650    cx.set_global(Settings::test(cx));
2651    let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
2652    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2653
2654    view.update(cx, |view, cx| {
2655        view.change_selections(None, cx, |s| {
2656            s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
2657        });
2658    });
2659    view.update(cx, |view, cx| {
2660        view.add_selection_above(&AddSelectionAbove, cx);
2661        assert_eq!(
2662            view.selections.display_ranges(cx),
2663            vec![
2664                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
2665                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
2666            ]
2667        );
2668    });
2669
2670    view.update(cx, |view, cx| {
2671        view.add_selection_above(&AddSelectionAbove, cx);
2672        assert_eq!(
2673            view.selections.display_ranges(cx),
2674            vec![
2675                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
2676                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
2677            ]
2678        );
2679    });
2680
2681    view.update(cx, |view, cx| {
2682        view.add_selection_below(&AddSelectionBelow, cx);
2683        assert_eq!(
2684            view.selections.display_ranges(cx),
2685            vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
2686        );
2687
2688        view.undo_selection(&UndoSelection, cx);
2689        assert_eq!(
2690            view.selections.display_ranges(cx),
2691            vec![
2692                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
2693                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
2694            ]
2695        );
2696
2697        view.redo_selection(&RedoSelection, cx);
2698        assert_eq!(
2699            view.selections.display_ranges(cx),
2700            vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
2701        );
2702    });
2703
2704    view.update(cx, |view, cx| {
2705        view.add_selection_below(&AddSelectionBelow, cx);
2706        assert_eq!(
2707            view.selections.display_ranges(cx),
2708            vec![
2709                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
2710                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
2711            ]
2712        );
2713    });
2714
2715    view.update(cx, |view, cx| {
2716        view.add_selection_below(&AddSelectionBelow, cx);
2717        assert_eq!(
2718            view.selections.display_ranges(cx),
2719            vec![
2720                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
2721                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
2722            ]
2723        );
2724    });
2725
2726    view.update(cx, |view, cx| {
2727        view.change_selections(None, cx, |s| {
2728            s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
2729        });
2730    });
2731    view.update(cx, |view, cx| {
2732        view.add_selection_below(&AddSelectionBelow, cx);
2733        assert_eq!(
2734            view.selections.display_ranges(cx),
2735            vec![
2736                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
2737                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
2738            ]
2739        );
2740    });
2741
2742    view.update(cx, |view, cx| {
2743        view.add_selection_below(&AddSelectionBelow, cx);
2744        assert_eq!(
2745            view.selections.display_ranges(cx),
2746            vec![
2747                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
2748                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
2749            ]
2750        );
2751    });
2752
2753    view.update(cx, |view, cx| {
2754        view.add_selection_above(&AddSelectionAbove, cx);
2755        assert_eq!(
2756            view.selections.display_ranges(cx),
2757            vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
2758        );
2759    });
2760
2761    view.update(cx, |view, cx| {
2762        view.add_selection_above(&AddSelectionAbove, cx);
2763        assert_eq!(
2764            view.selections.display_ranges(cx),
2765            vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
2766        );
2767    });
2768
2769    view.update(cx, |view, cx| {
2770        view.change_selections(None, cx, |s| {
2771            s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
2772        });
2773        view.add_selection_below(&AddSelectionBelow, cx);
2774        assert_eq!(
2775            view.selections.display_ranges(cx),
2776            vec![
2777                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
2778                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
2779                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
2780            ]
2781        );
2782    });
2783
2784    view.update(cx, |view, cx| {
2785        view.add_selection_below(&AddSelectionBelow, cx);
2786        assert_eq!(
2787            view.selections.display_ranges(cx),
2788            vec![
2789                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
2790                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
2791                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
2792                DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
2793            ]
2794        );
2795    });
2796
2797    view.update(cx, |view, cx| {
2798        view.add_selection_above(&AddSelectionAbove, cx);
2799        assert_eq!(
2800            view.selections.display_ranges(cx),
2801            vec![
2802                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
2803                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
2804                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
2805            ]
2806        );
2807    });
2808
2809    view.update(cx, |view, cx| {
2810        view.change_selections(None, cx, |s| {
2811            s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
2812        });
2813    });
2814    view.update(cx, |view, cx| {
2815        view.add_selection_above(&AddSelectionAbove, cx);
2816        assert_eq!(
2817            view.selections.display_ranges(cx),
2818            vec![
2819                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
2820                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
2821                DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
2822                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
2823            ]
2824        );
2825    });
2826
2827    view.update(cx, |view, cx| {
2828        view.add_selection_below(&AddSelectionBelow, cx);
2829        assert_eq!(
2830            view.selections.display_ranges(cx),
2831            vec![
2832                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
2833                DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
2834                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
2835            ]
2836        );
2837    });
2838}
2839
2840#[gpui::test]
2841async fn test_select_next(cx: &mut gpui::TestAppContext) {
2842    let mut cx = EditorTestContext::new(cx);
2843    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
2844
2845    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
2846    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
2847
2848    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
2849    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
2850
2851    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
2852    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
2853
2854    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
2855    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
2856
2857    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
2858    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
2859
2860    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
2861    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
2862}
2863
2864#[gpui::test]
2865async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
2866    cx.update(|cx| cx.set_global(Settings::test(cx)));
2867    let language = Arc::new(Language::new(
2868        LanguageConfig::default(),
2869        Some(tree_sitter_rust::language()),
2870    ));
2871
2872    let text = r#"
2873        use mod1::mod2::{mod3, mod4};
2874
2875        fn fn_1(param1: bool, param2: &str) {
2876            let var1 = "text";
2877        }
2878    "#
2879    .unindent();
2880
2881    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
2882    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
2883    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
2884    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
2885        .await;
2886
2887    view.update(cx, |view, cx| {
2888        view.change_selections(None, cx, |s| {
2889            s.select_display_ranges([
2890                DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
2891                DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
2892                DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
2893            ]);
2894        });
2895        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
2896    });
2897    assert_eq!(
2898        view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
2899        &[
2900            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
2901            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
2902            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
2903        ]
2904    );
2905
2906    view.update(cx, |view, cx| {
2907        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
2908    });
2909    assert_eq!(
2910        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2911        &[
2912            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
2913            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
2914        ]
2915    );
2916
2917    view.update(cx, |view, cx| {
2918        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
2919    });
2920    assert_eq!(
2921        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2922        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
2923    );
2924
2925    // Trying to expand the selected syntax node one more time has no effect.
2926    view.update(cx, |view, cx| {
2927        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
2928    });
2929    assert_eq!(
2930        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2931        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
2932    );
2933
2934    view.update(cx, |view, cx| {
2935        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
2936    });
2937    assert_eq!(
2938        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2939        &[
2940            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
2941            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
2942        ]
2943    );
2944
2945    view.update(cx, |view, cx| {
2946        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
2947    });
2948    assert_eq!(
2949        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2950        &[
2951            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
2952            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
2953            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
2954        ]
2955    );
2956
2957    view.update(cx, |view, cx| {
2958        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
2959    });
2960    assert_eq!(
2961        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2962        &[
2963            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
2964            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
2965            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
2966        ]
2967    );
2968
2969    // Trying to shrink the selected syntax node one more time has no effect.
2970    view.update(cx, |view, cx| {
2971        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
2972    });
2973    assert_eq!(
2974        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2975        &[
2976            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
2977            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
2978            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
2979        ]
2980    );
2981
2982    // Ensure that we keep expanding the selection if the larger selection starts or ends within
2983    // a fold.
2984    view.update(cx, |view, cx| {
2985        view.fold_ranges(
2986            vec![
2987                Point::new(0, 21)..Point::new(0, 24),
2988                Point::new(3, 20)..Point::new(3, 22),
2989            ],
2990            true,
2991            cx,
2992        );
2993        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
2994    });
2995    assert_eq!(
2996        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2997        &[
2998            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
2999            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3000            DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
3001        ]
3002    );
3003}
3004
3005#[gpui::test]
3006async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
3007    cx.update(|cx| cx.set_global(Settings::test(cx)));
3008    let language = Arc::new(
3009        Language::new(
3010            LanguageConfig {
3011                brackets: BracketPairConfig {
3012                    pairs: vec![
3013                        BracketPair {
3014                            start: "{".to_string(),
3015                            end: "}".to_string(),
3016                            close: false,
3017                            newline: true,
3018                        },
3019                        BracketPair {
3020                            start: "(".to_string(),
3021                            end: ")".to_string(),
3022                            close: false,
3023                            newline: true,
3024                        },
3025                    ],
3026                    ..Default::default()
3027                },
3028                ..Default::default()
3029            },
3030            Some(tree_sitter_rust::language()),
3031        )
3032        .with_indents_query(
3033            r#"
3034                (_ "(" ")" @end) @indent
3035                (_ "{" "}" @end) @indent
3036            "#,
3037        )
3038        .unwrap(),
3039    );
3040
3041    let text = "fn a() {}";
3042
3043    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3044    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3045    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3046    editor
3047        .condition(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
3048        .await;
3049
3050    editor.update(cx, |editor, cx| {
3051        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
3052        editor.newline(&Newline, cx);
3053        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
3054        assert_eq!(
3055            editor.selections.ranges(cx),
3056            &[
3057                Point::new(1, 4)..Point::new(1, 4),
3058                Point::new(3, 4)..Point::new(3, 4),
3059                Point::new(5, 0)..Point::new(5, 0)
3060            ]
3061        );
3062    });
3063}
3064
3065#[gpui::test]
3066async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
3067    let mut cx = EditorTestContext::new(cx);
3068
3069    let language = Arc::new(Language::new(
3070        LanguageConfig {
3071            brackets: BracketPairConfig {
3072                pairs: vec![
3073                    BracketPair {
3074                        start: "{".to_string(),
3075                        end: "}".to_string(),
3076                        close: true,
3077                        newline: true,
3078                    },
3079                    BracketPair {
3080                        start: "(".to_string(),
3081                        end: ")".to_string(),
3082                        close: true,
3083                        newline: true,
3084                    },
3085                    BracketPair {
3086                        start: "/*".to_string(),
3087                        end: " */".to_string(),
3088                        close: true,
3089                        newline: true,
3090                    },
3091                    BracketPair {
3092                        start: "[".to_string(),
3093                        end: "]".to_string(),
3094                        close: false,
3095                        newline: true,
3096                    },
3097                    BracketPair {
3098                        start: "\"".to_string(),
3099                        end: "\"".to_string(),
3100                        close: true,
3101                        newline: false,
3102                    },
3103                ],
3104                ..Default::default()
3105            },
3106            autoclose_before: "})]".to_string(),
3107            ..Default::default()
3108        },
3109        Some(tree_sitter_rust::language()),
3110    ));
3111
3112    let registry = Arc::new(LanguageRegistry::test());
3113    registry.add(language.clone());
3114    cx.update_buffer(|buffer, cx| {
3115        buffer.set_language_registry(registry);
3116        buffer.set_language(Some(language), cx);
3117    });
3118
3119    cx.set_state(
3120        &r#"
3121            🏀ˇ
3122            εˇ
3123            ❤️ˇ
3124        "#
3125        .unindent(),
3126    );
3127
3128    // autoclose multiple nested brackets at multiple cursors
3129    cx.update_editor(|view, cx| {
3130        view.handle_input("{", cx);
3131        view.handle_input("{", cx);
3132        view.handle_input("{", cx);
3133    });
3134    cx.assert_editor_state(
3135        &"
3136            🏀{{{ˇ}}}
3137            ε{{{ˇ}}}
3138            ❤️{{{ˇ}}}
3139        "
3140        .unindent(),
3141    );
3142
3143    // insert a different closing bracket
3144    cx.update_editor(|view, cx| {
3145        view.handle_input(")", cx);
3146    });
3147    cx.assert_editor_state(
3148        &"
3149            🏀{{{)ˇ}}}
3150            ε{{{)ˇ}}}
3151            ❤️{{{)ˇ}}}
3152        "
3153        .unindent(),
3154    );
3155
3156    // skip over the auto-closed brackets when typing a closing bracket
3157    cx.update_editor(|view, cx| {
3158        view.move_right(&MoveRight, cx);
3159        view.handle_input("}", cx);
3160        view.handle_input("}", cx);
3161        view.handle_input("}", cx);
3162    });
3163    cx.assert_editor_state(
3164        &"
3165            🏀{{{)}}}}ˇ
3166            ε{{{)}}}}ˇ
3167            ❤️{{{)}}}}ˇ
3168        "
3169        .unindent(),
3170    );
3171
3172    // autoclose multi-character pairs
3173    cx.set_state(
3174        &"
3175            ˇ
3176            ˇ
3177        "
3178        .unindent(),
3179    );
3180    cx.update_editor(|view, cx| {
3181        view.handle_input("/", cx);
3182        view.handle_input("*", cx);
3183    });
3184    cx.assert_editor_state(
3185        &"
3186            /*ˇ */
3187            /*ˇ */
3188        "
3189        .unindent(),
3190    );
3191
3192    // one cursor autocloses a multi-character pair, one cursor
3193    // does not autoclose.
3194    cx.set_state(
3195        &"
31963197            ˇ
3198        "
3199        .unindent(),
3200    );
3201    cx.update_editor(|view, cx| view.handle_input("*", cx));
3202    cx.assert_editor_state(
3203        &"
3204            /*ˇ */
32053206        "
3207        .unindent(),
3208    );
3209
3210    // Don't autoclose if the next character isn't whitespace and isn't
3211    // listed in the language's "autoclose_before" section.
3212    cx.set_state("ˇa b");
3213    cx.update_editor(|view, cx| view.handle_input("{", cx));
3214    cx.assert_editor_state("{ˇa b");
3215
3216    // Don't autoclose if `close` is false for the bracket pair
3217    cx.set_state("ˇ");
3218    cx.update_editor(|view, cx| view.handle_input("[", cx));
3219    cx.assert_editor_state("");
3220
3221    // Surround with brackets if text is selected
3222    cx.set_state("«aˇ» b");
3223    cx.update_editor(|view, cx| view.handle_input("{", cx));
3224    cx.assert_editor_state("{«aˇ»} b");
3225
3226    // Autclose pair where the start and end characters are the same
3227    cx.set_state("");
3228    cx.update_editor(|view, cx| view.handle_input("\"", cx));
3229    cx.assert_editor_state("a\"ˇ\"");
3230    cx.update_editor(|view, cx| view.handle_input("\"", cx));
3231    cx.assert_editor_state("a\"\"ˇ");
3232}
3233
3234#[gpui::test]
3235async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
3236    let mut cx = EditorTestContext::new(cx);
3237
3238    let html_language = Arc::new(
3239        Language::new(
3240            LanguageConfig {
3241                name: "HTML".into(),
3242                brackets: BracketPairConfig {
3243                    pairs: vec![
3244                        BracketPair {
3245                            start: "<".into(),
3246                            end: ">".into(),
3247                            close: true,
3248                            ..Default::default()
3249                        },
3250                        BracketPair {
3251                            start: "{".into(),
3252                            end: "}".into(),
3253                            close: true,
3254                            ..Default::default()
3255                        },
3256                        BracketPair {
3257                            start: "(".into(),
3258                            end: ")".into(),
3259                            close: true,
3260                            ..Default::default()
3261                        },
3262                    ],
3263                    ..Default::default()
3264                },
3265                autoclose_before: "})]>".into(),
3266                ..Default::default()
3267            },
3268            Some(tree_sitter_html::language()),
3269        )
3270        .with_injection_query(
3271            r#"
3272            (script_element
3273                (raw_text) @content
3274                (#set! "language" "javascript"))
3275            "#,
3276        )
3277        .unwrap(),
3278    );
3279
3280    let javascript_language = Arc::new(Language::new(
3281        LanguageConfig {
3282            name: "JavaScript".into(),
3283            brackets: BracketPairConfig {
3284                pairs: vec![
3285                    BracketPair {
3286                        start: "/*".into(),
3287                        end: " */".into(),
3288                        close: true,
3289                        ..Default::default()
3290                    },
3291                    BracketPair {
3292                        start: "{".into(),
3293                        end: "}".into(),
3294                        close: true,
3295                        ..Default::default()
3296                    },
3297                    BracketPair {
3298                        start: "(".into(),
3299                        end: ")".into(),
3300                        close: true,
3301                        ..Default::default()
3302                    },
3303                ],
3304                ..Default::default()
3305            },
3306            autoclose_before: "})]>".into(),
3307            ..Default::default()
3308        },
3309        Some(tree_sitter_javascript::language()),
3310    ));
3311
3312    let registry = Arc::new(LanguageRegistry::test());
3313    registry.add(html_language.clone());
3314    registry.add(javascript_language.clone());
3315
3316    cx.update_buffer(|buffer, cx| {
3317        buffer.set_language_registry(registry);
3318        buffer.set_language(Some(html_language), cx);
3319    });
3320
3321    cx.set_state(
3322        &r#"
3323            <body>ˇ
3324                <script>
3325                    var x = 1;ˇ
3326                </script>
3327            </body>ˇ
3328        "#
3329        .unindent(),
3330    );
3331
3332    // Precondition: different languages are active at different locations.
3333    cx.update_editor(|editor, cx| {
3334        let snapshot = editor.snapshot(cx);
3335        let cursors = editor.selections.ranges::<usize>(cx);
3336        let languages = cursors
3337            .iter()
3338            .map(|c| snapshot.language_at(c.start).unwrap().name())
3339            .collect::<Vec<_>>();
3340        assert_eq!(
3341            languages,
3342            &["HTML".into(), "JavaScript".into(), "HTML".into()]
3343        );
3344    });
3345
3346    // Angle brackets autoclose in HTML, but not JavaScript.
3347    cx.update_editor(|editor, cx| {
3348        editor.handle_input("<", cx);
3349        editor.handle_input("a", cx);
3350    });
3351    cx.assert_editor_state(
3352        &r#"
3353            <body><aˇ>
3354                <script>
3355                    var x = 1;<aˇ
3356                </script>
3357            </body><aˇ>
3358        "#
3359        .unindent(),
3360    );
3361
3362    // Curly braces and parens autoclose in both HTML and JavaScript.
3363    cx.update_editor(|editor, cx| {
3364        editor.handle_input(" b=", cx);
3365        editor.handle_input("{", cx);
3366        editor.handle_input("c", cx);
3367        editor.handle_input("(", cx);
3368    });
3369    cx.assert_editor_state(
3370        &r#"
3371            <body><a b={c(ˇ)}>
3372                <script>
3373                    var x = 1;<a b={c(ˇ)}
3374                </script>
3375            </body><a b={c(ˇ)}>
3376        "#
3377        .unindent(),
3378    );
3379
3380    // Brackets that were already autoclosed are skipped.
3381    cx.update_editor(|editor, cx| {
3382        editor.handle_input(")", cx);
3383        editor.handle_input("d", cx);
3384        editor.handle_input("}", cx);
3385    });
3386    cx.assert_editor_state(
3387        &r#"
3388            <body><a b={c()d}ˇ>
3389                <script>
3390                    var x = 1;<a b={c()d}ˇ
3391                </script>
3392            </body><a b={c()d}ˇ>
3393        "#
3394        .unindent(),
3395    );
3396    cx.update_editor(|editor, cx| {
3397        editor.handle_input(">", cx);
3398    });
3399    cx.assert_editor_state(
3400        &r#"
3401            <body><a b={c()d}>ˇ
3402                <script>
3403                    var x = 1;<a b={c()d}>ˇ
3404                </script>
3405            </body><a b={c()d}>ˇ
3406        "#
3407        .unindent(),
3408    );
3409
3410    // Reset
3411    cx.set_state(
3412        &r#"
3413            <body>ˇ
3414                <script>
3415                    var x = 1;ˇ
3416                </script>
3417            </body>ˇ
3418        "#
3419        .unindent(),
3420    );
3421
3422    cx.update_editor(|editor, cx| {
3423        editor.handle_input("<", cx);
3424    });
3425    cx.assert_editor_state(
3426        &r#"
3427            <body><ˇ>
3428                <script>
3429                    var x = 1;<ˇ
3430                </script>
3431            </body><ˇ>
3432        "#
3433        .unindent(),
3434    );
3435
3436    // When backspacing, the closing angle brackets are removed.
3437    cx.update_editor(|editor, cx| {
3438        editor.backspace(&Backspace, cx);
3439    });
3440    cx.assert_editor_state(
3441        &r#"
3442            <body>ˇ
3443                <script>
3444                    var x = 1;ˇ
3445                </script>
3446            </body>ˇ
3447        "#
3448        .unindent(),
3449    );
3450
3451    // Block comments autoclose in JavaScript, but not HTML.
3452    cx.update_editor(|editor, cx| {
3453        editor.handle_input("/", cx);
3454        editor.handle_input("*", cx);
3455    });
3456    cx.assert_editor_state(
3457        &r#"
3458            <body>/*ˇ
3459                <script>
3460                    var x = 1;/*ˇ */
3461                </script>
3462            </body>/*ˇ
3463        "#
3464        .unindent(),
3465    );
3466}
3467
3468#[gpui::test]
3469async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
3470    let mut cx = EditorTestContext::new(cx);
3471
3472    let rust_language = Arc::new(
3473        Language::new(
3474            LanguageConfig {
3475                name: "Rust".into(),
3476                brackets: serde_json::from_value(json!([
3477                    { "start": "{", "end": "}", "close": true, "newline": true },
3478                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
3479                ]))
3480                .unwrap(),
3481                autoclose_before: "})]>".into(),
3482                ..Default::default()
3483            },
3484            Some(tree_sitter_rust::language()),
3485        )
3486        .with_override_query("(string_literal) @string")
3487        .unwrap(),
3488    );
3489
3490    let registry = Arc::new(LanguageRegistry::test());
3491    registry.add(rust_language.clone());
3492
3493    cx.update_buffer(|buffer, cx| {
3494        buffer.set_language_registry(registry);
3495        buffer.set_language(Some(rust_language), cx);
3496    });
3497
3498    cx.set_state(
3499        &r#"
3500            let x = ˇ
3501        "#
3502        .unindent(),
3503    );
3504
3505    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
3506    cx.update_editor(|editor, cx| {
3507        editor.handle_input("\"", cx);
3508    });
3509    cx.assert_editor_state(
3510        &r#"
3511            let x = "ˇ"
3512        "#
3513        .unindent(),
3514    );
3515
3516    // Inserting another quotation mark. The cursor moves across the existing
3517    // automatically-inserted quotation mark.
3518    cx.update_editor(|editor, cx| {
3519        editor.handle_input("\"", cx);
3520    });
3521    cx.assert_editor_state(
3522        &r#"
3523            let x = ""ˇ
3524        "#
3525        .unindent(),
3526    );
3527
3528    // Reset
3529    cx.set_state(
3530        &r#"
3531            let x = ˇ
3532        "#
3533        .unindent(),
3534    );
3535
3536    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
3537    cx.update_editor(|editor, cx| {
3538        editor.handle_input("\"", cx);
3539        editor.handle_input(" ", cx);
3540        editor.move_left(&Default::default(), cx);
3541        editor.handle_input("\\", cx);
3542        editor.handle_input("\"", cx);
3543    });
3544    cx.assert_editor_state(
3545        &r#"
3546            let x = "\"ˇ "
3547        "#
3548        .unindent(),
3549    );
3550
3551    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
3552    // mark. Nothing is inserted.
3553    cx.update_editor(|editor, cx| {
3554        editor.move_right(&Default::default(), cx);
3555        editor.handle_input("\"", cx);
3556    });
3557    cx.assert_editor_state(
3558        &r#"
3559            let x = "\" "ˇ
3560        "#
3561        .unindent(),
3562    );
3563}
3564
3565#[gpui::test]
3566async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
3567    cx.update(|cx| cx.set_global(Settings::test(cx)));
3568    let language = Arc::new(Language::new(
3569        LanguageConfig {
3570            brackets: BracketPairConfig {
3571                pairs: vec![
3572                    BracketPair {
3573                        start: "{".to_string(),
3574                        end: "}".to_string(),
3575                        close: true,
3576                        newline: true,
3577                    },
3578                    BracketPair {
3579                        start: "/* ".to_string(),
3580                        end: "*/".to_string(),
3581                        close: true,
3582                        ..Default::default()
3583                    },
3584                ],
3585                ..Default::default()
3586            },
3587            ..Default::default()
3588        },
3589        Some(tree_sitter_rust::language()),
3590    ));
3591
3592    let text = r#"
3593        a
3594        b
3595        c
3596    "#
3597    .unindent();
3598
3599    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3600    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3601    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
3602    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
3603        .await;
3604
3605    view.update(cx, |view, cx| {
3606        view.change_selections(None, cx, |s| {
3607            s.select_display_ranges([
3608                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3609                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
3610                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
3611            ])
3612        });
3613
3614        view.handle_input("{", cx);
3615        view.handle_input("{", cx);
3616        view.handle_input("{", cx);
3617        assert_eq!(
3618            view.text(cx),
3619            "
3620                {{{a}}}
3621                {{{b}}}
3622                {{{c}}}
3623            "
3624            .unindent()
3625        );
3626        assert_eq!(
3627            view.selections.display_ranges(cx),
3628            [
3629                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
3630                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
3631                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
3632            ]
3633        );
3634
3635        view.undo(&Undo, cx);
3636        view.undo(&Undo, cx);
3637        view.undo(&Undo, cx);
3638        assert_eq!(
3639            view.text(cx),
3640            "
3641                a
3642                b
3643                c
3644            "
3645            .unindent()
3646        );
3647        assert_eq!(
3648            view.selections.display_ranges(cx),
3649            [
3650                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3651                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
3652                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
3653            ]
3654        );
3655
3656        // Ensure inserting the first character of a multi-byte bracket pair
3657        // doesn't surround the selections with the bracket.
3658        view.handle_input("/", cx);
3659        assert_eq!(
3660            view.text(cx),
3661            "
3662                /
3663                /
3664                /
3665            "
3666            .unindent()
3667        );
3668        assert_eq!(
3669            view.selections.display_ranges(cx),
3670            [
3671                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
3672                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
3673                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
3674            ]
3675        );
3676
3677        view.undo(&Undo, cx);
3678        assert_eq!(
3679            view.text(cx),
3680            "
3681                a
3682                b
3683                c
3684            "
3685            .unindent()
3686        );
3687        assert_eq!(
3688            view.selections.display_ranges(cx),
3689            [
3690                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3691                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
3692                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
3693            ]
3694        );
3695
3696        // Ensure inserting the last character of a multi-byte bracket pair
3697        // doesn't surround the selections with the bracket.
3698        view.handle_input("*", cx);
3699        assert_eq!(
3700            view.text(cx),
3701            "
3702                *
3703                *
3704                *
3705            "
3706            .unindent()
3707        );
3708        assert_eq!(
3709            view.selections.display_ranges(cx),
3710            [
3711                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
3712                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
3713                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
3714            ]
3715        );
3716    });
3717}
3718
3719#[gpui::test]
3720async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
3721    cx.update(|cx| cx.set_global(Settings::test(cx)));
3722    let language = Arc::new(Language::new(
3723        LanguageConfig {
3724            brackets: BracketPairConfig {
3725                pairs: vec![BracketPair {
3726                    start: "{".to_string(),
3727                    end: "}".to_string(),
3728                    close: true,
3729                    newline: true,
3730                }],
3731                ..Default::default()
3732            },
3733            autoclose_before: "}".to_string(),
3734            ..Default::default()
3735        },
3736        Some(tree_sitter_rust::language()),
3737    ));
3738
3739    let text = r#"
3740        a
3741        b
3742        c
3743    "#
3744    .unindent();
3745
3746    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3747    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3748    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3749    editor
3750        .condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
3751        .await;
3752
3753    editor.update(cx, |editor, cx| {
3754        editor.change_selections(None, cx, |s| {
3755            s.select_ranges([
3756                Point::new(0, 1)..Point::new(0, 1),
3757                Point::new(1, 1)..Point::new(1, 1),
3758                Point::new(2, 1)..Point::new(2, 1),
3759            ])
3760        });
3761
3762        editor.handle_input("{", cx);
3763        editor.handle_input("{", cx);
3764        editor.handle_input("_", cx);
3765        assert_eq!(
3766            editor.text(cx),
3767            "
3768                a{{_}}
3769                b{{_}}
3770                c{{_}}
3771            "
3772            .unindent()
3773        );
3774        assert_eq!(
3775            editor.selections.ranges::<Point>(cx),
3776            [
3777                Point::new(0, 4)..Point::new(0, 4),
3778                Point::new(1, 4)..Point::new(1, 4),
3779                Point::new(2, 4)..Point::new(2, 4)
3780            ]
3781        );
3782
3783        editor.backspace(&Default::default(), cx);
3784        editor.backspace(&Default::default(), cx);
3785        assert_eq!(
3786            editor.text(cx),
3787            "
3788                a{}
3789                b{}
3790                c{}
3791            "
3792            .unindent()
3793        );
3794        assert_eq!(
3795            editor.selections.ranges::<Point>(cx),
3796            [
3797                Point::new(0, 2)..Point::new(0, 2),
3798                Point::new(1, 2)..Point::new(1, 2),
3799                Point::new(2, 2)..Point::new(2, 2)
3800            ]
3801        );
3802
3803        editor.delete_to_previous_word_start(&Default::default(), cx);
3804        assert_eq!(
3805            editor.text(cx),
3806            "
3807                a
3808                b
3809                c
3810            "
3811            .unindent()
3812        );
3813        assert_eq!(
3814            editor.selections.ranges::<Point>(cx),
3815            [
3816                Point::new(0, 1)..Point::new(0, 1),
3817                Point::new(1, 1)..Point::new(1, 1),
3818                Point::new(2, 1)..Point::new(2, 1)
3819            ]
3820        );
3821    });
3822}
3823
3824#[gpui::test]
3825async fn test_snippets(cx: &mut gpui::TestAppContext) {
3826    cx.update(|cx| cx.set_global(Settings::test(cx)));
3827
3828    let (text, insertion_ranges) = marked_text_ranges(
3829        indoc! {"
3830            a.ˇ b
3831            a.ˇ b
3832            a.ˇ b
3833        "},
3834        false,
3835    );
3836
3837    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
3838    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3839
3840    editor.update(cx, |editor, cx| {
3841        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
3842
3843        editor
3844            .insert_snippet(&insertion_ranges, snippet, cx)
3845            .unwrap();
3846
3847        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
3848            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
3849            assert_eq!(editor.text(cx), expected_text);
3850            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
3851        }
3852
3853        assert(
3854            editor,
3855            cx,
3856            indoc! {"
3857                a.f(«one», two, «three») b
3858                a.f(«one», two, «three») b
3859                a.f(«one», two, «three») b
3860            "},
3861        );
3862
3863        // Can't move earlier than the first tab stop
3864        assert!(!editor.move_to_prev_snippet_tabstop(cx));
3865        assert(
3866            editor,
3867            cx,
3868            indoc! {"
3869                a.f(«one», two, «three») b
3870                a.f(«one», two, «three») b
3871                a.f(«one», two, «three») b
3872            "},
3873        );
3874
3875        assert!(editor.move_to_next_snippet_tabstop(cx));
3876        assert(
3877            editor,
3878            cx,
3879            indoc! {"
3880                a.f(one, «two», three) b
3881                a.f(one, «two», three) b
3882                a.f(one, «two», three) b
3883            "},
3884        );
3885
3886        editor.move_to_prev_snippet_tabstop(cx);
3887        assert(
3888            editor,
3889            cx,
3890            indoc! {"
3891                a.f(«one», two, «three») b
3892                a.f(«one», two, «three») b
3893                a.f(«one», two, «three») b
3894            "},
3895        );
3896
3897        assert!(editor.move_to_next_snippet_tabstop(cx));
3898        assert(
3899            editor,
3900            cx,
3901            indoc! {"
3902                a.f(one, «two», three) b
3903                a.f(one, «two», three) b
3904                a.f(one, «two», three) b
3905            "},
3906        );
3907        assert!(editor.move_to_next_snippet_tabstop(cx));
3908        assert(
3909            editor,
3910            cx,
3911            indoc! {"
3912                a.f(one, two, three)ˇ b
3913                a.f(one, two, three)ˇ b
3914                a.f(one, two, three)ˇ b
3915            "},
3916        );
3917
3918        // As soon as the last tab stop is reached, snippet state is gone
3919        editor.move_to_prev_snippet_tabstop(cx);
3920        assert(
3921            editor,
3922            cx,
3923            indoc! {"
3924                a.f(one, two, three)ˇ b
3925                a.f(one, two, three)ˇ b
3926                a.f(one, two, three)ˇ b
3927            "},
3928        );
3929    });
3930}
3931
3932#[gpui::test]
3933async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
3934    cx.foreground().forbid_parking();
3935
3936    let mut language = Language::new(
3937        LanguageConfig {
3938            name: "Rust".into(),
3939            path_suffixes: vec!["rs".to_string()],
3940            ..Default::default()
3941        },
3942        Some(tree_sitter_rust::language()),
3943    );
3944    let mut fake_servers = language
3945        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3946            capabilities: lsp::ServerCapabilities {
3947                document_formatting_provider: Some(lsp::OneOf::Left(true)),
3948                ..Default::default()
3949            },
3950            ..Default::default()
3951        }))
3952        .await;
3953
3954    let fs = FakeFs::new(cx.background());
3955    fs.insert_file("/file.rs", Default::default()).await;
3956
3957    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
3958    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
3959    let buffer = project
3960        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
3961        .await
3962        .unwrap();
3963
3964    cx.foreground().start_waiting();
3965    let fake_server = fake_servers.next().await.unwrap();
3966
3967    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3968    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3969    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
3970    assert!(cx.read(|cx| editor.is_dirty(cx)));
3971
3972    let save = cx.update(|cx| editor.save(project.clone(), cx));
3973    fake_server
3974        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
3975            assert_eq!(
3976                params.text_document.uri,
3977                lsp::Url::from_file_path("/file.rs").unwrap()
3978            );
3979            assert_eq!(params.options.tab_size, 4);
3980            Ok(Some(vec![lsp::TextEdit::new(
3981                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
3982                ", ".to_string(),
3983            )]))
3984        })
3985        .next()
3986        .await;
3987    cx.foreground().start_waiting();
3988    save.await.unwrap();
3989    assert_eq!(
3990        editor.read_with(cx, |editor, cx| editor.text(cx)),
3991        "one, two\nthree\n"
3992    );
3993    assert!(!cx.read(|cx| editor.is_dirty(cx)));
3994
3995    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
3996    assert!(cx.read(|cx| editor.is_dirty(cx)));
3997
3998    // Ensure we can still save even if formatting hangs.
3999    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4000        assert_eq!(
4001            params.text_document.uri,
4002            lsp::Url::from_file_path("/file.rs").unwrap()
4003        );
4004        futures::future::pending::<()>().await;
4005        unreachable!()
4006    });
4007    let save = cx.update(|cx| editor.save(project.clone(), cx));
4008    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4009    cx.foreground().start_waiting();
4010    save.await.unwrap();
4011    assert_eq!(
4012        editor.read_with(cx, |editor, cx| editor.text(cx)),
4013        "one\ntwo\nthree\n"
4014    );
4015    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4016
4017    // Set rust language override and assert overriden tabsize is sent to language server
4018    cx.update(|cx| {
4019        cx.update_global::<Settings, _, _>(|settings, _| {
4020            settings.language_overrides.insert(
4021                "Rust".into(),
4022                EditorSettings {
4023                    tab_size: Some(8.try_into().unwrap()),
4024                    ..Default::default()
4025                },
4026            );
4027        })
4028    });
4029
4030    let save = cx.update(|cx| editor.save(project.clone(), cx));
4031    fake_server
4032        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4033            assert_eq!(
4034                params.text_document.uri,
4035                lsp::Url::from_file_path("/file.rs").unwrap()
4036            );
4037            assert_eq!(params.options.tab_size, 8);
4038            Ok(Some(vec![]))
4039        })
4040        .next()
4041        .await;
4042    cx.foreground().start_waiting();
4043    save.await.unwrap();
4044}
4045
4046#[gpui::test]
4047async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
4048    cx.foreground().forbid_parking();
4049
4050    let mut language = Language::new(
4051        LanguageConfig {
4052            name: "Rust".into(),
4053            path_suffixes: vec!["rs".to_string()],
4054            ..Default::default()
4055        },
4056        Some(tree_sitter_rust::language()),
4057    );
4058    let mut fake_servers = language
4059        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4060            capabilities: lsp::ServerCapabilities {
4061                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
4062                ..Default::default()
4063            },
4064            ..Default::default()
4065        }))
4066        .await;
4067
4068    let fs = FakeFs::new(cx.background());
4069    fs.insert_file("/file.rs", Default::default()).await;
4070
4071    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4072    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4073    let buffer = project
4074        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4075        .await
4076        .unwrap();
4077
4078    cx.foreground().start_waiting();
4079    let fake_server = fake_servers.next().await.unwrap();
4080
4081    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4082    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4083    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4084    assert!(cx.read(|cx| editor.is_dirty(cx)));
4085
4086    let save = cx.update(|cx| editor.save(project.clone(), cx));
4087    fake_server
4088        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
4089            assert_eq!(
4090                params.text_document.uri,
4091                lsp::Url::from_file_path("/file.rs").unwrap()
4092            );
4093            assert_eq!(params.options.tab_size, 4);
4094            Ok(Some(vec![lsp::TextEdit::new(
4095                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4096                ", ".to_string(),
4097            )]))
4098        })
4099        .next()
4100        .await;
4101    cx.foreground().start_waiting();
4102    save.await.unwrap();
4103    assert_eq!(
4104        editor.read_with(cx, |editor, cx| editor.text(cx)),
4105        "one, two\nthree\n"
4106    );
4107    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4108
4109    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4110    assert!(cx.read(|cx| editor.is_dirty(cx)));
4111
4112    // Ensure we can still save even if formatting hangs.
4113    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
4114        move |params, _| async move {
4115            assert_eq!(
4116                params.text_document.uri,
4117                lsp::Url::from_file_path("/file.rs").unwrap()
4118            );
4119            futures::future::pending::<()>().await;
4120            unreachable!()
4121        },
4122    );
4123    let save = cx.update(|cx| editor.save(project.clone(), cx));
4124    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4125    cx.foreground().start_waiting();
4126    save.await.unwrap();
4127    assert_eq!(
4128        editor.read_with(cx, |editor, cx| editor.text(cx)),
4129        "one\ntwo\nthree\n"
4130    );
4131    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4132
4133    // Set rust language override and assert overriden tabsize is sent to language server
4134    cx.update(|cx| {
4135        cx.update_global::<Settings, _, _>(|settings, _| {
4136            settings.language_overrides.insert(
4137                "Rust".into(),
4138                EditorSettings {
4139                    tab_size: Some(8.try_into().unwrap()),
4140                    ..Default::default()
4141                },
4142            );
4143        })
4144    });
4145
4146    let save = cx.update(|cx| editor.save(project.clone(), cx));
4147    fake_server
4148        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
4149            assert_eq!(
4150                params.text_document.uri,
4151                lsp::Url::from_file_path("/file.rs").unwrap()
4152            );
4153            assert_eq!(params.options.tab_size, 8);
4154            Ok(Some(vec![]))
4155        })
4156        .next()
4157        .await;
4158    cx.foreground().start_waiting();
4159    save.await.unwrap();
4160}
4161
4162#[gpui::test]
4163async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
4164    cx.foreground().forbid_parking();
4165
4166    let mut language = Language::new(
4167        LanguageConfig {
4168            name: "Rust".into(),
4169            path_suffixes: vec!["rs".to_string()],
4170            ..Default::default()
4171        },
4172        Some(tree_sitter_rust::language()),
4173    );
4174    let mut fake_servers = language
4175        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4176            capabilities: lsp::ServerCapabilities {
4177                document_formatting_provider: Some(lsp::OneOf::Left(true)),
4178                ..Default::default()
4179            },
4180            ..Default::default()
4181        }))
4182        .await;
4183
4184    let fs = FakeFs::new(cx.background());
4185    fs.insert_file("/file.rs", Default::default()).await;
4186
4187    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4188    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4189    let buffer = project
4190        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4191        .await
4192        .unwrap();
4193
4194    cx.foreground().start_waiting();
4195    let fake_server = fake_servers.next().await.unwrap();
4196
4197    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4198    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4199    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4200
4201    let format = editor.update(cx, |editor, cx| {
4202        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
4203    });
4204    fake_server
4205        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4206            assert_eq!(
4207                params.text_document.uri,
4208                lsp::Url::from_file_path("/file.rs").unwrap()
4209            );
4210            assert_eq!(params.options.tab_size, 4);
4211            Ok(Some(vec![lsp::TextEdit::new(
4212                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4213                ", ".to_string(),
4214            )]))
4215        })
4216        .next()
4217        .await;
4218    cx.foreground().start_waiting();
4219    format.await.unwrap();
4220    assert_eq!(
4221        editor.read_with(cx, |editor, cx| editor.text(cx)),
4222        "one, two\nthree\n"
4223    );
4224
4225    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4226    // Ensure we don't lock if formatting hangs.
4227    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4228        assert_eq!(
4229            params.text_document.uri,
4230            lsp::Url::from_file_path("/file.rs").unwrap()
4231        );
4232        futures::future::pending::<()>().await;
4233        unreachable!()
4234    });
4235    let format = editor.update(cx, |editor, cx| {
4236        editor.perform_format(project, FormatTrigger::Manual, cx)
4237    });
4238    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4239    cx.foreground().start_waiting();
4240    format.await.unwrap();
4241    assert_eq!(
4242        editor.read_with(cx, |editor, cx| editor.text(cx)),
4243        "one\ntwo\nthree\n"
4244    );
4245}
4246
4247#[gpui::test]
4248async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
4249    cx.foreground().forbid_parking();
4250
4251    let mut cx = EditorLspTestContext::new_rust(
4252        lsp::ServerCapabilities {
4253            document_formatting_provider: Some(lsp::OneOf::Left(true)),
4254            ..Default::default()
4255        },
4256        cx,
4257    )
4258    .await;
4259
4260    cx.set_state(indoc! {"
4261        one.twoˇ
4262    "});
4263
4264    // The format request takes a long time. When it completes, it inserts
4265    // a newline and an indent before the `.`
4266    cx.lsp
4267        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
4268            let executor = cx.background();
4269            async move {
4270                executor.timer(Duration::from_millis(100)).await;
4271                Ok(Some(vec![lsp::TextEdit {
4272                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
4273                    new_text: "\n    ".into(),
4274                }]))
4275            }
4276        });
4277
4278    // Submit a format request.
4279    let format_1 = cx
4280        .update_editor(|editor, cx| editor.format(&Format, cx))
4281        .unwrap();
4282    cx.foreground().run_until_parked();
4283
4284    // Submit a second format request.
4285    let format_2 = cx
4286        .update_editor(|editor, cx| editor.format(&Format, cx))
4287        .unwrap();
4288    cx.foreground().run_until_parked();
4289
4290    // Wait for both format requests to complete
4291    cx.foreground().advance_clock(Duration::from_millis(200));
4292    cx.foreground().start_waiting();
4293    format_1.await.unwrap();
4294    cx.foreground().start_waiting();
4295    format_2.await.unwrap();
4296
4297    // The formatting edits only happens once.
4298    cx.assert_editor_state(indoc! {"
4299        one
4300            .twoˇ
4301    "});
4302}
4303
4304#[gpui::test]
4305async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
4306    cx.foreground().forbid_parking();
4307
4308    let mut cx = EditorLspTestContext::new_rust(
4309        lsp::ServerCapabilities {
4310            document_formatting_provider: Some(lsp::OneOf::Left(true)),
4311            ..Default::default()
4312        },
4313        cx,
4314    )
4315    .await;
4316
4317    // Set up a buffer white some trailing whitespace and no trailing newline.
4318    cx.set_state(
4319        &[
4320            "one ",   //
4321            "twoˇ",  //
4322            "three ", //
4323            "four",   //
4324        ]
4325        .join("\n"),
4326    );
4327
4328    // Submit a format request.
4329    let format = cx
4330        .update_editor(|editor, cx| editor.format(&Format, cx))
4331        .unwrap();
4332
4333    // Record which buffer changes have been sent to the language server
4334    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
4335    cx.lsp
4336        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
4337            let buffer_changes = buffer_changes.clone();
4338            move |params, _| {
4339                buffer_changes.lock().extend(
4340                    params
4341                        .content_changes
4342                        .into_iter()
4343                        .map(|e| (e.range.unwrap(), e.text)),
4344                );
4345            }
4346        });
4347
4348    // Handle formatting requests to the language server.
4349    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
4350        let buffer_changes = buffer_changes.clone();
4351        move |_, _| {
4352            // When formatting is requested, trailing whitespace has already been stripped,
4353            // and the trailing newline has already been added.
4354            assert_eq!(
4355                &buffer_changes.lock()[1..],
4356                &[
4357                    (
4358                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
4359                        "".into()
4360                    ),
4361                    (
4362                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
4363                        "".into()
4364                    ),
4365                    (
4366                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
4367                        "\n".into()
4368                    ),
4369                ]
4370            );
4371
4372            // Insert blank lines between each line of the buffer.
4373            async move {
4374                Ok(Some(vec![
4375                    lsp::TextEdit {
4376                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
4377                        new_text: "\n".into(),
4378                    },
4379                    lsp::TextEdit {
4380                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
4381                        new_text: "\n".into(),
4382                    },
4383                ]))
4384            }
4385        }
4386    });
4387
4388    // After formatting the buffer, the trailing whitespace is stripped,
4389    // a newline is appended, and the edits provided by the language server
4390    // have been applied.
4391    format.await.unwrap();
4392    cx.assert_editor_state(
4393        &[
4394            "one",   //
4395            "",      //
4396            "twoˇ", //
4397            "",      //
4398            "three", //
4399            "four",  //
4400            "",      //
4401        ]
4402        .join("\n"),
4403    );
4404
4405    // Undoing the formatting undoes the trailing whitespace removal, the
4406    // trailing newline, and the LSP edits.
4407    cx.update_buffer(|buffer, cx| buffer.undo(cx));
4408    cx.assert_editor_state(
4409        &[
4410            "one ",   //
4411            "twoˇ",  //
4412            "three ", //
4413            "four",   //
4414        ]
4415        .join("\n"),
4416    );
4417}
4418
4419#[gpui::test]
4420async fn test_completion(cx: &mut gpui::TestAppContext) {
4421    let mut cx = EditorLspTestContext::new_rust(
4422        lsp::ServerCapabilities {
4423            completion_provider: Some(lsp::CompletionOptions {
4424                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
4425                ..Default::default()
4426            }),
4427            ..Default::default()
4428        },
4429        cx,
4430    )
4431    .await;
4432
4433    cx.set_state(indoc! {"
4434        oneˇ
4435        two
4436        three
4437    "});
4438    cx.simulate_keystroke(".");
4439    handle_completion_request(
4440        &mut cx,
4441        indoc! {"
4442            one.|<>
4443            two
4444            three
4445        "},
4446        vec!["first_completion", "second_completion"],
4447    )
4448    .await;
4449    cx.condition(|editor, _| editor.context_menu_visible())
4450        .await;
4451    let apply_additional_edits = cx.update_editor(|editor, cx| {
4452        editor.move_down(&MoveDown, cx);
4453        editor
4454            .confirm_completion(&ConfirmCompletion::default(), cx)
4455            .unwrap()
4456    });
4457    cx.assert_editor_state(indoc! {"
4458        one.second_completionˇ
4459        two
4460        three
4461    "});
4462
4463    handle_resolve_completion_request(
4464        &mut cx,
4465        Some(vec![
4466            (
4467                //This overlaps with the primary completion edit which is
4468                //misbehavior from the LSP spec, test that we filter it out
4469                indoc! {"
4470                    one.second_ˇcompletion
4471                    two
4472                    threeˇ
4473                "},
4474                "overlapping aditional edit",
4475            ),
4476            (
4477                indoc! {"
4478                    one.second_completion
4479                    two
4480                    threeˇ
4481                "},
4482                "\nadditional edit",
4483            ),
4484        ]),
4485    )
4486    .await;
4487    apply_additional_edits.await.unwrap();
4488    cx.assert_editor_state(indoc! {"
4489        one.second_completionˇ
4490        two
4491        three
4492        additional edit
4493    "});
4494
4495    cx.set_state(indoc! {"
4496        one.second_completion
4497        twoˇ
4498        threeˇ
4499        additional edit
4500    "});
4501    cx.simulate_keystroke(" ");
4502    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4503    cx.simulate_keystroke("s");
4504    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4505
4506    cx.assert_editor_state(indoc! {"
4507        one.second_completion
4508        two sˇ
4509        three sˇ
4510        additional edit
4511    "});
4512    handle_completion_request(
4513        &mut cx,
4514        indoc! {"
4515            one.second_completion
4516            two s
4517            three <s|>
4518            additional edit
4519        "},
4520        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
4521    )
4522    .await;
4523    cx.condition(|editor, _| editor.context_menu_visible())
4524        .await;
4525
4526    cx.simulate_keystroke("i");
4527
4528    handle_completion_request(
4529        &mut cx,
4530        indoc! {"
4531            one.second_completion
4532            two si
4533            three <si|>
4534            additional edit
4535        "},
4536        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
4537    )
4538    .await;
4539    cx.condition(|editor, _| editor.context_menu_visible())
4540        .await;
4541
4542    let apply_additional_edits = cx.update_editor(|editor, cx| {
4543        editor
4544            .confirm_completion(&ConfirmCompletion::default(), cx)
4545            .unwrap()
4546    });
4547    cx.assert_editor_state(indoc! {"
4548        one.second_completion
4549        two sixth_completionˇ
4550        three sixth_completionˇ
4551        additional edit
4552    "});
4553
4554    handle_resolve_completion_request(&mut cx, None).await;
4555    apply_additional_edits.await.unwrap();
4556
4557    cx.update(|cx| {
4558        cx.update_global::<Settings, _, _>(|settings, _| {
4559            settings.show_completions_on_input = false;
4560        })
4561    });
4562    cx.set_state("editorˇ");
4563    cx.simulate_keystroke(".");
4564    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4565    cx.simulate_keystroke("c");
4566    cx.simulate_keystroke("l");
4567    cx.simulate_keystroke("o");
4568    cx.assert_editor_state("editor.cloˇ");
4569    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4570    cx.update_editor(|editor, cx| {
4571        editor.show_completions(&ShowCompletions, cx);
4572    });
4573    handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
4574    cx.condition(|editor, _| editor.context_menu_visible())
4575        .await;
4576    let apply_additional_edits = cx.update_editor(|editor, cx| {
4577        editor
4578            .confirm_completion(&ConfirmCompletion::default(), cx)
4579            .unwrap()
4580    });
4581    cx.assert_editor_state("editor.closeˇ");
4582    handle_resolve_completion_request(&mut cx, None).await;
4583    apply_additional_edits.await.unwrap();
4584
4585    // Handle completion request passing a marked string specifying where the completion
4586    // should be triggered from using '|' character, what range should be replaced, and what completions
4587    // should be returned using '<' and '>' to delimit the range
4588    async fn handle_completion_request<'a>(
4589        cx: &mut EditorLspTestContext<'a>,
4590        marked_string: &str,
4591        completions: Vec<&'static str>,
4592    ) {
4593        let complete_from_marker: TextRangeMarker = '|'.into();
4594        let replace_range_marker: TextRangeMarker = ('<', '>').into();
4595        let (_, mut marked_ranges) = marked_text_ranges_by(
4596            marked_string,
4597            vec![complete_from_marker.clone(), replace_range_marker.clone()],
4598        );
4599
4600        let complete_from_position =
4601            cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
4602        let replace_range =
4603            cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
4604
4605        cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
4606            let completions = completions.clone();
4607            async move {
4608                assert_eq!(params.text_document_position.text_document.uri, url.clone());
4609                assert_eq!(
4610                    params.text_document_position.position,
4611                    complete_from_position
4612                );
4613                Ok(Some(lsp::CompletionResponse::Array(
4614                    completions
4615                        .iter()
4616                        .map(|completion_text| lsp::CompletionItem {
4617                            label: completion_text.to_string(),
4618                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
4619                                range: replace_range,
4620                                new_text: completion_text.to_string(),
4621                            })),
4622                            ..Default::default()
4623                        })
4624                        .collect(),
4625                )))
4626            }
4627        })
4628        .next()
4629        .await;
4630    }
4631
4632    async fn handle_resolve_completion_request<'a>(
4633        cx: &mut EditorLspTestContext<'a>,
4634        edits: Option<Vec<(&'static str, &'static str)>>,
4635    ) {
4636        let edits = edits.map(|edits| {
4637            edits
4638                .iter()
4639                .map(|(marked_string, new_text)| {
4640                    let (_, marked_ranges) = marked_text_ranges(marked_string, false);
4641                    let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
4642                    lsp::TextEdit::new(replace_range, new_text.to_string())
4643                })
4644                .collect::<Vec<_>>()
4645        });
4646
4647        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
4648            let edits = edits.clone();
4649            async move {
4650                Ok(lsp::CompletionItem {
4651                    additional_text_edits: edits,
4652                    ..Default::default()
4653                })
4654            }
4655        })
4656        .next()
4657        .await;
4658    }
4659}
4660
4661#[gpui::test]
4662async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
4663    cx.update(|cx| cx.set_global(Settings::test(cx)));
4664    let language = Arc::new(Language::new(
4665        LanguageConfig {
4666            line_comment: Some("// ".into()),
4667            ..Default::default()
4668        },
4669        Some(tree_sitter_rust::language()),
4670    ));
4671
4672    let text = "
4673        fn a() {
4674            //b();
4675            // c();
4676            //  d();
4677        }
4678    "
4679    .unindent();
4680
4681    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
4682    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4683    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
4684
4685    view.update(cx, |editor, cx| {
4686        // If multiple selections intersect a line, the line is only
4687        // toggled once.
4688        editor.change_selections(None, cx, |s| {
4689            s.select_display_ranges([
4690                DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
4691                DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
4692            ])
4693        });
4694        editor.toggle_comments(&ToggleComments::default(), cx);
4695        assert_eq!(
4696            editor.text(cx),
4697            "
4698                fn a() {
4699                    b();
4700                    c();
4701                     d();
4702                }
4703            "
4704            .unindent()
4705        );
4706
4707        // The comment prefix is inserted at the same column for every line
4708        // in a selection.
4709        editor.change_selections(None, cx, |s| {
4710            s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)])
4711        });
4712        editor.toggle_comments(&ToggleComments::default(), cx);
4713        assert_eq!(
4714            editor.text(cx),
4715            "
4716                fn a() {
4717                    // b();
4718                    // c();
4719                    //  d();
4720                }
4721            "
4722            .unindent()
4723        );
4724
4725        // If a selection ends at the beginning of a line, that line is not toggled.
4726        editor.change_selections(None, cx, |s| {
4727            s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)])
4728        });
4729        editor.toggle_comments(&ToggleComments::default(), cx);
4730        assert_eq!(
4731            editor.text(cx),
4732            "
4733                fn a() {
4734                    // b();
4735                    c();
4736                    //  d();
4737                }
4738            "
4739            .unindent()
4740        );
4741    });
4742}
4743
4744#[gpui::test]
4745async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
4746    let mut cx = EditorTestContext::new(cx);
4747    cx.update(|cx| cx.set_global(Settings::test(cx)));
4748
4749    let language = Arc::new(Language::new(
4750        LanguageConfig {
4751            line_comment: Some("// ".into()),
4752            ..Default::default()
4753        },
4754        Some(tree_sitter_rust::language()),
4755    ));
4756
4757    let registry = Arc::new(LanguageRegistry::test());
4758    registry.add(language.clone());
4759
4760    cx.update_buffer(|buffer, cx| {
4761        buffer.set_language_registry(registry);
4762        buffer.set_language(Some(language), cx);
4763    });
4764
4765    let toggle_comments = &ToggleComments {
4766        advance_downwards: true,
4767    };
4768
4769    // Single cursor on one line -> advance
4770    // Cursor moves horizontally 3 characters as well on non-blank line
4771    cx.set_state(indoc!(
4772        "fn a() {
4773             ˇdog();
4774             cat();
4775        }"
4776    ));
4777    cx.update_editor(|editor, cx| {
4778        editor.toggle_comments(toggle_comments, cx);
4779    });
4780    cx.assert_editor_state(indoc!(
4781        "fn a() {
4782             // dog();
4783             catˇ();
4784        }"
4785    ));
4786
4787    // Single selection on one line -> don't advance
4788    cx.set_state(indoc!(
4789        "fn a() {
4790             «dog()ˇ»;
4791             cat();
4792        }"
4793    ));
4794    cx.update_editor(|editor, cx| {
4795        editor.toggle_comments(toggle_comments, cx);
4796    });
4797    cx.assert_editor_state(indoc!(
4798        "fn a() {
4799             // «dog()ˇ»;
4800             cat();
4801        }"
4802    ));
4803
4804    // Multiple cursors on one line -> advance
4805    cx.set_state(indoc!(
4806        "fn a() {
4807             ˇdˇog();
4808             cat();
4809        }"
4810    ));
4811    cx.update_editor(|editor, cx| {
4812        editor.toggle_comments(toggle_comments, cx);
4813    });
4814    cx.assert_editor_state(indoc!(
4815        "fn a() {
4816             // dog();
4817             catˇ(ˇ);
4818        }"
4819    ));
4820
4821    // Multiple cursors on one line, with selection -> don't advance
4822    cx.set_state(indoc!(
4823        "fn a() {
4824             ˇdˇog«()ˇ»;
4825             cat();
4826        }"
4827    ));
4828    cx.update_editor(|editor, cx| {
4829        editor.toggle_comments(toggle_comments, cx);
4830    });
4831    cx.assert_editor_state(indoc!(
4832        "fn a() {
4833             // ˇdˇog«()ˇ»;
4834             cat();
4835        }"
4836    ));
4837
4838    // Single cursor on one line -> advance
4839    // Cursor moves to column 0 on blank line
4840    cx.set_state(indoc!(
4841        "fn a() {
4842             ˇdog();
4843
4844             cat();
4845        }"
4846    ));
4847    cx.update_editor(|editor, cx| {
4848        editor.toggle_comments(toggle_comments, cx);
4849    });
4850    cx.assert_editor_state(indoc!(
4851        "fn a() {
4852             // dog();
4853        ˇ
4854             cat();
4855        }"
4856    ));
4857
4858    // Single cursor on one line -> advance
4859    // Cursor starts and ends at column 0
4860    cx.set_state(indoc!(
4861        "fn a() {
4862         ˇ    dog();
4863             cat();
4864        }"
4865    ));
4866    cx.update_editor(|editor, cx| {
4867        editor.toggle_comments(toggle_comments, cx);
4868    });
4869    cx.assert_editor_state(indoc!(
4870        "fn a() {
4871             // dog();
4872         ˇ    cat();
4873        }"
4874    ));
4875}
4876
4877#[gpui::test]
4878async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
4879    let mut cx = EditorTestContext::new(cx);
4880
4881    let html_language = Arc::new(
4882        Language::new(
4883            LanguageConfig {
4884                name: "HTML".into(),
4885                block_comment: Some(("<!-- ".into(), " -->".into())),
4886                ..Default::default()
4887            },
4888            Some(tree_sitter_html::language()),
4889        )
4890        .with_injection_query(
4891            r#"
4892            (script_element
4893                (raw_text) @content
4894                (#set! "language" "javascript"))
4895            "#,
4896        )
4897        .unwrap(),
4898    );
4899
4900    let javascript_language = Arc::new(Language::new(
4901        LanguageConfig {
4902            name: "JavaScript".into(),
4903            line_comment: Some("// ".into()),
4904            ..Default::default()
4905        },
4906        Some(tree_sitter_javascript::language()),
4907    ));
4908
4909    let registry = Arc::new(LanguageRegistry::test());
4910    registry.add(html_language.clone());
4911    registry.add(javascript_language.clone());
4912
4913    cx.update_buffer(|buffer, cx| {
4914        buffer.set_language_registry(registry);
4915        buffer.set_language(Some(html_language), cx);
4916    });
4917
4918    // Toggle comments for empty selections
4919    cx.set_state(
4920        &r#"
4921            <p>A</p>ˇ
4922            <p>B</p>ˇ
4923            <p>C</p>ˇ
4924        "#
4925        .unindent(),
4926    );
4927    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
4928    cx.assert_editor_state(
4929        &r#"
4930            <!-- <p>A</p>ˇ -->
4931            <!-- <p>B</p>ˇ -->
4932            <!-- <p>C</p>ˇ -->
4933        "#
4934        .unindent(),
4935    );
4936    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
4937    cx.assert_editor_state(
4938        &r#"
4939            <p>A</p>ˇ
4940            <p>B</p>ˇ
4941            <p>C</p>ˇ
4942        "#
4943        .unindent(),
4944    );
4945
4946    // Toggle comments for mixture of empty and non-empty selections, where
4947    // multiple selections occupy a given line.
4948    cx.set_state(
4949        &r#"
4950            <p>A«</p>
4951            <p>ˇ»B</p>ˇ
4952            <p>C«</p>
4953            <p>ˇ»D</p>ˇ
4954        "#
4955        .unindent(),
4956    );
4957
4958    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
4959    cx.assert_editor_state(
4960        &r#"
4961            <!-- <p>A«</p>
4962            <p>ˇ»B</p>ˇ -->
4963            <!-- <p>C«</p>
4964            <p>ˇ»D</p>ˇ -->
4965        "#
4966        .unindent(),
4967    );
4968    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
4969    cx.assert_editor_state(
4970        &r#"
4971            <p>A«</p>
4972            <p>ˇ»B</p>ˇ
4973            <p>C«</p>
4974            <p>ˇ»D</p>ˇ
4975        "#
4976        .unindent(),
4977    );
4978
4979    // Toggle comments when different languages are active for different
4980    // selections.
4981    cx.set_state(
4982        &r#"
4983            ˇ<script>
4984                ˇvar x = new Y();
4985            ˇ</script>
4986        "#
4987        .unindent(),
4988    );
4989    cx.foreground().run_until_parked();
4990    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
4991    cx.assert_editor_state(
4992        &r#"
4993            <!-- ˇ<script> -->
4994                // ˇvar x = new Y();
4995            <!-- ˇ</script> -->
4996        "#
4997        .unindent(),
4998    );
4999}
5000
5001#[gpui::test]
5002fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
5003    cx.set_global(Settings::test(cx));
5004    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5005    let multibuffer = cx.add_model(|cx| {
5006        let mut multibuffer = MultiBuffer::new(0);
5007        multibuffer.push_excerpts(
5008            buffer.clone(),
5009            [
5010                ExcerptRange {
5011                    context: Point::new(0, 0)..Point::new(0, 4),
5012                    primary: None,
5013                },
5014                ExcerptRange {
5015                    context: Point::new(1, 0)..Point::new(1, 4),
5016                    primary: None,
5017                },
5018            ],
5019            cx,
5020        );
5021        multibuffer
5022    });
5023
5024    assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
5025
5026    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
5027    view.update(cx, |view, cx| {
5028        assert_eq!(view.text(cx), "aaaa\nbbbb");
5029        view.change_selections(None, cx, |s| {
5030            s.select_ranges([
5031                Point::new(0, 0)..Point::new(0, 0),
5032                Point::new(1, 0)..Point::new(1, 0),
5033            ])
5034        });
5035
5036        view.handle_input("X", cx);
5037        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
5038        assert_eq!(
5039            view.selections.ranges(cx),
5040            [
5041                Point::new(0, 1)..Point::new(0, 1),
5042                Point::new(1, 1)..Point::new(1, 1),
5043            ]
5044        )
5045    });
5046}
5047
5048#[gpui::test]
5049fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
5050    cx.set_global(Settings::test(cx));
5051    let markers = vec![('[', ']').into(), ('(', ')').into()];
5052    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
5053        indoc! {"
5054            [aaaa
5055            (bbbb]
5056            cccc)",
5057        },
5058        markers.clone(),
5059    );
5060    let excerpt_ranges = markers.into_iter().map(|marker| {
5061        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
5062        ExcerptRange {
5063            context,
5064            primary: None,
5065        }
5066    });
5067    let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
5068    let multibuffer = cx.add_model(|cx| {
5069        let mut multibuffer = MultiBuffer::new(0);
5070        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
5071        multibuffer
5072    });
5073
5074    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
5075    view.update(cx, |view, cx| {
5076        let (expected_text, selection_ranges) = marked_text_ranges(
5077            indoc! {"
5078                aaaa
5079                bˇbbb
5080                bˇbbˇb
5081                cccc"
5082            },
5083            true,
5084        );
5085        assert_eq!(view.text(cx), expected_text);
5086        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
5087
5088        view.handle_input("X", cx);
5089
5090        let (expected_text, expected_selections) = marked_text_ranges(
5091            indoc! {"
5092                aaaa
5093                bXˇbbXb
5094                bXˇbbXˇb
5095                cccc"
5096            },
5097            false,
5098        );
5099        assert_eq!(view.text(cx), expected_text);
5100        assert_eq!(view.selections.ranges(cx), expected_selections);
5101
5102        view.newline(&Newline, cx);
5103        let (expected_text, expected_selections) = marked_text_ranges(
5104            indoc! {"
5105                aaaa
5106                bX
5107                ˇbbX
5108                b
5109                bX
5110                ˇbbX
5111                ˇb
5112                cccc"
5113            },
5114            false,
5115        );
5116        assert_eq!(view.text(cx), expected_text);
5117        assert_eq!(view.selections.ranges(cx), expected_selections);
5118    });
5119}
5120
5121#[gpui::test]
5122fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
5123    cx.set_global(Settings::test(cx));
5124    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5125    let mut excerpt1_id = None;
5126    let multibuffer = cx.add_model(|cx| {
5127        let mut multibuffer = MultiBuffer::new(0);
5128        excerpt1_id = multibuffer
5129            .push_excerpts(
5130                buffer.clone(),
5131                [
5132                    ExcerptRange {
5133                        context: Point::new(0, 0)..Point::new(1, 4),
5134                        primary: None,
5135                    },
5136                    ExcerptRange {
5137                        context: Point::new(1, 0)..Point::new(2, 4),
5138                        primary: None,
5139                    },
5140                ],
5141                cx,
5142            )
5143            .into_iter()
5144            .next();
5145        multibuffer
5146    });
5147    assert_eq!(
5148        multibuffer.read(cx).read(cx).text(),
5149        "aaaa\nbbbb\nbbbb\ncccc"
5150    );
5151    let (_, editor) = cx.add_window(Default::default(), |cx| {
5152        let mut editor = build_editor(multibuffer.clone(), cx);
5153        let snapshot = editor.snapshot(cx);
5154        editor.change_selections(None, cx, |s| {
5155            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
5156        });
5157        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
5158        assert_eq!(
5159            editor.selections.ranges(cx),
5160            [
5161                Point::new(1, 3)..Point::new(1, 3),
5162                Point::new(2, 1)..Point::new(2, 1),
5163            ]
5164        );
5165        editor
5166    });
5167
5168    // Refreshing selections is a no-op when excerpts haven't changed.
5169    editor.update(cx, |editor, cx| {
5170        editor.change_selections(None, cx, |s| s.refresh());
5171        assert_eq!(
5172            editor.selections.ranges(cx),
5173            [
5174                Point::new(1, 3)..Point::new(1, 3),
5175                Point::new(2, 1)..Point::new(2, 1),
5176            ]
5177        );
5178    });
5179
5180    multibuffer.update(cx, |multibuffer, cx| {
5181        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
5182    });
5183    editor.update(cx, |editor, cx| {
5184        // Removing an excerpt causes the first selection to become degenerate.
5185        assert_eq!(
5186            editor.selections.ranges(cx),
5187            [
5188                Point::new(0, 0)..Point::new(0, 0),
5189                Point::new(0, 1)..Point::new(0, 1)
5190            ]
5191        );
5192
5193        // Refreshing selections will relocate the first selection to the original buffer
5194        // location.
5195        editor.change_selections(None, cx, |s| s.refresh());
5196        assert_eq!(
5197            editor.selections.ranges(cx),
5198            [
5199                Point::new(0, 1)..Point::new(0, 1),
5200                Point::new(0, 3)..Point::new(0, 3)
5201            ]
5202        );
5203        assert!(editor.selections.pending_anchor().is_some());
5204    });
5205}
5206
5207#[gpui::test]
5208fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) {
5209    cx.set_global(Settings::test(cx));
5210    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5211    let mut excerpt1_id = None;
5212    let multibuffer = cx.add_model(|cx| {
5213        let mut multibuffer = MultiBuffer::new(0);
5214        excerpt1_id = multibuffer
5215            .push_excerpts(
5216                buffer.clone(),
5217                [
5218                    ExcerptRange {
5219                        context: Point::new(0, 0)..Point::new(1, 4),
5220                        primary: None,
5221                    },
5222                    ExcerptRange {
5223                        context: Point::new(1, 0)..Point::new(2, 4),
5224                        primary: None,
5225                    },
5226                ],
5227                cx,
5228            )
5229            .into_iter()
5230            .next();
5231        multibuffer
5232    });
5233    assert_eq!(
5234        multibuffer.read(cx).read(cx).text(),
5235        "aaaa\nbbbb\nbbbb\ncccc"
5236    );
5237    let (_, editor) = cx.add_window(Default::default(), |cx| {
5238        let mut editor = build_editor(multibuffer.clone(), cx);
5239        let snapshot = editor.snapshot(cx);
5240        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
5241        assert_eq!(
5242            editor.selections.ranges(cx),
5243            [Point::new(1, 3)..Point::new(1, 3)]
5244        );
5245        editor
5246    });
5247
5248    multibuffer.update(cx, |multibuffer, cx| {
5249        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
5250    });
5251    editor.update(cx, |editor, cx| {
5252        assert_eq!(
5253            editor.selections.ranges(cx),
5254            [Point::new(0, 0)..Point::new(0, 0)]
5255        );
5256
5257        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
5258        editor.change_selections(None, cx, |s| s.refresh());
5259        assert_eq!(
5260            editor.selections.ranges(cx),
5261            [Point::new(0, 3)..Point::new(0, 3)]
5262        );
5263        assert!(editor.selections.pending_anchor().is_some());
5264    });
5265}
5266
5267#[gpui::test]
5268async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
5269    cx.update(|cx| cx.set_global(Settings::test(cx)));
5270    let language = Arc::new(
5271        Language::new(
5272            LanguageConfig {
5273                brackets: BracketPairConfig {
5274                    pairs: vec![
5275                        BracketPair {
5276                            start: "{".to_string(),
5277                            end: "}".to_string(),
5278                            close: true,
5279                            newline: true,
5280                        },
5281                        BracketPair {
5282                            start: "/* ".to_string(),
5283                            end: " */".to_string(),
5284                            close: true,
5285                            newline: true,
5286                        },
5287                    ],
5288                    ..Default::default()
5289                },
5290                ..Default::default()
5291            },
5292            Some(tree_sitter_rust::language()),
5293        )
5294        .with_indents_query("")
5295        .unwrap(),
5296    );
5297
5298    let text = concat!(
5299        "{   }\n",     //
5300        "  x\n",       //
5301        "  /*   */\n", //
5302        "x\n",         //
5303        "{{} }\n",     //
5304    );
5305
5306    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
5307    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
5308    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
5309    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
5310        .await;
5311
5312    view.update(cx, |view, cx| {
5313        view.change_selections(None, cx, |s| {
5314            s.select_display_ranges([
5315                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
5316                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
5317                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
5318            ])
5319        });
5320        view.newline(&Newline, cx);
5321
5322        assert_eq!(
5323            view.buffer().read(cx).read(cx).text(),
5324            concat!(
5325                "{ \n",    // Suppress rustfmt
5326                "\n",      //
5327                "}\n",     //
5328                "  x\n",   //
5329                "  /* \n", //
5330                "  \n",    //
5331                "  */\n",  //
5332                "x\n",     //
5333                "{{} \n",  //
5334                "}\n",     //
5335            )
5336        );
5337    });
5338}
5339
5340#[gpui::test]
5341fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
5342    let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
5343
5344    cx.set_global(Settings::test(cx));
5345    let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
5346
5347    editor.update(cx, |editor, cx| {
5348        struct Type1;
5349        struct Type2;
5350
5351        let buffer = buffer.read(cx).snapshot(cx);
5352
5353        let anchor_range =
5354            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
5355
5356        editor.highlight_background::<Type1>(
5357            vec![
5358                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
5359                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
5360                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
5361                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
5362            ],
5363            |_| Color::red(),
5364            cx,
5365        );
5366        editor.highlight_background::<Type2>(
5367            vec![
5368                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
5369                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
5370                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
5371                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
5372            ],
5373            |_| Color::green(),
5374            cx,
5375        );
5376
5377        let snapshot = editor.snapshot(cx);
5378        let mut highlighted_ranges = editor.background_highlights_in_range(
5379            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
5380            &snapshot,
5381            cx.global::<Settings>().theme.as_ref(),
5382        );
5383        // Enforce a consistent ordering based on color without relying on the ordering of the
5384        // highlight's `TypeId` which is non-deterministic.
5385        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
5386        assert_eq!(
5387            highlighted_ranges,
5388            &[
5389                (
5390                    DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
5391                    Color::green(),
5392                ),
5393                (
5394                    DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
5395                    Color::green(),
5396                ),
5397                (
5398                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
5399                    Color::red(),
5400                ),
5401                (
5402                    DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
5403                    Color::red(),
5404                ),
5405            ]
5406        );
5407        assert_eq!(
5408            editor.background_highlights_in_range(
5409                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
5410                &snapshot,
5411                cx.global::<Settings>().theme.as_ref(),
5412            ),
5413            &[(
5414                DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
5415                Color::red(),
5416            )]
5417        );
5418    });
5419}
5420
5421#[gpui::test]
5422async fn test_following(cx: &mut gpui::TestAppContext) {
5423    Settings::test_async(cx);
5424    let fs = FakeFs::new(cx.background());
5425    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5426
5427    let buffer = project.update(cx, |project, cx| {
5428        let buffer = project
5429            .create_buffer(&sample_text(16, 8, 'a'), None, cx)
5430            .unwrap();
5431        cx.add_model(|cx| MultiBuffer::singleton(buffer, cx))
5432    });
5433    let (_, leader) = cx.add_window(|cx| build_editor(buffer.clone(), cx));
5434    let (_, follower) = cx.update(|cx| {
5435        cx.add_window(
5436            WindowOptions {
5437                bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
5438                ..Default::default()
5439            },
5440            |cx| build_editor(buffer.clone(), cx),
5441        )
5442    });
5443
5444    let is_still_following = Rc::new(RefCell::new(true));
5445    let pending_update = Rc::new(RefCell::new(None));
5446    follower.update(cx, {
5447        let update = pending_update.clone();
5448        let is_still_following = is_still_following.clone();
5449        |_, cx| {
5450            cx.subscribe(&leader, move |_, leader, event, cx| {
5451                leader
5452                    .read(cx)
5453                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
5454            })
5455            .detach();
5456
5457            cx.subscribe(&follower, move |_, _, event, cx| {
5458                if Editor::should_unfollow_on_event(event, cx) {
5459                    *is_still_following.borrow_mut() = false;
5460                }
5461            })
5462            .detach();
5463        }
5464    });
5465
5466    // Update the selections only
5467    leader.update(cx, |leader, cx| {
5468        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
5469    });
5470    follower
5471        .update(cx, |follower, cx| {
5472            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5473        })
5474        .await
5475        .unwrap();
5476    follower.read_with(cx, |follower, cx| {
5477        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
5478    });
5479    assert_eq!(*is_still_following.borrow(), true);
5480
5481    // Update the scroll position only
5482    leader.update(cx, |leader, cx| {
5483        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
5484    });
5485    follower
5486        .update(cx, |follower, cx| {
5487            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5488        })
5489        .await
5490        .unwrap();
5491    assert_eq!(
5492        follower.update(cx, |follower, cx| follower.scroll_position(cx)),
5493        vec2f(1.5, 3.5)
5494    );
5495    assert_eq!(*is_still_following.borrow(), true);
5496
5497    // Update the selections and scroll position. The follower's scroll position is updated
5498    // via autoscroll, not via the leader's exact scroll position.
5499    leader.update(cx, |leader, cx| {
5500        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
5501        leader.request_autoscroll(Autoscroll::newest(), cx);
5502        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
5503    });
5504    follower
5505        .update(cx, |follower, cx| {
5506            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5507        })
5508        .await
5509        .unwrap();
5510    follower.update(cx, |follower, cx| {
5511        assert_eq!(follower.scroll_position(cx), vec2f(1.5, 0.0));
5512        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
5513    });
5514    assert_eq!(*is_still_following.borrow(), true);
5515
5516    // Creating a pending selection that precedes another selection
5517    leader.update(cx, |leader, cx| {
5518        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
5519        leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
5520    });
5521    follower
5522        .update(cx, |follower, cx| {
5523            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5524        })
5525        .await
5526        .unwrap();
5527    follower.read_with(cx, |follower, cx| {
5528        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
5529    });
5530    assert_eq!(*is_still_following.borrow(), true);
5531
5532    // Extend the pending selection so that it surrounds another selection
5533    leader.update(cx, |leader, cx| {
5534        leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
5535    });
5536    follower
5537        .update(cx, |follower, cx| {
5538            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5539        })
5540        .await
5541        .unwrap();
5542    follower.read_with(cx, |follower, cx| {
5543        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
5544    });
5545
5546    // Scrolling locally breaks the follow
5547    follower.update(cx, |follower, cx| {
5548        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
5549        follower.set_scroll_anchor(
5550            ScrollAnchor {
5551                top_anchor,
5552                offset: vec2f(0.0, 0.5),
5553            },
5554            cx,
5555        );
5556    });
5557    assert_eq!(*is_still_following.borrow(), false);
5558}
5559
5560#[gpui::test]
5561async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
5562    Settings::test_async(cx);
5563    let fs = FakeFs::new(cx.background());
5564    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5565    let (_, pane) = cx.add_window(|cx| Pane::new(None, cx));
5566
5567    let leader = pane.update(cx, |_, cx| {
5568        let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
5569        cx.add_view(|cx| build_editor(multibuffer.clone(), cx))
5570    });
5571
5572    // Start following the editor when it has no excerpts.
5573    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
5574    let follower_1 = cx
5575        .update(|cx| {
5576            Editor::from_state_proto(
5577                pane.clone(),
5578                project.clone(),
5579                ViewId {
5580                    creator: Default::default(),
5581                    id: 0,
5582                },
5583                &mut state_message,
5584                cx,
5585            )
5586        })
5587        .unwrap()
5588        .await
5589        .unwrap();
5590
5591    let update_message = Rc::new(RefCell::new(None));
5592    follower_1.update(cx, {
5593        let update = update_message.clone();
5594        |_, cx| {
5595            cx.subscribe(&leader, move |_, leader, event, cx| {
5596                leader
5597                    .read(cx)
5598                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
5599            })
5600            .detach();
5601        }
5602    });
5603
5604    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
5605        (
5606            project
5607                .create_buffer("abc\ndef\nghi\njkl\n", None, cx)
5608                .unwrap(),
5609            project
5610                .create_buffer("mno\npqr\nstu\nvwx\n", None, cx)
5611                .unwrap(),
5612        )
5613    });
5614
5615    // Insert some excerpts.
5616    leader.update(cx, |leader, cx| {
5617        leader.buffer.update(cx, |multibuffer, cx| {
5618            let excerpt_ids = multibuffer.push_excerpts(
5619                buffer_1.clone(),
5620                [
5621                    ExcerptRange {
5622                        context: 1..6,
5623                        primary: None,
5624                    },
5625                    ExcerptRange {
5626                        context: 12..15,
5627                        primary: None,
5628                    },
5629                    ExcerptRange {
5630                        context: 0..3,
5631                        primary: None,
5632                    },
5633                ],
5634                cx,
5635            );
5636            multibuffer.insert_excerpts_after(
5637                excerpt_ids[0],
5638                buffer_2.clone(),
5639                [
5640                    ExcerptRange {
5641                        context: 8..12,
5642                        primary: None,
5643                    },
5644                    ExcerptRange {
5645                        context: 0..6,
5646                        primary: None,
5647                    },
5648                ],
5649                cx,
5650            );
5651        });
5652    });
5653
5654    // Apply the update of adding the excerpts.
5655    follower_1
5656        .update(cx, |follower, cx| {
5657            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
5658        })
5659        .await
5660        .unwrap();
5661    assert_eq!(
5662        follower_1.read_with(cx, Editor::text),
5663        leader.read_with(cx, Editor::text)
5664    );
5665    update_message.borrow_mut().take();
5666
5667    // Start following separately after it already has excerpts.
5668    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
5669    let follower_2 = cx
5670        .update(|cx| {
5671            Editor::from_state_proto(
5672                pane.clone(),
5673                project.clone(),
5674                ViewId {
5675                    creator: Default::default(),
5676                    id: 0,
5677                },
5678                &mut state_message,
5679                cx,
5680            )
5681        })
5682        .unwrap()
5683        .await
5684        .unwrap();
5685    assert_eq!(
5686        follower_2.read_with(cx, Editor::text),
5687        leader.read_with(cx, Editor::text)
5688    );
5689
5690    // Remove some excerpts.
5691    leader.update(cx, |leader, cx| {
5692        leader.buffer.update(cx, |multibuffer, cx| {
5693            let excerpt_ids = multibuffer.excerpt_ids();
5694            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
5695            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
5696        });
5697    });
5698
5699    // Apply the update of removing the excerpts.
5700    follower_1
5701        .update(cx, |follower, cx| {
5702            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
5703        })
5704        .await
5705        .unwrap();
5706    follower_2
5707        .update(cx, |follower, cx| {
5708            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
5709        })
5710        .await
5711        .unwrap();
5712    update_message.borrow_mut().take();
5713    assert_eq!(
5714        follower_1.read_with(cx, Editor::text),
5715        leader.read_with(cx, Editor::text)
5716    );
5717}
5718
5719#[test]
5720fn test_combine_syntax_and_fuzzy_match_highlights() {
5721    let string = "abcdefghijklmnop";
5722    let syntax_ranges = [
5723        (
5724            0..3,
5725            HighlightStyle {
5726                color: Some(Color::red()),
5727                ..Default::default()
5728            },
5729        ),
5730        (
5731            4..8,
5732            HighlightStyle {
5733                color: Some(Color::green()),
5734                ..Default::default()
5735            },
5736        ),
5737    ];
5738    let match_indices = [4, 6, 7, 8];
5739    assert_eq!(
5740        combine_syntax_and_fuzzy_match_highlights(
5741            string,
5742            Default::default(),
5743            syntax_ranges.into_iter(),
5744            &match_indices,
5745        ),
5746        &[
5747            (
5748                0..3,
5749                HighlightStyle {
5750                    color: Some(Color::red()),
5751                    ..Default::default()
5752                },
5753            ),
5754            (
5755                4..5,
5756                HighlightStyle {
5757                    color: Some(Color::green()),
5758                    weight: Some(fonts::Weight::BOLD),
5759                    ..Default::default()
5760                },
5761            ),
5762            (
5763                5..6,
5764                HighlightStyle {
5765                    color: Some(Color::green()),
5766                    ..Default::default()
5767                },
5768            ),
5769            (
5770                6..8,
5771                HighlightStyle {
5772                    color: Some(Color::green()),
5773                    weight: Some(fonts::Weight::BOLD),
5774                    ..Default::default()
5775                },
5776            ),
5777            (
5778                8..9,
5779                HighlightStyle {
5780                    weight: Some(fonts::Weight::BOLD),
5781                    ..Default::default()
5782                },
5783            ),
5784        ]
5785    );
5786}
5787
5788#[gpui::test]
5789async fn go_to_hunk(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
5790    let mut cx = EditorTestContext::new(cx);
5791
5792    let diff_base = r#"
5793        use some::mod;
5794
5795        const A: u32 = 42;
5796
5797        fn main() {
5798            println!("hello");
5799
5800            println!("world");
5801        }
5802        "#
5803    .unindent();
5804
5805    // Edits are modified, removed, modified, added
5806    cx.set_state(
5807        &r#"
5808        use some::modified;
5809
5810        ˇ
5811        fn main() {
5812            println!("hello there");
5813
5814            println!("around the");
5815            println!("world");
5816        }
5817        "#
5818        .unindent(),
5819    );
5820
5821    cx.set_diff_base(Some(&diff_base));
5822    deterministic.run_until_parked();
5823
5824    cx.update_editor(|editor, cx| {
5825        //Wrap around the bottom of the buffer
5826        for _ in 0..3 {
5827            editor.go_to_hunk(&GoToHunk, cx);
5828        }
5829    });
5830
5831    cx.assert_editor_state(
5832        &r#"
5833        ˇuse some::modified;
5834    
5835    
5836        fn main() {
5837            println!("hello there");
5838    
5839            println!("around the");
5840            println!("world");
5841        }
5842        "#
5843        .unindent(),
5844    );
5845
5846    cx.update_editor(|editor, cx| {
5847        //Wrap around the top of the buffer
5848        for _ in 0..2 {
5849            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
5850        }
5851    });
5852
5853    cx.assert_editor_state(
5854        &r#"
5855        use some::modified;
5856
5857
5858        fn main() {
5859        ˇ    println!("hello there");
5860
5861            println!("around the");
5862            println!("world");
5863        }
5864        "#
5865        .unindent(),
5866    );
5867
5868    cx.update_editor(|editor, cx| {
5869        editor.fold(&Fold, cx);
5870
5871        //Make sure that the fold only gets one hunk
5872        for _ in 0..4 {
5873            editor.go_to_hunk(&GoToHunk, cx);
5874        }
5875    });
5876
5877    cx.assert_editor_state(
5878        &r#"
5879        ˇuse some::modified;
5880
5881
5882        fn main() {
5883            println!("hello there");
5884
5885            println!("around the");
5886            println!("world");
5887        }
5888        "#
5889        .unindent(),
5890    );
5891}
5892
5893#[test]
5894fn test_split_words() {
5895    fn split<'a>(text: &'a str) -> Vec<&'a str> {
5896        split_words(text).collect()
5897    }
5898
5899    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
5900    assert_eq!(split("hello_world"), &["hello_", "world"]);
5901    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
5902    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
5903    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
5904    assert_eq!(split("helloworld"), &["helloworld"]);
5905}
5906
5907#[gpui::test]
5908async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
5909    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
5910    let mut assert = |before, after| {
5911        let _state_context = cx.set_state(before);
5912        cx.update_editor(|editor, cx| {
5913            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
5914        });
5915        cx.assert_editor_state(after);
5916    };
5917
5918    // Outside bracket jumps to outside of matching bracket
5919    assert("console.logˇ(var);", "console.log(var)ˇ;");
5920    assert("console.log(var)ˇ;", "console.logˇ(var);");
5921
5922    // Inside bracket jumps to inside of matching bracket
5923    assert("console.log(ˇvar);", "console.log(varˇ);");
5924    assert("console.log(varˇ);", "console.log(ˇvar);");
5925
5926    // When outside a bracket and inside, favor jumping to the inside bracket
5927    assert(
5928        "console.log('foo', [1, 2, 3]ˇ);",
5929        "console.log(ˇ'foo', [1, 2, 3]);",
5930    );
5931    assert(
5932        "console.log(ˇ'foo', [1, 2, 3]);",
5933        "console.log('foo', [1, 2, 3]ˇ);",
5934    );
5935
5936    // Bias forward if two options are equally likely
5937    assert(
5938        "let result = curried_fun()ˇ();",
5939        "let result = curried_fun()()ˇ;",
5940    );
5941
5942    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
5943    assert(
5944        indoc! {"
5945            function test() {
5946                console.log('test')ˇ
5947            }"},
5948        indoc! {"
5949            function test() {
5950                console.logˇ('test')
5951            }"},
5952    );
5953}
5954
5955fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
5956    let point = DisplayPoint::new(row as u32, column as u32);
5957    point..point
5958}
5959
5960fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
5961    let (text, ranges) = marked_text_ranges(marked_text, true);
5962    assert_eq!(view.text(cx), text);
5963    assert_eq!(
5964        view.selections.ranges(cx),
5965        ranges,
5966        "Assert selections are {}",
5967        marked_text
5968    );
5969}