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