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}
1748
1749#[gpui::test]
1750fn test_insert_with_old_selections(cx: &mut TestAppContext) {
1751    init_test(cx, |_| {});
1752
1753    let (_, editor) = cx.add_window(|cx| {
1754        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
1755        let mut editor = build_editor(buffer.clone(), cx);
1756        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
1757        editor
1758    });
1759
1760    editor.update(cx, |editor, cx| {
1761        // Edit the buffer directly, deleting ranges surrounding the editor's selections
1762        editor.buffer.update(cx, |buffer, cx| {
1763            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
1764            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
1765        });
1766        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
1767
1768        editor.insert("Z", cx);
1769        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
1770
1771        // The selections are moved after the inserted characters
1772        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
1773    });
1774}
1775
1776#[gpui::test]
1777async fn test_tab(cx: &mut gpui::TestAppContext) {
1778    init_test(cx, |settings| {
1779        settings.defaults.tab_size = NonZeroU32::new(3)
1780    });
1781
1782    let mut cx = EditorTestContext::new(cx).await;
1783    cx.set_state(indoc! {"
1784        ˇabˇc
1785        ˇ🏀ˇ🏀ˇefg
17861787    "});
1788    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1789    cx.assert_editor_state(indoc! {"
1790           ˇab ˇc
1791           ˇ🏀  ˇ🏀  ˇefg
1792        d  ˇ
1793    "});
1794
1795    cx.set_state(indoc! {"
1796        a
1797        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
1798    "});
1799    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1800    cx.assert_editor_state(indoc! {"
1801        a
1802           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
1803    "});
1804}
1805
1806#[gpui::test]
1807async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
1808    init_test(cx, |_| {});
1809
1810    let mut cx = EditorTestContext::new(cx).await;
1811    let language = Arc::new(
1812        Language::new(
1813            LanguageConfig::default(),
1814            Some(tree_sitter_rust::language()),
1815        )
1816        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
1817        .unwrap(),
1818    );
1819    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1820
1821    // cursors that are already at the suggested indent level insert
1822    // a soft tab. cursors that are to the left of the suggested indent
1823    // auto-indent their line.
1824    cx.set_state(indoc! {"
1825        ˇ
1826        const a: B = (
1827            c(
1828                d(
1829        ˇ
1830                )
1831        ˇ
1832        ˇ    )
1833        );
1834    "});
1835    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1836    cx.assert_editor_state(indoc! {"
1837            ˇ
1838        const a: B = (
1839            c(
1840                d(
1841                    ˇ
1842                )
1843                ˇ
1844            ˇ)
1845        );
1846    "});
1847
1848    // handle auto-indent when there are multiple cursors on the same line
1849    cx.set_state(indoc! {"
1850        const a: B = (
1851            c(
1852        ˇ    ˇ
1853        ˇ    )
1854        );
1855    "});
1856    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1857    cx.assert_editor_state(indoc! {"
1858        const a: B = (
1859            c(
1860                ˇ
1861            ˇ)
1862        );
1863    "});
1864}
1865
1866#[gpui::test]
1867async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
1868    init_test(cx, |settings| {
1869        settings.defaults.tab_size = NonZeroU32::new(4)
1870    });
1871
1872    let language = Arc::new(
1873        Language::new(
1874            LanguageConfig::default(),
1875            Some(tree_sitter_rust::language()),
1876        )
1877        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
1878        .unwrap(),
1879    );
1880
1881    let mut cx = EditorTestContext::new(cx).await;
1882    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1883    cx.set_state(indoc! {"
1884        fn a() {
1885            if b {
1886        \t ˇc
1887            }
1888        }
1889    "});
1890
1891    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1892    cx.assert_editor_state(indoc! {"
1893        fn a() {
1894            if b {
1895                ˇc
1896            }
1897        }
1898    "});
1899}
1900
1901#[gpui::test]
1902async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
1903    init_test(cx, |settings| {
1904        settings.defaults.tab_size = NonZeroU32::new(4);
1905    });
1906
1907    let mut cx = EditorTestContext::new(cx).await;
1908
1909    cx.set_state(indoc! {"
1910          «oneˇ» «twoˇ»
1911        three
1912         four
1913    "});
1914    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1915    cx.assert_editor_state(indoc! {"
1916            «oneˇ» «twoˇ»
1917        three
1918         four
1919    "});
1920
1921    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1922    cx.assert_editor_state(indoc! {"
1923        «oneˇ» «twoˇ»
1924        three
1925         four
1926    "});
1927
1928    // select across line ending
1929    cx.set_state(indoc! {"
1930        one two
1931        t«hree
1932        ˇ» four
1933    "});
1934    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1935    cx.assert_editor_state(indoc! {"
1936        one two
1937            t«hree
1938        ˇ» four
1939    "});
1940
1941    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1942    cx.assert_editor_state(indoc! {"
1943        one two
1944        t«hree
1945        ˇ» four
1946    "});
1947
1948    // Ensure that indenting/outdenting works when the cursor is at column 0.
1949    cx.set_state(indoc! {"
1950        one two
1951        ˇthree
1952            four
1953    "});
1954    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1955    cx.assert_editor_state(indoc! {"
1956        one two
1957            ˇthree
1958            four
1959    "});
1960
1961    cx.set_state(indoc! {"
1962        one two
1963        ˇ    three
1964            four
1965    "});
1966    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1967    cx.assert_editor_state(indoc! {"
1968        one two
1969        ˇthree
1970            four
1971    "});
1972}
1973
1974#[gpui::test]
1975async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
1976    init_test(cx, |settings| {
1977        settings.defaults.hard_tabs = Some(true);
1978    });
1979
1980    let mut cx = EditorTestContext::new(cx).await;
1981
1982    // select two ranges on one line
1983    cx.set_state(indoc! {"
1984        «oneˇ» «twoˇ»
1985        three
1986        four
1987    "});
1988    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1989    cx.assert_editor_state(indoc! {"
1990        \t«oneˇ» «twoˇ»
1991        three
1992        four
1993    "});
1994    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1995    cx.assert_editor_state(indoc! {"
1996        \t\t«oneˇ» «twoˇ»
1997        three
1998        four
1999    "});
2000    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2001    cx.assert_editor_state(indoc! {"
2002        \t«oneˇ» «twoˇ»
2003        three
2004        four
2005    "});
2006    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2007    cx.assert_editor_state(indoc! {"
2008        «oneˇ» «twoˇ»
2009        three
2010        four
2011    "});
2012
2013    // select across a line ending
2014    cx.set_state(indoc! {"
2015        one two
2016        t«hree
2017        ˇ»four
2018    "});
2019    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2020    cx.assert_editor_state(indoc! {"
2021        one two
2022        \tt«hree
2023        ˇ»four
2024    "});
2025    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2026    cx.assert_editor_state(indoc! {"
2027        one two
2028        \t\tt«hree
2029        ˇ»four
2030    "});
2031    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2032    cx.assert_editor_state(indoc! {"
2033        one two
2034        \tt«hree
2035        ˇ»four
2036    "});
2037    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2038    cx.assert_editor_state(indoc! {"
2039        one two
2040        t«hree
2041        ˇ»four
2042    "});
2043
2044    // Ensure that indenting/outdenting works when the cursor is at column 0.
2045    cx.set_state(indoc! {"
2046        one two
2047        ˇthree
2048        four
2049    "});
2050    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2051    cx.assert_editor_state(indoc! {"
2052        one two
2053        ˇthree
2054        four
2055    "});
2056    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2057    cx.assert_editor_state(indoc! {"
2058        one two
2059        \tˇthree
2060        four
2061    "});
2062    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2063    cx.assert_editor_state(indoc! {"
2064        one two
2065        ˇthree
2066        four
2067    "});
2068}
2069
2070#[gpui::test]
2071fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
2072    init_test(cx, |settings| {
2073        settings.languages.extend([
2074            (
2075                "TOML".into(),
2076                LanguageSettingsContent {
2077                    tab_size: NonZeroU32::new(2),
2078                    ..Default::default()
2079                },
2080            ),
2081            (
2082                "Rust".into(),
2083                LanguageSettingsContent {
2084                    tab_size: NonZeroU32::new(4),
2085                    ..Default::default()
2086                },
2087            ),
2088        ]);
2089    });
2090
2091    let toml_language = Arc::new(Language::new(
2092        LanguageConfig {
2093            name: "TOML".into(),
2094            ..Default::default()
2095        },
2096        None,
2097    ));
2098    let rust_language = Arc::new(Language::new(
2099        LanguageConfig {
2100            name: "Rust".into(),
2101            ..Default::default()
2102        },
2103        None,
2104    ));
2105
2106    let toml_buffer =
2107        cx.add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx));
2108    let rust_buffer = cx.add_model(|cx| {
2109        Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx)
2110    });
2111    let multibuffer = cx.add_model(|cx| {
2112        let mut multibuffer = MultiBuffer::new(0);
2113        multibuffer.push_excerpts(
2114            toml_buffer.clone(),
2115            [ExcerptRange {
2116                context: Point::new(0, 0)..Point::new(2, 0),
2117                primary: None,
2118            }],
2119            cx,
2120        );
2121        multibuffer.push_excerpts(
2122            rust_buffer.clone(),
2123            [ExcerptRange {
2124                context: Point::new(0, 0)..Point::new(1, 0),
2125                primary: None,
2126            }],
2127            cx,
2128        );
2129        multibuffer
2130    });
2131
2132    cx.add_window(|cx| {
2133        let mut editor = build_editor(multibuffer, cx);
2134
2135        assert_eq!(
2136            editor.text(cx),
2137            indoc! {"
2138                a = 1
2139                b = 2
2140
2141                const c: usize = 3;
2142            "}
2143        );
2144
2145        select_ranges(
2146            &mut editor,
2147            indoc! {"
2148                «aˇ» = 1
2149                b = 2
2150
2151                «const c:ˇ» usize = 3;
2152            "},
2153            cx,
2154        );
2155
2156        editor.tab(&Tab, cx);
2157        assert_text_with_selections(
2158            &mut editor,
2159            indoc! {"
2160                  «aˇ» = 1
2161                b = 2
2162
2163                    «const c:ˇ» usize = 3;
2164            "},
2165            cx,
2166        );
2167        editor.tab_prev(&TabPrev, cx);
2168        assert_text_with_selections(
2169            &mut editor,
2170            indoc! {"
2171                «aˇ» = 1
2172                b = 2
2173
2174                «const c:ˇ» usize = 3;
2175            "},
2176            cx,
2177        );
2178
2179        editor
2180    });
2181}
2182
2183#[gpui::test]
2184async fn test_backspace(cx: &mut gpui::TestAppContext) {
2185    init_test(cx, |_| {});
2186
2187    let mut cx = EditorTestContext::new(cx).await;
2188
2189    // Basic backspace
2190    cx.set_state(indoc! {"
2191        onˇe two three
2192        fou«rˇ» five six
2193        seven «ˇeight nine
2194        »ten
2195    "});
2196    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2197    cx.assert_editor_state(indoc! {"
2198        oˇe two three
2199        fouˇ five six
2200        seven ˇten
2201    "});
2202
2203    // Test backspace inside and around indents
2204    cx.set_state(indoc! {"
2205        zero
2206            ˇone
2207                ˇtwo
2208            ˇ ˇ ˇ  three
2209        ˇ  ˇ  four
2210    "});
2211    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2212    cx.assert_editor_state(indoc! {"
2213        zero
2214        ˇone
2215            ˇtwo
2216        ˇ  threeˇ  four
2217    "});
2218
2219    // Test backspace with line_mode set to true
2220    cx.update_editor(|e, _| e.selections.line_mode = true);
2221    cx.set_state(indoc! {"
2222        The ˇquick ˇbrown
2223        fox jumps over
2224        the lazy dog
2225        ˇThe qu«ick bˇ»rown"});
2226    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2227    cx.assert_editor_state(indoc! {"
2228        ˇfox jumps over
2229        the lazy dogˇ"});
2230}
2231
2232#[gpui::test]
2233async fn test_delete(cx: &mut gpui::TestAppContext) {
2234    init_test(cx, |_| {});
2235
2236    let mut cx = EditorTestContext::new(cx).await;
2237    cx.set_state(indoc! {"
2238        onˇe two three
2239        fou«rˇ» five six
2240        seven «ˇeight nine
2241        »ten
2242    "});
2243    cx.update_editor(|e, cx| e.delete(&Delete, cx));
2244    cx.assert_editor_state(indoc! {"
2245        onˇ two three
2246        fouˇ five six
2247        seven ˇten
2248    "});
2249
2250    // Test backspace with line_mode set to true
2251    cx.update_editor(|e, _| e.selections.line_mode = true);
2252    cx.set_state(indoc! {"
2253        The ˇquick ˇbrown
2254        fox «ˇjum»ps over
2255        the lazy dog
2256        ˇThe qu«ick bˇ»rown"});
2257    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2258    cx.assert_editor_state("ˇthe lazy dogˇ");
2259}
2260
2261#[gpui::test]
2262fn test_delete_line(cx: &mut TestAppContext) {
2263    init_test(cx, |_| {});
2264
2265    let (_, view) = cx.add_window(|cx| {
2266        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2267        build_editor(buffer, cx)
2268    });
2269    view.update(cx, |view, cx| {
2270        view.change_selections(None, cx, |s| {
2271            s.select_display_ranges([
2272                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2273                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
2274                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2275            ])
2276        });
2277        view.delete_line(&DeleteLine, cx);
2278        assert_eq!(view.display_text(cx), "ghi");
2279        assert_eq!(
2280            view.selections.display_ranges(cx),
2281            vec![
2282                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
2283                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
2284            ]
2285        );
2286    });
2287
2288    let (_, view) = cx.add_window(|cx| {
2289        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2290        build_editor(buffer, cx)
2291    });
2292    view.update(cx, |view, cx| {
2293        view.change_selections(None, cx, |s| {
2294            s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
2295        });
2296        view.delete_line(&DeleteLine, cx);
2297        assert_eq!(view.display_text(cx), "ghi\n");
2298        assert_eq!(
2299            view.selections.display_ranges(cx),
2300            vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
2301        );
2302    });
2303}
2304
2305#[gpui::test]
2306fn test_duplicate_line(cx: &mut TestAppContext) {
2307    init_test(cx, |_| {});
2308
2309    let (_, view) = cx.add_window(|cx| {
2310        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2311        build_editor(buffer, cx)
2312    });
2313    view.update(cx, |view, cx| {
2314        view.change_selections(None, cx, |s| {
2315            s.select_display_ranges([
2316                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2317                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2318                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2319                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2320            ])
2321        });
2322        view.duplicate_line(&DuplicateLine, cx);
2323        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
2324        assert_eq!(
2325            view.selections.display_ranges(cx),
2326            vec![
2327                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
2328                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
2329                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2330                DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
2331            ]
2332        );
2333    });
2334
2335    let (_, view) = cx.add_window(|cx| {
2336        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2337        build_editor(buffer, cx)
2338    });
2339    view.update(cx, |view, cx| {
2340        view.change_selections(None, cx, |s| {
2341            s.select_display_ranges([
2342                DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
2343                DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
2344            ])
2345        });
2346        view.duplicate_line(&DuplicateLine, cx);
2347        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
2348        assert_eq!(
2349            view.selections.display_ranges(cx),
2350            vec![
2351                DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
2352                DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
2353            ]
2354        );
2355    });
2356}
2357
2358#[gpui::test]
2359fn test_move_line_up_down(cx: &mut TestAppContext) {
2360    init_test(cx, |_| {});
2361
2362    let (_, view) = cx.add_window(|cx| {
2363        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
2364        build_editor(buffer, cx)
2365    });
2366    view.update(cx, |view, cx| {
2367        view.fold_ranges(
2368            vec![
2369                Point::new(0, 2)..Point::new(1, 2),
2370                Point::new(2, 3)..Point::new(4, 1),
2371                Point::new(7, 0)..Point::new(8, 4),
2372            ],
2373            true,
2374            cx,
2375        );
2376        view.change_selections(None, cx, |s| {
2377            s.select_display_ranges([
2378                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2379                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2380                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2381                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
2382            ])
2383        });
2384        assert_eq!(
2385            view.display_text(cx),
2386            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
2387        );
2388
2389        view.move_line_up(&MoveLineUp, cx);
2390        assert_eq!(
2391            view.display_text(cx),
2392            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
2393        );
2394        assert_eq!(
2395            view.selections.display_ranges(cx),
2396            vec![
2397                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2398                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2399                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
2400                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
2401            ]
2402        );
2403    });
2404
2405    view.update(cx, |view, cx| {
2406        view.move_line_down(&MoveLineDown, cx);
2407        assert_eq!(
2408            view.display_text(cx),
2409            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
2410        );
2411        assert_eq!(
2412            view.selections.display_ranges(cx),
2413            vec![
2414                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
2415                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2416                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2417                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
2418            ]
2419        );
2420    });
2421
2422    view.update(cx, |view, cx| {
2423        view.move_line_down(&MoveLineDown, cx);
2424        assert_eq!(
2425            view.display_text(cx),
2426            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
2427        );
2428        assert_eq!(
2429            view.selections.display_ranges(cx),
2430            vec![
2431                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2432                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2433                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2434                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
2435            ]
2436        );
2437    });
2438
2439    view.update(cx, |view, cx| {
2440        view.move_line_up(&MoveLineUp, cx);
2441        assert_eq!(
2442            view.display_text(cx),
2443            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
2444        );
2445        assert_eq!(
2446            view.selections.display_ranges(cx),
2447            vec![
2448                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
2449                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2450                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
2451                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
2452            ]
2453        );
2454    });
2455}
2456
2457#[gpui::test]
2458fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
2459    init_test(cx, |_| {});
2460
2461    let (_, editor) = cx.add_window(|cx| {
2462        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
2463        build_editor(buffer, cx)
2464    });
2465    editor.update(cx, |editor, cx| {
2466        let snapshot = editor.buffer.read(cx).snapshot(cx);
2467        editor.insert_blocks(
2468            [BlockProperties {
2469                style: BlockStyle::Fixed,
2470                position: snapshot.anchor_after(Point::new(2, 0)),
2471                disposition: BlockDisposition::Below,
2472                height: 1,
2473                render: Arc::new(|_| Empty::new().into_any()),
2474            }],
2475            cx,
2476        );
2477        editor.change_selections(None, cx, |s| {
2478            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
2479        });
2480        editor.move_line_down(&MoveLineDown, cx);
2481    });
2482}
2483
2484#[gpui::test]
2485fn test_transpose(cx: &mut TestAppContext) {
2486    init_test(cx, |_| {});
2487
2488    _ = cx
2489        .add_window(|cx| {
2490            let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
2491
2492            editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
2493            editor.transpose(&Default::default(), cx);
2494            assert_eq!(editor.text(cx), "bac");
2495            assert_eq!(editor.selections.ranges(cx), [2..2]);
2496
2497            editor.transpose(&Default::default(), cx);
2498            assert_eq!(editor.text(cx), "bca");
2499            assert_eq!(editor.selections.ranges(cx), [3..3]);
2500
2501            editor.transpose(&Default::default(), cx);
2502            assert_eq!(editor.text(cx), "bac");
2503            assert_eq!(editor.selections.ranges(cx), [3..3]);
2504
2505            editor
2506        })
2507        .1;
2508
2509    _ = cx
2510        .add_window(|cx| {
2511            let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
2512
2513            editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
2514            editor.transpose(&Default::default(), cx);
2515            assert_eq!(editor.text(cx), "acb\nde");
2516            assert_eq!(editor.selections.ranges(cx), [3..3]);
2517
2518            editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
2519            editor.transpose(&Default::default(), cx);
2520            assert_eq!(editor.text(cx), "acbd\ne");
2521            assert_eq!(editor.selections.ranges(cx), [5..5]);
2522
2523            editor.transpose(&Default::default(), cx);
2524            assert_eq!(editor.text(cx), "acbde\n");
2525            assert_eq!(editor.selections.ranges(cx), [6..6]);
2526
2527            editor.transpose(&Default::default(), cx);
2528            assert_eq!(editor.text(cx), "acbd\ne");
2529            assert_eq!(editor.selections.ranges(cx), [6..6]);
2530
2531            editor
2532        })
2533        .1;
2534
2535    _ = cx
2536        .add_window(|cx| {
2537            let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
2538
2539            editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
2540            editor.transpose(&Default::default(), cx);
2541            assert_eq!(editor.text(cx), "bacd\ne");
2542            assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
2543
2544            editor.transpose(&Default::default(), cx);
2545            assert_eq!(editor.text(cx), "bcade\n");
2546            assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
2547
2548            editor.transpose(&Default::default(), cx);
2549            assert_eq!(editor.text(cx), "bcda\ne");
2550            assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
2551
2552            editor.transpose(&Default::default(), cx);
2553            assert_eq!(editor.text(cx), "bcade\n");
2554            assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
2555
2556            editor.transpose(&Default::default(), cx);
2557            assert_eq!(editor.text(cx), "bcaed\n");
2558            assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
2559
2560            editor
2561        })
2562        .1;
2563
2564    _ = cx
2565        .add_window(|cx| {
2566            let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
2567
2568            editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
2569            editor.transpose(&Default::default(), cx);
2570            assert_eq!(editor.text(cx), "🏀🍐✋");
2571            assert_eq!(editor.selections.ranges(cx), [8..8]);
2572
2573            editor.transpose(&Default::default(), cx);
2574            assert_eq!(editor.text(cx), "🏀✋🍐");
2575            assert_eq!(editor.selections.ranges(cx), [11..11]);
2576
2577            editor.transpose(&Default::default(), cx);
2578            assert_eq!(editor.text(cx), "🏀🍐✋");
2579            assert_eq!(editor.selections.ranges(cx), [11..11]);
2580
2581            editor
2582        })
2583        .1;
2584}
2585
2586#[gpui::test]
2587async fn test_clipboard(cx: &mut gpui::TestAppContext) {
2588    init_test(cx, |_| {});
2589
2590    let mut cx = EditorTestContext::new(cx).await;
2591
2592    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
2593    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2594    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
2595
2596    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
2597    cx.set_state("two ˇfour ˇsix ˇ");
2598    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2599    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
2600
2601    // Paste again but with only two cursors. Since the number of cursors doesn't
2602    // match the number of slices in the clipboard, the entire clipboard text
2603    // is pasted at each cursor.
2604    cx.set_state("ˇtwo one✅ four three six five ˇ");
2605    cx.update_editor(|e, cx| {
2606        e.handle_input("( ", cx);
2607        e.paste(&Paste, cx);
2608        e.handle_input(") ", cx);
2609    });
2610    cx.assert_editor_state(
2611        &([
2612            "( one✅ ",
2613            "three ",
2614            "five ) ˇtwo one✅ four three six five ( one✅ ",
2615            "three ",
2616            "five ) ˇ",
2617        ]
2618        .join("\n")),
2619    );
2620
2621    // Cut with three selections, one of which is full-line.
2622    cx.set_state(indoc! {"
2623        1«2ˇ»3
2624        4ˇ567
2625        «8ˇ»9"});
2626    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2627    cx.assert_editor_state(indoc! {"
2628        1ˇ3
2629        ˇ9"});
2630
2631    // Paste with three selections, noticing how the copied selection that was full-line
2632    // gets inserted before the second cursor.
2633    cx.set_state(indoc! {"
2634        1ˇ3
26352636        «oˇ»ne"});
2637    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2638    cx.assert_editor_state(indoc! {"
2639        12ˇ3
2640        4567
26412642        8ˇne"});
2643
2644    // Copy with a single cursor only, which writes the whole line into the clipboard.
2645    cx.set_state(indoc! {"
2646        The quick brown
2647        fox juˇmps over
2648        the lazy dog"});
2649    cx.update_editor(|e, cx| e.copy(&Copy, cx));
2650    cx.cx.assert_clipboard_content(Some("fox jumps over\n"));
2651
2652    // Paste with three selections, noticing how the copied full-line selection is inserted
2653    // before the empty selections but replaces the selection that is non-empty.
2654    cx.set_state(indoc! {"
2655        Tˇhe quick brown
2656        «foˇ»x jumps over
2657        tˇhe lazy dog"});
2658    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2659    cx.assert_editor_state(indoc! {"
2660        fox jumps over
2661        Tˇhe quick brown
2662        fox jumps over
2663        ˇx jumps over
2664        fox jumps over
2665        tˇhe lazy dog"});
2666}
2667
2668#[gpui::test]
2669async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
2670    init_test(cx, |_| {});
2671
2672    let mut cx = EditorTestContext::new(cx).await;
2673    let language = Arc::new(Language::new(
2674        LanguageConfig::default(),
2675        Some(tree_sitter_rust::language()),
2676    ));
2677    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
2678
2679    // Cut an indented block, without the leading whitespace.
2680    cx.set_state(indoc! {"
2681        const a: B = (
2682            c(),
2683            «d(
2684                e,
2685                f
2686            )ˇ»
2687        );
2688    "});
2689    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2690    cx.assert_editor_state(indoc! {"
2691        const a: B = (
2692            c(),
2693            ˇ
2694        );
2695    "});
2696
2697    // Paste it at the same position.
2698    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2699    cx.assert_editor_state(indoc! {"
2700        const a: B = (
2701            c(),
2702            d(
2703                e,
2704                f
27052706        );
2707    "});
2708
2709    // Paste it at a line with a lower indent level.
2710    cx.set_state(indoc! {"
2711        ˇ
2712        const a: B = (
2713            c(),
2714        );
2715    "});
2716    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2717    cx.assert_editor_state(indoc! {"
2718        d(
2719            e,
2720            f
27212722        const a: B = (
2723            c(),
2724        );
2725    "});
2726
2727    // Cut an indented block, with the leading whitespace.
2728    cx.set_state(indoc! {"
2729        const a: B = (
2730            c(),
2731        «    d(
2732                e,
2733                f
2734            )
2735        ˇ»);
2736    "});
2737    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2738    cx.assert_editor_state(indoc! {"
2739        const a: B = (
2740            c(),
2741        ˇ);
2742    "});
2743
2744    // Paste it at the same position.
2745    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2746    cx.assert_editor_state(indoc! {"
2747        const a: B = (
2748            c(),
2749            d(
2750                e,
2751                f
2752            )
2753        ˇ);
2754    "});
2755
2756    // Paste it at a line with a higher indent level.
2757    cx.set_state(indoc! {"
2758        const a: B = (
2759            c(),
2760            d(
2761                e,
27622763            )
2764        );
2765    "});
2766    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2767    cx.assert_editor_state(indoc! {"
2768        const a: B = (
2769            c(),
2770            d(
2771                e,
2772                f    d(
2773                    e,
2774                    f
2775                )
2776        ˇ
2777            )
2778        );
2779    "});
2780}
2781
2782#[gpui::test]
2783fn test_select_all(cx: &mut TestAppContext) {
2784    init_test(cx, |_| {});
2785
2786    let (_, view) = cx.add_window(|cx| {
2787        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
2788        build_editor(buffer, cx)
2789    });
2790    view.update(cx, |view, cx| {
2791        view.select_all(&SelectAll, cx);
2792        assert_eq!(
2793            view.selections.display_ranges(cx),
2794            &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
2795        );
2796    });
2797}
2798
2799#[gpui::test]
2800fn test_select_line(cx: &mut TestAppContext) {
2801    init_test(cx, |_| {});
2802
2803    let (_, view) = cx.add_window(|cx| {
2804        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
2805        build_editor(buffer, cx)
2806    });
2807    view.update(cx, |view, cx| {
2808        view.change_selections(None, cx, |s| {
2809            s.select_display_ranges([
2810                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2811                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2812                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2813                DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
2814            ])
2815        });
2816        view.select_line(&SelectLine, cx);
2817        assert_eq!(
2818            view.selections.display_ranges(cx),
2819            vec![
2820                DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
2821                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
2822            ]
2823        );
2824    });
2825
2826    view.update(cx, |view, cx| {
2827        view.select_line(&SelectLine, cx);
2828        assert_eq!(
2829            view.selections.display_ranges(cx),
2830            vec![
2831                DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
2832                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
2833            ]
2834        );
2835    });
2836
2837    view.update(cx, |view, cx| {
2838        view.select_line(&SelectLine, cx);
2839        assert_eq!(
2840            view.selections.display_ranges(cx),
2841            vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
2842        );
2843    });
2844}
2845
2846#[gpui::test]
2847fn test_split_selection_into_lines(cx: &mut TestAppContext) {
2848    init_test(cx, |_| {});
2849
2850    let (_, view) = cx.add_window(|cx| {
2851        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
2852        build_editor(buffer, cx)
2853    });
2854    view.update(cx, |view, cx| {
2855        view.fold_ranges(
2856            vec![
2857                Point::new(0, 2)..Point::new(1, 2),
2858                Point::new(2, 3)..Point::new(4, 1),
2859                Point::new(7, 0)..Point::new(8, 4),
2860            ],
2861            true,
2862            cx,
2863        );
2864        view.change_selections(None, cx, |s| {
2865            s.select_display_ranges([
2866                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2867                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2868                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2869                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
2870            ])
2871        });
2872        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
2873    });
2874
2875    view.update(cx, |view, cx| {
2876        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
2877        assert_eq!(
2878            view.display_text(cx),
2879            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
2880        );
2881        assert_eq!(
2882            view.selections.display_ranges(cx),
2883            [
2884                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2885                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2886                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
2887                DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
2888            ]
2889        );
2890    });
2891
2892    view.update(cx, |view, cx| {
2893        view.change_selections(None, cx, |s| {
2894            s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
2895        });
2896        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
2897        assert_eq!(
2898            view.display_text(cx),
2899            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
2900        );
2901        assert_eq!(
2902            view.selections.display_ranges(cx),
2903            [
2904                DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
2905                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
2906                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
2907                DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
2908                DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
2909                DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
2910                DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
2911                DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
2912            ]
2913        );
2914    });
2915}
2916
2917#[gpui::test]
2918fn test_add_selection_above_below(cx: &mut TestAppContext) {
2919    init_test(cx, |_| {});
2920
2921    let (_, view) = cx.add_window(|cx| {
2922        let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
2923        build_editor(buffer, cx)
2924    });
2925
2926    view.update(cx, |view, cx| {
2927        view.change_selections(None, cx, |s| {
2928            s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
2929        });
2930    });
2931    view.update(cx, |view, cx| {
2932        view.add_selection_above(&AddSelectionAbove, cx);
2933        assert_eq!(
2934            view.selections.display_ranges(cx),
2935            vec![
2936                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
2937                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
2938            ]
2939        );
2940    });
2941
2942    view.update(cx, |view, cx| {
2943        view.add_selection_above(&AddSelectionAbove, cx);
2944        assert_eq!(
2945            view.selections.display_ranges(cx),
2946            vec![
2947                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
2948                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
2949            ]
2950        );
2951    });
2952
2953    view.update(cx, |view, cx| {
2954        view.add_selection_below(&AddSelectionBelow, cx);
2955        assert_eq!(
2956            view.selections.display_ranges(cx),
2957            vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
2958        );
2959
2960        view.undo_selection(&UndoSelection, cx);
2961        assert_eq!(
2962            view.selections.display_ranges(cx),
2963            vec![
2964                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
2965                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
2966            ]
2967        );
2968
2969        view.redo_selection(&RedoSelection, cx);
2970        assert_eq!(
2971            view.selections.display_ranges(cx),
2972            vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
2973        );
2974    });
2975
2976    view.update(cx, |view, cx| {
2977        view.add_selection_below(&AddSelectionBelow, cx);
2978        assert_eq!(
2979            view.selections.display_ranges(cx),
2980            vec![
2981                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
2982                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
2983            ]
2984        );
2985    });
2986
2987    view.update(cx, |view, cx| {
2988        view.add_selection_below(&AddSelectionBelow, cx);
2989        assert_eq!(
2990            view.selections.display_ranges(cx),
2991            vec![
2992                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
2993                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
2994            ]
2995        );
2996    });
2997
2998    view.update(cx, |view, cx| {
2999        view.change_selections(None, cx, |s| {
3000            s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
3001        });
3002    });
3003    view.update(cx, |view, cx| {
3004        view.add_selection_below(&AddSelectionBelow, cx);
3005        assert_eq!(
3006            view.selections.display_ranges(cx),
3007            vec![
3008                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
3009                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
3010            ]
3011        );
3012    });
3013
3014    view.update(cx, |view, cx| {
3015        view.add_selection_below(&AddSelectionBelow, cx);
3016        assert_eq!(
3017            view.selections.display_ranges(cx),
3018            vec![
3019                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
3020                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
3021            ]
3022        );
3023    });
3024
3025    view.update(cx, |view, cx| {
3026        view.add_selection_above(&AddSelectionAbove, cx);
3027        assert_eq!(
3028            view.selections.display_ranges(cx),
3029            vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
3030        );
3031    });
3032
3033    view.update(cx, |view, cx| {
3034        view.add_selection_above(&AddSelectionAbove, cx);
3035        assert_eq!(
3036            view.selections.display_ranges(cx),
3037            vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
3038        );
3039    });
3040
3041    view.update(cx, |view, cx| {
3042        view.change_selections(None, cx, |s| {
3043            s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
3044        });
3045        view.add_selection_below(&AddSelectionBelow, cx);
3046        assert_eq!(
3047            view.selections.display_ranges(cx),
3048            vec![
3049                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
3050                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
3051                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
3052            ]
3053        );
3054    });
3055
3056    view.update(cx, |view, cx| {
3057        view.add_selection_below(&AddSelectionBelow, cx);
3058        assert_eq!(
3059            view.selections.display_ranges(cx),
3060            vec![
3061                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
3062                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
3063                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
3064                DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
3065            ]
3066        );
3067    });
3068
3069    view.update(cx, |view, cx| {
3070        view.add_selection_above(&AddSelectionAbove, cx);
3071        assert_eq!(
3072            view.selections.display_ranges(cx),
3073            vec![
3074                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
3075                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
3076                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
3077            ]
3078        );
3079    });
3080
3081    view.update(cx, |view, cx| {
3082        view.change_selections(None, cx, |s| {
3083            s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
3084        });
3085    });
3086    view.update(cx, |view, cx| {
3087        view.add_selection_above(&AddSelectionAbove, cx);
3088        assert_eq!(
3089            view.selections.display_ranges(cx),
3090            vec![
3091                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
3092                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
3093                DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
3094                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
3095            ]
3096        );
3097    });
3098
3099    view.update(cx, |view, cx| {
3100        view.add_selection_below(&AddSelectionBelow, cx);
3101        assert_eq!(
3102            view.selections.display_ranges(cx),
3103            vec![
3104                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
3105                DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
3106                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
3107            ]
3108        );
3109    });
3110}
3111
3112#[gpui::test]
3113async fn test_select_next(cx: &mut gpui::TestAppContext) {
3114    init_test(cx, |_| {});
3115
3116    let mut cx = EditorTestContext::new(cx).await;
3117    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
3118
3119    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
3120    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3121
3122    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
3123    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
3124
3125    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
3126    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3127
3128    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
3129    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
3130
3131    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
3132    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
3133
3134    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
3135    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
3136}
3137
3138#[gpui::test]
3139async fn test_select_previous(cx: &mut gpui::TestAppContext) {
3140    init_test(cx, |_| {});
3141    {
3142        // `Select previous` without a selection (selects wordwise)
3143        let mut cx = EditorTestContext::new(cx).await;
3144        cx.set_state("abc\nˇabc abc\ndefabc\nabc");
3145
3146        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3147        cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3148
3149        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3150        cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
3151
3152        cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
3153        cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3154
3155        cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
3156        cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
3157
3158        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3159        cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
3160
3161        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3162        cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
3163    }
3164    {
3165        // `Select previous` with a selection
3166        let mut cx = EditorTestContext::new(cx).await;
3167        cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
3168
3169        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3170        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
3171
3172        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3173        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
3174
3175        cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
3176        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
3177
3178        cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
3179        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
3180
3181        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3182        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
3183
3184        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3185        cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
3186    }
3187}
3188
3189#[gpui::test]
3190async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
3191    init_test(cx, |_| {});
3192
3193    let language = Arc::new(Language::new(
3194        LanguageConfig::default(),
3195        Some(tree_sitter_rust::language()),
3196    ));
3197
3198    let text = r#"
3199        use mod1::mod2::{mod3, mod4};
3200
3201        fn fn_1(param1: bool, param2: &str) {
3202            let var1 = "text";
3203        }
3204    "#
3205    .unindent();
3206
3207    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3208    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3209    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
3210    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
3211        .await;
3212
3213    view.update(cx, |view, cx| {
3214        view.change_selections(None, cx, |s| {
3215            s.select_display_ranges([
3216                DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3217                DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3218                DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3219            ]);
3220        });
3221        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3222    });
3223    assert_eq!(
3224        view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
3225        &[
3226            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
3227            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3228            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
3229        ]
3230    );
3231
3232    view.update(cx, |view, cx| {
3233        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3234    });
3235    assert_eq!(
3236        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3237        &[
3238            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3239            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
3240        ]
3241    );
3242
3243    view.update(cx, |view, cx| {
3244        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3245    });
3246    assert_eq!(
3247        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3248        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
3249    );
3250
3251    // Trying to expand the selected syntax node one more time has no effect.
3252    view.update(cx, |view, cx| {
3253        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3254    });
3255    assert_eq!(
3256        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3257        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
3258    );
3259
3260    view.update(cx, |view, cx| {
3261        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3262    });
3263    assert_eq!(
3264        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3265        &[
3266            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3267            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
3268        ]
3269    );
3270
3271    view.update(cx, |view, cx| {
3272        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3273    });
3274    assert_eq!(
3275        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3276        &[
3277            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
3278            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3279            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
3280        ]
3281    );
3282
3283    view.update(cx, |view, cx| {
3284        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3285    });
3286    assert_eq!(
3287        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3288        &[
3289            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3290            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3291            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3292        ]
3293    );
3294
3295    // Trying to shrink the selected syntax node one more time has no effect.
3296    view.update(cx, |view, cx| {
3297        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3298    });
3299    assert_eq!(
3300        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3301        &[
3302            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3303            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3304            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3305        ]
3306    );
3307
3308    // Ensure that we keep expanding the selection if the larger selection starts or ends within
3309    // a fold.
3310    view.update(cx, |view, cx| {
3311        view.fold_ranges(
3312            vec![
3313                Point::new(0, 21)..Point::new(0, 24),
3314                Point::new(3, 20)..Point::new(3, 22),
3315            ],
3316            true,
3317            cx,
3318        );
3319        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3320    });
3321    assert_eq!(
3322        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3323        &[
3324            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3325            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3326            DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
3327        ]
3328    );
3329}
3330
3331#[gpui::test]
3332async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
3333    init_test(cx, |_| {});
3334
3335    let language = Arc::new(
3336        Language::new(
3337            LanguageConfig {
3338                brackets: BracketPairConfig {
3339                    pairs: vec![
3340                        BracketPair {
3341                            start: "{".to_string(),
3342                            end: "}".to_string(),
3343                            close: false,
3344                            newline: true,
3345                        },
3346                        BracketPair {
3347                            start: "(".to_string(),
3348                            end: ")".to_string(),
3349                            close: false,
3350                            newline: true,
3351                        },
3352                    ],
3353                    ..Default::default()
3354                },
3355                ..Default::default()
3356            },
3357            Some(tree_sitter_rust::language()),
3358        )
3359        .with_indents_query(
3360            r#"
3361                (_ "(" ")" @end) @indent
3362                (_ "{" "}" @end) @indent
3363            "#,
3364        )
3365        .unwrap(),
3366    );
3367
3368    let text = "fn a() {}";
3369
3370    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3371    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3372    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3373    editor
3374        .condition(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
3375        .await;
3376
3377    editor.update(cx, |editor, cx| {
3378        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
3379        editor.newline(&Newline, cx);
3380        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
3381        assert_eq!(
3382            editor.selections.ranges(cx),
3383            &[
3384                Point::new(1, 4)..Point::new(1, 4),
3385                Point::new(3, 4)..Point::new(3, 4),
3386                Point::new(5, 0)..Point::new(5, 0)
3387            ]
3388        );
3389    });
3390}
3391
3392#[gpui::test]
3393async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
3394    init_test(cx, |_| {});
3395
3396    let mut cx = EditorTestContext::new(cx).await;
3397
3398    let language = Arc::new(Language::new(
3399        LanguageConfig {
3400            brackets: BracketPairConfig {
3401                pairs: vec![
3402                    BracketPair {
3403                        start: "{".to_string(),
3404                        end: "}".to_string(),
3405                        close: true,
3406                        newline: true,
3407                    },
3408                    BracketPair {
3409                        start: "(".to_string(),
3410                        end: ")".to_string(),
3411                        close: true,
3412                        newline: true,
3413                    },
3414                    BracketPair {
3415                        start: "/*".to_string(),
3416                        end: " */".to_string(),
3417                        close: true,
3418                        newline: true,
3419                    },
3420                    BracketPair {
3421                        start: "[".to_string(),
3422                        end: "]".to_string(),
3423                        close: false,
3424                        newline: true,
3425                    },
3426                    BracketPair {
3427                        start: "\"".to_string(),
3428                        end: "\"".to_string(),
3429                        close: true,
3430                        newline: false,
3431                    },
3432                ],
3433                ..Default::default()
3434            },
3435            autoclose_before: "})]".to_string(),
3436            ..Default::default()
3437        },
3438        Some(tree_sitter_rust::language()),
3439    ));
3440
3441    let registry = Arc::new(LanguageRegistry::test());
3442    registry.add(language.clone());
3443    cx.update_buffer(|buffer, cx| {
3444        buffer.set_language_registry(registry);
3445        buffer.set_language(Some(language), cx);
3446    });
3447
3448    cx.set_state(
3449        &r#"
3450            🏀ˇ
3451            εˇ
3452            ❤️ˇ
3453        "#
3454        .unindent(),
3455    );
3456
3457    // autoclose multiple nested brackets at multiple cursors
3458    cx.update_editor(|view, cx| {
3459        view.handle_input("{", cx);
3460        view.handle_input("{", cx);
3461        view.handle_input("{", cx);
3462    });
3463    cx.assert_editor_state(
3464        &"
3465            🏀{{{ˇ}}}
3466            ε{{{ˇ}}}
3467            ❤️{{{ˇ}}}
3468        "
3469        .unindent(),
3470    );
3471
3472    // insert a different closing bracket
3473    cx.update_editor(|view, cx| {
3474        view.handle_input(")", cx);
3475    });
3476    cx.assert_editor_state(
3477        &"
3478            🏀{{{)ˇ}}}
3479            ε{{{)ˇ}}}
3480            ❤️{{{)ˇ}}}
3481        "
3482        .unindent(),
3483    );
3484
3485    // skip over the auto-closed brackets when typing a closing bracket
3486    cx.update_editor(|view, cx| {
3487        view.move_right(&MoveRight, cx);
3488        view.handle_input("}", cx);
3489        view.handle_input("}", cx);
3490        view.handle_input("}", cx);
3491    });
3492    cx.assert_editor_state(
3493        &"
3494            🏀{{{)}}}}ˇ
3495            ε{{{)}}}}ˇ
3496            ❤️{{{)}}}}ˇ
3497        "
3498        .unindent(),
3499    );
3500
3501    // autoclose multi-character pairs
3502    cx.set_state(
3503        &"
3504            ˇ
3505            ˇ
3506        "
3507        .unindent(),
3508    );
3509    cx.update_editor(|view, cx| {
3510        view.handle_input("/", cx);
3511        view.handle_input("*", cx);
3512    });
3513    cx.assert_editor_state(
3514        &"
3515            /*ˇ */
3516            /*ˇ */
3517        "
3518        .unindent(),
3519    );
3520
3521    // one cursor autocloses a multi-character pair, one cursor
3522    // does not autoclose.
3523    cx.set_state(
3524        &"
35253526            ˇ
3527        "
3528        .unindent(),
3529    );
3530    cx.update_editor(|view, cx| view.handle_input("*", cx));
3531    cx.assert_editor_state(
3532        &"
3533            /*ˇ */
35343535        "
3536        .unindent(),
3537    );
3538
3539    // Don't autoclose if the next character isn't whitespace and isn't
3540    // listed in the language's "autoclose_before" section.
3541    cx.set_state("ˇa b");
3542    cx.update_editor(|view, cx| view.handle_input("{", cx));
3543    cx.assert_editor_state("{ˇa b");
3544
3545    // Don't autoclose if `close` is false for the bracket pair
3546    cx.set_state("ˇ");
3547    cx.update_editor(|view, cx| view.handle_input("[", cx));
3548    cx.assert_editor_state("");
3549
3550    // Surround with brackets if text is selected
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    // Autclose pair where the start and end characters are the same
3556    cx.set_state("");
3557    cx.update_editor(|view, cx| view.handle_input("\"", cx));
3558    cx.assert_editor_state("a\"ˇ\"");
3559    cx.update_editor(|view, cx| view.handle_input("\"", cx));
3560    cx.assert_editor_state("a\"\"ˇ");
3561}
3562
3563#[gpui::test]
3564async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
3565    init_test(cx, |_| {});
3566
3567    let mut cx = EditorTestContext::new(cx).await;
3568
3569    let html_language = Arc::new(
3570        Language::new(
3571            LanguageConfig {
3572                name: "HTML".into(),
3573                brackets: BracketPairConfig {
3574                    pairs: vec![
3575                        BracketPair {
3576                            start: "<".into(),
3577                            end: ">".into(),
3578                            close: true,
3579                            ..Default::default()
3580                        },
3581                        BracketPair {
3582                            start: "{".into(),
3583                            end: "}".into(),
3584                            close: true,
3585                            ..Default::default()
3586                        },
3587                        BracketPair {
3588                            start: "(".into(),
3589                            end: ")".into(),
3590                            close: true,
3591                            ..Default::default()
3592                        },
3593                    ],
3594                    ..Default::default()
3595                },
3596                autoclose_before: "})]>".into(),
3597                ..Default::default()
3598            },
3599            Some(tree_sitter_html::language()),
3600        )
3601        .with_injection_query(
3602            r#"
3603            (script_element
3604                (raw_text) @content
3605                (#set! "language" "javascript"))
3606            "#,
3607        )
3608        .unwrap(),
3609    );
3610
3611    let javascript_language = Arc::new(Language::new(
3612        LanguageConfig {
3613            name: "JavaScript".into(),
3614            brackets: BracketPairConfig {
3615                pairs: vec![
3616                    BracketPair {
3617                        start: "/*".into(),
3618                        end: " */".into(),
3619                        close: true,
3620                        ..Default::default()
3621                    },
3622                    BracketPair {
3623                        start: "{".into(),
3624                        end: "}".into(),
3625                        close: true,
3626                        ..Default::default()
3627                    },
3628                    BracketPair {
3629                        start: "(".into(),
3630                        end: ")".into(),
3631                        close: true,
3632                        ..Default::default()
3633                    },
3634                ],
3635                ..Default::default()
3636            },
3637            autoclose_before: "})]>".into(),
3638            ..Default::default()
3639        },
3640        Some(tree_sitter_javascript::language()),
3641    ));
3642
3643    let registry = Arc::new(LanguageRegistry::test());
3644    registry.add(html_language.clone());
3645    registry.add(javascript_language.clone());
3646
3647    cx.update_buffer(|buffer, cx| {
3648        buffer.set_language_registry(registry);
3649        buffer.set_language(Some(html_language), cx);
3650    });
3651
3652    cx.set_state(
3653        &r#"
3654            <body>ˇ
3655                <script>
3656                    var x = 1;ˇ
3657                </script>
3658            </body>ˇ
3659        "#
3660        .unindent(),
3661    );
3662
3663    // Precondition: different languages are active at different locations.
3664    cx.update_editor(|editor, cx| {
3665        let snapshot = editor.snapshot(cx);
3666        let cursors = editor.selections.ranges::<usize>(cx);
3667        let languages = cursors
3668            .iter()
3669            .map(|c| snapshot.language_at(c.start).unwrap().name())
3670            .collect::<Vec<_>>();
3671        assert_eq!(
3672            languages,
3673            &["HTML".into(), "JavaScript".into(), "HTML".into()]
3674        );
3675    });
3676
3677    // Angle brackets autoclose in HTML, but not JavaScript.
3678    cx.update_editor(|editor, cx| {
3679        editor.handle_input("<", cx);
3680        editor.handle_input("a", cx);
3681    });
3682    cx.assert_editor_state(
3683        &r#"
3684            <body><aˇ>
3685                <script>
3686                    var x = 1;<aˇ
3687                </script>
3688            </body><aˇ>
3689        "#
3690        .unindent(),
3691    );
3692
3693    // Curly braces and parens autoclose in both HTML and JavaScript.
3694    cx.update_editor(|editor, cx| {
3695        editor.handle_input(" b=", cx);
3696        editor.handle_input("{", cx);
3697        editor.handle_input("c", cx);
3698        editor.handle_input("(", cx);
3699    });
3700    cx.assert_editor_state(
3701        &r#"
3702            <body><a b={c(ˇ)}>
3703                <script>
3704                    var x = 1;<a b={c(ˇ)}
3705                </script>
3706            </body><a b={c(ˇ)}>
3707        "#
3708        .unindent(),
3709    );
3710
3711    // Brackets that were already autoclosed are skipped.
3712    cx.update_editor(|editor, cx| {
3713        editor.handle_input(")", cx);
3714        editor.handle_input("d", cx);
3715        editor.handle_input("}", cx);
3716    });
3717    cx.assert_editor_state(
3718        &r#"
3719            <body><a b={c()d}ˇ>
3720                <script>
3721                    var x = 1;<a b={c()d}ˇ
3722                </script>
3723            </body><a b={c()d}ˇ>
3724        "#
3725        .unindent(),
3726    );
3727    cx.update_editor(|editor, cx| {
3728        editor.handle_input(">", cx);
3729    });
3730    cx.assert_editor_state(
3731        &r#"
3732            <body><a b={c()d}>ˇ
3733                <script>
3734                    var x = 1;<a b={c()d}>ˇ
3735                </script>
3736            </body><a b={c()d}>ˇ
3737        "#
3738        .unindent(),
3739    );
3740
3741    // Reset
3742    cx.set_state(
3743        &r#"
3744            <body>ˇ
3745                <script>
3746                    var x = 1;ˇ
3747                </script>
3748            </body>ˇ
3749        "#
3750        .unindent(),
3751    );
3752
3753    cx.update_editor(|editor, cx| {
3754        editor.handle_input("<", cx);
3755    });
3756    cx.assert_editor_state(
3757        &r#"
3758            <body><ˇ>
3759                <script>
3760                    var x = 1;<ˇ
3761                </script>
3762            </body><ˇ>
3763        "#
3764        .unindent(),
3765    );
3766
3767    // When backspacing, the closing angle brackets are removed.
3768    cx.update_editor(|editor, cx| {
3769        editor.backspace(&Backspace, cx);
3770    });
3771    cx.assert_editor_state(
3772        &r#"
3773            <body>ˇ
3774                <script>
3775                    var x = 1;ˇ
3776                </script>
3777            </body>ˇ
3778        "#
3779        .unindent(),
3780    );
3781
3782    // Block comments autoclose in JavaScript, but not HTML.
3783    cx.update_editor(|editor, cx| {
3784        editor.handle_input("/", cx);
3785        editor.handle_input("*", cx);
3786    });
3787    cx.assert_editor_state(
3788        &r#"
3789            <body>/*ˇ
3790                <script>
3791                    var x = 1;/*ˇ */
3792                </script>
3793            </body>/*ˇ
3794        "#
3795        .unindent(),
3796    );
3797}
3798
3799#[gpui::test]
3800async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
3801    init_test(cx, |_| {});
3802
3803    let mut cx = EditorTestContext::new(cx).await;
3804
3805    let rust_language = Arc::new(
3806        Language::new(
3807            LanguageConfig {
3808                name: "Rust".into(),
3809                brackets: serde_json::from_value(json!([
3810                    { "start": "{", "end": "}", "close": true, "newline": true },
3811                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
3812                ]))
3813                .unwrap(),
3814                autoclose_before: "})]>".into(),
3815                ..Default::default()
3816            },
3817            Some(tree_sitter_rust::language()),
3818        )
3819        .with_override_query("(string_literal) @string")
3820        .unwrap(),
3821    );
3822
3823    let registry = Arc::new(LanguageRegistry::test());
3824    registry.add(rust_language.clone());
3825
3826    cx.update_buffer(|buffer, cx| {
3827        buffer.set_language_registry(registry);
3828        buffer.set_language(Some(rust_language), cx);
3829    });
3830
3831    cx.set_state(
3832        &r#"
3833            let x = ˇ
3834        "#
3835        .unindent(),
3836    );
3837
3838    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
3839    cx.update_editor(|editor, cx| {
3840        editor.handle_input("\"", cx);
3841    });
3842    cx.assert_editor_state(
3843        &r#"
3844            let x = "ˇ"
3845        "#
3846        .unindent(),
3847    );
3848
3849    // Inserting another quotation mark. The cursor moves across the existing
3850    // automatically-inserted quotation mark.
3851    cx.update_editor(|editor, cx| {
3852        editor.handle_input("\"", cx);
3853    });
3854    cx.assert_editor_state(
3855        &r#"
3856            let x = ""ˇ
3857        "#
3858        .unindent(),
3859    );
3860
3861    // Reset
3862    cx.set_state(
3863        &r#"
3864            let x = ˇ
3865        "#
3866        .unindent(),
3867    );
3868
3869    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
3870    cx.update_editor(|editor, cx| {
3871        editor.handle_input("\"", cx);
3872        editor.handle_input(" ", cx);
3873        editor.move_left(&Default::default(), cx);
3874        editor.handle_input("\\", cx);
3875        editor.handle_input("\"", cx);
3876    });
3877    cx.assert_editor_state(
3878        &r#"
3879            let x = "\"ˇ "
3880        "#
3881        .unindent(),
3882    );
3883
3884    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
3885    // mark. Nothing is inserted.
3886    cx.update_editor(|editor, cx| {
3887        editor.move_right(&Default::default(), cx);
3888        editor.handle_input("\"", cx);
3889    });
3890    cx.assert_editor_state(
3891        &r#"
3892            let x = "\" "ˇ
3893        "#
3894        .unindent(),
3895    );
3896}
3897
3898#[gpui::test]
3899async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
3900    init_test(cx, |_| {});
3901
3902    let language = Arc::new(Language::new(
3903        LanguageConfig {
3904            brackets: BracketPairConfig {
3905                pairs: vec![
3906                    BracketPair {
3907                        start: "{".to_string(),
3908                        end: "}".to_string(),
3909                        close: true,
3910                        newline: true,
3911                    },
3912                    BracketPair {
3913                        start: "/* ".to_string(),
3914                        end: "*/".to_string(),
3915                        close: true,
3916                        ..Default::default()
3917                    },
3918                ],
3919                ..Default::default()
3920            },
3921            ..Default::default()
3922        },
3923        Some(tree_sitter_rust::language()),
3924    ));
3925
3926    let text = r#"
3927        a
3928        b
3929        c
3930    "#
3931    .unindent();
3932
3933    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3934    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3935    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
3936    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
3937        .await;
3938
3939    view.update(cx, |view, cx| {
3940        view.change_selections(None, cx, |s| {
3941            s.select_display_ranges([
3942                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3943                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
3944                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
3945            ])
3946        });
3947
3948        view.handle_input("{", cx);
3949        view.handle_input("{", cx);
3950        view.handle_input("{", cx);
3951        assert_eq!(
3952            view.text(cx),
3953            "
3954                {{{a}}}
3955                {{{b}}}
3956                {{{c}}}
3957            "
3958            .unindent()
3959        );
3960        assert_eq!(
3961            view.selections.display_ranges(cx),
3962            [
3963                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
3964                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
3965                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
3966            ]
3967        );
3968
3969        view.undo(&Undo, cx);
3970        view.undo(&Undo, cx);
3971        view.undo(&Undo, cx);
3972        assert_eq!(
3973            view.text(cx),
3974            "
3975                a
3976                b
3977                c
3978            "
3979            .unindent()
3980        );
3981        assert_eq!(
3982            view.selections.display_ranges(cx),
3983            [
3984                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3985                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
3986                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
3987            ]
3988        );
3989
3990        // Ensure inserting the first character of a multi-byte bracket pair
3991        // doesn't surround the selections with the bracket.
3992        view.handle_input("/", cx);
3993        assert_eq!(
3994            view.text(cx),
3995            "
3996                /
3997                /
3998                /
3999            "
4000            .unindent()
4001        );
4002        assert_eq!(
4003            view.selections.display_ranges(cx),
4004            [
4005                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
4006                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
4007                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
4008            ]
4009        );
4010
4011        view.undo(&Undo, cx);
4012        assert_eq!(
4013            view.text(cx),
4014            "
4015                a
4016                b
4017                c
4018            "
4019            .unindent()
4020        );
4021        assert_eq!(
4022            view.selections.display_ranges(cx),
4023            [
4024                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
4025                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
4026                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
4027            ]
4028        );
4029
4030        // Ensure inserting the last character of a multi-byte bracket pair
4031        // doesn't surround the selections with the bracket.
4032        view.handle_input("*", cx);
4033        assert_eq!(
4034            view.text(cx),
4035            "
4036                *
4037                *
4038                *
4039            "
4040            .unindent()
4041        );
4042        assert_eq!(
4043            view.selections.display_ranges(cx),
4044            [
4045                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
4046                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
4047                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
4048            ]
4049        );
4050    });
4051}
4052
4053#[gpui::test]
4054async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
4055    init_test(cx, |_| {});
4056
4057    let language = Arc::new(Language::new(
4058        LanguageConfig {
4059            brackets: BracketPairConfig {
4060                pairs: vec![BracketPair {
4061                    start: "{".to_string(),
4062                    end: "}".to_string(),
4063                    close: true,
4064                    newline: true,
4065                }],
4066                ..Default::default()
4067            },
4068            autoclose_before: "}".to_string(),
4069            ..Default::default()
4070        },
4071        Some(tree_sitter_rust::language()),
4072    ));
4073
4074    let text = r#"
4075        a
4076        b
4077        c
4078    "#
4079    .unindent();
4080
4081    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
4082    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4083    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4084    editor
4085        .condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
4086        .await;
4087
4088    editor.update(cx, |editor, cx| {
4089        editor.change_selections(None, cx, |s| {
4090            s.select_ranges([
4091                Point::new(0, 1)..Point::new(0, 1),
4092                Point::new(1, 1)..Point::new(1, 1),
4093                Point::new(2, 1)..Point::new(2, 1),
4094            ])
4095        });
4096
4097        editor.handle_input("{", cx);
4098        editor.handle_input("{", cx);
4099        editor.handle_input("_", cx);
4100        assert_eq!(
4101            editor.text(cx),
4102            "
4103                a{{_}}
4104                b{{_}}
4105                c{{_}}
4106            "
4107            .unindent()
4108        );
4109        assert_eq!(
4110            editor.selections.ranges::<Point>(cx),
4111            [
4112                Point::new(0, 4)..Point::new(0, 4),
4113                Point::new(1, 4)..Point::new(1, 4),
4114                Point::new(2, 4)..Point::new(2, 4)
4115            ]
4116        );
4117
4118        editor.backspace(&Default::default(), cx);
4119        editor.backspace(&Default::default(), cx);
4120        assert_eq!(
4121            editor.text(cx),
4122            "
4123                a{}
4124                b{}
4125                c{}
4126            "
4127            .unindent()
4128        );
4129        assert_eq!(
4130            editor.selections.ranges::<Point>(cx),
4131            [
4132                Point::new(0, 2)..Point::new(0, 2),
4133                Point::new(1, 2)..Point::new(1, 2),
4134                Point::new(2, 2)..Point::new(2, 2)
4135            ]
4136        );
4137
4138        editor.delete_to_previous_word_start(&Default::default(), cx);
4139        assert_eq!(
4140            editor.text(cx),
4141            "
4142                a
4143                b
4144                c
4145            "
4146            .unindent()
4147        );
4148        assert_eq!(
4149            editor.selections.ranges::<Point>(cx),
4150            [
4151                Point::new(0, 1)..Point::new(0, 1),
4152                Point::new(1, 1)..Point::new(1, 1),
4153                Point::new(2, 1)..Point::new(2, 1)
4154            ]
4155        );
4156    });
4157}
4158
4159#[gpui::test]
4160async fn test_snippets(cx: &mut gpui::TestAppContext) {
4161    init_test(cx, |_| {});
4162
4163    let (text, insertion_ranges) = marked_text_ranges(
4164        indoc! {"
4165            a.ˇ b
4166            a.ˇ b
4167            a.ˇ b
4168        "},
4169        false,
4170    );
4171
4172    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
4173    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4174
4175    editor.update(cx, |editor, cx| {
4176        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
4177
4178        editor
4179            .insert_snippet(&insertion_ranges, snippet, cx)
4180            .unwrap();
4181
4182        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
4183            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
4184            assert_eq!(editor.text(cx), expected_text);
4185            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
4186        }
4187
4188        assert(
4189            editor,
4190            cx,
4191            indoc! {"
4192                a.f(«one», two, «three») b
4193                a.f(«one», two, «three») b
4194                a.f(«one», two, «three») b
4195            "},
4196        );
4197
4198        // Can't move earlier than the first tab stop
4199        assert!(!editor.move_to_prev_snippet_tabstop(cx));
4200        assert(
4201            editor,
4202            cx,
4203            indoc! {"
4204                a.f(«one», two, «three») b
4205                a.f(«one», two, «three») b
4206                a.f(«one», two, «three») b
4207            "},
4208        );
4209
4210        assert!(editor.move_to_next_snippet_tabstop(cx));
4211        assert(
4212            editor,
4213            cx,
4214            indoc! {"
4215                a.f(one, «two», three) b
4216                a.f(one, «two», three) b
4217                a.f(one, «two», three) b
4218            "},
4219        );
4220
4221        editor.move_to_prev_snippet_tabstop(cx);
4222        assert(
4223            editor,
4224            cx,
4225            indoc! {"
4226                a.f(«one», two, «three») b
4227                a.f(«one», two, «three») b
4228                a.f(«one», two, «three») b
4229            "},
4230        );
4231
4232        assert!(editor.move_to_next_snippet_tabstop(cx));
4233        assert(
4234            editor,
4235            cx,
4236            indoc! {"
4237                a.f(one, «two», three) b
4238                a.f(one, «two», three) b
4239                a.f(one, «two», three) b
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
4253        // As soon as the last tab stop is reached, snippet state is gone
4254        editor.move_to_prev_snippet_tabstop(cx);
4255        assert(
4256            editor,
4257            cx,
4258            indoc! {"
4259                a.f(one, two, three)ˇ b
4260                a.f(one, two, three)ˇ b
4261                a.f(one, two, three)ˇ b
4262            "},
4263        );
4264    });
4265}
4266
4267#[gpui::test]
4268async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
4269    init_test(cx, |_| {});
4270
4271    let mut language = Language::new(
4272        LanguageConfig {
4273            name: "Rust".into(),
4274            path_suffixes: vec!["rs".to_string()],
4275            ..Default::default()
4276        },
4277        Some(tree_sitter_rust::language()),
4278    );
4279    let mut fake_servers = language
4280        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4281            capabilities: lsp::ServerCapabilities {
4282                document_formatting_provider: Some(lsp::OneOf::Left(true)),
4283                ..Default::default()
4284            },
4285            ..Default::default()
4286        }))
4287        .await;
4288
4289    let fs = FakeFs::new(cx.background());
4290    fs.insert_file("/file.rs", Default::default()).await;
4291
4292    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4293    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4294    let buffer = project
4295        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4296        .await
4297        .unwrap();
4298
4299    cx.foreground().start_waiting();
4300    let fake_server = fake_servers.next().await.unwrap();
4301
4302    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4303    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4304    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4305    assert!(cx.read(|cx| editor.is_dirty(cx)));
4306
4307    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4308    fake_server
4309        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4310            assert_eq!(
4311                params.text_document.uri,
4312                lsp::Url::from_file_path("/file.rs").unwrap()
4313            );
4314            assert_eq!(params.options.tab_size, 4);
4315            Ok(Some(vec![lsp::TextEdit::new(
4316                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4317                ", ".to_string(),
4318            )]))
4319        })
4320        .next()
4321        .await;
4322    cx.foreground().start_waiting();
4323    save.await.unwrap();
4324    assert_eq!(
4325        editor.read_with(cx, |editor, cx| editor.text(cx)),
4326        "one, two\nthree\n"
4327    );
4328    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4329
4330    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4331    assert!(cx.read(|cx| editor.is_dirty(cx)));
4332
4333    // Ensure we can still save even if formatting hangs.
4334    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4335        assert_eq!(
4336            params.text_document.uri,
4337            lsp::Url::from_file_path("/file.rs").unwrap()
4338        );
4339        futures::future::pending::<()>().await;
4340        unreachable!()
4341    });
4342    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4343    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4344    cx.foreground().start_waiting();
4345    save.await.unwrap();
4346    assert_eq!(
4347        editor.read_with(cx, |editor, cx| editor.text(cx)),
4348        "one\ntwo\nthree\n"
4349    );
4350    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4351
4352    // Set rust language override and assert overridden tabsize is sent to language server
4353    update_test_settings(cx, |settings| {
4354        settings.languages.insert(
4355            "Rust".into(),
4356            LanguageSettingsContent {
4357                tab_size: NonZeroU32::new(8),
4358                ..Default::default()
4359            },
4360        );
4361    });
4362
4363    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4364    fake_server
4365        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4366            assert_eq!(
4367                params.text_document.uri,
4368                lsp::Url::from_file_path("/file.rs").unwrap()
4369            );
4370            assert_eq!(params.options.tab_size, 8);
4371            Ok(Some(vec![]))
4372        })
4373        .next()
4374        .await;
4375    cx.foreground().start_waiting();
4376    save.await.unwrap();
4377}
4378
4379#[gpui::test]
4380async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
4381    init_test(cx, |_| {});
4382
4383    let mut language = Language::new(
4384        LanguageConfig {
4385            name: "Rust".into(),
4386            path_suffixes: vec!["rs".to_string()],
4387            ..Default::default()
4388        },
4389        Some(tree_sitter_rust::language()),
4390    );
4391    let mut fake_servers = language
4392        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4393            capabilities: lsp::ServerCapabilities {
4394                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
4395                ..Default::default()
4396            },
4397            ..Default::default()
4398        }))
4399        .await;
4400
4401    let fs = FakeFs::new(cx.background());
4402    fs.insert_file("/file.rs", Default::default()).await;
4403
4404    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4405    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4406    let buffer = project
4407        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4408        .await
4409        .unwrap();
4410
4411    cx.foreground().start_waiting();
4412    let fake_server = fake_servers.next().await.unwrap();
4413
4414    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4415    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4416    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4417    assert!(cx.read(|cx| editor.is_dirty(cx)));
4418
4419    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4420    fake_server
4421        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
4422            assert_eq!(
4423                params.text_document.uri,
4424                lsp::Url::from_file_path("/file.rs").unwrap()
4425            );
4426            assert_eq!(params.options.tab_size, 4);
4427            Ok(Some(vec![lsp::TextEdit::new(
4428                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4429                ", ".to_string(),
4430            )]))
4431        })
4432        .next()
4433        .await;
4434    cx.foreground().start_waiting();
4435    save.await.unwrap();
4436    assert_eq!(
4437        editor.read_with(cx, |editor, cx| editor.text(cx)),
4438        "one, two\nthree\n"
4439    );
4440    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4441
4442    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4443    assert!(cx.read(|cx| editor.is_dirty(cx)));
4444
4445    // Ensure we can still save even if formatting hangs.
4446    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
4447        move |params, _| async move {
4448            assert_eq!(
4449                params.text_document.uri,
4450                lsp::Url::from_file_path("/file.rs").unwrap()
4451            );
4452            futures::future::pending::<()>().await;
4453            unreachable!()
4454        },
4455    );
4456    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4457    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4458    cx.foreground().start_waiting();
4459    save.await.unwrap();
4460    assert_eq!(
4461        editor.read_with(cx, |editor, cx| editor.text(cx)),
4462        "one\ntwo\nthree\n"
4463    );
4464    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4465
4466    // Set rust language override and assert overridden tabsize is sent to language server
4467    update_test_settings(cx, |settings| {
4468        settings.languages.insert(
4469            "Rust".into(),
4470            LanguageSettingsContent {
4471                tab_size: NonZeroU32::new(8),
4472                ..Default::default()
4473            },
4474        );
4475    });
4476
4477    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4478    fake_server
4479        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
4480            assert_eq!(
4481                params.text_document.uri,
4482                lsp::Url::from_file_path("/file.rs").unwrap()
4483            );
4484            assert_eq!(params.options.tab_size, 8);
4485            Ok(Some(vec![]))
4486        })
4487        .next()
4488        .await;
4489    cx.foreground().start_waiting();
4490    save.await.unwrap();
4491}
4492
4493#[gpui::test]
4494async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
4495    init_test(cx, |_| {});
4496
4497    let mut language = Language::new(
4498        LanguageConfig {
4499            name: "Rust".into(),
4500            path_suffixes: vec!["rs".to_string()],
4501            ..Default::default()
4502        },
4503        Some(tree_sitter_rust::language()),
4504    );
4505    let mut fake_servers = language
4506        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4507            capabilities: lsp::ServerCapabilities {
4508                document_formatting_provider: Some(lsp::OneOf::Left(true)),
4509                ..Default::default()
4510            },
4511            ..Default::default()
4512        }))
4513        .await;
4514
4515    let fs = FakeFs::new(cx.background());
4516    fs.insert_file("/file.rs", Default::default()).await;
4517
4518    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4519    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4520    let buffer = project
4521        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4522        .await
4523        .unwrap();
4524
4525    cx.foreground().start_waiting();
4526    let fake_server = fake_servers.next().await.unwrap();
4527
4528    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4529    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4530    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4531
4532    let format = editor.update(cx, |editor, cx| {
4533        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
4534    });
4535    fake_server
4536        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4537            assert_eq!(
4538                params.text_document.uri,
4539                lsp::Url::from_file_path("/file.rs").unwrap()
4540            );
4541            assert_eq!(params.options.tab_size, 4);
4542            Ok(Some(vec![lsp::TextEdit::new(
4543                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4544                ", ".to_string(),
4545            )]))
4546        })
4547        .next()
4548        .await;
4549    cx.foreground().start_waiting();
4550    format.await.unwrap();
4551    assert_eq!(
4552        editor.read_with(cx, |editor, cx| editor.text(cx)),
4553        "one, two\nthree\n"
4554    );
4555
4556    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4557    // Ensure we don't lock if formatting hangs.
4558    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4559        assert_eq!(
4560            params.text_document.uri,
4561            lsp::Url::from_file_path("/file.rs").unwrap()
4562        );
4563        futures::future::pending::<()>().await;
4564        unreachable!()
4565    });
4566    let format = editor.update(cx, |editor, cx| {
4567        editor.perform_format(project, FormatTrigger::Manual, cx)
4568    });
4569    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4570    cx.foreground().start_waiting();
4571    format.await.unwrap();
4572    assert_eq!(
4573        editor.read_with(cx, |editor, cx| editor.text(cx)),
4574        "one\ntwo\nthree\n"
4575    );
4576}
4577
4578#[gpui::test]
4579async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
4580    init_test(cx, |_| {});
4581
4582    let mut cx = EditorLspTestContext::new_rust(
4583        lsp::ServerCapabilities {
4584            document_formatting_provider: Some(lsp::OneOf::Left(true)),
4585            ..Default::default()
4586        },
4587        cx,
4588    )
4589    .await;
4590
4591    cx.set_state(indoc! {"
4592        one.twoˇ
4593    "});
4594
4595    // The format request takes a long time. When it completes, it inserts
4596    // a newline and an indent before the `.`
4597    cx.lsp
4598        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
4599            let executor = cx.background();
4600            async move {
4601                executor.timer(Duration::from_millis(100)).await;
4602                Ok(Some(vec![lsp::TextEdit {
4603                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
4604                    new_text: "\n    ".into(),
4605                }]))
4606            }
4607        });
4608
4609    // Submit a format request.
4610    let format_1 = cx
4611        .update_editor(|editor, cx| editor.format(&Format, cx))
4612        .unwrap();
4613    cx.foreground().run_until_parked();
4614
4615    // Submit a second format request.
4616    let format_2 = cx
4617        .update_editor(|editor, cx| editor.format(&Format, cx))
4618        .unwrap();
4619    cx.foreground().run_until_parked();
4620
4621    // Wait for both format requests to complete
4622    cx.foreground().advance_clock(Duration::from_millis(200));
4623    cx.foreground().start_waiting();
4624    format_1.await.unwrap();
4625    cx.foreground().start_waiting();
4626    format_2.await.unwrap();
4627
4628    // The formatting edits only happens once.
4629    cx.assert_editor_state(indoc! {"
4630        one
4631            .twoˇ
4632    "});
4633}
4634
4635#[gpui::test]
4636async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
4637    init_test(cx, |_| {});
4638
4639    let mut cx = EditorLspTestContext::new_rust(
4640        lsp::ServerCapabilities {
4641            document_formatting_provider: Some(lsp::OneOf::Left(true)),
4642            ..Default::default()
4643        },
4644        cx,
4645    )
4646    .await;
4647
4648    // Set up a buffer white some trailing whitespace and no trailing newline.
4649    cx.set_state(
4650        &[
4651            "one ",   //
4652            "twoˇ",   //
4653            "three ", //
4654            "four",   //
4655        ]
4656        .join("\n"),
4657    );
4658
4659    // Submit a format request.
4660    let format = cx
4661        .update_editor(|editor, cx| editor.format(&Format, cx))
4662        .unwrap();
4663
4664    // Record which buffer changes have been sent to the language server
4665    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
4666    cx.lsp
4667        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
4668            let buffer_changes = buffer_changes.clone();
4669            move |params, _| {
4670                buffer_changes.lock().extend(
4671                    params
4672                        .content_changes
4673                        .into_iter()
4674                        .map(|e| (e.range.unwrap(), e.text)),
4675                );
4676            }
4677        });
4678
4679    // Handle formatting requests to the language server.
4680    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
4681        let buffer_changes = buffer_changes.clone();
4682        move |_, _| {
4683            // When formatting is requested, trailing whitespace has already been stripped,
4684            // and the trailing newline has already been added.
4685            assert_eq!(
4686                &buffer_changes.lock()[1..],
4687                &[
4688                    (
4689                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
4690                        "".into()
4691                    ),
4692                    (
4693                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
4694                        "".into()
4695                    ),
4696                    (
4697                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
4698                        "\n".into()
4699                    ),
4700                ]
4701            );
4702
4703            // Insert blank lines between each line of the buffer.
4704            async move {
4705                Ok(Some(vec![
4706                    lsp::TextEdit {
4707                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
4708                        new_text: "\n".into(),
4709                    },
4710                    lsp::TextEdit {
4711                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
4712                        new_text: "\n".into(),
4713                    },
4714                ]))
4715            }
4716        }
4717    });
4718
4719    // After formatting the buffer, the trailing whitespace is stripped,
4720    // a newline is appended, and the edits provided by the language server
4721    // have been applied.
4722    format.await.unwrap();
4723    cx.assert_editor_state(
4724        &[
4725            "one",   //
4726            "",      //
4727            "twoˇ",  //
4728            "",      //
4729            "three", //
4730            "four",  //
4731            "",      //
4732        ]
4733        .join("\n"),
4734    );
4735
4736    // Undoing the formatting undoes the trailing whitespace removal, the
4737    // trailing newline, and the LSP edits.
4738    cx.update_buffer(|buffer, cx| buffer.undo(cx));
4739    cx.assert_editor_state(
4740        &[
4741            "one ",   //
4742            "twoˇ",   //
4743            "three ", //
4744            "four",   //
4745        ]
4746        .join("\n"),
4747    );
4748}
4749
4750#[gpui::test]
4751async fn test_completion(cx: &mut gpui::TestAppContext) {
4752    init_test(cx, |_| {});
4753
4754    let mut cx = EditorLspTestContext::new_rust(
4755        lsp::ServerCapabilities {
4756            completion_provider: Some(lsp::CompletionOptions {
4757                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
4758                ..Default::default()
4759            }),
4760            ..Default::default()
4761        },
4762        cx,
4763    )
4764    .await;
4765
4766    cx.set_state(indoc! {"
4767        oneˇ
4768        two
4769        three
4770    "});
4771    cx.simulate_keystroke(".");
4772    handle_completion_request(
4773        &mut cx,
4774        indoc! {"
4775            one.|<>
4776            two
4777            three
4778        "},
4779        vec!["first_completion", "second_completion"],
4780    )
4781    .await;
4782    cx.condition(|editor, _| editor.context_menu_visible())
4783        .await;
4784    let apply_additional_edits = cx.update_editor(|editor, cx| {
4785        editor.move_down(&MoveDown, cx);
4786        editor
4787            .confirm_completion(&ConfirmCompletion::default(), cx)
4788            .unwrap()
4789    });
4790    cx.assert_editor_state(indoc! {"
4791        one.second_completionˇ
4792        two
4793        three
4794    "});
4795
4796    handle_resolve_completion_request(
4797        &mut cx,
4798        Some(vec![
4799            (
4800                //This overlaps with the primary completion edit which is
4801                //misbehavior from the LSP spec, test that we filter it out
4802                indoc! {"
4803                    one.second_ˇcompletion
4804                    two
4805                    threeˇ
4806                "},
4807                "overlapping additional edit",
4808            ),
4809            (
4810                indoc! {"
4811                    one.second_completion
4812                    two
4813                    threeˇ
4814                "},
4815                "\nadditional edit",
4816            ),
4817        ]),
4818    )
4819    .await;
4820    apply_additional_edits.await.unwrap();
4821    cx.assert_editor_state(indoc! {"
4822        one.second_completionˇ
4823        two
4824        three
4825        additional edit
4826    "});
4827
4828    cx.set_state(indoc! {"
4829        one.second_completion
4830        twoˇ
4831        threeˇ
4832        additional edit
4833    "});
4834    cx.simulate_keystroke(" ");
4835    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4836    cx.simulate_keystroke("s");
4837    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4838
4839    cx.assert_editor_state(indoc! {"
4840        one.second_completion
4841        two sˇ
4842        three sˇ
4843        additional edit
4844    "});
4845    handle_completion_request(
4846        &mut cx,
4847        indoc! {"
4848            one.second_completion
4849            two s
4850            three <s|>
4851            additional edit
4852        "},
4853        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
4854    )
4855    .await;
4856    cx.condition(|editor, _| editor.context_menu_visible())
4857        .await;
4858
4859    cx.simulate_keystroke("i");
4860
4861    handle_completion_request(
4862        &mut cx,
4863        indoc! {"
4864            one.second_completion
4865            two si
4866            three <si|>
4867            additional edit
4868        "},
4869        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
4870    )
4871    .await;
4872    cx.condition(|editor, _| editor.context_menu_visible())
4873        .await;
4874
4875    let apply_additional_edits = cx.update_editor(|editor, cx| {
4876        editor
4877            .confirm_completion(&ConfirmCompletion::default(), cx)
4878            .unwrap()
4879    });
4880    cx.assert_editor_state(indoc! {"
4881        one.second_completion
4882        two sixth_completionˇ
4883        three sixth_completionˇ
4884        additional edit
4885    "});
4886
4887    handle_resolve_completion_request(&mut cx, None).await;
4888    apply_additional_edits.await.unwrap();
4889
4890    cx.update(|cx| {
4891        cx.update_global::<SettingsStore, _, _>(|settings, cx| {
4892            settings.update_user_settings::<EditorSettings>(cx, |settings| {
4893                settings.show_completions_on_input = Some(false);
4894            });
4895        })
4896    });
4897    cx.set_state("editorˇ");
4898    cx.simulate_keystroke(".");
4899    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4900    cx.simulate_keystroke("c");
4901    cx.simulate_keystroke("l");
4902    cx.simulate_keystroke("o");
4903    cx.assert_editor_state("editor.cloˇ");
4904    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4905    cx.update_editor(|editor, cx| {
4906        editor.show_completions(&ShowCompletions, cx);
4907    });
4908    handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
4909    cx.condition(|editor, _| editor.context_menu_visible())
4910        .await;
4911    let apply_additional_edits = cx.update_editor(|editor, cx| {
4912        editor
4913            .confirm_completion(&ConfirmCompletion::default(), cx)
4914            .unwrap()
4915    });
4916    cx.assert_editor_state("editor.closeˇ");
4917    handle_resolve_completion_request(&mut cx, None).await;
4918    apply_additional_edits.await.unwrap();
4919}
4920
4921#[gpui::test]
4922async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
4923    init_test(cx, |_| {});
4924
4925    let language = Arc::new(Language::new(
4926        LanguageConfig {
4927            line_comment: Some("// ".into()),
4928            ..Default::default()
4929        },
4930        Some(tree_sitter_rust::language()),
4931    ));
4932
4933    let text = "
4934        fn a() {
4935            //b();
4936            // c();
4937            //  d();
4938        }
4939    "
4940    .unindent();
4941
4942    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
4943    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4944    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
4945
4946    view.update(cx, |editor, cx| {
4947        // If multiple selections intersect a line, the line is only
4948        // toggled once.
4949        editor.change_selections(None, cx, |s| {
4950            s.select_display_ranges([
4951                DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
4952                DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
4953            ])
4954        });
4955        editor.toggle_comments(&ToggleComments::default(), cx);
4956        assert_eq!(
4957            editor.text(cx),
4958            "
4959                fn a() {
4960                    b();
4961                    c();
4962                     d();
4963                }
4964            "
4965            .unindent()
4966        );
4967
4968        // The comment prefix is inserted at the same column for every line
4969        // in a selection.
4970        editor.change_selections(None, cx, |s| {
4971            s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)])
4972        });
4973        editor.toggle_comments(&ToggleComments::default(), cx);
4974        assert_eq!(
4975            editor.text(cx),
4976            "
4977                fn a() {
4978                    // b();
4979                    // c();
4980                    //  d();
4981                }
4982            "
4983            .unindent()
4984        );
4985
4986        // If a selection ends at the beginning of a line, that line is not toggled.
4987        editor.change_selections(None, cx, |s| {
4988            s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)])
4989        });
4990        editor.toggle_comments(&ToggleComments::default(), cx);
4991        assert_eq!(
4992            editor.text(cx),
4993            "
4994                fn a() {
4995                    // b();
4996                    c();
4997                    //  d();
4998                }
4999            "
5000            .unindent()
5001        );
5002    });
5003}
5004
5005#[gpui::test]
5006async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
5007    init_test(cx, |_| {});
5008
5009    let language = Arc::new(Language::new(
5010        LanguageConfig {
5011            line_comment: Some("// ".into()),
5012            ..Default::default()
5013        },
5014        Some(tree_sitter_rust::language()),
5015    ));
5016
5017    let registry = Arc::new(LanguageRegistry::test());
5018    registry.add(language.clone());
5019
5020    let mut cx = EditorTestContext::new(cx).await;
5021    cx.update_buffer(|buffer, cx| {
5022        buffer.set_language_registry(registry);
5023        buffer.set_language(Some(language), cx);
5024    });
5025
5026    let toggle_comments = &ToggleComments {
5027        advance_downwards: true,
5028    };
5029
5030    // Single cursor on one line -> advance
5031    // Cursor moves horizontally 3 characters as well on non-blank line
5032    cx.set_state(indoc!(
5033        "fn a() {
5034             ˇdog();
5035             cat();
5036        }"
5037    ));
5038    cx.update_editor(|editor, cx| {
5039        editor.toggle_comments(toggle_comments, cx);
5040    });
5041    cx.assert_editor_state(indoc!(
5042        "fn a() {
5043             // dog();
5044             catˇ();
5045        }"
5046    ));
5047
5048    // Single selection on one line -> don't advance
5049    cx.set_state(indoc!(
5050        "fn a() {
5051             «dog()ˇ»;
5052             cat();
5053        }"
5054    ));
5055    cx.update_editor(|editor, cx| {
5056        editor.toggle_comments(toggle_comments, cx);
5057    });
5058    cx.assert_editor_state(indoc!(
5059        "fn a() {
5060             // «dog()ˇ»;
5061             cat();
5062        }"
5063    ));
5064
5065    // Multiple cursors on one line -> advance
5066    cx.set_state(indoc!(
5067        "fn a() {
5068             ˇdˇog();
5069             cat();
5070        }"
5071    ));
5072    cx.update_editor(|editor, cx| {
5073        editor.toggle_comments(toggle_comments, cx);
5074    });
5075    cx.assert_editor_state(indoc!(
5076        "fn a() {
5077             // dog();
5078             catˇ(ˇ);
5079        }"
5080    ));
5081
5082    // Multiple cursors on one line, with selection -> don't advance
5083    cx.set_state(indoc!(
5084        "fn a() {
5085             ˇdˇog«()ˇ»;
5086             cat();
5087        }"
5088    ));
5089    cx.update_editor(|editor, cx| {
5090        editor.toggle_comments(toggle_comments, cx);
5091    });
5092    cx.assert_editor_state(indoc!(
5093        "fn a() {
5094             // ˇdˇog«()ˇ»;
5095             cat();
5096        }"
5097    ));
5098
5099    // Single cursor on one line -> advance
5100    // Cursor moves to column 0 on blank line
5101    cx.set_state(indoc!(
5102        "fn a() {
5103             ˇdog();
5104
5105             cat();
5106        }"
5107    ));
5108    cx.update_editor(|editor, cx| {
5109        editor.toggle_comments(toggle_comments, cx);
5110    });
5111    cx.assert_editor_state(indoc!(
5112        "fn a() {
5113             // dog();
5114        ˇ
5115             cat();
5116        }"
5117    ));
5118
5119    // Single cursor on one line -> advance
5120    // Cursor starts and ends at column 0
5121    cx.set_state(indoc!(
5122        "fn a() {
5123         ˇ    dog();
5124             cat();
5125        }"
5126    ));
5127    cx.update_editor(|editor, cx| {
5128        editor.toggle_comments(toggle_comments, cx);
5129    });
5130    cx.assert_editor_state(indoc!(
5131        "fn a() {
5132             // dog();
5133         ˇ    cat();
5134        }"
5135    ));
5136}
5137
5138#[gpui::test]
5139async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
5140    init_test(cx, |_| {});
5141
5142    let mut cx = EditorTestContext::new(cx).await;
5143
5144    let html_language = Arc::new(
5145        Language::new(
5146            LanguageConfig {
5147                name: "HTML".into(),
5148                block_comment: Some(("<!-- ".into(), " -->".into())),
5149                ..Default::default()
5150            },
5151            Some(tree_sitter_html::language()),
5152        )
5153        .with_injection_query(
5154            r#"
5155            (script_element
5156                (raw_text) @content
5157                (#set! "language" "javascript"))
5158            "#,
5159        )
5160        .unwrap(),
5161    );
5162
5163    let javascript_language = Arc::new(Language::new(
5164        LanguageConfig {
5165            name: "JavaScript".into(),
5166            line_comment: Some("// ".into()),
5167            ..Default::default()
5168        },
5169        Some(tree_sitter_javascript::language()),
5170    ));
5171
5172    let registry = Arc::new(LanguageRegistry::test());
5173    registry.add(html_language.clone());
5174    registry.add(javascript_language.clone());
5175
5176    cx.update_buffer(|buffer, cx| {
5177        buffer.set_language_registry(registry);
5178        buffer.set_language(Some(html_language), cx);
5179    });
5180
5181    // Toggle comments for empty selections
5182    cx.set_state(
5183        &r#"
5184            <p>A</p>ˇ
5185            <p>B</p>ˇ
5186            <p>C</p>ˇ
5187        "#
5188        .unindent(),
5189    );
5190    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5191    cx.assert_editor_state(
5192        &r#"
5193            <!-- <p>A</p>ˇ -->
5194            <!-- <p>B</p>ˇ -->
5195            <!-- <p>C</p>ˇ -->
5196        "#
5197        .unindent(),
5198    );
5199    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5200    cx.assert_editor_state(
5201        &r#"
5202            <p>A</p>ˇ
5203            <p>B</p>ˇ
5204            <p>C</p>ˇ
5205        "#
5206        .unindent(),
5207    );
5208
5209    // Toggle comments for mixture of empty and non-empty selections, where
5210    // multiple selections occupy a given line.
5211    cx.set_state(
5212        &r#"
5213            <p>A«</p>
5214            <p>ˇ»B</p>ˇ
5215            <p>C«</p>
5216            <p>ˇ»D</p>ˇ
5217        "#
5218        .unindent(),
5219    );
5220
5221    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5222    cx.assert_editor_state(
5223        &r#"
5224            <!-- <p>A«</p>
5225            <p>ˇ»B</p>ˇ -->
5226            <!-- <p>C«</p>
5227            <p>ˇ»D</p>ˇ -->
5228        "#
5229        .unindent(),
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
5242    // Toggle comments when different languages are active for different
5243    // selections.
5244    cx.set_state(
5245        &r#"
5246            ˇ<script>
5247                ˇvar x = new Y();
5248            ˇ</script>
5249        "#
5250        .unindent(),
5251    );
5252    cx.foreground().run_until_parked();
5253    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5254    cx.assert_editor_state(
5255        &r#"
5256            <!-- ˇ<script> -->
5257                // ˇvar x = new Y();
5258            <!-- ˇ</script> -->
5259        "#
5260        .unindent(),
5261    );
5262}
5263
5264#[gpui::test]
5265fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
5266    init_test(cx, |_| {});
5267
5268    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5269    let multibuffer = cx.add_model(|cx| {
5270        let mut multibuffer = MultiBuffer::new(0);
5271        multibuffer.push_excerpts(
5272            buffer.clone(),
5273            [
5274                ExcerptRange {
5275                    context: Point::new(0, 0)..Point::new(0, 4),
5276                    primary: None,
5277                },
5278                ExcerptRange {
5279                    context: Point::new(1, 0)..Point::new(1, 4),
5280                    primary: None,
5281                },
5282            ],
5283            cx,
5284        );
5285        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
5286        multibuffer
5287    });
5288
5289    let (_, view) = cx.add_window(|cx| build_editor(multibuffer, cx));
5290    view.update(cx, |view, cx| {
5291        assert_eq!(view.text(cx), "aaaa\nbbbb");
5292        view.change_selections(None, cx, |s| {
5293            s.select_ranges([
5294                Point::new(0, 0)..Point::new(0, 0),
5295                Point::new(1, 0)..Point::new(1, 0),
5296            ])
5297        });
5298
5299        view.handle_input("X", cx);
5300        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
5301        assert_eq!(
5302            view.selections.ranges(cx),
5303            [
5304                Point::new(0, 1)..Point::new(0, 1),
5305                Point::new(1, 1)..Point::new(1, 1),
5306            ]
5307        );
5308
5309        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
5310        view.change_selections(None, cx, |s| {
5311            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
5312        });
5313        view.backspace(&Default::default(), cx);
5314        assert_eq!(view.text(cx), "Xa\nbbb");
5315        assert_eq!(
5316            view.selections.ranges(cx),
5317            [Point::new(1, 0)..Point::new(1, 0)]
5318        );
5319
5320        view.change_selections(None, cx, |s| {
5321            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
5322        });
5323        view.backspace(&Default::default(), cx);
5324        assert_eq!(view.text(cx), "X\nbb");
5325        assert_eq!(
5326            view.selections.ranges(cx),
5327            [Point::new(0, 1)..Point::new(0, 1)]
5328        );
5329    });
5330}
5331
5332#[gpui::test]
5333fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
5334    init_test(cx, |_| {});
5335
5336    let markers = vec![('[', ']').into(), ('(', ')').into()];
5337    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
5338        indoc! {"
5339            [aaaa
5340            (bbbb]
5341            cccc)",
5342        },
5343        markers.clone(),
5344    );
5345    let excerpt_ranges = markers.into_iter().map(|marker| {
5346        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
5347        ExcerptRange {
5348            context,
5349            primary: None,
5350        }
5351    });
5352    let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
5353    let multibuffer = cx.add_model(|cx| {
5354        let mut multibuffer = MultiBuffer::new(0);
5355        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
5356        multibuffer
5357    });
5358
5359    let (_, view) = cx.add_window(|cx| build_editor(multibuffer, cx));
5360    view.update(cx, |view, cx| {
5361        let (expected_text, selection_ranges) = marked_text_ranges(
5362            indoc! {"
5363                aaaa
5364                bˇbbb
5365                bˇbbˇb
5366                cccc"
5367            },
5368            true,
5369        );
5370        assert_eq!(view.text(cx), expected_text);
5371        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
5372
5373        view.handle_input("X", cx);
5374
5375        let (expected_text, expected_selections) = marked_text_ranges(
5376            indoc! {"
5377                aaaa
5378                bXˇbbXb
5379                bXˇbbXˇb
5380                cccc"
5381            },
5382            false,
5383        );
5384        assert_eq!(view.text(cx), expected_text);
5385        assert_eq!(view.selections.ranges(cx), expected_selections);
5386
5387        view.newline(&Newline, cx);
5388        let (expected_text, expected_selections) = marked_text_ranges(
5389            indoc! {"
5390                aaaa
5391                bX
5392                ˇbbX
5393                b
5394                bX
5395                ˇbbX
5396                ˇb
5397                cccc"
5398            },
5399            false,
5400        );
5401        assert_eq!(view.text(cx), expected_text);
5402        assert_eq!(view.selections.ranges(cx), expected_selections);
5403    });
5404}
5405
5406#[gpui::test]
5407fn test_refresh_selections(cx: &mut TestAppContext) {
5408    init_test(cx, |_| {});
5409
5410    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5411    let mut excerpt1_id = None;
5412    let multibuffer = cx.add_model(|cx| {
5413        let mut multibuffer = MultiBuffer::new(0);
5414        excerpt1_id = multibuffer
5415            .push_excerpts(
5416                buffer.clone(),
5417                [
5418                    ExcerptRange {
5419                        context: Point::new(0, 0)..Point::new(1, 4),
5420                        primary: None,
5421                    },
5422                    ExcerptRange {
5423                        context: Point::new(1, 0)..Point::new(2, 4),
5424                        primary: None,
5425                    },
5426                ],
5427                cx,
5428            )
5429            .into_iter()
5430            .next();
5431        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
5432        multibuffer
5433    });
5434
5435    let (_, editor) = cx.add_window(|cx| {
5436        let mut editor = build_editor(multibuffer.clone(), cx);
5437        let snapshot = editor.snapshot(cx);
5438        editor.change_selections(None, cx, |s| {
5439            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
5440        });
5441        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
5442        assert_eq!(
5443            editor.selections.ranges(cx),
5444            [
5445                Point::new(1, 3)..Point::new(1, 3),
5446                Point::new(2, 1)..Point::new(2, 1),
5447            ]
5448        );
5449        editor
5450    });
5451
5452    // Refreshing selections is a no-op when excerpts haven't changed.
5453    editor.update(cx, |editor, cx| {
5454        editor.change_selections(None, cx, |s| s.refresh());
5455        assert_eq!(
5456            editor.selections.ranges(cx),
5457            [
5458                Point::new(1, 3)..Point::new(1, 3),
5459                Point::new(2, 1)..Point::new(2, 1),
5460            ]
5461        );
5462    });
5463
5464    multibuffer.update(cx, |multibuffer, cx| {
5465        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
5466    });
5467    editor.update(cx, |editor, cx| {
5468        // Removing an excerpt causes the first selection to become degenerate.
5469        assert_eq!(
5470            editor.selections.ranges(cx),
5471            [
5472                Point::new(0, 0)..Point::new(0, 0),
5473                Point::new(0, 1)..Point::new(0, 1)
5474            ]
5475        );
5476
5477        // Refreshing selections will relocate the first selection to the original buffer
5478        // location.
5479        editor.change_selections(None, cx, |s| s.refresh());
5480        assert_eq!(
5481            editor.selections.ranges(cx),
5482            [
5483                Point::new(0, 1)..Point::new(0, 1),
5484                Point::new(0, 3)..Point::new(0, 3)
5485            ]
5486        );
5487        assert!(editor.selections.pending_anchor().is_some());
5488    });
5489}
5490
5491#[gpui::test]
5492fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
5493    init_test(cx, |_| {});
5494
5495    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5496    let mut excerpt1_id = None;
5497    let multibuffer = cx.add_model(|cx| {
5498        let mut multibuffer = MultiBuffer::new(0);
5499        excerpt1_id = multibuffer
5500            .push_excerpts(
5501                buffer.clone(),
5502                [
5503                    ExcerptRange {
5504                        context: Point::new(0, 0)..Point::new(1, 4),
5505                        primary: None,
5506                    },
5507                    ExcerptRange {
5508                        context: Point::new(1, 0)..Point::new(2, 4),
5509                        primary: None,
5510                    },
5511                ],
5512                cx,
5513            )
5514            .into_iter()
5515            .next();
5516        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
5517        multibuffer
5518    });
5519
5520    let (_, editor) = cx.add_window(|cx| {
5521        let mut editor = build_editor(multibuffer.clone(), cx);
5522        let snapshot = editor.snapshot(cx);
5523        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
5524        assert_eq!(
5525            editor.selections.ranges(cx),
5526            [Point::new(1, 3)..Point::new(1, 3)]
5527        );
5528        editor
5529    });
5530
5531    multibuffer.update(cx, |multibuffer, cx| {
5532        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
5533    });
5534    editor.update(cx, |editor, cx| {
5535        assert_eq!(
5536            editor.selections.ranges(cx),
5537            [Point::new(0, 0)..Point::new(0, 0)]
5538        );
5539
5540        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
5541        editor.change_selections(None, cx, |s| s.refresh());
5542        assert_eq!(
5543            editor.selections.ranges(cx),
5544            [Point::new(0, 3)..Point::new(0, 3)]
5545        );
5546        assert!(editor.selections.pending_anchor().is_some());
5547    });
5548}
5549
5550#[gpui::test]
5551async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
5552    init_test(cx, |_| {});
5553
5554    let language = Arc::new(
5555        Language::new(
5556            LanguageConfig {
5557                brackets: BracketPairConfig {
5558                    pairs: vec![
5559                        BracketPair {
5560                            start: "{".to_string(),
5561                            end: "}".to_string(),
5562                            close: true,
5563                            newline: true,
5564                        },
5565                        BracketPair {
5566                            start: "/* ".to_string(),
5567                            end: " */".to_string(),
5568                            close: true,
5569                            newline: true,
5570                        },
5571                    ],
5572                    ..Default::default()
5573                },
5574                ..Default::default()
5575            },
5576            Some(tree_sitter_rust::language()),
5577        )
5578        .with_indents_query("")
5579        .unwrap(),
5580    );
5581
5582    let text = concat!(
5583        "{   }\n",     //
5584        "  x\n",       //
5585        "  /*   */\n", //
5586        "x\n",         //
5587        "{{} }\n",     //
5588    );
5589
5590    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
5591    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
5592    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
5593    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
5594        .await;
5595
5596    view.update(cx, |view, cx| {
5597        view.change_selections(None, cx, |s| {
5598            s.select_display_ranges([
5599                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
5600                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
5601                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
5602            ])
5603        });
5604        view.newline(&Newline, cx);
5605
5606        assert_eq!(
5607            view.buffer().read(cx).read(cx).text(),
5608            concat!(
5609                "{ \n",    // Suppress rustfmt
5610                "\n",      //
5611                "}\n",     //
5612                "  x\n",   //
5613                "  /* \n", //
5614                "  \n",    //
5615                "  */\n",  //
5616                "x\n",     //
5617                "{{} \n",  //
5618                "}\n",     //
5619            )
5620        );
5621    });
5622}
5623
5624#[gpui::test]
5625fn test_highlighted_ranges(cx: &mut TestAppContext) {
5626    init_test(cx, |_| {});
5627
5628    let (_, editor) = cx.add_window(|cx| {
5629        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
5630        build_editor(buffer.clone(), cx)
5631    });
5632
5633    editor.update(cx, |editor, cx| {
5634        struct Type1;
5635        struct Type2;
5636
5637        let buffer = editor.buffer.read(cx).snapshot(cx);
5638
5639        let anchor_range =
5640            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
5641
5642        editor.highlight_background::<Type1>(
5643            vec![
5644                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
5645                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
5646                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
5647                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
5648            ],
5649            |_| Color::red(),
5650            cx,
5651        );
5652        editor.highlight_background::<Type2>(
5653            vec![
5654                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
5655                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
5656                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
5657                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
5658            ],
5659            |_| Color::green(),
5660            cx,
5661        );
5662
5663        let snapshot = editor.snapshot(cx);
5664        let mut highlighted_ranges = editor.background_highlights_in_range(
5665            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
5666            &snapshot,
5667            theme::current(cx).as_ref(),
5668        );
5669        // Enforce a consistent ordering based on color without relying on the ordering of the
5670        // highlight's `TypeId` which is non-deterministic.
5671        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
5672        assert_eq!(
5673            highlighted_ranges,
5674            &[
5675                (
5676                    DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
5677                    Color::green(),
5678                ),
5679                (
5680                    DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
5681                    Color::green(),
5682                ),
5683                (
5684                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
5685                    Color::red(),
5686                ),
5687                (
5688                    DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
5689                    Color::red(),
5690                ),
5691            ]
5692        );
5693        assert_eq!(
5694            editor.background_highlights_in_range(
5695                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
5696                &snapshot,
5697                theme::current(cx).as_ref(),
5698            ),
5699            &[(
5700                DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
5701                Color::red(),
5702            )]
5703        );
5704    });
5705}
5706
5707#[gpui::test]
5708async fn test_following(cx: &mut gpui::TestAppContext) {
5709    init_test(cx, |_| {});
5710
5711    let fs = FakeFs::new(cx.background());
5712    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5713
5714    let buffer = project.update(cx, |project, cx| {
5715        let buffer = project
5716            .create_buffer(&sample_text(16, 8, 'a'), None, cx)
5717            .unwrap();
5718        cx.add_model(|cx| MultiBuffer::singleton(buffer, cx))
5719    });
5720    let (_, leader) = cx.add_window(|cx| build_editor(buffer.clone(), cx));
5721    let (_, follower) = cx.update(|cx| {
5722        cx.add_window(
5723            WindowOptions {
5724                bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
5725                ..Default::default()
5726            },
5727            |cx| build_editor(buffer.clone(), cx),
5728        )
5729    });
5730
5731    let is_still_following = Rc::new(RefCell::new(true));
5732    let follower_edit_event_count = Rc::new(RefCell::new(0));
5733    let pending_update = Rc::new(RefCell::new(None));
5734    follower.update(cx, {
5735        let update = pending_update.clone();
5736        let is_still_following = is_still_following.clone();
5737        let follower_edit_event_count = follower_edit_event_count.clone();
5738        |_, cx| {
5739            cx.subscribe(&leader, move |_, leader, event, cx| {
5740                leader
5741                    .read(cx)
5742                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
5743            })
5744            .detach();
5745
5746            cx.subscribe(&follower, move |_, _, event, cx| {
5747                if Editor::should_unfollow_on_event(event, cx) {
5748                    *is_still_following.borrow_mut() = false;
5749                }
5750                if let Event::BufferEdited = event {
5751                    *follower_edit_event_count.borrow_mut() += 1;
5752                }
5753            })
5754            .detach();
5755        }
5756    });
5757
5758    // Update the selections only
5759    leader.update(cx, |leader, cx| {
5760        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
5761    });
5762    follower
5763        .update(cx, |follower, cx| {
5764            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5765        })
5766        .await
5767        .unwrap();
5768    follower.read_with(cx, |follower, cx| {
5769        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
5770    });
5771    assert_eq!(*is_still_following.borrow(), true);
5772    assert_eq!(*follower_edit_event_count.borrow(), 0);
5773
5774    // Update the scroll position only
5775    leader.update(cx, |leader, cx| {
5776        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
5777    });
5778    follower
5779        .update(cx, |follower, cx| {
5780            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5781        })
5782        .await
5783        .unwrap();
5784    assert_eq!(
5785        follower.update(cx, |follower, cx| follower.scroll_position(cx)),
5786        vec2f(1.5, 3.5)
5787    );
5788    assert_eq!(*is_still_following.borrow(), true);
5789    assert_eq!(*follower_edit_event_count.borrow(), 0);
5790
5791    // Update the selections and scroll position. The follower's scroll position is updated
5792    // via autoscroll, not via the leader's exact scroll position.
5793    leader.update(cx, |leader, cx| {
5794        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
5795        leader.request_autoscroll(Autoscroll::newest(), cx);
5796        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
5797    });
5798    follower
5799        .update(cx, |follower, cx| {
5800            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5801        })
5802        .await
5803        .unwrap();
5804    follower.update(cx, |follower, cx| {
5805        assert_eq!(follower.scroll_position(cx), vec2f(1.5, 0.0));
5806        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
5807    });
5808    assert_eq!(*is_still_following.borrow(), true);
5809
5810    // Creating a pending selection that precedes another selection
5811    leader.update(cx, |leader, cx| {
5812        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
5813        leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
5814    });
5815    follower
5816        .update(cx, |follower, cx| {
5817            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5818        })
5819        .await
5820        .unwrap();
5821    follower.read_with(cx, |follower, cx| {
5822        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
5823    });
5824    assert_eq!(*is_still_following.borrow(), true);
5825
5826    // Extend the pending selection so that it surrounds another selection
5827    leader.update(cx, |leader, cx| {
5828        leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
5829    });
5830    follower
5831        .update(cx, |follower, cx| {
5832            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5833        })
5834        .await
5835        .unwrap();
5836    follower.read_with(cx, |follower, cx| {
5837        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
5838    });
5839
5840    // Scrolling locally breaks the follow
5841    follower.update(cx, |follower, cx| {
5842        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
5843        follower.set_scroll_anchor(
5844            ScrollAnchor {
5845                anchor: top_anchor,
5846                offset: vec2f(0.0, 0.5),
5847            },
5848            cx,
5849        );
5850    });
5851    assert_eq!(*is_still_following.borrow(), false);
5852}
5853
5854#[gpui::test]
5855async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
5856    init_test(cx, |_| {});
5857
5858    let fs = FakeFs::new(cx.background());
5859    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5860    let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
5861    let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
5862
5863    let leader = pane.update(cx, |_, cx| {
5864        let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
5865        cx.add_view(|cx| build_editor(multibuffer.clone(), cx))
5866    });
5867
5868    // Start following the editor when it has no excerpts.
5869    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
5870    let follower_1 = cx
5871        .update(|cx| {
5872            Editor::from_state_proto(
5873                pane.clone(),
5874                project.clone(),
5875                ViewId {
5876                    creator: Default::default(),
5877                    id: 0,
5878                },
5879                &mut state_message,
5880                cx,
5881            )
5882        })
5883        .unwrap()
5884        .await
5885        .unwrap();
5886
5887    let update_message = Rc::new(RefCell::new(None));
5888    follower_1.update(cx, {
5889        let update = update_message.clone();
5890        |_, cx| {
5891            cx.subscribe(&leader, move |_, leader, event, cx| {
5892                leader
5893                    .read(cx)
5894                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
5895            })
5896            .detach();
5897        }
5898    });
5899
5900    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
5901        (
5902            project
5903                .create_buffer("abc\ndef\nghi\njkl\n", None, cx)
5904                .unwrap(),
5905            project
5906                .create_buffer("mno\npqr\nstu\nvwx\n", None, cx)
5907                .unwrap(),
5908        )
5909    });
5910
5911    // Insert some excerpts.
5912    leader.update(cx, |leader, cx| {
5913        leader.buffer.update(cx, |multibuffer, cx| {
5914            let excerpt_ids = multibuffer.push_excerpts(
5915                buffer_1.clone(),
5916                [
5917                    ExcerptRange {
5918                        context: 1..6,
5919                        primary: None,
5920                    },
5921                    ExcerptRange {
5922                        context: 12..15,
5923                        primary: None,
5924                    },
5925                    ExcerptRange {
5926                        context: 0..3,
5927                        primary: None,
5928                    },
5929                ],
5930                cx,
5931            );
5932            multibuffer.insert_excerpts_after(
5933                excerpt_ids[0],
5934                buffer_2.clone(),
5935                [
5936                    ExcerptRange {
5937                        context: 8..12,
5938                        primary: None,
5939                    },
5940                    ExcerptRange {
5941                        context: 0..6,
5942                        primary: None,
5943                    },
5944                ],
5945                cx,
5946            );
5947        });
5948    });
5949
5950    // Apply the update of adding the excerpts.
5951    follower_1
5952        .update(cx, |follower, cx| {
5953            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
5954        })
5955        .await
5956        .unwrap();
5957    assert_eq!(
5958        follower_1.read_with(cx, |editor, cx| editor.text(cx)),
5959        leader.read_with(cx, |editor, cx| editor.text(cx))
5960    );
5961    update_message.borrow_mut().take();
5962
5963    // Start following separately after it already has excerpts.
5964    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
5965    let follower_2 = cx
5966        .update(|cx| {
5967            Editor::from_state_proto(
5968                pane.clone(),
5969                project.clone(),
5970                ViewId {
5971                    creator: Default::default(),
5972                    id: 0,
5973                },
5974                &mut state_message,
5975                cx,
5976            )
5977        })
5978        .unwrap()
5979        .await
5980        .unwrap();
5981    assert_eq!(
5982        follower_2.read_with(cx, |editor, cx| editor.text(cx)),
5983        leader.read_with(cx, |editor, cx| editor.text(cx))
5984    );
5985
5986    // Remove some excerpts.
5987    leader.update(cx, |leader, cx| {
5988        leader.buffer.update(cx, |multibuffer, cx| {
5989            let excerpt_ids = multibuffer.excerpt_ids();
5990            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
5991            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
5992        });
5993    });
5994
5995    // Apply the update of removing the excerpts.
5996    follower_1
5997        .update(cx, |follower, cx| {
5998            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
5999        })
6000        .await
6001        .unwrap();
6002    follower_2
6003        .update(cx, |follower, cx| {
6004            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6005        })
6006        .await
6007        .unwrap();
6008    update_message.borrow_mut().take();
6009    assert_eq!(
6010        follower_1.read_with(cx, |editor, cx| editor.text(cx)),
6011        leader.read_with(cx, |editor, cx| editor.text(cx))
6012    );
6013}
6014
6015#[test]
6016fn test_combine_syntax_and_fuzzy_match_highlights() {
6017    let string = "abcdefghijklmnop";
6018    let syntax_ranges = [
6019        (
6020            0..3,
6021            HighlightStyle {
6022                color: Some(Color::red()),
6023                ..Default::default()
6024            },
6025        ),
6026        (
6027            4..8,
6028            HighlightStyle {
6029                color: Some(Color::green()),
6030                ..Default::default()
6031            },
6032        ),
6033    ];
6034    let match_indices = [4, 6, 7, 8];
6035    assert_eq!(
6036        combine_syntax_and_fuzzy_match_highlights(
6037            string,
6038            Default::default(),
6039            syntax_ranges.into_iter(),
6040            &match_indices,
6041        ),
6042        &[
6043            (
6044                0..3,
6045                HighlightStyle {
6046                    color: Some(Color::red()),
6047                    ..Default::default()
6048                },
6049            ),
6050            (
6051                4..5,
6052                HighlightStyle {
6053                    color: Some(Color::green()),
6054                    weight: Some(fonts::Weight::BOLD),
6055                    ..Default::default()
6056                },
6057            ),
6058            (
6059                5..6,
6060                HighlightStyle {
6061                    color: Some(Color::green()),
6062                    ..Default::default()
6063                },
6064            ),
6065            (
6066                6..8,
6067                HighlightStyle {
6068                    color: Some(Color::green()),
6069                    weight: Some(fonts::Weight::BOLD),
6070                    ..Default::default()
6071                },
6072            ),
6073            (
6074                8..9,
6075                HighlightStyle {
6076                    weight: Some(fonts::Weight::BOLD),
6077                    ..Default::default()
6078                },
6079            ),
6080        ]
6081    );
6082}
6083
6084#[gpui::test]
6085async fn go_to_hunk(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
6086    init_test(cx, |_| {});
6087
6088    let mut cx = EditorTestContext::new(cx).await;
6089
6090    let diff_base = r#"
6091        use some::mod;
6092
6093        const A: u32 = 42;
6094
6095        fn main() {
6096            println!("hello");
6097
6098            println!("world");
6099        }
6100        "#
6101    .unindent();
6102
6103    // Edits are modified, removed, modified, added
6104    cx.set_state(
6105        &r#"
6106        use some::modified;
6107
6108        ˇ
6109        fn main() {
6110            println!("hello there");
6111
6112            println!("around the");
6113            println!("world");
6114        }
6115        "#
6116        .unindent(),
6117    );
6118
6119    cx.set_diff_base(Some(&diff_base));
6120    deterministic.run_until_parked();
6121
6122    cx.update_editor(|editor, cx| {
6123        //Wrap around the bottom of the buffer
6124        for _ in 0..3 {
6125            editor.go_to_hunk(&GoToHunk, cx);
6126        }
6127    });
6128
6129    cx.assert_editor_state(
6130        &r#"
6131        ˇuse some::modified;
6132
6133
6134        fn main() {
6135            println!("hello there");
6136
6137            println!("around the");
6138            println!("world");
6139        }
6140        "#
6141        .unindent(),
6142    );
6143
6144    cx.update_editor(|editor, cx| {
6145        //Wrap around the top of the buffer
6146        for _ in 0..2 {
6147            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
6148        }
6149    });
6150
6151    cx.assert_editor_state(
6152        &r#"
6153        use some::modified;
6154
6155
6156        fn main() {
6157        ˇ    println!("hello there");
6158
6159            println!("around the");
6160            println!("world");
6161        }
6162        "#
6163        .unindent(),
6164    );
6165
6166    cx.update_editor(|editor, cx| {
6167        editor.fold(&Fold, cx);
6168
6169        //Make sure that the fold only gets one hunk
6170        for _ in 0..4 {
6171            editor.go_to_hunk(&GoToHunk, cx);
6172        }
6173    });
6174
6175    cx.assert_editor_state(
6176        &r#"
6177        ˇuse some::modified;
6178
6179
6180        fn main() {
6181            println!("hello there");
6182
6183            println!("around the");
6184            println!("world");
6185        }
6186        "#
6187        .unindent(),
6188    );
6189}
6190
6191#[test]
6192fn test_split_words() {
6193    fn split<'a>(text: &'a str) -> Vec<&'a str> {
6194        split_words(text).collect()
6195    }
6196
6197    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
6198    assert_eq!(split("hello_world"), &["hello_", "world"]);
6199    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
6200    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
6201    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
6202    assert_eq!(split("helloworld"), &["helloworld"]);
6203}
6204
6205#[gpui::test]
6206async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
6207    init_test(cx, |_| {});
6208
6209    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
6210    let mut assert = |before, after| {
6211        let _state_context = cx.set_state(before);
6212        cx.update_editor(|editor, cx| {
6213            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
6214        });
6215        cx.assert_editor_state(after);
6216    };
6217
6218    // Outside bracket jumps to outside of matching bracket
6219    assert("console.logˇ(var);", "console.log(var)ˇ;");
6220    assert("console.log(var)ˇ;", "console.logˇ(var);");
6221
6222    // Inside bracket jumps to inside of matching bracket
6223    assert("console.log(ˇvar);", "console.log(varˇ);");
6224    assert("console.log(varˇ);", "console.log(ˇvar);");
6225
6226    // When outside a bracket and inside, favor jumping to the inside bracket
6227    assert(
6228        "console.log('foo', [1, 2, 3]ˇ);",
6229        "console.log(ˇ'foo', [1, 2, 3]);",
6230    );
6231    assert(
6232        "console.log(ˇ'foo', [1, 2, 3]);",
6233        "console.log('foo', [1, 2, 3]ˇ);",
6234    );
6235
6236    // Bias forward if two options are equally likely
6237    assert(
6238        "let result = curried_fun()ˇ();",
6239        "let result = curried_fun()()ˇ;",
6240    );
6241
6242    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
6243    assert(
6244        indoc! {"
6245            function test() {
6246                console.log('test')ˇ
6247            }"},
6248        indoc! {"
6249            function test() {
6250                console.logˇ('test')
6251            }"},
6252    );
6253}
6254
6255#[gpui::test(iterations = 10)]
6256async fn test_copilot(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
6257    init_test(cx, |_| {});
6258
6259    let (copilot, copilot_lsp) = Copilot::fake(cx);
6260    cx.update(|cx| cx.set_global(copilot));
6261    let mut cx = EditorLspTestContext::new_rust(
6262        lsp::ServerCapabilities {
6263            completion_provider: Some(lsp::CompletionOptions {
6264                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
6265                ..Default::default()
6266            }),
6267            ..Default::default()
6268        },
6269        cx,
6270    )
6271    .await;
6272
6273    // When inserting, ensure autocompletion is favored over Copilot suggestions.
6274    cx.set_state(indoc! {"
6275        oneˇ
6276        two
6277        three
6278    "});
6279    cx.simulate_keystroke(".");
6280    let _ = handle_completion_request(
6281        &mut cx,
6282        indoc! {"
6283            one.|<>
6284            two
6285            three
6286        "},
6287        vec!["completion_a", "completion_b"],
6288    );
6289    handle_copilot_completion_request(
6290        &copilot_lsp,
6291        vec![copilot::request::Completion {
6292            text: "one.copilot1".into(),
6293            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6294            ..Default::default()
6295        }],
6296        vec![],
6297    );
6298    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6299    cx.update_editor(|editor, cx| {
6300        assert!(editor.context_menu_visible());
6301        assert!(!editor.has_active_copilot_suggestion(cx));
6302
6303        // Confirming a completion inserts it and hides the context menu, without showing
6304        // the copilot suggestion afterwards.
6305        editor
6306            .confirm_completion(&Default::default(), cx)
6307            .unwrap()
6308            .detach();
6309        assert!(!editor.context_menu_visible());
6310        assert!(!editor.has_active_copilot_suggestion(cx));
6311        assert_eq!(editor.text(cx), "one.completion_a\ntwo\nthree\n");
6312        assert_eq!(editor.display_text(cx), "one.completion_a\ntwo\nthree\n");
6313    });
6314
6315    // Ensure Copilot suggestions are shown right away if no autocompletion is available.
6316    cx.set_state(indoc! {"
6317        oneˇ
6318        two
6319        three
6320    "});
6321    cx.simulate_keystroke(".");
6322    let _ = handle_completion_request(
6323        &mut cx,
6324        indoc! {"
6325            one.|<>
6326            two
6327            three
6328        "},
6329        vec![],
6330    );
6331    handle_copilot_completion_request(
6332        &copilot_lsp,
6333        vec![copilot::request::Completion {
6334            text: "one.copilot1".into(),
6335            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6336            ..Default::default()
6337        }],
6338        vec![],
6339    );
6340    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6341    cx.update_editor(|editor, cx| {
6342        assert!(!editor.context_menu_visible());
6343        assert!(editor.has_active_copilot_suggestion(cx));
6344        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
6345        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
6346    });
6347
6348    // Reset editor, and ensure autocompletion is still favored over Copilot suggestions.
6349    cx.set_state(indoc! {"
6350        oneˇ
6351        two
6352        three
6353    "});
6354    cx.simulate_keystroke(".");
6355    let _ = handle_completion_request(
6356        &mut cx,
6357        indoc! {"
6358            one.|<>
6359            two
6360            three
6361        "},
6362        vec!["completion_a", "completion_b"],
6363    );
6364    handle_copilot_completion_request(
6365        &copilot_lsp,
6366        vec![copilot::request::Completion {
6367            text: "one.copilot1".into(),
6368            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6369            ..Default::default()
6370        }],
6371        vec![],
6372    );
6373    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6374    cx.update_editor(|editor, cx| {
6375        assert!(editor.context_menu_visible());
6376        assert!(!editor.has_active_copilot_suggestion(cx));
6377
6378        // When hiding the context menu, the Copilot suggestion becomes visible.
6379        editor.hide_context_menu(cx);
6380        assert!(!editor.context_menu_visible());
6381        assert!(editor.has_active_copilot_suggestion(cx));
6382        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
6383        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
6384    });
6385
6386    // Ensure existing completion is interpolated when inserting again.
6387    cx.simulate_keystroke("c");
6388    deterministic.run_until_parked();
6389    cx.update_editor(|editor, 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.c\ntwo\nthree\n");
6394    });
6395
6396    // After debouncing, new Copilot completions should be requested.
6397    handle_copilot_completion_request(
6398        &copilot_lsp,
6399        vec![copilot::request::Completion {
6400            text: "one.copilot2".into(),
6401            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 5)),
6402            ..Default::default()
6403        }],
6404        vec![],
6405    );
6406    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6407    cx.update_editor(|editor, cx| {
6408        assert!(!editor.context_menu_visible());
6409        assert!(editor.has_active_copilot_suggestion(cx));
6410        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6411        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6412
6413        // Canceling should remove the active Copilot suggestion.
6414        editor.cancel(&Default::default(), cx);
6415        assert!(!editor.has_active_copilot_suggestion(cx));
6416        assert_eq!(editor.display_text(cx), "one.c\ntwo\nthree\n");
6417        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6418
6419        // After canceling, tabbing shouldn't insert the previously shown suggestion.
6420        editor.tab(&Default::default(), cx);
6421        assert!(!editor.has_active_copilot_suggestion(cx));
6422        assert_eq!(editor.display_text(cx), "one.c   \ntwo\nthree\n");
6423        assert_eq!(editor.text(cx), "one.c   \ntwo\nthree\n");
6424
6425        // When undoing the previously active suggestion is shown again.
6426        editor.undo(&Default::default(), cx);
6427        assert!(editor.has_active_copilot_suggestion(cx));
6428        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6429        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6430    });
6431
6432    // If an edit occurs outside of this editor, the suggestion is still correctly interpolated.
6433    cx.update_buffer(|buffer, cx| buffer.edit([(5..5, "o")], None, cx));
6434    cx.update_editor(|editor, cx| {
6435        assert!(editor.has_active_copilot_suggestion(cx));
6436        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6437        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6438
6439        // Tabbing when there is an active suggestion inserts it.
6440        editor.tab(&Default::default(), cx);
6441        assert!(!editor.has_active_copilot_suggestion(cx));
6442        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6443        assert_eq!(editor.text(cx), "one.copilot2\ntwo\nthree\n");
6444
6445        // When undoing the previously active suggestion is shown again.
6446        editor.undo(&Default::default(), cx);
6447        assert!(editor.has_active_copilot_suggestion(cx));
6448        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6449        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6450
6451        // Hide suggestion.
6452        editor.cancel(&Default::default(), cx);
6453        assert!(!editor.has_active_copilot_suggestion(cx));
6454        assert_eq!(editor.display_text(cx), "one.co\ntwo\nthree\n");
6455        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6456    });
6457
6458    // If an edit occurs outside of this editor but no suggestion is being shown,
6459    // we won't make it visible.
6460    cx.update_buffer(|buffer, cx| buffer.edit([(6..6, "p")], None, cx));
6461    cx.update_editor(|editor, cx| {
6462        assert!(!editor.has_active_copilot_suggestion(cx));
6463        assert_eq!(editor.display_text(cx), "one.cop\ntwo\nthree\n");
6464        assert_eq!(editor.text(cx), "one.cop\ntwo\nthree\n");
6465    });
6466
6467    // Reset the editor to verify how suggestions behave when tabbing on leading indentation.
6468    cx.update_editor(|editor, cx| {
6469        editor.set_text("fn foo() {\n  \n}", cx);
6470        editor.change_selections(None, cx, |s| {
6471            s.select_ranges([Point::new(1, 2)..Point::new(1, 2)])
6472        });
6473    });
6474    handle_copilot_completion_request(
6475        &copilot_lsp,
6476        vec![copilot::request::Completion {
6477            text: "    let x = 4;".into(),
6478            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
6479            ..Default::default()
6480        }],
6481        vec![],
6482    );
6483
6484    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
6485    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6486    cx.update_editor(|editor, cx| {
6487        assert!(editor.has_active_copilot_suggestion(cx));
6488        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6489        assert_eq!(editor.text(cx), "fn foo() {\n  \n}");
6490
6491        // Tabbing inside of leading whitespace inserts indentation without accepting the suggestion.
6492        editor.tab(&Default::default(), cx);
6493        assert!(editor.has_active_copilot_suggestion(cx));
6494        assert_eq!(editor.text(cx), "fn foo() {\n    \n}");
6495        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6496
6497        // Tabbing again accepts the suggestion.
6498        editor.tab(&Default::default(), cx);
6499        assert!(!editor.has_active_copilot_suggestion(cx));
6500        assert_eq!(editor.text(cx), "fn foo() {\n    let x = 4;\n}");
6501        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6502    });
6503}
6504
6505#[gpui::test]
6506async fn test_copilot_completion_invalidation(
6507    deterministic: Arc<Deterministic>,
6508    cx: &mut gpui::TestAppContext,
6509) {
6510    init_test(cx, |_| {});
6511
6512    let (copilot, copilot_lsp) = Copilot::fake(cx);
6513    cx.update(|cx| cx.set_global(copilot));
6514    let mut cx = EditorLspTestContext::new_rust(
6515        lsp::ServerCapabilities {
6516            completion_provider: Some(lsp::CompletionOptions {
6517                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
6518                ..Default::default()
6519            }),
6520            ..Default::default()
6521        },
6522        cx,
6523    )
6524    .await;
6525
6526    cx.set_state(indoc! {"
6527        one
6528        twˇ
6529        three
6530    "});
6531
6532    handle_copilot_completion_request(
6533        &copilot_lsp,
6534        vec![copilot::request::Completion {
6535            text: "two.foo()".into(),
6536            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
6537            ..Default::default()
6538        }],
6539        vec![],
6540    );
6541    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
6542    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6543    cx.update_editor(|editor, cx| {
6544        assert!(editor.has_active_copilot_suggestion(cx));
6545        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6546        assert_eq!(editor.text(cx), "one\ntw\nthree\n");
6547
6548        editor.backspace(&Default::default(), cx);
6549        assert!(editor.has_active_copilot_suggestion(cx));
6550        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6551        assert_eq!(editor.text(cx), "one\nt\nthree\n");
6552
6553        editor.backspace(&Default::default(), 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\n\nthree\n");
6557
6558        // Deleting across the original suggestion range invalidates it.
6559        editor.backspace(&Default::default(), cx);
6560        assert!(!editor.has_active_copilot_suggestion(cx));
6561        assert_eq!(editor.display_text(cx), "one\nthree\n");
6562        assert_eq!(editor.text(cx), "one\nthree\n");
6563
6564        // Undoing the deletion restores the suggestion.
6565        editor.undo(&Default::default(), cx);
6566        assert!(editor.has_active_copilot_suggestion(cx));
6567        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6568        assert_eq!(editor.text(cx), "one\n\nthree\n");
6569    });
6570}
6571
6572#[gpui::test]
6573async fn test_copilot_multibuffer(
6574    deterministic: Arc<Deterministic>,
6575    cx: &mut gpui::TestAppContext,
6576) {
6577    init_test(cx, |_| {});
6578
6579    let (copilot, copilot_lsp) = Copilot::fake(cx);
6580    cx.update(|cx| cx.set_global(copilot));
6581
6582    let buffer_1 = cx.add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx));
6583    let buffer_2 = cx.add_model(|cx| Buffer::new(0, "c = 3\nd = 4\n", cx));
6584    let multibuffer = cx.add_model(|cx| {
6585        let mut multibuffer = MultiBuffer::new(0);
6586        multibuffer.push_excerpts(
6587            buffer_1.clone(),
6588            [ExcerptRange {
6589                context: Point::new(0, 0)..Point::new(2, 0),
6590                primary: None,
6591            }],
6592            cx,
6593        );
6594        multibuffer.push_excerpts(
6595            buffer_2.clone(),
6596            [ExcerptRange {
6597                context: Point::new(0, 0)..Point::new(2, 0),
6598                primary: None,
6599            }],
6600            cx,
6601        );
6602        multibuffer
6603    });
6604    let (_, editor) = cx.add_window(|cx| build_editor(multibuffer, cx));
6605
6606    handle_copilot_completion_request(
6607        &copilot_lsp,
6608        vec![copilot::request::Completion {
6609            text: "b = 2 + a".into(),
6610            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 5)),
6611            ..Default::default()
6612        }],
6613        vec![],
6614    );
6615    editor.update(cx, |editor, cx| {
6616        // Ensure copilot suggestions are shown for the first excerpt.
6617        editor.change_selections(None, cx, |s| {
6618            s.select_ranges([Point::new(1, 5)..Point::new(1, 5)])
6619        });
6620        editor.next_copilot_suggestion(&Default::default(), cx);
6621    });
6622    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6623    editor.update(cx, |editor, cx| {
6624        assert!(editor.has_active_copilot_suggestion(cx));
6625        assert_eq!(
6626            editor.display_text(cx),
6627            "\n\na = 1\nb = 2 + a\n\n\n\nc = 3\nd = 4\n"
6628        );
6629        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
6630    });
6631
6632    handle_copilot_completion_request(
6633        &copilot_lsp,
6634        vec![copilot::request::Completion {
6635            text: "d = 4 + c".into(),
6636            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 6)),
6637            ..Default::default()
6638        }],
6639        vec![],
6640    );
6641    editor.update(cx, |editor, cx| {
6642        // Move to another excerpt, ensuring the suggestion gets cleared.
6643        editor.change_selections(None, cx, |s| {
6644            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
6645        });
6646        assert!(!editor.has_active_copilot_suggestion(cx));
6647        assert_eq!(
6648            editor.display_text(cx),
6649            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4\n"
6650        );
6651        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
6652
6653        // Type a character, ensuring we don't even try to interpolate the previous suggestion.
6654        editor.handle_input(" ", cx);
6655        assert!(!editor.has_active_copilot_suggestion(cx));
6656        assert_eq!(
6657            editor.display_text(cx),
6658            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 \n"
6659        );
6660        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
6661    });
6662
6663    // Ensure the new suggestion is displayed when the debounce timeout expires.
6664    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6665    editor.update(cx, |editor, cx| {
6666        assert!(editor.has_active_copilot_suggestion(cx));
6667        assert_eq!(
6668            editor.display_text(cx),
6669            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 + c\n"
6670        );
6671        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
6672    });
6673}
6674
6675#[gpui::test]
6676async fn test_copilot_disabled_globs(
6677    deterministic: Arc<Deterministic>,
6678    cx: &mut gpui::TestAppContext,
6679) {
6680    init_test(cx, |settings| {
6681        settings
6682            .copilot
6683            .get_or_insert(Default::default())
6684            .disabled_globs = Some(vec![".env*".to_string()]);
6685    });
6686
6687    let (copilot, copilot_lsp) = Copilot::fake(cx);
6688    cx.update(|cx| cx.set_global(copilot));
6689
6690    let fs = FakeFs::new(cx.background());
6691    fs.insert_tree(
6692        "/test",
6693        json!({
6694            ".env": "SECRET=something\n",
6695            "README.md": "hello\n"
6696        }),
6697    )
6698    .await;
6699    let project = Project::test(fs, ["/test".as_ref()], cx).await;
6700
6701    let private_buffer = project
6702        .update(cx, |project, cx| {
6703            project.open_local_buffer("/test/.env", cx)
6704        })
6705        .await
6706        .unwrap();
6707    let public_buffer = project
6708        .update(cx, |project, cx| {
6709            project.open_local_buffer("/test/README.md", cx)
6710        })
6711        .await
6712        .unwrap();
6713
6714    let multibuffer = cx.add_model(|cx| {
6715        let mut multibuffer = MultiBuffer::new(0);
6716        multibuffer.push_excerpts(
6717            private_buffer.clone(),
6718            [ExcerptRange {
6719                context: Point::new(0, 0)..Point::new(1, 0),
6720                primary: None,
6721            }],
6722            cx,
6723        );
6724        multibuffer.push_excerpts(
6725            public_buffer.clone(),
6726            [ExcerptRange {
6727                context: Point::new(0, 0)..Point::new(1, 0),
6728                primary: None,
6729            }],
6730            cx,
6731        );
6732        multibuffer
6733    });
6734    let (_, editor) = cx.add_window(|cx| build_editor(multibuffer, cx));
6735
6736    let mut copilot_requests = copilot_lsp
6737        .handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| async move {
6738            Ok(copilot::request::GetCompletionsResult {
6739                completions: vec![copilot::request::Completion {
6740                    text: "next line".into(),
6741                    range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
6742                    ..Default::default()
6743                }],
6744            })
6745        });
6746
6747    editor.update(cx, |editor, cx| {
6748        editor.change_selections(None, cx, |selections| {
6749            selections.select_ranges([Point::new(0, 0)..Point::new(0, 0)])
6750        });
6751        editor.next_copilot_suggestion(&Default::default(), cx);
6752    });
6753
6754    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6755    assert!(copilot_requests.try_next().is_err());
6756
6757    editor.update(cx, |editor, cx| {
6758        editor.change_selections(None, cx, |s| {
6759            s.select_ranges([Point::new(2, 0)..Point::new(2, 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_ok());
6766}
6767
6768fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
6769    let point = DisplayPoint::new(row as u32, column as u32);
6770    point..point
6771}
6772
6773fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
6774    let (text, ranges) = marked_text_ranges(marked_text, true);
6775    assert_eq!(view.text(cx), text);
6776    assert_eq!(
6777        view.selections.ranges(cx),
6778        ranges,
6779        "Assert selections are {}",
6780        marked_text
6781    );
6782}
6783
6784/// Handle completion request passing a marked string specifying where the completion
6785/// should be triggered from using '|' character, what range should be replaced, and what completions
6786/// should be returned using '<' and '>' to delimit the range
6787fn handle_completion_request<'a>(
6788    cx: &mut EditorLspTestContext<'a>,
6789    marked_string: &str,
6790    completions: Vec<&'static str>,
6791) -> impl Future<Output = ()> {
6792    let complete_from_marker: TextRangeMarker = '|'.into();
6793    let replace_range_marker: TextRangeMarker = ('<', '>').into();
6794    let (_, mut marked_ranges) = marked_text_ranges_by(
6795        marked_string,
6796        vec![complete_from_marker.clone(), replace_range_marker.clone()],
6797    );
6798
6799    let complete_from_position =
6800        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
6801    let replace_range =
6802        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
6803
6804    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
6805        let completions = completions.clone();
6806        async move {
6807            assert_eq!(params.text_document_position.text_document.uri, url.clone());
6808            assert_eq!(
6809                params.text_document_position.position,
6810                complete_from_position
6811            );
6812            Ok(Some(lsp::CompletionResponse::Array(
6813                completions
6814                    .iter()
6815                    .map(|completion_text| lsp::CompletionItem {
6816                        label: completion_text.to_string(),
6817                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
6818                            range: replace_range,
6819                            new_text: completion_text.to_string(),
6820                        })),
6821                        ..Default::default()
6822                    })
6823                    .collect(),
6824            )))
6825        }
6826    });
6827
6828    async move {
6829        request.next().await;
6830    }
6831}
6832
6833fn handle_resolve_completion_request<'a>(
6834    cx: &mut EditorLspTestContext<'a>,
6835    edits: Option<Vec<(&'static str, &'static str)>>,
6836) -> impl Future<Output = ()> {
6837    let edits = edits.map(|edits| {
6838        edits
6839            .iter()
6840            .map(|(marked_string, new_text)| {
6841                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
6842                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
6843                lsp::TextEdit::new(replace_range, new_text.to_string())
6844            })
6845            .collect::<Vec<_>>()
6846    });
6847
6848    let mut request =
6849        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
6850            let edits = edits.clone();
6851            async move {
6852                Ok(lsp::CompletionItem {
6853                    additional_text_edits: edits,
6854                    ..Default::default()
6855                })
6856            }
6857        });
6858
6859    async move {
6860        request.next().await;
6861    }
6862}
6863
6864fn handle_copilot_completion_request(
6865    lsp: &lsp::FakeLanguageServer,
6866    completions: Vec<copilot::request::Completion>,
6867    completions_cycling: Vec<copilot::request::Completion>,
6868) {
6869    lsp.handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| {
6870        let completions = completions.clone();
6871        async move {
6872            Ok(copilot::request::GetCompletionsResult {
6873                completions: completions.clone(),
6874            })
6875        }
6876    });
6877    lsp.handle_request::<copilot::request::GetCompletionsCycling, _, _>(move |_params, _cx| {
6878        let completions_cycling = completions_cycling.clone();
6879        async move {
6880            Ok(copilot::request::GetCompletionsResult {
6881                completions: completions_cycling.clone(),
6882            })
6883        }
6884    });
6885}
6886
6887pub(crate) fn update_test_settings(
6888    cx: &mut TestAppContext,
6889    f: impl Fn(&mut AllLanguageSettingsContent),
6890) {
6891    cx.update(|cx| {
6892        cx.update_global::<SettingsStore, _, _>(|store, cx| {
6893            store.update_user_settings::<AllLanguageSettings>(cx, f);
6894        });
6895    });
6896}
6897
6898pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
6899    cx.foreground().forbid_parking();
6900
6901    cx.update(|cx| {
6902        cx.set_global(SettingsStore::test(cx));
6903        theme::init((), cx);
6904        client::init_settings(cx);
6905        language::init(cx);
6906        Project::init_settings(cx);
6907        workspace::init_settings(cx);
6908        crate::init(cx);
6909    });
6910
6911    update_test_settings(cx, f);
6912}