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