editor_tests.rs

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