editor_tests.rs

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