editor_tests.rs

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