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(&DuplicateLine::default(), 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(&DuplicateLine::default(), 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(&DuplicateLine { move_upwards: true }, 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(&DuplicateLine { move_upwards: true }, 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_with_single_caret(cx: &mut gpui::TestAppContext) {
4092    init_test(cx, |_| {});
4093
4094    let mut cx = EditorTestContext::new(cx).await;
4095    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
4096
4097    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4098        .unwrap();
4099    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
4100
4101    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4102        .unwrap();
4103    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
4104
4105    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
4106    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
4107
4108    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
4109    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
4110
4111    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4112        .unwrap();
4113    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
4114
4115    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4116        .unwrap();
4117    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
4118
4119    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4120        .unwrap();
4121    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
4122}
4123
4124#[gpui::test]
4125async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
4126    init_test(cx, |_| {});
4127
4128    let mut cx = EditorTestContext::new(cx).await;
4129    cx.set_state(
4130        r#"let foo = 2;
4131lˇet foo = 2;
4132let fooˇ = 2;
4133let foo = 2;
4134let foo = ˇ2;"#,
4135    );
4136
4137    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4138        .unwrap();
4139    cx.assert_editor_state(
4140        r#"let foo = 2;
4141«letˇ» foo = 2;
4142let «fooˇ» = 2;
4143let foo = 2;
4144let foo = «2ˇ»;"#,
4145    );
4146
4147    // noop for multiple selections with different contents
4148    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4149        .unwrap();
4150    cx.assert_editor_state(
4151        r#"let foo = 2;
4152«letˇ» foo = 2;
4153let «fooˇ» = 2;
4154let foo = 2;
4155let foo = «2ˇ»;"#,
4156    );
4157}
4158
4159#[gpui::test]
4160async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
4161    init_test(cx, |_| {});
4162
4163    let mut cx = EditorTestContext::new(cx).await;
4164    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
4165
4166    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4167        .unwrap();
4168    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
4169
4170    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4171        .unwrap();
4172    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
4173
4174    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
4175    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
4176
4177    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
4178    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
4179
4180    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4181        .unwrap();
4182    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
4183
4184    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4185        .unwrap();
4186    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
4187}
4188
4189#[gpui::test]
4190async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
4191    init_test(cx, |_| {});
4192
4193    let language = Arc::new(Language::new(
4194        LanguageConfig::default(),
4195        Some(tree_sitter_rust::language()),
4196    ));
4197
4198    let text = r#"
4199        use mod1::mod2::{mod3, mod4};
4200
4201        fn fn_1(param1: bool, param2: &str) {
4202            let var1 = "text";
4203        }
4204    "#
4205    .unindent();
4206
4207    let buffer = cx.new_model(|cx| {
4208        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
4209            .with_language(language, cx)
4210    });
4211    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
4212    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
4213
4214    view.condition::<crate::EditorEvent>(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
4215        .await;
4216
4217    _ = view.update(cx, |view, cx| {
4218        view.change_selections(None, cx, |s| {
4219            s.select_display_ranges([
4220                DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
4221                DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
4222                DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
4223            ]);
4224        });
4225        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
4226    });
4227    assert_eq!(
4228        view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
4229        &[
4230            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
4231            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
4232            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
4233        ]
4234    );
4235
4236    _ = view.update(cx, |view, cx| {
4237        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
4238    });
4239    assert_eq!(
4240        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4241        &[
4242            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
4243            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
4244        ]
4245    );
4246
4247    _ = view.update(cx, |view, cx| {
4248        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
4249    });
4250    assert_eq!(
4251        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4252        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
4253    );
4254
4255    // Trying to expand the selected syntax node one more time has no effect.
4256    _ = view.update(cx, |view, cx| {
4257        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
4258    });
4259    assert_eq!(
4260        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4261        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
4262    );
4263
4264    _ = view.update(cx, |view, cx| {
4265        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
4266    });
4267    assert_eq!(
4268        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4269        &[
4270            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
4271            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
4272        ]
4273    );
4274
4275    _ = view.update(cx, |view, cx| {
4276        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
4277    });
4278    assert_eq!(
4279        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4280        &[
4281            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
4282            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
4283            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
4284        ]
4285    );
4286
4287    _ = view.update(cx, |view, cx| {
4288        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
4289    });
4290    assert_eq!(
4291        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4292        &[
4293            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
4294            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
4295            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
4296        ]
4297    );
4298
4299    // Trying to shrink the selected syntax node one more time has no effect.
4300    _ = view.update(cx, |view, cx| {
4301        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
4302    });
4303    assert_eq!(
4304        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4305        &[
4306            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
4307            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
4308            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
4309        ]
4310    );
4311
4312    // Ensure that we keep expanding the selection if the larger selection starts or ends within
4313    // a fold.
4314    _ = view.update(cx, |view, cx| {
4315        view.fold_ranges(
4316            vec![
4317                Point::new(0, 21)..Point::new(0, 24),
4318                Point::new(3, 20)..Point::new(3, 22),
4319            ],
4320            true,
4321            cx,
4322        );
4323        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
4324    });
4325    assert_eq!(
4326        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4327        &[
4328            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
4329            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
4330            DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
4331        ]
4332    );
4333}
4334
4335#[gpui::test]
4336async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
4337    init_test(cx, |_| {});
4338
4339    let language = Arc::new(
4340        Language::new(
4341            LanguageConfig {
4342                brackets: BracketPairConfig {
4343                    pairs: vec![
4344                        BracketPair {
4345                            start: "{".to_string(),
4346                            end: "}".to_string(),
4347                            close: false,
4348                            newline: true,
4349                        },
4350                        BracketPair {
4351                            start: "(".to_string(),
4352                            end: ")".to_string(),
4353                            close: false,
4354                            newline: true,
4355                        },
4356                    ],
4357                    ..Default::default()
4358                },
4359                ..Default::default()
4360            },
4361            Some(tree_sitter_rust::language()),
4362        )
4363        .with_indents_query(
4364            r#"
4365                (_ "(" ")" @end) @indent
4366                (_ "{" "}" @end) @indent
4367            "#,
4368        )
4369        .unwrap(),
4370    );
4371
4372    let text = "fn a() {}";
4373
4374    let buffer = cx.new_model(|cx| {
4375        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
4376            .with_language(language, cx)
4377    });
4378    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
4379    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
4380    editor
4381        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
4382        .await;
4383
4384    _ = editor.update(cx, |editor, cx| {
4385        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
4386        editor.newline(&Newline, cx);
4387        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
4388        assert_eq!(
4389            editor.selections.ranges(cx),
4390            &[
4391                Point::new(1, 4)..Point::new(1, 4),
4392                Point::new(3, 4)..Point::new(3, 4),
4393                Point::new(5, 0)..Point::new(5, 0)
4394            ]
4395        );
4396    });
4397}
4398
4399#[gpui::test]
4400async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
4401    init_test(cx, |_| {});
4402
4403    let mut cx = EditorTestContext::new(cx).await;
4404
4405    let language = Arc::new(Language::new(
4406        LanguageConfig {
4407            brackets: BracketPairConfig {
4408                pairs: vec![
4409                    BracketPair {
4410                        start: "{".to_string(),
4411                        end: "}".to_string(),
4412                        close: true,
4413                        newline: true,
4414                    },
4415                    BracketPair {
4416                        start: "(".to_string(),
4417                        end: ")".to_string(),
4418                        close: true,
4419                        newline: true,
4420                    },
4421                    BracketPair {
4422                        start: "/*".to_string(),
4423                        end: " */".to_string(),
4424                        close: true,
4425                        newline: true,
4426                    },
4427                    BracketPair {
4428                        start: "[".to_string(),
4429                        end: "]".to_string(),
4430                        close: false,
4431                        newline: true,
4432                    },
4433                    BracketPair {
4434                        start: "\"".to_string(),
4435                        end: "\"".to_string(),
4436                        close: true,
4437                        newline: false,
4438                    },
4439                ],
4440                ..Default::default()
4441            },
4442            autoclose_before: "})]".to_string(),
4443            ..Default::default()
4444        },
4445        Some(tree_sitter_rust::language()),
4446    ));
4447
4448    cx.language_registry().add(language.clone());
4449    cx.update_buffer(|buffer, cx| {
4450        buffer.set_language(Some(language), cx);
4451    });
4452
4453    cx.set_state(
4454        &r#"
4455            🏀ˇ
4456            εˇ
4457            ❤️ˇ
4458        "#
4459        .unindent(),
4460    );
4461
4462    // autoclose multiple nested brackets at multiple cursors
4463    cx.update_editor(|view, cx| {
4464        view.handle_input("{", cx);
4465        view.handle_input("{", cx);
4466        view.handle_input("{", cx);
4467    });
4468    cx.assert_editor_state(
4469        &"
4470            🏀{{{ˇ}}}
4471            ε{{{ˇ}}}
4472            ❤️{{{ˇ}}}
4473        "
4474        .unindent(),
4475    );
4476
4477    // insert a different closing bracket
4478    cx.update_editor(|view, cx| {
4479        view.handle_input(")", cx);
4480    });
4481    cx.assert_editor_state(
4482        &"
4483            🏀{{{)ˇ}}}
4484            ε{{{)ˇ}}}
4485            ❤️{{{)ˇ}}}
4486        "
4487        .unindent(),
4488    );
4489
4490    // skip over the auto-closed brackets when typing a closing bracket
4491    cx.update_editor(|view, cx| {
4492        view.move_right(&MoveRight, cx);
4493        view.handle_input("}", cx);
4494        view.handle_input("}", cx);
4495        view.handle_input("}", cx);
4496    });
4497    cx.assert_editor_state(
4498        &"
4499            🏀{{{)}}}}ˇ
4500            ε{{{)}}}}ˇ
4501            ❤️{{{)}}}}ˇ
4502        "
4503        .unindent(),
4504    );
4505
4506    // autoclose multi-character pairs
4507    cx.set_state(
4508        &"
4509            ˇ
4510            ˇ
4511        "
4512        .unindent(),
4513    );
4514    cx.update_editor(|view, cx| {
4515        view.handle_input("/", cx);
4516        view.handle_input("*", cx);
4517    });
4518    cx.assert_editor_state(
4519        &"
4520            /*ˇ */
4521            /*ˇ */
4522        "
4523        .unindent(),
4524    );
4525
4526    // one cursor autocloses a multi-character pair, one cursor
4527    // does not autoclose.
4528    cx.set_state(
4529        &"
45304531            ˇ
4532        "
4533        .unindent(),
4534    );
4535    cx.update_editor(|view, cx| view.handle_input("*", cx));
4536    cx.assert_editor_state(
4537        &"
4538            /*ˇ */
45394540        "
4541        .unindent(),
4542    );
4543
4544    // Don't autoclose if the next character isn't whitespace and isn't
4545    // listed in the language's "autoclose_before" section.
4546    cx.set_state("ˇa b");
4547    cx.update_editor(|view, cx| view.handle_input("{", cx));
4548    cx.assert_editor_state("{ˇa b");
4549
4550    // Don't autoclose if `close` is false for the bracket pair
4551    cx.set_state("ˇ");
4552    cx.update_editor(|view, cx| view.handle_input("[", cx));
4553    cx.assert_editor_state("");
4554
4555    // Surround with brackets if text is selected
4556    cx.set_state("«aˇ» b");
4557    cx.update_editor(|view, cx| view.handle_input("{", cx));
4558    cx.assert_editor_state("{«aˇ»} b");
4559
4560    // Autclose pair where the start and end characters are the same
4561    cx.set_state("");
4562    cx.update_editor(|view, cx| view.handle_input("\"", cx));
4563    cx.assert_editor_state("a\"ˇ\"");
4564    cx.update_editor(|view, cx| view.handle_input("\"", cx));
4565    cx.assert_editor_state("a\"\"ˇ");
4566}
4567
4568#[gpui::test]
4569async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
4570    init_test(cx, |settings| {
4571        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
4572    });
4573
4574    let mut cx = EditorTestContext::new(cx).await;
4575
4576    let language = Arc::new(Language::new(
4577        LanguageConfig {
4578            brackets: BracketPairConfig {
4579                pairs: vec![
4580                    BracketPair {
4581                        start: "{".to_string(),
4582                        end: "}".to_string(),
4583                        close: true,
4584                        newline: true,
4585                    },
4586                    BracketPair {
4587                        start: "(".to_string(),
4588                        end: ")".to_string(),
4589                        close: true,
4590                        newline: true,
4591                    },
4592                    BracketPair {
4593                        start: "[".to_string(),
4594                        end: "]".to_string(),
4595                        close: false,
4596                        newline: true,
4597                    },
4598                ],
4599                ..Default::default()
4600            },
4601            autoclose_before: "})]".to_string(),
4602            ..Default::default()
4603        },
4604        Some(tree_sitter_rust::language()),
4605    ));
4606
4607    cx.language_registry().add(language.clone());
4608    cx.update_buffer(|buffer, cx| {
4609        buffer.set_language(Some(language), cx);
4610    });
4611
4612    cx.set_state(
4613        &"
4614            ˇ
4615            ˇ
4616            ˇ
4617        "
4618        .unindent(),
4619    );
4620
4621    // ensure only matching closing brackets are skipped over
4622    cx.update_editor(|view, cx| {
4623        view.handle_input("}", cx);
4624        view.move_left(&MoveLeft, cx);
4625        view.handle_input(")", cx);
4626        view.move_left(&MoveLeft, cx);
4627    });
4628    cx.assert_editor_state(
4629        &"
4630            ˇ)}
4631            ˇ)}
4632            ˇ)}
4633        "
4634        .unindent(),
4635    );
4636
4637    // skip-over closing brackets at multiple cursors
4638    cx.update_editor(|view, cx| {
4639        view.handle_input(")", cx);
4640        view.handle_input("}", cx);
4641    });
4642    cx.assert_editor_state(
4643        &"
4644            )}ˇ
4645            )}ˇ
4646            )}ˇ
4647        "
4648        .unindent(),
4649    );
4650
4651    // ignore non-close brackets
4652    cx.update_editor(|view, cx| {
4653        view.handle_input("]", cx);
4654        view.move_left(&MoveLeft, cx);
4655        view.handle_input("]", cx);
4656    });
4657    cx.assert_editor_state(
4658        &"
4659            )}]ˇ]
4660            )}]ˇ]
4661            )}]ˇ]
4662        "
4663        .unindent(),
4664    );
4665}
4666
4667#[gpui::test]
4668async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
4669    init_test(cx, |_| {});
4670
4671    let mut cx = EditorTestContext::new(cx).await;
4672
4673    let html_language = Arc::new(
4674        Language::new(
4675            LanguageConfig {
4676                name: "HTML".into(),
4677                brackets: BracketPairConfig {
4678                    pairs: vec![
4679                        BracketPair {
4680                            start: "<".into(),
4681                            end: ">".into(),
4682                            close: true,
4683                            ..Default::default()
4684                        },
4685                        BracketPair {
4686                            start: "{".into(),
4687                            end: "}".into(),
4688                            close: true,
4689                            ..Default::default()
4690                        },
4691                        BracketPair {
4692                            start: "(".into(),
4693                            end: ")".into(),
4694                            close: true,
4695                            ..Default::default()
4696                        },
4697                    ],
4698                    ..Default::default()
4699                },
4700                autoclose_before: "})]>".into(),
4701                ..Default::default()
4702            },
4703            Some(tree_sitter_html::language()),
4704        )
4705        .with_injection_query(
4706            r#"
4707            (script_element
4708                (raw_text) @content
4709                (#set! "language" "javascript"))
4710            "#,
4711        )
4712        .unwrap(),
4713    );
4714
4715    let javascript_language = Arc::new(Language::new(
4716        LanguageConfig {
4717            name: "JavaScript".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_typescript::language_tsx()),
4745    ));
4746
4747    cx.language_registry().add(html_language.clone());
4748    cx.language_registry().add(javascript_language.clone());
4749
4750    cx.update_buffer(|buffer, cx| {
4751        buffer.set_language(Some(html_language), cx);
4752    });
4753
4754    cx.set_state(
4755        &r#"
4756            <body>ˇ
4757                <script>
4758                    var x = 1;ˇ
4759                </script>
4760            </body>ˇ
4761        "#
4762        .unindent(),
4763    );
4764
4765    // Precondition: different languages are active at different locations.
4766    cx.update_editor(|editor, cx| {
4767        let snapshot = editor.snapshot(cx);
4768        let cursors = editor.selections.ranges::<usize>(cx);
4769        let languages = cursors
4770            .iter()
4771            .map(|c| snapshot.language_at(c.start).unwrap().name())
4772            .collect::<Vec<_>>();
4773        assert_eq!(
4774            languages,
4775            &["HTML".into(), "JavaScript".into(), "HTML".into()]
4776        );
4777    });
4778
4779    // Angle brackets autoclose in HTML, but not JavaScript.
4780    cx.update_editor(|editor, cx| {
4781        editor.handle_input("<", cx);
4782        editor.handle_input("a", cx);
4783    });
4784    cx.assert_editor_state(
4785        &r#"
4786            <body><aˇ>
4787                <script>
4788                    var x = 1;<aˇ
4789                </script>
4790            </body><aˇ>
4791        "#
4792        .unindent(),
4793    );
4794
4795    // Curly braces and parens autoclose in both HTML and JavaScript.
4796    cx.update_editor(|editor, cx| {
4797        editor.handle_input(" b=", cx);
4798        editor.handle_input("{", cx);
4799        editor.handle_input("c", cx);
4800        editor.handle_input("(", cx);
4801    });
4802    cx.assert_editor_state(
4803        &r#"
4804            <body><a b={c(ˇ)}>
4805                <script>
4806                    var x = 1;<a b={c(ˇ)}
4807                </script>
4808            </body><a b={c(ˇ)}>
4809        "#
4810        .unindent(),
4811    );
4812
4813    // Brackets that were already autoclosed are skipped.
4814    cx.update_editor(|editor, cx| {
4815        editor.handle_input(")", cx);
4816        editor.handle_input("d", cx);
4817        editor.handle_input("}", cx);
4818    });
4819    cx.assert_editor_state(
4820        &r#"
4821            <body><a b={c()d}ˇ>
4822                <script>
4823                    var x = 1;<a b={c()d}ˇ
4824                </script>
4825            </body><a b={c()d}ˇ>
4826        "#
4827        .unindent(),
4828    );
4829    cx.update_editor(|editor, cx| {
4830        editor.handle_input(">", cx);
4831    });
4832    cx.assert_editor_state(
4833        &r#"
4834            <body><a b={c()d}>ˇ
4835                <script>
4836                    var x = 1;<a b={c()d}>ˇ
4837                </script>
4838            </body><a b={c()d}>ˇ
4839        "#
4840        .unindent(),
4841    );
4842
4843    // Reset
4844    cx.set_state(
4845        &r#"
4846            <body>ˇ
4847                <script>
4848                    var x = 1;ˇ
4849                </script>
4850            </body>ˇ
4851        "#
4852        .unindent(),
4853    );
4854
4855    cx.update_editor(|editor, cx| {
4856        editor.handle_input("<", cx);
4857    });
4858    cx.assert_editor_state(
4859        &r#"
4860            <body><ˇ>
4861                <script>
4862                    var x = 1;<ˇ
4863                </script>
4864            </body><ˇ>
4865        "#
4866        .unindent(),
4867    );
4868
4869    // When backspacing, the closing angle brackets are removed.
4870    cx.update_editor(|editor, cx| {
4871        editor.backspace(&Backspace, cx);
4872    });
4873    cx.assert_editor_state(
4874        &r#"
4875            <body>ˇ
4876                <script>
4877                    var x = 1;ˇ
4878                </script>
4879            </body>ˇ
4880        "#
4881        .unindent(),
4882    );
4883
4884    // Block comments autoclose in JavaScript, but not HTML.
4885    cx.update_editor(|editor, cx| {
4886        editor.handle_input("/", cx);
4887        editor.handle_input("*", cx);
4888    });
4889    cx.assert_editor_state(
4890        &r#"
4891            <body>/*ˇ
4892                <script>
4893                    var x = 1;/*ˇ */
4894                </script>
4895            </body>/*ˇ
4896        "#
4897        .unindent(),
4898    );
4899}
4900
4901#[gpui::test]
4902async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
4903    init_test(cx, |_| {});
4904
4905    let mut cx = EditorTestContext::new(cx).await;
4906
4907    let rust_language = Arc::new(
4908        Language::new(
4909            LanguageConfig {
4910                name: "Rust".into(),
4911                brackets: serde_json::from_value(json!([
4912                    { "start": "{", "end": "}", "close": true, "newline": true },
4913                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
4914                ]))
4915                .unwrap(),
4916                autoclose_before: "})]>".into(),
4917                ..Default::default()
4918            },
4919            Some(tree_sitter_rust::language()),
4920        )
4921        .with_override_query("(string_literal) @string")
4922        .unwrap(),
4923    );
4924
4925    cx.language_registry().add(rust_language.clone());
4926    cx.update_buffer(|buffer, cx| {
4927        buffer.set_language(Some(rust_language), cx);
4928    });
4929
4930    cx.set_state(
4931        &r#"
4932            let x = ˇ
4933        "#
4934        .unindent(),
4935    );
4936
4937    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
4938    cx.update_editor(|editor, cx| {
4939        editor.handle_input("\"", cx);
4940    });
4941    cx.assert_editor_state(
4942        &r#"
4943            let x = "ˇ"
4944        "#
4945        .unindent(),
4946    );
4947
4948    // Inserting another quotation mark. The cursor moves across the existing
4949    // automatically-inserted quotation mark.
4950    cx.update_editor(|editor, cx| {
4951        editor.handle_input("\"", cx);
4952    });
4953    cx.assert_editor_state(
4954        &r#"
4955            let x = ""ˇ
4956        "#
4957        .unindent(),
4958    );
4959
4960    // Reset
4961    cx.set_state(
4962        &r#"
4963            let x = ˇ
4964        "#
4965        .unindent(),
4966    );
4967
4968    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
4969    cx.update_editor(|editor, cx| {
4970        editor.handle_input("\"", cx);
4971        editor.handle_input(" ", cx);
4972        editor.move_left(&Default::default(), cx);
4973        editor.handle_input("\\", cx);
4974        editor.handle_input("\"", cx);
4975    });
4976    cx.assert_editor_state(
4977        &r#"
4978            let x = "\"ˇ "
4979        "#
4980        .unindent(),
4981    );
4982
4983    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
4984    // mark. Nothing is inserted.
4985    cx.update_editor(|editor, cx| {
4986        editor.move_right(&Default::default(), cx);
4987        editor.handle_input("\"", cx);
4988    });
4989    cx.assert_editor_state(
4990        &r#"
4991            let x = "\" "ˇ
4992        "#
4993        .unindent(),
4994    );
4995}
4996
4997#[gpui::test]
4998async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
4999    init_test(cx, |_| {});
5000
5001    let language = Arc::new(Language::new(
5002        LanguageConfig {
5003            brackets: BracketPairConfig {
5004                pairs: vec![
5005                    BracketPair {
5006                        start: "{".to_string(),
5007                        end: "}".to_string(),
5008                        close: true,
5009                        newline: true,
5010                    },
5011                    BracketPair {
5012                        start: "/* ".to_string(),
5013                        end: "*/".to_string(),
5014                        close: true,
5015                        ..Default::default()
5016                    },
5017                ],
5018                ..Default::default()
5019            },
5020            ..Default::default()
5021        },
5022        Some(tree_sitter_rust::language()),
5023    ));
5024
5025    let text = r#"
5026        a
5027        b
5028        c
5029    "#
5030    .unindent();
5031
5032    let buffer = cx.new_model(|cx| {
5033        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
5034            .with_language(language, cx)
5035    });
5036    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
5037    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5038    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
5039        .await;
5040
5041    _ = view.update(cx, |view, cx| {
5042        view.change_selections(None, cx, |s| {
5043            s.select_display_ranges([
5044                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
5045                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
5046                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
5047            ])
5048        });
5049
5050        view.handle_input("{", cx);
5051        view.handle_input("{", cx);
5052        view.handle_input("{", cx);
5053        assert_eq!(
5054            view.text(cx),
5055            "
5056                {{{a}}}
5057                {{{b}}}
5058                {{{c}}}
5059            "
5060            .unindent()
5061        );
5062        assert_eq!(
5063            view.selections.display_ranges(cx),
5064            [
5065                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
5066                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
5067                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
5068            ]
5069        );
5070
5071        view.undo(&Undo, cx);
5072        view.undo(&Undo, cx);
5073        view.undo(&Undo, cx);
5074        assert_eq!(
5075            view.text(cx),
5076            "
5077                a
5078                b
5079                c
5080            "
5081            .unindent()
5082        );
5083        assert_eq!(
5084            view.selections.display_ranges(cx),
5085            [
5086                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
5087                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
5088                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
5089            ]
5090        );
5091
5092        // Ensure inserting the first character of a multi-byte bracket pair
5093        // doesn't surround the selections with the bracket.
5094        view.handle_input("/", cx);
5095        assert_eq!(
5096            view.text(cx),
5097            "
5098                /
5099                /
5100                /
5101            "
5102            .unindent()
5103        );
5104        assert_eq!(
5105            view.selections.display_ranges(cx),
5106            [
5107                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
5108                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
5109                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
5110            ]
5111        );
5112
5113        view.undo(&Undo, cx);
5114        assert_eq!(
5115            view.text(cx),
5116            "
5117                a
5118                b
5119                c
5120            "
5121            .unindent()
5122        );
5123        assert_eq!(
5124            view.selections.display_ranges(cx),
5125            [
5126                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
5127                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
5128                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
5129            ]
5130        );
5131
5132        // Ensure inserting the last character of a multi-byte bracket pair
5133        // doesn't surround the selections with the bracket.
5134        view.handle_input("*", cx);
5135        assert_eq!(
5136            view.text(cx),
5137            "
5138                *
5139                *
5140                *
5141            "
5142            .unindent()
5143        );
5144        assert_eq!(
5145            view.selections.display_ranges(cx),
5146            [
5147                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
5148                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
5149                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
5150            ]
5151        );
5152    });
5153}
5154
5155#[gpui::test]
5156async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
5157    init_test(cx, |_| {});
5158
5159    let language = Arc::new(Language::new(
5160        LanguageConfig {
5161            brackets: BracketPairConfig {
5162                pairs: vec![BracketPair {
5163                    start: "{".to_string(),
5164                    end: "}".to_string(),
5165                    close: true,
5166                    newline: true,
5167                }],
5168                ..Default::default()
5169            },
5170            autoclose_before: "}".to_string(),
5171            ..Default::default()
5172        },
5173        Some(tree_sitter_rust::language()),
5174    ));
5175
5176    let text = r#"
5177        a
5178        b
5179        c
5180    "#
5181    .unindent();
5182
5183    let buffer = cx.new_model(|cx| {
5184        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
5185            .with_language(language, cx)
5186    });
5187    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
5188    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5189    editor
5190        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
5191        .await;
5192
5193    _ = editor.update(cx, |editor, cx| {
5194        editor.change_selections(None, cx, |s| {
5195            s.select_ranges([
5196                Point::new(0, 1)..Point::new(0, 1),
5197                Point::new(1, 1)..Point::new(1, 1),
5198                Point::new(2, 1)..Point::new(2, 1),
5199            ])
5200        });
5201
5202        editor.handle_input("{", cx);
5203        editor.handle_input("{", cx);
5204        editor.handle_input("_", cx);
5205        assert_eq!(
5206            editor.text(cx),
5207            "
5208                a{{_}}
5209                b{{_}}
5210                c{{_}}
5211            "
5212            .unindent()
5213        );
5214        assert_eq!(
5215            editor.selections.ranges::<Point>(cx),
5216            [
5217                Point::new(0, 4)..Point::new(0, 4),
5218                Point::new(1, 4)..Point::new(1, 4),
5219                Point::new(2, 4)..Point::new(2, 4)
5220            ]
5221        );
5222
5223        editor.backspace(&Default::default(), cx);
5224        editor.backspace(&Default::default(), cx);
5225        assert_eq!(
5226            editor.text(cx),
5227            "
5228                a{}
5229                b{}
5230                c{}
5231            "
5232            .unindent()
5233        );
5234        assert_eq!(
5235            editor.selections.ranges::<Point>(cx),
5236            [
5237                Point::new(0, 2)..Point::new(0, 2),
5238                Point::new(1, 2)..Point::new(1, 2),
5239                Point::new(2, 2)..Point::new(2, 2)
5240            ]
5241        );
5242
5243        editor.delete_to_previous_word_start(&Default::default(), cx);
5244        assert_eq!(
5245            editor.text(cx),
5246            "
5247                a
5248                b
5249                c
5250            "
5251            .unindent()
5252        );
5253        assert_eq!(
5254            editor.selections.ranges::<Point>(cx),
5255            [
5256                Point::new(0, 1)..Point::new(0, 1),
5257                Point::new(1, 1)..Point::new(1, 1),
5258                Point::new(2, 1)..Point::new(2, 1)
5259            ]
5260        );
5261    });
5262}
5263
5264#[gpui::test]
5265async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
5266    init_test(cx, |settings| {
5267        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
5268    });
5269
5270    let mut cx = EditorTestContext::new(cx).await;
5271
5272    let language = Arc::new(Language::new(
5273        LanguageConfig {
5274            brackets: BracketPairConfig {
5275                pairs: vec![
5276                    BracketPair {
5277                        start: "{".to_string(),
5278                        end: "}".to_string(),
5279                        close: true,
5280                        newline: true,
5281                    },
5282                    BracketPair {
5283                        start: "(".to_string(),
5284                        end: ")".to_string(),
5285                        close: true,
5286                        newline: true,
5287                    },
5288                    BracketPair {
5289                        start: "[".to_string(),
5290                        end: "]".to_string(),
5291                        close: false,
5292                        newline: true,
5293                    },
5294                ],
5295                ..Default::default()
5296            },
5297            autoclose_before: "})]".to_string(),
5298            ..Default::default()
5299        },
5300        Some(tree_sitter_rust::language()),
5301    ));
5302
5303    cx.language_registry().add(language.clone());
5304    cx.update_buffer(|buffer, cx| {
5305        buffer.set_language(Some(language), cx);
5306    });
5307
5308    cx.set_state(
5309        &"
5310            {(ˇ)}
5311            [[ˇ]]
5312            {(ˇ)}
5313        "
5314        .unindent(),
5315    );
5316
5317    cx.update_editor(|view, cx| {
5318        view.backspace(&Default::default(), cx);
5319        view.backspace(&Default::default(), cx);
5320    });
5321
5322    cx.assert_editor_state(
5323        &"
5324            ˇ
5325            ˇ]]
5326            ˇ
5327        "
5328        .unindent(),
5329    );
5330
5331    cx.update_editor(|view, cx| {
5332        view.handle_input("{", cx);
5333        view.handle_input("{", cx);
5334        view.move_right(&MoveRight, cx);
5335        view.move_right(&MoveRight, cx);
5336        view.move_left(&MoveLeft, cx);
5337        view.move_left(&MoveLeft, cx);
5338        view.backspace(&Default::default(), cx);
5339    });
5340
5341    cx.assert_editor_state(
5342        &"
5343            {ˇ}
5344            {ˇ}]]
5345            {ˇ}
5346        "
5347        .unindent(),
5348    );
5349
5350    cx.update_editor(|view, cx| {
5351        view.backspace(&Default::default(), cx);
5352    });
5353
5354    cx.assert_editor_state(
5355        &"
5356            ˇ
5357            ˇ]]
5358            ˇ
5359        "
5360        .unindent(),
5361    );
5362}
5363
5364#[gpui::test]
5365async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
5366    init_test(cx, |_| {});
5367
5368    let language = Arc::new(Language::new(
5369        LanguageConfig::default(),
5370        Some(tree_sitter_rust::language()),
5371    ));
5372
5373    let buffer = cx.new_model(|cx| {
5374        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "")
5375            .with_language(language, cx)
5376    });
5377    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
5378    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5379    editor
5380        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
5381        .await;
5382
5383    _ = editor.update(cx, |editor, cx| {
5384        editor.set_auto_replace_emoji_shortcode(true);
5385
5386        editor.handle_input("Hello ", cx);
5387        editor.handle_input(":wave", cx);
5388        assert_eq!(editor.text(cx), "Hello :wave".unindent());
5389
5390        editor.handle_input(":", cx);
5391        assert_eq!(editor.text(cx), "Hello 👋".unindent());
5392
5393        editor.handle_input(" :smile", cx);
5394        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
5395
5396        editor.handle_input(":", cx);
5397        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
5398
5399        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
5400        editor.handle_input(":wave", cx);
5401        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
5402
5403        editor.handle_input(":", cx);
5404        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
5405
5406        editor.handle_input(":1", cx);
5407        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
5408
5409        editor.handle_input(":", cx);
5410        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
5411
5412        // Ensure shortcode does not get replaced when it is part of a word
5413        editor.handle_input(" Test:wave", cx);
5414        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
5415
5416        editor.handle_input(":", cx);
5417        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
5418
5419        editor.set_auto_replace_emoji_shortcode(false);
5420
5421        // Ensure shortcode does not get replaced when auto replace is off
5422        editor.handle_input(" :wave", cx);
5423        assert_eq!(
5424            editor.text(cx),
5425            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
5426        );
5427
5428        editor.handle_input(":", cx);
5429        assert_eq!(
5430            editor.text(cx),
5431            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
5432        );
5433    });
5434}
5435
5436#[gpui::test]
5437async fn test_snippets(cx: &mut gpui::TestAppContext) {
5438    init_test(cx, |_| {});
5439
5440    let (text, insertion_ranges) = marked_text_ranges(
5441        indoc! {"
5442            a.ˇ b
5443            a.ˇ b
5444            a.ˇ b
5445        "},
5446        false,
5447    );
5448
5449    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
5450    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5451
5452    _ = editor.update(cx, |editor, cx| {
5453        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
5454
5455        editor
5456            .insert_snippet(&insertion_ranges, snippet, cx)
5457            .unwrap();
5458
5459        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
5460            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
5461            assert_eq!(editor.text(cx), expected_text);
5462            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
5463        }
5464
5465        assert(
5466            editor,
5467            cx,
5468            indoc! {"
5469                a.f(«one», two, «three») b
5470                a.f(«one», two, «three») b
5471                a.f(«one», two, «three») b
5472            "},
5473        );
5474
5475        // Can't move earlier than the first tab stop
5476        assert!(!editor.move_to_prev_snippet_tabstop(cx));
5477        assert(
5478            editor,
5479            cx,
5480            indoc! {"
5481                a.f(«one», two, «three») b
5482                a.f(«one», two, «three») b
5483                a.f(«one», two, «three») b
5484            "},
5485        );
5486
5487        assert!(editor.move_to_next_snippet_tabstop(cx));
5488        assert(
5489            editor,
5490            cx,
5491            indoc! {"
5492                a.f(one, «two», three) b
5493                a.f(one, «two», three) b
5494                a.f(one, «two», three) b
5495            "},
5496        );
5497
5498        editor.move_to_prev_snippet_tabstop(cx);
5499        assert(
5500            editor,
5501            cx,
5502            indoc! {"
5503                a.f(«one», two, «three») b
5504                a.f(«one», two, «three») b
5505                a.f(«one», two, «three») b
5506            "},
5507        );
5508
5509        assert!(editor.move_to_next_snippet_tabstop(cx));
5510        assert(
5511            editor,
5512            cx,
5513            indoc! {"
5514                a.f(one, «two», three) b
5515                a.f(one, «two», three) b
5516                a.f(one, «two», three) b
5517            "},
5518        );
5519        assert!(editor.move_to_next_snippet_tabstop(cx));
5520        assert(
5521            editor,
5522            cx,
5523            indoc! {"
5524                a.f(one, two, three)ˇ b
5525                a.f(one, two, three)ˇ b
5526                a.f(one, two, three)ˇ b
5527            "},
5528        );
5529
5530        // As soon as the last tab stop is reached, snippet state is gone
5531        editor.move_to_prev_snippet_tabstop(cx);
5532        assert(
5533            editor,
5534            cx,
5535            indoc! {"
5536                a.f(one, two, three)ˇ b
5537                a.f(one, two, three)ˇ b
5538                a.f(one, two, three)ˇ b
5539            "},
5540        );
5541    });
5542}
5543
5544#[gpui::test]
5545async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
5546    init_test(cx, |_| {});
5547
5548    let fs = FakeFs::new(cx.executor());
5549    fs.insert_file("/file.rs", Default::default()).await;
5550
5551    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5552
5553    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
5554    language_registry.add(rust_lang());
5555    let mut fake_servers = language_registry.register_fake_lsp_adapter(
5556        "Rust",
5557        FakeLspAdapter {
5558            capabilities: lsp::ServerCapabilities {
5559                document_formatting_provider: Some(lsp::OneOf::Left(true)),
5560                ..Default::default()
5561            },
5562            ..Default::default()
5563        },
5564    );
5565
5566    let buffer = project
5567        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
5568        .await
5569        .unwrap();
5570
5571    cx.executor().start_waiting();
5572    let fake_server = fake_servers.next().await.unwrap();
5573
5574    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
5575    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5576    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5577    assert!(cx.read(|cx| editor.is_dirty(cx)));
5578
5579    let save = editor
5580        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5581        .unwrap();
5582    fake_server
5583        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5584            assert_eq!(
5585                params.text_document.uri,
5586                lsp::Url::from_file_path("/file.rs").unwrap()
5587            );
5588            assert_eq!(params.options.tab_size, 4);
5589            Ok(Some(vec![lsp::TextEdit::new(
5590                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
5591                ", ".to_string(),
5592            )]))
5593        })
5594        .next()
5595        .await;
5596    cx.executor().start_waiting();
5597    save.await;
5598
5599    assert_eq!(
5600        editor.update(cx, |editor, cx| editor.text(cx)),
5601        "one, two\nthree\n"
5602    );
5603    assert!(!cx.read(|cx| editor.is_dirty(cx)));
5604
5605    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5606    assert!(cx.read(|cx| editor.is_dirty(cx)));
5607
5608    // Ensure we can still save even if formatting hangs.
5609    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5610        assert_eq!(
5611            params.text_document.uri,
5612            lsp::Url::from_file_path("/file.rs").unwrap()
5613        );
5614        futures::future::pending::<()>().await;
5615        unreachable!()
5616    });
5617    let save = editor
5618        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5619        .unwrap();
5620    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
5621    cx.executor().start_waiting();
5622    save.await;
5623    assert_eq!(
5624        editor.update(cx, |editor, cx| editor.text(cx)),
5625        "one\ntwo\nthree\n"
5626    );
5627    assert!(!cx.read(|cx| editor.is_dirty(cx)));
5628
5629    // For non-dirty buffer, no formatting request should be sent
5630    let save = editor
5631        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5632        .unwrap();
5633    let _pending_format_request = fake_server
5634        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
5635            panic!("Should not be invoked on non-dirty buffer");
5636        })
5637        .next();
5638    cx.executor().start_waiting();
5639    save.await;
5640
5641    // Set rust language override and assert overridden tabsize is sent to language server
5642    update_test_language_settings(cx, |settings| {
5643        settings.languages.insert(
5644            "Rust".into(),
5645            LanguageSettingsContent {
5646                tab_size: NonZeroU32::new(8),
5647                ..Default::default()
5648            },
5649        );
5650    });
5651
5652    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
5653    assert!(cx.read(|cx| editor.is_dirty(cx)));
5654    let save = editor
5655        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5656        .unwrap();
5657    fake_server
5658        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5659            assert_eq!(
5660                params.text_document.uri,
5661                lsp::Url::from_file_path("/file.rs").unwrap()
5662            );
5663            assert_eq!(params.options.tab_size, 8);
5664            Ok(Some(vec![]))
5665        })
5666        .next()
5667        .await;
5668    cx.executor().start_waiting();
5669    save.await;
5670}
5671
5672#[gpui::test]
5673async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
5674    init_test(cx, |_| {});
5675
5676    let cols = 4;
5677    let rows = 10;
5678    let sample_text_1 = sample_text(rows, cols, 'a');
5679    assert_eq!(
5680        sample_text_1,
5681        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
5682    );
5683    let sample_text_2 = sample_text(rows, cols, 'l');
5684    assert_eq!(
5685        sample_text_2,
5686        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
5687    );
5688    let sample_text_3 = sample_text(rows, cols, 'v');
5689    assert_eq!(
5690        sample_text_3,
5691        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
5692    );
5693
5694    let fs = FakeFs::new(cx.executor());
5695    fs.insert_tree(
5696        "/a",
5697        json!({
5698            "main.rs": sample_text_1,
5699            "other.rs": sample_text_2,
5700            "lib.rs": sample_text_3,
5701        }),
5702    )
5703    .await;
5704
5705    let project = Project::test(fs, ["/a".as_ref()], cx).await;
5706    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
5707    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
5708
5709    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
5710    language_registry.add(rust_lang());
5711    let mut fake_servers = language_registry.register_fake_lsp_adapter(
5712        "Rust",
5713        FakeLspAdapter {
5714            capabilities: lsp::ServerCapabilities {
5715                document_formatting_provider: Some(lsp::OneOf::Left(true)),
5716                ..Default::default()
5717            },
5718            ..Default::default()
5719        },
5720    );
5721
5722    let worktree = project.update(cx, |project, _| {
5723        let mut worktrees = project.worktrees().collect::<Vec<_>>();
5724        assert_eq!(worktrees.len(), 1);
5725        worktrees.pop().unwrap()
5726    });
5727    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
5728
5729    let buffer_1 = project
5730        .update(cx, |project, cx| {
5731            project.open_buffer((worktree_id, "main.rs"), cx)
5732        })
5733        .await
5734        .unwrap();
5735    let buffer_2 = project
5736        .update(cx, |project, cx| {
5737            project.open_buffer((worktree_id, "other.rs"), cx)
5738        })
5739        .await
5740        .unwrap();
5741    let buffer_3 = project
5742        .update(cx, |project, cx| {
5743            project.open_buffer((worktree_id, "lib.rs"), cx)
5744        })
5745        .await
5746        .unwrap();
5747
5748    let multi_buffer = cx.new_model(|cx| {
5749        let mut multi_buffer = MultiBuffer::new(0, ReadWrite);
5750        multi_buffer.push_excerpts(
5751            buffer_1.clone(),
5752            [
5753                ExcerptRange {
5754                    context: Point::new(0, 0)..Point::new(3, 0),
5755                    primary: None,
5756                },
5757                ExcerptRange {
5758                    context: Point::new(5, 0)..Point::new(7, 0),
5759                    primary: None,
5760                },
5761                ExcerptRange {
5762                    context: Point::new(9, 0)..Point::new(10, 4),
5763                    primary: None,
5764                },
5765            ],
5766            cx,
5767        );
5768        multi_buffer.push_excerpts(
5769            buffer_2.clone(),
5770            [
5771                ExcerptRange {
5772                    context: Point::new(0, 0)..Point::new(3, 0),
5773                    primary: None,
5774                },
5775                ExcerptRange {
5776                    context: Point::new(5, 0)..Point::new(7, 0),
5777                    primary: None,
5778                },
5779                ExcerptRange {
5780                    context: Point::new(9, 0)..Point::new(10, 4),
5781                    primary: None,
5782                },
5783            ],
5784            cx,
5785        );
5786        multi_buffer.push_excerpts(
5787            buffer_3.clone(),
5788            [
5789                ExcerptRange {
5790                    context: Point::new(0, 0)..Point::new(3, 0),
5791                    primary: None,
5792                },
5793                ExcerptRange {
5794                    context: Point::new(5, 0)..Point::new(7, 0),
5795                    primary: None,
5796                },
5797                ExcerptRange {
5798                    context: Point::new(9, 0)..Point::new(10, 4),
5799                    primary: None,
5800                },
5801            ],
5802            cx,
5803        );
5804        multi_buffer
5805    });
5806    let multi_buffer_editor =
5807        cx.new_view(|cx| Editor::new(EditorMode::Full, multi_buffer, Some(project.clone()), cx));
5808
5809    multi_buffer_editor.update(cx, |editor, cx| {
5810        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
5811        editor.insert("|one|two|three|", cx);
5812    });
5813    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
5814    multi_buffer_editor.update(cx, |editor, cx| {
5815        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
5816            s.select_ranges(Some(60..70))
5817        });
5818        editor.insert("|four|five|six|", cx);
5819    });
5820    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
5821
5822    // First two buffers should be edited, but not the third one.
5823    assert_eq!(
5824        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
5825        "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}",
5826    );
5827    buffer_1.update(cx, |buffer, _| {
5828        assert!(buffer.is_dirty());
5829        assert_eq!(
5830            buffer.text(),
5831            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
5832        )
5833    });
5834    buffer_2.update(cx, |buffer, _| {
5835        assert!(buffer.is_dirty());
5836        assert_eq!(
5837            buffer.text(),
5838            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
5839        )
5840    });
5841    buffer_3.update(cx, |buffer, _| {
5842        assert!(!buffer.is_dirty());
5843        assert_eq!(buffer.text(), sample_text_3,)
5844    });
5845
5846    cx.executor().start_waiting();
5847    let save = multi_buffer_editor
5848        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5849        .unwrap();
5850
5851    let fake_server = fake_servers.next().await.unwrap();
5852    fake_server
5853        .server
5854        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5855            Ok(Some(vec![lsp::TextEdit::new(
5856                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
5857                format!("[{} formatted]", params.text_document.uri),
5858            )]))
5859        })
5860        .detach();
5861    save.await;
5862
5863    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
5864    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
5865    assert_eq!(
5866        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
5867        "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}",
5868    );
5869    buffer_1.update(cx, |buffer, _| {
5870        assert!(!buffer.is_dirty());
5871        assert_eq!(
5872            buffer.text(),
5873            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
5874        )
5875    });
5876    buffer_2.update(cx, |buffer, _| {
5877        assert!(!buffer.is_dirty());
5878        assert_eq!(
5879            buffer.text(),
5880            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
5881        )
5882    });
5883    buffer_3.update(cx, |buffer, _| {
5884        assert!(!buffer.is_dirty());
5885        assert_eq!(buffer.text(), sample_text_3,)
5886    });
5887}
5888
5889#[gpui::test]
5890async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
5891    init_test(cx, |_| {});
5892
5893    let fs = FakeFs::new(cx.executor());
5894    fs.insert_file("/file.rs", Default::default()).await;
5895
5896    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5897
5898    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
5899    language_registry.add(rust_lang());
5900    let mut fake_servers = language_registry.register_fake_lsp_adapter(
5901        "Rust",
5902        FakeLspAdapter {
5903            capabilities: lsp::ServerCapabilities {
5904                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
5905                ..Default::default()
5906            },
5907            ..Default::default()
5908        },
5909    );
5910
5911    let buffer = project
5912        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
5913        .await
5914        .unwrap();
5915
5916    cx.executor().start_waiting();
5917    let fake_server = fake_servers.next().await.unwrap();
5918
5919    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
5920    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5921    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5922    assert!(cx.read(|cx| editor.is_dirty(cx)));
5923
5924    let save = editor
5925        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5926        .unwrap();
5927    fake_server
5928        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
5929            assert_eq!(
5930                params.text_document.uri,
5931                lsp::Url::from_file_path("/file.rs").unwrap()
5932            );
5933            assert_eq!(params.options.tab_size, 4);
5934            Ok(Some(vec![lsp::TextEdit::new(
5935                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
5936                ", ".to_string(),
5937            )]))
5938        })
5939        .next()
5940        .await;
5941    cx.executor().start_waiting();
5942    save.await;
5943    assert_eq!(
5944        editor.update(cx, |editor, cx| editor.text(cx)),
5945        "one, two\nthree\n"
5946    );
5947    assert!(!cx.read(|cx| editor.is_dirty(cx)));
5948
5949    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5950    assert!(cx.read(|cx| editor.is_dirty(cx)));
5951
5952    // Ensure we can still save even if formatting hangs.
5953    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
5954        move |params, _| async move {
5955            assert_eq!(
5956                params.text_document.uri,
5957                lsp::Url::from_file_path("/file.rs").unwrap()
5958            );
5959            futures::future::pending::<()>().await;
5960            unreachable!()
5961        },
5962    );
5963    let save = editor
5964        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5965        .unwrap();
5966    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
5967    cx.executor().start_waiting();
5968    save.await;
5969    assert_eq!(
5970        editor.update(cx, |editor, cx| editor.text(cx)),
5971        "one\ntwo\nthree\n"
5972    );
5973    assert!(!cx.read(|cx| editor.is_dirty(cx)));
5974
5975    // For non-dirty buffer, no formatting request should be sent
5976    let save = editor
5977        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5978        .unwrap();
5979    let _pending_format_request = fake_server
5980        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
5981            panic!("Should not be invoked on non-dirty buffer");
5982        })
5983        .next();
5984    cx.executor().start_waiting();
5985    save.await;
5986
5987    // Set Rust language override and assert overridden tabsize is sent to language server
5988    update_test_language_settings(cx, |settings| {
5989        settings.languages.insert(
5990            "Rust".into(),
5991            LanguageSettingsContent {
5992                tab_size: NonZeroU32::new(8),
5993                ..Default::default()
5994            },
5995        );
5996    });
5997
5998    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
5999    assert!(cx.read(|cx| editor.is_dirty(cx)));
6000    let save = editor
6001        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
6002        .unwrap();
6003    fake_server
6004        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
6005            assert_eq!(
6006                params.text_document.uri,
6007                lsp::Url::from_file_path("/file.rs").unwrap()
6008            );
6009            assert_eq!(params.options.tab_size, 8);
6010            Ok(Some(vec![]))
6011        })
6012        .next()
6013        .await;
6014    cx.executor().start_waiting();
6015    save.await;
6016}
6017
6018#[gpui::test]
6019async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
6020    init_test(cx, |settings| {
6021        settings.defaults.formatter = Some(language_settings::Formatter::LanguageServer)
6022    });
6023
6024    let fs = FakeFs::new(cx.executor());
6025    fs.insert_file("/file.rs", Default::default()).await;
6026
6027    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
6028
6029    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
6030    language_registry.add(Arc::new(Language::new(
6031        LanguageConfig {
6032            name: "Rust".into(),
6033            matcher: LanguageMatcher {
6034                path_suffixes: vec!["rs".to_string()],
6035                ..Default::default()
6036            },
6037            // Enable Prettier formatting for the same buffer, and ensure
6038            // LSP is called instead of Prettier.
6039            prettier_parser_name: Some("test_parser".to_string()),
6040            ..Default::default()
6041        },
6042        Some(tree_sitter_rust::language()),
6043    )));
6044    let mut fake_servers = language_registry.register_fake_lsp_adapter(
6045        "Rust",
6046        FakeLspAdapter {
6047            capabilities: lsp::ServerCapabilities {
6048                document_formatting_provider: Some(lsp::OneOf::Left(true)),
6049                ..Default::default()
6050            },
6051            ..Default::default()
6052        },
6053    );
6054
6055    let buffer = project
6056        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
6057        .await
6058        .unwrap();
6059
6060    cx.executor().start_waiting();
6061    let fake_server = fake_servers.next().await.unwrap();
6062
6063    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
6064    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
6065    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
6066
6067    let format = editor
6068        .update(cx, |editor, cx| {
6069            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
6070        })
6071        .unwrap();
6072    fake_server
6073        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
6074            assert_eq!(
6075                params.text_document.uri,
6076                lsp::Url::from_file_path("/file.rs").unwrap()
6077            );
6078            assert_eq!(params.options.tab_size, 4);
6079            Ok(Some(vec![lsp::TextEdit::new(
6080                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
6081                ", ".to_string(),
6082            )]))
6083        })
6084        .next()
6085        .await;
6086    cx.executor().start_waiting();
6087    format.await;
6088    assert_eq!(
6089        editor.update(cx, |editor, cx| editor.text(cx)),
6090        "one, two\nthree\n"
6091    );
6092
6093    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
6094    // Ensure we don't lock if formatting hangs.
6095    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
6096        assert_eq!(
6097            params.text_document.uri,
6098            lsp::Url::from_file_path("/file.rs").unwrap()
6099        );
6100        futures::future::pending::<()>().await;
6101        unreachable!()
6102    });
6103    let format = editor
6104        .update(cx, |editor, cx| {
6105            editor.perform_format(project, FormatTrigger::Manual, cx)
6106        })
6107        .unwrap();
6108    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
6109    cx.executor().start_waiting();
6110    format.await;
6111    assert_eq!(
6112        editor.update(cx, |editor, cx| editor.text(cx)),
6113        "one\ntwo\nthree\n"
6114    );
6115}
6116
6117#[gpui::test]
6118async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
6119    init_test(cx, |_| {});
6120
6121    let mut cx = EditorLspTestContext::new_rust(
6122        lsp::ServerCapabilities {
6123            document_formatting_provider: Some(lsp::OneOf::Left(true)),
6124            ..Default::default()
6125        },
6126        cx,
6127    )
6128    .await;
6129
6130    cx.set_state(indoc! {"
6131        one.twoˇ
6132    "});
6133
6134    // The format request takes a long time. When it completes, it inserts
6135    // a newline and an indent before the `.`
6136    cx.lsp
6137        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
6138            let executor = cx.background_executor().clone();
6139            async move {
6140                executor.timer(Duration::from_millis(100)).await;
6141                Ok(Some(vec![lsp::TextEdit {
6142                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
6143                    new_text: "\n    ".into(),
6144                }]))
6145            }
6146        });
6147
6148    // Submit a format request.
6149    let format_1 = cx
6150        .update_editor(|editor, cx| editor.format(&Format, cx))
6151        .unwrap();
6152    cx.executor().run_until_parked();
6153
6154    // Submit a second format request.
6155    let format_2 = cx
6156        .update_editor(|editor, cx| editor.format(&Format, cx))
6157        .unwrap();
6158    cx.executor().run_until_parked();
6159
6160    // Wait for both format requests to complete
6161    cx.executor().advance_clock(Duration::from_millis(200));
6162    cx.executor().start_waiting();
6163    format_1.await.unwrap();
6164    cx.executor().start_waiting();
6165    format_2.await.unwrap();
6166
6167    // The formatting edits only happens once.
6168    cx.assert_editor_state(indoc! {"
6169        one
6170            .twoˇ
6171    "});
6172}
6173
6174#[gpui::test]
6175async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
6176    init_test(cx, |settings| {
6177        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
6178    });
6179
6180    let mut cx = EditorLspTestContext::new_rust(
6181        lsp::ServerCapabilities {
6182            document_formatting_provider: Some(lsp::OneOf::Left(true)),
6183            ..Default::default()
6184        },
6185        cx,
6186    )
6187    .await;
6188
6189    // Set up a buffer white some trailing whitespace and no trailing newline.
6190    cx.set_state(
6191        &[
6192            "one ",   //
6193            "twoˇ",   //
6194            "three ", //
6195            "four",   //
6196        ]
6197        .join("\n"),
6198    );
6199
6200    // Submit a format request.
6201    let format = cx
6202        .update_editor(|editor, cx| editor.format(&Format, cx))
6203        .unwrap();
6204
6205    // Record which buffer changes have been sent to the language server
6206    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
6207    cx.lsp
6208        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
6209            let buffer_changes = buffer_changes.clone();
6210            move |params, _| {
6211                buffer_changes.lock().extend(
6212                    params
6213                        .content_changes
6214                        .into_iter()
6215                        .map(|e| (e.range.unwrap(), e.text)),
6216                );
6217            }
6218        });
6219
6220    // Handle formatting requests to the language server.
6221    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
6222        let buffer_changes = buffer_changes.clone();
6223        move |_, _| {
6224            // When formatting is requested, trailing whitespace has already been stripped,
6225            // and the trailing newline has already been added.
6226            assert_eq!(
6227                &buffer_changes.lock()[1..],
6228                &[
6229                    (
6230                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
6231                        "".into()
6232                    ),
6233                    (
6234                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
6235                        "".into()
6236                    ),
6237                    (
6238                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
6239                        "\n".into()
6240                    ),
6241                ]
6242            );
6243
6244            // Insert blank lines between each line of the buffer.
6245            async move {
6246                Ok(Some(vec![
6247                    lsp::TextEdit {
6248                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
6249                        new_text: "\n".into(),
6250                    },
6251                    lsp::TextEdit {
6252                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
6253                        new_text: "\n".into(),
6254                    },
6255                ]))
6256            }
6257        }
6258    });
6259
6260    // After formatting the buffer, the trailing whitespace is stripped,
6261    // a newline is appended, and the edits provided by the language server
6262    // have been applied.
6263    format.await.unwrap();
6264    cx.assert_editor_state(
6265        &[
6266            "one",   //
6267            "",      //
6268            "twoˇ",  //
6269            "",      //
6270            "three", //
6271            "four",  //
6272            "",      //
6273        ]
6274        .join("\n"),
6275    );
6276
6277    // Undoing the formatting undoes the trailing whitespace removal, the
6278    // trailing newline, and the LSP edits.
6279    cx.update_buffer(|buffer, cx| buffer.undo(cx));
6280    cx.assert_editor_state(
6281        &[
6282            "one ",   //
6283            "twoˇ",   //
6284            "three ", //
6285            "four",   //
6286        ]
6287        .join("\n"),
6288    );
6289}
6290
6291#[gpui::test]
6292async fn test_completion(cx: &mut gpui::TestAppContext) {
6293    init_test(cx, |_| {});
6294
6295    let mut cx = EditorLspTestContext::new_rust(
6296        lsp::ServerCapabilities {
6297            completion_provider: Some(lsp::CompletionOptions {
6298                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
6299                resolve_provider: Some(true),
6300                ..Default::default()
6301            }),
6302            ..Default::default()
6303        },
6304        cx,
6305    )
6306    .await;
6307
6308    cx.set_state(indoc! {"
6309        oneˇ
6310        two
6311        three
6312    "});
6313    cx.simulate_keystroke(".");
6314    handle_completion_request(
6315        &mut cx,
6316        indoc! {"
6317            one.|<>
6318            two
6319            three
6320        "},
6321        vec!["first_completion", "second_completion"],
6322    )
6323    .await;
6324    cx.condition(|editor, _| editor.context_menu_visible())
6325        .await;
6326    let apply_additional_edits = cx.update_editor(|editor, cx| {
6327        editor.context_menu_next(&Default::default(), cx);
6328        editor
6329            .confirm_completion(&ConfirmCompletion::default(), cx)
6330            .unwrap()
6331    });
6332    cx.assert_editor_state(indoc! {"
6333        one.second_completionˇ
6334        two
6335        three
6336    "});
6337
6338    handle_resolve_completion_request(
6339        &mut cx,
6340        Some(vec![
6341            (
6342                //This overlaps with the primary completion edit which is
6343                //misbehavior from the LSP spec, test that we filter it out
6344                indoc! {"
6345                    one.second_ˇcompletion
6346                    two
6347                    threeˇ
6348                "},
6349                "overlapping additional edit",
6350            ),
6351            (
6352                indoc! {"
6353                    one.second_completion
6354                    two
6355                    threeˇ
6356                "},
6357                "\nadditional edit",
6358            ),
6359        ]),
6360    )
6361    .await;
6362    apply_additional_edits.await.unwrap();
6363    cx.assert_editor_state(indoc! {"
6364        one.second_completionˇ
6365        two
6366        three
6367        additional edit
6368    "});
6369
6370    cx.set_state(indoc! {"
6371        one.second_completion
6372        twoˇ
6373        threeˇ
6374        additional edit
6375    "});
6376    cx.simulate_keystroke(" ");
6377    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
6378    cx.simulate_keystroke("s");
6379    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
6380
6381    cx.assert_editor_state(indoc! {"
6382        one.second_completion
6383        two sˇ
6384        three sˇ
6385        additional edit
6386    "});
6387    handle_completion_request(
6388        &mut cx,
6389        indoc! {"
6390            one.second_completion
6391            two s
6392            three <s|>
6393            additional edit
6394        "},
6395        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
6396    )
6397    .await;
6398    cx.condition(|editor, _| editor.context_menu_visible())
6399        .await;
6400
6401    cx.simulate_keystroke("i");
6402
6403    handle_completion_request(
6404        &mut cx,
6405        indoc! {"
6406            one.second_completion
6407            two si
6408            three <si|>
6409            additional edit
6410        "},
6411        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
6412    )
6413    .await;
6414    cx.condition(|editor, _| editor.context_menu_visible())
6415        .await;
6416
6417    let apply_additional_edits = cx.update_editor(|editor, cx| {
6418        editor
6419            .confirm_completion(&ConfirmCompletion::default(), cx)
6420            .unwrap()
6421    });
6422    cx.assert_editor_state(indoc! {"
6423        one.second_completion
6424        two sixth_completionˇ
6425        three sixth_completionˇ
6426        additional edit
6427    "});
6428
6429    handle_resolve_completion_request(&mut cx, None).await;
6430    apply_additional_edits.await.unwrap();
6431
6432    _ = cx.update(|cx| {
6433        cx.update_global::<SettingsStore, _>(|settings, cx| {
6434            settings.update_user_settings::<EditorSettings>(cx, |settings| {
6435                settings.show_completions_on_input = Some(false);
6436            });
6437        })
6438    });
6439    cx.set_state("editorˇ");
6440    cx.simulate_keystroke(".");
6441    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
6442    cx.simulate_keystroke("c");
6443    cx.simulate_keystroke("l");
6444    cx.simulate_keystroke("o");
6445    cx.assert_editor_state("editor.cloˇ");
6446    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
6447    cx.update_editor(|editor, cx| {
6448        editor.show_completions(&ShowCompletions, cx);
6449    });
6450    handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
6451    cx.condition(|editor, _| editor.context_menu_visible())
6452        .await;
6453    let apply_additional_edits = cx.update_editor(|editor, cx| {
6454        editor
6455            .confirm_completion(&ConfirmCompletion::default(), cx)
6456            .unwrap()
6457    });
6458    cx.assert_editor_state("editor.closeˇ");
6459    handle_resolve_completion_request(&mut cx, None).await;
6460    apply_additional_edits.await.unwrap();
6461}
6462
6463#[gpui::test]
6464async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
6465    init_test(cx, |_| {});
6466    let mut cx = EditorTestContext::new(cx).await;
6467    let language = Arc::new(Language::new(
6468        LanguageConfig {
6469            line_comments: vec!["// ".into()],
6470            ..Default::default()
6471        },
6472        Some(tree_sitter_rust::language()),
6473    ));
6474    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
6475
6476    // If multiple selections intersect a line, the line is only toggled once.
6477    cx.set_state(indoc! {"
6478        fn a() {
6479            «//b();
6480            ˇ»// «c();
6481            //ˇ»  d();
6482        }
6483    "});
6484
6485    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
6486
6487    cx.assert_editor_state(indoc! {"
6488        fn a() {
6489            «b();
6490            c();
6491            ˇ» d();
6492        }
6493    "});
6494
6495    // The comment prefix is inserted at the same column for every line in a
6496    // selection.
6497    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
6498
6499    cx.assert_editor_state(indoc! {"
6500        fn a() {
6501            // «b();
6502            // c();
6503            ˇ»//  d();
6504        }
6505    "});
6506
6507    // If a selection ends at the beginning of a line, that line is not toggled.
6508    cx.set_selections_state(indoc! {"
6509        fn a() {
6510            // b();
6511            «// c();
6512        ˇ»    //  d();
6513        }
6514    "});
6515
6516    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
6517
6518    cx.assert_editor_state(indoc! {"
6519        fn a() {
6520            // b();
6521            «c();
6522        ˇ»    //  d();
6523        }
6524    "});
6525
6526    // If a selection span a single line and is empty, the line is toggled.
6527    cx.set_state(indoc! {"
6528        fn a() {
6529            a();
6530            b();
6531        ˇ
6532        }
6533    "});
6534
6535    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
6536
6537    cx.assert_editor_state(indoc! {"
6538        fn a() {
6539            a();
6540            b();
6541        //•ˇ
6542        }
6543    "});
6544
6545    // If a selection span multiple lines, empty lines are not toggled.
6546    cx.set_state(indoc! {"
6547        fn a() {
6548            «a();
6549
6550            c();ˇ»
6551        }
6552    "});
6553
6554    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
6555
6556    cx.assert_editor_state(indoc! {"
6557        fn a() {
6558            // «a();
6559
6560            // c();ˇ»
6561        }
6562    "});
6563}
6564
6565#[gpui::test]
6566async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
6567    init_test(cx, |_| {});
6568
6569    let language = Arc::new(Language::new(
6570        LanguageConfig {
6571            line_comments: vec!["// ".into()],
6572            ..Default::default()
6573        },
6574        Some(tree_sitter_rust::language()),
6575    ));
6576
6577    let mut cx = EditorTestContext::new(cx).await;
6578
6579    cx.language_registry().add(language.clone());
6580    cx.update_buffer(|buffer, cx| {
6581        buffer.set_language(Some(language), cx);
6582    });
6583
6584    let toggle_comments = &ToggleComments {
6585        advance_downwards: true,
6586    };
6587
6588    // Single cursor on one line -> advance
6589    // Cursor moves horizontally 3 characters as well on non-blank line
6590    cx.set_state(indoc!(
6591        "fn a() {
6592             ˇdog();
6593             cat();
6594        }"
6595    ));
6596    cx.update_editor(|editor, cx| {
6597        editor.toggle_comments(toggle_comments, cx);
6598    });
6599    cx.assert_editor_state(indoc!(
6600        "fn a() {
6601             // dog();
6602             catˇ();
6603        }"
6604    ));
6605
6606    // Single selection on one line -> don't advance
6607    cx.set_state(indoc!(
6608        "fn a() {
6609             «dog()ˇ»;
6610             cat();
6611        }"
6612    ));
6613    cx.update_editor(|editor, cx| {
6614        editor.toggle_comments(toggle_comments, cx);
6615    });
6616    cx.assert_editor_state(indoc!(
6617        "fn a() {
6618             // «dog()ˇ»;
6619             cat();
6620        }"
6621    ));
6622
6623    // Multiple cursors on one line -> advance
6624    cx.set_state(indoc!(
6625        "fn a() {
6626             ˇdˇog();
6627             cat();
6628        }"
6629    ));
6630    cx.update_editor(|editor, cx| {
6631        editor.toggle_comments(toggle_comments, cx);
6632    });
6633    cx.assert_editor_state(indoc!(
6634        "fn a() {
6635             // dog();
6636             catˇ(ˇ);
6637        }"
6638    ));
6639
6640    // Multiple cursors on one line, with selection -> don't advance
6641    cx.set_state(indoc!(
6642        "fn a() {
6643             ˇdˇog«()ˇ»;
6644             cat();
6645        }"
6646    ));
6647    cx.update_editor(|editor, cx| {
6648        editor.toggle_comments(toggle_comments, cx);
6649    });
6650    cx.assert_editor_state(indoc!(
6651        "fn a() {
6652             // ˇdˇog«()ˇ»;
6653             cat();
6654        }"
6655    ));
6656
6657    // Single cursor on one line -> advance
6658    // Cursor moves to column 0 on blank line
6659    cx.set_state(indoc!(
6660        "fn a() {
6661             ˇdog();
6662
6663             cat();
6664        }"
6665    ));
6666    cx.update_editor(|editor, cx| {
6667        editor.toggle_comments(toggle_comments, cx);
6668    });
6669    cx.assert_editor_state(indoc!(
6670        "fn a() {
6671             // dog();
6672        ˇ
6673             cat();
6674        }"
6675    ));
6676
6677    // Single cursor on one line -> advance
6678    // Cursor starts and ends at column 0
6679    cx.set_state(indoc!(
6680        "fn a() {
6681         ˇ    dog();
6682             cat();
6683        }"
6684    ));
6685    cx.update_editor(|editor, cx| {
6686        editor.toggle_comments(toggle_comments, cx);
6687    });
6688    cx.assert_editor_state(indoc!(
6689        "fn a() {
6690             // dog();
6691         ˇ    cat();
6692        }"
6693    ));
6694}
6695
6696#[gpui::test]
6697async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
6698    init_test(cx, |_| {});
6699
6700    let mut cx = EditorTestContext::new(cx).await;
6701
6702    let html_language = Arc::new(
6703        Language::new(
6704            LanguageConfig {
6705                name: "HTML".into(),
6706                block_comment: Some(("<!-- ".into(), " -->".into())),
6707                ..Default::default()
6708            },
6709            Some(tree_sitter_html::language()),
6710        )
6711        .with_injection_query(
6712            r#"
6713            (script_element
6714                (raw_text) @content
6715                (#set! "language" "javascript"))
6716            "#,
6717        )
6718        .unwrap(),
6719    );
6720
6721    let javascript_language = Arc::new(Language::new(
6722        LanguageConfig {
6723            name: "JavaScript".into(),
6724            line_comments: vec!["// ".into()],
6725            ..Default::default()
6726        },
6727        Some(tree_sitter_typescript::language_tsx()),
6728    ));
6729
6730    cx.language_registry().add(html_language.clone());
6731    cx.language_registry().add(javascript_language.clone());
6732    cx.update_buffer(|buffer, cx| {
6733        buffer.set_language(Some(html_language), cx);
6734    });
6735
6736    // Toggle comments for empty selections
6737    cx.set_state(
6738        &r#"
6739            <p>A</p>ˇ
6740            <p>B</p>ˇ
6741            <p>C</p>ˇ
6742        "#
6743        .unindent(),
6744    );
6745    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6746    cx.assert_editor_state(
6747        &r#"
6748            <!-- <p>A</p>ˇ -->
6749            <!-- <p>B</p>ˇ -->
6750            <!-- <p>C</p>ˇ -->
6751        "#
6752        .unindent(),
6753    );
6754    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6755    cx.assert_editor_state(
6756        &r#"
6757            <p>A</p>ˇ
6758            <p>B</p>ˇ
6759            <p>C</p>ˇ
6760        "#
6761        .unindent(),
6762    );
6763
6764    // Toggle comments for mixture of empty and non-empty selections, where
6765    // multiple selections occupy a given line.
6766    cx.set_state(
6767        &r#"
6768            <p>A«</p>
6769            <p>ˇ»B</p>ˇ
6770            <p>C«</p>
6771            <p>ˇ»D</p>ˇ
6772        "#
6773        .unindent(),
6774    );
6775
6776    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6777    cx.assert_editor_state(
6778        &r#"
6779            <!-- <p>A«</p>
6780            <p>ˇ»B</p>ˇ -->
6781            <!-- <p>C«</p>
6782            <p>ˇ»D</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            <p>ˇ»D</p>ˇ
6793        "#
6794        .unindent(),
6795    );
6796
6797    // Toggle comments when different languages are active for different
6798    // selections.
6799    cx.set_state(
6800        &r#"
6801            ˇ<script>
6802                ˇvar x = new Y();
6803            ˇ</script>
6804        "#
6805        .unindent(),
6806    );
6807    cx.executor().run_until_parked();
6808    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6809    cx.assert_editor_state(
6810        &r#"
6811            <!-- ˇ<script> -->
6812                // ˇvar x = new Y();
6813            <!-- ˇ</script> -->
6814        "#
6815        .unindent(),
6816    );
6817}
6818
6819#[gpui::test]
6820fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
6821    init_test(cx, |_| {});
6822
6823    let buffer = cx.new_model(|cx| {
6824        Buffer::new(
6825            0,
6826            BufferId::new(cx.entity_id().as_u64()).unwrap(),
6827            sample_text(3, 4, 'a'),
6828        )
6829    });
6830    let multibuffer = cx.new_model(|cx| {
6831        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
6832        multibuffer.push_excerpts(
6833            buffer.clone(),
6834            [
6835                ExcerptRange {
6836                    context: Point::new(0, 0)..Point::new(0, 4),
6837                    primary: None,
6838                },
6839                ExcerptRange {
6840                    context: Point::new(1, 0)..Point::new(1, 4),
6841                    primary: None,
6842                },
6843            ],
6844            cx,
6845        );
6846        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
6847        multibuffer
6848    });
6849
6850    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
6851    _ = view.update(cx, |view, cx| {
6852        assert_eq!(view.text(cx), "aaaa\nbbbb");
6853        view.change_selections(None, cx, |s| {
6854            s.select_ranges([
6855                Point::new(0, 0)..Point::new(0, 0),
6856                Point::new(1, 0)..Point::new(1, 0),
6857            ])
6858        });
6859
6860        view.handle_input("X", cx);
6861        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
6862        assert_eq!(
6863            view.selections.ranges(cx),
6864            [
6865                Point::new(0, 1)..Point::new(0, 1),
6866                Point::new(1, 1)..Point::new(1, 1),
6867            ]
6868        );
6869
6870        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
6871        view.change_selections(None, cx, |s| {
6872            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
6873        });
6874        view.backspace(&Default::default(), cx);
6875        assert_eq!(view.text(cx), "Xa\nbbb");
6876        assert_eq!(
6877            view.selections.ranges(cx),
6878            [Point::new(1, 0)..Point::new(1, 0)]
6879        );
6880
6881        view.change_selections(None, cx, |s| {
6882            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
6883        });
6884        view.backspace(&Default::default(), cx);
6885        assert_eq!(view.text(cx), "X\nbb");
6886        assert_eq!(
6887            view.selections.ranges(cx),
6888            [Point::new(0, 1)..Point::new(0, 1)]
6889        );
6890    });
6891}
6892
6893#[gpui::test]
6894fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
6895    init_test(cx, |_| {});
6896
6897    let markers = vec![('[', ']').into(), ('(', ')').into()];
6898    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
6899        indoc! {"
6900            [aaaa
6901            (bbbb]
6902            cccc)",
6903        },
6904        markers.clone(),
6905    );
6906    let excerpt_ranges = markers.into_iter().map(|marker| {
6907        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
6908        ExcerptRange {
6909            context,
6910            primary: None,
6911        }
6912    });
6913    let buffer = cx.new_model(|cx| {
6914        Buffer::new(
6915            0,
6916            BufferId::new(cx.entity_id().as_u64()).unwrap(),
6917            initial_text,
6918        )
6919    });
6920    let multibuffer = cx.new_model(|cx| {
6921        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
6922        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
6923        multibuffer
6924    });
6925
6926    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
6927    _ = view.update(cx, |view, cx| {
6928        let (expected_text, selection_ranges) = marked_text_ranges(
6929            indoc! {"
6930                aaaa
6931                bˇbbb
6932                bˇbbˇb
6933                cccc"
6934            },
6935            true,
6936        );
6937        assert_eq!(view.text(cx), expected_text);
6938        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
6939
6940        view.handle_input("X", cx);
6941
6942        let (expected_text, expected_selections) = marked_text_ranges(
6943            indoc! {"
6944                aaaa
6945                bXˇbbXb
6946                bXˇbbXˇb
6947                cccc"
6948            },
6949            false,
6950        );
6951        assert_eq!(view.text(cx), expected_text);
6952        assert_eq!(view.selections.ranges(cx), expected_selections);
6953
6954        view.newline(&Newline, cx);
6955        let (expected_text, expected_selections) = marked_text_ranges(
6956            indoc! {"
6957                aaaa
6958                bX
6959                ˇbbX
6960                b
6961                bX
6962                ˇbbX
6963                ˇb
6964                cccc"
6965            },
6966            false,
6967        );
6968        assert_eq!(view.text(cx), expected_text);
6969        assert_eq!(view.selections.ranges(cx), expected_selections);
6970    });
6971}
6972
6973#[gpui::test]
6974fn test_refresh_selections(cx: &mut TestAppContext) {
6975    init_test(cx, |_| {});
6976
6977    let buffer = cx.new_model(|cx| {
6978        Buffer::new(
6979            0,
6980            BufferId::new(cx.entity_id().as_u64()).unwrap(),
6981            sample_text(3, 4, 'a'),
6982        )
6983    });
6984    let mut excerpt1_id = None;
6985    let multibuffer = cx.new_model(|cx| {
6986        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
6987        excerpt1_id = multibuffer
6988            .push_excerpts(
6989                buffer.clone(),
6990                [
6991                    ExcerptRange {
6992                        context: Point::new(0, 0)..Point::new(1, 4),
6993                        primary: None,
6994                    },
6995                    ExcerptRange {
6996                        context: Point::new(1, 0)..Point::new(2, 4),
6997                        primary: None,
6998                    },
6999                ],
7000                cx,
7001            )
7002            .into_iter()
7003            .next();
7004        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
7005        multibuffer
7006    });
7007
7008    let editor = cx.add_window(|cx| {
7009        let mut editor = build_editor(multibuffer.clone(), cx);
7010        let snapshot = editor.snapshot(cx);
7011        editor.change_selections(None, cx, |s| {
7012            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
7013        });
7014        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
7015        assert_eq!(
7016            editor.selections.ranges(cx),
7017            [
7018                Point::new(1, 3)..Point::new(1, 3),
7019                Point::new(2, 1)..Point::new(2, 1),
7020            ]
7021        );
7022        editor
7023    });
7024
7025    // Refreshing selections is a no-op when excerpts haven't changed.
7026    _ = editor.update(cx, |editor, cx| {
7027        editor.change_selections(None, cx, |s| s.refresh());
7028        assert_eq!(
7029            editor.selections.ranges(cx),
7030            [
7031                Point::new(1, 3)..Point::new(1, 3),
7032                Point::new(2, 1)..Point::new(2, 1),
7033            ]
7034        );
7035    });
7036
7037    _ = multibuffer.update(cx, |multibuffer, cx| {
7038        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
7039    });
7040    _ = editor.update(cx, |editor, cx| {
7041        // Removing an excerpt causes the first selection to become degenerate.
7042        assert_eq!(
7043            editor.selections.ranges(cx),
7044            [
7045                Point::new(0, 0)..Point::new(0, 0),
7046                Point::new(0, 1)..Point::new(0, 1)
7047            ]
7048        );
7049
7050        // Refreshing selections will relocate the first selection to the original buffer
7051        // location.
7052        editor.change_selections(None, cx, |s| s.refresh());
7053        assert_eq!(
7054            editor.selections.ranges(cx),
7055            [
7056                Point::new(0, 1)..Point::new(0, 1),
7057                Point::new(0, 3)..Point::new(0, 3)
7058            ]
7059        );
7060        assert!(editor.selections.pending_anchor().is_some());
7061    });
7062}
7063
7064#[gpui::test]
7065fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
7066    init_test(cx, |_| {});
7067
7068    let buffer = cx.new_model(|cx| {
7069        Buffer::new(
7070            0,
7071            BufferId::new(cx.entity_id().as_u64()).unwrap(),
7072            sample_text(3, 4, 'a'),
7073        )
7074    });
7075    let mut excerpt1_id = None;
7076    let multibuffer = cx.new_model(|cx| {
7077        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
7078        excerpt1_id = multibuffer
7079            .push_excerpts(
7080                buffer.clone(),
7081                [
7082                    ExcerptRange {
7083                        context: Point::new(0, 0)..Point::new(1, 4),
7084                        primary: None,
7085                    },
7086                    ExcerptRange {
7087                        context: Point::new(1, 0)..Point::new(2, 4),
7088                        primary: None,
7089                    },
7090                ],
7091                cx,
7092            )
7093            .into_iter()
7094            .next();
7095        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
7096        multibuffer
7097    });
7098
7099    let editor = cx.add_window(|cx| {
7100        let mut editor = build_editor(multibuffer.clone(), cx);
7101        let snapshot = editor.snapshot(cx);
7102        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
7103        assert_eq!(
7104            editor.selections.ranges(cx),
7105            [Point::new(1, 3)..Point::new(1, 3)]
7106        );
7107        editor
7108    });
7109
7110    _ = multibuffer.update(cx, |multibuffer, cx| {
7111        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
7112    });
7113    _ = editor.update(cx, |editor, cx| {
7114        assert_eq!(
7115            editor.selections.ranges(cx),
7116            [Point::new(0, 0)..Point::new(0, 0)]
7117        );
7118
7119        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
7120        editor.change_selections(None, cx, |s| s.refresh());
7121        assert_eq!(
7122            editor.selections.ranges(cx),
7123            [Point::new(0, 3)..Point::new(0, 3)]
7124        );
7125        assert!(editor.selections.pending_anchor().is_some());
7126    });
7127}
7128
7129#[gpui::test]
7130async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
7131    init_test(cx, |_| {});
7132
7133    let language = Arc::new(
7134        Language::new(
7135            LanguageConfig {
7136                brackets: BracketPairConfig {
7137                    pairs: vec![
7138                        BracketPair {
7139                            start: "{".to_string(),
7140                            end: "}".to_string(),
7141                            close: true,
7142                            newline: true,
7143                        },
7144                        BracketPair {
7145                            start: "/* ".to_string(),
7146                            end: " */".to_string(),
7147                            close: true,
7148                            newline: true,
7149                        },
7150                    ],
7151                    ..Default::default()
7152                },
7153                ..Default::default()
7154            },
7155            Some(tree_sitter_rust::language()),
7156        )
7157        .with_indents_query("")
7158        .unwrap(),
7159    );
7160
7161    let text = concat!(
7162        "{   }\n",     //
7163        "  x\n",       //
7164        "  /*   */\n", //
7165        "x\n",         //
7166        "{{} }\n",     //
7167    );
7168
7169    let buffer = cx.new_model(|cx| {
7170        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
7171            .with_language(language, cx)
7172    });
7173    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
7174    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
7175    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
7176        .await;
7177
7178    _ = view.update(cx, |view, cx| {
7179        view.change_selections(None, cx, |s| {
7180            s.select_display_ranges([
7181                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
7182                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
7183                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
7184            ])
7185        });
7186        view.newline(&Newline, cx);
7187
7188        assert_eq!(
7189            view.buffer().read(cx).read(cx).text(),
7190            concat!(
7191                "{ \n",    // Suppress rustfmt
7192                "\n",      //
7193                "}\n",     //
7194                "  x\n",   //
7195                "  /* \n", //
7196                "  \n",    //
7197                "  */\n",  //
7198                "x\n",     //
7199                "{{} \n",  //
7200                "}\n",     //
7201            )
7202        );
7203    });
7204}
7205
7206#[gpui::test]
7207fn test_highlighted_ranges(cx: &mut TestAppContext) {
7208    init_test(cx, |_| {});
7209
7210    let editor = cx.add_window(|cx| {
7211        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
7212        build_editor(buffer.clone(), cx)
7213    });
7214
7215    _ = editor.update(cx, |editor, cx| {
7216        struct Type1;
7217        struct Type2;
7218
7219        let buffer = editor.buffer.read(cx).snapshot(cx);
7220
7221        let anchor_range =
7222            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
7223
7224        editor.highlight_background::<Type1>(
7225            vec![
7226                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
7227                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
7228                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
7229                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
7230            ],
7231            |_| Hsla::red(),
7232            cx,
7233        );
7234        editor.highlight_background::<Type2>(
7235            vec![
7236                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
7237                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
7238                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
7239                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
7240            ],
7241            |_| Hsla::green(),
7242            cx,
7243        );
7244
7245        let snapshot = editor.snapshot(cx);
7246        let mut highlighted_ranges = editor.background_highlights_in_range(
7247            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
7248            &snapshot,
7249            cx.theme().colors(),
7250        );
7251        // Enforce a consistent ordering based on color without relying on the ordering of the
7252        // highlight's `TypeId` which is non-executor.
7253        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
7254        assert_eq!(
7255            highlighted_ranges,
7256            &[
7257                (
7258                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
7259                    Hsla::red(),
7260                ),
7261                (
7262                    DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
7263                    Hsla::red(),
7264                ),
7265                (
7266                    DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
7267                    Hsla::green(),
7268                ),
7269                (
7270                    DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
7271                    Hsla::green(),
7272                ),
7273            ]
7274        );
7275        assert_eq!(
7276            editor.background_highlights_in_range(
7277                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
7278                &snapshot,
7279                cx.theme().colors(),
7280            ),
7281            &[(
7282                DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
7283                Hsla::red(),
7284            )]
7285        );
7286    });
7287}
7288
7289#[gpui::test]
7290async fn test_following(cx: &mut gpui::TestAppContext) {
7291    init_test(cx, |_| {});
7292
7293    let fs = FakeFs::new(cx.executor());
7294    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
7295
7296    let buffer = project.update(cx, |project, cx| {
7297        let buffer = project
7298            .create_buffer(&sample_text(16, 8, 'a'), None, cx)
7299            .unwrap();
7300        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
7301    });
7302    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
7303    let follower = cx.update(|cx| {
7304        cx.open_window(
7305            WindowOptions {
7306                bounds: Some(Bounds::from_corners(
7307                    gpui::Point::new(0.into(), 0.into()),
7308                    gpui::Point::new(10.into(), 80.into()),
7309                )),
7310                ..Default::default()
7311            },
7312            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
7313        )
7314    });
7315
7316    let is_still_following = Rc::new(RefCell::new(true));
7317    let follower_edit_event_count = Rc::new(RefCell::new(0));
7318    let pending_update = Rc::new(RefCell::new(None));
7319    _ = follower.update(cx, {
7320        let update = pending_update.clone();
7321        let is_still_following = is_still_following.clone();
7322        let follower_edit_event_count = follower_edit_event_count.clone();
7323        |_, cx| {
7324            cx.subscribe(
7325                &leader.root_view(cx).unwrap(),
7326                move |_, leader, event, cx| {
7327                    leader
7328                        .read(cx)
7329                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
7330                },
7331            )
7332            .detach();
7333
7334            cx.subscribe(
7335                &follower.root_view(cx).unwrap(),
7336                move |_, _, event: &EditorEvent, _cx| {
7337                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
7338                        *is_still_following.borrow_mut() = false;
7339                    }
7340
7341                    if let EditorEvent::BufferEdited = event {
7342                        *follower_edit_event_count.borrow_mut() += 1;
7343                    }
7344                },
7345            )
7346            .detach();
7347        }
7348    });
7349
7350    // Update the selections only
7351    _ = leader.update(cx, |leader, cx| {
7352        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
7353    });
7354    follower
7355        .update(cx, |follower, cx| {
7356            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
7357        })
7358        .unwrap()
7359        .await
7360        .unwrap();
7361    _ = follower.update(cx, |follower, cx| {
7362        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
7363    });
7364    assert_eq!(*is_still_following.borrow(), true);
7365    assert_eq!(*follower_edit_event_count.borrow(), 0);
7366
7367    // Update the scroll position only
7368    _ = leader.update(cx, |leader, cx| {
7369        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
7370    });
7371    follower
7372        .update(cx, |follower, cx| {
7373            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
7374        })
7375        .unwrap()
7376        .await
7377        .unwrap();
7378    assert_eq!(
7379        follower
7380            .update(cx, |follower, cx| follower.scroll_position(cx))
7381            .unwrap(),
7382        gpui::Point::new(1.5, 3.5)
7383    );
7384    assert_eq!(*is_still_following.borrow(), true);
7385    assert_eq!(*follower_edit_event_count.borrow(), 0);
7386
7387    // Update the selections and scroll position. The follower's scroll position is updated
7388    // via autoscroll, not via the leader's exact scroll position.
7389    _ = leader.update(cx, |leader, cx| {
7390        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
7391        leader.request_autoscroll(Autoscroll::newest(), cx);
7392        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
7393    });
7394    follower
7395        .update(cx, |follower, cx| {
7396            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
7397        })
7398        .unwrap()
7399        .await
7400        .unwrap();
7401    _ = follower.update(cx, |follower, cx| {
7402        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
7403        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
7404    });
7405    assert_eq!(*is_still_following.borrow(), true);
7406
7407    // Creating a pending selection that precedes another selection
7408    _ = leader.update(cx, |leader, cx| {
7409        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
7410        leader.begin_selection(DisplayPoint::new(0, 0), true, 1, 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    _ = follower.update(cx, |follower, cx| {
7420        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
7421    });
7422    assert_eq!(*is_still_following.borrow(), true);
7423
7424    // Extend the pending selection so that it surrounds another selection
7425    _ = leader.update(cx, |leader, cx| {
7426        leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
7427    });
7428    follower
7429        .update(cx, |follower, cx| {
7430            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
7431        })
7432        .unwrap()
7433        .await
7434        .unwrap();
7435    _ = follower.update(cx, |follower, cx| {
7436        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
7437    });
7438
7439    // Scrolling locally breaks the follow
7440    _ = follower.update(cx, |follower, cx| {
7441        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
7442        follower.set_scroll_anchor(
7443            ScrollAnchor {
7444                anchor: top_anchor,
7445                offset: gpui::Point::new(0.0, 0.5),
7446            },
7447            cx,
7448        );
7449    });
7450    assert_eq!(*is_still_following.borrow(), false);
7451}
7452
7453#[gpui::test]
7454async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
7455    init_test(cx, |_| {});
7456
7457    let fs = FakeFs::new(cx.executor());
7458    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
7459    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
7460    let pane = workspace
7461        .update(cx, |workspace, _| workspace.active_pane().clone())
7462        .unwrap();
7463
7464    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
7465
7466    let leader = pane.update(cx, |_, cx| {
7467        let multibuffer = cx.new_model(|_| MultiBuffer::new(0, ReadWrite));
7468        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
7469    });
7470
7471    // Start following the editor when it has no excerpts.
7472    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
7473    let follower_1 = cx
7474        .update_window(*workspace.deref(), |_, cx| {
7475            Editor::from_state_proto(
7476                pane.clone(),
7477                workspace.root_view(cx).unwrap(),
7478                ViewId {
7479                    creator: Default::default(),
7480                    id: 0,
7481                },
7482                &mut state_message,
7483                cx,
7484            )
7485        })
7486        .unwrap()
7487        .unwrap()
7488        .await
7489        .unwrap();
7490
7491    let update_message = Rc::new(RefCell::new(None));
7492    follower_1.update(cx, {
7493        let update = update_message.clone();
7494        |_, cx| {
7495            cx.subscribe(&leader, move |_, leader, event, cx| {
7496                leader
7497                    .read(cx)
7498                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
7499            })
7500            .detach();
7501        }
7502    });
7503
7504    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
7505        (
7506            project
7507                .create_buffer("abc\ndef\nghi\njkl\n", None, cx)
7508                .unwrap(),
7509            project
7510                .create_buffer("mno\npqr\nstu\nvwx\n", None, cx)
7511                .unwrap(),
7512        )
7513    });
7514
7515    // Insert some excerpts.
7516    _ = leader.update(cx, |leader, cx| {
7517        leader.buffer.update(cx, |multibuffer, cx| {
7518            let excerpt_ids = multibuffer.push_excerpts(
7519                buffer_1.clone(),
7520                [
7521                    ExcerptRange {
7522                        context: 1..6,
7523                        primary: None,
7524                    },
7525                    ExcerptRange {
7526                        context: 12..15,
7527                        primary: None,
7528                    },
7529                    ExcerptRange {
7530                        context: 0..3,
7531                        primary: None,
7532                    },
7533                ],
7534                cx,
7535            );
7536            multibuffer.insert_excerpts_after(
7537                excerpt_ids[0],
7538                buffer_2.clone(),
7539                [
7540                    ExcerptRange {
7541                        context: 8..12,
7542                        primary: None,
7543                    },
7544                    ExcerptRange {
7545                        context: 0..6,
7546                        primary: None,
7547                    },
7548                ],
7549                cx,
7550            );
7551        });
7552    });
7553
7554    // Apply the update of adding the excerpts.
7555    follower_1
7556        .update(cx, |follower, cx| {
7557            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
7558        })
7559        .await
7560        .unwrap();
7561    assert_eq!(
7562        follower_1.update(cx, |editor, cx| editor.text(cx)),
7563        leader.update(cx, |editor, cx| editor.text(cx))
7564    );
7565    update_message.borrow_mut().take();
7566
7567    // Start following separately after it already has excerpts.
7568    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
7569    let follower_2 = cx
7570        .update_window(*workspace.deref(), |_, cx| {
7571            Editor::from_state_proto(
7572                pane.clone(),
7573                workspace.root_view(cx).unwrap().clone(),
7574                ViewId {
7575                    creator: Default::default(),
7576                    id: 0,
7577                },
7578                &mut state_message,
7579                cx,
7580            )
7581        })
7582        .unwrap()
7583        .unwrap()
7584        .await
7585        .unwrap();
7586    assert_eq!(
7587        follower_2.update(cx, |editor, cx| editor.text(cx)),
7588        leader.update(cx, |editor, cx| editor.text(cx))
7589    );
7590
7591    // Remove some excerpts.
7592    _ = leader.update(cx, |leader, cx| {
7593        leader.buffer.update(cx, |multibuffer, cx| {
7594            let excerpt_ids = multibuffer.excerpt_ids();
7595            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
7596            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
7597        });
7598    });
7599
7600    // Apply the update of removing the excerpts.
7601    follower_1
7602        .update(cx, |follower, cx| {
7603            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
7604        })
7605        .await
7606        .unwrap();
7607    follower_2
7608        .update(cx, |follower, cx| {
7609            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
7610        })
7611        .await
7612        .unwrap();
7613    update_message.borrow_mut().take();
7614    assert_eq!(
7615        follower_1.update(cx, |editor, cx| editor.text(cx)),
7616        leader.update(cx, |editor, cx| editor.text(cx))
7617    );
7618}
7619
7620#[gpui::test]
7621async fn go_to_prev_overlapping_diagnostic(
7622    executor: BackgroundExecutor,
7623    cx: &mut gpui::TestAppContext,
7624) {
7625    init_test(cx, |_| {});
7626
7627    let mut cx = EditorTestContext::new(cx).await;
7628    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
7629
7630    cx.set_state(indoc! {"
7631        ˇfn func(abc def: i32) -> u32 {
7632        }
7633    "});
7634
7635    _ = cx.update(|cx| {
7636        _ = project.update(cx, |project, cx| {
7637            project
7638                .update_diagnostics(
7639                    LanguageServerId(0),
7640                    lsp::PublishDiagnosticsParams {
7641                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
7642                        version: None,
7643                        diagnostics: vec![
7644                            lsp::Diagnostic {
7645                                range: lsp::Range::new(
7646                                    lsp::Position::new(0, 11),
7647                                    lsp::Position::new(0, 12),
7648                                ),
7649                                severity: Some(lsp::DiagnosticSeverity::ERROR),
7650                                ..Default::default()
7651                            },
7652                            lsp::Diagnostic {
7653                                range: lsp::Range::new(
7654                                    lsp::Position::new(0, 12),
7655                                    lsp::Position::new(0, 15),
7656                                ),
7657                                severity: Some(lsp::DiagnosticSeverity::ERROR),
7658                                ..Default::default()
7659                            },
7660                            lsp::Diagnostic {
7661                                range: lsp::Range::new(
7662                                    lsp::Position::new(0, 25),
7663                                    lsp::Position::new(0, 28),
7664                                ),
7665                                severity: Some(lsp::DiagnosticSeverity::ERROR),
7666                                ..Default::default()
7667                            },
7668                        ],
7669                    },
7670                    &[],
7671                    cx,
7672                )
7673                .unwrap()
7674        });
7675    });
7676
7677    executor.run_until_parked();
7678
7679    cx.update_editor(|editor, cx| {
7680        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
7681    });
7682
7683    cx.assert_editor_state(indoc! {"
7684        fn func(abc def: i32) -> ˇu32 {
7685        }
7686    "});
7687
7688    cx.update_editor(|editor, cx| {
7689        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
7690    });
7691
7692    cx.assert_editor_state(indoc! {"
7693        fn func(abc ˇdef: i32) -> u32 {
7694        }
7695    "});
7696
7697    cx.update_editor(|editor, cx| {
7698        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
7699    });
7700
7701    cx.assert_editor_state(indoc! {"
7702        fn func(abcˇ def: i32) -> u32 {
7703        }
7704    "});
7705
7706    cx.update_editor(|editor, cx| {
7707        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
7708    });
7709
7710    cx.assert_editor_state(indoc! {"
7711        fn func(abc def: i32) -> ˇu32 {
7712        }
7713    "});
7714}
7715
7716#[gpui::test]
7717async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
7718    init_test(cx, |_| {});
7719
7720    let mut cx = EditorTestContext::new(cx).await;
7721
7722    let diff_base = r#"
7723        use some::mod;
7724
7725        const A: u32 = 42;
7726
7727        fn main() {
7728            println!("hello");
7729
7730            println!("world");
7731        }
7732        "#
7733    .unindent();
7734
7735    // Edits are modified, removed, modified, added
7736    cx.set_state(
7737        &r#"
7738        use some::modified;
7739
7740        ˇ
7741        fn main() {
7742            println!("hello there");
7743
7744            println!("around the");
7745            println!("world");
7746        }
7747        "#
7748        .unindent(),
7749    );
7750
7751    cx.set_diff_base(Some(&diff_base));
7752    executor.run_until_parked();
7753
7754    cx.update_editor(|editor, cx| {
7755        //Wrap around the bottom of the buffer
7756        for _ in 0..3 {
7757            editor.go_to_hunk(&GoToHunk, cx);
7758        }
7759    });
7760
7761    cx.assert_editor_state(
7762        &r#"
7763        ˇuse some::modified;
7764
7765
7766        fn main() {
7767            println!("hello there");
7768
7769            println!("around the");
7770            println!("world");
7771        }
7772        "#
7773        .unindent(),
7774    );
7775
7776    cx.update_editor(|editor, cx| {
7777        //Wrap around the top of the buffer
7778        for _ in 0..2 {
7779            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
7780        }
7781    });
7782
7783    cx.assert_editor_state(
7784        &r#"
7785        use some::modified;
7786
7787
7788        fn main() {
7789        ˇ    println!("hello there");
7790
7791            println!("around the");
7792            println!("world");
7793        }
7794        "#
7795        .unindent(),
7796    );
7797
7798    cx.update_editor(|editor, cx| {
7799        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
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        for _ in 0..3 {
7819            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
7820        }
7821    });
7822
7823    cx.assert_editor_state(
7824        &r#"
7825        use some::modified;
7826
7827
7828        fn main() {
7829        ˇ    println!("hello there");
7830
7831            println!("around the");
7832            println!("world");
7833        }
7834        "#
7835        .unindent(),
7836    );
7837
7838    cx.update_editor(|editor, cx| {
7839        editor.fold(&Fold, cx);
7840
7841        //Make sure that the fold only gets one hunk
7842        for _ in 0..4 {
7843            editor.go_to_hunk(&GoToHunk, cx);
7844        }
7845    });
7846
7847    cx.assert_editor_state(
7848        &r#"
7849        ˇuse some::modified;
7850
7851
7852        fn main() {
7853            println!("hello there");
7854
7855            println!("around the");
7856            println!("world");
7857        }
7858        "#
7859        .unindent(),
7860    );
7861}
7862
7863#[test]
7864fn test_split_words() {
7865    fn split(text: &str) -> Vec<&str> {
7866        split_words(text).collect()
7867    }
7868
7869    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
7870    assert_eq!(split("hello_world"), &["hello_", "world"]);
7871    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
7872    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
7873    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
7874    assert_eq!(split("helloworld"), &["helloworld"]);
7875
7876    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
7877}
7878
7879#[gpui::test]
7880async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
7881    init_test(cx, |_| {});
7882
7883    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
7884    let mut assert = |before, after| {
7885        let _state_context = cx.set_state(before);
7886        cx.update_editor(|editor, cx| {
7887            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
7888        });
7889        cx.assert_editor_state(after);
7890    };
7891
7892    // Outside bracket jumps to outside of matching bracket
7893    assert("console.logˇ(var);", "console.log(var)ˇ;");
7894    assert("console.log(var)ˇ;", "console.logˇ(var);");
7895
7896    // Inside bracket jumps to inside of matching bracket
7897    assert("console.log(ˇvar);", "console.log(varˇ);");
7898    assert("console.log(varˇ);", "console.log(ˇvar);");
7899
7900    // When outside a bracket and inside, favor jumping to the inside bracket
7901    assert(
7902        "console.log('foo', [1, 2, 3]ˇ);",
7903        "console.log(ˇ'foo', [1, 2, 3]);",
7904    );
7905    assert(
7906        "console.log(ˇ'foo', [1, 2, 3]);",
7907        "console.log('foo', [1, 2, 3]ˇ);",
7908    );
7909
7910    // Bias forward if two options are equally likely
7911    assert(
7912        "let result = curried_fun()ˇ();",
7913        "let result = curried_fun()()ˇ;",
7914    );
7915
7916    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
7917    assert(
7918        indoc! {"
7919            function test() {
7920                console.log('test')ˇ
7921            }"},
7922        indoc! {"
7923            function test() {
7924                console.logˇ('test')
7925            }"},
7926    );
7927}
7928
7929#[gpui::test]
7930async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
7931    init_test(cx, |_| {});
7932
7933    let fs = FakeFs::new(cx.executor());
7934    fs.insert_tree(
7935        "/a",
7936        json!({
7937            "main.rs": "fn main() { let a = 5; }",
7938            "other.rs": "// Test file",
7939        }),
7940    )
7941    .await;
7942    let project = Project::test(fs, ["/a".as_ref()], cx).await;
7943
7944    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
7945    language_registry.add(Arc::new(Language::new(
7946        LanguageConfig {
7947            name: "Rust".into(),
7948            matcher: LanguageMatcher {
7949                path_suffixes: vec!["rs".to_string()],
7950                ..Default::default()
7951            },
7952            brackets: BracketPairConfig {
7953                pairs: vec![BracketPair {
7954                    start: "{".to_string(),
7955                    end: "}".to_string(),
7956                    close: true,
7957                    newline: true,
7958                }],
7959                disabled_scopes_by_bracket_ix: Vec::new(),
7960            },
7961            ..Default::default()
7962        },
7963        Some(tree_sitter_rust::language()),
7964    )));
7965    let mut fake_servers = language_registry.register_fake_lsp_adapter(
7966        "Rust",
7967        FakeLspAdapter {
7968            capabilities: lsp::ServerCapabilities {
7969                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
7970                    first_trigger_character: "{".to_string(),
7971                    more_trigger_character: None,
7972                }),
7973                ..Default::default()
7974            },
7975            ..Default::default()
7976        },
7977    );
7978
7979    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
7980
7981    let cx = &mut VisualTestContext::from_window(*workspace, cx);
7982
7983    let worktree_id = workspace
7984        .update(cx, |workspace, cx| {
7985            workspace.project().update(cx, |project, cx| {
7986                project.worktrees().next().unwrap().read(cx).id()
7987            })
7988        })
7989        .unwrap();
7990
7991    let buffer = project
7992        .update(cx, |project, cx| {
7993            project.open_local_buffer("/a/main.rs", cx)
7994        })
7995        .await
7996        .unwrap();
7997    cx.executor().run_until_parked();
7998    cx.executor().start_waiting();
7999    let fake_server = fake_servers.next().await.unwrap();
8000    let editor_handle = workspace
8001        .update(cx, |workspace, cx| {
8002            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
8003        })
8004        .unwrap()
8005        .await
8006        .unwrap()
8007        .downcast::<Editor>()
8008        .unwrap();
8009
8010    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
8011        assert_eq!(
8012            params.text_document_position.text_document.uri,
8013            lsp::Url::from_file_path("/a/main.rs").unwrap(),
8014        );
8015        assert_eq!(
8016            params.text_document_position.position,
8017            lsp::Position::new(0, 21),
8018        );
8019
8020        Ok(Some(vec![lsp::TextEdit {
8021            new_text: "]".to_string(),
8022            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
8023        }]))
8024    });
8025
8026    editor_handle.update(cx, |editor, cx| {
8027        editor.focus(cx);
8028        editor.change_selections(None, cx, |s| {
8029            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
8030        });
8031        editor.handle_input("{", cx);
8032    });
8033
8034    cx.executor().run_until_parked();
8035
8036    _ = buffer.update(cx, |buffer, _| {
8037        assert_eq!(
8038            buffer.text(),
8039            "fn main() { let a = {5}; }",
8040            "No extra braces from on type formatting should appear in the buffer"
8041        )
8042    });
8043}
8044
8045#[gpui::test]
8046async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
8047    init_test(cx, |_| {});
8048
8049    let fs = FakeFs::new(cx.executor());
8050    fs.insert_tree(
8051        "/a",
8052        json!({
8053            "main.rs": "fn main() { let a = 5; }",
8054            "other.rs": "// Test file",
8055        }),
8056    )
8057    .await;
8058
8059    let project = Project::test(fs, ["/a".as_ref()], cx).await;
8060
8061    let server_restarts = Arc::new(AtomicUsize::new(0));
8062    let closure_restarts = Arc::clone(&server_restarts);
8063    let language_server_name = "test language server";
8064    let language_name: Arc<str> = "Rust".into();
8065
8066    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
8067    language_registry.add(Arc::new(Language::new(
8068        LanguageConfig {
8069            name: Arc::clone(&language_name),
8070            matcher: LanguageMatcher {
8071                path_suffixes: vec!["rs".to_string()],
8072                ..Default::default()
8073            },
8074            ..Default::default()
8075        },
8076        Some(tree_sitter_rust::language()),
8077    )));
8078    let mut fake_servers = language_registry.register_fake_lsp_adapter(
8079        "Rust",
8080        FakeLspAdapter {
8081            name: language_server_name,
8082            initialization_options: Some(json!({
8083                "testOptionValue": true
8084            })),
8085            initializer: Some(Box::new(move |fake_server| {
8086                let task_restarts = Arc::clone(&closure_restarts);
8087                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
8088                    task_restarts.fetch_add(1, atomic::Ordering::Release);
8089                    futures::future::ready(Ok(()))
8090                });
8091            })),
8092            ..Default::default()
8093        },
8094    );
8095
8096    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
8097    let _buffer = project
8098        .update(cx, |project, cx| {
8099            project.open_local_buffer("/a/main.rs", cx)
8100        })
8101        .await
8102        .unwrap();
8103    let _fake_server = fake_servers.next().await.unwrap();
8104    update_test_language_settings(cx, |language_settings| {
8105        language_settings.languages.insert(
8106            Arc::clone(&language_name),
8107            LanguageSettingsContent {
8108                tab_size: NonZeroU32::new(8),
8109                ..Default::default()
8110            },
8111        );
8112    });
8113    cx.executor().run_until_parked();
8114    assert_eq!(
8115        server_restarts.load(atomic::Ordering::Acquire),
8116        0,
8117        "Should not restart LSP server on an unrelated change"
8118    );
8119
8120    update_test_project_settings(cx, |project_settings| {
8121        project_settings.lsp.insert(
8122            "Some other server name".into(),
8123            LspSettings {
8124                binary: None,
8125                settings: None,
8126                initialization_options: Some(json!({
8127                    "some other init value": false
8128                })),
8129            },
8130        );
8131    });
8132    cx.executor().run_until_parked();
8133    assert_eq!(
8134        server_restarts.load(atomic::Ordering::Acquire),
8135        0,
8136        "Should not restart LSP server on an unrelated LSP settings change"
8137    );
8138
8139    update_test_project_settings(cx, |project_settings| {
8140        project_settings.lsp.insert(
8141            language_server_name.into(),
8142            LspSettings {
8143                binary: None,
8144                settings: None,
8145                initialization_options: Some(json!({
8146                    "anotherInitValue": false
8147                })),
8148            },
8149        );
8150    });
8151    cx.executor().run_until_parked();
8152    assert_eq!(
8153        server_restarts.load(atomic::Ordering::Acquire),
8154        1,
8155        "Should restart LSP server on a related LSP settings change"
8156    );
8157
8158    update_test_project_settings(cx, |project_settings| {
8159        project_settings.lsp.insert(
8160            language_server_name.into(),
8161            LspSettings {
8162                binary: None,
8163                settings: None,
8164                initialization_options: Some(json!({
8165                    "anotherInitValue": false
8166                })),
8167            },
8168        );
8169    });
8170    cx.executor().run_until_parked();
8171    assert_eq!(
8172        server_restarts.load(atomic::Ordering::Acquire),
8173        1,
8174        "Should not restart LSP server on a related LSP settings change that is the same"
8175    );
8176
8177    update_test_project_settings(cx, |project_settings| {
8178        project_settings.lsp.insert(
8179            language_server_name.into(),
8180            LspSettings {
8181                binary: None,
8182                settings: None,
8183                initialization_options: None,
8184            },
8185        );
8186    });
8187    cx.executor().run_until_parked();
8188    assert_eq!(
8189        server_restarts.load(atomic::Ordering::Acquire),
8190        2,
8191        "Should restart LSP server on another related LSP settings change"
8192    );
8193}
8194
8195#[gpui::test]
8196async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
8197    init_test(cx, |_| {});
8198
8199    let mut cx = EditorLspTestContext::new_rust(
8200        lsp::ServerCapabilities {
8201            completion_provider: Some(lsp::CompletionOptions {
8202                trigger_characters: Some(vec![".".to_string()]),
8203                resolve_provider: Some(true),
8204                ..Default::default()
8205            }),
8206            ..Default::default()
8207        },
8208        cx,
8209    )
8210    .await;
8211
8212    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
8213    cx.simulate_keystroke(".");
8214    let completion_item = lsp::CompletionItem {
8215        label: "some".into(),
8216        kind: Some(lsp::CompletionItemKind::SNIPPET),
8217        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
8218        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
8219            kind: lsp::MarkupKind::Markdown,
8220            value: "```rust\nSome(2)\n```".to_string(),
8221        })),
8222        deprecated: Some(false),
8223        sort_text: Some("fffffff2".to_string()),
8224        filter_text: Some("some".to_string()),
8225        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
8226        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
8227            range: lsp::Range {
8228                start: lsp::Position {
8229                    line: 0,
8230                    character: 22,
8231                },
8232                end: lsp::Position {
8233                    line: 0,
8234                    character: 22,
8235                },
8236            },
8237            new_text: "Some(2)".to_string(),
8238        })),
8239        additional_text_edits: Some(vec![lsp::TextEdit {
8240            range: lsp::Range {
8241                start: lsp::Position {
8242                    line: 0,
8243                    character: 20,
8244                },
8245                end: lsp::Position {
8246                    line: 0,
8247                    character: 22,
8248                },
8249            },
8250            new_text: "".to_string(),
8251        }]),
8252        ..Default::default()
8253    };
8254
8255    let closure_completion_item = completion_item.clone();
8256    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
8257        let task_completion_item = closure_completion_item.clone();
8258        async move {
8259            Ok(Some(lsp::CompletionResponse::Array(vec![
8260                task_completion_item,
8261            ])))
8262        }
8263    });
8264
8265    request.next().await;
8266
8267    cx.condition(|editor, _| editor.context_menu_visible())
8268        .await;
8269    let apply_additional_edits = cx.update_editor(|editor, cx| {
8270        editor
8271            .confirm_completion(&ConfirmCompletion::default(), cx)
8272            .unwrap()
8273    });
8274    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
8275
8276    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
8277        let task_completion_item = completion_item.clone();
8278        async move { Ok(task_completion_item) }
8279    })
8280    .next()
8281    .await
8282    .unwrap();
8283    apply_additional_edits.await.unwrap();
8284    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
8285}
8286
8287#[gpui::test]
8288async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
8289    init_test(cx, |_| {});
8290
8291    let mut cx = EditorLspTestContext::new(
8292        Language::new(
8293            LanguageConfig {
8294                matcher: LanguageMatcher {
8295                    path_suffixes: vec!["jsx".into()],
8296                    ..Default::default()
8297                },
8298                overrides: [(
8299                    "element".into(),
8300                    LanguageConfigOverride {
8301                        word_characters: Override::Set(['-'].into_iter().collect()),
8302                        ..Default::default()
8303                    },
8304                )]
8305                .into_iter()
8306                .collect(),
8307                ..Default::default()
8308            },
8309            Some(tree_sitter_typescript::language_tsx()),
8310        )
8311        .with_override_query("(jsx_self_closing_element) @element")
8312        .unwrap(),
8313        lsp::ServerCapabilities {
8314            completion_provider: Some(lsp::CompletionOptions {
8315                trigger_characters: Some(vec![":".to_string()]),
8316                ..Default::default()
8317            }),
8318            ..Default::default()
8319        },
8320        cx,
8321    )
8322    .await;
8323
8324    cx.lsp
8325        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
8326            Ok(Some(lsp::CompletionResponse::Array(vec![
8327                lsp::CompletionItem {
8328                    label: "bg-blue".into(),
8329                    ..Default::default()
8330                },
8331                lsp::CompletionItem {
8332                    label: "bg-red".into(),
8333                    ..Default::default()
8334                },
8335                lsp::CompletionItem {
8336                    label: "bg-yellow".into(),
8337                    ..Default::default()
8338                },
8339            ])))
8340        });
8341
8342    cx.set_state(r#"<p class="bgˇ" />"#);
8343
8344    // Trigger completion when typing a dash, because the dash is an extra
8345    // word character in the 'element' scope, which contains the cursor.
8346    cx.simulate_keystroke("-");
8347    cx.executor().run_until_parked();
8348    cx.update_editor(|editor, _| {
8349        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
8350            assert_eq!(
8351                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
8352                &["bg-red", "bg-blue", "bg-yellow"]
8353            );
8354        } else {
8355            panic!("expected completion menu to be open");
8356        }
8357    });
8358
8359    cx.simulate_keystroke("l");
8360    cx.executor().run_until_parked();
8361    cx.update_editor(|editor, _| {
8362        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
8363            assert_eq!(
8364                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
8365                &["bg-blue", "bg-yellow"]
8366            );
8367        } else {
8368            panic!("expected completion menu to be open");
8369        }
8370    });
8371
8372    // When filtering completions, consider the character after the '-' to
8373    // be the start of a subword.
8374    cx.set_state(r#"<p class="yelˇ" />"#);
8375    cx.simulate_keystroke("l");
8376    cx.executor().run_until_parked();
8377    cx.update_editor(|editor, _| {
8378        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
8379            assert_eq!(
8380                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
8381                &["bg-yellow"]
8382            );
8383        } else {
8384            panic!("expected completion menu to be open");
8385        }
8386    });
8387}
8388
8389#[gpui::test]
8390async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
8391    init_test(cx, |settings| {
8392        settings.defaults.formatter = Some(language_settings::Formatter::Prettier)
8393    });
8394
8395    let fs = FakeFs::new(cx.executor());
8396    fs.insert_file("/file.rs", Default::default()).await;
8397
8398    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
8399    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
8400
8401    language_registry.add(Arc::new(Language::new(
8402        LanguageConfig {
8403            name: "Rust".into(),
8404            matcher: LanguageMatcher {
8405                path_suffixes: vec!["rs".to_string()],
8406                ..Default::default()
8407            },
8408            prettier_parser_name: Some("test_parser".to_string()),
8409            ..Default::default()
8410        },
8411        Some(tree_sitter_rust::language()),
8412    )));
8413
8414    let test_plugin = "test_plugin";
8415    let _ = language_registry.register_fake_lsp_adapter(
8416        "Rust",
8417        FakeLspAdapter {
8418            prettier_plugins: vec![test_plugin],
8419            ..Default::default()
8420        },
8421    );
8422
8423    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
8424    let buffer = project
8425        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
8426        .await
8427        .unwrap();
8428
8429    let buffer_text = "one\ntwo\nthree\n";
8430    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
8431    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
8432    _ = editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
8433
8434    editor
8435        .update(cx, |editor, cx| {
8436            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
8437        })
8438        .unwrap()
8439        .await;
8440    assert_eq!(
8441        editor.update(cx, |editor, cx| editor.text(cx)),
8442        buffer_text.to_string() + prettier_format_suffix,
8443        "Test prettier formatting was not applied to the original buffer text",
8444    );
8445
8446    update_test_language_settings(cx, |settings| {
8447        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
8448    });
8449    let format = editor.update(cx, |editor, cx| {
8450        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
8451    });
8452    format.await.unwrap();
8453    assert_eq!(
8454        editor.update(cx, |editor, cx| editor.text(cx)),
8455        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
8456        "Autoformatting (via test prettier) was not applied to the original buffer text",
8457    );
8458}
8459
8460#[gpui::test]
8461async fn test_find_all_references(cx: &mut gpui::TestAppContext) {
8462    init_test(cx, |_| {});
8463
8464    let mut cx = EditorLspTestContext::new_rust(
8465        lsp::ServerCapabilities {
8466            document_formatting_provider: Some(lsp::OneOf::Left(true)),
8467            ..Default::default()
8468        },
8469        cx,
8470    )
8471    .await;
8472
8473    cx.set_state(indoc! {"
8474        fn foo(«paramˇ»: i64) {
8475            println!(param);
8476        }
8477    "});
8478
8479    cx.lsp
8480        .handle_request::<lsp::request::References, _, _>(move |_, _| async move {
8481            Ok(Some(vec![
8482                lsp::Location {
8483                    uri: lsp::Url::from_file_path("/root/dir/file.rs").unwrap(),
8484                    range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 12)),
8485                },
8486                lsp::Location {
8487                    uri: lsp::Url::from_file_path("/root/dir/file.rs").unwrap(),
8488                    range: lsp::Range::new(lsp::Position::new(1, 13), lsp::Position::new(1, 18)),
8489                },
8490            ]))
8491        });
8492
8493    let references = cx
8494        .update_editor(|editor, cx| editor.find_all_references(&FindAllReferences, cx))
8495        .unwrap();
8496
8497    cx.executor().run_until_parked();
8498
8499    cx.executor().start_waiting();
8500    references.await.unwrap();
8501
8502    cx.assert_editor_state(indoc! {"
8503        fn foo(param: i64) {
8504            println!(«paramˇ»);
8505        }
8506    "});
8507
8508    let references = cx
8509        .update_editor(|editor, cx| editor.find_all_references(&FindAllReferences, cx))
8510        .unwrap();
8511
8512    cx.executor().run_until_parked();
8513
8514    cx.executor().start_waiting();
8515    references.await.unwrap();
8516
8517    cx.assert_editor_state(indoc! {"
8518        fn foo(«paramˇ»: i64) {
8519            println!(param);
8520        }
8521    "});
8522
8523    cx.set_state(indoc! {"
8524        fn foo(param: i64) {
8525            let a = param;
8526            let aˇ = param;
8527            let a = param;
8528            println!(param);
8529        }
8530    "});
8531
8532    cx.lsp
8533        .handle_request::<lsp::request::References, _, _>(move |_, _| async move {
8534            Ok(Some(vec![lsp::Location {
8535                uri: lsp::Url::from_file_path("/root/dir/file.rs").unwrap(),
8536                range: lsp::Range::new(lsp::Position::new(2, 8), lsp::Position::new(2, 9)),
8537            }]))
8538        });
8539
8540    let references = cx
8541        .update_editor(|editor, cx| editor.find_all_references(&FindAllReferences, cx))
8542        .unwrap();
8543
8544    cx.executor().run_until_parked();
8545
8546    cx.executor().start_waiting();
8547    references.await.unwrap();
8548
8549    cx.assert_editor_state(indoc! {"
8550        fn foo(param: i64) {
8551            let a = param;
8552            let «aˇ» = param;
8553            let a = param;
8554            println!(param);
8555        }
8556    "});
8557}
8558
8559#[gpui::test]
8560async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
8561    init_test(cx, |_| {});
8562    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
8563    let base_text = indoc! {r#"struct Row;
8564struct Row1;
8565struct Row2;
8566
8567struct Row4;
8568struct Row5;
8569struct Row6;
8570
8571struct Row8;
8572struct Row9;
8573struct Row10;"#};
8574
8575    // When addition hunks are not adjacent to carets, no hunk revert is performed
8576    assert_hunk_revert(
8577        indoc! {r#"struct Row;
8578                   struct Row1;
8579                   struct Row1.1;
8580                   struct Row1.2;
8581                   struct Row2;ˇ
8582
8583                   struct Row4;
8584                   struct Row5;
8585                   struct Row6;
8586
8587                   struct Row8;
8588                   ˇstruct Row9;
8589                   struct Row9.1;
8590                   struct Row9.2;
8591                   struct Row9.3;
8592                   struct Row10;"#},
8593        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
8594        indoc! {r#"struct Row;
8595                   struct Row1;
8596                   struct Row1.1;
8597                   struct Row1.2;
8598                   struct Row2;ˇ
8599
8600                   struct Row4;
8601                   struct Row5;
8602                   struct Row6;
8603
8604                   struct Row8;
8605                   ˇstruct Row9;
8606                   struct Row9.1;
8607                   struct Row9.2;
8608                   struct Row9.3;
8609                   struct Row10;"#},
8610        base_text,
8611        &mut cx,
8612    );
8613    // Same for selections
8614    assert_hunk_revert(
8615        indoc! {r#"struct Row;
8616                   struct Row1;
8617                   struct Row2;
8618                   struct Row2.1;
8619                   struct Row2.2;
8620                   «ˇ
8621                   struct Row4;
8622                   struct» Row5;
8623                   «struct Row6;
8624                   ˇ»
8625                   struct Row9.1;
8626                   struct Row9.2;
8627                   struct Row9.3;
8628                   struct Row8;
8629                   struct Row9;
8630                   struct Row10;"#},
8631        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
8632        indoc! {r#"struct Row;
8633                   struct Row1;
8634                   struct Row2;
8635                   struct Row2.1;
8636                   struct Row2.2;
8637                   «ˇ
8638                   struct Row4;
8639                   struct» Row5;
8640                   «struct Row6;
8641                   ˇ»
8642                   struct Row9.1;
8643                   struct Row9.2;
8644                   struct Row9.3;
8645                   struct Row8;
8646                   struct Row9;
8647                   struct Row10;"#},
8648        base_text,
8649        &mut cx,
8650    );
8651
8652    // When carets and selections intersect the addition hunks, those are reverted.
8653    // Adjacent carets got merged.
8654    assert_hunk_revert(
8655        indoc! {r#"struct Row;
8656                   ˇ// something on the top
8657                   struct Row1;
8658                   struct Row2;
8659                   struct Roˇw3.1;
8660                   struct Row2.2;
8661                   struct Row2.3;ˇ
8662
8663                   struct Row4;
8664                   struct ˇRow5.1;
8665                   struct Row5.2;
8666                   struct «Rowˇ»5.3;
8667                   struct Row5;
8668                   struct Row6;
8669                   ˇ
8670                   struct Row9.1;
8671                   struct «Rowˇ»9.2;
8672                   struct «ˇRow»9.3;
8673                   struct Row8;
8674                   struct Row9;
8675                   «ˇ// something on bottom»
8676                   struct Row10;"#},
8677        vec![
8678            DiffHunkStatus::Added,
8679            DiffHunkStatus::Added,
8680            DiffHunkStatus::Added,
8681            DiffHunkStatus::Added,
8682            DiffHunkStatus::Added,
8683        ],
8684        indoc! {r#"struct Row;
8685                   ˇstruct Row1;
8686                   struct Row2;
8687                   ˇ
8688                   struct Row4;
8689                   ˇstruct Row5;
8690                   struct Row6;
8691                   ˇ
8692                   ˇstruct Row8;
8693                   struct Row9;
8694                   ˇstruct Row10;"#},
8695        base_text,
8696        &mut cx,
8697    );
8698}
8699
8700#[gpui::test]
8701async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
8702    init_test(cx, |_| {});
8703    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
8704    let base_text = indoc! {r#"struct Row;
8705struct Row1;
8706struct Row2;
8707
8708struct Row4;
8709struct Row5;
8710struct Row6;
8711
8712struct Row8;
8713struct Row9;
8714struct Row10;"#};
8715
8716    // Modification hunks behave the same as the addition ones.
8717    assert_hunk_revert(
8718        indoc! {r#"struct Row;
8719                   struct Row1;
8720                   struct Row33;
8721                   ˇ
8722                   struct Row4;
8723                   struct Row5;
8724                   struct Row6;
8725                   ˇ
8726                   struct Row99;
8727                   struct Row9;
8728                   struct Row10;"#},
8729        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
8730        indoc! {r#"struct Row;
8731                   struct Row1;
8732                   struct Row33;
8733                   ˇ
8734                   struct Row4;
8735                   struct Row5;
8736                   struct Row6;
8737                   ˇ
8738                   struct Row99;
8739                   struct Row9;
8740                   struct Row10;"#},
8741        base_text,
8742        &mut cx,
8743    );
8744    assert_hunk_revert(
8745        indoc! {r#"struct Row;
8746                   struct Row1;
8747                   struct Row33;
8748                   «ˇ
8749                   struct Row4;
8750                   struct» Row5;
8751                   «struct Row6;
8752                   ˇ»
8753                   struct Row99;
8754                   struct Row9;
8755                   struct Row10;"#},
8756        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
8757        indoc! {r#"struct Row;
8758                   struct Row1;
8759                   struct Row33;
8760                   «ˇ
8761                   struct Row4;
8762                   struct» Row5;
8763                   «struct Row6;
8764                   ˇ»
8765                   struct Row99;
8766                   struct Row9;
8767                   struct Row10;"#},
8768        base_text,
8769        &mut cx,
8770    );
8771
8772    assert_hunk_revert(
8773        indoc! {r#"ˇstruct Row1.1;
8774                   struct Row1;
8775                   «ˇstr»uct Row22;
8776
8777                   struct ˇRow44;
8778                   struct Row5;
8779                   struct «Rˇ»ow66;ˇ
8780
8781                   «struˇ»ct Row88;
8782                   struct Row9;
8783                   struct Row1011;ˇ"#},
8784        vec![
8785            DiffHunkStatus::Modified,
8786            DiffHunkStatus::Modified,
8787            DiffHunkStatus::Modified,
8788            DiffHunkStatus::Modified,
8789            DiffHunkStatus::Modified,
8790            DiffHunkStatus::Modified,
8791        ],
8792        indoc! {r#"struct Row;
8793                   ˇstruct Row1;
8794                   struct Row2;
8795                   ˇ
8796                   struct Row4;
8797                   ˇstruct Row5;
8798                   struct Row6;
8799                   ˇ
8800                   struct Row8;
8801                   ˇstruct Row9;
8802                   struct Row10;ˇ"#},
8803        base_text,
8804        &mut cx,
8805    );
8806}
8807
8808#[gpui::test]
8809async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
8810    init_test(cx, |_| {});
8811    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
8812    let base_text = indoc! {r#"struct Row;
8813struct Row1;
8814struct Row2;
8815
8816struct Row4;
8817struct Row5;
8818struct Row6;
8819
8820struct Row8;
8821struct Row9;
8822struct Row10;"#};
8823
8824    // Deletion hunks trigger with carets on ajacent rows, so carets and selections have to stay farther to avoid the revert
8825    assert_hunk_revert(
8826        indoc! {r#"struct Row;
8827                   struct Row2;
8828
8829                   ˇstruct Row4;
8830                   struct Row5;
8831                   struct Row6;
8832                   ˇ
8833                   struct Row8;
8834                   struct Row10;"#},
8835        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
8836        indoc! {r#"struct Row;
8837                   struct Row2;
8838
8839                   ˇstruct Row4;
8840                   struct Row5;
8841                   struct Row6;
8842                   ˇ
8843                   struct Row8;
8844                   struct Row10;"#},
8845        base_text,
8846        &mut cx,
8847    );
8848    assert_hunk_revert(
8849        indoc! {r#"struct Row;
8850                   struct Row2;
8851
8852                   «ˇstruct Row4;
8853                   struct» Row5;
8854                   «struct Row6;
8855                   ˇ»
8856                   struct Row8;
8857                   struct Row10;"#},
8858        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
8859        indoc! {r#"struct Row;
8860                   struct Row2;
8861
8862                   «ˇstruct Row4;
8863                   struct» Row5;
8864                   «struct Row6;
8865                   ˇ»
8866                   struct Row8;
8867                   struct Row10;"#},
8868        base_text,
8869        &mut cx,
8870    );
8871
8872    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
8873    assert_hunk_revert(
8874        indoc! {r#"struct Row;
8875                   ˇstruct Row2;
8876
8877                   struct Row4;
8878                   struct Row5;
8879                   struct Row6;
8880
8881                   struct Row8;ˇ
8882                   struct Row10;"#},
8883        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
8884        indoc! {r#"struct Row;
8885                   struct Row1;
8886                   ˇstruct Row2;
8887
8888                   struct Row4;
8889                   struct Row5;
8890                   struct Row6;
8891
8892                   struct Row8;ˇ
8893                   struct Row9;
8894                   struct Row10;"#},
8895        base_text,
8896        &mut cx,
8897    );
8898    assert_hunk_revert(
8899        indoc! {r#"struct Row;
8900                   struct Row2«ˇ;
8901                   struct Row4;
8902                   struct» Row5;
8903                   «struct Row6;
8904
8905                   struct Row8;ˇ»
8906                   struct Row10;"#},
8907        vec![
8908            DiffHunkStatus::Removed,
8909            DiffHunkStatus::Removed,
8910            DiffHunkStatus::Removed,
8911        ],
8912        indoc! {r#"struct Row;
8913                   struct Row1;
8914                   struct Row2«ˇ;
8915
8916                   struct Row4;
8917                   struct» Row5;
8918                   «struct Row6;
8919
8920                   struct Row8;ˇ»
8921                   struct Row9;
8922                   struct Row10;"#},
8923        base_text,
8924        &mut cx,
8925    );
8926}
8927
8928#[gpui::test]
8929async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
8930    init_test(cx, |_| {});
8931
8932    let cols = 4;
8933    let rows = 10;
8934    let sample_text_1 = sample_text(rows, cols, 'a');
8935    assert_eq!(
8936        sample_text_1,
8937        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
8938    );
8939    let sample_text_2 = sample_text(rows, cols, 'l');
8940    assert_eq!(
8941        sample_text_2,
8942        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
8943    );
8944    let sample_text_3 = sample_text(rows, cols, 'v');
8945    assert_eq!(
8946        sample_text_3,
8947        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
8948    );
8949
8950    fn diff_every_buffer_row(
8951        buffer: &Model<Buffer>,
8952        sample_text: String,
8953        cols: usize,
8954        cx: &mut gpui::TestAppContext,
8955    ) {
8956        // revert first character in each row, creating one large diff hunk per buffer
8957        let is_first_char = |offset: usize| offset % cols == 0;
8958        buffer.update(cx, |buffer, cx| {
8959            buffer.set_text(
8960                sample_text
8961                    .chars()
8962                    .enumerate()
8963                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
8964                    .collect::<String>(),
8965                cx,
8966            );
8967            buffer.set_diff_base(Some(sample_text), cx);
8968        });
8969        cx.executor().run_until_parked();
8970    }
8971
8972    let buffer_1 = cx.new_model(|cx| {
8973        Buffer::new(
8974            0,
8975            BufferId::new(cx.entity_id().as_u64()).unwrap(),
8976            sample_text_1.clone(),
8977        )
8978    });
8979    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
8980
8981    let buffer_2 = cx.new_model(|cx| {
8982        Buffer::new(
8983            1,
8984            BufferId::new(cx.entity_id().as_u64() + 1).unwrap(),
8985            sample_text_2.clone(),
8986        )
8987    });
8988    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
8989
8990    let buffer_3 = cx.new_model(|cx| {
8991        Buffer::new(
8992            2,
8993            BufferId::new(cx.entity_id().as_u64() + 2).unwrap(),
8994            sample_text_3.clone(),
8995        )
8996    });
8997    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
8998
8999    let multibuffer = cx.new_model(|cx| {
9000        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
9001        multibuffer.push_excerpts(
9002            buffer_1.clone(),
9003            [
9004                ExcerptRange {
9005                    context: Point::new(0, 0)..Point::new(3, 0),
9006                    primary: None,
9007                },
9008                ExcerptRange {
9009                    context: Point::new(5, 0)..Point::new(7, 0),
9010                    primary: None,
9011                },
9012                ExcerptRange {
9013                    context: Point::new(9, 0)..Point::new(10, 4),
9014                    primary: None,
9015                },
9016            ],
9017            cx,
9018        );
9019        multibuffer.push_excerpts(
9020            buffer_2.clone(),
9021            [
9022                ExcerptRange {
9023                    context: Point::new(0, 0)..Point::new(3, 0),
9024                    primary: None,
9025                },
9026                ExcerptRange {
9027                    context: Point::new(5, 0)..Point::new(7, 0),
9028                    primary: None,
9029                },
9030                ExcerptRange {
9031                    context: Point::new(9, 0)..Point::new(10, 4),
9032                    primary: None,
9033                },
9034            ],
9035            cx,
9036        );
9037        multibuffer.push_excerpts(
9038            buffer_3.clone(),
9039            [
9040                ExcerptRange {
9041                    context: Point::new(0, 0)..Point::new(3, 0),
9042                    primary: None,
9043                },
9044                ExcerptRange {
9045                    context: Point::new(5, 0)..Point::new(7, 0),
9046                    primary: None,
9047                },
9048                ExcerptRange {
9049                    context: Point::new(9, 0)..Point::new(10, 4),
9050                    primary: None,
9051                },
9052            ],
9053            cx,
9054        );
9055        multibuffer
9056    });
9057
9058    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
9059    editor.update(cx, |editor, cx| {
9060        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");
9061        editor.select_all(&SelectAll, cx);
9062        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
9063    });
9064    cx.executor().run_until_parked();
9065    // When all ranges are selected, all buffer hunks are reverted.
9066    editor.update(cx, |editor, cx| {
9067        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");
9068    });
9069    buffer_1.update(cx, |buffer, _| {
9070        assert_eq!(buffer.text(), sample_text_1);
9071    });
9072    buffer_2.update(cx, |buffer, _| {
9073        assert_eq!(buffer.text(), sample_text_2);
9074    });
9075    buffer_3.update(cx, |buffer, _| {
9076        assert_eq!(buffer.text(), sample_text_3);
9077    });
9078
9079    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
9080    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
9081    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
9082    editor.update(cx, |editor, cx| {
9083        editor.change_selections(None, cx, |s| {
9084            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
9085        });
9086        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
9087    });
9088    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
9089    // but not affect buffer_2 and its related excerpts.
9090    editor.update(cx, |editor, cx| {
9091        assert_eq!(
9092            editor.text(cx),
9093            "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"
9094        );
9095    });
9096    buffer_1.update(cx, |buffer, _| {
9097        assert_eq!(buffer.text(), sample_text_1);
9098    });
9099    buffer_2.update(cx, |buffer, _| {
9100        assert_eq!(
9101            buffer.text(),
9102            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
9103        );
9104    });
9105    buffer_3.update(cx, |buffer, _| {
9106        assert_eq!(
9107            buffer.text(),
9108            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
9109        );
9110    });
9111}
9112
9113#[gpui::test]
9114async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
9115    init_test(cx, |_| {});
9116
9117    let cols = 4;
9118    let rows = 10;
9119    let sample_text_1 = sample_text(rows, cols, 'a');
9120    assert_eq!(
9121        sample_text_1,
9122        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
9123    );
9124    let sample_text_2 = sample_text(rows, cols, 'l');
9125    assert_eq!(
9126        sample_text_2,
9127        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
9128    );
9129    let sample_text_3 = sample_text(rows, cols, 'v');
9130    assert_eq!(
9131        sample_text_3,
9132        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
9133    );
9134
9135    let buffer_1 = cx.new_model(|cx| {
9136        Buffer::new(
9137            0,
9138            BufferId::new(cx.entity_id().as_u64()).unwrap(),
9139            sample_text_1.clone(),
9140        )
9141    });
9142
9143    let buffer_2 = cx.new_model(|cx| {
9144        Buffer::new(
9145            1,
9146            BufferId::new(cx.entity_id().as_u64() + 1).unwrap(),
9147            sample_text_2.clone(),
9148        )
9149    });
9150
9151    let buffer_3 = cx.new_model(|cx| {
9152        Buffer::new(
9153            2,
9154            BufferId::new(cx.entity_id().as_u64() + 2).unwrap(),
9155            sample_text_3.clone(),
9156        )
9157    });
9158
9159    let multi_buffer = cx.new_model(|cx| {
9160        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
9161        multibuffer.push_excerpts(
9162            buffer_1.clone(),
9163            [
9164                ExcerptRange {
9165                    context: Point::new(0, 0)..Point::new(3, 0),
9166                    primary: None,
9167                },
9168                ExcerptRange {
9169                    context: Point::new(5, 0)..Point::new(7, 0),
9170                    primary: None,
9171                },
9172                ExcerptRange {
9173                    context: Point::new(9, 0)..Point::new(10, 4),
9174                    primary: None,
9175                },
9176            ],
9177            cx,
9178        );
9179        multibuffer.push_excerpts(
9180            buffer_2.clone(),
9181            [
9182                ExcerptRange {
9183                    context: Point::new(0, 0)..Point::new(3, 0),
9184                    primary: None,
9185                },
9186                ExcerptRange {
9187                    context: Point::new(5, 0)..Point::new(7, 0),
9188                    primary: None,
9189                },
9190                ExcerptRange {
9191                    context: Point::new(9, 0)..Point::new(10, 4),
9192                    primary: None,
9193                },
9194            ],
9195            cx,
9196        );
9197        multibuffer.push_excerpts(
9198            buffer_3.clone(),
9199            [
9200                ExcerptRange {
9201                    context: Point::new(0, 0)..Point::new(3, 0),
9202                    primary: None,
9203                },
9204                ExcerptRange {
9205                    context: Point::new(5, 0)..Point::new(7, 0),
9206                    primary: None,
9207                },
9208                ExcerptRange {
9209                    context: Point::new(9, 0)..Point::new(10, 4),
9210                    primary: None,
9211                },
9212            ],
9213            cx,
9214        );
9215        multibuffer
9216    });
9217
9218    let fs = FakeFs::new(cx.executor());
9219    fs.insert_tree(
9220        "/a",
9221        json!({
9222            "main.rs": sample_text_1,
9223            "other.rs": sample_text_2,
9224            "lib.rs": sample_text_3,
9225        }),
9226    )
9227    .await;
9228    let project = Project::test(fs, ["/a".as_ref()], cx).await;
9229    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
9230    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
9231    let multi_buffer_editor =
9232        cx.new_view(|cx| Editor::new(EditorMode::Full, multi_buffer, Some(project.clone()), cx));
9233    let multibuffer_item_id = workspace
9234        .update(cx, |workspace, cx| {
9235            assert!(
9236                workspace.active_item(cx).is_none(),
9237                "active item should be None before the first item is added"
9238            );
9239            workspace.add_item_to_active_pane(Box::new(multi_buffer_editor.clone()), cx);
9240            let active_item = workspace
9241                .active_item(cx)
9242                .expect("should have an active item after adding the multi buffer");
9243            assert!(
9244                !active_item.is_singleton(cx),
9245                "A multi buffer was expected to active after adding"
9246            );
9247            active_item.item_id()
9248        })
9249        .unwrap();
9250    cx.executor().run_until_parked();
9251
9252    multi_buffer_editor.update(cx, |editor, cx| {
9253        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
9254        editor.open_excerpts(&OpenExcerpts, cx);
9255    });
9256    cx.executor().run_until_parked();
9257    let first_item_id = workspace
9258        .update(cx, |workspace, cx| {
9259            let active_item = workspace
9260                .active_item(cx)
9261                .expect("should have an active item after navigating into the 1st buffer");
9262            let first_item_id = active_item.item_id();
9263            assert_ne!(
9264                first_item_id, multibuffer_item_id,
9265                "Should navigate into the 1st buffer and activate it"
9266            );
9267            assert!(
9268                active_item.is_singleton(cx),
9269                "New active item should be a singleton buffer"
9270            );
9271            assert_eq!(
9272                active_item
9273                    .act_as::<Editor>(cx)
9274                    .expect("should have navigated into an editor for the 1st buffer")
9275                    .read(cx)
9276                    .text(cx),
9277                sample_text_1
9278            );
9279
9280            workspace
9281                .go_back(workspace.active_pane().downgrade(), cx)
9282                .detach_and_log_err(cx);
9283
9284            first_item_id
9285        })
9286        .unwrap();
9287    cx.executor().run_until_parked();
9288    workspace
9289        .update(cx, |workspace, cx| {
9290            let active_item = workspace
9291                .active_item(cx)
9292                .expect("should have an active item after navigating back");
9293            assert_eq!(
9294                active_item.item_id(),
9295                multibuffer_item_id,
9296                "Should navigate back to the multi buffer"
9297            );
9298            assert!(!active_item.is_singleton(cx));
9299        })
9300        .unwrap();
9301
9302    multi_buffer_editor.update(cx, |editor, cx| {
9303        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
9304            s.select_ranges(Some(39..40))
9305        });
9306        editor.open_excerpts(&OpenExcerpts, cx);
9307    });
9308    cx.executor().run_until_parked();
9309    let second_item_id = workspace
9310        .update(cx, |workspace, cx| {
9311            let active_item = workspace
9312                .active_item(cx)
9313                .expect("should have an active item after navigating into the 2nd buffer");
9314            let second_item_id = active_item.item_id();
9315            assert_ne!(
9316                second_item_id, multibuffer_item_id,
9317                "Should navigate away from the multibuffer"
9318            );
9319            assert_ne!(
9320                second_item_id, first_item_id,
9321                "Should navigate into the 2nd buffer and activate it"
9322            );
9323            assert!(
9324                active_item.is_singleton(cx),
9325                "New active item should be a singleton buffer"
9326            );
9327            assert_eq!(
9328                active_item
9329                    .act_as::<Editor>(cx)
9330                    .expect("should have navigated into an editor")
9331                    .read(cx)
9332                    .text(cx),
9333                sample_text_2
9334            );
9335
9336            workspace
9337                .go_back(workspace.active_pane().downgrade(), cx)
9338                .detach_and_log_err(cx);
9339
9340            second_item_id
9341        })
9342        .unwrap();
9343    cx.executor().run_until_parked();
9344    workspace
9345        .update(cx, |workspace, cx| {
9346            let active_item = workspace
9347                .active_item(cx)
9348                .expect("should have an active item after navigating back from the 2nd buffer");
9349            assert_eq!(
9350                active_item.item_id(),
9351                multibuffer_item_id,
9352                "Should navigate back from the 2nd buffer to the multi buffer"
9353            );
9354            assert!(!active_item.is_singleton(cx));
9355        })
9356        .unwrap();
9357
9358    multi_buffer_editor.update(cx, |editor, cx| {
9359        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
9360            s.select_ranges(Some(60..70))
9361        });
9362        editor.open_excerpts(&OpenExcerpts, cx);
9363    });
9364    cx.executor().run_until_parked();
9365    workspace
9366        .update(cx, |workspace, cx| {
9367            let active_item = workspace
9368                .active_item(cx)
9369                .expect("should have an active item after navigating into the 3rd buffer");
9370            let third_item_id = active_item.item_id();
9371            assert_ne!(
9372                third_item_id, multibuffer_item_id,
9373                "Should navigate into the 3rd buffer and activate it"
9374            );
9375            assert_ne!(third_item_id, first_item_id);
9376            assert_ne!(third_item_id, second_item_id);
9377            assert!(
9378                active_item.is_singleton(cx),
9379                "New active item should be a singleton buffer"
9380            );
9381            assert_eq!(
9382                active_item
9383                    .act_as::<Editor>(cx)
9384                    .expect("should have navigated into an editor")
9385                    .read(cx)
9386                    .text(cx),
9387                sample_text_3
9388            );
9389
9390            workspace
9391                .go_back(workspace.active_pane().downgrade(), cx)
9392                .detach_and_log_err(cx);
9393        })
9394        .unwrap();
9395    cx.executor().run_until_parked();
9396    workspace
9397        .update(cx, |workspace, cx| {
9398            let active_item = workspace
9399                .active_item(cx)
9400                .expect("should have an active item after navigating back from the 3rd buffer");
9401            assert_eq!(
9402                active_item.item_id(),
9403                multibuffer_item_id,
9404                "Should navigate back from the 3rd buffer to the multi buffer"
9405            );
9406            assert!(!active_item.is_singleton(cx));
9407        })
9408        .unwrap();
9409}
9410
9411fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
9412    let point = DisplayPoint::new(row as u32, column as u32);
9413    point..point
9414}
9415
9416fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
9417    let (text, ranges) = marked_text_ranges(marked_text, true);
9418    assert_eq!(view.text(cx), text);
9419    assert_eq!(
9420        view.selections.ranges(cx),
9421        ranges,
9422        "Assert selections are {}",
9423        marked_text
9424    );
9425}
9426
9427/// Handle completion request passing a marked string specifying where the completion
9428/// should be triggered from using '|' character, what range should be replaced, and what completions
9429/// should be returned using '<' and '>' to delimit the range
9430pub fn handle_completion_request(
9431    cx: &mut EditorLspTestContext,
9432    marked_string: &str,
9433    completions: Vec<&'static str>,
9434) -> impl Future<Output = ()> {
9435    let complete_from_marker: TextRangeMarker = '|'.into();
9436    let replace_range_marker: TextRangeMarker = ('<', '>').into();
9437    let (_, mut marked_ranges) = marked_text_ranges_by(
9438        marked_string,
9439        vec![complete_from_marker.clone(), replace_range_marker.clone()],
9440    );
9441
9442    let complete_from_position =
9443        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
9444    let replace_range =
9445        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
9446
9447    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
9448        let completions = completions.clone();
9449        async move {
9450            assert_eq!(params.text_document_position.text_document.uri, url.clone());
9451            assert_eq!(
9452                params.text_document_position.position,
9453                complete_from_position
9454            );
9455            Ok(Some(lsp::CompletionResponse::Array(
9456                completions
9457                    .iter()
9458                    .map(|completion_text| lsp::CompletionItem {
9459                        label: completion_text.to_string(),
9460                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
9461                            range: replace_range,
9462                            new_text: completion_text.to_string(),
9463                        })),
9464                        ..Default::default()
9465                    })
9466                    .collect(),
9467            )))
9468        }
9469    });
9470
9471    async move {
9472        request.next().await;
9473    }
9474}
9475
9476fn handle_resolve_completion_request(
9477    cx: &mut EditorLspTestContext,
9478    edits: Option<Vec<(&'static str, &'static str)>>,
9479) -> impl Future<Output = ()> {
9480    let edits = edits.map(|edits| {
9481        edits
9482            .iter()
9483            .map(|(marked_string, new_text)| {
9484                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
9485                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
9486                lsp::TextEdit::new(replace_range, new_text.to_string())
9487            })
9488            .collect::<Vec<_>>()
9489    });
9490
9491    let mut request =
9492        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
9493            let edits = edits.clone();
9494            async move {
9495                Ok(lsp::CompletionItem {
9496                    additional_text_edits: edits,
9497                    ..Default::default()
9498                })
9499            }
9500        });
9501
9502    async move {
9503        request.next().await;
9504    }
9505}
9506
9507pub(crate) fn update_test_language_settings(
9508    cx: &mut TestAppContext,
9509    f: impl Fn(&mut AllLanguageSettingsContent),
9510) {
9511    _ = cx.update(|cx| {
9512        cx.update_global(|store: &mut SettingsStore, cx| {
9513            store.update_user_settings::<AllLanguageSettings>(cx, f);
9514        });
9515    });
9516}
9517
9518pub(crate) fn update_test_project_settings(
9519    cx: &mut TestAppContext,
9520    f: impl Fn(&mut ProjectSettings),
9521) {
9522    _ = cx.update(|cx| {
9523        cx.update_global(|store: &mut SettingsStore, cx| {
9524            store.update_user_settings::<ProjectSettings>(cx, f);
9525        });
9526    });
9527}
9528
9529pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
9530    _ = cx.update(|cx| {
9531        let store = SettingsStore::test(cx);
9532        cx.set_global(store);
9533        theme::init(theme::LoadThemes::JustBase, cx);
9534        release_channel::init("0.0.0", cx);
9535        client::init_settings(cx);
9536        language::init(cx);
9537        Project::init_settings(cx);
9538        workspace::init_settings(cx);
9539        crate::init(cx);
9540    });
9541
9542    update_test_language_settings(cx, f);
9543}
9544
9545pub(crate) fn rust_lang() -> Arc<Language> {
9546    Arc::new(Language::new(
9547        LanguageConfig {
9548            name: "Rust".into(),
9549            matcher: LanguageMatcher {
9550                path_suffixes: vec!["rs".to_string()],
9551                ..Default::default()
9552            },
9553            ..Default::default()
9554        },
9555        Some(tree_sitter_rust::language()),
9556    ))
9557}
9558
9559#[track_caller]
9560fn assert_hunk_revert(
9561    not_reverted_text_with_selections: &str,
9562    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
9563    expected_reverted_text_with_selections: &str,
9564    base_text: &str,
9565    cx: &mut EditorLspTestContext,
9566) {
9567    cx.set_state(not_reverted_text_with_selections);
9568    cx.update_editor(|editor, cx| {
9569        editor
9570            .buffer()
9571            .read(cx)
9572            .as_singleton()
9573            .unwrap()
9574            .update(cx, |buffer, cx| {
9575                buffer.set_diff_base(Some(base_text.to_string()), cx);
9576            });
9577    });
9578    cx.executor().run_until_parked();
9579
9580    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
9581        let snapshot = editor
9582            .buffer()
9583            .read(cx)
9584            .as_singleton()
9585            .unwrap()
9586            .read(cx)
9587            .snapshot();
9588        let reverted_hunk_statuses = snapshot
9589            .git_diff_hunks_in_row_range(0..u32::MAX)
9590            .map(|hunk| hunk.status())
9591            .collect::<Vec<_>>();
9592
9593        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
9594        reverted_hunk_statuses
9595    });
9596    cx.executor().run_until_parked();
9597    cx.assert_editor_state(expected_reverted_text_with_selections);
9598    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
9599}