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