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