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