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