editor_tests.rs

   1use super::*;
   2use crate::{
   3    scroll::scroll_amount::ScrollAmount,
   4    test::{
   5        assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
   6        editor_test_context::EditorTestContext, select_ranges,
   7    },
   8    JoinLines,
   9};
  10use futures::StreamExt;
  11use gpui::{div, TestAppContext, VisualTestContext, WindowOptions};
  12use indoc::indoc;
  13use language::{
  14    language_settings::{AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent},
  15    BracketPairConfig,
  16    Capability::ReadWrite,
  17    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, Override, Point,
  18};
  19use parking_lot::Mutex;
  20use project::project_settings::{LspSettings, ProjectSettings};
  21use project::FakeFs;
  22use serde_json::{self, json};
  23use std::sync::atomic;
  24use std::sync::atomic::AtomicUsize;
  25use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
  26use unindent::Unindent;
  27use util::{
  28    assert_set_eq,
  29    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
  30};
  31use workspace::{
  32    item::{FollowEvent, FollowableItem, Item, ItemHandle},
  33    NavigationEntry, ViewId,
  34};
  35
  36#[gpui::test]
  37fn test_edit_events(cx: &mut TestAppContext) {
  38    init_test(cx, |_| {});
  39
  40    let buffer = cx.new_model(|cx| {
  41        let mut buffer =
  42            language::Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "123456");
  43        buffer.set_group_interval(Duration::from_secs(1));
  44        buffer
  45    });
  46
  47    let events = Rc::new(RefCell::new(Vec::new()));
  48    let editor1 = cx.add_window({
  49        let events = events.clone();
  50        |cx| {
  51            let view = cx.view().clone();
  52            cx.subscribe(&view, move |_, _, event: &EditorEvent, _| {
  53                if matches!(event, EditorEvent::Edited | EditorEvent::BufferEdited) {
  54                    events.borrow_mut().push(("editor1", event.clone()));
  55                }
  56            })
  57            .detach();
  58            Editor::for_buffer(buffer.clone(), None, cx)
  59        }
  60    });
  61
  62    let editor2 = cx.add_window({
  63        let events = events.clone();
  64        |cx| {
  65            cx.subscribe(&cx.view().clone(), move |_, _, event: &EditorEvent, _| {
  66                if matches!(event, EditorEvent::Edited | EditorEvent::BufferEdited) {
  67                    events.borrow_mut().push(("editor2", event.clone()));
  68                }
  69            })
  70            .detach();
  71            Editor::for_buffer(buffer.clone(), None, cx)
  72        }
  73    });
  74
  75    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  76
  77    // Mutating editor 1 will emit an `Edited` event only for that editor.
  78    _ = editor1.update(cx, |editor, cx| editor.insert("X", cx));
  79    assert_eq!(
  80        mem::take(&mut *events.borrow_mut()),
  81        [
  82            ("editor1", EditorEvent::Edited),
  83            ("editor1", EditorEvent::BufferEdited),
  84            ("editor2", EditorEvent::BufferEdited),
  85        ]
  86    );
  87
  88    // Mutating editor 2 will emit an `Edited` event only for that editor.
  89    _ = editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
  90    assert_eq!(
  91        mem::take(&mut *events.borrow_mut()),
  92        [
  93            ("editor2", EditorEvent::Edited),
  94            ("editor1", EditorEvent::BufferEdited),
  95            ("editor2", EditorEvent::BufferEdited),
  96        ]
  97    );
  98
  99    // Undoing on editor 1 will emit an `Edited` event only for that editor.
 100    _ = editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
 101    assert_eq!(
 102        mem::take(&mut *events.borrow_mut()),
 103        [
 104            ("editor1", EditorEvent::Edited),
 105            ("editor1", EditorEvent::BufferEdited),
 106            ("editor2", EditorEvent::BufferEdited),
 107        ]
 108    );
 109
 110    // Redoing on editor 1 will emit an `Edited` event only for that editor.
 111    _ = editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
 112    assert_eq!(
 113        mem::take(&mut *events.borrow_mut()),
 114        [
 115            ("editor1", EditorEvent::Edited),
 116            ("editor1", EditorEvent::BufferEdited),
 117            ("editor2", EditorEvent::BufferEdited),
 118        ]
 119    );
 120
 121    // Undoing on editor 2 will emit an `Edited` event only for that editor.
 122    _ = editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
 123    assert_eq!(
 124        mem::take(&mut *events.borrow_mut()),
 125        [
 126            ("editor2", EditorEvent::Edited),
 127            ("editor1", EditorEvent::BufferEdited),
 128            ("editor2", EditorEvent::BufferEdited),
 129        ]
 130    );
 131
 132    // Redoing on editor 2 will emit an `Edited` event only for that editor.
 133    _ = editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
 134    assert_eq!(
 135        mem::take(&mut *events.borrow_mut()),
 136        [
 137            ("editor2", EditorEvent::Edited),
 138            ("editor1", EditorEvent::BufferEdited),
 139            ("editor2", EditorEvent::BufferEdited),
 140        ]
 141    );
 142
 143    // No event is emitted when the mutation is a no-op.
 144    _ = editor2.update(cx, |editor, cx| {
 145        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 146
 147        editor.backspace(&Backspace, cx);
 148    });
 149    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
 150}
 151
 152#[gpui::test]
 153fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
 154    init_test(cx, |_| {});
 155
 156    let mut now = Instant::now();
 157    let buffer = cx.new_model(|cx| {
 158        language::Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "123456")
 159    });
 160    let group_interval = buffer.update(cx, |buffer, _| buffer.transaction_group_interval());
 161    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 162    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 163
 164    _ = editor.update(cx, |editor, cx| {
 165        editor.start_transaction_at(now, cx);
 166        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
 167
 168        editor.insert("cd", cx);
 169        editor.end_transaction_at(now, cx);
 170        assert_eq!(editor.text(cx), "12cd56");
 171        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
 172
 173        editor.start_transaction_at(now, cx);
 174        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
 175        editor.insert("e", cx);
 176        editor.end_transaction_at(now, cx);
 177        assert_eq!(editor.text(cx), "12cde6");
 178        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
 179
 180        now += group_interval + Duration::from_millis(1);
 181        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
 182
 183        // Simulate an edit in another editor
 184        _ = buffer.update(cx, |buffer, cx| {
 185            buffer.start_transaction_at(now, cx);
 186            buffer.edit([(0..1, "a")], None, cx);
 187            buffer.edit([(1..1, "b")], None, cx);
 188            buffer.end_transaction_at(now, cx);
 189        });
 190
 191        assert_eq!(editor.text(cx), "ab2cde6");
 192        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
 193
 194        // Last transaction happened past the group interval in a different editor.
 195        // Undo it individually and don't restore selections.
 196        editor.undo(&Undo, cx);
 197        assert_eq!(editor.text(cx), "12cde6");
 198        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
 199
 200        // First two transactions happened within the group interval in this editor.
 201        // Undo them together and restore selections.
 202        editor.undo(&Undo, cx);
 203        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
 204        assert_eq!(editor.text(cx), "123456");
 205        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
 206
 207        // Redo the first two transactions together.
 208        editor.redo(&Redo, cx);
 209        assert_eq!(editor.text(cx), "12cde6");
 210        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
 211
 212        // Redo the last transaction on its own.
 213        editor.redo(&Redo, cx);
 214        assert_eq!(editor.text(cx), "ab2cde6");
 215        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
 216
 217        // Test empty transactions.
 218        editor.start_transaction_at(now, cx);
 219        editor.end_transaction_at(now, cx);
 220        editor.undo(&Undo, cx);
 221        assert_eq!(editor.text(cx), "12cde6");
 222    });
 223}
 224
 225#[gpui::test]
 226fn test_ime_composition(cx: &mut TestAppContext) {
 227    init_test(cx, |_| {});
 228
 229    let buffer = cx.new_model(|cx| {
 230        let mut buffer =
 231            language::Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "abcde");
 232        // Ensure automatic grouping doesn't occur.
 233        buffer.set_group_interval(Duration::ZERO);
 234        buffer
 235    });
 236
 237    let buffer = cx.new_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    init_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
 338    _ = editor.update(cx, |view, cx| {
 339        view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
 340    });
 341    assert_eq!(
 342        editor
 343            .update(cx, |view, cx| view.selections.display_ranges(cx))
 344            .unwrap(),
 345        [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
 346    );
 347
 348    _ = editor.update(cx, |view, cx| {
 349        view.update_selection(
 350            DisplayPoint::new(3, 3),
 351            0,
 352            gpui::Point::<f32>::default(),
 353            cx,
 354        );
 355    });
 356
 357    assert_eq!(
 358        editor
 359            .update(cx, |view, cx| view.selections.display_ranges(cx))
 360            .unwrap(),
 361        [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
 362    );
 363
 364    _ = editor.update(cx, |view, cx| {
 365        view.update_selection(
 366            DisplayPoint::new(1, 1),
 367            0,
 368            gpui::Point::<f32>::default(),
 369            cx,
 370        );
 371    });
 372
 373    assert_eq!(
 374        editor
 375            .update(cx, |view, cx| view.selections.display_ranges(cx))
 376            .unwrap(),
 377        [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
 378    );
 379
 380    _ = editor.update(cx, |view, cx| {
 381        view.end_selection(cx);
 382        view.update_selection(
 383            DisplayPoint::new(3, 3),
 384            0,
 385            gpui::Point::<f32>::default(),
 386            cx,
 387        );
 388    });
 389
 390    assert_eq!(
 391        editor
 392            .update(cx, |view, cx| view.selections.display_ranges(cx))
 393            .unwrap(),
 394        [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
 395    );
 396
 397    _ = editor.update(cx, |view, cx| {
 398        view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
 399        view.update_selection(
 400            DisplayPoint::new(0, 0),
 401            0,
 402            gpui::Point::<f32>::default(),
 403            cx,
 404        );
 405    });
 406
 407    assert_eq!(
 408        editor
 409            .update(cx, |view, cx| view.selections.display_ranges(cx))
 410            .unwrap(),
 411        [
 412            DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
 413            DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
 414        ]
 415    );
 416
 417    _ = editor.update(cx, |view, cx| {
 418        view.end_selection(cx);
 419    });
 420
 421    assert_eq!(
 422        editor
 423            .update(cx, |view, cx| view.selections.display_ranges(cx))
 424            .unwrap(),
 425        [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
 426    );
 427}
 428
 429#[gpui::test]
 430fn test_canceling_pending_selection(cx: &mut TestAppContext) {
 431    init_test(cx, |_| {});
 432
 433    let view = cx.add_window(|cx| {
 434        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
 435        build_editor(buffer, cx)
 436    });
 437
 438    _ = view.update(cx, |view, cx| {
 439        view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
 440        assert_eq!(
 441            view.selections.display_ranges(cx),
 442            [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
 443        );
 444    });
 445
 446    _ = view.update(cx, |view, cx| {
 447        view.update_selection(
 448            DisplayPoint::new(3, 3),
 449            0,
 450            gpui::Point::<f32>::default(),
 451            cx,
 452        );
 453        assert_eq!(
 454            view.selections.display_ranges(cx),
 455            [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
 456        );
 457    });
 458
 459    _ = view.update(cx, |view, cx| {
 460        view.cancel(&Cancel, cx);
 461        view.update_selection(
 462            DisplayPoint::new(1, 1),
 463            0,
 464            gpui::Point::<f32>::default(),
 465            cx,
 466        );
 467        assert_eq!(
 468            view.selections.display_ranges(cx),
 469            [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
 470        );
 471    });
 472}
 473
 474#[gpui::test]
 475fn test_clone(cx: &mut TestAppContext) {
 476    init_test(cx, |_| {});
 477
 478    let (text, selection_ranges) = marked_text_ranges(
 479        indoc! {"
 480            one
 481            two
 482            threeˇ
 483            four
 484            fiveˇ
 485        "},
 486        true,
 487    );
 488
 489    let editor = cx.add_window(|cx| {
 490        let buffer = MultiBuffer::build_simple(&text, cx);
 491        build_editor(buffer, cx)
 492    });
 493
 494    _ = editor.update(cx, |editor, cx| {
 495        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
 496        editor.fold_ranges(
 497            [
 498                Point::new(1, 0)..Point::new(2, 0),
 499                Point::new(3, 0)..Point::new(4, 0),
 500            ],
 501            true,
 502            cx,
 503        );
 504    });
 505
 506    let cloned_editor = editor
 507        .update(cx, |editor, cx| {
 508            cx.open_window(Default::default(), |cx| cx.new_view(|cx| editor.clone(cx)))
 509        })
 510        .unwrap();
 511
 512    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
 513    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
 514
 515    assert_eq!(
 516        cloned_editor
 517            .update(cx, |e, cx| e.display_text(cx))
 518            .unwrap(),
 519        editor.update(cx, |e, cx| e.display_text(cx)).unwrap()
 520    );
 521    assert_eq!(
 522        cloned_snapshot
 523            .folds_in_range(0..text.len())
 524            .collect::<Vec<_>>(),
 525        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
 526    );
 527    assert_set_eq!(
 528        cloned_editor
 529            .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
 530            .unwrap(),
 531        editor
 532            .update(cx, |editor, cx| editor.selections.ranges(cx))
 533            .unwrap()
 534    );
 535    assert_set_eq!(
 536        cloned_editor
 537            .update(cx, |e, cx| e.selections.display_ranges(cx))
 538            .unwrap(),
 539        editor
 540            .update(cx, |e, cx| e.selections.display_ranges(cx))
 541            .unwrap()
 542    );
 543}
 544
 545#[gpui::test]
 546async fn test_navigation_history(cx: &mut TestAppContext) {
 547    init_test(cx, |_| {});
 548
 549    use workspace::item::Item;
 550
 551    let fs = FakeFs::new(cx.executor());
 552    let project = Project::test(fs, [], cx).await;
 553    let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
 554    let pane = workspace
 555        .update(cx, |workspace, _| workspace.active_pane().clone())
 556        .unwrap();
 557
 558    _ = workspace.update(cx, |_v, cx| {
 559        cx.new_view(|cx| {
 560            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
 561            let mut editor = build_editor(buffer.clone(), cx);
 562            let handle = cx.view();
 563            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
 564
 565            fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
 566                editor.nav_history.as_mut().unwrap().pop_backward(cx)
 567            }
 568
 569            // Move the cursor a small distance.
 570            // Nothing is added to the navigation history.
 571            editor.change_selections(None, cx, |s| {
 572                s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
 573            });
 574            editor.change_selections(None, cx, |s| {
 575                s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
 576            });
 577            assert!(pop_history(&mut editor, cx).is_none());
 578
 579            // Move the cursor a large distance.
 580            // The history can jump back to the previous position.
 581            editor.change_selections(None, cx, |s| {
 582                s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
 583            });
 584            let nav_entry = pop_history(&mut editor, cx).unwrap();
 585            editor.navigate(nav_entry.data.unwrap(), cx);
 586            assert_eq!(nav_entry.item.id(), cx.entity_id());
 587            assert_eq!(
 588                editor.selections.display_ranges(cx),
 589                &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
 590            );
 591            assert!(pop_history(&mut editor, cx).is_none());
 592
 593            // Move the cursor a small distance via the mouse.
 594            // Nothing is added to the navigation history.
 595            editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
 596            editor.end_selection(cx);
 597            assert_eq!(
 598                editor.selections.display_ranges(cx),
 599                &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
 600            );
 601            assert!(pop_history(&mut editor, cx).is_none());
 602
 603            // Move the cursor a large distance via the mouse.
 604            // The history can jump back to the previous position.
 605            editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
 606            editor.end_selection(cx);
 607            assert_eq!(
 608                editor.selections.display_ranges(cx),
 609                &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
 610            );
 611            let nav_entry = pop_history(&mut editor, cx).unwrap();
 612            editor.navigate(nav_entry.data.unwrap(), cx);
 613            assert_eq!(nav_entry.item.id(), cx.entity_id());
 614            assert_eq!(
 615                editor.selections.display_ranges(cx),
 616                &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
 617            );
 618            assert!(pop_history(&mut editor, cx).is_none());
 619
 620            // Set scroll position to check later
 621            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), cx);
 622            let original_scroll_position = editor.scroll_manager.anchor();
 623
 624            // Jump to the end of the document and adjust scroll
 625            editor.move_to_end(&MoveToEnd, cx);
 626            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), cx);
 627            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
 628
 629            let nav_entry = pop_history(&mut editor, cx).unwrap();
 630            editor.navigate(nav_entry.data.unwrap(), cx);
 631            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
 632
 633            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
 634            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
 635            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
 636            let invalid_point = Point::new(9999, 0);
 637            editor.navigate(
 638                Box::new(NavigationData {
 639                    cursor_anchor: invalid_anchor,
 640                    cursor_position: invalid_point,
 641                    scroll_anchor: ScrollAnchor {
 642                        anchor: invalid_anchor,
 643                        offset: Default::default(),
 644                    },
 645                    scroll_top_row: invalid_point.row,
 646                }),
 647                cx,
 648            );
 649            assert_eq!(
 650                editor.selections.display_ranges(cx),
 651                &[editor.max_point(cx)..editor.max_point(cx)]
 652            );
 653            assert_eq!(
 654                editor.scroll_position(cx),
 655                gpui::Point::new(0., editor.max_point(cx).row() as f32)
 656            );
 657
 658            editor
 659        })
 660    });
 661}
 662
 663#[gpui::test]
 664fn test_cancel(cx: &mut TestAppContext) {
 665    init_test(cx, |_| {});
 666
 667    let view = cx.add_window(|cx| {
 668        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
 669        build_editor(buffer, cx)
 670    });
 671
 672    _ = view.update(cx, |view, cx| {
 673        view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
 674        view.update_selection(
 675            DisplayPoint::new(1, 1),
 676            0,
 677            gpui::Point::<f32>::default(),
 678            cx,
 679        );
 680        view.end_selection(cx);
 681
 682        view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
 683        view.update_selection(
 684            DisplayPoint::new(0, 3),
 685            0,
 686            gpui::Point::<f32>::default(),
 687            cx,
 688        );
 689        view.end_selection(cx);
 690        assert_eq!(
 691            view.selections.display_ranges(cx),
 692            [
 693                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
 694                DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
 695            ]
 696        );
 697    });
 698
 699    _ = view.update(cx, |view, cx| {
 700        view.cancel(&Cancel, cx);
 701        assert_eq!(
 702            view.selections.display_ranges(cx),
 703            [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
 704        );
 705    });
 706
 707    _ = view.update(cx, |view, cx| {
 708        view.cancel(&Cancel, cx);
 709        assert_eq!(
 710            view.selections.display_ranges(cx),
 711            [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
 712        );
 713    });
 714}
 715
 716#[gpui::test]
 717fn test_fold_action(cx: &mut TestAppContext) {
 718    init_test(cx, |_| {});
 719
 720    let view = cx.add_window(|cx| {
 721        let buffer = MultiBuffer::build_simple(
 722            &"
 723                impl Foo {
 724                    // Hello!
 725
 726                    fn a() {
 727                        1
 728                    }
 729
 730                    fn b() {
 731                        2
 732                    }
 733
 734                    fn c() {
 735                        3
 736                    }
 737                }
 738            "
 739            .unindent(),
 740            cx,
 741        );
 742        build_editor(buffer.clone(), cx)
 743    });
 744
 745    _ = view.update(cx, |view, cx| {
 746        view.change_selections(None, cx, |s| {
 747            s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]);
 748        });
 749        view.fold(&Fold, cx);
 750        assert_eq!(
 751            view.display_text(cx),
 752            "
 753                impl Foo {
 754                    // Hello!
 755
 756                    fn a() {
 757                        1
 758                    }
 759
 760                    fn b() {⋯
 761                    }
 762
 763                    fn c() {⋯
 764                    }
 765                }
 766            "
 767            .unindent(),
 768        );
 769
 770        view.fold(&Fold, cx);
 771        assert_eq!(
 772            view.display_text(cx),
 773            "
 774                impl Foo {⋯
 775                }
 776            "
 777            .unindent(),
 778        );
 779
 780        view.unfold_lines(&UnfoldLines, cx);
 781        assert_eq!(
 782            view.display_text(cx),
 783            "
 784                impl Foo {
 785                    // Hello!
 786
 787                    fn a() {
 788                        1
 789                    }
 790
 791                    fn b() {⋯
 792                    }
 793
 794                    fn c() {⋯
 795                    }
 796                }
 797            "
 798            .unindent(),
 799        );
 800
 801        view.unfold_lines(&UnfoldLines, cx);
 802        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 803    });
 804}
 805
 806#[gpui::test]
 807fn test_move_cursor(cx: &mut TestAppContext) {
 808    init_test(cx, |_| {});
 809
 810    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 811    let view = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 812
 813    _ = buffer.update(cx, |buffer, cx| {
 814        buffer.edit(
 815            vec![
 816                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 817                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 818            ],
 819            None,
 820            cx,
 821        );
 822    });
 823    _ = view.update(cx, |view, cx| {
 824        assert_eq!(
 825            view.selections.display_ranges(cx),
 826            &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
 827        );
 828
 829        view.move_down(&MoveDown, cx);
 830        assert_eq!(
 831            view.selections.display_ranges(cx),
 832            &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
 833        );
 834
 835        view.move_right(&MoveRight, cx);
 836        assert_eq!(
 837            view.selections.display_ranges(cx),
 838            &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
 839        );
 840
 841        view.move_left(&MoveLeft, cx);
 842        assert_eq!(
 843            view.selections.display_ranges(cx),
 844            &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
 845        );
 846
 847        view.move_up(&MoveUp, cx);
 848        assert_eq!(
 849            view.selections.display_ranges(cx),
 850            &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
 851        );
 852
 853        view.move_to_end(&MoveToEnd, cx);
 854        assert_eq!(
 855            view.selections.display_ranges(cx),
 856            &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
 857        );
 858
 859        view.move_to_beginning(&MoveToBeginning, cx);
 860        assert_eq!(
 861            view.selections.display_ranges(cx),
 862            &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
 863        );
 864
 865        view.change_selections(None, cx, |s| {
 866            s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]);
 867        });
 868        view.select_to_beginning(&SelectToBeginning, cx);
 869        assert_eq!(
 870            view.selections.display_ranges(cx),
 871            &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
 872        );
 873
 874        view.select_to_end(&SelectToEnd, cx);
 875        assert_eq!(
 876            view.selections.display_ranges(cx),
 877            &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
 878        );
 879    });
 880}
 881
 882#[gpui::test]
 883fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 884    init_test(cx, |_| {});
 885
 886    let view = cx.add_window(|cx| {
 887        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
 888        build_editor(buffer.clone(), cx)
 889    });
 890
 891    assert_eq!('ⓐ'.len_utf8(), 3);
 892    assert_eq!('α'.len_utf8(), 2);
 893
 894    _ = view.update(cx, |view, cx| {
 895        view.fold_ranges(
 896            vec![
 897                Point::new(0, 6)..Point::new(0, 12),
 898                Point::new(1, 2)..Point::new(1, 4),
 899                Point::new(2, 4)..Point::new(2, 8),
 900            ],
 901            true,
 902            cx,
 903        );
 904        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
 905
 906        view.move_right(&MoveRight, cx);
 907        assert_eq!(
 908            view.selections.display_ranges(cx),
 909            &[empty_range(0, "".len())]
 910        );
 911        view.move_right(&MoveRight, cx);
 912        assert_eq!(
 913            view.selections.display_ranges(cx),
 914            &[empty_range(0, "ⓐⓑ".len())]
 915        );
 916        view.move_right(&MoveRight, cx);
 917        assert_eq!(
 918            view.selections.display_ranges(cx),
 919            &[empty_range(0, "ⓐⓑ⋯".len())]
 920        );
 921
 922        view.move_down(&MoveDown, cx);
 923        assert_eq!(
 924            view.selections.display_ranges(cx),
 925            &[empty_range(1, "ab⋯e".len())]
 926        );
 927        view.move_left(&MoveLeft, cx);
 928        assert_eq!(
 929            view.selections.display_ranges(cx),
 930            &[empty_range(1, "ab⋯".len())]
 931        );
 932        view.move_left(&MoveLeft, cx);
 933        assert_eq!(
 934            view.selections.display_ranges(cx),
 935            &[empty_range(1, "ab".len())]
 936        );
 937        view.move_left(&MoveLeft, cx);
 938        assert_eq!(
 939            view.selections.display_ranges(cx),
 940            &[empty_range(1, "a".len())]
 941        );
 942
 943        view.move_down(&MoveDown, cx);
 944        assert_eq!(
 945            view.selections.display_ranges(cx),
 946            &[empty_range(2, "α".len())]
 947        );
 948        view.move_right(&MoveRight, cx);
 949        assert_eq!(
 950            view.selections.display_ranges(cx),
 951            &[empty_range(2, "αβ".len())]
 952        );
 953        view.move_right(&MoveRight, cx);
 954        assert_eq!(
 955            view.selections.display_ranges(cx),
 956            &[empty_range(2, "αβ⋯".len())]
 957        );
 958        view.move_right(&MoveRight, cx);
 959        assert_eq!(
 960            view.selections.display_ranges(cx),
 961            &[empty_range(2, "αβ⋯ε".len())]
 962        );
 963
 964        view.move_up(&MoveUp, cx);
 965        assert_eq!(
 966            view.selections.display_ranges(cx),
 967            &[empty_range(1, "ab⋯e".len())]
 968        );
 969        view.move_down(&MoveDown, cx);
 970        assert_eq!(
 971            view.selections.display_ranges(cx),
 972            &[empty_range(2, "αβ⋯ε".len())]
 973        );
 974        view.move_up(&MoveUp, cx);
 975        assert_eq!(
 976            view.selections.display_ranges(cx),
 977            &[empty_range(1, "ab⋯e".len())]
 978        );
 979
 980        view.move_up(&MoveUp, cx);
 981        assert_eq!(
 982            view.selections.display_ranges(cx),
 983            &[empty_range(0, "ⓐⓑ".len())]
 984        );
 985        view.move_left(&MoveLeft, cx);
 986        assert_eq!(
 987            view.selections.display_ranges(cx),
 988            &[empty_range(0, "".len())]
 989        );
 990        view.move_left(&MoveLeft, cx);
 991        assert_eq!(
 992            view.selections.display_ranges(cx),
 993            &[empty_range(0, "".len())]
 994        );
 995    });
 996}
 997
 998#[gpui::test]
 999fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
1000    init_test(cx, |_| {});
1001
1002    let view = cx.add_window(|cx| {
1003        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
1004        build_editor(buffer.clone(), cx)
1005    });
1006    _ = view.update(cx, |view, cx| {
1007        view.change_selections(None, cx, |s| {
1008            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
1009        });
1010        view.move_down(&MoveDown, cx);
1011        assert_eq!(
1012            view.selections.display_ranges(cx),
1013            &[empty_range(1, "abcd".len())]
1014        );
1015
1016        view.move_down(&MoveDown, cx);
1017        assert_eq!(
1018            view.selections.display_ranges(cx),
1019            &[empty_range(2, "αβγ".len())]
1020        );
1021
1022        view.move_down(&MoveDown, cx);
1023        assert_eq!(
1024            view.selections.display_ranges(cx),
1025            &[empty_range(3, "abcd".len())]
1026        );
1027
1028        view.move_down(&MoveDown, cx);
1029        assert_eq!(
1030            view.selections.display_ranges(cx),
1031            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
1032        );
1033
1034        view.move_up(&MoveUp, cx);
1035        assert_eq!(
1036            view.selections.display_ranges(cx),
1037            &[empty_range(3, "abcd".len())]
1038        );
1039
1040        view.move_up(&MoveUp, cx);
1041        assert_eq!(
1042            view.selections.display_ranges(cx),
1043            &[empty_range(2, "αβγ".len())]
1044        );
1045    });
1046}
1047
1048#[gpui::test]
1049fn test_beginning_end_of_line(cx: &mut TestAppContext) {
1050    init_test(cx, |_| {});
1051
1052    let view = cx.add_window(|cx| {
1053        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
1054        build_editor(buffer, cx)
1055    });
1056    _ = view.update(cx, |view, cx| {
1057        view.change_selections(None, cx, |s| {
1058            s.select_display_ranges([
1059                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
1060                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
1061            ]);
1062        });
1063    });
1064
1065    _ = view.update(cx, |view, cx| {
1066        view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
1067        assert_eq!(
1068            view.selections.display_ranges(cx),
1069            &[
1070                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
1071                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
1072            ]
1073        );
1074    });
1075
1076    _ = view.update(cx, |view, cx| {
1077        view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
1078        assert_eq!(
1079            view.selections.display_ranges(cx),
1080            &[
1081                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
1082                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
1083            ]
1084        );
1085    });
1086
1087    _ = view.update(cx, |view, cx| {
1088        view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
1089        assert_eq!(
1090            view.selections.display_ranges(cx),
1091            &[
1092                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
1093                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
1094            ]
1095        );
1096    });
1097
1098    _ = view.update(cx, |view, cx| {
1099        view.move_to_end_of_line(&MoveToEndOfLine, cx);
1100        assert_eq!(
1101            view.selections.display_ranges(cx),
1102            &[
1103                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
1104                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
1105            ]
1106        );
1107    });
1108
1109    // Moving to the end of line again is a no-op.
1110    _ = view.update(cx, |view, cx| {
1111        view.move_to_end_of_line(&MoveToEndOfLine, cx);
1112        assert_eq!(
1113            view.selections.display_ranges(cx),
1114            &[
1115                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
1116                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
1117            ]
1118        );
1119    });
1120
1121    _ = view.update(cx, |view, cx| {
1122        view.move_left(&MoveLeft, cx);
1123        view.select_to_beginning_of_line(
1124            &SelectToBeginningOfLine {
1125                stop_at_soft_wraps: true,
1126            },
1127            cx,
1128        );
1129        assert_eq!(
1130            view.selections.display_ranges(cx),
1131            &[
1132                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
1133                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
1134            ]
1135        );
1136    });
1137
1138    _ = view.update(cx, |view, cx| {
1139        view.select_to_beginning_of_line(
1140            &SelectToBeginningOfLine {
1141                stop_at_soft_wraps: true,
1142            },
1143            cx,
1144        );
1145        assert_eq!(
1146            view.selections.display_ranges(cx),
1147            &[
1148                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
1149                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
1150            ]
1151        );
1152    });
1153
1154    _ = view.update(cx, |view, cx| {
1155        view.select_to_beginning_of_line(
1156            &SelectToBeginningOfLine {
1157                stop_at_soft_wraps: true,
1158            },
1159            cx,
1160        );
1161        assert_eq!(
1162            view.selections.display_ranges(cx),
1163            &[
1164                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
1165                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
1166            ]
1167        );
1168    });
1169
1170    _ = view.update(cx, |view, cx| {
1171        view.select_to_end_of_line(
1172            &SelectToEndOfLine {
1173                stop_at_soft_wraps: true,
1174            },
1175            cx,
1176        );
1177        assert_eq!(
1178            view.selections.display_ranges(cx),
1179            &[
1180                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
1181                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
1182            ]
1183        );
1184    });
1185
1186    _ = view.update(cx, |view, cx| {
1187        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
1188        assert_eq!(view.display_text(cx), "ab\n  de");
1189        assert_eq!(
1190            view.selections.display_ranges(cx),
1191            &[
1192                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
1193                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
1194            ]
1195        );
1196    });
1197
1198    _ = view.update(cx, |view, cx| {
1199        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
1200        assert_eq!(view.display_text(cx), "\n");
1201        assert_eq!(
1202            view.selections.display_ranges(cx),
1203            &[
1204                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
1205                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
1206            ]
1207        );
1208    });
1209}
1210
1211#[gpui::test]
1212fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
1213    init_test(cx, |_| {});
1214
1215    let view = cx.add_window(|cx| {
1216        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
1217        build_editor(buffer, cx)
1218    });
1219    _ = view.update(cx, |view, cx| {
1220        view.change_selections(None, cx, |s| {
1221            s.select_display_ranges([
1222                DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
1223                DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
1224            ])
1225        });
1226
1227        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1228        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
1229
1230        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1231        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
1232
1233        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1234        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
1235
1236        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1237        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
1238
1239        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1240        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
1241
1242        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1243        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
1244
1245        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1246        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
1247
1248        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1249        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
1250
1251        view.move_right(&MoveRight, cx);
1252        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
1253        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
1254
1255        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
1256        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
1257
1258        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
1259        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
1260    });
1261}
1262
1263#[gpui::test]
1264fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
1265    init_test(cx, |_| {});
1266
1267    let view = cx.add_window(|cx| {
1268        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
1269        build_editor(buffer, cx)
1270    });
1271
1272    _ = view.update(cx, |view, cx| {
1273        view.set_wrap_width(Some(140.0.into()), cx);
1274        assert_eq!(
1275            view.display_text(cx),
1276            "use one::{\n    two::three::\n    four::five\n};"
1277        );
1278
1279        view.change_selections(None, cx, |s| {
1280            s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]);
1281        });
1282
1283        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1284        assert_eq!(
1285            view.selections.display_ranges(cx),
1286            &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
1287        );
1288
1289        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1290        assert_eq!(
1291            view.selections.display_ranges(cx),
1292            &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
1293        );
1294
1295        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1296        assert_eq!(
1297            view.selections.display_ranges(cx),
1298            &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
1299        );
1300
1301        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1302        assert_eq!(
1303            view.selections.display_ranges(cx),
1304            &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
1305        );
1306
1307        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1308        assert_eq!(
1309            view.selections.display_ranges(cx),
1310            &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
1311        );
1312
1313        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1314        assert_eq!(
1315            view.selections.display_ranges(cx),
1316            &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
1317        );
1318    });
1319}
1320
1321#[gpui::test]
1322async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
1323    init_test(cx, |_| {});
1324    let mut cx = EditorTestContext::new(cx).await;
1325
1326    let line_height = cx.editor(|editor, cx| {
1327        editor
1328            .style()
1329            .unwrap()
1330            .text
1331            .line_height_in_pixels(cx.rem_size())
1332    });
1333    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
1334
1335    cx.set_state(
1336        &r#"ˇone
1337        two
1338
1339        three
1340        fourˇ
1341        five
1342
1343        six"#
1344            .unindent(),
1345    );
1346
1347    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
1348    cx.assert_editor_state(
1349        &r#"one
1350        two
1351        ˇ
1352        three
1353        four
1354        five
1355        ˇ
1356        six"#
1357            .unindent(),
1358    );
1359
1360    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
1361    cx.assert_editor_state(
1362        &r#"one
1363        two
1364
1365        three
1366        four
1367        five
1368        ˇ
1369        sixˇ"#
1370            .unindent(),
1371    );
1372
1373    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
1374    cx.assert_editor_state(
1375        &r#"one
1376        two
1377
1378        three
1379        four
1380        five
1381
1382        sixˇ"#
1383            .unindent(),
1384    );
1385
1386    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
1387    cx.assert_editor_state(
1388        &r#"one
1389        two
1390
1391        three
1392        four
1393        five
1394        ˇ
1395        six"#
1396            .unindent(),
1397    );
1398
1399    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
1400    cx.assert_editor_state(
1401        &r#"one
1402        two
1403        ˇ
1404        three
1405        four
1406        five
1407
1408        six"#
1409            .unindent(),
1410    );
1411
1412    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
1413    cx.assert_editor_state(
1414        &r#"ˇone
1415        two
1416
1417        three
1418        four
1419        five
1420
1421        six"#
1422            .unindent(),
1423    );
1424}
1425
1426#[gpui::test]
1427async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
1428    init_test(cx, |_| {});
1429    let mut cx = EditorTestContext::new(cx).await;
1430    let line_height = cx.editor(|editor, cx| {
1431        editor
1432            .style()
1433            .unwrap()
1434            .text
1435            .line_height_in_pixels(cx.rem_size())
1436    });
1437    let window = cx.window;
1438    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
1439
1440    cx.set_state(
1441        &r#"ˇone
1442        two
1443        three
1444        four
1445        five
1446        six
1447        seven
1448        eight
1449        nine
1450        ten
1451        "#,
1452    );
1453
1454    cx.update_editor(|editor, cx| {
1455        assert_eq!(
1456            editor.snapshot(cx).scroll_position(),
1457            gpui::Point::new(0., 0.)
1458        );
1459        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
1460        assert_eq!(
1461            editor.snapshot(cx).scroll_position(),
1462            gpui::Point::new(0., 3.)
1463        );
1464        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
1465        assert_eq!(
1466            editor.snapshot(cx).scroll_position(),
1467            gpui::Point::new(0., 6.)
1468        );
1469        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
1470        assert_eq!(
1471            editor.snapshot(cx).scroll_position(),
1472            gpui::Point::new(0., 3.)
1473        );
1474
1475        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
1476        assert_eq!(
1477            editor.snapshot(cx).scroll_position(),
1478            gpui::Point::new(0., 1.)
1479        );
1480        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
1481        assert_eq!(
1482            editor.snapshot(cx).scroll_position(),
1483            gpui::Point::new(0., 3.)
1484        );
1485    });
1486}
1487
1488#[gpui::test]
1489async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
1490    init_test(cx, |_| {});
1491    let mut cx = EditorTestContext::new(cx).await;
1492
1493    let line_height = cx.update_editor(|editor, cx| {
1494        editor.set_vertical_scroll_margin(2, cx);
1495        editor
1496            .style()
1497            .unwrap()
1498            .text
1499            .line_height_in_pixels(cx.rem_size())
1500    });
1501    let window = cx.window;
1502    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
1503
1504    cx.set_state(
1505        &r#"ˇone
1506            two
1507            three
1508            four
1509            five
1510            six
1511            seven
1512            eight
1513            nine
1514            ten
1515        "#,
1516    );
1517    cx.update_editor(|editor, cx| {
1518        assert_eq!(
1519            editor.snapshot(cx).scroll_position(),
1520            gpui::Point::new(0., 0.0)
1521        );
1522    });
1523
1524    // Add a cursor below the visible area. Since both cursors cannot fit
1525    // on screen, the editor autoscrolls to reveal the newest cursor, and
1526    // allows the vertical scroll margin below that cursor.
1527    cx.update_editor(|editor, cx| {
1528        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
1529            selections.select_ranges([
1530                Point::new(0, 0)..Point::new(0, 0),
1531                Point::new(6, 0)..Point::new(6, 0),
1532            ]);
1533        })
1534    });
1535    cx.update_editor(|editor, cx| {
1536        assert_eq!(
1537            editor.snapshot(cx).scroll_position(),
1538            gpui::Point::new(0., 3.0)
1539        );
1540    });
1541
1542    // Move down. The editor cursor scrolls down to track the newest cursor.
1543    cx.update_editor(|editor, cx| {
1544        editor.move_down(&Default::default(), cx);
1545    });
1546    cx.update_editor(|editor, cx| {
1547        assert_eq!(
1548            editor.snapshot(cx).scroll_position(),
1549            gpui::Point::new(0., 4.0)
1550        );
1551    });
1552
1553    // Add a cursor above the visible area. Since both cursors fit on screen,
1554    // the editor scrolls to show both.
1555    cx.update_editor(|editor, cx| {
1556        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
1557            selections.select_ranges([
1558                Point::new(1, 0)..Point::new(1, 0),
1559                Point::new(6, 0)..Point::new(6, 0),
1560            ]);
1561        })
1562    });
1563    cx.update_editor(|editor, cx| {
1564        assert_eq!(
1565            editor.snapshot(cx).scroll_position(),
1566            gpui::Point::new(0., 1.0)
1567        );
1568    });
1569}
1570
1571#[gpui::test]
1572async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
1573    init_test(cx, |_| {});
1574    let mut cx = EditorTestContext::new(cx).await;
1575
1576    let line_height = cx.editor(|editor, cx| {
1577        editor
1578            .style()
1579            .unwrap()
1580            .text
1581            .line_height_in_pixels(cx.rem_size())
1582    });
1583    let window = cx.window;
1584    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
1585    cx.set_state(
1586        &r#"
1587        ˇone
1588        two
1589        threeˇ
1590        four
1591        five
1592        six
1593        seven
1594        eight
1595        nine
1596        ten
1597        "#
1598        .unindent(),
1599    );
1600
1601    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
1602    cx.assert_editor_state(
1603        &r#"
1604        one
1605        two
1606        three
1607        ˇfour
1608        five
1609        sixˇ
1610        seven
1611        eight
1612        nine
1613        ten
1614        "#
1615        .unindent(),
1616    );
1617
1618    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
1619    cx.assert_editor_state(
1620        &r#"
1621        one
1622        two
1623        three
1624        four
1625        five
1626        six
1627        ˇseven
1628        eight
1629        nineˇ
1630        ten
1631        "#
1632        .unindent(),
1633    );
1634
1635    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
1636    cx.assert_editor_state(
1637        &r#"
1638        one
1639        two
1640        three
1641        ˇfour
1642        five
1643        sixˇ
1644        seven
1645        eight
1646        nine
1647        ten
1648        "#
1649        .unindent(),
1650    );
1651
1652    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
1653    cx.assert_editor_state(
1654        &r#"
1655        ˇone
1656        two
1657        threeˇ
1658        four
1659        five
1660        six
1661        seven
1662        eight
1663        nine
1664        ten
1665        "#
1666        .unindent(),
1667    );
1668
1669    // Test select collapsing
1670    cx.update_editor(|editor, cx| {
1671        editor.move_page_down(&MovePageDown::default(), cx);
1672        editor.move_page_down(&MovePageDown::default(), cx);
1673        editor.move_page_down(&MovePageDown::default(), cx);
1674    });
1675    cx.assert_editor_state(
1676        &r#"
1677        one
1678        two
1679        three
1680        four
1681        five
1682        six
1683        seven
1684        eight
1685        nine
1686        ˇten
1687        ˇ"#
1688        .unindent(),
1689    );
1690}
1691
1692#[gpui::test]
1693async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
1694    init_test(cx, |_| {});
1695    let mut cx = EditorTestContext::new(cx).await;
1696    cx.set_state("one «two threeˇ» four");
1697    cx.update_editor(|editor, cx| {
1698        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
1699        assert_eq!(editor.text(cx), " four");
1700    });
1701}
1702
1703#[gpui::test]
1704fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
1705    init_test(cx, |_| {});
1706
1707    let view = cx.add_window(|cx| {
1708        let buffer = MultiBuffer::build_simple("one two three four", cx);
1709        build_editor(buffer.clone(), cx)
1710    });
1711
1712    _ = view.update(cx, |view, cx| {
1713        view.change_selections(None, cx, |s| {
1714            s.select_display_ranges([
1715                // an empty selection - the preceding word fragment is deleted
1716                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
1717                // characters selected - they are deleted
1718                DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
1719            ])
1720        });
1721        view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
1722        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
1723    });
1724
1725    _ = view.update(cx, |view, cx| {
1726        view.change_selections(None, cx, |s| {
1727            s.select_display_ranges([
1728                // an empty selection - the following word fragment is deleted
1729                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
1730                // characters selected - they are deleted
1731                DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
1732            ])
1733        });
1734        view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
1735        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
1736    });
1737}
1738
1739#[gpui::test]
1740fn test_newline(cx: &mut TestAppContext) {
1741    init_test(cx, |_| {});
1742
1743    let view = cx.add_window(|cx| {
1744        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
1745        build_editor(buffer.clone(), cx)
1746    });
1747
1748    _ = view.update(cx, |view, cx| {
1749        view.change_selections(None, cx, |s| {
1750            s.select_display_ranges([
1751                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
1752                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
1753                DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
1754            ])
1755        });
1756
1757        view.newline(&Newline, cx);
1758        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
1759    });
1760}
1761
1762#[gpui::test]
1763fn test_newline_with_old_selections(cx: &mut TestAppContext) {
1764    init_test(cx, |_| {});
1765
1766    let editor = cx.add_window(|cx| {
1767        let buffer = MultiBuffer::build_simple(
1768            "
1769                a
1770                b(
1771                    X
1772                )
1773                c(
1774                    X
1775                )
1776            "
1777            .unindent()
1778            .as_str(),
1779            cx,
1780        );
1781        let mut editor = build_editor(buffer.clone(), cx);
1782        editor.change_selections(None, cx, |s| {
1783            s.select_ranges([
1784                Point::new(2, 4)..Point::new(2, 5),
1785                Point::new(5, 4)..Point::new(5, 5),
1786            ])
1787        });
1788        editor
1789    });
1790
1791    _ = editor.update(cx, |editor, cx| {
1792        // Edit the buffer directly, deleting ranges surrounding the editor's selections
1793        editor.buffer.update(cx, |buffer, cx| {
1794            buffer.edit(
1795                [
1796                    (Point::new(1, 2)..Point::new(3, 0), ""),
1797                    (Point::new(4, 2)..Point::new(6, 0), ""),
1798                ],
1799                None,
1800                cx,
1801            );
1802            assert_eq!(
1803                buffer.read(cx).text(),
1804                "
1805                    a
1806                    b()
1807                    c()
1808                "
1809                .unindent()
1810            );
1811        });
1812        assert_eq!(
1813            editor.selections.ranges(cx),
1814            &[
1815                Point::new(1, 2)..Point::new(1, 2),
1816                Point::new(2, 2)..Point::new(2, 2),
1817            ],
1818        );
1819
1820        editor.newline(&Newline, cx);
1821        assert_eq!(
1822            editor.text(cx),
1823            "
1824                a
1825                b(
1826                )
1827                c(
1828                )
1829            "
1830            .unindent()
1831        );
1832
1833        // The selections are moved after the inserted newlines
1834        assert_eq!(
1835            editor.selections.ranges(cx),
1836            &[
1837                Point::new(2, 0)..Point::new(2, 0),
1838                Point::new(4, 0)..Point::new(4, 0),
1839            ],
1840        );
1841    });
1842}
1843
1844#[gpui::test]
1845async fn test_newline_above(cx: &mut gpui::TestAppContext) {
1846    init_test(cx, |settings| {
1847        settings.defaults.tab_size = NonZeroU32::new(4)
1848    });
1849
1850    let language = Arc::new(
1851        Language::new(
1852            LanguageConfig::default(),
1853            Some(tree_sitter_rust::language()),
1854        )
1855        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
1856        .unwrap(),
1857    );
1858
1859    let mut cx = EditorTestContext::new(cx).await;
1860    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1861    cx.set_state(indoc! {"
1862        const a: ˇA = (
18631864                «const_functionˇ»(ˇ),
1865                so«mˇ»et«hˇ»ing_ˇelse,ˇ
18661867        ˇ);ˇ
1868    "});
1869
1870    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
1871    cx.assert_editor_state(indoc! {"
1872        ˇ
1873        const a: A = (
1874            ˇ
1875            (
1876                ˇ
1877                ˇ
1878                const_function(),
1879                ˇ
1880                ˇ
1881                ˇ
1882                ˇ
1883                something_else,
1884                ˇ
1885            )
1886            ˇ
1887            ˇ
1888        );
1889    "});
1890}
1891
1892#[gpui::test]
1893async fn test_newline_below(cx: &mut gpui::TestAppContext) {
1894    init_test(cx, |settings| {
1895        settings.defaults.tab_size = NonZeroU32::new(4)
1896    });
1897
1898    let language = Arc::new(
1899        Language::new(
1900            LanguageConfig::default(),
1901            Some(tree_sitter_rust::language()),
1902        )
1903        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
1904        .unwrap(),
1905    );
1906
1907    let mut cx = EditorTestContext::new(cx).await;
1908    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1909    cx.set_state(indoc! {"
1910        const a: ˇA = (
19111912                «const_functionˇ»(ˇ),
1913                so«mˇ»et«hˇ»ing_ˇelse,ˇ
19141915        ˇ);ˇ
1916    "});
1917
1918    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
1919    cx.assert_editor_state(indoc! {"
1920        const a: A = (
1921            ˇ
1922            (
1923                ˇ
1924                const_function(),
1925                ˇ
1926                ˇ
1927                something_else,
1928                ˇ
1929                ˇ
1930                ˇ
1931                ˇ
1932            )
1933            ˇ
1934        );
1935        ˇ
1936        ˇ
1937    "});
1938}
1939
1940#[gpui::test]
1941async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
1942    init_test(cx, |settings| {
1943        settings.defaults.tab_size = NonZeroU32::new(4)
1944    });
1945
1946    let language = Arc::new(Language::new(
1947        LanguageConfig {
1948            line_comments: vec!["//".into()],
1949            ..LanguageConfig::default()
1950        },
1951        None,
1952    ));
1953    {
1954        let mut cx = EditorTestContext::new(cx).await;
1955        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1956        cx.set_state(indoc! {"
1957        // Fooˇ
1958    "});
1959
1960        cx.update_editor(|e, cx| e.newline(&Newline, cx));
1961        cx.assert_editor_state(indoc! {"
1962        // Foo
1963        //ˇ
1964    "});
1965        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
1966        cx.set_state(indoc! {"
1967        ˇ// Foo
1968    "});
1969        cx.update_editor(|e, cx| e.newline(&Newline, cx));
1970        cx.assert_editor_state(indoc! {"
1971
1972        ˇ// Foo
1973    "});
1974    }
1975    // Ensure that comment continuations can be disabled.
1976    update_test_language_settings(cx, |settings| {
1977        settings.defaults.extend_comment_on_newline = Some(false);
1978    });
1979    let mut cx = EditorTestContext::new(cx).await;
1980    cx.set_state(indoc! {"
1981        // Fooˇ
1982    "});
1983    cx.update_editor(|e, cx| e.newline(&Newline, cx));
1984    cx.assert_editor_state(indoc! {"
1985        // Foo
1986        ˇ
1987    "});
1988}
1989
1990#[gpui::test]
1991fn test_insert_with_old_selections(cx: &mut TestAppContext) {
1992    init_test(cx, |_| {});
1993
1994    let editor = cx.add_window(|cx| {
1995        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
1996        let mut editor = build_editor(buffer.clone(), cx);
1997        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
1998        editor
1999    });
2000
2001    _ = editor.update(cx, |editor, cx| {
2002        // Edit the buffer directly, deleting ranges surrounding the editor's selections
2003        editor.buffer.update(cx, |buffer, cx| {
2004            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
2005            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
2006        });
2007        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
2008
2009        editor.insert("Z", cx);
2010        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
2011
2012        // The selections are moved after the inserted characters
2013        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
2014    });
2015}
2016
2017#[gpui::test]
2018async fn test_tab(cx: &mut gpui::TestAppContext) {
2019    init_test(cx, |settings| {
2020        settings.defaults.tab_size = NonZeroU32::new(3)
2021    });
2022
2023    let mut cx = EditorTestContext::new(cx).await;
2024    cx.set_state(indoc! {"
2025        ˇabˇc
2026        ˇ🏀ˇ🏀ˇefg
20272028    "});
2029    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2030    cx.assert_editor_state(indoc! {"
2031           ˇab ˇc
2032           ˇ🏀  ˇ🏀  ˇefg
2033        d  ˇ
2034    "});
2035
2036    cx.set_state(indoc! {"
2037        a
2038        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
2039    "});
2040    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2041    cx.assert_editor_state(indoc! {"
2042        a
2043           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
2044    "});
2045}
2046
2047#[gpui::test]
2048async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
2049    init_test(cx, |_| {});
2050
2051    let mut cx = EditorTestContext::new(cx).await;
2052    let language = Arc::new(
2053        Language::new(
2054            LanguageConfig::default(),
2055            Some(tree_sitter_rust::language()),
2056        )
2057        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
2058        .unwrap(),
2059    );
2060    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
2061
2062    // cursors that are already at the suggested indent level insert
2063    // a soft tab. cursors that are to the left of the suggested indent
2064    // auto-indent their line.
2065    cx.set_state(indoc! {"
2066        ˇ
2067        const a: B = (
2068            c(
2069                d(
2070        ˇ
2071                )
2072        ˇ
2073        ˇ    )
2074        );
2075    "});
2076    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2077    cx.assert_editor_state(indoc! {"
2078            ˇ
2079        const a: B = (
2080            c(
2081                d(
2082                    ˇ
2083                )
2084                ˇ
2085            ˇ)
2086        );
2087    "});
2088
2089    // handle auto-indent when there are multiple cursors on the same line
2090    cx.set_state(indoc! {"
2091        const a: B = (
2092            c(
2093        ˇ    ˇ
2094        ˇ    )
2095        );
2096    "});
2097    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2098    cx.assert_editor_state(indoc! {"
2099        const a: B = (
2100            c(
2101                ˇ
2102            ˇ)
2103        );
2104    "});
2105}
2106
2107#[gpui::test]
2108async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
2109    init_test(cx, |settings| {
2110        settings.defaults.tab_size = NonZeroU32::new(4)
2111    });
2112
2113    let language = Arc::new(
2114        Language::new(
2115            LanguageConfig::default(),
2116            Some(tree_sitter_rust::language()),
2117        )
2118        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
2119        .unwrap(),
2120    );
2121
2122    let mut cx = EditorTestContext::new(cx).await;
2123    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
2124    cx.set_state(indoc! {"
2125        fn a() {
2126            if b {
2127        \t ˇc
2128            }
2129        }
2130    "});
2131
2132    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2133    cx.assert_editor_state(indoc! {"
2134        fn a() {
2135            if b {
2136                ˇc
2137            }
2138        }
2139    "});
2140}
2141
2142#[gpui::test]
2143async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
2144    init_test(cx, |settings| {
2145        settings.defaults.tab_size = NonZeroU32::new(4);
2146    });
2147
2148    let mut cx = EditorTestContext::new(cx).await;
2149
2150    cx.set_state(indoc! {"
2151          «oneˇ» «twoˇ»
2152        three
2153         four
2154    "});
2155    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2156    cx.assert_editor_state(indoc! {"
2157            «oneˇ» «twoˇ»
2158        three
2159         four
2160    "});
2161
2162    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2163    cx.assert_editor_state(indoc! {"
2164        «oneˇ» «twoˇ»
2165        three
2166         four
2167    "});
2168
2169    // select across line ending
2170    cx.set_state(indoc! {"
2171        one two
2172        t«hree
2173        ˇ» four
2174    "});
2175    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2176    cx.assert_editor_state(indoc! {"
2177        one two
2178            t«hree
2179        ˇ» four
2180    "});
2181
2182    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2183    cx.assert_editor_state(indoc! {"
2184        one two
2185        t«hree
2186        ˇ» four
2187    "});
2188
2189    // Ensure that indenting/outdenting works when the cursor is at column 0.
2190    cx.set_state(indoc! {"
2191        one two
2192        ˇthree
2193            four
2194    "});
2195    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2196    cx.assert_editor_state(indoc! {"
2197        one two
2198            ˇthree
2199            four
2200    "});
2201
2202    cx.set_state(indoc! {"
2203        one two
2204        ˇ    three
2205            four
2206    "});
2207    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2208    cx.assert_editor_state(indoc! {"
2209        one two
2210        ˇthree
2211            four
2212    "});
2213}
2214
2215#[gpui::test]
2216async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
2217    init_test(cx, |settings| {
2218        settings.defaults.hard_tabs = Some(true);
2219    });
2220
2221    let mut cx = EditorTestContext::new(cx).await;
2222
2223    // select two ranges on one line
2224    cx.set_state(indoc! {"
2225        «oneˇ» «twoˇ»
2226        three
2227        four
2228    "});
2229    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2230    cx.assert_editor_state(indoc! {"
2231        \t«oneˇ» «twoˇ»
2232        three
2233        four
2234    "});
2235    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2236    cx.assert_editor_state(indoc! {"
2237        \t\t«oneˇ» «twoˇ»
2238        three
2239        four
2240    "});
2241    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2242    cx.assert_editor_state(indoc! {"
2243        \t«oneˇ» «twoˇ»
2244        three
2245        four
2246    "});
2247    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2248    cx.assert_editor_state(indoc! {"
2249        «oneˇ» «twoˇ»
2250        three
2251        four
2252    "});
2253
2254    // select across a line ending
2255    cx.set_state(indoc! {"
2256        one two
2257        t«hree
2258        ˇ»four
2259    "});
2260    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2261    cx.assert_editor_state(indoc! {"
2262        one two
2263        \tt«hree
2264        ˇ»four
2265    "});
2266    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2267    cx.assert_editor_state(indoc! {"
2268        one two
2269        \t\tt«hree
2270        ˇ»four
2271    "});
2272    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2273    cx.assert_editor_state(indoc! {"
2274        one two
2275        \tt«hree
2276        ˇ»four
2277    "});
2278    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2279    cx.assert_editor_state(indoc! {"
2280        one two
2281        t«hree
2282        ˇ»four
2283    "});
2284
2285    // Ensure that indenting/outdenting works when the cursor is at column 0.
2286    cx.set_state(indoc! {"
2287        one two
2288        ˇthree
2289        four
2290    "});
2291    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2292    cx.assert_editor_state(indoc! {"
2293        one two
2294        ˇthree
2295        four
2296    "});
2297    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2298    cx.assert_editor_state(indoc! {"
2299        one two
2300        \tˇthree
2301        four
2302    "});
2303    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2304    cx.assert_editor_state(indoc! {"
2305        one two
2306        ˇthree
2307        four
2308    "});
2309}
2310
2311#[gpui::test]
2312fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
2313    init_test(cx, |settings| {
2314        settings.languages.extend([
2315            (
2316                "TOML".into(),
2317                LanguageSettingsContent {
2318                    tab_size: NonZeroU32::new(2),
2319                    ..Default::default()
2320                },
2321            ),
2322            (
2323                "Rust".into(),
2324                LanguageSettingsContent {
2325                    tab_size: NonZeroU32::new(4),
2326                    ..Default::default()
2327                },
2328            ),
2329        ]);
2330    });
2331
2332    let toml_language = Arc::new(Language::new(
2333        LanguageConfig {
2334            name: "TOML".into(),
2335            ..Default::default()
2336        },
2337        None,
2338    ));
2339    let rust_language = Arc::new(Language::new(
2340        LanguageConfig {
2341            name: "Rust".into(),
2342            ..Default::default()
2343        },
2344        None,
2345    ));
2346
2347    let toml_buffer = cx.new_model(|cx| {
2348        Buffer::new(
2349            0,
2350            BufferId::new(cx.entity_id().as_u64()).unwrap(),
2351            "a = 1\nb = 2\n",
2352        )
2353        .with_language(toml_language, cx)
2354    });
2355    let rust_buffer = cx.new_model(|cx| {
2356        Buffer::new(
2357            0,
2358            BufferId::new(cx.entity_id().as_u64()).unwrap(),
2359            "const c: usize = 3;\n",
2360        )
2361        .with_language(rust_language, cx)
2362    });
2363    let multibuffer = cx.new_model(|cx| {
2364        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
2365        multibuffer.push_excerpts(
2366            toml_buffer.clone(),
2367            [ExcerptRange {
2368                context: Point::new(0, 0)..Point::new(2, 0),
2369                primary: None,
2370            }],
2371            cx,
2372        );
2373        multibuffer.push_excerpts(
2374            rust_buffer.clone(),
2375            [ExcerptRange {
2376                context: Point::new(0, 0)..Point::new(1, 0),
2377                primary: None,
2378            }],
2379            cx,
2380        );
2381        multibuffer
2382    });
2383
2384    cx.add_window(|cx| {
2385        let mut editor = build_editor(multibuffer, cx);
2386
2387        assert_eq!(
2388            editor.text(cx),
2389            indoc! {"
2390                a = 1
2391                b = 2
2392
2393                const c: usize = 3;
2394            "}
2395        );
2396
2397        select_ranges(
2398            &mut editor,
2399            indoc! {"
2400                «aˇ» = 1
2401                b = 2
2402
2403                «const c:ˇ» usize = 3;
2404            "},
2405            cx,
2406        );
2407
2408        editor.tab(&Tab, cx);
2409        assert_text_with_selections(
2410            &mut editor,
2411            indoc! {"
2412                  «aˇ» = 1
2413                b = 2
2414
2415                    «const c:ˇ» usize = 3;
2416            "},
2417            cx,
2418        );
2419        editor.tab_prev(&TabPrev, cx);
2420        assert_text_with_selections(
2421            &mut editor,
2422            indoc! {"
2423                «aˇ» = 1
2424                b = 2
2425
2426                «const c:ˇ» usize = 3;
2427            "},
2428            cx,
2429        );
2430
2431        editor
2432    });
2433}
2434
2435#[gpui::test]
2436async fn test_backspace(cx: &mut gpui::TestAppContext) {
2437    init_test(cx, |_| {});
2438
2439    let mut cx = EditorTestContext::new(cx).await;
2440
2441    // Basic backspace
2442    cx.set_state(indoc! {"
2443        onˇe two three
2444        fou«rˇ» five six
2445        seven «ˇeight nine
2446        »ten
2447    "});
2448    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2449    cx.assert_editor_state(indoc! {"
2450        oˇe two three
2451        fouˇ five six
2452        seven ˇten
2453    "});
2454
2455    // Test backspace inside and around indents
2456    cx.set_state(indoc! {"
2457        zero
2458            ˇone
2459                ˇtwo
2460            ˇ ˇ ˇ  three
2461        ˇ  ˇ  four
2462    "});
2463    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2464    cx.assert_editor_state(indoc! {"
2465        zero
2466        ˇone
2467            ˇtwo
2468        ˇ  threeˇ  four
2469    "});
2470
2471    // Test backspace with line_mode set to true
2472    cx.update_editor(|e, _| e.selections.line_mode = true);
2473    cx.set_state(indoc! {"
2474        The ˇquick ˇbrown
2475        fox jumps over
2476        the lazy dog
2477        ˇThe qu«ick bˇ»rown"});
2478    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2479    cx.assert_editor_state(indoc! {"
2480        ˇfox jumps over
2481        the lazy dogˇ"});
2482}
2483
2484#[gpui::test]
2485async fn test_delete(cx: &mut gpui::TestAppContext) {
2486    init_test(cx, |_| {});
2487
2488    let mut cx = EditorTestContext::new(cx).await;
2489    cx.set_state(indoc! {"
2490        onˇe two three
2491        fou«rˇ» five six
2492        seven «ˇeight nine
2493        »ten
2494    "});
2495    cx.update_editor(|e, cx| e.delete(&Delete, cx));
2496    cx.assert_editor_state(indoc! {"
2497        onˇ two three
2498        fouˇ five six
2499        seven ˇten
2500    "});
2501
2502    // Test backspace with line_mode set to true
2503    cx.update_editor(|e, _| e.selections.line_mode = true);
2504    cx.set_state(indoc! {"
2505        The ˇquick ˇbrown
2506        fox «ˇjum»ps over
2507        the lazy dog
2508        ˇThe qu«ick bˇ»rown"});
2509    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2510    cx.assert_editor_state("ˇthe lazy dogˇ");
2511}
2512
2513#[gpui::test]
2514fn test_delete_line(cx: &mut TestAppContext) {
2515    init_test(cx, |_| {});
2516
2517    let view = cx.add_window(|cx| {
2518        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2519        build_editor(buffer, cx)
2520    });
2521    _ = view.update(cx, |view, cx| {
2522        view.change_selections(None, cx, |s| {
2523            s.select_display_ranges([
2524                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2525                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
2526                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2527            ])
2528        });
2529        view.delete_line(&DeleteLine, cx);
2530        assert_eq!(view.display_text(cx), "ghi");
2531        assert_eq!(
2532            view.selections.display_ranges(cx),
2533            vec![
2534                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
2535                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
2536            ]
2537        );
2538    });
2539
2540    let view = cx.add_window(|cx| {
2541        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2542        build_editor(buffer, cx)
2543    });
2544    _ = view.update(cx, |view, cx| {
2545        view.change_selections(None, cx, |s| {
2546            s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
2547        });
2548        view.delete_line(&DeleteLine, cx);
2549        assert_eq!(view.display_text(cx), "ghi\n");
2550        assert_eq!(
2551            view.selections.display_ranges(cx),
2552            vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
2553        );
2554    });
2555}
2556
2557#[gpui::test]
2558fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
2559    init_test(cx, |_| {});
2560
2561    cx.add_window(|cx| {
2562        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
2563        let mut editor = build_editor(buffer.clone(), cx);
2564        let buffer = buffer.read(cx).as_singleton().unwrap();
2565
2566        assert_eq!(
2567            editor.selections.ranges::<Point>(cx),
2568            &[Point::new(0, 0)..Point::new(0, 0)]
2569        );
2570
2571        // When on single line, replace newline at end by space
2572        editor.join_lines(&JoinLines, cx);
2573        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
2574        assert_eq!(
2575            editor.selections.ranges::<Point>(cx),
2576            &[Point::new(0, 3)..Point::new(0, 3)]
2577        );
2578
2579        // When multiple lines are selected, remove newlines that are spanned by the selection
2580        editor.change_selections(None, cx, |s| {
2581            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
2582        });
2583        editor.join_lines(&JoinLines, cx);
2584        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
2585        assert_eq!(
2586            editor.selections.ranges::<Point>(cx),
2587            &[Point::new(0, 11)..Point::new(0, 11)]
2588        );
2589
2590        // Undo should be transactional
2591        editor.undo(&Undo, cx);
2592        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
2593        assert_eq!(
2594            editor.selections.ranges::<Point>(cx),
2595            &[Point::new(0, 5)..Point::new(2, 2)]
2596        );
2597
2598        // When joining an empty line don't insert a space
2599        editor.change_selections(None, cx, |s| {
2600            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
2601        });
2602        editor.join_lines(&JoinLines, cx);
2603        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
2604        assert_eq!(
2605            editor.selections.ranges::<Point>(cx),
2606            [Point::new(2, 3)..Point::new(2, 3)]
2607        );
2608
2609        // We can remove trailing newlines
2610        editor.join_lines(&JoinLines, cx);
2611        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
2612        assert_eq!(
2613            editor.selections.ranges::<Point>(cx),
2614            [Point::new(2, 3)..Point::new(2, 3)]
2615        );
2616
2617        // We don't blow up on the last line
2618        editor.join_lines(&JoinLines, cx);
2619        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
2620        assert_eq!(
2621            editor.selections.ranges::<Point>(cx),
2622            [Point::new(2, 3)..Point::new(2, 3)]
2623        );
2624
2625        // reset to test indentation
2626        editor.buffer.update(cx, |buffer, cx| {
2627            buffer.edit(
2628                [
2629                    (Point::new(1, 0)..Point::new(1, 2), "  "),
2630                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
2631                ],
2632                None,
2633                cx,
2634            )
2635        });
2636
2637        // We remove any leading spaces
2638        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
2639        editor.change_selections(None, cx, |s| {
2640            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
2641        });
2642        editor.join_lines(&JoinLines, cx);
2643        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
2644
2645        // We don't insert a space for a line containing only spaces
2646        editor.join_lines(&JoinLines, cx);
2647        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
2648
2649        // We ignore any leading tabs
2650        editor.join_lines(&JoinLines, cx);
2651        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
2652
2653        editor
2654    });
2655}
2656
2657#[gpui::test]
2658fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
2659    init_test(cx, |_| {});
2660
2661    cx.add_window(|cx| {
2662        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
2663        let mut editor = build_editor(buffer.clone(), cx);
2664        let buffer = buffer.read(cx).as_singleton().unwrap();
2665
2666        editor.change_selections(None, cx, |s| {
2667            s.select_ranges([
2668                Point::new(0, 2)..Point::new(1, 1),
2669                Point::new(1, 2)..Point::new(1, 2),
2670                Point::new(3, 1)..Point::new(3, 2),
2671            ])
2672        });
2673
2674        editor.join_lines(&JoinLines, cx);
2675        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
2676
2677        assert_eq!(
2678            editor.selections.ranges::<Point>(cx),
2679            [
2680                Point::new(0, 7)..Point::new(0, 7),
2681                Point::new(1, 3)..Point::new(1, 3)
2682            ]
2683        );
2684        editor
2685    });
2686}
2687
2688#[gpui::test]
2689async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
2690    init_test(cx, |_| {});
2691
2692    let mut cx = EditorTestContext::new(cx).await;
2693
2694    // Test sort_lines_case_insensitive()
2695    cx.set_state(indoc! {"
2696        «z
2697        y
2698        x
2699        Z
2700        Y
2701        Xˇ»
2702    "});
2703    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
2704    cx.assert_editor_state(indoc! {"
2705        «x
2706        X
2707        y
2708        Y
2709        z
2710        Zˇ»
2711    "});
2712
2713    // Test reverse_lines()
2714    cx.set_state(indoc! {"
2715        «5
2716        4
2717        3
2718        2
2719        1ˇ»
2720    "});
2721    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
2722    cx.assert_editor_state(indoc! {"
2723        «1
2724        2
2725        3
2726        4
2727        5ˇ»
2728    "});
2729
2730    // Skip testing shuffle_line()
2731
2732    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
2733    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
2734
2735    // Don't manipulate when cursor is on single line, but expand the selection
2736    cx.set_state(indoc! {"
2737        ddˇdd
2738        ccc
2739        bb
2740        a
2741    "});
2742    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
2743    cx.assert_editor_state(indoc! {"
2744        «ddddˇ»
2745        ccc
2746        bb
2747        a
2748    "});
2749
2750    // Basic manipulate case
2751    // Start selection moves to column 0
2752    // End of selection shrinks to fit shorter line
2753    cx.set_state(indoc! {"
2754        dd«d
2755        ccc
2756        bb
2757        aaaaaˇ»
2758    "});
2759    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
2760    cx.assert_editor_state(indoc! {"
2761        «aaaaa
2762        bb
2763        ccc
2764        dddˇ»
2765    "});
2766
2767    // Manipulate case with newlines
2768    cx.set_state(indoc! {"
2769        dd«d
2770        ccc
2771
2772        bb
2773        aaaaa
2774
2775        ˇ»
2776    "});
2777    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
2778    cx.assert_editor_state(indoc! {"
2779        «
2780
2781        aaaaa
2782        bb
2783        ccc
2784        dddˇ»
2785
2786    "});
2787
2788    // Adding new line
2789    cx.set_state(indoc! {"
2790        aa«a
2791        bbˇ»b
2792    "});
2793    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
2794    cx.assert_editor_state(indoc! {"
2795        «aaa
2796        bbb
2797        added_lineˇ»
2798    "});
2799
2800    // Removing line
2801    cx.set_state(indoc! {"
2802        aa«a
2803        bbbˇ»
2804    "});
2805    cx.update_editor(|e, cx| {
2806        e.manipulate_lines(cx, |lines| {
2807            lines.pop();
2808        })
2809    });
2810    cx.assert_editor_state(indoc! {"
2811        «aaaˇ»
2812    "});
2813
2814    // Removing all lines
2815    cx.set_state(indoc! {"
2816        aa«a
2817        bbbˇ»
2818    "});
2819    cx.update_editor(|e, cx| {
2820        e.manipulate_lines(cx, |lines| {
2821            lines.drain(..);
2822        })
2823    });
2824    cx.assert_editor_state(indoc! {"
2825        ˇ
2826    "});
2827}
2828
2829#[gpui::test]
2830async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
2831    init_test(cx, |_| {});
2832
2833    let mut cx = EditorTestContext::new(cx).await;
2834
2835    // Consider continuous selection as single selection
2836    cx.set_state(indoc! {"
2837        Aaa«aa
2838        cˇ»c«c
2839        bb
2840        aaaˇ»aa
2841    "});
2842    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
2843    cx.assert_editor_state(indoc! {"
2844        «Aaaaa
2845        ccc
2846        bb
2847        aaaaaˇ»
2848    "});
2849
2850    cx.set_state(indoc! {"
2851        Aaa«aa
2852        cˇ»c«c
2853        bb
2854        aaaˇ»aa
2855    "});
2856    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
2857    cx.assert_editor_state(indoc! {"
2858        «Aaaaa
2859        ccc
2860        bbˇ»
2861    "});
2862
2863    // Consider non continuous selection as distinct dedup operations
2864    cx.set_state(indoc! {"
2865        «aaaaa
2866        bb
2867        aaaaa
2868        aaaaaˇ»
2869
2870        aaa«aaˇ»
2871    "});
2872    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
2873    cx.assert_editor_state(indoc! {"
2874        «aaaaa
2875        bbˇ»
2876
2877        «aaaaaˇ»
2878    "});
2879}
2880
2881#[gpui::test]
2882async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
2883    init_test(cx, |_| {});
2884
2885    let mut cx = EditorTestContext::new(cx).await;
2886
2887    cx.set_state(indoc! {"
2888        «Aaa
2889        aAa
2890        Aaaˇ»
2891    "});
2892    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
2893    cx.assert_editor_state(indoc! {"
2894        «Aaa
2895        aAaˇ»
2896    "});
2897
2898    cx.set_state(indoc! {"
2899        «Aaa
2900        aAa
2901        aaAˇ»
2902    "});
2903    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
2904    cx.assert_editor_state(indoc! {"
2905        «Aaaˇ»
2906    "});
2907}
2908
2909#[gpui::test]
2910async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
2911    init_test(cx, |_| {});
2912
2913    let mut cx = EditorTestContext::new(cx).await;
2914
2915    // Manipulate with multiple selections on a single line
2916    cx.set_state(indoc! {"
2917        dd«dd
2918        cˇ»c«c
2919        bb
2920        aaaˇ»aa
2921    "});
2922    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
2923    cx.assert_editor_state(indoc! {"
2924        «aaaaa
2925        bb
2926        ccc
2927        ddddˇ»
2928    "});
2929
2930    // Manipulate with multiple disjoin selections
2931    cx.set_state(indoc! {"
29322933        4
2934        3
2935        2
2936        1ˇ»
2937
2938        dd«dd
2939        ccc
2940        bb
2941        aaaˇ»aa
2942    "});
2943    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
2944    cx.assert_editor_state(indoc! {"
2945        «1
2946        2
2947        3
2948        4
2949        5ˇ»
2950
2951        «aaaaa
2952        bb
2953        ccc
2954        ddddˇ»
2955    "});
2956
2957    // Adding lines on each selection
2958    cx.set_state(indoc! {"
29592960        1ˇ»
2961
2962        bb«bb
2963        aaaˇ»aa
2964    "});
2965    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
2966    cx.assert_editor_state(indoc! {"
2967        «2
2968        1
2969        added lineˇ»
2970
2971        «bbbb
2972        aaaaa
2973        added lineˇ»
2974    "});
2975
2976    // Removing lines on each selection
2977    cx.set_state(indoc! {"
29782979        1ˇ»
2980
2981        bb«bb
2982        aaaˇ»aa
2983    "});
2984    cx.update_editor(|e, cx| {
2985        e.manipulate_lines(cx, |lines| {
2986            lines.pop();
2987        })
2988    });
2989    cx.assert_editor_state(indoc! {"
2990        «2ˇ»
2991
2992        «bbbbˇ»
2993    "});
2994}
2995
2996#[gpui::test]
2997async fn test_manipulate_text(cx: &mut TestAppContext) {
2998    init_test(cx, |_| {});
2999
3000    let mut cx = EditorTestContext::new(cx).await;
3001
3002    // Test convert_to_upper_case()
3003    cx.set_state(indoc! {"
3004        «hello worldˇ»
3005    "});
3006    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
3007    cx.assert_editor_state(indoc! {"
3008        «HELLO WORLDˇ»
3009    "});
3010
3011    // Test convert_to_lower_case()
3012    cx.set_state(indoc! {"
3013        «HELLO WORLDˇ»
3014    "});
3015    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
3016    cx.assert_editor_state(indoc! {"
3017        «hello worldˇ»
3018    "});
3019
3020    // Test multiple line, single selection case
3021    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
3022    cx.set_state(indoc! {"
3023        «The quick brown
3024        fox jumps over
3025        the lazy dogˇ»
3026    "});
3027    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
3028    cx.assert_editor_state(indoc! {"
3029        «The Quick Brown
3030        Fox Jumps Over
3031        The Lazy Dogˇ»
3032    "});
3033
3034    // Test multiple line, single selection case
3035    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
3036    cx.set_state(indoc! {"
3037        «The quick brown
3038        fox jumps over
3039        the lazy dogˇ»
3040    "});
3041    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
3042    cx.assert_editor_state(indoc! {"
3043        «TheQuickBrown
3044        FoxJumpsOver
3045        TheLazyDogˇ»
3046    "});
3047
3048    // From here on out, test more complex cases of manipulate_text()
3049
3050    // Test no selection case - should affect words cursors are in
3051    // Cursor at beginning, middle, and end of word
3052    cx.set_state(indoc! {"
3053        ˇhello big beauˇtiful worldˇ
3054    "});
3055    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
3056    cx.assert_editor_state(indoc! {"
3057        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
3058    "});
3059
3060    // Test multiple selections on a single line and across multiple lines
3061    cx.set_state(indoc! {"
3062        «Theˇ» quick «brown
3063        foxˇ» jumps «overˇ»
3064        the «lazyˇ» dog
3065    "});
3066    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
3067    cx.assert_editor_state(indoc! {"
3068        «THEˇ» quick «BROWN
3069        FOXˇ» jumps «OVERˇ»
3070        the «LAZYˇ» dog
3071    "});
3072
3073    // Test case where text length grows
3074    cx.set_state(indoc! {"
3075        «tschüߡ»
3076    "});
3077    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
3078    cx.assert_editor_state(indoc! {"
3079        «TSCHÜSSˇ»
3080    "});
3081
3082    // Test to make sure we don't crash when text shrinks
3083    cx.set_state(indoc! {"
3084        aaa_bbbˇ
3085    "});
3086    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
3087    cx.assert_editor_state(indoc! {"
3088        «aaaBbbˇ»
3089    "});
3090
3091    // Test to make sure we all aware of the fact that each word can grow and shrink
3092    // Final selections should be aware of this fact
3093    cx.set_state(indoc! {"
3094        aaa_bˇbb bbˇb_ccc ˇccc_ddd
3095    "});
3096    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
3097    cx.assert_editor_state(indoc! {"
3098        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
3099    "});
3100}
3101
3102#[gpui::test]
3103fn test_duplicate_line(cx: &mut TestAppContext) {
3104    init_test(cx, |_| {});
3105
3106    let view = cx.add_window(|cx| {
3107        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
3108        build_editor(buffer, cx)
3109    });
3110    _ = view.update(cx, |view, cx| {
3111        view.change_selections(None, cx, |s| {
3112            s.select_display_ranges([
3113                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3114                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3115                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
3116                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
3117            ])
3118        });
3119        view.duplicate_line_down(&DuplicateLineDown, cx);
3120        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
3121        assert_eq!(
3122            view.selections.display_ranges(cx),
3123            vec![
3124                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
3125                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
3126                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
3127                DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
3128            ]
3129        );
3130    });
3131
3132    let view = cx.add_window(|cx| {
3133        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
3134        build_editor(buffer, cx)
3135    });
3136    _ = view.update(cx, |view, cx| {
3137        view.change_selections(None, cx, |s| {
3138            s.select_display_ranges([
3139                DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
3140                DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
3141            ])
3142        });
3143        view.duplicate_line_down(&DuplicateLineDown, cx);
3144        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
3145        assert_eq!(
3146            view.selections.display_ranges(cx),
3147            vec![
3148                DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
3149                DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
3150            ]
3151        );
3152    });
3153
3154    // With `move_upwards` the selections stay in place, except for
3155    // the lines inserted above them
3156    let view = cx.add_window(|cx| {
3157        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
3158        build_editor(buffer, cx)
3159    });
3160    _ = view.update(cx, |view, cx| {
3161        view.change_selections(None, cx, |s| {
3162            s.select_display_ranges([
3163                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3164                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3165                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
3166                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
3167            ])
3168        });
3169        view.duplicate_line_up(&DuplicateLineUp, cx);
3170        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
3171        assert_eq!(
3172            view.selections.display_ranges(cx),
3173            vec![
3174                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3175                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3176                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
3177                DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
3178            ]
3179        );
3180    });
3181
3182    let view = cx.add_window(|cx| {
3183        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
3184        build_editor(buffer, cx)
3185    });
3186    _ = view.update(cx, |view, cx| {
3187        view.change_selections(None, cx, |s| {
3188            s.select_display_ranges([
3189                DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
3190                DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
3191            ])
3192        });
3193        view.duplicate_line_up(&DuplicateLineUp, cx);
3194        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
3195        assert_eq!(
3196            view.selections.display_ranges(cx),
3197            vec![
3198                DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
3199                DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
3200            ]
3201        );
3202    });
3203}
3204
3205#[gpui::test]
3206fn test_move_line_up_down(cx: &mut TestAppContext) {
3207    init_test(cx, |_| {});
3208
3209    let view = cx.add_window(|cx| {
3210        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
3211        build_editor(buffer, cx)
3212    });
3213    _ = view.update(cx, |view, cx| {
3214        view.fold_ranges(
3215            vec![
3216                Point::new(0, 2)..Point::new(1, 2),
3217                Point::new(2, 3)..Point::new(4, 1),
3218                Point::new(7, 0)..Point::new(8, 4),
3219            ],
3220            true,
3221            cx,
3222        );
3223        view.change_selections(None, cx, |s| {
3224            s.select_display_ranges([
3225                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
3226                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
3227                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
3228                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
3229            ])
3230        });
3231        assert_eq!(
3232            view.display_text(cx),
3233            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
3234        );
3235
3236        view.move_line_up(&MoveLineUp, cx);
3237        assert_eq!(
3238            view.display_text(cx),
3239            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
3240        );
3241        assert_eq!(
3242            view.selections.display_ranges(cx),
3243            vec![
3244                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
3245                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
3246                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
3247                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
3248            ]
3249        );
3250    });
3251
3252    _ = view.update(cx, |view, cx| {
3253        view.move_line_down(&MoveLineDown, cx);
3254        assert_eq!(
3255            view.display_text(cx),
3256            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
3257        );
3258        assert_eq!(
3259            view.selections.display_ranges(cx),
3260            vec![
3261                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
3262                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
3263                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
3264                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
3265            ]
3266        );
3267    });
3268
3269    _ = view.update(cx, |view, cx| {
3270        view.move_line_down(&MoveLineDown, cx);
3271        assert_eq!(
3272            view.display_text(cx),
3273            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
3274        );
3275        assert_eq!(
3276            view.selections.display_ranges(cx),
3277            vec![
3278                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
3279                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
3280                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
3281                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
3282            ]
3283        );
3284    });
3285
3286    _ = view.update(cx, |view, cx| {
3287        view.move_line_up(&MoveLineUp, cx);
3288        assert_eq!(
3289            view.display_text(cx),
3290            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
3291        );
3292        assert_eq!(
3293            view.selections.display_ranges(cx),
3294            vec![
3295                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
3296                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
3297                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
3298                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
3299            ]
3300        );
3301    });
3302}
3303
3304#[gpui::test]
3305fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
3306    init_test(cx, |_| {});
3307
3308    let editor = cx.add_window(|cx| {
3309        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
3310        build_editor(buffer, cx)
3311    });
3312    _ = editor.update(cx, |editor, cx| {
3313        let snapshot = editor.buffer.read(cx).snapshot(cx);
3314        editor.insert_blocks(
3315            [BlockProperties {
3316                style: BlockStyle::Fixed,
3317                position: snapshot.anchor_after(Point::new(2, 0)),
3318                disposition: BlockDisposition::Below,
3319                height: 1,
3320                render: Arc::new(|_| div().into_any()),
3321            }],
3322            Some(Autoscroll::fit()),
3323            cx,
3324        );
3325        editor.change_selections(None, cx, |s| {
3326            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
3327        });
3328        editor.move_line_down(&MoveLineDown, cx);
3329    });
3330}
3331
3332#[gpui::test]
3333fn test_transpose(cx: &mut TestAppContext) {
3334    init_test(cx, |_| {});
3335
3336    _ = cx.add_window(|cx| {
3337        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
3338        editor.set_style(EditorStyle::default(), cx);
3339        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
3340        editor.transpose(&Default::default(), cx);
3341        assert_eq!(editor.text(cx), "bac");
3342        assert_eq!(editor.selections.ranges(cx), [2..2]);
3343
3344        editor.transpose(&Default::default(), cx);
3345        assert_eq!(editor.text(cx), "bca");
3346        assert_eq!(editor.selections.ranges(cx), [3..3]);
3347
3348        editor.transpose(&Default::default(), cx);
3349        assert_eq!(editor.text(cx), "bac");
3350        assert_eq!(editor.selections.ranges(cx), [3..3]);
3351
3352        editor
3353    });
3354
3355    _ = cx.add_window(|cx| {
3356        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
3357        editor.set_style(EditorStyle::default(), cx);
3358        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
3359        editor.transpose(&Default::default(), cx);
3360        assert_eq!(editor.text(cx), "acb\nde");
3361        assert_eq!(editor.selections.ranges(cx), [3..3]);
3362
3363        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
3364        editor.transpose(&Default::default(), cx);
3365        assert_eq!(editor.text(cx), "acbd\ne");
3366        assert_eq!(editor.selections.ranges(cx), [5..5]);
3367
3368        editor.transpose(&Default::default(), cx);
3369        assert_eq!(editor.text(cx), "acbde\n");
3370        assert_eq!(editor.selections.ranges(cx), [6..6]);
3371
3372        editor.transpose(&Default::default(), cx);
3373        assert_eq!(editor.text(cx), "acbd\ne");
3374        assert_eq!(editor.selections.ranges(cx), [6..6]);
3375
3376        editor
3377    });
3378
3379    _ = cx.add_window(|cx| {
3380        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
3381        editor.set_style(EditorStyle::default(), cx);
3382        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
3383        editor.transpose(&Default::default(), cx);
3384        assert_eq!(editor.text(cx), "bacd\ne");
3385        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
3386
3387        editor.transpose(&Default::default(), cx);
3388        assert_eq!(editor.text(cx), "bcade\n");
3389        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
3390
3391        editor.transpose(&Default::default(), cx);
3392        assert_eq!(editor.text(cx), "bcda\ne");
3393        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
3394
3395        editor.transpose(&Default::default(), cx);
3396        assert_eq!(editor.text(cx), "bcade\n");
3397        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
3398
3399        editor.transpose(&Default::default(), cx);
3400        assert_eq!(editor.text(cx), "bcaed\n");
3401        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
3402
3403        editor
3404    });
3405
3406    _ = cx.add_window(|cx| {
3407        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
3408        editor.set_style(EditorStyle::default(), cx);
3409        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
3410        editor.transpose(&Default::default(), cx);
3411        assert_eq!(editor.text(cx), "🏀🍐✋");
3412        assert_eq!(editor.selections.ranges(cx), [8..8]);
3413
3414        editor.transpose(&Default::default(), cx);
3415        assert_eq!(editor.text(cx), "🏀✋🍐");
3416        assert_eq!(editor.selections.ranges(cx), [11..11]);
3417
3418        editor.transpose(&Default::default(), cx);
3419        assert_eq!(editor.text(cx), "🏀🍐✋");
3420        assert_eq!(editor.selections.ranges(cx), [11..11]);
3421
3422        editor
3423    });
3424}
3425
3426#[gpui::test]
3427async fn test_clipboard(cx: &mut gpui::TestAppContext) {
3428    init_test(cx, |_| {});
3429
3430    let mut cx = EditorTestContext::new(cx).await;
3431
3432    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
3433    cx.update_editor(|e, cx| e.cut(&Cut, cx));
3434    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
3435
3436    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
3437    cx.set_state("two ˇfour ˇsix ˇ");
3438    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3439    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
3440
3441    // Paste again but with only two cursors. Since the number of cursors doesn't
3442    // match the number of slices in the clipboard, the entire clipboard text
3443    // is pasted at each cursor.
3444    cx.set_state("ˇtwo one✅ four three six five ˇ");
3445    cx.update_editor(|e, cx| {
3446        e.handle_input("( ", cx);
3447        e.paste(&Paste, cx);
3448        e.handle_input(") ", cx);
3449    });
3450    cx.assert_editor_state(
3451        &([
3452            "( one✅ ",
3453            "three ",
3454            "five ) ˇtwo one✅ four three six five ( one✅ ",
3455            "three ",
3456            "five ) ˇ",
3457        ]
3458        .join("\n")),
3459    );
3460
3461    // Cut with three selections, one of which is full-line.
3462    cx.set_state(indoc! {"
3463        1«2ˇ»3
3464        4ˇ567
3465        «8ˇ»9"});
3466    cx.update_editor(|e, cx| e.cut(&Cut, cx));
3467    cx.assert_editor_state(indoc! {"
3468        1ˇ3
3469        ˇ9"});
3470
3471    // Paste with three selections, noticing how the copied selection that was full-line
3472    // gets inserted before the second cursor.
3473    cx.set_state(indoc! {"
3474        1ˇ3
34753476        «oˇ»ne"});
3477    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3478    cx.assert_editor_state(indoc! {"
3479        12ˇ3
3480        4567
34813482        8ˇne"});
3483
3484    // Copy with a single cursor only, which writes the whole line into the clipboard.
3485    cx.set_state(indoc! {"
3486        The quick brown
3487        fox juˇmps over
3488        the lazy dog"});
3489    cx.update_editor(|e, cx| e.copy(&Copy, cx));
3490    assert_eq!(
3491        cx.read_from_clipboard().map(|item| item.text().to_owned()),
3492        Some("fox jumps over\n".to_owned())
3493    );
3494
3495    // Paste with three selections, noticing how the copied full-line selection is inserted
3496    // before the empty selections but replaces the selection that is non-empty.
3497    cx.set_state(indoc! {"
3498        Tˇhe quick brown
3499        «foˇ»x jumps over
3500        tˇhe lazy dog"});
3501    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3502    cx.assert_editor_state(indoc! {"
3503        fox jumps over
3504        Tˇhe quick brown
3505        fox jumps over
3506        ˇx jumps over
3507        fox jumps over
3508        tˇhe lazy dog"});
3509}
3510
3511#[gpui::test]
3512async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
3513    init_test(cx, |_| {});
3514
3515    let mut cx = EditorTestContext::new(cx).await;
3516    let language = Arc::new(Language::new(
3517        LanguageConfig::default(),
3518        Some(tree_sitter_rust::language()),
3519    ));
3520    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
3521
3522    // Cut an indented block, without the leading whitespace.
3523    cx.set_state(indoc! {"
3524        const a: B = (
3525            c(),
3526            «d(
3527                e,
3528                f
3529            )ˇ»
3530        );
3531    "});
3532    cx.update_editor(|e, cx| e.cut(&Cut, cx));
3533    cx.assert_editor_state(indoc! {"
3534        const a: B = (
3535            c(),
3536            ˇ
3537        );
3538    "});
3539
3540    // Paste it at the same position.
3541    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3542    cx.assert_editor_state(indoc! {"
3543        const a: B = (
3544            c(),
3545            d(
3546                e,
3547                f
35483549        );
3550    "});
3551
3552    // Paste it at a line with a lower indent level.
3553    cx.set_state(indoc! {"
3554        ˇ
3555        const a: B = (
3556            c(),
3557        );
3558    "});
3559    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3560    cx.assert_editor_state(indoc! {"
3561        d(
3562            e,
3563            f
35643565        const a: B = (
3566            c(),
3567        );
3568    "});
3569
3570    // Cut an indented block, with the leading whitespace.
3571    cx.set_state(indoc! {"
3572        const a: B = (
3573            c(),
3574        «    d(
3575                e,
3576                f
3577            )
3578        ˇ»);
3579    "});
3580    cx.update_editor(|e, cx| e.cut(&Cut, cx));
3581    cx.assert_editor_state(indoc! {"
3582        const a: B = (
3583            c(),
3584        ˇ);
3585    "});
3586
3587    // Paste it at the same position.
3588    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3589    cx.assert_editor_state(indoc! {"
3590        const a: B = (
3591            c(),
3592            d(
3593                e,
3594                f
3595            )
3596        ˇ);
3597    "});
3598
3599    // Paste it at a line with a higher indent level.
3600    cx.set_state(indoc! {"
3601        const a: B = (
3602            c(),
3603            d(
3604                e,
36053606            )
3607        );
3608    "});
3609    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3610    cx.assert_editor_state(indoc! {"
3611        const a: B = (
3612            c(),
3613            d(
3614                e,
3615                f    d(
3616                    e,
3617                    f
3618                )
3619        ˇ
3620            )
3621        );
3622    "});
3623}
3624
3625#[gpui::test]
3626fn test_select_all(cx: &mut TestAppContext) {
3627    init_test(cx, |_| {});
3628
3629    let view = cx.add_window(|cx| {
3630        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
3631        build_editor(buffer, cx)
3632    });
3633    _ = view.update(cx, |view, cx| {
3634        view.select_all(&SelectAll, cx);
3635        assert_eq!(
3636            view.selections.display_ranges(cx),
3637            &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
3638        );
3639    });
3640}
3641
3642#[gpui::test]
3643fn test_select_line(cx: &mut TestAppContext) {
3644    init_test(cx, |_| {});
3645
3646    let view = cx.add_window(|cx| {
3647        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
3648        build_editor(buffer, cx)
3649    });
3650    _ = view.update(cx, |view, cx| {
3651        view.change_selections(None, cx, |s| {
3652            s.select_display_ranges([
3653                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3654                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3655                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
3656                DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
3657            ])
3658        });
3659        view.select_line(&SelectLine, cx);
3660        assert_eq!(
3661            view.selections.display_ranges(cx),
3662            vec![
3663                DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
3664                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
3665            ]
3666        );
3667    });
3668
3669    _ = view.update(cx, |view, cx| {
3670        view.select_line(&SelectLine, cx);
3671        assert_eq!(
3672            view.selections.display_ranges(cx),
3673            vec![
3674                DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
3675                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
3676            ]
3677        );
3678    });
3679
3680    _ = view.update(cx, |view, cx| {
3681        view.select_line(&SelectLine, cx);
3682        assert_eq!(
3683            view.selections.display_ranges(cx),
3684            vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
3685        );
3686    });
3687}
3688
3689#[gpui::test]
3690fn test_split_selection_into_lines(cx: &mut TestAppContext) {
3691    init_test(cx, |_| {});
3692
3693    let view = cx.add_window(|cx| {
3694        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
3695        build_editor(buffer, cx)
3696    });
3697    _ = view.update(cx, |view, cx| {
3698        view.fold_ranges(
3699            vec![
3700                Point::new(0, 2)..Point::new(1, 2),
3701                Point::new(2, 3)..Point::new(4, 1),
3702                Point::new(7, 0)..Point::new(8, 4),
3703            ],
3704            true,
3705            cx,
3706        );
3707        view.change_selections(None, cx, |s| {
3708            s.select_display_ranges([
3709                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3710                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3711                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
3712                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
3713            ])
3714        });
3715        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
3716    });
3717
3718    _ = view.update(cx, |view, cx| {
3719        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
3720        assert_eq!(
3721            view.display_text(cx),
3722            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
3723        );
3724        assert_eq!(
3725            view.selections.display_ranges(cx),
3726            [
3727                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
3728                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3729                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
3730                DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
3731            ]
3732        );
3733    });
3734
3735    _ = view.update(cx, |view, cx| {
3736        view.change_selections(None, cx, |s| {
3737            s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
3738        });
3739        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
3740        assert_eq!(
3741            view.display_text(cx),
3742            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
3743        );
3744        assert_eq!(
3745            view.selections.display_ranges(cx),
3746            [
3747                DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
3748                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
3749                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
3750                DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
3751                DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
3752                DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
3753                DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
3754                DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
3755            ]
3756        );
3757    });
3758}
3759
3760#[gpui::test]
3761async fn test_add_selection_above_below(cx: &mut TestAppContext) {
3762    init_test(cx, |_| {});
3763
3764    let mut cx = EditorTestContext::new(cx).await;
3765
3766    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
3767    cx.set_state(indoc!(
3768        r#"abc
3769           defˇghi
3770
3771           jk
3772           nlmo
3773           "#
3774    ));
3775
3776    cx.update_editor(|editor, cx| {
3777        editor.add_selection_above(&Default::default(), cx);
3778    });
3779
3780    cx.assert_editor_state(indoc!(
3781        r#"abcˇ
3782           defˇghi
3783
3784           jk
3785           nlmo
3786           "#
3787    ));
3788
3789    cx.update_editor(|editor, cx| {
3790        editor.add_selection_above(&Default::default(), cx);
3791    });
3792
3793    cx.assert_editor_state(indoc!(
3794        r#"abcˇ
3795            defˇghi
3796
3797            jk
3798            nlmo
3799            "#
3800    ));
3801
3802    cx.update_editor(|view, cx| {
3803        view.add_selection_below(&Default::default(), cx);
3804    });
3805
3806    cx.assert_editor_state(indoc!(
3807        r#"abc
3808           defˇghi
3809
3810           jk
3811           nlmo
3812           "#
3813    ));
3814
3815    cx.update_editor(|view, cx| {
3816        view.undo_selection(&Default::default(), cx);
3817    });
3818
3819    cx.assert_editor_state(indoc!(
3820        r#"abcˇ
3821           defˇghi
3822
3823           jk
3824           nlmo
3825           "#
3826    ));
3827
3828    cx.update_editor(|view, cx| {
3829        view.redo_selection(&Default::default(), cx);
3830    });
3831
3832    cx.assert_editor_state(indoc!(
3833        r#"abc
3834           defˇghi
3835
3836           jk
3837           nlmo
3838           "#
3839    ));
3840
3841    cx.update_editor(|view, cx| {
3842        view.add_selection_below(&Default::default(), cx);
3843    });
3844
3845    cx.assert_editor_state(indoc!(
3846        r#"abc
3847           defˇghi
3848
3849           jk
3850           nlmˇo
3851           "#
3852    ));
3853
3854    cx.update_editor(|view, cx| {
3855        view.add_selection_below(&Default::default(), cx);
3856    });
3857
3858    cx.assert_editor_state(indoc!(
3859        r#"abc
3860           defˇghi
3861
3862           jk
3863           nlmˇo
3864           "#
3865    ));
3866
3867    // change selections
3868    cx.set_state(indoc!(
3869        r#"abc
3870           def«ˇg»hi
3871
3872           jk
3873           nlmo
3874           "#
3875    ));
3876
3877    cx.update_editor(|view, cx| {
3878        view.add_selection_below(&Default::default(), cx);
3879    });
3880
3881    cx.assert_editor_state(indoc!(
3882        r#"abc
3883           def«ˇg»hi
3884
3885           jk
3886           nlm«ˇo»
3887           "#
3888    ));
3889
3890    cx.update_editor(|view, cx| {
3891        view.add_selection_below(&Default::default(), cx);
3892    });
3893
3894    cx.assert_editor_state(indoc!(
3895        r#"abc
3896           def«ˇg»hi
3897
3898           jk
3899           nlm«ˇo»
3900           "#
3901    ));
3902
3903    cx.update_editor(|view, cx| {
3904        view.add_selection_above(&Default::default(), cx);
3905    });
3906
3907    cx.assert_editor_state(indoc!(
3908        r#"abc
3909           def«ˇg»hi
3910
3911           jk
3912           nlmo
3913           "#
3914    ));
3915
3916    cx.update_editor(|view, cx| {
3917        view.add_selection_above(&Default::default(), cx);
3918    });
3919
3920    cx.assert_editor_state(indoc!(
3921        r#"abc
3922           def«ˇg»hi
3923
3924           jk
3925           nlmo
3926           "#
3927    ));
3928
3929    // Change selections again
3930    cx.set_state(indoc!(
3931        r#"a«bc
3932           defgˇ»hi
3933
3934           jk
3935           nlmo
3936           "#
3937    ));
3938
3939    cx.update_editor(|view, cx| {
3940        view.add_selection_below(&Default::default(), cx);
3941    });
3942
3943    cx.assert_editor_state(indoc!(
3944        r#"a«bcˇ»
3945           d«efgˇ»hi
3946
3947           j«kˇ»
3948           nlmo
3949           "#
3950    ));
3951
3952    cx.update_editor(|view, cx| {
3953        view.add_selection_below(&Default::default(), cx);
3954    });
3955    cx.assert_editor_state(indoc!(
3956        r#"a«bcˇ»
3957           d«efgˇ»hi
3958
3959           j«kˇ»
3960           n«lmoˇ»
3961           "#
3962    ));
3963    cx.update_editor(|view, cx| {
3964        view.add_selection_above(&Default::default(), cx);
3965    });
3966
3967    cx.assert_editor_state(indoc!(
3968        r#"a«bcˇ»
3969           d«efgˇ»hi
3970
3971           j«kˇ»
3972           nlmo
3973           "#
3974    ));
3975
3976    // Change selections again
3977    cx.set_state(indoc!(
3978        r#"abc
3979           d«ˇefghi
3980
3981           jk
3982           nlm»o
3983           "#
3984    ));
3985
3986    cx.update_editor(|view, cx| {
3987        view.add_selection_above(&Default::default(), cx);
3988    });
3989
3990    cx.assert_editor_state(indoc!(
3991        r#"a«ˇbc»
3992           d«ˇef»ghi
3993
3994           j«ˇk»
3995           n«ˇlm»o
3996           "#
3997    ));
3998
3999    cx.update_editor(|view, cx| {
4000        view.add_selection_below(&Default::default(), cx);
4001    });
4002
4003    cx.assert_editor_state(indoc!(
4004        r#"abc
4005           d«ˇef»ghi
4006
4007           j«ˇk»
4008           n«ˇlm»o
4009           "#
4010    ));
4011}
4012
4013#[gpui::test]
4014async fn test_select_next(cx: &mut gpui::TestAppContext) {
4015    init_test(cx, |_| {});
4016
4017    let mut cx = EditorTestContext::new(cx).await;
4018    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
4019
4020    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
4021        .unwrap();
4022    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
4023
4024    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
4025        .unwrap();
4026    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
4027
4028    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
4029    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
4030
4031    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
4032    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
4033
4034    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
4035        .unwrap();
4036    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
4037
4038    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
4039        .unwrap();
4040    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
4041}
4042
4043#[gpui::test]
4044async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
4045    init_test(cx, |_| {});
4046
4047    let mut cx = EditorTestContext::new(cx).await;
4048    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
4049
4050    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
4051        .unwrap();
4052    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
4053}
4054
4055#[gpui::test]
4056async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
4057    init_test(cx, |_| {});
4058
4059    let mut cx = EditorTestContext::new(cx).await;
4060    cx.set_state(
4061        r#"let foo = 2;
4062lˇet foo = 2;
4063let fooˇ = 2;
4064let foo = 2;
4065let foo = ˇ2;"#,
4066    );
4067
4068    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
4069        .unwrap();
4070    cx.assert_editor_state(
4071        r#"let foo = 2;
4072«letˇ» foo = 2;
4073let «fooˇ» = 2;
4074let foo = 2;
4075let foo = «2ˇ»;"#,
4076    );
4077
4078    // noop for multiple selections with different contents
4079    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
4080        .unwrap();
4081    cx.assert_editor_state(
4082        r#"let foo = 2;
4083«letˇ» foo = 2;
4084let «fooˇ» = 2;
4085let foo = 2;
4086let foo = «2ˇ»;"#,
4087    );
4088}
4089
4090#[gpui::test]
4091async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
4092    init_test(cx, |_| {});
4093
4094    let mut cx = EditorTestContext::new_multibuffer(
4095        cx,
4096        [
4097            indoc! {
4098                "aaa\n«bbb\nccc\n»ddd"
4099            },
4100            indoc! {
4101                "aaa\n«bbb\nccc\n»ddd"
4102            },
4103        ],
4104    );
4105
4106    cx.assert_editor_state(indoc! {"
4107        ˇbbb
4108        ccc
4109
4110        bbb
4111        ccc
4112        "});
4113    cx.dispatch_action(SelectPrevious::default());
4114    cx.assert_editor_state(indoc! {"
4115                «bbbˇ»
4116                ccc
4117
4118                bbb
4119                ccc
4120                "});
4121    cx.dispatch_action(SelectPrevious::default());
4122    cx.assert_editor_state(indoc! {"
4123                «bbbˇ»
4124                ccc
4125
4126                «bbbˇ»
4127                ccc
4128                "});
4129}
4130
4131#[gpui::test]
4132async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
4133    init_test(cx, |_| {});
4134
4135    let mut cx = EditorTestContext::new(cx).await;
4136    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
4137
4138    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4139        .unwrap();
4140    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
4141
4142    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4143        .unwrap();
4144    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
4145
4146    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
4147    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
4148
4149    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
4150    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
4151
4152    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4153        .unwrap();
4154    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
4155
4156    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4157        .unwrap();
4158    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
4159
4160    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4161        .unwrap();
4162    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
4163}
4164
4165#[gpui::test]
4166async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
4167    init_test(cx, |_| {});
4168
4169    let mut cx = EditorTestContext::new(cx).await;
4170    cx.set_state(
4171        r#"let foo = 2;
4172lˇet foo = 2;
4173let fooˇ = 2;
4174let foo = 2;
4175let foo = ˇ2;"#,
4176    );
4177
4178    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4179        .unwrap();
4180    cx.assert_editor_state(
4181        r#"let foo = 2;
4182«letˇ» foo = 2;
4183let «fooˇ» = 2;
4184let foo = 2;
4185let foo = «2ˇ»;"#,
4186    );
4187
4188    // noop for multiple selections with different contents
4189    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4190        .unwrap();
4191    cx.assert_editor_state(
4192        r#"let foo = 2;
4193«letˇ» foo = 2;
4194let «fooˇ» = 2;
4195let foo = 2;
4196let foo = «2ˇ»;"#,
4197    );
4198}
4199
4200#[gpui::test]
4201async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
4202    init_test(cx, |_| {});
4203
4204    let mut cx = EditorTestContext::new(cx).await;
4205    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
4206
4207    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4208        .unwrap();
4209    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
4210
4211    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4212        .unwrap();
4213    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
4214
4215    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
4216    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
4217
4218    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
4219    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
4220
4221    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4222        .unwrap();
4223    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
4224
4225    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4226        .unwrap();
4227    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
4228}
4229
4230#[gpui::test]
4231async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
4232    init_test(cx, |_| {});
4233
4234    let language = Arc::new(Language::new(
4235        LanguageConfig::default(),
4236        Some(tree_sitter_rust::language()),
4237    ));
4238
4239    let text = r#"
4240        use mod1::mod2::{mod3, mod4};
4241
4242        fn fn_1(param1: bool, param2: &str) {
4243            let var1 = "text";
4244        }
4245    "#
4246    .unindent();
4247
4248    let buffer = cx.new_model(|cx| {
4249        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
4250            .with_language(language, cx)
4251    });
4252    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
4253    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
4254
4255    view.condition::<crate::EditorEvent>(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
4256        .await;
4257
4258    _ = view.update(cx, |view, cx| {
4259        view.change_selections(None, cx, |s| {
4260            s.select_display_ranges([
4261                DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
4262                DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
4263                DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
4264            ]);
4265        });
4266        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
4267    });
4268    assert_eq!(
4269        view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
4270        &[
4271            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
4272            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
4273            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
4274        ]
4275    );
4276
4277    _ = view.update(cx, |view, cx| {
4278        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
4279    });
4280    assert_eq!(
4281        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4282        &[
4283            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
4284            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
4285        ]
4286    );
4287
4288    _ = view.update(cx, |view, cx| {
4289        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
4290    });
4291    assert_eq!(
4292        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4293        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
4294    );
4295
4296    // Trying to expand the selected syntax node one more time has no effect.
4297    _ = view.update(cx, |view, cx| {
4298        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
4299    });
4300    assert_eq!(
4301        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4302        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
4303    );
4304
4305    _ = view.update(cx, |view, cx| {
4306        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
4307    });
4308    assert_eq!(
4309        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4310        &[
4311            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
4312            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
4313        ]
4314    );
4315
4316    _ = view.update(cx, |view, cx| {
4317        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
4318    });
4319    assert_eq!(
4320        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4321        &[
4322            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
4323            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
4324            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
4325        ]
4326    );
4327
4328    _ = view.update(cx, |view, cx| {
4329        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
4330    });
4331    assert_eq!(
4332        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4333        &[
4334            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
4335            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
4336            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
4337        ]
4338    );
4339
4340    // Trying to shrink the selected syntax node one more time has no effect.
4341    _ = view.update(cx, |view, cx| {
4342        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
4343    });
4344    assert_eq!(
4345        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4346        &[
4347            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
4348            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
4349            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
4350        ]
4351    );
4352
4353    // Ensure that we keep expanding the selection if the larger selection starts or ends within
4354    // a fold.
4355    _ = view.update(cx, |view, cx| {
4356        view.fold_ranges(
4357            vec![
4358                Point::new(0, 21)..Point::new(0, 24),
4359                Point::new(3, 20)..Point::new(3, 22),
4360            ],
4361            true,
4362            cx,
4363        );
4364        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
4365    });
4366    assert_eq!(
4367        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4368        &[
4369            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
4370            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
4371            DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
4372        ]
4373    );
4374}
4375
4376#[gpui::test]
4377async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
4378    init_test(cx, |_| {});
4379
4380    let language = Arc::new(
4381        Language::new(
4382            LanguageConfig {
4383                brackets: BracketPairConfig {
4384                    pairs: vec![
4385                        BracketPair {
4386                            start: "{".to_string(),
4387                            end: "}".to_string(),
4388                            close: false,
4389                            newline: true,
4390                        },
4391                        BracketPair {
4392                            start: "(".to_string(),
4393                            end: ")".to_string(),
4394                            close: false,
4395                            newline: true,
4396                        },
4397                    ],
4398                    ..Default::default()
4399                },
4400                ..Default::default()
4401            },
4402            Some(tree_sitter_rust::language()),
4403        )
4404        .with_indents_query(
4405            r#"
4406                (_ "(" ")" @end) @indent
4407                (_ "{" "}" @end) @indent
4408            "#,
4409        )
4410        .unwrap(),
4411    );
4412
4413    let text = "fn a() {}";
4414
4415    let buffer = cx.new_model(|cx| {
4416        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
4417            .with_language(language, cx)
4418    });
4419    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
4420    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
4421    editor
4422        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
4423        .await;
4424
4425    _ = editor.update(cx, |editor, cx| {
4426        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
4427        editor.newline(&Newline, cx);
4428        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
4429        assert_eq!(
4430            editor.selections.ranges(cx),
4431            &[
4432                Point::new(1, 4)..Point::new(1, 4),
4433                Point::new(3, 4)..Point::new(3, 4),
4434                Point::new(5, 0)..Point::new(5, 0)
4435            ]
4436        );
4437    });
4438}
4439
4440#[gpui::test]
4441async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
4442    init_test(cx, |_| {});
4443
4444    let mut cx = EditorTestContext::new(cx).await;
4445
4446    let language = Arc::new(Language::new(
4447        LanguageConfig {
4448            brackets: BracketPairConfig {
4449                pairs: vec![
4450                    BracketPair {
4451                        start: "{".to_string(),
4452                        end: "}".to_string(),
4453                        close: true,
4454                        newline: true,
4455                    },
4456                    BracketPair {
4457                        start: "(".to_string(),
4458                        end: ")".to_string(),
4459                        close: true,
4460                        newline: true,
4461                    },
4462                    BracketPair {
4463                        start: "/*".to_string(),
4464                        end: " */".to_string(),
4465                        close: true,
4466                        newline: true,
4467                    },
4468                    BracketPair {
4469                        start: "[".to_string(),
4470                        end: "]".to_string(),
4471                        close: false,
4472                        newline: true,
4473                    },
4474                    BracketPair {
4475                        start: "\"".to_string(),
4476                        end: "\"".to_string(),
4477                        close: true,
4478                        newline: false,
4479                    },
4480                ],
4481                ..Default::default()
4482            },
4483            autoclose_before: "})]".to_string(),
4484            ..Default::default()
4485        },
4486        Some(tree_sitter_rust::language()),
4487    ));
4488
4489    cx.language_registry().add(language.clone());
4490    cx.update_buffer(|buffer, cx| {
4491        buffer.set_language(Some(language), cx);
4492    });
4493
4494    cx.set_state(
4495        &r#"
4496            🏀ˇ
4497            εˇ
4498            ❤️ˇ
4499        "#
4500        .unindent(),
4501    );
4502
4503    // autoclose multiple nested brackets at multiple cursors
4504    cx.update_editor(|view, cx| {
4505        view.handle_input("{", cx);
4506        view.handle_input("{", cx);
4507        view.handle_input("{", cx);
4508    });
4509    cx.assert_editor_state(
4510        &"
4511            🏀{{{ˇ}}}
4512            ε{{{ˇ}}}
4513            ❤️{{{ˇ}}}
4514        "
4515        .unindent(),
4516    );
4517
4518    // insert a different closing bracket
4519    cx.update_editor(|view, cx| {
4520        view.handle_input(")", cx);
4521    });
4522    cx.assert_editor_state(
4523        &"
4524            🏀{{{)ˇ}}}
4525            ε{{{)ˇ}}}
4526            ❤️{{{)ˇ}}}
4527        "
4528        .unindent(),
4529    );
4530
4531    // skip over the auto-closed brackets when typing a closing bracket
4532    cx.update_editor(|view, cx| {
4533        view.move_right(&MoveRight, cx);
4534        view.handle_input("}", cx);
4535        view.handle_input("}", cx);
4536        view.handle_input("}", cx);
4537    });
4538    cx.assert_editor_state(
4539        &"
4540            🏀{{{)}}}}ˇ
4541            ε{{{)}}}}ˇ
4542            ❤️{{{)}}}}ˇ
4543        "
4544        .unindent(),
4545    );
4546
4547    // autoclose multi-character pairs
4548    cx.set_state(
4549        &"
4550            ˇ
4551            ˇ
4552        "
4553        .unindent(),
4554    );
4555    cx.update_editor(|view, cx| {
4556        view.handle_input("/", cx);
4557        view.handle_input("*", cx);
4558    });
4559    cx.assert_editor_state(
4560        &"
4561            /*ˇ */
4562            /*ˇ */
4563        "
4564        .unindent(),
4565    );
4566
4567    // one cursor autocloses a multi-character pair, one cursor
4568    // does not autoclose.
4569    cx.set_state(
4570        &"
45714572            ˇ
4573        "
4574        .unindent(),
4575    );
4576    cx.update_editor(|view, cx| view.handle_input("*", cx));
4577    cx.assert_editor_state(
4578        &"
4579            /*ˇ */
45804581        "
4582        .unindent(),
4583    );
4584
4585    // Don't autoclose if the next character isn't whitespace and isn't
4586    // listed in the language's "autoclose_before" section.
4587    cx.set_state("ˇa b");
4588    cx.update_editor(|view, cx| view.handle_input("{", cx));
4589    cx.assert_editor_state("{ˇa b");
4590
4591    // Don't autoclose if `close` is false for the bracket pair
4592    cx.set_state("ˇ");
4593    cx.update_editor(|view, cx| view.handle_input("[", cx));
4594    cx.assert_editor_state("");
4595
4596    // Surround with brackets if text is selected
4597    cx.set_state("«aˇ» b");
4598    cx.update_editor(|view, cx| view.handle_input("{", cx));
4599    cx.assert_editor_state("{«aˇ»} b");
4600
4601    // Autclose pair where the start and end characters are the same
4602    cx.set_state("");
4603    cx.update_editor(|view, cx| view.handle_input("\"", cx));
4604    cx.assert_editor_state("a\"ˇ\"");
4605    cx.update_editor(|view, cx| view.handle_input("\"", cx));
4606    cx.assert_editor_state("a\"\"ˇ");
4607}
4608
4609#[gpui::test]
4610async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
4611    init_test(cx, |settings| {
4612        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
4613    });
4614
4615    let mut cx = EditorTestContext::new(cx).await;
4616
4617    let language = Arc::new(Language::new(
4618        LanguageConfig {
4619            brackets: BracketPairConfig {
4620                pairs: vec![
4621                    BracketPair {
4622                        start: "{".to_string(),
4623                        end: "}".to_string(),
4624                        close: true,
4625                        newline: true,
4626                    },
4627                    BracketPair {
4628                        start: "(".to_string(),
4629                        end: ")".to_string(),
4630                        close: true,
4631                        newline: true,
4632                    },
4633                    BracketPair {
4634                        start: "[".to_string(),
4635                        end: "]".to_string(),
4636                        close: false,
4637                        newline: true,
4638                    },
4639                ],
4640                ..Default::default()
4641            },
4642            autoclose_before: "})]".to_string(),
4643            ..Default::default()
4644        },
4645        Some(tree_sitter_rust::language()),
4646    ));
4647
4648    cx.language_registry().add(language.clone());
4649    cx.update_buffer(|buffer, cx| {
4650        buffer.set_language(Some(language), cx);
4651    });
4652
4653    cx.set_state(
4654        &"
4655            ˇ
4656            ˇ
4657            ˇ
4658        "
4659        .unindent(),
4660    );
4661
4662    // ensure only matching closing brackets are skipped over
4663    cx.update_editor(|view, cx| {
4664        view.handle_input("}", cx);
4665        view.move_left(&MoveLeft, cx);
4666        view.handle_input(")", cx);
4667        view.move_left(&MoveLeft, cx);
4668    });
4669    cx.assert_editor_state(
4670        &"
4671            ˇ)}
4672            ˇ)}
4673            ˇ)}
4674        "
4675        .unindent(),
4676    );
4677
4678    // skip-over closing brackets at multiple cursors
4679    cx.update_editor(|view, cx| {
4680        view.handle_input(")", cx);
4681        view.handle_input("}", cx);
4682    });
4683    cx.assert_editor_state(
4684        &"
4685            )}ˇ
4686            )}ˇ
4687            )}ˇ
4688        "
4689        .unindent(),
4690    );
4691
4692    // ignore non-close brackets
4693    cx.update_editor(|view, cx| {
4694        view.handle_input("]", cx);
4695        view.move_left(&MoveLeft, cx);
4696        view.handle_input("]", cx);
4697    });
4698    cx.assert_editor_state(
4699        &"
4700            )}]ˇ]
4701            )}]ˇ]
4702            )}]ˇ]
4703        "
4704        .unindent(),
4705    );
4706}
4707
4708#[gpui::test]
4709async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
4710    init_test(cx, |_| {});
4711
4712    let mut cx = EditorTestContext::new(cx).await;
4713
4714    let html_language = Arc::new(
4715        Language::new(
4716            LanguageConfig {
4717                name: "HTML".into(),
4718                brackets: BracketPairConfig {
4719                    pairs: vec![
4720                        BracketPair {
4721                            start: "<".into(),
4722                            end: ">".into(),
4723                            close: true,
4724                            ..Default::default()
4725                        },
4726                        BracketPair {
4727                            start: "{".into(),
4728                            end: "}".into(),
4729                            close: true,
4730                            ..Default::default()
4731                        },
4732                        BracketPair {
4733                            start: "(".into(),
4734                            end: ")".into(),
4735                            close: true,
4736                            ..Default::default()
4737                        },
4738                    ],
4739                    ..Default::default()
4740                },
4741                autoclose_before: "})]>".into(),
4742                ..Default::default()
4743            },
4744            Some(tree_sitter_html::language()),
4745        )
4746        .with_injection_query(
4747            r#"
4748            (script_element
4749                (raw_text) @content
4750                (#set! "language" "javascript"))
4751            "#,
4752        )
4753        .unwrap(),
4754    );
4755
4756    let javascript_language = Arc::new(Language::new(
4757        LanguageConfig {
4758            name: "JavaScript".into(),
4759            brackets: BracketPairConfig {
4760                pairs: vec![
4761                    BracketPair {
4762                        start: "/*".into(),
4763                        end: " */".into(),
4764                        close: true,
4765                        ..Default::default()
4766                    },
4767                    BracketPair {
4768                        start: "{".into(),
4769                        end: "}".into(),
4770                        close: true,
4771                        ..Default::default()
4772                    },
4773                    BracketPair {
4774                        start: "(".into(),
4775                        end: ")".into(),
4776                        close: true,
4777                        ..Default::default()
4778                    },
4779                ],
4780                ..Default::default()
4781            },
4782            autoclose_before: "})]>".into(),
4783            ..Default::default()
4784        },
4785        Some(tree_sitter_typescript::language_tsx()),
4786    ));
4787
4788    cx.language_registry().add(html_language.clone());
4789    cx.language_registry().add(javascript_language.clone());
4790
4791    cx.update_buffer(|buffer, cx| {
4792        buffer.set_language(Some(html_language), cx);
4793    });
4794
4795    cx.set_state(
4796        &r#"
4797            <body>ˇ
4798                <script>
4799                    var x = 1;ˇ
4800                </script>
4801            </body>ˇ
4802        "#
4803        .unindent(),
4804    );
4805
4806    // Precondition: different languages are active at different locations.
4807    cx.update_editor(|editor, cx| {
4808        let snapshot = editor.snapshot(cx);
4809        let cursors = editor.selections.ranges::<usize>(cx);
4810        let languages = cursors
4811            .iter()
4812            .map(|c| snapshot.language_at(c.start).unwrap().name())
4813            .collect::<Vec<_>>();
4814        assert_eq!(
4815            languages,
4816            &["HTML".into(), "JavaScript".into(), "HTML".into()]
4817        );
4818    });
4819
4820    // Angle brackets autoclose in HTML, but not JavaScript.
4821    cx.update_editor(|editor, cx| {
4822        editor.handle_input("<", cx);
4823        editor.handle_input("a", cx);
4824    });
4825    cx.assert_editor_state(
4826        &r#"
4827            <body><aˇ>
4828                <script>
4829                    var x = 1;<aˇ
4830                </script>
4831            </body><aˇ>
4832        "#
4833        .unindent(),
4834    );
4835
4836    // Curly braces and parens autoclose in both HTML and JavaScript.
4837    cx.update_editor(|editor, cx| {
4838        editor.handle_input(" b=", cx);
4839        editor.handle_input("{", cx);
4840        editor.handle_input("c", cx);
4841        editor.handle_input("(", cx);
4842    });
4843    cx.assert_editor_state(
4844        &r#"
4845            <body><a b={c(ˇ)}>
4846                <script>
4847                    var x = 1;<a b={c(ˇ)}
4848                </script>
4849            </body><a b={c(ˇ)}>
4850        "#
4851        .unindent(),
4852    );
4853
4854    // Brackets that were already autoclosed are skipped.
4855    cx.update_editor(|editor, cx| {
4856        editor.handle_input(")", cx);
4857        editor.handle_input("d", cx);
4858        editor.handle_input("}", cx);
4859    });
4860    cx.assert_editor_state(
4861        &r#"
4862            <body><a b={c()d}ˇ>
4863                <script>
4864                    var x = 1;<a b={c()d}ˇ
4865                </script>
4866            </body><a b={c()d}ˇ>
4867        "#
4868        .unindent(),
4869    );
4870    cx.update_editor(|editor, cx| {
4871        editor.handle_input(">", cx);
4872    });
4873    cx.assert_editor_state(
4874        &r#"
4875            <body><a b={c()d}>ˇ
4876                <script>
4877                    var x = 1;<a b={c()d}>ˇ
4878                </script>
4879            </body><a b={c()d}>ˇ
4880        "#
4881        .unindent(),
4882    );
4883
4884    // Reset
4885    cx.set_state(
4886        &r#"
4887            <body>ˇ
4888                <script>
4889                    var x = 1;ˇ
4890                </script>
4891            </body>ˇ
4892        "#
4893        .unindent(),
4894    );
4895
4896    cx.update_editor(|editor, cx| {
4897        editor.handle_input("<", cx);
4898    });
4899    cx.assert_editor_state(
4900        &r#"
4901            <body><ˇ>
4902                <script>
4903                    var x = 1;<ˇ
4904                </script>
4905            </body><ˇ>
4906        "#
4907        .unindent(),
4908    );
4909
4910    // When backspacing, the closing angle brackets are removed.
4911    cx.update_editor(|editor, cx| {
4912        editor.backspace(&Backspace, cx);
4913    });
4914    cx.assert_editor_state(
4915        &r#"
4916            <body>ˇ
4917                <script>
4918                    var x = 1;ˇ
4919                </script>
4920            </body>ˇ
4921        "#
4922        .unindent(),
4923    );
4924
4925    // Block comments autoclose in JavaScript, but not HTML.
4926    cx.update_editor(|editor, cx| {
4927        editor.handle_input("/", cx);
4928        editor.handle_input("*", cx);
4929    });
4930    cx.assert_editor_state(
4931        &r#"
4932            <body>/*ˇ
4933                <script>
4934                    var x = 1;/*ˇ */
4935                </script>
4936            </body>/*ˇ
4937        "#
4938        .unindent(),
4939    );
4940}
4941
4942#[gpui::test]
4943async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
4944    init_test(cx, |_| {});
4945
4946    let mut cx = EditorTestContext::new(cx).await;
4947
4948    let rust_language = Arc::new(
4949        Language::new(
4950            LanguageConfig {
4951                name: "Rust".into(),
4952                brackets: serde_json::from_value(json!([
4953                    { "start": "{", "end": "}", "close": true, "newline": true },
4954                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
4955                ]))
4956                .unwrap(),
4957                autoclose_before: "})]>".into(),
4958                ..Default::default()
4959            },
4960            Some(tree_sitter_rust::language()),
4961        )
4962        .with_override_query("(string_literal) @string")
4963        .unwrap(),
4964    );
4965
4966    cx.language_registry().add(rust_language.clone());
4967    cx.update_buffer(|buffer, cx| {
4968        buffer.set_language(Some(rust_language), cx);
4969    });
4970
4971    cx.set_state(
4972        &r#"
4973            let x = ˇ
4974        "#
4975        .unindent(),
4976    );
4977
4978    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
4979    cx.update_editor(|editor, cx| {
4980        editor.handle_input("\"", cx);
4981    });
4982    cx.assert_editor_state(
4983        &r#"
4984            let x = "ˇ"
4985        "#
4986        .unindent(),
4987    );
4988
4989    // Inserting another quotation mark. The cursor moves across the existing
4990    // automatically-inserted quotation mark.
4991    cx.update_editor(|editor, cx| {
4992        editor.handle_input("\"", cx);
4993    });
4994    cx.assert_editor_state(
4995        &r#"
4996            let x = ""ˇ
4997        "#
4998        .unindent(),
4999    );
5000
5001    // Reset
5002    cx.set_state(
5003        &r#"
5004            let x = ˇ
5005        "#
5006        .unindent(),
5007    );
5008
5009    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
5010    cx.update_editor(|editor, cx| {
5011        editor.handle_input("\"", cx);
5012        editor.handle_input(" ", cx);
5013        editor.move_left(&Default::default(), cx);
5014        editor.handle_input("\\", cx);
5015        editor.handle_input("\"", cx);
5016    });
5017    cx.assert_editor_state(
5018        &r#"
5019            let x = "\"ˇ "
5020        "#
5021        .unindent(),
5022    );
5023
5024    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
5025    // mark. Nothing is inserted.
5026    cx.update_editor(|editor, cx| {
5027        editor.move_right(&Default::default(), cx);
5028        editor.handle_input("\"", cx);
5029    });
5030    cx.assert_editor_state(
5031        &r#"
5032            let x = "\" "ˇ
5033        "#
5034        .unindent(),
5035    );
5036}
5037
5038#[gpui::test]
5039async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
5040    init_test(cx, |_| {});
5041
5042    let language = Arc::new(Language::new(
5043        LanguageConfig {
5044            brackets: BracketPairConfig {
5045                pairs: vec![
5046                    BracketPair {
5047                        start: "{".to_string(),
5048                        end: "}".to_string(),
5049                        close: true,
5050                        newline: true,
5051                    },
5052                    BracketPair {
5053                        start: "/* ".to_string(),
5054                        end: "*/".to_string(),
5055                        close: true,
5056                        ..Default::default()
5057                    },
5058                ],
5059                ..Default::default()
5060            },
5061            ..Default::default()
5062        },
5063        Some(tree_sitter_rust::language()),
5064    ));
5065
5066    let text = r#"
5067        a
5068        b
5069        c
5070    "#
5071    .unindent();
5072
5073    let buffer = cx.new_model(|cx| {
5074        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
5075            .with_language(language, cx)
5076    });
5077    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
5078    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5079    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
5080        .await;
5081
5082    _ = view.update(cx, |view, cx| {
5083        view.change_selections(None, cx, |s| {
5084            s.select_display_ranges([
5085                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
5086                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
5087                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
5088            ])
5089        });
5090
5091        view.handle_input("{", cx);
5092        view.handle_input("{", cx);
5093        view.handle_input("{", cx);
5094        assert_eq!(
5095            view.text(cx),
5096            "
5097                {{{a}}}
5098                {{{b}}}
5099                {{{c}}}
5100            "
5101            .unindent()
5102        );
5103        assert_eq!(
5104            view.selections.display_ranges(cx),
5105            [
5106                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
5107                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
5108                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
5109            ]
5110        );
5111
5112        view.undo(&Undo, cx);
5113        view.undo(&Undo, cx);
5114        view.undo(&Undo, cx);
5115        assert_eq!(
5116            view.text(cx),
5117            "
5118                a
5119                b
5120                c
5121            "
5122            .unindent()
5123        );
5124        assert_eq!(
5125            view.selections.display_ranges(cx),
5126            [
5127                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
5128                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
5129                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
5130            ]
5131        );
5132
5133        // Ensure inserting the first character of a multi-byte bracket pair
5134        // doesn't surround the selections with the bracket.
5135        view.handle_input("/", cx);
5136        assert_eq!(
5137            view.text(cx),
5138            "
5139                /
5140                /
5141                /
5142            "
5143            .unindent()
5144        );
5145        assert_eq!(
5146            view.selections.display_ranges(cx),
5147            [
5148                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
5149                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
5150                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
5151            ]
5152        );
5153
5154        view.undo(&Undo, cx);
5155        assert_eq!(
5156            view.text(cx),
5157            "
5158                a
5159                b
5160                c
5161            "
5162            .unindent()
5163        );
5164        assert_eq!(
5165            view.selections.display_ranges(cx),
5166            [
5167                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
5168                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
5169                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
5170            ]
5171        );
5172
5173        // Ensure inserting the last character of a multi-byte bracket pair
5174        // doesn't surround the selections with the bracket.
5175        view.handle_input("*", cx);
5176        assert_eq!(
5177            view.text(cx),
5178            "
5179                *
5180                *
5181                *
5182            "
5183            .unindent()
5184        );
5185        assert_eq!(
5186            view.selections.display_ranges(cx),
5187            [
5188                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
5189                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
5190                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
5191            ]
5192        );
5193    });
5194}
5195
5196#[gpui::test]
5197async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
5198    init_test(cx, |_| {});
5199
5200    let language = Arc::new(Language::new(
5201        LanguageConfig {
5202            brackets: BracketPairConfig {
5203                pairs: vec![BracketPair {
5204                    start: "{".to_string(),
5205                    end: "}".to_string(),
5206                    close: true,
5207                    newline: true,
5208                }],
5209                ..Default::default()
5210            },
5211            autoclose_before: "}".to_string(),
5212            ..Default::default()
5213        },
5214        Some(tree_sitter_rust::language()),
5215    ));
5216
5217    let text = r#"
5218        a
5219        b
5220        c
5221    "#
5222    .unindent();
5223
5224    let buffer = cx.new_model(|cx| {
5225        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
5226            .with_language(language, cx)
5227    });
5228    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
5229    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5230    editor
5231        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
5232        .await;
5233
5234    _ = editor.update(cx, |editor, cx| {
5235        editor.change_selections(None, cx, |s| {
5236            s.select_ranges([
5237                Point::new(0, 1)..Point::new(0, 1),
5238                Point::new(1, 1)..Point::new(1, 1),
5239                Point::new(2, 1)..Point::new(2, 1),
5240            ])
5241        });
5242
5243        editor.handle_input("{", cx);
5244        editor.handle_input("{", cx);
5245        editor.handle_input("_", cx);
5246        assert_eq!(
5247            editor.text(cx),
5248            "
5249                a{{_}}
5250                b{{_}}
5251                c{{_}}
5252            "
5253            .unindent()
5254        );
5255        assert_eq!(
5256            editor.selections.ranges::<Point>(cx),
5257            [
5258                Point::new(0, 4)..Point::new(0, 4),
5259                Point::new(1, 4)..Point::new(1, 4),
5260                Point::new(2, 4)..Point::new(2, 4)
5261            ]
5262        );
5263
5264        editor.backspace(&Default::default(), cx);
5265        editor.backspace(&Default::default(), cx);
5266        assert_eq!(
5267            editor.text(cx),
5268            "
5269                a{}
5270                b{}
5271                c{}
5272            "
5273            .unindent()
5274        );
5275        assert_eq!(
5276            editor.selections.ranges::<Point>(cx),
5277            [
5278                Point::new(0, 2)..Point::new(0, 2),
5279                Point::new(1, 2)..Point::new(1, 2),
5280                Point::new(2, 2)..Point::new(2, 2)
5281            ]
5282        );
5283
5284        editor.delete_to_previous_word_start(&Default::default(), cx);
5285        assert_eq!(
5286            editor.text(cx),
5287            "
5288                a
5289                b
5290                c
5291            "
5292            .unindent()
5293        );
5294        assert_eq!(
5295            editor.selections.ranges::<Point>(cx),
5296            [
5297                Point::new(0, 1)..Point::new(0, 1),
5298                Point::new(1, 1)..Point::new(1, 1),
5299                Point::new(2, 1)..Point::new(2, 1)
5300            ]
5301        );
5302    });
5303}
5304
5305#[gpui::test]
5306async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
5307    init_test(cx, |settings| {
5308        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
5309    });
5310
5311    let mut cx = EditorTestContext::new(cx).await;
5312
5313    let language = Arc::new(Language::new(
5314        LanguageConfig {
5315            brackets: BracketPairConfig {
5316                pairs: vec![
5317                    BracketPair {
5318                        start: "{".to_string(),
5319                        end: "}".to_string(),
5320                        close: true,
5321                        newline: true,
5322                    },
5323                    BracketPair {
5324                        start: "(".to_string(),
5325                        end: ")".to_string(),
5326                        close: true,
5327                        newline: true,
5328                    },
5329                    BracketPair {
5330                        start: "[".to_string(),
5331                        end: "]".to_string(),
5332                        close: false,
5333                        newline: true,
5334                    },
5335                ],
5336                ..Default::default()
5337            },
5338            autoclose_before: "})]".to_string(),
5339            ..Default::default()
5340        },
5341        Some(tree_sitter_rust::language()),
5342    ));
5343
5344    cx.language_registry().add(language.clone());
5345    cx.update_buffer(|buffer, cx| {
5346        buffer.set_language(Some(language), cx);
5347    });
5348
5349    cx.set_state(
5350        &"
5351            {(ˇ)}
5352            [[ˇ]]
5353            {(ˇ)}
5354        "
5355        .unindent(),
5356    );
5357
5358    cx.update_editor(|view, cx| {
5359        view.backspace(&Default::default(), cx);
5360        view.backspace(&Default::default(), cx);
5361    });
5362
5363    cx.assert_editor_state(
5364        &"
5365            ˇ
5366            ˇ]]
5367            ˇ
5368        "
5369        .unindent(),
5370    );
5371
5372    cx.update_editor(|view, cx| {
5373        view.handle_input("{", cx);
5374        view.handle_input("{", cx);
5375        view.move_right(&MoveRight, cx);
5376        view.move_right(&MoveRight, cx);
5377        view.move_left(&MoveLeft, cx);
5378        view.move_left(&MoveLeft, cx);
5379        view.backspace(&Default::default(), cx);
5380    });
5381
5382    cx.assert_editor_state(
5383        &"
5384            {ˇ}
5385            {ˇ}]]
5386            {ˇ}
5387        "
5388        .unindent(),
5389    );
5390
5391    cx.update_editor(|view, cx| {
5392        view.backspace(&Default::default(), cx);
5393    });
5394
5395    cx.assert_editor_state(
5396        &"
5397            ˇ
5398            ˇ]]
5399            ˇ
5400        "
5401        .unindent(),
5402    );
5403}
5404
5405#[gpui::test]
5406async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
5407    init_test(cx, |_| {});
5408
5409    let language = Arc::new(Language::new(
5410        LanguageConfig::default(),
5411        Some(tree_sitter_rust::language()),
5412    ));
5413
5414    let buffer = cx.new_model(|cx| {
5415        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "")
5416            .with_language(language, cx)
5417    });
5418    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
5419    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5420    editor
5421        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
5422        .await;
5423
5424    _ = editor.update(cx, |editor, cx| {
5425        editor.set_auto_replace_emoji_shortcode(true);
5426
5427        editor.handle_input("Hello ", cx);
5428        editor.handle_input(":wave", cx);
5429        assert_eq!(editor.text(cx), "Hello :wave".unindent());
5430
5431        editor.handle_input(":", cx);
5432        assert_eq!(editor.text(cx), "Hello 👋".unindent());
5433
5434        editor.handle_input(" :smile", cx);
5435        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
5436
5437        editor.handle_input(":", cx);
5438        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
5439
5440        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
5441        editor.handle_input(":wave", cx);
5442        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
5443
5444        editor.handle_input(":", cx);
5445        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
5446
5447        editor.handle_input(":1", cx);
5448        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
5449
5450        editor.handle_input(":", cx);
5451        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
5452
5453        // Ensure shortcode does not get replaced when it is part of a word
5454        editor.handle_input(" Test:wave", cx);
5455        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
5456
5457        editor.handle_input(":", cx);
5458        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
5459
5460        editor.set_auto_replace_emoji_shortcode(false);
5461
5462        // Ensure shortcode does not get replaced when auto replace is off
5463        editor.handle_input(" :wave", cx);
5464        assert_eq!(
5465            editor.text(cx),
5466            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
5467        );
5468
5469        editor.handle_input(":", cx);
5470        assert_eq!(
5471            editor.text(cx),
5472            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
5473        );
5474    });
5475}
5476
5477#[gpui::test]
5478async fn test_snippets(cx: &mut gpui::TestAppContext) {
5479    init_test(cx, |_| {});
5480
5481    let (text, insertion_ranges) = marked_text_ranges(
5482        indoc! {"
5483            a.ˇ b
5484            a.ˇ b
5485            a.ˇ b
5486        "},
5487        false,
5488    );
5489
5490    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
5491    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5492
5493    _ = editor.update(cx, |editor, cx| {
5494        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
5495
5496        editor
5497            .insert_snippet(&insertion_ranges, snippet, cx)
5498            .unwrap();
5499
5500        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
5501            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
5502            assert_eq!(editor.text(cx), expected_text);
5503            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
5504        }
5505
5506        assert(
5507            editor,
5508            cx,
5509            indoc! {"
5510                a.f(«one», two, «three») b
5511                a.f(«one», two, «three») b
5512                a.f(«one», two, «three») b
5513            "},
5514        );
5515
5516        // Can't move earlier than the first tab stop
5517        assert!(!editor.move_to_prev_snippet_tabstop(cx));
5518        assert(
5519            editor,
5520            cx,
5521            indoc! {"
5522                a.f(«one», two, «three») b
5523                a.f(«one», two, «three») b
5524                a.f(«one», two, «three») b
5525            "},
5526        );
5527
5528        assert!(editor.move_to_next_snippet_tabstop(cx));
5529        assert(
5530            editor,
5531            cx,
5532            indoc! {"
5533                a.f(one, «two», three) b
5534                a.f(one, «two», three) b
5535                a.f(one, «two», three) b
5536            "},
5537        );
5538
5539        editor.move_to_prev_snippet_tabstop(cx);
5540        assert(
5541            editor,
5542            cx,
5543            indoc! {"
5544                a.f(«one», two, «three») b
5545                a.f(«one», two, «three») b
5546                a.f(«one», two, «three») b
5547            "},
5548        );
5549
5550        assert!(editor.move_to_next_snippet_tabstop(cx));
5551        assert(
5552            editor,
5553            cx,
5554            indoc! {"
5555                a.f(one, «two», three) b
5556                a.f(one, «two», three) b
5557                a.f(one, «two», three) b
5558            "},
5559        );
5560        assert!(editor.move_to_next_snippet_tabstop(cx));
5561        assert(
5562            editor,
5563            cx,
5564            indoc! {"
5565                a.f(one, two, three)ˇ b
5566                a.f(one, two, three)ˇ b
5567                a.f(one, two, three)ˇ b
5568            "},
5569        );
5570
5571        // As soon as the last tab stop is reached, snippet state is gone
5572        editor.move_to_prev_snippet_tabstop(cx);
5573        assert(
5574            editor,
5575            cx,
5576            indoc! {"
5577                a.f(one, two, three)ˇ b
5578                a.f(one, two, three)ˇ b
5579                a.f(one, two, three)ˇ b
5580            "},
5581        );
5582    });
5583}
5584
5585#[gpui::test]
5586async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
5587    init_test(cx, |_| {});
5588
5589    let fs = FakeFs::new(cx.executor());
5590    fs.insert_file("/file.rs", Default::default()).await;
5591
5592    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5593
5594    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
5595    language_registry.add(rust_lang());
5596    let mut fake_servers = language_registry.register_fake_lsp_adapter(
5597        "Rust",
5598        FakeLspAdapter {
5599            capabilities: lsp::ServerCapabilities {
5600                document_formatting_provider: Some(lsp::OneOf::Left(true)),
5601                ..Default::default()
5602            },
5603            ..Default::default()
5604        },
5605    );
5606
5607    let buffer = project
5608        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
5609        .await
5610        .unwrap();
5611
5612    cx.executor().start_waiting();
5613    let fake_server = fake_servers.next().await.unwrap();
5614
5615    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
5616    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5617    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5618    assert!(cx.read(|cx| editor.is_dirty(cx)));
5619
5620    let save = editor
5621        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5622        .unwrap();
5623    fake_server
5624        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5625            assert_eq!(
5626                params.text_document.uri,
5627                lsp::Url::from_file_path("/file.rs").unwrap()
5628            );
5629            assert_eq!(params.options.tab_size, 4);
5630            Ok(Some(vec![lsp::TextEdit::new(
5631                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
5632                ", ".to_string(),
5633            )]))
5634        })
5635        .next()
5636        .await;
5637    cx.executor().start_waiting();
5638    save.await;
5639
5640    assert_eq!(
5641        editor.update(cx, |editor, cx| editor.text(cx)),
5642        "one, two\nthree\n"
5643    );
5644    assert!(!cx.read(|cx| editor.is_dirty(cx)));
5645
5646    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5647    assert!(cx.read(|cx| editor.is_dirty(cx)));
5648
5649    // Ensure we can still save even if formatting hangs.
5650    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5651        assert_eq!(
5652            params.text_document.uri,
5653            lsp::Url::from_file_path("/file.rs").unwrap()
5654        );
5655        futures::future::pending::<()>().await;
5656        unreachable!()
5657    });
5658    let save = editor
5659        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5660        .unwrap();
5661    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
5662    cx.executor().start_waiting();
5663    save.await;
5664    assert_eq!(
5665        editor.update(cx, |editor, cx| editor.text(cx)),
5666        "one\ntwo\nthree\n"
5667    );
5668    assert!(!cx.read(|cx| editor.is_dirty(cx)));
5669
5670    // For non-dirty buffer, no formatting request should be sent
5671    let save = editor
5672        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5673        .unwrap();
5674    let _pending_format_request = fake_server
5675        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
5676            panic!("Should not be invoked on non-dirty buffer");
5677        })
5678        .next();
5679    cx.executor().start_waiting();
5680    save.await;
5681
5682    // Set rust language override and assert overridden tabsize is sent to language server
5683    update_test_language_settings(cx, |settings| {
5684        settings.languages.insert(
5685            "Rust".into(),
5686            LanguageSettingsContent {
5687                tab_size: NonZeroU32::new(8),
5688                ..Default::default()
5689            },
5690        );
5691    });
5692
5693    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
5694    assert!(cx.read(|cx| editor.is_dirty(cx)));
5695    let save = editor
5696        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5697        .unwrap();
5698    fake_server
5699        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5700            assert_eq!(
5701                params.text_document.uri,
5702                lsp::Url::from_file_path("/file.rs").unwrap()
5703            );
5704            assert_eq!(params.options.tab_size, 8);
5705            Ok(Some(vec![]))
5706        })
5707        .next()
5708        .await;
5709    cx.executor().start_waiting();
5710    save.await;
5711}
5712
5713#[gpui::test]
5714async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
5715    init_test(cx, |_| {});
5716
5717    let cols = 4;
5718    let rows = 10;
5719    let sample_text_1 = sample_text(rows, cols, 'a');
5720    assert_eq!(
5721        sample_text_1,
5722        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
5723    );
5724    let sample_text_2 = sample_text(rows, cols, 'l');
5725    assert_eq!(
5726        sample_text_2,
5727        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
5728    );
5729    let sample_text_3 = sample_text(rows, cols, 'v');
5730    assert_eq!(
5731        sample_text_3,
5732        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
5733    );
5734
5735    let fs = FakeFs::new(cx.executor());
5736    fs.insert_tree(
5737        "/a",
5738        json!({
5739            "main.rs": sample_text_1,
5740            "other.rs": sample_text_2,
5741            "lib.rs": sample_text_3,
5742        }),
5743    )
5744    .await;
5745
5746    let project = Project::test(fs, ["/a".as_ref()], cx).await;
5747    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
5748    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
5749
5750    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
5751    language_registry.add(rust_lang());
5752    let mut fake_servers = language_registry.register_fake_lsp_adapter(
5753        "Rust",
5754        FakeLspAdapter {
5755            capabilities: lsp::ServerCapabilities {
5756                document_formatting_provider: Some(lsp::OneOf::Left(true)),
5757                ..Default::default()
5758            },
5759            ..Default::default()
5760        },
5761    );
5762
5763    let worktree = project.update(cx, |project, _| {
5764        let mut worktrees = project.worktrees().collect::<Vec<_>>();
5765        assert_eq!(worktrees.len(), 1);
5766        worktrees.pop().unwrap()
5767    });
5768    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
5769
5770    let buffer_1 = project
5771        .update(cx, |project, cx| {
5772            project.open_buffer((worktree_id, "main.rs"), cx)
5773        })
5774        .await
5775        .unwrap();
5776    let buffer_2 = project
5777        .update(cx, |project, cx| {
5778            project.open_buffer((worktree_id, "other.rs"), cx)
5779        })
5780        .await
5781        .unwrap();
5782    let buffer_3 = project
5783        .update(cx, |project, cx| {
5784            project.open_buffer((worktree_id, "lib.rs"), cx)
5785        })
5786        .await
5787        .unwrap();
5788
5789    let multi_buffer = cx.new_model(|cx| {
5790        let mut multi_buffer = MultiBuffer::new(0, ReadWrite);
5791        multi_buffer.push_excerpts(
5792            buffer_1.clone(),
5793            [
5794                ExcerptRange {
5795                    context: Point::new(0, 0)..Point::new(3, 0),
5796                    primary: None,
5797                },
5798                ExcerptRange {
5799                    context: Point::new(5, 0)..Point::new(7, 0),
5800                    primary: None,
5801                },
5802                ExcerptRange {
5803                    context: Point::new(9, 0)..Point::new(10, 4),
5804                    primary: None,
5805                },
5806            ],
5807            cx,
5808        );
5809        multi_buffer.push_excerpts(
5810            buffer_2.clone(),
5811            [
5812                ExcerptRange {
5813                    context: Point::new(0, 0)..Point::new(3, 0),
5814                    primary: None,
5815                },
5816                ExcerptRange {
5817                    context: Point::new(5, 0)..Point::new(7, 0),
5818                    primary: None,
5819                },
5820                ExcerptRange {
5821                    context: Point::new(9, 0)..Point::new(10, 4),
5822                    primary: None,
5823                },
5824            ],
5825            cx,
5826        );
5827        multi_buffer.push_excerpts(
5828            buffer_3.clone(),
5829            [
5830                ExcerptRange {
5831                    context: Point::new(0, 0)..Point::new(3, 0),
5832                    primary: None,
5833                },
5834                ExcerptRange {
5835                    context: Point::new(5, 0)..Point::new(7, 0),
5836                    primary: None,
5837                },
5838                ExcerptRange {
5839                    context: Point::new(9, 0)..Point::new(10, 4),
5840                    primary: None,
5841                },
5842            ],
5843            cx,
5844        );
5845        multi_buffer
5846    });
5847    let multi_buffer_editor =
5848        cx.new_view(|cx| Editor::new(EditorMode::Full, multi_buffer, Some(project.clone()), cx));
5849
5850    multi_buffer_editor.update(cx, |editor, cx| {
5851        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
5852        editor.insert("|one|two|three|", cx);
5853    });
5854    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
5855    multi_buffer_editor.update(cx, |editor, cx| {
5856        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
5857            s.select_ranges(Some(60..70))
5858        });
5859        editor.insert("|four|five|six|", cx);
5860    });
5861    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
5862
5863    // First two buffers should be edited, but not the third one.
5864    assert_eq!(
5865        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
5866        "a|one|two|three|aa\nbbbb\ncccc\n\nffff\ngggg\n\njjjj\nllll\nmmmm\nnnnn|four|five|six|\nr\n\nuuuu\nvvvv\nwwww\nxxxx\n\n{{{{\n||||\n\n\u{7f}\u{7f}\u{7f}\u{7f}",
5867    );
5868    buffer_1.update(cx, |buffer, _| {
5869        assert!(buffer.is_dirty());
5870        assert_eq!(
5871            buffer.text(),
5872            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
5873        )
5874    });
5875    buffer_2.update(cx, |buffer, _| {
5876        assert!(buffer.is_dirty());
5877        assert_eq!(
5878            buffer.text(),
5879            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
5880        )
5881    });
5882    buffer_3.update(cx, |buffer, _| {
5883        assert!(!buffer.is_dirty());
5884        assert_eq!(buffer.text(), sample_text_3,)
5885    });
5886
5887    cx.executor().start_waiting();
5888    let save = multi_buffer_editor
5889        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5890        .unwrap();
5891
5892    let fake_server = fake_servers.next().await.unwrap();
5893    fake_server
5894        .server
5895        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5896            Ok(Some(vec![lsp::TextEdit::new(
5897                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
5898                format!("[{} formatted]", params.text_document.uri),
5899            )]))
5900        })
5901        .detach();
5902    save.await;
5903
5904    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
5905    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
5906    assert_eq!(
5907        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
5908        "a|o[file:///a/main.rs formatted]bbbb\ncccc\n\nffff\ngggg\n\njjjj\n\nlll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|\nr\n\nuuuu\n\nvvvv\nwwww\nxxxx\n\n{{{{\n||||\n\n\u{7f}\u{7f}\u{7f}\u{7f}",
5909    );
5910    buffer_1.update(cx, |buffer, _| {
5911        assert!(!buffer.is_dirty());
5912        assert_eq!(
5913            buffer.text(),
5914            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
5915        )
5916    });
5917    buffer_2.update(cx, |buffer, _| {
5918        assert!(!buffer.is_dirty());
5919        assert_eq!(
5920            buffer.text(),
5921            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
5922        )
5923    });
5924    buffer_3.update(cx, |buffer, _| {
5925        assert!(!buffer.is_dirty());
5926        assert_eq!(buffer.text(), sample_text_3,)
5927    });
5928}
5929
5930#[gpui::test]
5931async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
5932    init_test(cx, |_| {});
5933
5934    let fs = FakeFs::new(cx.executor());
5935    fs.insert_file("/file.rs", Default::default()).await;
5936
5937    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5938
5939    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
5940    language_registry.add(rust_lang());
5941    let mut fake_servers = language_registry.register_fake_lsp_adapter(
5942        "Rust",
5943        FakeLspAdapter {
5944            capabilities: lsp::ServerCapabilities {
5945                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
5946                ..Default::default()
5947            },
5948            ..Default::default()
5949        },
5950    );
5951
5952    let buffer = project
5953        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
5954        .await
5955        .unwrap();
5956
5957    cx.executor().start_waiting();
5958    let fake_server = fake_servers.next().await.unwrap();
5959
5960    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
5961    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5962    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5963    assert!(cx.read(|cx| editor.is_dirty(cx)));
5964
5965    let save = editor
5966        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5967        .unwrap();
5968    fake_server
5969        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
5970            assert_eq!(
5971                params.text_document.uri,
5972                lsp::Url::from_file_path("/file.rs").unwrap()
5973            );
5974            assert_eq!(params.options.tab_size, 4);
5975            Ok(Some(vec![lsp::TextEdit::new(
5976                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
5977                ", ".to_string(),
5978            )]))
5979        })
5980        .next()
5981        .await;
5982    cx.executor().start_waiting();
5983    save.await;
5984    assert_eq!(
5985        editor.update(cx, |editor, cx| editor.text(cx)),
5986        "one, two\nthree\n"
5987    );
5988    assert!(!cx.read(|cx| editor.is_dirty(cx)));
5989
5990    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5991    assert!(cx.read(|cx| editor.is_dirty(cx)));
5992
5993    // Ensure we can still save even if formatting hangs.
5994    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
5995        move |params, _| async move {
5996            assert_eq!(
5997                params.text_document.uri,
5998                lsp::Url::from_file_path("/file.rs").unwrap()
5999            );
6000            futures::future::pending::<()>().await;
6001            unreachable!()
6002        },
6003    );
6004    let save = editor
6005        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
6006        .unwrap();
6007    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
6008    cx.executor().start_waiting();
6009    save.await;
6010    assert_eq!(
6011        editor.update(cx, |editor, cx| editor.text(cx)),
6012        "one\ntwo\nthree\n"
6013    );
6014    assert!(!cx.read(|cx| editor.is_dirty(cx)));
6015
6016    // For non-dirty buffer, no formatting request should be sent
6017    let save = editor
6018        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
6019        .unwrap();
6020    let _pending_format_request = fake_server
6021        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
6022            panic!("Should not be invoked on non-dirty buffer");
6023        })
6024        .next();
6025    cx.executor().start_waiting();
6026    save.await;
6027
6028    // Set Rust language override and assert overridden tabsize is sent to language server
6029    update_test_language_settings(cx, |settings| {
6030        settings.languages.insert(
6031            "Rust".into(),
6032            LanguageSettingsContent {
6033                tab_size: NonZeroU32::new(8),
6034                ..Default::default()
6035            },
6036        );
6037    });
6038
6039    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
6040    assert!(cx.read(|cx| editor.is_dirty(cx)));
6041    let save = editor
6042        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
6043        .unwrap();
6044    fake_server
6045        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
6046            assert_eq!(
6047                params.text_document.uri,
6048                lsp::Url::from_file_path("/file.rs").unwrap()
6049            );
6050            assert_eq!(params.options.tab_size, 8);
6051            Ok(Some(vec![]))
6052        })
6053        .next()
6054        .await;
6055    cx.executor().start_waiting();
6056    save.await;
6057}
6058
6059#[gpui::test]
6060async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
6061    init_test(cx, |settings| {
6062        settings.defaults.formatter = Some(language_settings::Formatter::LanguageServer)
6063    });
6064
6065    let fs = FakeFs::new(cx.executor());
6066    fs.insert_file("/file.rs", Default::default()).await;
6067
6068    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
6069
6070    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
6071    language_registry.add(Arc::new(Language::new(
6072        LanguageConfig {
6073            name: "Rust".into(),
6074            matcher: LanguageMatcher {
6075                path_suffixes: vec!["rs".to_string()],
6076                ..Default::default()
6077            },
6078            // Enable Prettier formatting for the same buffer, and ensure
6079            // LSP is called instead of Prettier.
6080            prettier_parser_name: Some("test_parser".to_string()),
6081            ..Default::default()
6082        },
6083        Some(tree_sitter_rust::language()),
6084    )));
6085    let mut fake_servers = language_registry.register_fake_lsp_adapter(
6086        "Rust",
6087        FakeLspAdapter {
6088            capabilities: lsp::ServerCapabilities {
6089                document_formatting_provider: Some(lsp::OneOf::Left(true)),
6090                ..Default::default()
6091            },
6092            ..Default::default()
6093        },
6094    );
6095
6096    let buffer = project
6097        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
6098        .await
6099        .unwrap();
6100
6101    cx.executor().start_waiting();
6102    let fake_server = fake_servers.next().await.unwrap();
6103
6104    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
6105    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
6106    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
6107
6108    let format = editor
6109        .update(cx, |editor, cx| {
6110            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
6111        })
6112        .unwrap();
6113    fake_server
6114        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
6115            assert_eq!(
6116                params.text_document.uri,
6117                lsp::Url::from_file_path("/file.rs").unwrap()
6118            );
6119            assert_eq!(params.options.tab_size, 4);
6120            Ok(Some(vec![lsp::TextEdit::new(
6121                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
6122                ", ".to_string(),
6123            )]))
6124        })
6125        .next()
6126        .await;
6127    cx.executor().start_waiting();
6128    format.await;
6129    assert_eq!(
6130        editor.update(cx, |editor, cx| editor.text(cx)),
6131        "one, two\nthree\n"
6132    );
6133
6134    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
6135    // Ensure we don't lock if formatting hangs.
6136    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
6137        assert_eq!(
6138            params.text_document.uri,
6139            lsp::Url::from_file_path("/file.rs").unwrap()
6140        );
6141        futures::future::pending::<()>().await;
6142        unreachable!()
6143    });
6144    let format = editor
6145        .update(cx, |editor, cx| {
6146            editor.perform_format(project, FormatTrigger::Manual, cx)
6147        })
6148        .unwrap();
6149    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
6150    cx.executor().start_waiting();
6151    format.await;
6152    assert_eq!(
6153        editor.update(cx, |editor, cx| editor.text(cx)),
6154        "one\ntwo\nthree\n"
6155    );
6156}
6157
6158#[gpui::test]
6159async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
6160    init_test(cx, |_| {});
6161
6162    let mut cx = EditorLspTestContext::new_rust(
6163        lsp::ServerCapabilities {
6164            document_formatting_provider: Some(lsp::OneOf::Left(true)),
6165            ..Default::default()
6166        },
6167        cx,
6168    )
6169    .await;
6170
6171    cx.set_state(indoc! {"
6172        one.twoˇ
6173    "});
6174
6175    // The format request takes a long time. When it completes, it inserts
6176    // a newline and an indent before the `.`
6177    cx.lsp
6178        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
6179            let executor = cx.background_executor().clone();
6180            async move {
6181                executor.timer(Duration::from_millis(100)).await;
6182                Ok(Some(vec![lsp::TextEdit {
6183                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
6184                    new_text: "\n    ".into(),
6185                }]))
6186            }
6187        });
6188
6189    // Submit a format request.
6190    let format_1 = cx
6191        .update_editor(|editor, cx| editor.format(&Format, cx))
6192        .unwrap();
6193    cx.executor().run_until_parked();
6194
6195    // Submit a second format request.
6196    let format_2 = cx
6197        .update_editor(|editor, cx| editor.format(&Format, cx))
6198        .unwrap();
6199    cx.executor().run_until_parked();
6200
6201    // Wait for both format requests to complete
6202    cx.executor().advance_clock(Duration::from_millis(200));
6203    cx.executor().start_waiting();
6204    format_1.await.unwrap();
6205    cx.executor().start_waiting();
6206    format_2.await.unwrap();
6207
6208    // The formatting edits only happens once.
6209    cx.assert_editor_state(indoc! {"
6210        one
6211            .twoˇ
6212    "});
6213}
6214
6215#[gpui::test]
6216async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
6217    init_test(cx, |settings| {
6218        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
6219    });
6220
6221    let mut cx = EditorLspTestContext::new_rust(
6222        lsp::ServerCapabilities {
6223            document_formatting_provider: Some(lsp::OneOf::Left(true)),
6224            ..Default::default()
6225        },
6226        cx,
6227    )
6228    .await;
6229
6230    // Set up a buffer white some trailing whitespace and no trailing newline.
6231    cx.set_state(
6232        &[
6233            "one ",   //
6234            "twoˇ",   //
6235            "three ", //
6236            "four",   //
6237        ]
6238        .join("\n"),
6239    );
6240
6241    // Submit a format request.
6242    let format = cx
6243        .update_editor(|editor, cx| editor.format(&Format, cx))
6244        .unwrap();
6245
6246    // Record which buffer changes have been sent to the language server
6247    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
6248    cx.lsp
6249        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
6250            let buffer_changes = buffer_changes.clone();
6251            move |params, _| {
6252                buffer_changes.lock().extend(
6253                    params
6254                        .content_changes
6255                        .into_iter()
6256                        .map(|e| (e.range.unwrap(), e.text)),
6257                );
6258            }
6259        });
6260
6261    // Handle formatting requests to the language server.
6262    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
6263        let buffer_changes = buffer_changes.clone();
6264        move |_, _| {
6265            // When formatting is requested, trailing whitespace has already been stripped,
6266            // and the trailing newline has already been added.
6267            assert_eq!(
6268                &buffer_changes.lock()[1..],
6269                &[
6270                    (
6271                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
6272                        "".into()
6273                    ),
6274                    (
6275                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
6276                        "".into()
6277                    ),
6278                    (
6279                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
6280                        "\n".into()
6281                    ),
6282                ]
6283            );
6284
6285            // Insert blank lines between each line of the buffer.
6286            async move {
6287                Ok(Some(vec![
6288                    lsp::TextEdit {
6289                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
6290                        new_text: "\n".into(),
6291                    },
6292                    lsp::TextEdit {
6293                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
6294                        new_text: "\n".into(),
6295                    },
6296                ]))
6297            }
6298        }
6299    });
6300
6301    // After formatting the buffer, the trailing whitespace is stripped,
6302    // a newline is appended, and the edits provided by the language server
6303    // have been applied.
6304    format.await.unwrap();
6305    cx.assert_editor_state(
6306        &[
6307            "one",   //
6308            "",      //
6309            "twoˇ",  //
6310            "",      //
6311            "three", //
6312            "four",  //
6313            "",      //
6314        ]
6315        .join("\n"),
6316    );
6317
6318    // Undoing the formatting undoes the trailing whitespace removal, the
6319    // trailing newline, and the LSP edits.
6320    cx.update_buffer(|buffer, cx| buffer.undo(cx));
6321    cx.assert_editor_state(
6322        &[
6323            "one ",   //
6324            "twoˇ",   //
6325            "three ", //
6326            "four",   //
6327        ]
6328        .join("\n"),
6329    );
6330}
6331
6332#[gpui::test]
6333async fn test_completion(cx: &mut gpui::TestAppContext) {
6334    init_test(cx, |_| {});
6335
6336    let mut cx = EditorLspTestContext::new_rust(
6337        lsp::ServerCapabilities {
6338            completion_provider: Some(lsp::CompletionOptions {
6339                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
6340                resolve_provider: Some(true),
6341                ..Default::default()
6342            }),
6343            ..Default::default()
6344        },
6345        cx,
6346    )
6347    .await;
6348
6349    cx.set_state(indoc! {"
6350        oneˇ
6351        two
6352        three
6353    "});
6354    cx.simulate_keystroke(".");
6355    handle_completion_request(
6356        &mut cx,
6357        indoc! {"
6358            one.|<>
6359            two
6360            three
6361        "},
6362        vec!["first_completion", "second_completion"],
6363    )
6364    .await;
6365    cx.condition(|editor, _| editor.context_menu_visible())
6366        .await;
6367    let apply_additional_edits = cx.update_editor(|editor, cx| {
6368        editor.context_menu_next(&Default::default(), cx);
6369        editor
6370            .confirm_completion(&ConfirmCompletion::default(), cx)
6371            .unwrap()
6372    });
6373    cx.assert_editor_state(indoc! {"
6374        one.second_completionˇ
6375        two
6376        three
6377    "});
6378
6379    handle_resolve_completion_request(
6380        &mut cx,
6381        Some(vec![
6382            (
6383                //This overlaps with the primary completion edit which is
6384                //misbehavior from the LSP spec, test that we filter it out
6385                indoc! {"
6386                    one.second_ˇcompletion
6387                    two
6388                    threeˇ
6389                "},
6390                "overlapping additional edit",
6391            ),
6392            (
6393                indoc! {"
6394                    one.second_completion
6395                    two
6396                    threeˇ
6397                "},
6398                "\nadditional edit",
6399            ),
6400        ]),
6401    )
6402    .await;
6403    apply_additional_edits.await.unwrap();
6404    cx.assert_editor_state(indoc! {"
6405        one.second_completionˇ
6406        two
6407        three
6408        additional edit
6409    "});
6410
6411    cx.set_state(indoc! {"
6412        one.second_completion
6413        twoˇ
6414        threeˇ
6415        additional edit
6416    "});
6417    cx.simulate_keystroke(" ");
6418    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
6419    cx.simulate_keystroke("s");
6420    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
6421
6422    cx.assert_editor_state(indoc! {"
6423        one.second_completion
6424        two sˇ
6425        three sˇ
6426        additional edit
6427    "});
6428    handle_completion_request(
6429        &mut cx,
6430        indoc! {"
6431            one.second_completion
6432            two s
6433            three <s|>
6434            additional edit
6435        "},
6436        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
6437    )
6438    .await;
6439    cx.condition(|editor, _| editor.context_menu_visible())
6440        .await;
6441
6442    cx.simulate_keystroke("i");
6443
6444    handle_completion_request(
6445        &mut cx,
6446        indoc! {"
6447            one.second_completion
6448            two si
6449            three <si|>
6450            additional edit
6451        "},
6452        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
6453    )
6454    .await;
6455    cx.condition(|editor, _| editor.context_menu_visible())
6456        .await;
6457
6458    let apply_additional_edits = cx.update_editor(|editor, cx| {
6459        editor
6460            .confirm_completion(&ConfirmCompletion::default(), cx)
6461            .unwrap()
6462    });
6463    cx.assert_editor_state(indoc! {"
6464        one.second_completion
6465        two sixth_completionˇ
6466        three sixth_completionˇ
6467        additional edit
6468    "});
6469
6470    handle_resolve_completion_request(&mut cx, None).await;
6471    apply_additional_edits.await.unwrap();
6472
6473    _ = cx.update(|cx| {
6474        cx.update_global::<SettingsStore, _>(|settings, cx| {
6475            settings.update_user_settings::<EditorSettings>(cx, |settings| {
6476                settings.show_completions_on_input = Some(false);
6477            });
6478        })
6479    });
6480    cx.set_state("editorˇ");
6481    cx.simulate_keystroke(".");
6482    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
6483    cx.simulate_keystroke("c");
6484    cx.simulate_keystroke("l");
6485    cx.simulate_keystroke("o");
6486    cx.assert_editor_state("editor.cloˇ");
6487    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
6488    cx.update_editor(|editor, cx| {
6489        editor.show_completions(&ShowCompletions, cx);
6490    });
6491    handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
6492    cx.condition(|editor, _| editor.context_menu_visible())
6493        .await;
6494    let apply_additional_edits = cx.update_editor(|editor, cx| {
6495        editor
6496            .confirm_completion(&ConfirmCompletion::default(), cx)
6497            .unwrap()
6498    });
6499    cx.assert_editor_state("editor.closeˇ");
6500    handle_resolve_completion_request(&mut cx, None).await;
6501    apply_additional_edits.await.unwrap();
6502}
6503
6504#[gpui::test]
6505async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
6506    init_test(cx, |_| {});
6507    let mut cx = EditorTestContext::new(cx).await;
6508    let language = Arc::new(Language::new(
6509        LanguageConfig {
6510            line_comments: vec!["// ".into()],
6511            ..Default::default()
6512        },
6513        Some(tree_sitter_rust::language()),
6514    ));
6515    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
6516
6517    // If multiple selections intersect a line, the line is only toggled once.
6518    cx.set_state(indoc! {"
6519        fn a() {
6520            «//b();
6521            ˇ»// «c();
6522            //ˇ»  d();
6523        }
6524    "});
6525
6526    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
6527
6528    cx.assert_editor_state(indoc! {"
6529        fn a() {
6530            «b();
6531            c();
6532            ˇ» d();
6533        }
6534    "});
6535
6536    // The comment prefix is inserted at the same column for every line in a
6537    // selection.
6538    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
6539
6540    cx.assert_editor_state(indoc! {"
6541        fn a() {
6542            // «b();
6543            // c();
6544            ˇ»//  d();
6545        }
6546    "});
6547
6548    // If a selection ends at the beginning of a line, that line is not toggled.
6549    cx.set_selections_state(indoc! {"
6550        fn a() {
6551            // b();
6552            «// c();
6553        ˇ»    //  d();
6554        }
6555    "});
6556
6557    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
6558
6559    cx.assert_editor_state(indoc! {"
6560        fn a() {
6561            // b();
6562            «c();
6563        ˇ»    //  d();
6564        }
6565    "});
6566
6567    // If a selection span a single line and is empty, the line is toggled.
6568    cx.set_state(indoc! {"
6569        fn a() {
6570            a();
6571            b();
6572        ˇ
6573        }
6574    "});
6575
6576    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
6577
6578    cx.assert_editor_state(indoc! {"
6579        fn a() {
6580            a();
6581            b();
6582        //•ˇ
6583        }
6584    "});
6585
6586    // If a selection span multiple lines, empty lines are not toggled.
6587    cx.set_state(indoc! {"
6588        fn a() {
6589            «a();
6590
6591            c();ˇ»
6592        }
6593    "});
6594
6595    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
6596
6597    cx.assert_editor_state(indoc! {"
6598        fn a() {
6599            // «a();
6600
6601            // c();ˇ»
6602        }
6603    "});
6604}
6605
6606#[gpui::test]
6607async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
6608    init_test(cx, |_| {});
6609
6610    let language = Arc::new(Language::new(
6611        LanguageConfig {
6612            line_comments: vec!["// ".into()],
6613            ..Default::default()
6614        },
6615        Some(tree_sitter_rust::language()),
6616    ));
6617
6618    let mut cx = EditorTestContext::new(cx).await;
6619
6620    cx.language_registry().add(language.clone());
6621    cx.update_buffer(|buffer, cx| {
6622        buffer.set_language(Some(language), cx);
6623    });
6624
6625    let toggle_comments = &ToggleComments {
6626        advance_downwards: true,
6627    };
6628
6629    // Single cursor on one line -> advance
6630    // Cursor moves horizontally 3 characters as well on non-blank line
6631    cx.set_state(indoc!(
6632        "fn a() {
6633             ˇdog();
6634             cat();
6635        }"
6636    ));
6637    cx.update_editor(|editor, cx| {
6638        editor.toggle_comments(toggle_comments, cx);
6639    });
6640    cx.assert_editor_state(indoc!(
6641        "fn a() {
6642             // dog();
6643             catˇ();
6644        }"
6645    ));
6646
6647    // Single selection on one line -> don't advance
6648    cx.set_state(indoc!(
6649        "fn a() {
6650             «dog()ˇ»;
6651             cat();
6652        }"
6653    ));
6654    cx.update_editor(|editor, cx| {
6655        editor.toggle_comments(toggle_comments, cx);
6656    });
6657    cx.assert_editor_state(indoc!(
6658        "fn a() {
6659             // «dog()ˇ»;
6660             cat();
6661        }"
6662    ));
6663
6664    // Multiple cursors on one line -> advance
6665    cx.set_state(indoc!(
6666        "fn a() {
6667             ˇdˇog();
6668             cat();
6669        }"
6670    ));
6671    cx.update_editor(|editor, cx| {
6672        editor.toggle_comments(toggle_comments, cx);
6673    });
6674    cx.assert_editor_state(indoc!(
6675        "fn a() {
6676             // dog();
6677             catˇ(ˇ);
6678        }"
6679    ));
6680
6681    // Multiple cursors on one line, with selection -> don't advance
6682    cx.set_state(indoc!(
6683        "fn a() {
6684             ˇdˇog«()ˇ»;
6685             cat();
6686        }"
6687    ));
6688    cx.update_editor(|editor, cx| {
6689        editor.toggle_comments(toggle_comments, cx);
6690    });
6691    cx.assert_editor_state(indoc!(
6692        "fn a() {
6693             // ˇdˇog«()ˇ»;
6694             cat();
6695        }"
6696    ));
6697
6698    // Single cursor on one line -> advance
6699    // Cursor moves to column 0 on blank line
6700    cx.set_state(indoc!(
6701        "fn a() {
6702             ˇdog();
6703
6704             cat();
6705        }"
6706    ));
6707    cx.update_editor(|editor, cx| {
6708        editor.toggle_comments(toggle_comments, cx);
6709    });
6710    cx.assert_editor_state(indoc!(
6711        "fn a() {
6712             // dog();
6713        ˇ
6714             cat();
6715        }"
6716    ));
6717
6718    // Single cursor on one line -> advance
6719    // Cursor starts and ends at column 0
6720    cx.set_state(indoc!(
6721        "fn a() {
6722         ˇ    dog();
6723             cat();
6724        }"
6725    ));
6726    cx.update_editor(|editor, cx| {
6727        editor.toggle_comments(toggle_comments, cx);
6728    });
6729    cx.assert_editor_state(indoc!(
6730        "fn a() {
6731             // dog();
6732         ˇ    cat();
6733        }"
6734    ));
6735}
6736
6737#[gpui::test]
6738async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
6739    init_test(cx, |_| {});
6740
6741    let mut cx = EditorTestContext::new(cx).await;
6742
6743    let html_language = Arc::new(
6744        Language::new(
6745            LanguageConfig {
6746                name: "HTML".into(),
6747                block_comment: Some(("<!-- ".into(), " -->".into())),
6748                ..Default::default()
6749            },
6750            Some(tree_sitter_html::language()),
6751        )
6752        .with_injection_query(
6753            r#"
6754            (script_element
6755                (raw_text) @content
6756                (#set! "language" "javascript"))
6757            "#,
6758        )
6759        .unwrap(),
6760    );
6761
6762    let javascript_language = Arc::new(Language::new(
6763        LanguageConfig {
6764            name: "JavaScript".into(),
6765            line_comments: vec!["// ".into()],
6766            ..Default::default()
6767        },
6768        Some(tree_sitter_typescript::language_tsx()),
6769    ));
6770
6771    cx.language_registry().add(html_language.clone());
6772    cx.language_registry().add(javascript_language.clone());
6773    cx.update_buffer(|buffer, cx| {
6774        buffer.set_language(Some(html_language), cx);
6775    });
6776
6777    // Toggle comments for empty selections
6778    cx.set_state(
6779        &r#"
6780            <p>A</p>ˇ
6781            <p>B</p>ˇ
6782            <p>C</p>ˇ
6783        "#
6784        .unindent(),
6785    );
6786    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6787    cx.assert_editor_state(
6788        &r#"
6789            <!-- <p>A</p>ˇ -->
6790            <!-- <p>B</p>ˇ -->
6791            <!-- <p>C</p>ˇ -->
6792        "#
6793        .unindent(),
6794    );
6795    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6796    cx.assert_editor_state(
6797        &r#"
6798            <p>A</p>ˇ
6799            <p>B</p>ˇ
6800            <p>C</p>ˇ
6801        "#
6802        .unindent(),
6803    );
6804
6805    // Toggle comments for mixture of empty and non-empty selections, where
6806    // multiple selections occupy a given line.
6807    cx.set_state(
6808        &r#"
6809            <p>A«</p>
6810            <p>ˇ»B</p>ˇ
6811            <p>C«</p>
6812            <p>ˇ»D</p>ˇ
6813        "#
6814        .unindent(),
6815    );
6816
6817    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6818    cx.assert_editor_state(
6819        &r#"
6820            <!-- <p>A«</p>
6821            <p>ˇ»B</p>ˇ -->
6822            <!-- <p>C«</p>
6823            <p>ˇ»D</p>ˇ -->
6824        "#
6825        .unindent(),
6826    );
6827    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6828    cx.assert_editor_state(
6829        &r#"
6830            <p>A«</p>
6831            <p>ˇ»B</p>ˇ
6832            <p>C«</p>
6833            <p>ˇ»D</p>ˇ
6834        "#
6835        .unindent(),
6836    );
6837
6838    // Toggle comments when different languages are active for different
6839    // selections.
6840    cx.set_state(
6841        &r#"
6842            ˇ<script>
6843                ˇvar x = new Y();
6844            ˇ</script>
6845        "#
6846        .unindent(),
6847    );
6848    cx.executor().run_until_parked();
6849    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6850    cx.assert_editor_state(
6851        &r#"
6852            <!-- ˇ<script> -->
6853                // ˇvar x = new Y();
6854            <!-- ˇ</script> -->
6855        "#
6856        .unindent(),
6857    );
6858}
6859
6860#[gpui::test]
6861fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
6862    init_test(cx, |_| {});
6863
6864    let buffer = cx.new_model(|cx| {
6865        Buffer::new(
6866            0,
6867            BufferId::new(cx.entity_id().as_u64()).unwrap(),
6868            sample_text(3, 4, 'a'),
6869        )
6870    });
6871    let multibuffer = cx.new_model(|cx| {
6872        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
6873        multibuffer.push_excerpts(
6874            buffer.clone(),
6875            [
6876                ExcerptRange {
6877                    context: Point::new(0, 0)..Point::new(0, 4),
6878                    primary: None,
6879                },
6880                ExcerptRange {
6881                    context: Point::new(1, 0)..Point::new(1, 4),
6882                    primary: None,
6883                },
6884            ],
6885            cx,
6886        );
6887        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
6888        multibuffer
6889    });
6890
6891    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
6892    _ = view.update(cx, |view, cx| {
6893        assert_eq!(view.text(cx), "aaaa\nbbbb");
6894        view.change_selections(None, cx, |s| {
6895            s.select_ranges([
6896                Point::new(0, 0)..Point::new(0, 0),
6897                Point::new(1, 0)..Point::new(1, 0),
6898            ])
6899        });
6900
6901        view.handle_input("X", cx);
6902        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
6903        assert_eq!(
6904            view.selections.ranges(cx),
6905            [
6906                Point::new(0, 1)..Point::new(0, 1),
6907                Point::new(1, 1)..Point::new(1, 1),
6908            ]
6909        );
6910
6911        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
6912        view.change_selections(None, cx, |s| {
6913            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
6914        });
6915        view.backspace(&Default::default(), cx);
6916        assert_eq!(view.text(cx), "Xa\nbbb");
6917        assert_eq!(
6918            view.selections.ranges(cx),
6919            [Point::new(1, 0)..Point::new(1, 0)]
6920        );
6921
6922        view.change_selections(None, cx, |s| {
6923            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
6924        });
6925        view.backspace(&Default::default(), cx);
6926        assert_eq!(view.text(cx), "X\nbb");
6927        assert_eq!(
6928            view.selections.ranges(cx),
6929            [Point::new(0, 1)..Point::new(0, 1)]
6930        );
6931    });
6932}
6933
6934#[gpui::test]
6935fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
6936    init_test(cx, |_| {});
6937
6938    let markers = vec![('[', ']').into(), ('(', ')').into()];
6939    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
6940        indoc! {"
6941            [aaaa
6942            (bbbb]
6943            cccc)",
6944        },
6945        markers.clone(),
6946    );
6947    let excerpt_ranges = markers.into_iter().map(|marker| {
6948        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
6949        ExcerptRange {
6950            context,
6951            primary: None,
6952        }
6953    });
6954    let buffer = cx.new_model(|cx| {
6955        Buffer::new(
6956            0,
6957            BufferId::new(cx.entity_id().as_u64()).unwrap(),
6958            initial_text,
6959        )
6960    });
6961    let multibuffer = cx.new_model(|cx| {
6962        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
6963        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
6964        multibuffer
6965    });
6966
6967    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
6968    _ = view.update(cx, |view, cx| {
6969        let (expected_text, selection_ranges) = marked_text_ranges(
6970            indoc! {"
6971                aaaa
6972                bˇbbb
6973                bˇbbˇb
6974                cccc"
6975            },
6976            true,
6977        );
6978        assert_eq!(view.text(cx), expected_text);
6979        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
6980
6981        view.handle_input("X", cx);
6982
6983        let (expected_text, expected_selections) = marked_text_ranges(
6984            indoc! {"
6985                aaaa
6986                bXˇbbXb
6987                bXˇbbXˇb
6988                cccc"
6989            },
6990            false,
6991        );
6992        assert_eq!(view.text(cx), expected_text);
6993        assert_eq!(view.selections.ranges(cx), expected_selections);
6994
6995        view.newline(&Newline, cx);
6996        let (expected_text, expected_selections) = marked_text_ranges(
6997            indoc! {"
6998                aaaa
6999                bX
7000                ˇbbX
7001                b
7002                bX
7003                ˇbbX
7004                ˇb
7005                cccc"
7006            },
7007            false,
7008        );
7009        assert_eq!(view.text(cx), expected_text);
7010        assert_eq!(view.selections.ranges(cx), expected_selections);
7011    });
7012}
7013
7014#[gpui::test]
7015fn test_refresh_selections(cx: &mut TestAppContext) {
7016    init_test(cx, |_| {});
7017
7018    let buffer = cx.new_model(|cx| {
7019        Buffer::new(
7020            0,
7021            BufferId::new(cx.entity_id().as_u64()).unwrap(),
7022            sample_text(3, 4, 'a'),
7023        )
7024    });
7025    let mut excerpt1_id = None;
7026    let multibuffer = cx.new_model(|cx| {
7027        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
7028        excerpt1_id = multibuffer
7029            .push_excerpts(
7030                buffer.clone(),
7031                [
7032                    ExcerptRange {
7033                        context: Point::new(0, 0)..Point::new(1, 4),
7034                        primary: None,
7035                    },
7036                    ExcerptRange {
7037                        context: Point::new(1, 0)..Point::new(2, 4),
7038                        primary: None,
7039                    },
7040                ],
7041                cx,
7042            )
7043            .into_iter()
7044            .next();
7045        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
7046        multibuffer
7047    });
7048
7049    let editor = cx.add_window(|cx| {
7050        let mut editor = build_editor(multibuffer.clone(), cx);
7051        let snapshot = editor.snapshot(cx);
7052        editor.change_selections(None, cx, |s| {
7053            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
7054        });
7055        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
7056        assert_eq!(
7057            editor.selections.ranges(cx),
7058            [
7059                Point::new(1, 3)..Point::new(1, 3),
7060                Point::new(2, 1)..Point::new(2, 1),
7061            ]
7062        );
7063        editor
7064    });
7065
7066    // Refreshing selections is a no-op when excerpts haven't changed.
7067    _ = editor.update(cx, |editor, cx| {
7068        editor.change_selections(None, cx, |s| s.refresh());
7069        assert_eq!(
7070            editor.selections.ranges(cx),
7071            [
7072                Point::new(1, 3)..Point::new(1, 3),
7073                Point::new(2, 1)..Point::new(2, 1),
7074            ]
7075        );
7076    });
7077
7078    _ = multibuffer.update(cx, |multibuffer, cx| {
7079        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
7080    });
7081    _ = editor.update(cx, |editor, cx| {
7082        // Removing an excerpt causes the first selection to become degenerate.
7083        assert_eq!(
7084            editor.selections.ranges(cx),
7085            [
7086                Point::new(0, 0)..Point::new(0, 0),
7087                Point::new(0, 1)..Point::new(0, 1)
7088            ]
7089        );
7090
7091        // Refreshing selections will relocate the first selection to the original buffer
7092        // location.
7093        editor.change_selections(None, cx, |s| s.refresh());
7094        assert_eq!(
7095            editor.selections.ranges(cx),
7096            [
7097                Point::new(0, 1)..Point::new(0, 1),
7098                Point::new(0, 3)..Point::new(0, 3)
7099            ]
7100        );
7101        assert!(editor.selections.pending_anchor().is_some());
7102    });
7103}
7104
7105#[gpui::test]
7106fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
7107    init_test(cx, |_| {});
7108
7109    let buffer = cx.new_model(|cx| {
7110        Buffer::new(
7111            0,
7112            BufferId::new(cx.entity_id().as_u64()).unwrap(),
7113            sample_text(3, 4, 'a'),
7114        )
7115    });
7116    let mut excerpt1_id = None;
7117    let multibuffer = cx.new_model(|cx| {
7118        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
7119        excerpt1_id = multibuffer
7120            .push_excerpts(
7121                buffer.clone(),
7122                [
7123                    ExcerptRange {
7124                        context: Point::new(0, 0)..Point::new(1, 4),
7125                        primary: None,
7126                    },
7127                    ExcerptRange {
7128                        context: Point::new(1, 0)..Point::new(2, 4),
7129                        primary: None,
7130                    },
7131                ],
7132                cx,
7133            )
7134            .into_iter()
7135            .next();
7136        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
7137        multibuffer
7138    });
7139
7140    let editor = cx.add_window(|cx| {
7141        let mut editor = build_editor(multibuffer.clone(), cx);
7142        let snapshot = editor.snapshot(cx);
7143        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
7144        assert_eq!(
7145            editor.selections.ranges(cx),
7146            [Point::new(1, 3)..Point::new(1, 3)]
7147        );
7148        editor
7149    });
7150
7151    _ = multibuffer.update(cx, |multibuffer, cx| {
7152        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
7153    });
7154    _ = editor.update(cx, |editor, cx| {
7155        assert_eq!(
7156            editor.selections.ranges(cx),
7157            [Point::new(0, 0)..Point::new(0, 0)]
7158        );
7159
7160        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
7161        editor.change_selections(None, cx, |s| s.refresh());
7162        assert_eq!(
7163            editor.selections.ranges(cx),
7164            [Point::new(0, 3)..Point::new(0, 3)]
7165        );
7166        assert!(editor.selections.pending_anchor().is_some());
7167    });
7168}
7169
7170#[gpui::test]
7171async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
7172    init_test(cx, |_| {});
7173
7174    let language = Arc::new(
7175        Language::new(
7176            LanguageConfig {
7177                brackets: BracketPairConfig {
7178                    pairs: vec![
7179                        BracketPair {
7180                            start: "{".to_string(),
7181                            end: "}".to_string(),
7182                            close: true,
7183                            newline: true,
7184                        },
7185                        BracketPair {
7186                            start: "/* ".to_string(),
7187                            end: " */".to_string(),
7188                            close: true,
7189                            newline: true,
7190                        },
7191                    ],
7192                    ..Default::default()
7193                },
7194                ..Default::default()
7195            },
7196            Some(tree_sitter_rust::language()),
7197        )
7198        .with_indents_query("")
7199        .unwrap(),
7200    );
7201
7202    let text = concat!(
7203        "{   }\n",     //
7204        "  x\n",       //
7205        "  /*   */\n", //
7206        "x\n",         //
7207        "{{} }\n",     //
7208    );
7209
7210    let buffer = cx.new_model(|cx| {
7211        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
7212            .with_language(language, cx)
7213    });
7214    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
7215    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
7216    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
7217        .await;
7218
7219    _ = view.update(cx, |view, cx| {
7220        view.change_selections(None, cx, |s| {
7221            s.select_display_ranges([
7222                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
7223                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
7224                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
7225            ])
7226        });
7227        view.newline(&Newline, cx);
7228
7229        assert_eq!(
7230            view.buffer().read(cx).read(cx).text(),
7231            concat!(
7232                "{ \n",    // Suppress rustfmt
7233                "\n",      //
7234                "}\n",     //
7235                "  x\n",   //
7236                "  /* \n", //
7237                "  \n",    //
7238                "  */\n",  //
7239                "x\n",     //
7240                "{{} \n",  //
7241                "}\n",     //
7242            )
7243        );
7244    });
7245}
7246
7247#[gpui::test]
7248fn test_highlighted_ranges(cx: &mut TestAppContext) {
7249    init_test(cx, |_| {});
7250
7251    let editor = cx.add_window(|cx| {
7252        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
7253        build_editor(buffer.clone(), cx)
7254    });
7255
7256    _ = editor.update(cx, |editor, cx| {
7257        struct Type1;
7258        struct Type2;
7259
7260        let buffer = editor.buffer.read(cx).snapshot(cx);
7261
7262        let anchor_range =
7263            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
7264
7265        editor.highlight_background::<Type1>(
7266            vec![
7267                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
7268                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
7269                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
7270                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
7271            ],
7272            |_| Hsla::red(),
7273            cx,
7274        );
7275        editor.highlight_background::<Type2>(
7276            vec![
7277                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
7278                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
7279                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
7280                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
7281            ],
7282            |_| Hsla::green(),
7283            cx,
7284        );
7285
7286        let snapshot = editor.snapshot(cx);
7287        let mut highlighted_ranges = editor.background_highlights_in_range(
7288            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
7289            &snapshot,
7290            cx.theme().colors(),
7291        );
7292        // Enforce a consistent ordering based on color without relying on the ordering of the
7293        // highlight's `TypeId` which is non-executor.
7294        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
7295        assert_eq!(
7296            highlighted_ranges,
7297            &[
7298                (
7299                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
7300                    Hsla::red(),
7301                ),
7302                (
7303                    DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
7304                    Hsla::red(),
7305                ),
7306                (
7307                    DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
7308                    Hsla::green(),
7309                ),
7310                (
7311                    DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
7312                    Hsla::green(),
7313                ),
7314            ]
7315        );
7316        assert_eq!(
7317            editor.background_highlights_in_range(
7318                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
7319                &snapshot,
7320                cx.theme().colors(),
7321            ),
7322            &[(
7323                DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
7324                Hsla::red(),
7325            )]
7326        );
7327    });
7328}
7329
7330#[gpui::test]
7331async fn test_following(cx: &mut gpui::TestAppContext) {
7332    init_test(cx, |_| {});
7333
7334    let fs = FakeFs::new(cx.executor());
7335    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
7336
7337    let buffer = project.update(cx, |project, cx| {
7338        let buffer = project
7339            .create_buffer(&sample_text(16, 8, 'a'), None, cx)
7340            .unwrap();
7341        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
7342    });
7343    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
7344    let follower = cx.update(|cx| {
7345        cx.open_window(
7346            WindowOptions {
7347                bounds: Some(Bounds::from_corners(
7348                    gpui::Point::new(0.into(), 0.into()),
7349                    gpui::Point::new(10.into(), 80.into()),
7350                )),
7351                ..Default::default()
7352            },
7353            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
7354        )
7355    });
7356
7357    let is_still_following = Rc::new(RefCell::new(true));
7358    let follower_edit_event_count = Rc::new(RefCell::new(0));
7359    let pending_update = Rc::new(RefCell::new(None));
7360    _ = follower.update(cx, {
7361        let update = pending_update.clone();
7362        let is_still_following = is_still_following.clone();
7363        let follower_edit_event_count = follower_edit_event_count.clone();
7364        |_, cx| {
7365            cx.subscribe(
7366                &leader.root_view(cx).unwrap(),
7367                move |_, leader, event, cx| {
7368                    leader
7369                        .read(cx)
7370                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
7371                },
7372            )
7373            .detach();
7374
7375            cx.subscribe(
7376                &follower.root_view(cx).unwrap(),
7377                move |_, _, event: &EditorEvent, _cx| {
7378                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
7379                        *is_still_following.borrow_mut() = false;
7380                    }
7381
7382                    if let EditorEvent::BufferEdited = event {
7383                        *follower_edit_event_count.borrow_mut() += 1;
7384                    }
7385                },
7386            )
7387            .detach();
7388        }
7389    });
7390
7391    // Update the selections only
7392    _ = leader.update(cx, |leader, cx| {
7393        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
7394    });
7395    follower
7396        .update(cx, |follower, cx| {
7397            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
7398        })
7399        .unwrap()
7400        .await
7401        .unwrap();
7402    _ = follower.update(cx, |follower, cx| {
7403        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
7404    });
7405    assert_eq!(*is_still_following.borrow(), true);
7406    assert_eq!(*follower_edit_event_count.borrow(), 0);
7407
7408    // Update the scroll position only
7409    _ = leader.update(cx, |leader, cx| {
7410        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
7411    });
7412    follower
7413        .update(cx, |follower, cx| {
7414            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
7415        })
7416        .unwrap()
7417        .await
7418        .unwrap();
7419    assert_eq!(
7420        follower
7421            .update(cx, |follower, cx| follower.scroll_position(cx))
7422            .unwrap(),
7423        gpui::Point::new(1.5, 3.5)
7424    );
7425    assert_eq!(*is_still_following.borrow(), true);
7426    assert_eq!(*follower_edit_event_count.borrow(), 0);
7427
7428    // Update the selections and scroll position. The follower's scroll position is updated
7429    // via autoscroll, not via the leader's exact scroll position.
7430    _ = leader.update(cx, |leader, cx| {
7431        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
7432        leader.request_autoscroll(Autoscroll::newest(), cx);
7433        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
7434    });
7435    follower
7436        .update(cx, |follower, cx| {
7437            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
7438        })
7439        .unwrap()
7440        .await
7441        .unwrap();
7442    _ = follower.update(cx, |follower, cx| {
7443        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
7444        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
7445    });
7446    assert_eq!(*is_still_following.borrow(), true);
7447
7448    // Creating a pending selection that precedes another selection
7449    _ = leader.update(cx, |leader, cx| {
7450        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
7451        leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
7452    });
7453    follower
7454        .update(cx, |follower, cx| {
7455            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
7456        })
7457        .unwrap()
7458        .await
7459        .unwrap();
7460    _ = follower.update(cx, |follower, cx| {
7461        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
7462    });
7463    assert_eq!(*is_still_following.borrow(), true);
7464
7465    // Extend the pending selection so that it surrounds another selection
7466    _ = leader.update(cx, |leader, cx| {
7467        leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
7468    });
7469    follower
7470        .update(cx, |follower, cx| {
7471            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
7472        })
7473        .unwrap()
7474        .await
7475        .unwrap();
7476    _ = follower.update(cx, |follower, cx| {
7477        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
7478    });
7479
7480    // Scrolling locally breaks the follow
7481    _ = follower.update(cx, |follower, cx| {
7482        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
7483        follower.set_scroll_anchor(
7484            ScrollAnchor {
7485                anchor: top_anchor,
7486                offset: gpui::Point::new(0.0, 0.5),
7487            },
7488            cx,
7489        );
7490    });
7491    assert_eq!(*is_still_following.borrow(), false);
7492}
7493
7494#[gpui::test]
7495async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
7496    init_test(cx, |_| {});
7497
7498    let fs = FakeFs::new(cx.executor());
7499    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
7500    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
7501    let pane = workspace
7502        .update(cx, |workspace, _| workspace.active_pane().clone())
7503        .unwrap();
7504
7505    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
7506
7507    let leader = pane.update(cx, |_, cx| {
7508        let multibuffer = cx.new_model(|_| MultiBuffer::new(0, ReadWrite));
7509        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
7510    });
7511
7512    // Start following the editor when it has no excerpts.
7513    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
7514    let follower_1 = cx
7515        .update_window(*workspace.deref(), |_, cx| {
7516            Editor::from_state_proto(
7517                pane.clone(),
7518                workspace.root_view(cx).unwrap(),
7519                ViewId {
7520                    creator: Default::default(),
7521                    id: 0,
7522                },
7523                &mut state_message,
7524                cx,
7525            )
7526        })
7527        .unwrap()
7528        .unwrap()
7529        .await
7530        .unwrap();
7531
7532    let update_message = Rc::new(RefCell::new(None));
7533    follower_1.update(cx, {
7534        let update = update_message.clone();
7535        |_, cx| {
7536            cx.subscribe(&leader, move |_, leader, event, cx| {
7537                leader
7538                    .read(cx)
7539                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
7540            })
7541            .detach();
7542        }
7543    });
7544
7545    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
7546        (
7547            project
7548                .create_buffer("abc\ndef\nghi\njkl\n", None, cx)
7549                .unwrap(),
7550            project
7551                .create_buffer("mno\npqr\nstu\nvwx\n", None, cx)
7552                .unwrap(),
7553        )
7554    });
7555
7556    // Insert some excerpts.
7557    _ = leader.update(cx, |leader, cx| {
7558        leader.buffer.update(cx, |multibuffer, cx| {
7559            let excerpt_ids = multibuffer.push_excerpts(
7560                buffer_1.clone(),
7561                [
7562                    ExcerptRange {
7563                        context: 1..6,
7564                        primary: None,
7565                    },
7566                    ExcerptRange {
7567                        context: 12..15,
7568                        primary: None,
7569                    },
7570                    ExcerptRange {
7571                        context: 0..3,
7572                        primary: None,
7573                    },
7574                ],
7575                cx,
7576            );
7577            multibuffer.insert_excerpts_after(
7578                excerpt_ids[0],
7579                buffer_2.clone(),
7580                [
7581                    ExcerptRange {
7582                        context: 8..12,
7583                        primary: None,
7584                    },
7585                    ExcerptRange {
7586                        context: 0..6,
7587                        primary: None,
7588                    },
7589                ],
7590                cx,
7591            );
7592        });
7593    });
7594
7595    // Apply the update of adding the excerpts.
7596    follower_1
7597        .update(cx, |follower, cx| {
7598            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
7599        })
7600        .await
7601        .unwrap();
7602    assert_eq!(
7603        follower_1.update(cx, |editor, cx| editor.text(cx)),
7604        leader.update(cx, |editor, cx| editor.text(cx))
7605    );
7606    update_message.borrow_mut().take();
7607
7608    // Start following separately after it already has excerpts.
7609    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
7610    let follower_2 = cx
7611        .update_window(*workspace.deref(), |_, cx| {
7612            Editor::from_state_proto(
7613                pane.clone(),
7614                workspace.root_view(cx).unwrap().clone(),
7615                ViewId {
7616                    creator: Default::default(),
7617                    id: 0,
7618                },
7619                &mut state_message,
7620                cx,
7621            )
7622        })
7623        .unwrap()
7624        .unwrap()
7625        .await
7626        .unwrap();
7627    assert_eq!(
7628        follower_2.update(cx, |editor, cx| editor.text(cx)),
7629        leader.update(cx, |editor, cx| editor.text(cx))
7630    );
7631
7632    // Remove some excerpts.
7633    _ = leader.update(cx, |leader, cx| {
7634        leader.buffer.update(cx, |multibuffer, cx| {
7635            let excerpt_ids = multibuffer.excerpt_ids();
7636            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
7637            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
7638        });
7639    });
7640
7641    // Apply the update of removing the excerpts.
7642    follower_1
7643        .update(cx, |follower, cx| {
7644            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
7645        })
7646        .await
7647        .unwrap();
7648    follower_2
7649        .update(cx, |follower, cx| {
7650            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
7651        })
7652        .await
7653        .unwrap();
7654    update_message.borrow_mut().take();
7655    assert_eq!(
7656        follower_1.update(cx, |editor, cx| editor.text(cx)),
7657        leader.update(cx, |editor, cx| editor.text(cx))
7658    );
7659}
7660
7661#[gpui::test]
7662async fn go_to_prev_overlapping_diagnostic(
7663    executor: BackgroundExecutor,
7664    cx: &mut gpui::TestAppContext,
7665) {
7666    init_test(cx, |_| {});
7667
7668    let mut cx = EditorTestContext::new(cx).await;
7669    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
7670
7671    cx.set_state(indoc! {"
7672        ˇfn func(abc def: i32) -> u32 {
7673        }
7674    "});
7675
7676    _ = cx.update(|cx| {
7677        _ = project.update(cx, |project, cx| {
7678            project
7679                .update_diagnostics(
7680                    LanguageServerId(0),
7681                    lsp::PublishDiagnosticsParams {
7682                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
7683                        version: None,
7684                        diagnostics: vec![
7685                            lsp::Diagnostic {
7686                                range: lsp::Range::new(
7687                                    lsp::Position::new(0, 11),
7688                                    lsp::Position::new(0, 12),
7689                                ),
7690                                severity: Some(lsp::DiagnosticSeverity::ERROR),
7691                                ..Default::default()
7692                            },
7693                            lsp::Diagnostic {
7694                                range: lsp::Range::new(
7695                                    lsp::Position::new(0, 12),
7696                                    lsp::Position::new(0, 15),
7697                                ),
7698                                severity: Some(lsp::DiagnosticSeverity::ERROR),
7699                                ..Default::default()
7700                            },
7701                            lsp::Diagnostic {
7702                                range: lsp::Range::new(
7703                                    lsp::Position::new(0, 25),
7704                                    lsp::Position::new(0, 28),
7705                                ),
7706                                severity: Some(lsp::DiagnosticSeverity::ERROR),
7707                                ..Default::default()
7708                            },
7709                        ],
7710                    },
7711                    &[],
7712                    cx,
7713                )
7714                .unwrap()
7715        });
7716    });
7717
7718    executor.run_until_parked();
7719
7720    cx.update_editor(|editor, cx| {
7721        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
7722    });
7723
7724    cx.assert_editor_state(indoc! {"
7725        fn func(abc def: i32) -> ˇu32 {
7726        }
7727    "});
7728
7729    cx.update_editor(|editor, cx| {
7730        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
7731    });
7732
7733    cx.assert_editor_state(indoc! {"
7734        fn func(abc ˇdef: i32) -> u32 {
7735        }
7736    "});
7737
7738    cx.update_editor(|editor, cx| {
7739        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
7740    });
7741
7742    cx.assert_editor_state(indoc! {"
7743        fn func(abcˇ def: i32) -> u32 {
7744        }
7745    "});
7746
7747    cx.update_editor(|editor, cx| {
7748        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
7749    });
7750
7751    cx.assert_editor_state(indoc! {"
7752        fn func(abc def: i32) -> ˇu32 {
7753        }
7754    "});
7755}
7756
7757#[gpui::test]
7758async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
7759    init_test(cx, |_| {});
7760
7761    let mut cx = EditorTestContext::new(cx).await;
7762
7763    let diff_base = r#"
7764        use some::mod;
7765
7766        const A: u32 = 42;
7767
7768        fn main() {
7769            println!("hello");
7770
7771            println!("world");
7772        }
7773        "#
7774    .unindent();
7775
7776    // Edits are modified, removed, modified, added
7777    cx.set_state(
7778        &r#"
7779        use some::modified;
7780
7781        ˇ
7782        fn main() {
7783            println!("hello there");
7784
7785            println!("around the");
7786            println!("world");
7787        }
7788        "#
7789        .unindent(),
7790    );
7791
7792    cx.set_diff_base(Some(&diff_base));
7793    executor.run_until_parked();
7794
7795    cx.update_editor(|editor, cx| {
7796        //Wrap around the bottom of the buffer
7797        for _ in 0..3 {
7798            editor.go_to_hunk(&GoToHunk, cx);
7799        }
7800    });
7801
7802    cx.assert_editor_state(
7803        &r#"
7804        ˇuse some::modified;
7805
7806
7807        fn main() {
7808            println!("hello there");
7809
7810            println!("around the");
7811            println!("world");
7812        }
7813        "#
7814        .unindent(),
7815    );
7816
7817    cx.update_editor(|editor, cx| {
7818        //Wrap around the top of the buffer
7819        for _ in 0..2 {
7820            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
7821        }
7822    });
7823
7824    cx.assert_editor_state(
7825        &r#"
7826        use some::modified;
7827
7828
7829        fn main() {
7830        ˇ    println!("hello there");
7831
7832            println!("around the");
7833            println!("world");
7834        }
7835        "#
7836        .unindent(),
7837    );
7838
7839    cx.update_editor(|editor, cx| {
7840        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
7841    });
7842
7843    cx.assert_editor_state(
7844        &r#"
7845        use some::modified;
7846
7847        ˇ
7848        fn main() {
7849            println!("hello there");
7850
7851            println!("around the");
7852            println!("world");
7853        }
7854        "#
7855        .unindent(),
7856    );
7857
7858    cx.update_editor(|editor, cx| {
7859        for _ in 0..3 {
7860            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
7861        }
7862    });
7863
7864    cx.assert_editor_state(
7865        &r#"
7866        use some::modified;
7867
7868
7869        fn main() {
7870        ˇ    println!("hello there");
7871
7872            println!("around the");
7873            println!("world");
7874        }
7875        "#
7876        .unindent(),
7877    );
7878
7879    cx.update_editor(|editor, cx| {
7880        editor.fold(&Fold, cx);
7881
7882        //Make sure that the fold only gets one hunk
7883        for _ in 0..4 {
7884            editor.go_to_hunk(&GoToHunk, cx);
7885        }
7886    });
7887
7888    cx.assert_editor_state(
7889        &r#"
7890        ˇuse some::modified;
7891
7892
7893        fn main() {
7894            println!("hello there");
7895
7896            println!("around the");
7897            println!("world");
7898        }
7899        "#
7900        .unindent(),
7901    );
7902}
7903
7904#[test]
7905fn test_split_words() {
7906    fn split(text: &str) -> Vec<&str> {
7907        split_words(text).collect()
7908    }
7909
7910    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
7911    assert_eq!(split("hello_world"), &["hello_", "world"]);
7912    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
7913    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
7914    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
7915    assert_eq!(split("helloworld"), &["helloworld"]);
7916
7917    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
7918}
7919
7920#[gpui::test]
7921async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
7922    init_test(cx, |_| {});
7923
7924    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
7925    let mut assert = |before, after| {
7926        let _state_context = cx.set_state(before);
7927        cx.update_editor(|editor, cx| {
7928            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
7929        });
7930        cx.assert_editor_state(after);
7931    };
7932
7933    // Outside bracket jumps to outside of matching bracket
7934    assert("console.logˇ(var);", "console.log(var)ˇ;");
7935    assert("console.log(var)ˇ;", "console.logˇ(var);");
7936
7937    // Inside bracket jumps to inside of matching bracket
7938    assert("console.log(ˇvar);", "console.log(varˇ);");
7939    assert("console.log(varˇ);", "console.log(ˇvar);");
7940
7941    // When outside a bracket and inside, favor jumping to the inside bracket
7942    assert(
7943        "console.log('foo', [1, 2, 3]ˇ);",
7944        "console.log(ˇ'foo', [1, 2, 3]);",
7945    );
7946    assert(
7947        "console.log(ˇ'foo', [1, 2, 3]);",
7948        "console.log('foo', [1, 2, 3]ˇ);",
7949    );
7950
7951    // Bias forward if two options are equally likely
7952    assert(
7953        "let result = curried_fun()ˇ();",
7954        "let result = curried_fun()()ˇ;",
7955    );
7956
7957    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
7958    assert(
7959        indoc! {"
7960            function test() {
7961                console.log('test')ˇ
7962            }"},
7963        indoc! {"
7964            function test() {
7965                console.logˇ('test')
7966            }"},
7967    );
7968}
7969
7970#[gpui::test]
7971async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
7972    init_test(cx, |_| {});
7973
7974    let fs = FakeFs::new(cx.executor());
7975    fs.insert_tree(
7976        "/a",
7977        json!({
7978            "main.rs": "fn main() { let a = 5; }",
7979            "other.rs": "// Test file",
7980        }),
7981    )
7982    .await;
7983    let project = Project::test(fs, ["/a".as_ref()], cx).await;
7984
7985    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
7986    language_registry.add(Arc::new(Language::new(
7987        LanguageConfig {
7988            name: "Rust".into(),
7989            matcher: LanguageMatcher {
7990                path_suffixes: vec!["rs".to_string()],
7991                ..Default::default()
7992            },
7993            brackets: BracketPairConfig {
7994                pairs: vec![BracketPair {
7995                    start: "{".to_string(),
7996                    end: "}".to_string(),
7997                    close: true,
7998                    newline: true,
7999                }],
8000                disabled_scopes_by_bracket_ix: Vec::new(),
8001            },
8002            ..Default::default()
8003        },
8004        Some(tree_sitter_rust::language()),
8005    )));
8006    let mut fake_servers = language_registry.register_fake_lsp_adapter(
8007        "Rust",
8008        FakeLspAdapter {
8009            capabilities: lsp::ServerCapabilities {
8010                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
8011                    first_trigger_character: "{".to_string(),
8012                    more_trigger_character: None,
8013                }),
8014                ..Default::default()
8015            },
8016            ..Default::default()
8017        },
8018    );
8019
8020    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
8021
8022    let cx = &mut VisualTestContext::from_window(*workspace, cx);
8023
8024    let worktree_id = workspace
8025        .update(cx, |workspace, cx| {
8026            workspace.project().update(cx, |project, cx| {
8027                project.worktrees().next().unwrap().read(cx).id()
8028            })
8029        })
8030        .unwrap();
8031
8032    let buffer = project
8033        .update(cx, |project, cx| {
8034            project.open_local_buffer("/a/main.rs", cx)
8035        })
8036        .await
8037        .unwrap();
8038    cx.executor().run_until_parked();
8039    cx.executor().start_waiting();
8040    let fake_server = fake_servers.next().await.unwrap();
8041    let editor_handle = workspace
8042        .update(cx, |workspace, cx| {
8043            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
8044        })
8045        .unwrap()
8046        .await
8047        .unwrap()
8048        .downcast::<Editor>()
8049        .unwrap();
8050
8051    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
8052        assert_eq!(
8053            params.text_document_position.text_document.uri,
8054            lsp::Url::from_file_path("/a/main.rs").unwrap(),
8055        );
8056        assert_eq!(
8057            params.text_document_position.position,
8058            lsp::Position::new(0, 21),
8059        );
8060
8061        Ok(Some(vec![lsp::TextEdit {
8062            new_text: "]".to_string(),
8063            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
8064        }]))
8065    });
8066
8067    editor_handle.update(cx, |editor, cx| {
8068        editor.focus(cx);
8069        editor.change_selections(None, cx, |s| {
8070            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
8071        });
8072        editor.handle_input("{", cx);
8073    });
8074
8075    cx.executor().run_until_parked();
8076
8077    _ = buffer.update(cx, |buffer, _| {
8078        assert_eq!(
8079            buffer.text(),
8080            "fn main() { let a = {5}; }",
8081            "No extra braces from on type formatting should appear in the buffer"
8082        )
8083    });
8084}
8085
8086#[gpui::test]
8087async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
8088    init_test(cx, |_| {});
8089
8090    let fs = FakeFs::new(cx.executor());
8091    fs.insert_tree(
8092        "/a",
8093        json!({
8094            "main.rs": "fn main() { let a = 5; }",
8095            "other.rs": "// Test file",
8096        }),
8097    )
8098    .await;
8099
8100    let project = Project::test(fs, ["/a".as_ref()], cx).await;
8101
8102    let server_restarts = Arc::new(AtomicUsize::new(0));
8103    let closure_restarts = Arc::clone(&server_restarts);
8104    let language_server_name = "test language server";
8105    let language_name: Arc<str> = "Rust".into();
8106
8107    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
8108    language_registry.add(Arc::new(Language::new(
8109        LanguageConfig {
8110            name: Arc::clone(&language_name),
8111            matcher: LanguageMatcher {
8112                path_suffixes: vec!["rs".to_string()],
8113                ..Default::default()
8114            },
8115            ..Default::default()
8116        },
8117        Some(tree_sitter_rust::language()),
8118    )));
8119    let mut fake_servers = language_registry.register_fake_lsp_adapter(
8120        "Rust",
8121        FakeLspAdapter {
8122            name: language_server_name,
8123            initialization_options: Some(json!({
8124                "testOptionValue": true
8125            })),
8126            initializer: Some(Box::new(move |fake_server| {
8127                let task_restarts = Arc::clone(&closure_restarts);
8128                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
8129                    task_restarts.fetch_add(1, atomic::Ordering::Release);
8130                    futures::future::ready(Ok(()))
8131                });
8132            })),
8133            ..Default::default()
8134        },
8135    );
8136
8137    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
8138    let _buffer = project
8139        .update(cx, |project, cx| {
8140            project.open_local_buffer("/a/main.rs", cx)
8141        })
8142        .await
8143        .unwrap();
8144    let _fake_server = fake_servers.next().await.unwrap();
8145    update_test_language_settings(cx, |language_settings| {
8146        language_settings.languages.insert(
8147            Arc::clone(&language_name),
8148            LanguageSettingsContent {
8149                tab_size: NonZeroU32::new(8),
8150                ..Default::default()
8151            },
8152        );
8153    });
8154    cx.executor().run_until_parked();
8155    assert_eq!(
8156        server_restarts.load(atomic::Ordering::Acquire),
8157        0,
8158        "Should not restart LSP server on an unrelated change"
8159    );
8160
8161    update_test_project_settings(cx, |project_settings| {
8162        project_settings.lsp.insert(
8163            "Some other server name".into(),
8164            LspSettings {
8165                binary: None,
8166                settings: None,
8167                initialization_options: Some(json!({
8168                    "some other init value": false
8169                })),
8170            },
8171        );
8172    });
8173    cx.executor().run_until_parked();
8174    assert_eq!(
8175        server_restarts.load(atomic::Ordering::Acquire),
8176        0,
8177        "Should not restart LSP server on an unrelated LSP settings change"
8178    );
8179
8180    update_test_project_settings(cx, |project_settings| {
8181        project_settings.lsp.insert(
8182            language_server_name.into(),
8183            LspSettings {
8184                binary: None,
8185                settings: None,
8186                initialization_options: Some(json!({
8187                    "anotherInitValue": false
8188                })),
8189            },
8190        );
8191    });
8192    cx.executor().run_until_parked();
8193    assert_eq!(
8194        server_restarts.load(atomic::Ordering::Acquire),
8195        1,
8196        "Should restart LSP server on a related LSP settings change"
8197    );
8198
8199    update_test_project_settings(cx, |project_settings| {
8200        project_settings.lsp.insert(
8201            language_server_name.into(),
8202            LspSettings {
8203                binary: None,
8204                settings: None,
8205                initialization_options: Some(json!({
8206                    "anotherInitValue": false
8207                })),
8208            },
8209        );
8210    });
8211    cx.executor().run_until_parked();
8212    assert_eq!(
8213        server_restarts.load(atomic::Ordering::Acquire),
8214        1,
8215        "Should not restart LSP server on a related LSP settings change that is the same"
8216    );
8217
8218    update_test_project_settings(cx, |project_settings| {
8219        project_settings.lsp.insert(
8220            language_server_name.into(),
8221            LspSettings {
8222                binary: None,
8223                settings: None,
8224                initialization_options: None,
8225            },
8226        );
8227    });
8228    cx.executor().run_until_parked();
8229    assert_eq!(
8230        server_restarts.load(atomic::Ordering::Acquire),
8231        2,
8232        "Should restart LSP server on another related LSP settings change"
8233    );
8234}
8235
8236#[gpui::test]
8237async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
8238    init_test(cx, |_| {});
8239
8240    let mut cx = EditorLspTestContext::new_rust(
8241        lsp::ServerCapabilities {
8242            completion_provider: Some(lsp::CompletionOptions {
8243                trigger_characters: Some(vec![".".to_string()]),
8244                resolve_provider: Some(true),
8245                ..Default::default()
8246            }),
8247            ..Default::default()
8248        },
8249        cx,
8250    )
8251    .await;
8252
8253    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
8254    cx.simulate_keystroke(".");
8255    let completion_item = lsp::CompletionItem {
8256        label: "some".into(),
8257        kind: Some(lsp::CompletionItemKind::SNIPPET),
8258        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
8259        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
8260            kind: lsp::MarkupKind::Markdown,
8261            value: "```rust\nSome(2)\n```".to_string(),
8262        })),
8263        deprecated: Some(false),
8264        sort_text: Some("fffffff2".to_string()),
8265        filter_text: Some("some".to_string()),
8266        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
8267        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
8268            range: lsp::Range {
8269                start: lsp::Position {
8270                    line: 0,
8271                    character: 22,
8272                },
8273                end: lsp::Position {
8274                    line: 0,
8275                    character: 22,
8276                },
8277            },
8278            new_text: "Some(2)".to_string(),
8279        })),
8280        additional_text_edits: Some(vec![lsp::TextEdit {
8281            range: lsp::Range {
8282                start: lsp::Position {
8283                    line: 0,
8284                    character: 20,
8285                },
8286                end: lsp::Position {
8287                    line: 0,
8288                    character: 22,
8289                },
8290            },
8291            new_text: "".to_string(),
8292        }]),
8293        ..Default::default()
8294    };
8295
8296    let closure_completion_item = completion_item.clone();
8297    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
8298        let task_completion_item = closure_completion_item.clone();
8299        async move {
8300            Ok(Some(lsp::CompletionResponse::Array(vec![
8301                task_completion_item,
8302            ])))
8303        }
8304    });
8305
8306    request.next().await;
8307
8308    cx.condition(|editor, _| editor.context_menu_visible())
8309        .await;
8310    let apply_additional_edits = cx.update_editor(|editor, cx| {
8311        editor
8312            .confirm_completion(&ConfirmCompletion::default(), cx)
8313            .unwrap()
8314    });
8315    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
8316
8317    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
8318        let task_completion_item = completion_item.clone();
8319        async move { Ok(task_completion_item) }
8320    })
8321    .next()
8322    .await
8323    .unwrap();
8324    apply_additional_edits.await.unwrap();
8325    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
8326}
8327
8328#[gpui::test]
8329async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
8330    init_test(cx, |_| {});
8331
8332    let mut cx = EditorLspTestContext::new(
8333        Language::new(
8334            LanguageConfig {
8335                matcher: LanguageMatcher {
8336                    path_suffixes: vec!["jsx".into()],
8337                    ..Default::default()
8338                },
8339                overrides: [(
8340                    "element".into(),
8341                    LanguageConfigOverride {
8342                        word_characters: Override::Set(['-'].into_iter().collect()),
8343                        ..Default::default()
8344                    },
8345                )]
8346                .into_iter()
8347                .collect(),
8348                ..Default::default()
8349            },
8350            Some(tree_sitter_typescript::language_tsx()),
8351        )
8352        .with_override_query("(jsx_self_closing_element) @element")
8353        .unwrap(),
8354        lsp::ServerCapabilities {
8355            completion_provider: Some(lsp::CompletionOptions {
8356                trigger_characters: Some(vec![":".to_string()]),
8357                ..Default::default()
8358            }),
8359            ..Default::default()
8360        },
8361        cx,
8362    )
8363    .await;
8364
8365    cx.lsp
8366        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
8367            Ok(Some(lsp::CompletionResponse::Array(vec![
8368                lsp::CompletionItem {
8369                    label: "bg-blue".into(),
8370                    ..Default::default()
8371                },
8372                lsp::CompletionItem {
8373                    label: "bg-red".into(),
8374                    ..Default::default()
8375                },
8376                lsp::CompletionItem {
8377                    label: "bg-yellow".into(),
8378                    ..Default::default()
8379                },
8380            ])))
8381        });
8382
8383    cx.set_state(r#"<p class="bgˇ" />"#);
8384
8385    // Trigger completion when typing a dash, because the dash is an extra
8386    // word character in the 'element' scope, which contains the cursor.
8387    cx.simulate_keystroke("-");
8388    cx.executor().run_until_parked();
8389    cx.update_editor(|editor, _| {
8390        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
8391            assert_eq!(
8392                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
8393                &["bg-red", "bg-blue", "bg-yellow"]
8394            );
8395        } else {
8396            panic!("expected completion menu to be open");
8397        }
8398    });
8399
8400    cx.simulate_keystroke("l");
8401    cx.executor().run_until_parked();
8402    cx.update_editor(|editor, _| {
8403        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
8404            assert_eq!(
8405                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
8406                &["bg-blue", "bg-yellow"]
8407            );
8408        } else {
8409            panic!("expected completion menu to be open");
8410        }
8411    });
8412
8413    // When filtering completions, consider the character after the '-' to
8414    // be the start of a subword.
8415    cx.set_state(r#"<p class="yelˇ" />"#);
8416    cx.simulate_keystroke("l");
8417    cx.executor().run_until_parked();
8418    cx.update_editor(|editor, _| {
8419        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
8420            assert_eq!(
8421                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
8422                &["bg-yellow"]
8423            );
8424        } else {
8425            panic!("expected completion menu to be open");
8426        }
8427    });
8428}
8429
8430#[gpui::test]
8431async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
8432    init_test(cx, |settings| {
8433        settings.defaults.formatter = Some(language_settings::Formatter::Prettier)
8434    });
8435
8436    let fs = FakeFs::new(cx.executor());
8437    fs.insert_file("/file.rs", Default::default()).await;
8438
8439    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
8440    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
8441
8442    language_registry.add(Arc::new(Language::new(
8443        LanguageConfig {
8444            name: "Rust".into(),
8445            matcher: LanguageMatcher {
8446                path_suffixes: vec!["rs".to_string()],
8447                ..Default::default()
8448            },
8449            prettier_parser_name: Some("test_parser".to_string()),
8450            ..Default::default()
8451        },
8452        Some(tree_sitter_rust::language()),
8453    )));
8454
8455    let test_plugin = "test_plugin";
8456    let _ = language_registry.register_fake_lsp_adapter(
8457        "Rust",
8458        FakeLspAdapter {
8459            prettier_plugins: vec![test_plugin],
8460            ..Default::default()
8461        },
8462    );
8463
8464    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
8465    let buffer = project
8466        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
8467        .await
8468        .unwrap();
8469
8470    let buffer_text = "one\ntwo\nthree\n";
8471    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
8472    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
8473    _ = editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
8474
8475    editor
8476        .update(cx, |editor, cx| {
8477            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
8478        })
8479        .unwrap()
8480        .await;
8481    assert_eq!(
8482        editor.update(cx, |editor, cx| editor.text(cx)),
8483        buffer_text.to_string() + prettier_format_suffix,
8484        "Test prettier formatting was not applied to the original buffer text",
8485    );
8486
8487    update_test_language_settings(cx, |settings| {
8488        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
8489    });
8490    let format = editor.update(cx, |editor, cx| {
8491        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
8492    });
8493    format.await.unwrap();
8494    assert_eq!(
8495        editor.update(cx, |editor, cx| editor.text(cx)),
8496        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
8497        "Autoformatting (via test prettier) was not applied to the original buffer text",
8498    );
8499}
8500
8501#[gpui::test]
8502async fn test_find_all_references(cx: &mut gpui::TestAppContext) {
8503    init_test(cx, |_| {});
8504
8505    let mut cx = EditorLspTestContext::new_rust(
8506        lsp::ServerCapabilities {
8507            document_formatting_provider: Some(lsp::OneOf::Left(true)),
8508            ..Default::default()
8509        },
8510        cx,
8511    )
8512    .await;
8513
8514    cx.set_state(indoc! {"
8515        fn foo(«paramˇ»: i64) {
8516            println!(param);
8517        }
8518    "});
8519
8520    cx.lsp
8521        .handle_request::<lsp::request::References, _, _>(move |_, _| async move {
8522            Ok(Some(vec![
8523                lsp::Location {
8524                    uri: lsp::Url::from_file_path("/root/dir/file.rs").unwrap(),
8525                    range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 12)),
8526                },
8527                lsp::Location {
8528                    uri: lsp::Url::from_file_path("/root/dir/file.rs").unwrap(),
8529                    range: lsp::Range::new(lsp::Position::new(1, 13), lsp::Position::new(1, 18)),
8530                },
8531            ]))
8532        });
8533
8534    let references = cx
8535        .update_editor(|editor, cx| editor.find_all_references(&FindAllReferences, cx))
8536        .unwrap();
8537
8538    cx.executor().run_until_parked();
8539
8540    cx.executor().start_waiting();
8541    references.await.unwrap();
8542
8543    cx.assert_editor_state(indoc! {"
8544        fn foo(param: i64) {
8545            println!(«paramˇ»);
8546        }
8547    "});
8548
8549    let references = cx
8550        .update_editor(|editor, cx| editor.find_all_references(&FindAllReferences, cx))
8551        .unwrap();
8552
8553    cx.executor().run_until_parked();
8554
8555    cx.executor().start_waiting();
8556    references.await.unwrap();
8557
8558    cx.assert_editor_state(indoc! {"
8559        fn foo(«paramˇ»: i64) {
8560            println!(param);
8561        }
8562    "});
8563
8564    cx.set_state(indoc! {"
8565        fn foo(param: i64) {
8566            let a = param;
8567            let aˇ = param;
8568            let a = param;
8569            println!(param);
8570        }
8571    "});
8572
8573    cx.lsp
8574        .handle_request::<lsp::request::References, _, _>(move |_, _| async move {
8575            Ok(Some(vec![lsp::Location {
8576                uri: lsp::Url::from_file_path("/root/dir/file.rs").unwrap(),
8577                range: lsp::Range::new(lsp::Position::new(2, 8), lsp::Position::new(2, 9)),
8578            }]))
8579        });
8580
8581    let references = cx
8582        .update_editor(|editor, cx| editor.find_all_references(&FindAllReferences, cx))
8583        .unwrap();
8584
8585    cx.executor().run_until_parked();
8586
8587    cx.executor().start_waiting();
8588    references.await.unwrap();
8589
8590    cx.assert_editor_state(indoc! {"
8591        fn foo(param: i64) {
8592            let a = param;
8593            let «aˇ» = param;
8594            let a = param;
8595            println!(param);
8596        }
8597    "});
8598}
8599
8600#[gpui::test]
8601async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
8602    init_test(cx, |_| {});
8603    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
8604    let base_text = indoc! {r#"struct Row;
8605struct Row1;
8606struct Row2;
8607
8608struct Row4;
8609struct Row5;
8610struct Row6;
8611
8612struct Row8;
8613struct Row9;
8614struct Row10;"#};
8615
8616    // When addition hunks are not adjacent to carets, no hunk revert is performed
8617    assert_hunk_revert(
8618        indoc! {r#"struct Row;
8619                   struct Row1;
8620                   struct Row1.1;
8621                   struct Row1.2;
8622                   struct Row2;ˇ
8623
8624                   struct Row4;
8625                   struct Row5;
8626                   struct Row6;
8627
8628                   struct Row8;
8629                   ˇstruct Row9;
8630                   struct Row9.1;
8631                   struct Row9.2;
8632                   struct Row9.3;
8633                   struct Row10;"#},
8634        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
8635        indoc! {r#"struct Row;
8636                   struct Row1;
8637                   struct Row1.1;
8638                   struct Row1.2;
8639                   struct Row2;ˇ
8640
8641                   struct Row4;
8642                   struct Row5;
8643                   struct Row6;
8644
8645                   struct Row8;
8646                   ˇstruct Row9;
8647                   struct Row9.1;
8648                   struct Row9.2;
8649                   struct Row9.3;
8650                   struct Row10;"#},
8651        base_text,
8652        &mut cx,
8653    );
8654    // Same for selections
8655    assert_hunk_revert(
8656        indoc! {r#"struct Row;
8657                   struct Row1;
8658                   struct Row2;
8659                   struct Row2.1;
8660                   struct Row2.2;
8661                   «ˇ
8662                   struct Row4;
8663                   struct» Row5;
8664                   «struct Row6;
8665                   ˇ»
8666                   struct Row9.1;
8667                   struct Row9.2;
8668                   struct Row9.3;
8669                   struct Row8;
8670                   struct Row9;
8671                   struct Row10;"#},
8672        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
8673        indoc! {r#"struct Row;
8674                   struct Row1;
8675                   struct Row2;
8676                   struct Row2.1;
8677                   struct Row2.2;
8678                   «ˇ
8679                   struct Row4;
8680                   struct» Row5;
8681                   «struct Row6;
8682                   ˇ»
8683                   struct Row9.1;
8684                   struct Row9.2;
8685                   struct Row9.3;
8686                   struct Row8;
8687                   struct Row9;
8688                   struct Row10;"#},
8689        base_text,
8690        &mut cx,
8691    );
8692
8693    // When carets and selections intersect the addition hunks, those are reverted.
8694    // Adjacent carets got merged.
8695    assert_hunk_revert(
8696        indoc! {r#"struct Row;
8697                   ˇ// something on the top
8698                   struct Row1;
8699                   struct Row2;
8700                   struct Roˇw3.1;
8701                   struct Row2.2;
8702                   struct Row2.3;ˇ
8703
8704                   struct Row4;
8705                   struct ˇRow5.1;
8706                   struct Row5.2;
8707                   struct «Rowˇ»5.3;
8708                   struct Row5;
8709                   struct Row6;
8710                   ˇ
8711                   struct Row9.1;
8712                   struct «Rowˇ»9.2;
8713                   struct «ˇRow»9.3;
8714                   struct Row8;
8715                   struct Row9;
8716                   «ˇ// something on bottom»
8717                   struct Row10;"#},
8718        vec![
8719            DiffHunkStatus::Added,
8720            DiffHunkStatus::Added,
8721            DiffHunkStatus::Added,
8722            DiffHunkStatus::Added,
8723            DiffHunkStatus::Added,
8724        ],
8725        indoc! {r#"struct Row;
8726                   ˇstruct Row1;
8727                   struct Row2;
8728                   ˇ
8729                   struct Row4;
8730                   ˇstruct Row5;
8731                   struct Row6;
8732                   ˇ
8733                   ˇstruct Row8;
8734                   struct Row9;
8735                   ˇstruct Row10;"#},
8736        base_text,
8737        &mut cx,
8738    );
8739}
8740
8741#[gpui::test]
8742async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
8743    init_test(cx, |_| {});
8744    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
8745    let base_text = indoc! {r#"struct Row;
8746struct Row1;
8747struct Row2;
8748
8749struct Row4;
8750struct Row5;
8751struct Row6;
8752
8753struct Row8;
8754struct Row9;
8755struct Row10;"#};
8756
8757    // Modification hunks behave the same as the addition ones.
8758    assert_hunk_revert(
8759        indoc! {r#"struct Row;
8760                   struct Row1;
8761                   struct Row33;
8762                   ˇ
8763                   struct Row4;
8764                   struct Row5;
8765                   struct Row6;
8766                   ˇ
8767                   struct Row99;
8768                   struct Row9;
8769                   struct Row10;"#},
8770        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
8771        indoc! {r#"struct Row;
8772                   struct Row1;
8773                   struct Row33;
8774                   ˇ
8775                   struct Row4;
8776                   struct Row5;
8777                   struct Row6;
8778                   ˇ
8779                   struct Row99;
8780                   struct Row9;
8781                   struct Row10;"#},
8782        base_text,
8783        &mut cx,
8784    );
8785    assert_hunk_revert(
8786        indoc! {r#"struct Row;
8787                   struct Row1;
8788                   struct Row33;
8789                   «ˇ
8790                   struct Row4;
8791                   struct» Row5;
8792                   «struct Row6;
8793                   ˇ»
8794                   struct Row99;
8795                   struct Row9;
8796                   struct Row10;"#},
8797        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
8798        indoc! {r#"struct Row;
8799                   struct Row1;
8800                   struct Row33;
8801                   «ˇ
8802                   struct Row4;
8803                   struct» Row5;
8804                   «struct Row6;
8805                   ˇ»
8806                   struct Row99;
8807                   struct Row9;
8808                   struct Row10;"#},
8809        base_text,
8810        &mut cx,
8811    );
8812
8813    assert_hunk_revert(
8814        indoc! {r#"ˇstruct Row1.1;
8815                   struct Row1;
8816                   «ˇstr»uct Row22;
8817
8818                   struct ˇRow44;
8819                   struct Row5;
8820                   struct «Rˇ»ow66;ˇ
8821
8822                   «struˇ»ct Row88;
8823                   struct Row9;
8824                   struct Row1011;ˇ"#},
8825        vec![
8826            DiffHunkStatus::Modified,
8827            DiffHunkStatus::Modified,
8828            DiffHunkStatus::Modified,
8829            DiffHunkStatus::Modified,
8830            DiffHunkStatus::Modified,
8831            DiffHunkStatus::Modified,
8832        ],
8833        indoc! {r#"struct Row;
8834                   ˇstruct Row1;
8835                   struct Row2;
8836                   ˇ
8837                   struct Row4;
8838                   ˇstruct Row5;
8839                   struct Row6;
8840                   ˇ
8841                   struct Row8;
8842                   ˇstruct Row9;
8843                   struct Row10;ˇ"#},
8844        base_text,
8845        &mut cx,
8846    );
8847}
8848
8849#[gpui::test]
8850async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
8851    init_test(cx, |_| {});
8852    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
8853    let base_text = indoc! {r#"struct Row;
8854struct Row1;
8855struct Row2;
8856
8857struct Row4;
8858struct Row5;
8859struct Row6;
8860
8861struct Row8;
8862struct Row9;
8863struct Row10;"#};
8864
8865    // Deletion hunks trigger with carets on ajacent rows, so carets and selections have to stay farther to avoid the revert
8866    assert_hunk_revert(
8867        indoc! {r#"struct Row;
8868                   struct Row2;
8869
8870                   ˇstruct Row4;
8871                   struct Row5;
8872                   struct Row6;
8873                   ˇ
8874                   struct Row8;
8875                   struct Row10;"#},
8876        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
8877        indoc! {r#"struct Row;
8878                   struct Row2;
8879
8880                   ˇstruct Row4;
8881                   struct Row5;
8882                   struct Row6;
8883                   ˇ
8884                   struct Row8;
8885                   struct Row10;"#},
8886        base_text,
8887        &mut cx,
8888    );
8889    assert_hunk_revert(
8890        indoc! {r#"struct Row;
8891                   struct Row2;
8892
8893                   «ˇstruct Row4;
8894                   struct» Row5;
8895                   «struct Row6;
8896                   ˇ»
8897                   struct Row8;
8898                   struct Row10;"#},
8899        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
8900        indoc! {r#"struct Row;
8901                   struct Row2;
8902
8903                   «ˇstruct Row4;
8904                   struct» Row5;
8905                   «struct Row6;
8906                   ˇ»
8907                   struct Row8;
8908                   struct Row10;"#},
8909        base_text,
8910        &mut cx,
8911    );
8912
8913    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
8914    assert_hunk_revert(
8915        indoc! {r#"struct Row;
8916                   ˇstruct Row2;
8917
8918                   struct Row4;
8919                   struct Row5;
8920                   struct Row6;
8921
8922                   struct Row8;ˇ
8923                   struct Row10;"#},
8924        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
8925        indoc! {r#"struct Row;
8926                   struct Row1;
8927                   ˇstruct Row2;
8928
8929                   struct Row4;
8930                   struct Row5;
8931                   struct Row6;
8932
8933                   struct Row8;ˇ
8934                   struct Row9;
8935                   struct Row10;"#},
8936        base_text,
8937        &mut cx,
8938    );
8939    assert_hunk_revert(
8940        indoc! {r#"struct Row;
8941                   struct Row2«ˇ;
8942                   struct Row4;
8943                   struct» Row5;
8944                   «struct Row6;
8945
8946                   struct Row8;ˇ»
8947                   struct Row10;"#},
8948        vec![
8949            DiffHunkStatus::Removed,
8950            DiffHunkStatus::Removed,
8951            DiffHunkStatus::Removed,
8952        ],
8953        indoc! {r#"struct Row;
8954                   struct Row1;
8955                   struct Row2«ˇ;
8956
8957                   struct Row4;
8958                   struct» Row5;
8959                   «struct Row6;
8960
8961                   struct Row8;ˇ»
8962                   struct Row9;
8963                   struct Row10;"#},
8964        base_text,
8965        &mut cx,
8966    );
8967}
8968
8969#[gpui::test]
8970async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
8971    init_test(cx, |_| {});
8972
8973    let cols = 4;
8974    let rows = 10;
8975    let sample_text_1 = sample_text(rows, cols, 'a');
8976    assert_eq!(
8977        sample_text_1,
8978        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
8979    );
8980    let sample_text_2 = sample_text(rows, cols, 'l');
8981    assert_eq!(
8982        sample_text_2,
8983        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
8984    );
8985    let sample_text_3 = sample_text(rows, cols, 'v');
8986    assert_eq!(
8987        sample_text_3,
8988        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
8989    );
8990
8991    fn diff_every_buffer_row(
8992        buffer: &Model<Buffer>,
8993        sample_text: String,
8994        cols: usize,
8995        cx: &mut gpui::TestAppContext,
8996    ) {
8997        // revert first character in each row, creating one large diff hunk per buffer
8998        let is_first_char = |offset: usize| offset % cols == 0;
8999        buffer.update(cx, |buffer, cx| {
9000            buffer.set_text(
9001                sample_text
9002                    .chars()
9003                    .enumerate()
9004                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
9005                    .collect::<String>(),
9006                cx,
9007            );
9008            buffer.set_diff_base(Some(sample_text), cx);
9009        });
9010        cx.executor().run_until_parked();
9011    }
9012
9013    let buffer_1 = cx.new_model(|cx| {
9014        Buffer::new(
9015            0,
9016            BufferId::new(cx.entity_id().as_u64()).unwrap(),
9017            sample_text_1.clone(),
9018        )
9019    });
9020    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
9021
9022    let buffer_2 = cx.new_model(|cx| {
9023        Buffer::new(
9024            1,
9025            BufferId::new(cx.entity_id().as_u64() + 1).unwrap(),
9026            sample_text_2.clone(),
9027        )
9028    });
9029    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
9030
9031    let buffer_3 = cx.new_model(|cx| {
9032        Buffer::new(
9033            2,
9034            BufferId::new(cx.entity_id().as_u64() + 2).unwrap(),
9035            sample_text_3.clone(),
9036        )
9037    });
9038    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
9039
9040    let multibuffer = cx.new_model(|cx| {
9041        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
9042        multibuffer.push_excerpts(
9043            buffer_1.clone(),
9044            [
9045                ExcerptRange {
9046                    context: Point::new(0, 0)..Point::new(3, 0),
9047                    primary: None,
9048                },
9049                ExcerptRange {
9050                    context: Point::new(5, 0)..Point::new(7, 0),
9051                    primary: None,
9052                },
9053                ExcerptRange {
9054                    context: Point::new(9, 0)..Point::new(10, 4),
9055                    primary: None,
9056                },
9057            ],
9058            cx,
9059        );
9060        multibuffer.push_excerpts(
9061            buffer_2.clone(),
9062            [
9063                ExcerptRange {
9064                    context: Point::new(0, 0)..Point::new(3, 0),
9065                    primary: None,
9066                },
9067                ExcerptRange {
9068                    context: Point::new(5, 0)..Point::new(7, 0),
9069                    primary: None,
9070                },
9071                ExcerptRange {
9072                    context: Point::new(9, 0)..Point::new(10, 4),
9073                    primary: None,
9074                },
9075            ],
9076            cx,
9077        );
9078        multibuffer.push_excerpts(
9079            buffer_3.clone(),
9080            [
9081                ExcerptRange {
9082                    context: Point::new(0, 0)..Point::new(3, 0),
9083                    primary: None,
9084                },
9085                ExcerptRange {
9086                    context: Point::new(5, 0)..Point::new(7, 0),
9087                    primary: None,
9088                },
9089                ExcerptRange {
9090                    context: Point::new(9, 0)..Point::new(10, 4),
9091                    primary: None,
9092                },
9093            ],
9094            cx,
9095        );
9096        multibuffer
9097    });
9098
9099    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
9100    editor.update(cx, |editor, cx| {
9101        assert_eq!(editor.text(cx), "XaaaXbbbX\nccXc\ndXdd\n\nhXhh\nXiiiXjjjX\n\nXlllXmmmX\nnnXn\noXoo\n\nsXss\nXtttXuuuX\n\nXvvvXwwwX\nxxXx\nyXyy\n\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X\n");
9102        editor.select_all(&SelectAll, cx);
9103        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
9104    });
9105    cx.executor().run_until_parked();
9106    // When all ranges are selected, all buffer hunks are reverted.
9107    editor.update(cx, |editor, cx| {
9108        assert_eq!(editor.text(cx), "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n\n\nllll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu\n\n\nvvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}\n\n");
9109    });
9110    buffer_1.update(cx, |buffer, _| {
9111        assert_eq!(buffer.text(), sample_text_1);
9112    });
9113    buffer_2.update(cx, |buffer, _| {
9114        assert_eq!(buffer.text(), sample_text_2);
9115    });
9116    buffer_3.update(cx, |buffer, _| {
9117        assert_eq!(buffer.text(), sample_text_3);
9118    });
9119
9120    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
9121    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
9122    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
9123    editor.update(cx, |editor, cx| {
9124        editor.change_selections(None, cx, |s| {
9125            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
9126        });
9127        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
9128    });
9129    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
9130    // but not affect buffer_2 and its related excerpts.
9131    editor.update(cx, |editor, cx| {
9132        assert_eq!(
9133            editor.text(cx),
9134            "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n\n\nXlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX\n\n\nXvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X\n\n"
9135        );
9136    });
9137    buffer_1.update(cx, |buffer, _| {
9138        assert_eq!(buffer.text(), sample_text_1);
9139    });
9140    buffer_2.update(cx, |buffer, _| {
9141        assert_eq!(
9142            buffer.text(),
9143            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
9144        );
9145    });
9146    buffer_3.update(cx, |buffer, _| {
9147        assert_eq!(
9148            buffer.text(),
9149            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
9150        );
9151    });
9152}
9153
9154#[gpui::test]
9155async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
9156    init_test(cx, |_| {});
9157
9158    let cols = 4;
9159    let rows = 10;
9160    let sample_text_1 = sample_text(rows, cols, 'a');
9161    assert_eq!(
9162        sample_text_1,
9163        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
9164    );
9165    let sample_text_2 = sample_text(rows, cols, 'l');
9166    assert_eq!(
9167        sample_text_2,
9168        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
9169    );
9170    let sample_text_3 = sample_text(rows, cols, 'v');
9171    assert_eq!(
9172        sample_text_3,
9173        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
9174    );
9175
9176    let buffer_1 = cx.new_model(|cx| {
9177        Buffer::new(
9178            0,
9179            BufferId::new(cx.entity_id().as_u64()).unwrap(),
9180            sample_text_1.clone(),
9181        )
9182    });
9183
9184    let buffer_2 = cx.new_model(|cx| {
9185        Buffer::new(
9186            1,
9187            BufferId::new(cx.entity_id().as_u64() + 1).unwrap(),
9188            sample_text_2.clone(),
9189        )
9190    });
9191
9192    let buffer_3 = cx.new_model(|cx| {
9193        Buffer::new(
9194            2,
9195            BufferId::new(cx.entity_id().as_u64() + 2).unwrap(),
9196            sample_text_3.clone(),
9197        )
9198    });
9199
9200    let multi_buffer = cx.new_model(|cx| {
9201        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
9202        multibuffer.push_excerpts(
9203            buffer_1.clone(),
9204            [
9205                ExcerptRange {
9206                    context: Point::new(0, 0)..Point::new(3, 0),
9207                    primary: None,
9208                },
9209                ExcerptRange {
9210                    context: Point::new(5, 0)..Point::new(7, 0),
9211                    primary: None,
9212                },
9213                ExcerptRange {
9214                    context: Point::new(9, 0)..Point::new(10, 4),
9215                    primary: None,
9216                },
9217            ],
9218            cx,
9219        );
9220        multibuffer.push_excerpts(
9221            buffer_2.clone(),
9222            [
9223                ExcerptRange {
9224                    context: Point::new(0, 0)..Point::new(3, 0),
9225                    primary: None,
9226                },
9227                ExcerptRange {
9228                    context: Point::new(5, 0)..Point::new(7, 0),
9229                    primary: None,
9230                },
9231                ExcerptRange {
9232                    context: Point::new(9, 0)..Point::new(10, 4),
9233                    primary: None,
9234                },
9235            ],
9236            cx,
9237        );
9238        multibuffer.push_excerpts(
9239            buffer_3.clone(),
9240            [
9241                ExcerptRange {
9242                    context: Point::new(0, 0)..Point::new(3, 0),
9243                    primary: None,
9244                },
9245                ExcerptRange {
9246                    context: Point::new(5, 0)..Point::new(7, 0),
9247                    primary: None,
9248                },
9249                ExcerptRange {
9250                    context: Point::new(9, 0)..Point::new(10, 4),
9251                    primary: None,
9252                },
9253            ],
9254            cx,
9255        );
9256        multibuffer
9257    });
9258
9259    let fs = FakeFs::new(cx.executor());
9260    fs.insert_tree(
9261        "/a",
9262        json!({
9263            "main.rs": sample_text_1,
9264            "other.rs": sample_text_2,
9265            "lib.rs": sample_text_3,
9266        }),
9267    )
9268    .await;
9269    let project = Project::test(fs, ["/a".as_ref()], cx).await;
9270    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
9271    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
9272    let multi_buffer_editor =
9273        cx.new_view(|cx| Editor::new(EditorMode::Full, multi_buffer, Some(project.clone()), cx));
9274    let multibuffer_item_id = workspace
9275        .update(cx, |workspace, cx| {
9276            assert!(
9277                workspace.active_item(cx).is_none(),
9278                "active item should be None before the first item is added"
9279            );
9280            workspace.add_item_to_active_pane(Box::new(multi_buffer_editor.clone()), cx);
9281            let active_item = workspace
9282                .active_item(cx)
9283                .expect("should have an active item after adding the multi buffer");
9284            assert!(
9285                !active_item.is_singleton(cx),
9286                "A multi buffer was expected to active after adding"
9287            );
9288            active_item.item_id()
9289        })
9290        .unwrap();
9291    cx.executor().run_until_parked();
9292
9293    multi_buffer_editor.update(cx, |editor, cx| {
9294        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
9295        editor.open_excerpts(&OpenExcerpts, cx);
9296    });
9297    cx.executor().run_until_parked();
9298    let first_item_id = workspace
9299        .update(cx, |workspace, cx| {
9300            let active_item = workspace
9301                .active_item(cx)
9302                .expect("should have an active item after navigating into the 1st buffer");
9303            let first_item_id = active_item.item_id();
9304            assert_ne!(
9305                first_item_id, multibuffer_item_id,
9306                "Should navigate into the 1st buffer and activate it"
9307            );
9308            assert!(
9309                active_item.is_singleton(cx),
9310                "New active item should be a singleton buffer"
9311            );
9312            assert_eq!(
9313                active_item
9314                    .act_as::<Editor>(cx)
9315                    .expect("should have navigated into an editor for the 1st buffer")
9316                    .read(cx)
9317                    .text(cx),
9318                sample_text_1
9319            );
9320
9321            workspace
9322                .go_back(workspace.active_pane().downgrade(), cx)
9323                .detach_and_log_err(cx);
9324
9325            first_item_id
9326        })
9327        .unwrap();
9328    cx.executor().run_until_parked();
9329    workspace
9330        .update(cx, |workspace, cx| {
9331            let active_item = workspace
9332                .active_item(cx)
9333                .expect("should have an active item after navigating back");
9334            assert_eq!(
9335                active_item.item_id(),
9336                multibuffer_item_id,
9337                "Should navigate back to the multi buffer"
9338            );
9339            assert!(!active_item.is_singleton(cx));
9340        })
9341        .unwrap();
9342
9343    multi_buffer_editor.update(cx, |editor, cx| {
9344        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
9345            s.select_ranges(Some(39..40))
9346        });
9347        editor.open_excerpts(&OpenExcerpts, cx);
9348    });
9349    cx.executor().run_until_parked();
9350    let second_item_id = workspace
9351        .update(cx, |workspace, cx| {
9352            let active_item = workspace
9353                .active_item(cx)
9354                .expect("should have an active item after navigating into the 2nd buffer");
9355            let second_item_id = active_item.item_id();
9356            assert_ne!(
9357                second_item_id, multibuffer_item_id,
9358                "Should navigate away from the multibuffer"
9359            );
9360            assert_ne!(
9361                second_item_id, first_item_id,
9362                "Should navigate into the 2nd buffer and activate it"
9363            );
9364            assert!(
9365                active_item.is_singleton(cx),
9366                "New active item should be a singleton buffer"
9367            );
9368            assert_eq!(
9369                active_item
9370                    .act_as::<Editor>(cx)
9371                    .expect("should have navigated into an editor")
9372                    .read(cx)
9373                    .text(cx),
9374                sample_text_2
9375            );
9376
9377            workspace
9378                .go_back(workspace.active_pane().downgrade(), cx)
9379                .detach_and_log_err(cx);
9380
9381            second_item_id
9382        })
9383        .unwrap();
9384    cx.executor().run_until_parked();
9385    workspace
9386        .update(cx, |workspace, cx| {
9387            let active_item = workspace
9388                .active_item(cx)
9389                .expect("should have an active item after navigating back from the 2nd buffer");
9390            assert_eq!(
9391                active_item.item_id(),
9392                multibuffer_item_id,
9393                "Should navigate back from the 2nd buffer to the multi buffer"
9394            );
9395            assert!(!active_item.is_singleton(cx));
9396        })
9397        .unwrap();
9398
9399    multi_buffer_editor.update(cx, |editor, cx| {
9400        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
9401            s.select_ranges(Some(60..70))
9402        });
9403        editor.open_excerpts(&OpenExcerpts, cx);
9404    });
9405    cx.executor().run_until_parked();
9406    workspace
9407        .update(cx, |workspace, cx| {
9408            let active_item = workspace
9409                .active_item(cx)
9410                .expect("should have an active item after navigating into the 3rd buffer");
9411            let third_item_id = active_item.item_id();
9412            assert_ne!(
9413                third_item_id, multibuffer_item_id,
9414                "Should navigate into the 3rd buffer and activate it"
9415            );
9416            assert_ne!(third_item_id, first_item_id);
9417            assert_ne!(third_item_id, second_item_id);
9418            assert!(
9419                active_item.is_singleton(cx),
9420                "New active item should be a singleton buffer"
9421            );
9422            assert_eq!(
9423                active_item
9424                    .act_as::<Editor>(cx)
9425                    .expect("should have navigated into an editor")
9426                    .read(cx)
9427                    .text(cx),
9428                sample_text_3
9429            );
9430
9431            workspace
9432                .go_back(workspace.active_pane().downgrade(), cx)
9433                .detach_and_log_err(cx);
9434        })
9435        .unwrap();
9436    cx.executor().run_until_parked();
9437    workspace
9438        .update(cx, |workspace, cx| {
9439            let active_item = workspace
9440                .active_item(cx)
9441                .expect("should have an active item after navigating back from the 3rd buffer");
9442            assert_eq!(
9443                active_item.item_id(),
9444                multibuffer_item_id,
9445                "Should navigate back from the 3rd buffer to the multi buffer"
9446            );
9447            assert!(!active_item.is_singleton(cx));
9448        })
9449        .unwrap();
9450}
9451
9452fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
9453    let point = DisplayPoint::new(row as u32, column as u32);
9454    point..point
9455}
9456
9457fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
9458    let (text, ranges) = marked_text_ranges(marked_text, true);
9459    assert_eq!(view.text(cx), text);
9460    assert_eq!(
9461        view.selections.ranges(cx),
9462        ranges,
9463        "Assert selections are {}",
9464        marked_text
9465    );
9466}
9467
9468/// Handle completion request passing a marked string specifying where the completion
9469/// should be triggered from using '|' character, what range should be replaced, and what completions
9470/// should be returned using '<' and '>' to delimit the range
9471pub fn handle_completion_request(
9472    cx: &mut EditorLspTestContext,
9473    marked_string: &str,
9474    completions: Vec<&'static str>,
9475) -> impl Future<Output = ()> {
9476    let complete_from_marker: TextRangeMarker = '|'.into();
9477    let replace_range_marker: TextRangeMarker = ('<', '>').into();
9478    let (_, mut marked_ranges) = marked_text_ranges_by(
9479        marked_string,
9480        vec![complete_from_marker.clone(), replace_range_marker.clone()],
9481    );
9482
9483    let complete_from_position =
9484        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
9485    let replace_range =
9486        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
9487
9488    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
9489        let completions = completions.clone();
9490        async move {
9491            assert_eq!(params.text_document_position.text_document.uri, url.clone());
9492            assert_eq!(
9493                params.text_document_position.position,
9494                complete_from_position
9495            );
9496            Ok(Some(lsp::CompletionResponse::Array(
9497                completions
9498                    .iter()
9499                    .map(|completion_text| lsp::CompletionItem {
9500                        label: completion_text.to_string(),
9501                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
9502                            range: replace_range,
9503                            new_text: completion_text.to_string(),
9504                        })),
9505                        ..Default::default()
9506                    })
9507                    .collect(),
9508            )))
9509        }
9510    });
9511
9512    async move {
9513        request.next().await;
9514    }
9515}
9516
9517fn handle_resolve_completion_request(
9518    cx: &mut EditorLspTestContext,
9519    edits: Option<Vec<(&'static str, &'static str)>>,
9520) -> impl Future<Output = ()> {
9521    let edits = edits.map(|edits| {
9522        edits
9523            .iter()
9524            .map(|(marked_string, new_text)| {
9525                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
9526                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
9527                lsp::TextEdit::new(replace_range, new_text.to_string())
9528            })
9529            .collect::<Vec<_>>()
9530    });
9531
9532    let mut request =
9533        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
9534            let edits = edits.clone();
9535            async move {
9536                Ok(lsp::CompletionItem {
9537                    additional_text_edits: edits,
9538                    ..Default::default()
9539                })
9540            }
9541        });
9542
9543    async move {
9544        request.next().await;
9545    }
9546}
9547
9548pub(crate) fn update_test_language_settings(
9549    cx: &mut TestAppContext,
9550    f: impl Fn(&mut AllLanguageSettingsContent),
9551) {
9552    _ = cx.update(|cx| {
9553        cx.update_global(|store: &mut SettingsStore, cx| {
9554            store.update_user_settings::<AllLanguageSettings>(cx, f);
9555        });
9556    });
9557}
9558
9559pub(crate) fn update_test_project_settings(
9560    cx: &mut TestAppContext,
9561    f: impl Fn(&mut ProjectSettings),
9562) {
9563    _ = cx.update(|cx| {
9564        cx.update_global(|store: &mut SettingsStore, cx| {
9565            store.update_user_settings::<ProjectSettings>(cx, f);
9566        });
9567    });
9568}
9569
9570pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
9571    _ = cx.update(|cx| {
9572        let store = SettingsStore::test(cx);
9573        cx.set_global(store);
9574        theme::init(theme::LoadThemes::JustBase, cx);
9575        release_channel::init("0.0.0", cx);
9576        client::init_settings(cx);
9577        language::init(cx);
9578        Project::init_settings(cx);
9579        workspace::init_settings(cx);
9580        crate::init(cx);
9581    });
9582
9583    update_test_language_settings(cx, f);
9584}
9585
9586pub(crate) fn rust_lang() -> Arc<Language> {
9587    Arc::new(Language::new(
9588        LanguageConfig {
9589            name: "Rust".into(),
9590            matcher: LanguageMatcher {
9591                path_suffixes: vec!["rs".to_string()],
9592                ..Default::default()
9593            },
9594            ..Default::default()
9595        },
9596        Some(tree_sitter_rust::language()),
9597    ))
9598}
9599
9600#[track_caller]
9601fn assert_hunk_revert(
9602    not_reverted_text_with_selections: &str,
9603    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
9604    expected_reverted_text_with_selections: &str,
9605    base_text: &str,
9606    cx: &mut EditorLspTestContext,
9607) {
9608    cx.set_state(not_reverted_text_with_selections);
9609    cx.update_editor(|editor, cx| {
9610        editor
9611            .buffer()
9612            .read(cx)
9613            .as_singleton()
9614            .unwrap()
9615            .update(cx, |buffer, cx| {
9616                buffer.set_diff_base(Some(base_text.to_string()), cx);
9617            });
9618    });
9619    cx.executor().run_until_parked();
9620
9621    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
9622        let snapshot = editor
9623            .buffer()
9624            .read(cx)
9625            .as_singleton()
9626            .unwrap()
9627            .read(cx)
9628            .snapshot();
9629        let reverted_hunk_statuses = snapshot
9630            .git_diff_hunks_in_row_range(0..u32::MAX)
9631            .map(|hunk| hunk.status())
9632            .collect::<Vec<_>>();
9633
9634        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
9635        reverted_hunk_statuses
9636    });
9637    cx.executor().run_until_parked();
9638    cx.assert_editor_state(expected_reverted_text_with_selections);
9639    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
9640}