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