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