editor_tests.rs

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