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    let mut cx = EditorTestContext::new(cx).await;
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    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
4957
4958    // If multiple selections intersect a line, the line is only toggled once.
4959    cx.set_state(indoc! {"
4960        fn a() {
4961            «//b();
4962            ˇ»// «c();
4963            //ˇ»  d();
4964        }
4965    "});
4966
4967    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
4968
4969    cx.assert_editor_state(indoc! {"
4970        fn a() {
4971            «b();
4972            c();
4973            ˇ» d();
4974        }
4975    "});
4976
4977    // The comment prefix is inserted at the same column for every line in a
4978    // selection.
4979    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
4980
4981    cx.assert_editor_state(indoc! {"
4982        fn a() {
4983            // «b();
4984            // c();
4985            ˇ»//  d();
4986        }
4987    "});
4988
4989    // If a selection ends at the beginning of a line, that line is not toggled.
4990    cx.set_selections_state(indoc! {"
4991        fn a() {
4992            // b();
4993            «// c();
4994        ˇ»    //  d();
4995        }
4996    "});
4997
4998    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
4999
5000    cx.assert_editor_state(indoc! {"
5001        fn a() {
5002            // b();
5003            «c();
5004        ˇ»    //  d();
5005        }
5006    "});
5007
5008    // If a selection span a single line and is empty, the line is toggled.
5009    cx.set_state(indoc! {"
5010        fn a() {
5011            a();
5012            b();
5013        ˇ
5014        }
5015    "});
5016
5017    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5018
5019    cx.assert_editor_state(indoc! {"
5020        fn a() {
5021            a();
5022            b();
5023        //•ˇ
5024        }
5025    "});
5026
5027    // If a selection span multiple lines, empty lines are not toggled.
5028    cx.set_state(indoc! {"
5029        fn a() {
5030            «a();
5031
5032            c();ˇ»
5033        }
5034    "});
5035
5036    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5037
5038    cx.assert_editor_state(indoc! {"
5039        fn a() {
5040            // «a();
5041
5042            // c();ˇ»
5043        }
5044    "});
5045}
5046
5047#[gpui::test]
5048async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
5049    init_test(cx, |_| {});
5050
5051    let language = Arc::new(Language::new(
5052        LanguageConfig {
5053            line_comment: Some("// ".into()),
5054            ..Default::default()
5055        },
5056        Some(tree_sitter_rust::language()),
5057    ));
5058
5059    let registry = Arc::new(LanguageRegistry::test());
5060    registry.add(language.clone());
5061
5062    let mut cx = EditorTestContext::new(cx).await;
5063    cx.update_buffer(|buffer, cx| {
5064        buffer.set_language_registry(registry);
5065        buffer.set_language(Some(language), cx);
5066    });
5067
5068    let toggle_comments = &ToggleComments {
5069        advance_downwards: true,
5070    };
5071
5072    // Single cursor on one line -> advance
5073    // Cursor moves horizontally 3 characters as well on non-blank line
5074    cx.set_state(indoc!(
5075        "fn a() {
5076             ˇdog();
5077             cat();
5078        }"
5079    ));
5080    cx.update_editor(|editor, cx| {
5081        editor.toggle_comments(toggle_comments, cx);
5082    });
5083    cx.assert_editor_state(indoc!(
5084        "fn a() {
5085             // dog();
5086             catˇ();
5087        }"
5088    ));
5089
5090    // Single selection on one line -> don't advance
5091    cx.set_state(indoc!(
5092        "fn a() {
5093             «dog()ˇ»;
5094             cat();
5095        }"
5096    ));
5097    cx.update_editor(|editor, cx| {
5098        editor.toggle_comments(toggle_comments, cx);
5099    });
5100    cx.assert_editor_state(indoc!(
5101        "fn a() {
5102             // «dog()ˇ»;
5103             cat();
5104        }"
5105    ));
5106
5107    // Multiple cursors on one line -> advance
5108    cx.set_state(indoc!(
5109        "fn a() {
5110             ˇdˇog();
5111             cat();
5112        }"
5113    ));
5114    cx.update_editor(|editor, cx| {
5115        editor.toggle_comments(toggle_comments, cx);
5116    });
5117    cx.assert_editor_state(indoc!(
5118        "fn a() {
5119             // dog();
5120             catˇ(ˇ);
5121        }"
5122    ));
5123
5124    // Multiple cursors on one line, with selection -> don't advance
5125    cx.set_state(indoc!(
5126        "fn a() {
5127             ˇdˇog«()ˇ»;
5128             cat();
5129        }"
5130    ));
5131    cx.update_editor(|editor, cx| {
5132        editor.toggle_comments(toggle_comments, cx);
5133    });
5134    cx.assert_editor_state(indoc!(
5135        "fn a() {
5136             // ˇdˇog«()ˇ»;
5137             cat();
5138        }"
5139    ));
5140
5141    // Single cursor on one line -> advance
5142    // Cursor moves to column 0 on blank line
5143    cx.set_state(indoc!(
5144        "fn a() {
5145             ˇdog();
5146
5147             cat();
5148        }"
5149    ));
5150    cx.update_editor(|editor, cx| {
5151        editor.toggle_comments(toggle_comments, cx);
5152    });
5153    cx.assert_editor_state(indoc!(
5154        "fn a() {
5155             // dog();
5156        ˇ
5157             cat();
5158        }"
5159    ));
5160
5161    // Single cursor on one line -> advance
5162    // Cursor starts and ends at column 0
5163    cx.set_state(indoc!(
5164        "fn a() {
5165         ˇ    dog();
5166             cat();
5167        }"
5168    ));
5169    cx.update_editor(|editor, cx| {
5170        editor.toggle_comments(toggle_comments, cx);
5171    });
5172    cx.assert_editor_state(indoc!(
5173        "fn a() {
5174             // dog();
5175         ˇ    cat();
5176        }"
5177    ));
5178}
5179
5180#[gpui::test]
5181async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
5182    init_test(cx, |_| {});
5183
5184    let mut cx = EditorTestContext::new(cx).await;
5185
5186    let html_language = Arc::new(
5187        Language::new(
5188            LanguageConfig {
5189                name: "HTML".into(),
5190                block_comment: Some(("<!-- ".into(), " -->".into())),
5191                ..Default::default()
5192            },
5193            Some(tree_sitter_html::language()),
5194        )
5195        .with_injection_query(
5196            r#"
5197            (script_element
5198                (raw_text) @content
5199                (#set! "language" "javascript"))
5200            "#,
5201        )
5202        .unwrap(),
5203    );
5204
5205    let javascript_language = Arc::new(Language::new(
5206        LanguageConfig {
5207            name: "JavaScript".into(),
5208            line_comment: Some("// ".into()),
5209            ..Default::default()
5210        },
5211        Some(tree_sitter_javascript::language()),
5212    ));
5213
5214    let registry = Arc::new(LanguageRegistry::test());
5215    registry.add(html_language.clone());
5216    registry.add(javascript_language.clone());
5217
5218    cx.update_buffer(|buffer, cx| {
5219        buffer.set_language_registry(registry);
5220        buffer.set_language(Some(html_language), cx);
5221    });
5222
5223    // Toggle comments for empty selections
5224    cx.set_state(
5225        &r#"
5226            <p>A</p>ˇ
5227            <p>B</p>ˇ
5228            <p>C</p>ˇ
5229        "#
5230        .unindent(),
5231    );
5232    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5233    cx.assert_editor_state(
5234        &r#"
5235            <!-- <p>A</p>ˇ -->
5236            <!-- <p>B</p>ˇ -->
5237            <!-- <p>C</p>ˇ -->
5238        "#
5239        .unindent(),
5240    );
5241    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5242    cx.assert_editor_state(
5243        &r#"
5244            <p>A</p>ˇ
5245            <p>B</p>ˇ
5246            <p>C</p>ˇ
5247        "#
5248        .unindent(),
5249    );
5250
5251    // Toggle comments for mixture of empty and non-empty selections, where
5252    // multiple selections occupy a given line.
5253    cx.set_state(
5254        &r#"
5255            <p>A«</p>
5256            <p>ˇ»B</p>ˇ
5257            <p>C«</p>
5258            <p>ˇ»D</p>ˇ
5259        "#
5260        .unindent(),
5261    );
5262
5263    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5264    cx.assert_editor_state(
5265        &r#"
5266            <!-- <p>A«</p>
5267            <p>ˇ»B</p>ˇ -->
5268            <!-- <p>C«</p>
5269            <p>ˇ»D</p>ˇ -->
5270        "#
5271        .unindent(),
5272    );
5273    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5274    cx.assert_editor_state(
5275        &r#"
5276            <p>A«</p>
5277            <p>ˇ»B</p>ˇ
5278            <p>C«</p>
5279            <p>ˇ»D</p>ˇ
5280        "#
5281        .unindent(),
5282    );
5283
5284    // Toggle comments when different languages are active for different
5285    // selections.
5286    cx.set_state(
5287        &r#"
5288            ˇ<script>
5289                ˇvar x = new Y();
5290            ˇ</script>
5291        "#
5292        .unindent(),
5293    );
5294    cx.foreground().run_until_parked();
5295    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5296    cx.assert_editor_state(
5297        &r#"
5298            <!-- ˇ<script> -->
5299                // ˇvar x = new Y();
5300            <!-- ˇ</script> -->
5301        "#
5302        .unindent(),
5303    );
5304}
5305
5306#[gpui::test]
5307fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
5308    init_test(cx, |_| {});
5309
5310    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5311    let multibuffer = cx.add_model(|cx| {
5312        let mut multibuffer = MultiBuffer::new(0);
5313        multibuffer.push_excerpts(
5314            buffer.clone(),
5315            [
5316                ExcerptRange {
5317                    context: Point::new(0, 0)..Point::new(0, 4),
5318                    primary: None,
5319                },
5320                ExcerptRange {
5321                    context: Point::new(1, 0)..Point::new(1, 4),
5322                    primary: None,
5323                },
5324            ],
5325            cx,
5326        );
5327        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
5328        multibuffer
5329    });
5330
5331    let (_, view) = cx.add_window(|cx| build_editor(multibuffer, cx));
5332    view.update(cx, |view, cx| {
5333        assert_eq!(view.text(cx), "aaaa\nbbbb");
5334        view.change_selections(None, cx, |s| {
5335            s.select_ranges([
5336                Point::new(0, 0)..Point::new(0, 0),
5337                Point::new(1, 0)..Point::new(1, 0),
5338            ])
5339        });
5340
5341        view.handle_input("X", cx);
5342        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
5343        assert_eq!(
5344            view.selections.ranges(cx),
5345            [
5346                Point::new(0, 1)..Point::new(0, 1),
5347                Point::new(1, 1)..Point::new(1, 1),
5348            ]
5349        );
5350
5351        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
5352        view.change_selections(None, cx, |s| {
5353            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
5354        });
5355        view.backspace(&Default::default(), cx);
5356        assert_eq!(view.text(cx), "Xa\nbbb");
5357        assert_eq!(
5358            view.selections.ranges(cx),
5359            [Point::new(1, 0)..Point::new(1, 0)]
5360        );
5361
5362        view.change_selections(None, cx, |s| {
5363            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
5364        });
5365        view.backspace(&Default::default(), cx);
5366        assert_eq!(view.text(cx), "X\nbb");
5367        assert_eq!(
5368            view.selections.ranges(cx),
5369            [Point::new(0, 1)..Point::new(0, 1)]
5370        );
5371    });
5372}
5373
5374#[gpui::test]
5375fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
5376    init_test(cx, |_| {});
5377
5378    let markers = vec![('[', ']').into(), ('(', ')').into()];
5379    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
5380        indoc! {"
5381            [aaaa
5382            (bbbb]
5383            cccc)",
5384        },
5385        markers.clone(),
5386    );
5387    let excerpt_ranges = markers.into_iter().map(|marker| {
5388        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
5389        ExcerptRange {
5390            context,
5391            primary: None,
5392        }
5393    });
5394    let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
5395    let multibuffer = cx.add_model(|cx| {
5396        let mut multibuffer = MultiBuffer::new(0);
5397        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
5398        multibuffer
5399    });
5400
5401    let (_, view) = cx.add_window(|cx| build_editor(multibuffer, cx));
5402    view.update(cx, |view, cx| {
5403        let (expected_text, selection_ranges) = marked_text_ranges(
5404            indoc! {"
5405                aaaa
5406                bˇbbb
5407                bˇbbˇb
5408                cccc"
5409            },
5410            true,
5411        );
5412        assert_eq!(view.text(cx), expected_text);
5413        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
5414
5415        view.handle_input("X", cx);
5416
5417        let (expected_text, expected_selections) = marked_text_ranges(
5418            indoc! {"
5419                aaaa
5420                bXˇbbXb
5421                bXˇbbXˇb
5422                cccc"
5423            },
5424            false,
5425        );
5426        assert_eq!(view.text(cx), expected_text);
5427        assert_eq!(view.selections.ranges(cx), expected_selections);
5428
5429        view.newline(&Newline, cx);
5430        let (expected_text, expected_selections) = marked_text_ranges(
5431            indoc! {"
5432                aaaa
5433                bX
5434                ˇbbX
5435                b
5436                bX
5437                ˇbbX
5438                ˇb
5439                cccc"
5440            },
5441            false,
5442        );
5443        assert_eq!(view.text(cx), expected_text);
5444        assert_eq!(view.selections.ranges(cx), expected_selections);
5445    });
5446}
5447
5448#[gpui::test]
5449fn test_refresh_selections(cx: &mut TestAppContext) {
5450    init_test(cx, |_| {});
5451
5452    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5453    let mut excerpt1_id = None;
5454    let multibuffer = cx.add_model(|cx| {
5455        let mut multibuffer = MultiBuffer::new(0);
5456        excerpt1_id = multibuffer
5457            .push_excerpts(
5458                buffer.clone(),
5459                [
5460                    ExcerptRange {
5461                        context: Point::new(0, 0)..Point::new(1, 4),
5462                        primary: None,
5463                    },
5464                    ExcerptRange {
5465                        context: Point::new(1, 0)..Point::new(2, 4),
5466                        primary: None,
5467                    },
5468                ],
5469                cx,
5470            )
5471            .into_iter()
5472            .next();
5473        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
5474        multibuffer
5475    });
5476
5477    let (_, editor) = cx.add_window(|cx| {
5478        let mut editor = build_editor(multibuffer.clone(), cx);
5479        let snapshot = editor.snapshot(cx);
5480        editor.change_selections(None, cx, |s| {
5481            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
5482        });
5483        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
5484        assert_eq!(
5485            editor.selections.ranges(cx),
5486            [
5487                Point::new(1, 3)..Point::new(1, 3),
5488                Point::new(2, 1)..Point::new(2, 1),
5489            ]
5490        );
5491        editor
5492    });
5493
5494    // Refreshing selections is a no-op when excerpts haven't changed.
5495    editor.update(cx, |editor, cx| {
5496        editor.change_selections(None, cx, |s| s.refresh());
5497        assert_eq!(
5498            editor.selections.ranges(cx),
5499            [
5500                Point::new(1, 3)..Point::new(1, 3),
5501                Point::new(2, 1)..Point::new(2, 1),
5502            ]
5503        );
5504    });
5505
5506    multibuffer.update(cx, |multibuffer, cx| {
5507        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
5508    });
5509    editor.update(cx, |editor, cx| {
5510        // Removing an excerpt causes the first selection to become degenerate.
5511        assert_eq!(
5512            editor.selections.ranges(cx),
5513            [
5514                Point::new(0, 0)..Point::new(0, 0),
5515                Point::new(0, 1)..Point::new(0, 1)
5516            ]
5517        );
5518
5519        // Refreshing selections will relocate the first selection to the original buffer
5520        // location.
5521        editor.change_selections(None, cx, |s| s.refresh());
5522        assert_eq!(
5523            editor.selections.ranges(cx),
5524            [
5525                Point::new(0, 1)..Point::new(0, 1),
5526                Point::new(0, 3)..Point::new(0, 3)
5527            ]
5528        );
5529        assert!(editor.selections.pending_anchor().is_some());
5530    });
5531}
5532
5533#[gpui::test]
5534fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
5535    init_test(cx, |_| {});
5536
5537    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5538    let mut excerpt1_id = None;
5539    let multibuffer = cx.add_model(|cx| {
5540        let mut multibuffer = MultiBuffer::new(0);
5541        excerpt1_id = multibuffer
5542            .push_excerpts(
5543                buffer.clone(),
5544                [
5545                    ExcerptRange {
5546                        context: Point::new(0, 0)..Point::new(1, 4),
5547                        primary: None,
5548                    },
5549                    ExcerptRange {
5550                        context: Point::new(1, 0)..Point::new(2, 4),
5551                        primary: None,
5552                    },
5553                ],
5554                cx,
5555            )
5556            .into_iter()
5557            .next();
5558        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
5559        multibuffer
5560    });
5561
5562    let (_, editor) = cx.add_window(|cx| {
5563        let mut editor = build_editor(multibuffer.clone(), cx);
5564        let snapshot = editor.snapshot(cx);
5565        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
5566        assert_eq!(
5567            editor.selections.ranges(cx),
5568            [Point::new(1, 3)..Point::new(1, 3)]
5569        );
5570        editor
5571    });
5572
5573    multibuffer.update(cx, |multibuffer, cx| {
5574        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
5575    });
5576    editor.update(cx, |editor, cx| {
5577        assert_eq!(
5578            editor.selections.ranges(cx),
5579            [Point::new(0, 0)..Point::new(0, 0)]
5580        );
5581
5582        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
5583        editor.change_selections(None, cx, |s| s.refresh());
5584        assert_eq!(
5585            editor.selections.ranges(cx),
5586            [Point::new(0, 3)..Point::new(0, 3)]
5587        );
5588        assert!(editor.selections.pending_anchor().is_some());
5589    });
5590}
5591
5592#[gpui::test]
5593async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
5594    init_test(cx, |_| {});
5595
5596    let language = Arc::new(
5597        Language::new(
5598            LanguageConfig {
5599                brackets: BracketPairConfig {
5600                    pairs: vec![
5601                        BracketPair {
5602                            start: "{".to_string(),
5603                            end: "}".to_string(),
5604                            close: true,
5605                            newline: true,
5606                        },
5607                        BracketPair {
5608                            start: "/* ".to_string(),
5609                            end: " */".to_string(),
5610                            close: true,
5611                            newline: true,
5612                        },
5613                    ],
5614                    ..Default::default()
5615                },
5616                ..Default::default()
5617            },
5618            Some(tree_sitter_rust::language()),
5619        )
5620        .with_indents_query("")
5621        .unwrap(),
5622    );
5623
5624    let text = concat!(
5625        "{   }\n",     //
5626        "  x\n",       //
5627        "  /*   */\n", //
5628        "x\n",         //
5629        "{{} }\n",     //
5630    );
5631
5632    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
5633    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
5634    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
5635    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
5636        .await;
5637
5638    view.update(cx, |view, cx| {
5639        view.change_selections(None, cx, |s| {
5640            s.select_display_ranges([
5641                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
5642                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
5643                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
5644            ])
5645        });
5646        view.newline(&Newline, cx);
5647
5648        assert_eq!(
5649            view.buffer().read(cx).read(cx).text(),
5650            concat!(
5651                "{ \n",    // Suppress rustfmt
5652                "\n",      //
5653                "}\n",     //
5654                "  x\n",   //
5655                "  /* \n", //
5656                "  \n",    //
5657                "  */\n",  //
5658                "x\n",     //
5659                "{{} \n",  //
5660                "}\n",     //
5661            )
5662        );
5663    });
5664}
5665
5666#[gpui::test]
5667fn test_highlighted_ranges(cx: &mut TestAppContext) {
5668    init_test(cx, |_| {});
5669
5670    let (_, editor) = cx.add_window(|cx| {
5671        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
5672        build_editor(buffer.clone(), cx)
5673    });
5674
5675    editor.update(cx, |editor, cx| {
5676        struct Type1;
5677        struct Type2;
5678
5679        let buffer = editor.buffer.read(cx).snapshot(cx);
5680
5681        let anchor_range =
5682            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
5683
5684        editor.highlight_background::<Type1>(
5685            vec![
5686                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
5687                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
5688                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
5689                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
5690            ],
5691            |_| Color::red(),
5692            cx,
5693        );
5694        editor.highlight_background::<Type2>(
5695            vec![
5696                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
5697                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
5698                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
5699                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
5700            ],
5701            |_| Color::green(),
5702            cx,
5703        );
5704
5705        let snapshot = editor.snapshot(cx);
5706        let mut highlighted_ranges = editor.background_highlights_in_range(
5707            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
5708            &snapshot,
5709            theme::current(cx).as_ref(),
5710        );
5711        // Enforce a consistent ordering based on color without relying on the ordering of the
5712        // highlight's `TypeId` which is non-deterministic.
5713        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
5714        assert_eq!(
5715            highlighted_ranges,
5716            &[
5717                (
5718                    DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
5719                    Color::green(),
5720                ),
5721                (
5722                    DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
5723                    Color::green(),
5724                ),
5725                (
5726                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
5727                    Color::red(),
5728                ),
5729                (
5730                    DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
5731                    Color::red(),
5732                ),
5733            ]
5734        );
5735        assert_eq!(
5736            editor.background_highlights_in_range(
5737                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
5738                &snapshot,
5739                theme::current(cx).as_ref(),
5740            ),
5741            &[(
5742                DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
5743                Color::red(),
5744            )]
5745        );
5746    });
5747}
5748
5749#[gpui::test]
5750async fn test_following(cx: &mut gpui::TestAppContext) {
5751    init_test(cx, |_| {});
5752
5753    let fs = FakeFs::new(cx.background());
5754    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5755
5756    let buffer = project.update(cx, |project, cx| {
5757        let buffer = project
5758            .create_buffer(&sample_text(16, 8, 'a'), None, cx)
5759            .unwrap();
5760        cx.add_model(|cx| MultiBuffer::singleton(buffer, cx))
5761    });
5762    let (_, leader) = cx.add_window(|cx| build_editor(buffer.clone(), cx));
5763    let (_, follower) = cx.update(|cx| {
5764        cx.add_window(
5765            WindowOptions {
5766                bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
5767                ..Default::default()
5768            },
5769            |cx| build_editor(buffer.clone(), cx),
5770        )
5771    });
5772
5773    let is_still_following = Rc::new(RefCell::new(true));
5774    let follower_edit_event_count = Rc::new(RefCell::new(0));
5775    let pending_update = Rc::new(RefCell::new(None));
5776    follower.update(cx, {
5777        let update = pending_update.clone();
5778        let is_still_following = is_still_following.clone();
5779        let follower_edit_event_count = follower_edit_event_count.clone();
5780        |_, cx| {
5781            cx.subscribe(&leader, move |_, leader, event, cx| {
5782                leader
5783                    .read(cx)
5784                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
5785            })
5786            .detach();
5787
5788            cx.subscribe(&follower, move |_, _, event, cx| {
5789                if Editor::should_unfollow_on_event(event, cx) {
5790                    *is_still_following.borrow_mut() = false;
5791                }
5792                if let Event::BufferEdited = event {
5793                    *follower_edit_event_count.borrow_mut() += 1;
5794                }
5795            })
5796            .detach();
5797        }
5798    });
5799
5800    // Update the selections only
5801    leader.update(cx, |leader, cx| {
5802        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
5803    });
5804    follower
5805        .update(cx, |follower, cx| {
5806            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5807        })
5808        .await
5809        .unwrap();
5810    follower.read_with(cx, |follower, cx| {
5811        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
5812    });
5813    assert_eq!(*is_still_following.borrow(), true);
5814    assert_eq!(*follower_edit_event_count.borrow(), 0);
5815
5816    // Update the scroll position only
5817    leader.update(cx, |leader, cx| {
5818        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
5819    });
5820    follower
5821        .update(cx, |follower, cx| {
5822            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5823        })
5824        .await
5825        .unwrap();
5826    assert_eq!(
5827        follower.update(cx, |follower, cx| follower.scroll_position(cx)),
5828        vec2f(1.5, 3.5)
5829    );
5830    assert_eq!(*is_still_following.borrow(), true);
5831    assert_eq!(*follower_edit_event_count.borrow(), 0);
5832
5833    // Update the selections and scroll position. The follower's scroll position is updated
5834    // via autoscroll, not via the leader's exact scroll position.
5835    leader.update(cx, |leader, cx| {
5836        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
5837        leader.request_autoscroll(Autoscroll::newest(), cx);
5838        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
5839    });
5840    follower
5841        .update(cx, |follower, cx| {
5842            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5843        })
5844        .await
5845        .unwrap();
5846    follower.update(cx, |follower, cx| {
5847        assert_eq!(follower.scroll_position(cx), vec2f(1.5, 0.0));
5848        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
5849    });
5850    assert_eq!(*is_still_following.borrow(), true);
5851
5852    // Creating a pending selection that precedes another selection
5853    leader.update(cx, |leader, cx| {
5854        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
5855        leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
5856    });
5857    follower
5858        .update(cx, |follower, cx| {
5859            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5860        })
5861        .await
5862        .unwrap();
5863    follower.read_with(cx, |follower, cx| {
5864        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
5865    });
5866    assert_eq!(*is_still_following.borrow(), true);
5867
5868    // Extend the pending selection so that it surrounds another selection
5869    leader.update(cx, |leader, cx| {
5870        leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
5871    });
5872    follower
5873        .update(cx, |follower, cx| {
5874            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5875        })
5876        .await
5877        .unwrap();
5878    follower.read_with(cx, |follower, cx| {
5879        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
5880    });
5881
5882    // Scrolling locally breaks the follow
5883    follower.update(cx, |follower, cx| {
5884        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
5885        follower.set_scroll_anchor(
5886            ScrollAnchor {
5887                anchor: top_anchor,
5888                offset: vec2f(0.0, 0.5),
5889            },
5890            cx,
5891        );
5892    });
5893    assert_eq!(*is_still_following.borrow(), false);
5894}
5895
5896#[gpui::test]
5897async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
5898    init_test(cx, |_| {});
5899
5900    let fs = FakeFs::new(cx.background());
5901    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5902    let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
5903    let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
5904
5905    let leader = pane.update(cx, |_, cx| {
5906        let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
5907        cx.add_view(|cx| build_editor(multibuffer.clone(), cx))
5908    });
5909
5910    // Start following the editor when it has no excerpts.
5911    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
5912    let follower_1 = cx
5913        .update(|cx| {
5914            Editor::from_state_proto(
5915                pane.clone(),
5916                project.clone(),
5917                ViewId {
5918                    creator: Default::default(),
5919                    id: 0,
5920                },
5921                &mut state_message,
5922                cx,
5923            )
5924        })
5925        .unwrap()
5926        .await
5927        .unwrap();
5928
5929    let update_message = Rc::new(RefCell::new(None));
5930    follower_1.update(cx, {
5931        let update = update_message.clone();
5932        |_, cx| {
5933            cx.subscribe(&leader, move |_, leader, event, cx| {
5934                leader
5935                    .read(cx)
5936                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
5937            })
5938            .detach();
5939        }
5940    });
5941
5942    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
5943        (
5944            project
5945                .create_buffer("abc\ndef\nghi\njkl\n", None, cx)
5946                .unwrap(),
5947            project
5948                .create_buffer("mno\npqr\nstu\nvwx\n", None, cx)
5949                .unwrap(),
5950        )
5951    });
5952
5953    // Insert some excerpts.
5954    leader.update(cx, |leader, cx| {
5955        leader.buffer.update(cx, |multibuffer, cx| {
5956            let excerpt_ids = multibuffer.push_excerpts(
5957                buffer_1.clone(),
5958                [
5959                    ExcerptRange {
5960                        context: 1..6,
5961                        primary: None,
5962                    },
5963                    ExcerptRange {
5964                        context: 12..15,
5965                        primary: None,
5966                    },
5967                    ExcerptRange {
5968                        context: 0..3,
5969                        primary: None,
5970                    },
5971                ],
5972                cx,
5973            );
5974            multibuffer.insert_excerpts_after(
5975                excerpt_ids[0],
5976                buffer_2.clone(),
5977                [
5978                    ExcerptRange {
5979                        context: 8..12,
5980                        primary: None,
5981                    },
5982                    ExcerptRange {
5983                        context: 0..6,
5984                        primary: None,
5985                    },
5986                ],
5987                cx,
5988            );
5989        });
5990    });
5991
5992    // Apply the update of adding the excerpts.
5993    follower_1
5994        .update(cx, |follower, cx| {
5995            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
5996        })
5997        .await
5998        .unwrap();
5999    assert_eq!(
6000        follower_1.read_with(cx, |editor, cx| editor.text(cx)),
6001        leader.read_with(cx, |editor, cx| editor.text(cx))
6002    );
6003    update_message.borrow_mut().take();
6004
6005    // Start following separately after it already has excerpts.
6006    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
6007    let follower_2 = cx
6008        .update(|cx| {
6009            Editor::from_state_proto(
6010                pane.clone(),
6011                project.clone(),
6012                ViewId {
6013                    creator: Default::default(),
6014                    id: 0,
6015                },
6016                &mut state_message,
6017                cx,
6018            )
6019        })
6020        .unwrap()
6021        .await
6022        .unwrap();
6023    assert_eq!(
6024        follower_2.read_with(cx, |editor, cx| editor.text(cx)),
6025        leader.read_with(cx, |editor, cx| editor.text(cx))
6026    );
6027
6028    // Remove some excerpts.
6029    leader.update(cx, |leader, cx| {
6030        leader.buffer.update(cx, |multibuffer, cx| {
6031            let excerpt_ids = multibuffer.excerpt_ids();
6032            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
6033            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
6034        });
6035    });
6036
6037    // Apply the update of removing the excerpts.
6038    follower_1
6039        .update(cx, |follower, cx| {
6040            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6041        })
6042        .await
6043        .unwrap();
6044    follower_2
6045        .update(cx, |follower, cx| {
6046            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6047        })
6048        .await
6049        .unwrap();
6050    update_message.borrow_mut().take();
6051    assert_eq!(
6052        follower_1.read_with(cx, |editor, cx| editor.text(cx)),
6053        leader.read_with(cx, |editor, cx| editor.text(cx))
6054    );
6055}
6056
6057#[test]
6058fn test_combine_syntax_and_fuzzy_match_highlights() {
6059    let string = "abcdefghijklmnop";
6060    let syntax_ranges = [
6061        (
6062            0..3,
6063            HighlightStyle {
6064                color: Some(Color::red()),
6065                ..Default::default()
6066            },
6067        ),
6068        (
6069            4..8,
6070            HighlightStyle {
6071                color: Some(Color::green()),
6072                ..Default::default()
6073            },
6074        ),
6075    ];
6076    let match_indices = [4, 6, 7, 8];
6077    assert_eq!(
6078        combine_syntax_and_fuzzy_match_highlights(
6079            string,
6080            Default::default(),
6081            syntax_ranges.into_iter(),
6082            &match_indices,
6083        ),
6084        &[
6085            (
6086                0..3,
6087                HighlightStyle {
6088                    color: Some(Color::red()),
6089                    ..Default::default()
6090                },
6091            ),
6092            (
6093                4..5,
6094                HighlightStyle {
6095                    color: Some(Color::green()),
6096                    weight: Some(fonts::Weight::BOLD),
6097                    ..Default::default()
6098                },
6099            ),
6100            (
6101                5..6,
6102                HighlightStyle {
6103                    color: Some(Color::green()),
6104                    ..Default::default()
6105                },
6106            ),
6107            (
6108                6..8,
6109                HighlightStyle {
6110                    color: Some(Color::green()),
6111                    weight: Some(fonts::Weight::BOLD),
6112                    ..Default::default()
6113                },
6114            ),
6115            (
6116                8..9,
6117                HighlightStyle {
6118                    weight: Some(fonts::Weight::BOLD),
6119                    ..Default::default()
6120                },
6121            ),
6122        ]
6123    );
6124}
6125
6126#[gpui::test]
6127async fn go_to_hunk(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
6128    init_test(cx, |_| {});
6129
6130    let mut cx = EditorTestContext::new(cx).await;
6131
6132    let diff_base = r#"
6133        use some::mod;
6134
6135        const A: u32 = 42;
6136
6137        fn main() {
6138            println!("hello");
6139
6140            println!("world");
6141        }
6142        "#
6143    .unindent();
6144
6145    // Edits are modified, removed, modified, added
6146    cx.set_state(
6147        &r#"
6148        use some::modified;
6149
6150        ˇ
6151        fn main() {
6152            println!("hello there");
6153
6154            println!("around the");
6155            println!("world");
6156        }
6157        "#
6158        .unindent(),
6159    );
6160
6161    cx.set_diff_base(Some(&diff_base));
6162    deterministic.run_until_parked();
6163
6164    cx.update_editor(|editor, cx| {
6165        //Wrap around the bottom of the buffer
6166        for _ in 0..3 {
6167            editor.go_to_hunk(&GoToHunk, cx);
6168        }
6169    });
6170
6171    cx.assert_editor_state(
6172        &r#"
6173        ˇuse some::modified;
6174
6175
6176        fn main() {
6177            println!("hello there");
6178
6179            println!("around the");
6180            println!("world");
6181        }
6182        "#
6183        .unindent(),
6184    );
6185
6186    cx.update_editor(|editor, cx| {
6187        //Wrap around the top of the buffer
6188        for _ in 0..2 {
6189            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
6190        }
6191    });
6192
6193    cx.assert_editor_state(
6194        &r#"
6195        use some::modified;
6196
6197
6198        fn main() {
6199        ˇ    println!("hello there");
6200
6201            println!("around the");
6202            println!("world");
6203        }
6204        "#
6205        .unindent(),
6206    );
6207
6208    cx.update_editor(|editor, cx| {
6209        editor.fold(&Fold, cx);
6210
6211        //Make sure that the fold only gets one hunk
6212        for _ in 0..4 {
6213            editor.go_to_hunk(&GoToHunk, cx);
6214        }
6215    });
6216
6217    cx.assert_editor_state(
6218        &r#"
6219        ˇuse some::modified;
6220
6221
6222        fn main() {
6223            println!("hello there");
6224
6225            println!("around the");
6226            println!("world");
6227        }
6228        "#
6229        .unindent(),
6230    );
6231}
6232
6233#[test]
6234fn test_split_words() {
6235    fn split<'a>(text: &'a str) -> Vec<&'a str> {
6236        split_words(text).collect()
6237    }
6238
6239    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
6240    assert_eq!(split("hello_world"), &["hello_", "world"]);
6241    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
6242    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
6243    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
6244    assert_eq!(split("helloworld"), &["helloworld"]);
6245}
6246
6247#[gpui::test]
6248async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
6249    init_test(cx, |_| {});
6250
6251    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
6252    let mut assert = |before, after| {
6253        let _state_context = cx.set_state(before);
6254        cx.update_editor(|editor, cx| {
6255            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
6256        });
6257        cx.assert_editor_state(after);
6258    };
6259
6260    // Outside bracket jumps to outside of matching bracket
6261    assert("console.logˇ(var);", "console.log(var)ˇ;");
6262    assert("console.log(var)ˇ;", "console.logˇ(var);");
6263
6264    // Inside bracket jumps to inside of matching bracket
6265    assert("console.log(ˇvar);", "console.log(varˇ);");
6266    assert("console.log(varˇ);", "console.log(ˇvar);");
6267
6268    // When outside a bracket and inside, favor jumping to the inside bracket
6269    assert(
6270        "console.log('foo', [1, 2, 3]ˇ);",
6271        "console.log(ˇ'foo', [1, 2, 3]);",
6272    );
6273    assert(
6274        "console.log(ˇ'foo', [1, 2, 3]);",
6275        "console.log('foo', [1, 2, 3]ˇ);",
6276    );
6277
6278    // Bias forward if two options are equally likely
6279    assert(
6280        "let result = curried_fun()ˇ();",
6281        "let result = curried_fun()()ˇ;",
6282    );
6283
6284    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
6285    assert(
6286        indoc! {"
6287            function test() {
6288                console.log('test')ˇ
6289            }"},
6290        indoc! {"
6291            function test() {
6292                console.logˇ('test')
6293            }"},
6294    );
6295}
6296
6297#[gpui::test(iterations = 10)]
6298async fn test_copilot(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
6299    init_test(cx, |_| {});
6300
6301    let (copilot, copilot_lsp) = Copilot::fake(cx);
6302    cx.update(|cx| cx.set_global(copilot));
6303    let mut cx = EditorLspTestContext::new_rust(
6304        lsp::ServerCapabilities {
6305            completion_provider: Some(lsp::CompletionOptions {
6306                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
6307                ..Default::default()
6308            }),
6309            ..Default::default()
6310        },
6311        cx,
6312    )
6313    .await;
6314
6315    // When inserting, ensure autocompletion is favored over Copilot suggestions.
6316    cx.set_state(indoc! {"
6317        oneˇ
6318        two
6319        three
6320    "});
6321    cx.simulate_keystroke(".");
6322    let _ = handle_completion_request(
6323        &mut cx,
6324        indoc! {"
6325            one.|<>
6326            two
6327            three
6328        "},
6329        vec!["completion_a", "completion_b"],
6330    );
6331    handle_copilot_completion_request(
6332        &copilot_lsp,
6333        vec![copilot::request::Completion {
6334            text: "one.copilot1".into(),
6335            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6336            ..Default::default()
6337        }],
6338        vec![],
6339    );
6340    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6341    cx.update_editor(|editor, cx| {
6342        assert!(editor.context_menu_visible());
6343        assert!(!editor.has_active_copilot_suggestion(cx));
6344
6345        // Confirming a completion inserts it and hides the context menu, without showing
6346        // the copilot suggestion afterwards.
6347        editor
6348            .confirm_completion(&Default::default(), cx)
6349            .unwrap()
6350            .detach();
6351        assert!(!editor.context_menu_visible());
6352        assert!(!editor.has_active_copilot_suggestion(cx));
6353        assert_eq!(editor.text(cx), "one.completion_a\ntwo\nthree\n");
6354        assert_eq!(editor.display_text(cx), "one.completion_a\ntwo\nthree\n");
6355    });
6356
6357    // Ensure Copilot suggestions are shown right away if no autocompletion is available.
6358    cx.set_state(indoc! {"
6359        oneˇ
6360        two
6361        three
6362    "});
6363    cx.simulate_keystroke(".");
6364    let _ = handle_completion_request(
6365        &mut cx,
6366        indoc! {"
6367            one.|<>
6368            two
6369            three
6370        "},
6371        vec![],
6372    );
6373    handle_copilot_completion_request(
6374        &copilot_lsp,
6375        vec![copilot::request::Completion {
6376            text: "one.copilot1".into(),
6377            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6378            ..Default::default()
6379        }],
6380        vec![],
6381    );
6382    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6383    cx.update_editor(|editor, cx| {
6384        assert!(!editor.context_menu_visible());
6385        assert!(editor.has_active_copilot_suggestion(cx));
6386        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
6387        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
6388    });
6389
6390    // Reset editor, and ensure autocompletion is still favored over Copilot suggestions.
6391    cx.set_state(indoc! {"
6392        oneˇ
6393        two
6394        three
6395    "});
6396    cx.simulate_keystroke(".");
6397    let _ = handle_completion_request(
6398        &mut cx,
6399        indoc! {"
6400            one.|<>
6401            two
6402            three
6403        "},
6404        vec!["completion_a", "completion_b"],
6405    );
6406    handle_copilot_completion_request(
6407        &copilot_lsp,
6408        vec![copilot::request::Completion {
6409            text: "one.copilot1".into(),
6410            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6411            ..Default::default()
6412        }],
6413        vec![],
6414    );
6415    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6416    cx.update_editor(|editor, cx| {
6417        assert!(editor.context_menu_visible());
6418        assert!(!editor.has_active_copilot_suggestion(cx));
6419
6420        // When hiding the context menu, the Copilot suggestion becomes visible.
6421        editor.hide_context_menu(cx);
6422        assert!(!editor.context_menu_visible());
6423        assert!(editor.has_active_copilot_suggestion(cx));
6424        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
6425        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
6426    });
6427
6428    // Ensure existing completion is interpolated when inserting again.
6429    cx.simulate_keystroke("c");
6430    deterministic.run_until_parked();
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.copilot1\ntwo\nthree\n");
6435        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6436    });
6437
6438    // After debouncing, new Copilot completions should be requested.
6439    handle_copilot_completion_request(
6440        &copilot_lsp,
6441        vec![copilot::request::Completion {
6442            text: "one.copilot2".into(),
6443            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 5)),
6444            ..Default::default()
6445        }],
6446        vec![],
6447    );
6448    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6449    cx.update_editor(|editor, cx| {
6450        assert!(!editor.context_menu_visible());
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        // Canceling should remove the active Copilot suggestion.
6456        editor.cancel(&Default::default(), cx);
6457        assert!(!editor.has_active_copilot_suggestion(cx));
6458        assert_eq!(editor.display_text(cx), "one.c\ntwo\nthree\n");
6459        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6460
6461        // After canceling, tabbing shouldn't insert the previously shown suggestion.
6462        editor.tab(&Default::default(), cx);
6463        assert!(!editor.has_active_copilot_suggestion(cx));
6464        assert_eq!(editor.display_text(cx), "one.c   \ntwo\nthree\n");
6465        assert_eq!(editor.text(cx), "one.c   \ntwo\nthree\n");
6466
6467        // When undoing the previously active suggestion is shown again.
6468        editor.undo(&Default::default(), cx);
6469        assert!(editor.has_active_copilot_suggestion(cx));
6470        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6471        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6472    });
6473
6474    // If an edit occurs outside of this editor, the suggestion is still correctly interpolated.
6475    cx.update_buffer(|buffer, cx| buffer.edit([(5..5, "o")], None, cx));
6476    cx.update_editor(|editor, cx| {
6477        assert!(editor.has_active_copilot_suggestion(cx));
6478        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6479        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6480
6481        // Tabbing when there is an active suggestion inserts it.
6482        editor.tab(&Default::default(), cx);
6483        assert!(!editor.has_active_copilot_suggestion(cx));
6484        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6485        assert_eq!(editor.text(cx), "one.copilot2\ntwo\nthree\n");
6486
6487        // When undoing the previously active suggestion is shown again.
6488        editor.undo(&Default::default(), cx);
6489        assert!(editor.has_active_copilot_suggestion(cx));
6490        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6491        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6492
6493        // Hide suggestion.
6494        editor.cancel(&Default::default(), cx);
6495        assert!(!editor.has_active_copilot_suggestion(cx));
6496        assert_eq!(editor.display_text(cx), "one.co\ntwo\nthree\n");
6497        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6498    });
6499
6500    // If an edit occurs outside of this editor but no suggestion is being shown,
6501    // we won't make it visible.
6502    cx.update_buffer(|buffer, cx| buffer.edit([(6..6, "p")], None, cx));
6503    cx.update_editor(|editor, cx| {
6504        assert!(!editor.has_active_copilot_suggestion(cx));
6505        assert_eq!(editor.display_text(cx), "one.cop\ntwo\nthree\n");
6506        assert_eq!(editor.text(cx), "one.cop\ntwo\nthree\n");
6507    });
6508
6509    // Reset the editor to verify how suggestions behave when tabbing on leading indentation.
6510    cx.update_editor(|editor, cx| {
6511        editor.set_text("fn foo() {\n  \n}", cx);
6512        editor.change_selections(None, cx, |s| {
6513            s.select_ranges([Point::new(1, 2)..Point::new(1, 2)])
6514        });
6515    });
6516    handle_copilot_completion_request(
6517        &copilot_lsp,
6518        vec![copilot::request::Completion {
6519            text: "    let x = 4;".into(),
6520            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
6521            ..Default::default()
6522        }],
6523        vec![],
6524    );
6525
6526    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
6527    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6528    cx.update_editor(|editor, cx| {
6529        assert!(editor.has_active_copilot_suggestion(cx));
6530        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6531        assert_eq!(editor.text(cx), "fn foo() {\n  \n}");
6532
6533        // Tabbing inside of leading whitespace inserts indentation without accepting the suggestion.
6534        editor.tab(&Default::default(), cx);
6535        assert!(editor.has_active_copilot_suggestion(cx));
6536        assert_eq!(editor.text(cx), "fn foo() {\n    \n}");
6537        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6538
6539        // Tabbing again accepts the suggestion.
6540        editor.tab(&Default::default(), cx);
6541        assert!(!editor.has_active_copilot_suggestion(cx));
6542        assert_eq!(editor.text(cx), "fn foo() {\n    let x = 4;\n}");
6543        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6544    });
6545}
6546
6547#[gpui::test]
6548async fn test_copilot_completion_invalidation(
6549    deterministic: Arc<Deterministic>,
6550    cx: &mut gpui::TestAppContext,
6551) {
6552    init_test(cx, |_| {});
6553
6554    let (copilot, copilot_lsp) = Copilot::fake(cx);
6555    cx.update(|cx| cx.set_global(copilot));
6556    let mut cx = EditorLspTestContext::new_rust(
6557        lsp::ServerCapabilities {
6558            completion_provider: Some(lsp::CompletionOptions {
6559                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
6560                ..Default::default()
6561            }),
6562            ..Default::default()
6563        },
6564        cx,
6565    )
6566    .await;
6567
6568    cx.set_state(indoc! {"
6569        one
6570        twˇ
6571        three
6572    "});
6573
6574    handle_copilot_completion_request(
6575        &copilot_lsp,
6576        vec![copilot::request::Completion {
6577            text: "two.foo()".into(),
6578            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
6579            ..Default::default()
6580        }],
6581        vec![],
6582    );
6583    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
6584    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6585    cx.update_editor(|editor, cx| {
6586        assert!(editor.has_active_copilot_suggestion(cx));
6587        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6588        assert_eq!(editor.text(cx), "one\ntw\nthree\n");
6589
6590        editor.backspace(&Default::default(), cx);
6591        assert!(editor.has_active_copilot_suggestion(cx));
6592        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6593        assert_eq!(editor.text(cx), "one\nt\nthree\n");
6594
6595        editor.backspace(&Default::default(), cx);
6596        assert!(editor.has_active_copilot_suggestion(cx));
6597        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6598        assert_eq!(editor.text(cx), "one\n\nthree\n");
6599
6600        // Deleting across the original suggestion range invalidates it.
6601        editor.backspace(&Default::default(), cx);
6602        assert!(!editor.has_active_copilot_suggestion(cx));
6603        assert_eq!(editor.display_text(cx), "one\nthree\n");
6604        assert_eq!(editor.text(cx), "one\nthree\n");
6605
6606        // Undoing the deletion restores the suggestion.
6607        editor.undo(&Default::default(), cx);
6608        assert!(editor.has_active_copilot_suggestion(cx));
6609        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6610        assert_eq!(editor.text(cx), "one\n\nthree\n");
6611    });
6612}
6613
6614#[gpui::test]
6615async fn test_copilot_multibuffer(
6616    deterministic: Arc<Deterministic>,
6617    cx: &mut gpui::TestAppContext,
6618) {
6619    init_test(cx, |_| {});
6620
6621    let (copilot, copilot_lsp) = Copilot::fake(cx);
6622    cx.update(|cx| cx.set_global(copilot));
6623
6624    let buffer_1 = cx.add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx));
6625    let buffer_2 = cx.add_model(|cx| Buffer::new(0, "c = 3\nd = 4\n", cx));
6626    let multibuffer = cx.add_model(|cx| {
6627        let mut multibuffer = MultiBuffer::new(0);
6628        multibuffer.push_excerpts(
6629            buffer_1.clone(),
6630            [ExcerptRange {
6631                context: Point::new(0, 0)..Point::new(2, 0),
6632                primary: None,
6633            }],
6634            cx,
6635        );
6636        multibuffer.push_excerpts(
6637            buffer_2.clone(),
6638            [ExcerptRange {
6639                context: Point::new(0, 0)..Point::new(2, 0),
6640                primary: None,
6641            }],
6642            cx,
6643        );
6644        multibuffer
6645    });
6646    let (_, editor) = cx.add_window(|cx| build_editor(multibuffer, cx));
6647
6648    handle_copilot_completion_request(
6649        &copilot_lsp,
6650        vec![copilot::request::Completion {
6651            text: "b = 2 + a".into(),
6652            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 5)),
6653            ..Default::default()
6654        }],
6655        vec![],
6656    );
6657    editor.update(cx, |editor, cx| {
6658        // Ensure copilot suggestions are shown for the first excerpt.
6659        editor.change_selections(None, cx, |s| {
6660            s.select_ranges([Point::new(1, 5)..Point::new(1, 5)])
6661        });
6662        editor.next_copilot_suggestion(&Default::default(), cx);
6663    });
6664    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6665    editor.update(cx, |editor, cx| {
6666        assert!(editor.has_active_copilot_suggestion(cx));
6667        assert_eq!(
6668            editor.display_text(cx),
6669            "\n\na = 1\nb = 2 + a\n\n\n\nc = 3\nd = 4\n"
6670        );
6671        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
6672    });
6673
6674    handle_copilot_completion_request(
6675        &copilot_lsp,
6676        vec![copilot::request::Completion {
6677            text: "d = 4 + c".into(),
6678            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 6)),
6679            ..Default::default()
6680        }],
6681        vec![],
6682    );
6683    editor.update(cx, |editor, cx| {
6684        // Move to another excerpt, ensuring the suggestion gets cleared.
6685        editor.change_selections(None, cx, |s| {
6686            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
6687        });
6688        assert!(!editor.has_active_copilot_suggestion(cx));
6689        assert_eq!(
6690            editor.display_text(cx),
6691            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4\n"
6692        );
6693        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
6694
6695        // Type a character, ensuring we don't even try to interpolate the previous suggestion.
6696        editor.handle_input(" ", cx);
6697        assert!(!editor.has_active_copilot_suggestion(cx));
6698        assert_eq!(
6699            editor.display_text(cx),
6700            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 \n"
6701        );
6702        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
6703    });
6704
6705    // Ensure the new suggestion is displayed when the debounce timeout expires.
6706    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6707    editor.update(cx, |editor, cx| {
6708        assert!(editor.has_active_copilot_suggestion(cx));
6709        assert_eq!(
6710            editor.display_text(cx),
6711            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 + c\n"
6712        );
6713        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
6714    });
6715}
6716
6717#[gpui::test]
6718async fn test_copilot_disabled_globs(
6719    deterministic: Arc<Deterministic>,
6720    cx: &mut gpui::TestAppContext,
6721) {
6722    init_test(cx, |settings| {
6723        settings
6724            .copilot
6725            .get_or_insert(Default::default())
6726            .disabled_globs = Some(vec![".env*".to_string()]);
6727    });
6728
6729    let (copilot, copilot_lsp) = Copilot::fake(cx);
6730    cx.update(|cx| cx.set_global(copilot));
6731
6732    let fs = FakeFs::new(cx.background());
6733    fs.insert_tree(
6734        "/test",
6735        json!({
6736            ".env": "SECRET=something\n",
6737            "README.md": "hello\n"
6738        }),
6739    )
6740    .await;
6741    let project = Project::test(fs, ["/test".as_ref()], cx).await;
6742
6743    let private_buffer = project
6744        .update(cx, |project, cx| {
6745            project.open_local_buffer("/test/.env", cx)
6746        })
6747        .await
6748        .unwrap();
6749    let public_buffer = project
6750        .update(cx, |project, cx| {
6751            project.open_local_buffer("/test/README.md", cx)
6752        })
6753        .await
6754        .unwrap();
6755
6756    let multibuffer = cx.add_model(|cx| {
6757        let mut multibuffer = MultiBuffer::new(0);
6758        multibuffer.push_excerpts(
6759            private_buffer.clone(),
6760            [ExcerptRange {
6761                context: Point::new(0, 0)..Point::new(1, 0),
6762                primary: None,
6763            }],
6764            cx,
6765        );
6766        multibuffer.push_excerpts(
6767            public_buffer.clone(),
6768            [ExcerptRange {
6769                context: Point::new(0, 0)..Point::new(1, 0),
6770                primary: None,
6771            }],
6772            cx,
6773        );
6774        multibuffer
6775    });
6776    let (_, editor) = cx.add_window(|cx| build_editor(multibuffer, cx));
6777
6778    let mut copilot_requests = copilot_lsp
6779        .handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| async move {
6780            Ok(copilot::request::GetCompletionsResult {
6781                completions: vec![copilot::request::Completion {
6782                    text: "next line".into(),
6783                    range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
6784                    ..Default::default()
6785                }],
6786            })
6787        });
6788
6789    editor.update(cx, |editor, cx| {
6790        editor.change_selections(None, cx, |selections| {
6791            selections.select_ranges([Point::new(0, 0)..Point::new(0, 0)])
6792        });
6793        editor.next_copilot_suggestion(&Default::default(), cx);
6794    });
6795
6796    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6797    assert!(copilot_requests.try_next().is_err());
6798
6799    editor.update(cx, |editor, cx| {
6800        editor.change_selections(None, cx, |s| {
6801            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
6802        });
6803        editor.next_copilot_suggestion(&Default::default(), cx);
6804    });
6805
6806    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6807    assert!(copilot_requests.try_next().is_ok());
6808}
6809
6810fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
6811    let point = DisplayPoint::new(row as u32, column as u32);
6812    point..point
6813}
6814
6815fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
6816    let (text, ranges) = marked_text_ranges(marked_text, true);
6817    assert_eq!(view.text(cx), text);
6818    assert_eq!(
6819        view.selections.ranges(cx),
6820        ranges,
6821        "Assert selections are {}",
6822        marked_text
6823    );
6824}
6825
6826/// Handle completion request passing a marked string specifying where the completion
6827/// should be triggered from using '|' character, what range should be replaced, and what completions
6828/// should be returned using '<' and '>' to delimit the range
6829fn handle_completion_request<'a>(
6830    cx: &mut EditorLspTestContext<'a>,
6831    marked_string: &str,
6832    completions: Vec<&'static str>,
6833) -> impl Future<Output = ()> {
6834    let complete_from_marker: TextRangeMarker = '|'.into();
6835    let replace_range_marker: TextRangeMarker = ('<', '>').into();
6836    let (_, mut marked_ranges) = marked_text_ranges_by(
6837        marked_string,
6838        vec![complete_from_marker.clone(), replace_range_marker.clone()],
6839    );
6840
6841    let complete_from_position =
6842        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
6843    let replace_range =
6844        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
6845
6846    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
6847        let completions = completions.clone();
6848        async move {
6849            assert_eq!(params.text_document_position.text_document.uri, url.clone());
6850            assert_eq!(
6851                params.text_document_position.position,
6852                complete_from_position
6853            );
6854            Ok(Some(lsp::CompletionResponse::Array(
6855                completions
6856                    .iter()
6857                    .map(|completion_text| lsp::CompletionItem {
6858                        label: completion_text.to_string(),
6859                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
6860                            range: replace_range,
6861                            new_text: completion_text.to_string(),
6862                        })),
6863                        ..Default::default()
6864                    })
6865                    .collect(),
6866            )))
6867        }
6868    });
6869
6870    async move {
6871        request.next().await;
6872    }
6873}
6874
6875fn handle_resolve_completion_request<'a>(
6876    cx: &mut EditorLspTestContext<'a>,
6877    edits: Option<Vec<(&'static str, &'static str)>>,
6878) -> impl Future<Output = ()> {
6879    let edits = edits.map(|edits| {
6880        edits
6881            .iter()
6882            .map(|(marked_string, new_text)| {
6883                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
6884                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
6885                lsp::TextEdit::new(replace_range, new_text.to_string())
6886            })
6887            .collect::<Vec<_>>()
6888    });
6889
6890    let mut request =
6891        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
6892            let edits = edits.clone();
6893            async move {
6894                Ok(lsp::CompletionItem {
6895                    additional_text_edits: edits,
6896                    ..Default::default()
6897                })
6898            }
6899        });
6900
6901    async move {
6902        request.next().await;
6903    }
6904}
6905
6906fn handle_copilot_completion_request(
6907    lsp: &lsp::FakeLanguageServer,
6908    completions: Vec<copilot::request::Completion>,
6909    completions_cycling: Vec<copilot::request::Completion>,
6910) {
6911    lsp.handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| {
6912        let completions = completions.clone();
6913        async move {
6914            Ok(copilot::request::GetCompletionsResult {
6915                completions: completions.clone(),
6916            })
6917        }
6918    });
6919    lsp.handle_request::<copilot::request::GetCompletionsCycling, _, _>(move |_params, _cx| {
6920        let completions_cycling = completions_cycling.clone();
6921        async move {
6922            Ok(copilot::request::GetCompletionsResult {
6923                completions: completions_cycling.clone(),
6924            })
6925        }
6926    });
6927}
6928
6929pub(crate) fn update_test_settings(
6930    cx: &mut TestAppContext,
6931    f: impl Fn(&mut AllLanguageSettingsContent),
6932) {
6933    cx.update(|cx| {
6934        cx.update_global::<SettingsStore, _, _>(|store, cx| {
6935            store.update_user_settings::<AllLanguageSettings>(cx, f);
6936        });
6937    });
6938}
6939
6940pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
6941    cx.foreground().forbid_parking();
6942
6943    cx.update(|cx| {
6944        cx.set_global(SettingsStore::test(cx));
6945        theme::init((), cx);
6946        client::init_settings(cx);
6947        language::init(cx);
6948        Project::init_settings(cx);
6949        workspace::init_settings(cx);
6950        crate::init(cx);
6951    });
6952
6953    update_test_settings(cx, f);
6954}