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_join_lines_with_git_diff_base(
2690    executor: BackgroundExecutor,
2691    cx: &mut gpui::TestAppContext,
2692) {
2693    init_test(cx, |_| {});
2694
2695    let mut cx = EditorTestContext::new(cx).await;
2696
2697    let diff_base = r#"
2698        Line 0
2699        Line 1
2700        Line 2
2701        Line 3
2702        "#
2703    .unindent();
2704
2705    cx.set_state(
2706        &r#"
2707        ˇLine 0
2708        Line 1
2709        Line 2
2710        Line 3
2711        "#
2712        .unindent(),
2713    );
2714
2715    cx.set_diff_base(Some(&diff_base));
2716    executor.run_until_parked();
2717
2718    // Join lines
2719    cx.update_editor(|editor, cx| {
2720        editor.join_lines(&JoinLines, cx);
2721    });
2722    executor.run_until_parked();
2723
2724    cx.assert_editor_state(
2725        &r#"
2726        Line 0ˇ Line 1
2727        Line 2
2728        Line 3
2729        "#
2730        .unindent(),
2731    );
2732    // Join again
2733    cx.update_editor(|editor, cx| {
2734        editor.join_lines(&JoinLines, cx);
2735    });
2736    executor.run_until_parked();
2737
2738    cx.assert_editor_state(
2739        &r#"
2740        Line 0 Line 1ˇ Line 2
2741        Line 3
2742        "#
2743        .unindent(),
2744    );
2745}
2746
2747#[gpui::test]
2748async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
2749    init_test(cx, |_| {});
2750
2751    let mut cx = EditorTestContext::new(cx).await;
2752
2753    // Test sort_lines_case_insensitive()
2754    cx.set_state(indoc! {"
2755        «z
2756        y
2757        x
2758        Z
2759        Y
2760        Xˇ»
2761    "});
2762    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
2763    cx.assert_editor_state(indoc! {"
2764        «x
2765        X
2766        y
2767        Y
2768        z
2769        Zˇ»
2770    "});
2771
2772    // Test reverse_lines()
2773    cx.set_state(indoc! {"
2774        «5
2775        4
2776        3
2777        2
2778        1ˇ»
2779    "});
2780    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
2781    cx.assert_editor_state(indoc! {"
2782        «1
2783        2
2784        3
2785        4
2786        5ˇ»
2787    "});
2788
2789    // Skip testing shuffle_line()
2790
2791    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
2792    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
2793
2794    // Don't manipulate when cursor is on single line, but expand the selection
2795    cx.set_state(indoc! {"
2796        ddˇdd
2797        ccc
2798        bb
2799        a
2800    "});
2801    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
2802    cx.assert_editor_state(indoc! {"
2803        «ddddˇ»
2804        ccc
2805        bb
2806        a
2807    "});
2808
2809    // Basic manipulate case
2810    // Start selection moves to column 0
2811    // End of selection shrinks to fit shorter line
2812    cx.set_state(indoc! {"
2813        dd«d
2814        ccc
2815        bb
2816        aaaaaˇ»
2817    "});
2818    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
2819    cx.assert_editor_state(indoc! {"
2820        «aaaaa
2821        bb
2822        ccc
2823        dddˇ»
2824    "});
2825
2826    // Manipulate case with newlines
2827    cx.set_state(indoc! {"
2828        dd«d
2829        ccc
2830
2831        bb
2832        aaaaa
2833
2834        ˇ»
2835    "});
2836    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
2837    cx.assert_editor_state(indoc! {"
2838        «
2839
2840        aaaaa
2841        bb
2842        ccc
2843        dddˇ»
2844
2845    "});
2846
2847    // Adding new line
2848    cx.set_state(indoc! {"
2849        aa«a
2850        bbˇ»b
2851    "});
2852    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
2853    cx.assert_editor_state(indoc! {"
2854        «aaa
2855        bbb
2856        added_lineˇ»
2857    "});
2858
2859    // Removing line
2860    cx.set_state(indoc! {"
2861        aa«a
2862        bbbˇ»
2863    "});
2864    cx.update_editor(|e, cx| {
2865        e.manipulate_lines(cx, |lines| {
2866            lines.pop();
2867        })
2868    });
2869    cx.assert_editor_state(indoc! {"
2870        «aaaˇ»
2871    "});
2872
2873    // Removing all lines
2874    cx.set_state(indoc! {"
2875        aa«a
2876        bbbˇ»
2877    "});
2878    cx.update_editor(|e, cx| {
2879        e.manipulate_lines(cx, |lines| {
2880            lines.drain(..);
2881        })
2882    });
2883    cx.assert_editor_state(indoc! {"
2884        ˇ
2885    "});
2886}
2887
2888#[gpui::test]
2889async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
2890    init_test(cx, |_| {});
2891
2892    let mut cx = EditorTestContext::new(cx).await;
2893
2894    // Consider continuous selection as single selection
2895    cx.set_state(indoc! {"
2896        Aaa«aa
2897        cˇ»c«c
2898        bb
2899        aaaˇ»aa
2900    "});
2901    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
2902    cx.assert_editor_state(indoc! {"
2903        «Aaaaa
2904        ccc
2905        bb
2906        aaaaaˇ»
2907    "});
2908
2909    cx.set_state(indoc! {"
2910        Aaa«aa
2911        cˇ»c«c
2912        bb
2913        aaaˇ»aa
2914    "});
2915    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
2916    cx.assert_editor_state(indoc! {"
2917        «Aaaaa
2918        ccc
2919        bbˇ»
2920    "});
2921
2922    // Consider non continuous selection as distinct dedup operations
2923    cx.set_state(indoc! {"
2924        «aaaaa
2925        bb
2926        aaaaa
2927        aaaaaˇ»
2928
2929        aaa«aaˇ»
2930    "});
2931    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
2932    cx.assert_editor_state(indoc! {"
2933        «aaaaa
2934        bbˇ»
2935
2936        «aaaaaˇ»
2937    "});
2938}
2939
2940#[gpui::test]
2941async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
2942    init_test(cx, |_| {});
2943
2944    let mut cx = EditorTestContext::new(cx).await;
2945
2946    cx.set_state(indoc! {"
2947        «Aaa
2948        aAa
2949        Aaaˇ»
2950    "});
2951    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
2952    cx.assert_editor_state(indoc! {"
2953        «Aaa
2954        aAaˇ»
2955    "});
2956
2957    cx.set_state(indoc! {"
2958        «Aaa
2959        aAa
2960        aaAˇ»
2961    "});
2962    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
2963    cx.assert_editor_state(indoc! {"
2964        «Aaaˇ»
2965    "});
2966}
2967
2968#[gpui::test]
2969async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
2970    init_test(cx, |_| {});
2971
2972    let mut cx = EditorTestContext::new(cx).await;
2973
2974    // Manipulate with multiple selections on a single line
2975    cx.set_state(indoc! {"
2976        dd«dd
2977        cˇ»c«c
2978        bb
2979        aaaˇ»aa
2980    "});
2981    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
2982    cx.assert_editor_state(indoc! {"
2983        «aaaaa
2984        bb
2985        ccc
2986        ddddˇ»
2987    "});
2988
2989    // Manipulate with multiple disjoin selections
2990    cx.set_state(indoc! {"
29912992        4
2993        3
2994        2
2995        1ˇ»
2996
2997        dd«dd
2998        ccc
2999        bb
3000        aaaˇ»aa
3001    "});
3002    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
3003    cx.assert_editor_state(indoc! {"
3004        «1
3005        2
3006        3
3007        4
3008        5ˇ»
3009
3010        «aaaaa
3011        bb
3012        ccc
3013        ddddˇ»
3014    "});
3015
3016    // Adding lines on each selection
3017    cx.set_state(indoc! {"
30183019        1ˇ»
3020
3021        bb«bb
3022        aaaˇ»aa
3023    "});
3024    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
3025    cx.assert_editor_state(indoc! {"
3026        «2
3027        1
3028        added lineˇ»
3029
3030        «bbbb
3031        aaaaa
3032        added lineˇ»
3033    "});
3034
3035    // Removing lines on each selection
3036    cx.set_state(indoc! {"
30373038        1ˇ»
3039
3040        bb«bb
3041        aaaˇ»aa
3042    "});
3043    cx.update_editor(|e, cx| {
3044        e.manipulate_lines(cx, |lines| {
3045            lines.pop();
3046        })
3047    });
3048    cx.assert_editor_state(indoc! {"
3049        «2ˇ»
3050
3051        «bbbbˇ»
3052    "});
3053}
3054
3055#[gpui::test]
3056async fn test_manipulate_text(cx: &mut TestAppContext) {
3057    init_test(cx, |_| {});
3058
3059    let mut cx = EditorTestContext::new(cx).await;
3060
3061    // Test convert_to_upper_case()
3062    cx.set_state(indoc! {"
3063        «hello worldˇ»
3064    "});
3065    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
3066    cx.assert_editor_state(indoc! {"
3067        «HELLO WORLDˇ»
3068    "});
3069
3070    // Test convert_to_lower_case()
3071    cx.set_state(indoc! {"
3072        «HELLO WORLDˇ»
3073    "});
3074    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
3075    cx.assert_editor_state(indoc! {"
3076        «hello worldˇ»
3077    "});
3078
3079    // Test multiple line, single selection case
3080    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
3081    cx.set_state(indoc! {"
3082        «The quick brown
3083        fox jumps over
3084        the lazy dogˇ»
3085    "});
3086    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
3087    cx.assert_editor_state(indoc! {"
3088        «The Quick Brown
3089        Fox Jumps Over
3090        The Lazy Dogˇ»
3091    "});
3092
3093    // Test multiple line, single selection case
3094    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
3095    cx.set_state(indoc! {"
3096        «The quick brown
3097        fox jumps over
3098        the lazy dogˇ»
3099    "});
3100    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
3101    cx.assert_editor_state(indoc! {"
3102        «TheQuickBrown
3103        FoxJumpsOver
3104        TheLazyDogˇ»
3105    "});
3106
3107    // From here on out, test more complex cases of manipulate_text()
3108
3109    // Test no selection case - should affect words cursors are in
3110    // Cursor at beginning, middle, and end of word
3111    cx.set_state(indoc! {"
3112        ˇhello big beauˇtiful worldˇ
3113    "});
3114    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
3115    cx.assert_editor_state(indoc! {"
3116        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
3117    "});
3118
3119    // Test multiple selections on a single line and across multiple lines
3120    cx.set_state(indoc! {"
3121        «Theˇ» quick «brown
3122        foxˇ» jumps «overˇ»
3123        the «lazyˇ» dog
3124    "});
3125    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
3126    cx.assert_editor_state(indoc! {"
3127        «THEˇ» quick «BROWN
3128        FOXˇ» jumps «OVERˇ»
3129        the «LAZYˇ» dog
3130    "});
3131
3132    // Test case where text length grows
3133    cx.set_state(indoc! {"
3134        «tschüߡ»
3135    "});
3136    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
3137    cx.assert_editor_state(indoc! {"
3138        «TSCHÜSSˇ»
3139    "});
3140
3141    // Test to make sure we don't crash when text shrinks
3142    cx.set_state(indoc! {"
3143        aaa_bbbˇ
3144    "});
3145    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
3146    cx.assert_editor_state(indoc! {"
3147        «aaaBbbˇ»
3148    "});
3149
3150    // Test to make sure we all aware of the fact that each word can grow and shrink
3151    // Final selections should be aware of this fact
3152    cx.set_state(indoc! {"
3153        aaa_bˇbb bbˇb_ccc ˇccc_ddd
3154    "});
3155    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
3156    cx.assert_editor_state(indoc! {"
3157        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
3158    "});
3159}
3160
3161#[gpui::test]
3162fn test_duplicate_line(cx: &mut TestAppContext) {
3163    init_test(cx, |_| {});
3164
3165    let view = cx.add_window(|cx| {
3166        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
3167        build_editor(buffer, cx)
3168    });
3169    _ = view.update(cx, |view, cx| {
3170        view.change_selections(None, cx, |s| {
3171            s.select_display_ranges([
3172                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3173                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3174                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
3175                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
3176            ])
3177        });
3178        view.duplicate_line_down(&DuplicateLineDown, cx);
3179        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
3180        assert_eq!(
3181            view.selections.display_ranges(cx),
3182            vec![
3183                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
3184                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
3185                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
3186                DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
3187            ]
3188        );
3189    });
3190
3191    let view = cx.add_window(|cx| {
3192        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
3193        build_editor(buffer, cx)
3194    });
3195    _ = view.update(cx, |view, cx| {
3196        view.change_selections(None, cx, |s| {
3197            s.select_display_ranges([
3198                DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
3199                DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
3200            ])
3201        });
3202        view.duplicate_line_down(&DuplicateLineDown, cx);
3203        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
3204        assert_eq!(
3205            view.selections.display_ranges(cx),
3206            vec![
3207                DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
3208                DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
3209            ]
3210        );
3211    });
3212
3213    // With `move_upwards` the selections stay in place, except for
3214    // the lines inserted above them
3215    let view = cx.add_window(|cx| {
3216        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
3217        build_editor(buffer, cx)
3218    });
3219    _ = view.update(cx, |view, cx| {
3220        view.change_selections(None, cx, |s| {
3221            s.select_display_ranges([
3222                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3223                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3224                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
3225                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
3226            ])
3227        });
3228        view.duplicate_line_up(&DuplicateLineUp, cx);
3229        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
3230        assert_eq!(
3231            view.selections.display_ranges(cx),
3232            vec![
3233                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3234                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3235                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
3236                DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
3237            ]
3238        );
3239    });
3240
3241    let view = cx.add_window(|cx| {
3242        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
3243        build_editor(buffer, cx)
3244    });
3245    _ = view.update(cx, |view, cx| {
3246        view.change_selections(None, cx, |s| {
3247            s.select_display_ranges([
3248                DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
3249                DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
3250            ])
3251        });
3252        view.duplicate_line_up(&DuplicateLineUp, cx);
3253        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
3254        assert_eq!(
3255            view.selections.display_ranges(cx),
3256            vec![
3257                DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
3258                DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
3259            ]
3260        );
3261    });
3262}
3263
3264#[gpui::test]
3265fn test_move_line_up_down(cx: &mut TestAppContext) {
3266    init_test(cx, |_| {});
3267
3268    let view = cx.add_window(|cx| {
3269        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
3270        build_editor(buffer, cx)
3271    });
3272    _ = view.update(cx, |view, cx| {
3273        view.fold_ranges(
3274            vec![
3275                Point::new(0, 2)..Point::new(1, 2),
3276                Point::new(2, 3)..Point::new(4, 1),
3277                Point::new(7, 0)..Point::new(8, 4),
3278            ],
3279            true,
3280            cx,
3281        );
3282        view.change_selections(None, cx, |s| {
3283            s.select_display_ranges([
3284                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
3285                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
3286                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
3287                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
3288            ])
3289        });
3290        assert_eq!(
3291            view.display_text(cx),
3292            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
3293        );
3294
3295        view.move_line_up(&MoveLineUp, cx);
3296        assert_eq!(
3297            view.display_text(cx),
3298            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
3299        );
3300        assert_eq!(
3301            view.selections.display_ranges(cx),
3302            vec![
3303                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
3304                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
3305                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
3306                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
3307            ]
3308        );
3309    });
3310
3311    _ = view.update(cx, |view, cx| {
3312        view.move_line_down(&MoveLineDown, cx);
3313        assert_eq!(
3314            view.display_text(cx),
3315            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
3316        );
3317        assert_eq!(
3318            view.selections.display_ranges(cx),
3319            vec![
3320                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
3321                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
3322                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
3323                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
3324            ]
3325        );
3326    });
3327
3328    _ = view.update(cx, |view, cx| {
3329        view.move_line_down(&MoveLineDown, cx);
3330        assert_eq!(
3331            view.display_text(cx),
3332            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
3333        );
3334        assert_eq!(
3335            view.selections.display_ranges(cx),
3336            vec![
3337                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
3338                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
3339                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
3340                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
3341            ]
3342        );
3343    });
3344
3345    _ = view.update(cx, |view, cx| {
3346        view.move_line_up(&MoveLineUp, cx);
3347        assert_eq!(
3348            view.display_text(cx),
3349            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
3350        );
3351        assert_eq!(
3352            view.selections.display_ranges(cx),
3353            vec![
3354                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
3355                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
3356                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
3357                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
3358            ]
3359        );
3360    });
3361}
3362
3363#[gpui::test]
3364fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
3365    init_test(cx, |_| {});
3366
3367    let editor = cx.add_window(|cx| {
3368        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
3369        build_editor(buffer, cx)
3370    });
3371    _ = editor.update(cx, |editor, cx| {
3372        let snapshot = editor.buffer.read(cx).snapshot(cx);
3373        editor.insert_blocks(
3374            [BlockProperties {
3375                style: BlockStyle::Fixed,
3376                position: snapshot.anchor_after(Point::new(2, 0)),
3377                disposition: BlockDisposition::Below,
3378                height: 1,
3379                render: Box::new(|_| div().into_any()),
3380            }],
3381            Some(Autoscroll::fit()),
3382            cx,
3383        );
3384        editor.change_selections(None, cx, |s| {
3385            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
3386        });
3387        editor.move_line_down(&MoveLineDown, cx);
3388    });
3389}
3390
3391#[gpui::test]
3392fn test_transpose(cx: &mut TestAppContext) {
3393    init_test(cx, |_| {});
3394
3395    _ = cx.add_window(|cx| {
3396        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
3397        editor.set_style(EditorStyle::default(), cx);
3398        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
3399        editor.transpose(&Default::default(), cx);
3400        assert_eq!(editor.text(cx), "bac");
3401        assert_eq!(editor.selections.ranges(cx), [2..2]);
3402
3403        editor.transpose(&Default::default(), cx);
3404        assert_eq!(editor.text(cx), "bca");
3405        assert_eq!(editor.selections.ranges(cx), [3..3]);
3406
3407        editor.transpose(&Default::default(), cx);
3408        assert_eq!(editor.text(cx), "bac");
3409        assert_eq!(editor.selections.ranges(cx), [3..3]);
3410
3411        editor
3412    });
3413
3414    _ = cx.add_window(|cx| {
3415        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
3416        editor.set_style(EditorStyle::default(), cx);
3417        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
3418        editor.transpose(&Default::default(), cx);
3419        assert_eq!(editor.text(cx), "acb\nde");
3420        assert_eq!(editor.selections.ranges(cx), [3..3]);
3421
3422        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
3423        editor.transpose(&Default::default(), cx);
3424        assert_eq!(editor.text(cx), "acbd\ne");
3425        assert_eq!(editor.selections.ranges(cx), [5..5]);
3426
3427        editor.transpose(&Default::default(), cx);
3428        assert_eq!(editor.text(cx), "acbde\n");
3429        assert_eq!(editor.selections.ranges(cx), [6..6]);
3430
3431        editor.transpose(&Default::default(), cx);
3432        assert_eq!(editor.text(cx), "acbd\ne");
3433        assert_eq!(editor.selections.ranges(cx), [6..6]);
3434
3435        editor
3436    });
3437
3438    _ = cx.add_window(|cx| {
3439        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
3440        editor.set_style(EditorStyle::default(), cx);
3441        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
3442        editor.transpose(&Default::default(), cx);
3443        assert_eq!(editor.text(cx), "bacd\ne");
3444        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
3445
3446        editor.transpose(&Default::default(), cx);
3447        assert_eq!(editor.text(cx), "bcade\n");
3448        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
3449
3450        editor.transpose(&Default::default(), cx);
3451        assert_eq!(editor.text(cx), "bcda\ne");
3452        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
3453
3454        editor.transpose(&Default::default(), cx);
3455        assert_eq!(editor.text(cx), "bcade\n");
3456        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
3457
3458        editor.transpose(&Default::default(), cx);
3459        assert_eq!(editor.text(cx), "bcaed\n");
3460        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
3461
3462        editor
3463    });
3464
3465    _ = cx.add_window(|cx| {
3466        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
3467        editor.set_style(EditorStyle::default(), cx);
3468        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
3469        editor.transpose(&Default::default(), cx);
3470        assert_eq!(editor.text(cx), "🏀🍐✋");
3471        assert_eq!(editor.selections.ranges(cx), [8..8]);
3472
3473        editor.transpose(&Default::default(), cx);
3474        assert_eq!(editor.text(cx), "🏀✋🍐");
3475        assert_eq!(editor.selections.ranges(cx), [11..11]);
3476
3477        editor.transpose(&Default::default(), cx);
3478        assert_eq!(editor.text(cx), "🏀🍐✋");
3479        assert_eq!(editor.selections.ranges(cx), [11..11]);
3480
3481        editor
3482    });
3483}
3484
3485#[gpui::test]
3486async fn test_clipboard(cx: &mut gpui::TestAppContext) {
3487    init_test(cx, |_| {});
3488
3489    let mut cx = EditorTestContext::new(cx).await;
3490
3491    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
3492    cx.update_editor(|e, cx| e.cut(&Cut, cx));
3493    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
3494
3495    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
3496    cx.set_state("two ˇfour ˇsix ˇ");
3497    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3498    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
3499
3500    // Paste again but with only two cursors. Since the number of cursors doesn't
3501    // match the number of slices in the clipboard, the entire clipboard text
3502    // is pasted at each cursor.
3503    cx.set_state("ˇtwo one✅ four three six five ˇ");
3504    cx.update_editor(|e, cx| {
3505        e.handle_input("( ", cx);
3506        e.paste(&Paste, cx);
3507        e.handle_input(") ", cx);
3508    });
3509    cx.assert_editor_state(
3510        &([
3511            "( one✅ ",
3512            "three ",
3513            "five ) ˇtwo one✅ four three six five ( one✅ ",
3514            "three ",
3515            "five ) ˇ",
3516        ]
3517        .join("\n")),
3518    );
3519
3520    // Cut with three selections, one of which is full-line.
3521    cx.set_state(indoc! {"
3522        1«2ˇ»3
3523        4ˇ567
3524        «8ˇ»9"});
3525    cx.update_editor(|e, cx| e.cut(&Cut, cx));
3526    cx.assert_editor_state(indoc! {"
3527        1ˇ3
3528        ˇ9"});
3529
3530    // Paste with three selections, noticing how the copied selection that was full-line
3531    // gets inserted before the second cursor.
3532    cx.set_state(indoc! {"
3533        1ˇ3
35343535        «oˇ»ne"});
3536    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3537    cx.assert_editor_state(indoc! {"
3538        12ˇ3
3539        4567
35403541        8ˇne"});
3542
3543    // Copy with a single cursor only, which writes the whole line into the clipboard.
3544    cx.set_state(indoc! {"
3545        The quick brown
3546        fox juˇmps over
3547        the lazy dog"});
3548    cx.update_editor(|e, cx| e.copy(&Copy, cx));
3549    assert_eq!(
3550        cx.read_from_clipboard().map(|item| item.text().to_owned()),
3551        Some("fox jumps over\n".to_owned())
3552    );
3553
3554    // Paste with three selections, noticing how the copied full-line selection is inserted
3555    // before the empty selections but replaces the selection that is non-empty.
3556    cx.set_state(indoc! {"
3557        Tˇhe quick brown
3558        «foˇ»x jumps over
3559        tˇhe lazy dog"});
3560    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3561    cx.assert_editor_state(indoc! {"
3562        fox jumps over
3563        Tˇhe quick brown
3564        fox jumps over
3565        ˇx jumps over
3566        fox jumps over
3567        tˇhe lazy dog"});
3568}
3569
3570#[gpui::test]
3571async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
3572    init_test(cx, |_| {});
3573
3574    let mut cx = EditorTestContext::new(cx).await;
3575    let language = Arc::new(Language::new(
3576        LanguageConfig::default(),
3577        Some(tree_sitter_rust::language()),
3578    ));
3579    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
3580
3581    // Cut an indented block, without the leading whitespace.
3582    cx.set_state(indoc! {"
3583        const a: B = (
3584            c(),
3585            «d(
3586                e,
3587                f
3588            )ˇ»
3589        );
3590    "});
3591    cx.update_editor(|e, cx| e.cut(&Cut, cx));
3592    cx.assert_editor_state(indoc! {"
3593        const a: B = (
3594            c(),
3595            ˇ
3596        );
3597    "});
3598
3599    // Paste it at the same position.
3600    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3601    cx.assert_editor_state(indoc! {"
3602        const a: B = (
3603            c(),
3604            d(
3605                e,
3606                f
36073608        );
3609    "});
3610
3611    // Paste it at a line with a lower indent level.
3612    cx.set_state(indoc! {"
3613        ˇ
3614        const a: B = (
3615            c(),
3616        );
3617    "});
3618    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3619    cx.assert_editor_state(indoc! {"
3620        d(
3621            e,
3622            f
36233624        const a: B = (
3625            c(),
3626        );
3627    "});
3628
3629    // Cut an indented block, with the leading whitespace.
3630    cx.set_state(indoc! {"
3631        const a: B = (
3632            c(),
3633        «    d(
3634                e,
3635                f
3636            )
3637        ˇ»);
3638    "});
3639    cx.update_editor(|e, cx| e.cut(&Cut, cx));
3640    cx.assert_editor_state(indoc! {"
3641        const a: B = (
3642            c(),
3643        ˇ);
3644    "});
3645
3646    // Paste it at the same position.
3647    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3648    cx.assert_editor_state(indoc! {"
3649        const a: B = (
3650            c(),
3651            d(
3652                e,
3653                f
3654            )
3655        ˇ);
3656    "});
3657
3658    // Paste it at a line with a higher indent level.
3659    cx.set_state(indoc! {"
3660        const a: B = (
3661            c(),
3662            d(
3663                e,
36643665            )
3666        );
3667    "});
3668    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3669    cx.assert_editor_state(indoc! {"
3670        const a: B = (
3671            c(),
3672            d(
3673                e,
3674                f    d(
3675                    e,
3676                    f
3677                )
3678        ˇ
3679            )
3680        );
3681    "});
3682}
3683
3684#[gpui::test]
3685fn test_select_all(cx: &mut TestAppContext) {
3686    init_test(cx, |_| {});
3687
3688    let view = cx.add_window(|cx| {
3689        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
3690        build_editor(buffer, cx)
3691    });
3692    _ = view.update(cx, |view, cx| {
3693        view.select_all(&SelectAll, cx);
3694        assert_eq!(
3695            view.selections.display_ranges(cx),
3696            &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
3697        );
3698    });
3699}
3700
3701#[gpui::test]
3702fn test_select_line(cx: &mut TestAppContext) {
3703    init_test(cx, |_| {});
3704
3705    let view = cx.add_window(|cx| {
3706        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
3707        build_editor(buffer, cx)
3708    });
3709    _ = view.update(cx, |view, cx| {
3710        view.change_selections(None, cx, |s| {
3711            s.select_display_ranges([
3712                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3713                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3714                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
3715                DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
3716            ])
3717        });
3718        view.select_line(&SelectLine, cx);
3719        assert_eq!(
3720            view.selections.display_ranges(cx),
3721            vec![
3722                DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
3723                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
3724            ]
3725        );
3726    });
3727
3728    _ = view.update(cx, |view, cx| {
3729        view.select_line(&SelectLine, cx);
3730        assert_eq!(
3731            view.selections.display_ranges(cx),
3732            vec![
3733                DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
3734                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
3735            ]
3736        );
3737    });
3738
3739    _ = view.update(cx, |view, cx| {
3740        view.select_line(&SelectLine, cx);
3741        assert_eq!(
3742            view.selections.display_ranges(cx),
3743            vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
3744        );
3745    });
3746}
3747
3748#[gpui::test]
3749fn test_split_selection_into_lines(cx: &mut TestAppContext) {
3750    init_test(cx, |_| {});
3751
3752    let view = cx.add_window(|cx| {
3753        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
3754        build_editor(buffer, cx)
3755    });
3756    _ = view.update(cx, |view, cx| {
3757        view.fold_ranges(
3758            vec![
3759                Point::new(0, 2)..Point::new(1, 2),
3760                Point::new(2, 3)..Point::new(4, 1),
3761                Point::new(7, 0)..Point::new(8, 4),
3762            ],
3763            true,
3764            cx,
3765        );
3766        view.change_selections(None, cx, |s| {
3767            s.select_display_ranges([
3768                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3769                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3770                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
3771                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
3772            ])
3773        });
3774        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
3775    });
3776
3777    _ = view.update(cx, |view, cx| {
3778        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
3779        assert_eq!(
3780            view.display_text(cx),
3781            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
3782        );
3783        assert_eq!(
3784            view.selections.display_ranges(cx),
3785            [
3786                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
3787                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3788                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
3789                DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
3790            ]
3791        );
3792    });
3793
3794    _ = view.update(cx, |view, cx| {
3795        view.change_selections(None, cx, |s| {
3796            s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
3797        });
3798        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
3799        assert_eq!(
3800            view.display_text(cx),
3801            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
3802        );
3803        assert_eq!(
3804            view.selections.display_ranges(cx),
3805            [
3806                DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
3807                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
3808                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
3809                DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
3810                DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
3811                DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
3812                DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
3813                DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
3814            ]
3815        );
3816    });
3817}
3818
3819#[gpui::test]
3820async fn test_add_selection_above_below(cx: &mut TestAppContext) {
3821    init_test(cx, |_| {});
3822
3823    let mut cx = EditorTestContext::new(cx).await;
3824
3825    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
3826    cx.set_state(indoc!(
3827        r#"abc
3828           defˇghi
3829
3830           jk
3831           nlmo
3832           "#
3833    ));
3834
3835    cx.update_editor(|editor, cx| {
3836        editor.add_selection_above(&Default::default(), cx);
3837    });
3838
3839    cx.assert_editor_state(indoc!(
3840        r#"abcˇ
3841           defˇghi
3842
3843           jk
3844           nlmo
3845           "#
3846    ));
3847
3848    cx.update_editor(|editor, cx| {
3849        editor.add_selection_above(&Default::default(), cx);
3850    });
3851
3852    cx.assert_editor_state(indoc!(
3853        r#"abcˇ
3854            defˇghi
3855
3856            jk
3857            nlmo
3858            "#
3859    ));
3860
3861    cx.update_editor(|view, cx| {
3862        view.add_selection_below(&Default::default(), cx);
3863    });
3864
3865    cx.assert_editor_state(indoc!(
3866        r#"abc
3867           defˇghi
3868
3869           jk
3870           nlmo
3871           "#
3872    ));
3873
3874    cx.update_editor(|view, cx| {
3875        view.undo_selection(&Default::default(), cx);
3876    });
3877
3878    cx.assert_editor_state(indoc!(
3879        r#"abcˇ
3880           defˇghi
3881
3882           jk
3883           nlmo
3884           "#
3885    ));
3886
3887    cx.update_editor(|view, cx| {
3888        view.redo_selection(&Default::default(), cx);
3889    });
3890
3891    cx.assert_editor_state(indoc!(
3892        r#"abc
3893           defˇghi
3894
3895           jk
3896           nlmo
3897           "#
3898    ));
3899
3900    cx.update_editor(|view, cx| {
3901        view.add_selection_below(&Default::default(), cx);
3902    });
3903
3904    cx.assert_editor_state(indoc!(
3905        r#"abc
3906           defˇghi
3907
3908           jk
3909           nlmˇo
3910           "#
3911    ));
3912
3913    cx.update_editor(|view, cx| {
3914        view.add_selection_below(&Default::default(), cx);
3915    });
3916
3917    cx.assert_editor_state(indoc!(
3918        r#"abc
3919           defˇghi
3920
3921           jk
3922           nlmˇo
3923           "#
3924    ));
3925
3926    // change selections
3927    cx.set_state(indoc!(
3928        r#"abc
3929           def«ˇg»hi
3930
3931           jk
3932           nlmo
3933           "#
3934    ));
3935
3936    cx.update_editor(|view, cx| {
3937        view.add_selection_below(&Default::default(), cx);
3938    });
3939
3940    cx.assert_editor_state(indoc!(
3941        r#"abc
3942           def«ˇg»hi
3943
3944           jk
3945           nlm«ˇo»
3946           "#
3947    ));
3948
3949    cx.update_editor(|view, cx| {
3950        view.add_selection_below(&Default::default(), cx);
3951    });
3952
3953    cx.assert_editor_state(indoc!(
3954        r#"abc
3955           def«ˇg»hi
3956
3957           jk
3958           nlm«ˇo»
3959           "#
3960    ));
3961
3962    cx.update_editor(|view, cx| {
3963        view.add_selection_above(&Default::default(), cx);
3964    });
3965
3966    cx.assert_editor_state(indoc!(
3967        r#"abc
3968           def«ˇg»hi
3969
3970           jk
3971           nlmo
3972           "#
3973    ));
3974
3975    cx.update_editor(|view, cx| {
3976        view.add_selection_above(&Default::default(), cx);
3977    });
3978
3979    cx.assert_editor_state(indoc!(
3980        r#"abc
3981           def«ˇg»hi
3982
3983           jk
3984           nlmo
3985           "#
3986    ));
3987
3988    // Change selections again
3989    cx.set_state(indoc!(
3990        r#"a«bc
3991           defgˇ»hi
3992
3993           jk
3994           nlmo
3995           "#
3996    ));
3997
3998    cx.update_editor(|view, cx| {
3999        view.add_selection_below(&Default::default(), cx);
4000    });
4001
4002    cx.assert_editor_state(indoc!(
4003        r#"a«bcˇ»
4004           d«efgˇ»hi
4005
4006           j«kˇ»
4007           nlmo
4008           "#
4009    ));
4010
4011    cx.update_editor(|view, cx| {
4012        view.add_selection_below(&Default::default(), cx);
4013    });
4014    cx.assert_editor_state(indoc!(
4015        r#"a«bcˇ»
4016           d«efgˇ»hi
4017
4018           j«kˇ»
4019           n«lmoˇ»
4020           "#
4021    ));
4022    cx.update_editor(|view, cx| {
4023        view.add_selection_above(&Default::default(), cx);
4024    });
4025
4026    cx.assert_editor_state(indoc!(
4027        r#"a«bcˇ»
4028           d«efgˇ»hi
4029
4030           j«kˇ»
4031           nlmo
4032           "#
4033    ));
4034
4035    // Change selections again
4036    cx.set_state(indoc!(
4037        r#"abc
4038           d«ˇefghi
4039
4040           jk
4041           nlm»o
4042           "#
4043    ));
4044
4045    cx.update_editor(|view, cx| {
4046        view.add_selection_above(&Default::default(), cx);
4047    });
4048
4049    cx.assert_editor_state(indoc!(
4050        r#"a«ˇbc»
4051           d«ˇef»ghi
4052
4053           j«ˇk»
4054           n«ˇlm»o
4055           "#
4056    ));
4057
4058    cx.update_editor(|view, cx| {
4059        view.add_selection_below(&Default::default(), cx);
4060    });
4061
4062    cx.assert_editor_state(indoc!(
4063        r#"abc
4064           d«ˇef»ghi
4065
4066           j«ˇk»
4067           n«ˇlm»o
4068           "#
4069    ));
4070}
4071
4072#[gpui::test]
4073async fn test_select_next(cx: &mut gpui::TestAppContext) {
4074    init_test(cx, |_| {});
4075
4076    let mut cx = EditorTestContext::new(cx).await;
4077    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
4078
4079    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
4080        .unwrap();
4081    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
4082
4083    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
4084        .unwrap();
4085    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
4086
4087    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
4088    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
4089
4090    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
4091    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
4092
4093    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
4094        .unwrap();
4095    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
4096
4097    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
4098        .unwrap();
4099    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
4100}
4101
4102#[gpui::test]
4103async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
4104    init_test(cx, |_| {});
4105
4106    let mut cx = EditorTestContext::new(cx).await;
4107    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
4108
4109    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
4110        .unwrap();
4111    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
4112}
4113
4114#[gpui::test]
4115async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
4116    init_test(cx, |_| {});
4117
4118    let mut cx = EditorTestContext::new(cx).await;
4119    cx.set_state(
4120        r#"let foo = 2;
4121lˇet foo = 2;
4122let fooˇ = 2;
4123let foo = 2;
4124let foo = ˇ2;"#,
4125    );
4126
4127    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
4128        .unwrap();
4129    cx.assert_editor_state(
4130        r#"let foo = 2;
4131«letˇ» foo = 2;
4132let «fooˇ» = 2;
4133let foo = 2;
4134let foo = «2ˇ»;"#,
4135    );
4136
4137    // noop for multiple selections with different contents
4138    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
4139        .unwrap();
4140    cx.assert_editor_state(
4141        r#"let foo = 2;
4142«letˇ» foo = 2;
4143let «fooˇ» = 2;
4144let foo = 2;
4145let foo = «2ˇ»;"#,
4146    );
4147}
4148
4149#[gpui::test]
4150async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
4151    init_test(cx, |_| {});
4152
4153    let mut cx = EditorTestContext::new_multibuffer(
4154        cx,
4155        [
4156            indoc! {
4157                "aaa\n«bbb\nccc\n»ddd"
4158            },
4159            indoc! {
4160                "aaa\n«bbb\nccc\n»ddd"
4161            },
4162        ],
4163    );
4164
4165    cx.assert_editor_state(indoc! {"
4166        ˇbbb
4167        ccc
4168
4169        bbb
4170        ccc
4171        "});
4172    cx.dispatch_action(SelectPrevious::default());
4173    cx.assert_editor_state(indoc! {"
4174                «bbbˇ»
4175                ccc
4176
4177                bbb
4178                ccc
4179                "});
4180    cx.dispatch_action(SelectPrevious::default());
4181    cx.assert_editor_state(indoc! {"
4182                «bbbˇ»
4183                ccc
4184
4185                «bbbˇ»
4186                ccc
4187                "});
4188}
4189
4190#[gpui::test]
4191async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
4192    init_test(cx, |_| {});
4193
4194    let mut cx = EditorTestContext::new(cx).await;
4195    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
4196
4197    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4198        .unwrap();
4199    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
4200
4201    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4202        .unwrap();
4203    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
4204
4205    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
4206    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
4207
4208    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
4209    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
4210
4211    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4212        .unwrap();
4213    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
4214
4215    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4216        .unwrap();
4217    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
4218
4219    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4220        .unwrap();
4221    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
4222}
4223
4224#[gpui::test]
4225async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
4226    init_test(cx, |_| {});
4227
4228    let mut cx = EditorTestContext::new(cx).await;
4229    cx.set_state(
4230        r#"let foo = 2;
4231lˇet foo = 2;
4232let fooˇ = 2;
4233let foo = 2;
4234let foo = ˇ2;"#,
4235    );
4236
4237    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4238        .unwrap();
4239    cx.assert_editor_state(
4240        r#"let foo = 2;
4241«letˇ» foo = 2;
4242let «fooˇ» = 2;
4243let foo = 2;
4244let foo = «2ˇ»;"#,
4245    );
4246
4247    // noop for multiple selections with different contents
4248    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4249        .unwrap();
4250    cx.assert_editor_state(
4251        r#"let foo = 2;
4252«letˇ» foo = 2;
4253let «fooˇ» = 2;
4254let foo = 2;
4255let foo = «2ˇ»;"#,
4256    );
4257}
4258
4259#[gpui::test]
4260async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
4261    init_test(cx, |_| {});
4262
4263    let mut cx = EditorTestContext::new(cx).await;
4264    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
4265
4266    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4267        .unwrap();
4268    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
4269
4270    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4271        .unwrap();
4272    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
4273
4274    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
4275    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
4276
4277    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
4278    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
4279
4280    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4281        .unwrap();
4282    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
4283
4284    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4285        .unwrap();
4286    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
4287}
4288
4289#[gpui::test]
4290async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
4291    init_test(cx, |_| {});
4292
4293    let language = Arc::new(Language::new(
4294        LanguageConfig::default(),
4295        Some(tree_sitter_rust::language()),
4296    ));
4297
4298    let text = r#"
4299        use mod1::mod2::{mod3, mod4};
4300
4301        fn fn_1(param1: bool, param2: &str) {
4302            let var1 = "text";
4303        }
4304    "#
4305    .unindent();
4306
4307    let buffer = cx.new_model(|cx| {
4308        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
4309            .with_language(language, cx)
4310    });
4311    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
4312    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
4313
4314    view.condition::<crate::EditorEvent>(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
4315        .await;
4316
4317    _ = view.update(cx, |view, cx| {
4318        view.change_selections(None, cx, |s| {
4319            s.select_display_ranges([
4320                DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
4321                DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
4322                DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
4323            ]);
4324        });
4325        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
4326    });
4327    assert_eq!(
4328        view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
4329        &[
4330            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
4331            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
4332            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
4333        ]
4334    );
4335
4336    _ = view.update(cx, |view, cx| {
4337        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
4338    });
4339    assert_eq!(
4340        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4341        &[
4342            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
4343            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
4344        ]
4345    );
4346
4347    _ = view.update(cx, |view, cx| {
4348        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
4349    });
4350    assert_eq!(
4351        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4352        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
4353    );
4354
4355    // Trying to expand the selected syntax node one more time has no effect.
4356    _ = view.update(cx, |view, cx| {
4357        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
4358    });
4359    assert_eq!(
4360        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4361        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
4362    );
4363
4364    _ = view.update(cx, |view, cx| {
4365        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
4366    });
4367    assert_eq!(
4368        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4369        &[
4370            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
4371            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
4372        ]
4373    );
4374
4375    _ = view.update(cx, |view, cx| {
4376        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
4377    });
4378    assert_eq!(
4379        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4380        &[
4381            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
4382            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
4383            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
4384        ]
4385    );
4386
4387    _ = view.update(cx, |view, cx| {
4388        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
4389    });
4390    assert_eq!(
4391        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4392        &[
4393            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
4394            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
4395            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
4396        ]
4397    );
4398
4399    // Trying to shrink the selected syntax node one more time has no effect.
4400    _ = view.update(cx, |view, cx| {
4401        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
4402    });
4403    assert_eq!(
4404        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4405        &[
4406            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
4407            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
4408            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
4409        ]
4410    );
4411
4412    // Ensure that we keep expanding the selection if the larger selection starts or ends within
4413    // a fold.
4414    _ = view.update(cx, |view, cx| {
4415        view.fold_ranges(
4416            vec![
4417                Point::new(0, 21)..Point::new(0, 24),
4418                Point::new(3, 20)..Point::new(3, 22),
4419            ],
4420            true,
4421            cx,
4422        );
4423        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
4424    });
4425    assert_eq!(
4426        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4427        &[
4428            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
4429            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
4430            DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
4431        ]
4432    );
4433}
4434
4435#[gpui::test]
4436async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
4437    init_test(cx, |_| {});
4438
4439    let language = Arc::new(
4440        Language::new(
4441            LanguageConfig {
4442                brackets: BracketPairConfig {
4443                    pairs: vec![
4444                        BracketPair {
4445                            start: "{".to_string(),
4446                            end: "}".to_string(),
4447                            close: false,
4448                            newline: true,
4449                        },
4450                        BracketPair {
4451                            start: "(".to_string(),
4452                            end: ")".to_string(),
4453                            close: false,
4454                            newline: true,
4455                        },
4456                    ],
4457                    ..Default::default()
4458                },
4459                ..Default::default()
4460            },
4461            Some(tree_sitter_rust::language()),
4462        )
4463        .with_indents_query(
4464            r#"
4465                (_ "(" ")" @end) @indent
4466                (_ "{" "}" @end) @indent
4467            "#,
4468        )
4469        .unwrap(),
4470    );
4471
4472    let text = "fn a() {}";
4473
4474    let buffer = cx.new_model(|cx| {
4475        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
4476            .with_language(language, cx)
4477    });
4478    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
4479    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
4480    editor
4481        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
4482        .await;
4483
4484    _ = editor.update(cx, |editor, cx| {
4485        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
4486        editor.newline(&Newline, cx);
4487        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
4488        assert_eq!(
4489            editor.selections.ranges(cx),
4490            &[
4491                Point::new(1, 4)..Point::new(1, 4),
4492                Point::new(3, 4)..Point::new(3, 4),
4493                Point::new(5, 0)..Point::new(5, 0)
4494            ]
4495        );
4496    });
4497}
4498
4499#[gpui::test]
4500async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
4501    init_test(cx, |_| {});
4502
4503    let mut cx = EditorTestContext::new(cx).await;
4504
4505    let language = Arc::new(Language::new(
4506        LanguageConfig {
4507            brackets: BracketPairConfig {
4508                pairs: vec![
4509                    BracketPair {
4510                        start: "{".to_string(),
4511                        end: "}".to_string(),
4512                        close: true,
4513                        newline: true,
4514                    },
4515                    BracketPair {
4516                        start: "(".to_string(),
4517                        end: ")".to_string(),
4518                        close: true,
4519                        newline: true,
4520                    },
4521                    BracketPair {
4522                        start: "/*".to_string(),
4523                        end: " */".to_string(),
4524                        close: true,
4525                        newline: true,
4526                    },
4527                    BracketPair {
4528                        start: "[".to_string(),
4529                        end: "]".to_string(),
4530                        close: false,
4531                        newline: true,
4532                    },
4533                    BracketPair {
4534                        start: "\"".to_string(),
4535                        end: "\"".to_string(),
4536                        close: true,
4537                        newline: false,
4538                    },
4539                ],
4540                ..Default::default()
4541            },
4542            autoclose_before: "})]".to_string(),
4543            ..Default::default()
4544        },
4545        Some(tree_sitter_rust::language()),
4546    ));
4547
4548    cx.language_registry().add(language.clone());
4549    cx.update_buffer(|buffer, cx| {
4550        buffer.set_language(Some(language), cx);
4551    });
4552
4553    cx.set_state(
4554        &r#"
4555            🏀ˇ
4556            εˇ
4557            ❤️ˇ
4558        "#
4559        .unindent(),
4560    );
4561
4562    // autoclose multiple nested brackets at multiple cursors
4563    cx.update_editor(|view, cx| {
4564        view.handle_input("{", cx);
4565        view.handle_input("{", cx);
4566        view.handle_input("{", cx);
4567    });
4568    cx.assert_editor_state(
4569        &"
4570            🏀{{{ˇ}}}
4571            ε{{{ˇ}}}
4572            ❤️{{{ˇ}}}
4573        "
4574        .unindent(),
4575    );
4576
4577    // insert a different closing bracket
4578    cx.update_editor(|view, cx| {
4579        view.handle_input(")", cx);
4580    });
4581    cx.assert_editor_state(
4582        &"
4583            🏀{{{)ˇ}}}
4584            ε{{{)ˇ}}}
4585            ❤️{{{)ˇ}}}
4586        "
4587        .unindent(),
4588    );
4589
4590    // skip over the auto-closed brackets when typing a closing bracket
4591    cx.update_editor(|view, cx| {
4592        view.move_right(&MoveRight, cx);
4593        view.handle_input("}", cx);
4594        view.handle_input("}", cx);
4595        view.handle_input("}", cx);
4596    });
4597    cx.assert_editor_state(
4598        &"
4599            🏀{{{)}}}}ˇ
4600            ε{{{)}}}}ˇ
4601            ❤️{{{)}}}}ˇ
4602        "
4603        .unindent(),
4604    );
4605
4606    // autoclose multi-character pairs
4607    cx.set_state(
4608        &"
4609            ˇ
4610            ˇ
4611        "
4612        .unindent(),
4613    );
4614    cx.update_editor(|view, cx| {
4615        view.handle_input("/", cx);
4616        view.handle_input("*", cx);
4617    });
4618    cx.assert_editor_state(
4619        &"
4620            /*ˇ */
4621            /*ˇ */
4622        "
4623        .unindent(),
4624    );
4625
4626    // one cursor autocloses a multi-character pair, one cursor
4627    // does not autoclose.
4628    cx.set_state(
4629        &"
46304631            ˇ
4632        "
4633        .unindent(),
4634    );
4635    cx.update_editor(|view, cx| view.handle_input("*", cx));
4636    cx.assert_editor_state(
4637        &"
4638            /*ˇ */
46394640        "
4641        .unindent(),
4642    );
4643
4644    // Don't autoclose if the next character isn't whitespace and isn't
4645    // listed in the language's "autoclose_before" section.
4646    cx.set_state("ˇa b");
4647    cx.update_editor(|view, cx| view.handle_input("{", cx));
4648    cx.assert_editor_state("{ˇa b");
4649
4650    // Don't autoclose if `close` is false for the bracket pair
4651    cx.set_state("ˇ");
4652    cx.update_editor(|view, cx| view.handle_input("[", cx));
4653    cx.assert_editor_state("");
4654
4655    // Surround with brackets if text is selected
4656    cx.set_state("«aˇ» b");
4657    cx.update_editor(|view, cx| view.handle_input("{", cx));
4658    cx.assert_editor_state("{«aˇ»} b");
4659
4660    // Autclose pair where the start and end characters are the same
4661    cx.set_state("");
4662    cx.update_editor(|view, cx| view.handle_input("\"", cx));
4663    cx.assert_editor_state("a\"ˇ\"");
4664    cx.update_editor(|view, cx| view.handle_input("\"", cx));
4665    cx.assert_editor_state("a\"\"ˇ");
4666}
4667
4668#[gpui::test]
4669async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
4670    init_test(cx, |settings| {
4671        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
4672    });
4673
4674    let mut cx = EditorTestContext::new(cx).await;
4675
4676    let language = Arc::new(Language::new(
4677        LanguageConfig {
4678            brackets: BracketPairConfig {
4679                pairs: vec![
4680                    BracketPair {
4681                        start: "{".to_string(),
4682                        end: "}".to_string(),
4683                        close: true,
4684                        newline: true,
4685                    },
4686                    BracketPair {
4687                        start: "(".to_string(),
4688                        end: ")".to_string(),
4689                        close: true,
4690                        newline: true,
4691                    },
4692                    BracketPair {
4693                        start: "[".to_string(),
4694                        end: "]".to_string(),
4695                        close: false,
4696                        newline: true,
4697                    },
4698                ],
4699                ..Default::default()
4700            },
4701            autoclose_before: "})]".to_string(),
4702            ..Default::default()
4703        },
4704        Some(tree_sitter_rust::language()),
4705    ));
4706
4707    cx.language_registry().add(language.clone());
4708    cx.update_buffer(|buffer, cx| {
4709        buffer.set_language(Some(language), cx);
4710    });
4711
4712    cx.set_state(
4713        &"
4714            ˇ
4715            ˇ
4716            ˇ
4717        "
4718        .unindent(),
4719    );
4720
4721    // ensure only matching closing brackets are skipped over
4722    cx.update_editor(|view, cx| {
4723        view.handle_input("}", cx);
4724        view.move_left(&MoveLeft, cx);
4725        view.handle_input(")", cx);
4726        view.move_left(&MoveLeft, cx);
4727    });
4728    cx.assert_editor_state(
4729        &"
4730            ˇ)}
4731            ˇ)}
4732            ˇ)}
4733        "
4734        .unindent(),
4735    );
4736
4737    // skip-over closing brackets at multiple cursors
4738    cx.update_editor(|view, cx| {
4739        view.handle_input(")", cx);
4740        view.handle_input("}", cx);
4741    });
4742    cx.assert_editor_state(
4743        &"
4744            )}ˇ
4745            )}ˇ
4746            )}ˇ
4747        "
4748        .unindent(),
4749    );
4750
4751    // ignore non-close brackets
4752    cx.update_editor(|view, cx| {
4753        view.handle_input("]", cx);
4754        view.move_left(&MoveLeft, cx);
4755        view.handle_input("]", cx);
4756    });
4757    cx.assert_editor_state(
4758        &"
4759            )}]ˇ]
4760            )}]ˇ]
4761            )}]ˇ]
4762        "
4763        .unindent(),
4764    );
4765}
4766
4767#[gpui::test]
4768async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
4769    init_test(cx, |_| {});
4770
4771    let mut cx = EditorTestContext::new(cx).await;
4772
4773    let html_language = Arc::new(
4774        Language::new(
4775            LanguageConfig {
4776                name: "HTML".into(),
4777                brackets: BracketPairConfig {
4778                    pairs: vec![
4779                        BracketPair {
4780                            start: "<".into(),
4781                            end: ">".into(),
4782                            close: true,
4783                            ..Default::default()
4784                        },
4785                        BracketPair {
4786                            start: "{".into(),
4787                            end: "}".into(),
4788                            close: true,
4789                            ..Default::default()
4790                        },
4791                        BracketPair {
4792                            start: "(".into(),
4793                            end: ")".into(),
4794                            close: true,
4795                            ..Default::default()
4796                        },
4797                    ],
4798                    ..Default::default()
4799                },
4800                autoclose_before: "})]>".into(),
4801                ..Default::default()
4802            },
4803            Some(tree_sitter_html::language()),
4804        )
4805        .with_injection_query(
4806            r#"
4807            (script_element
4808                (raw_text) @content
4809                (#set! "language" "javascript"))
4810            "#,
4811        )
4812        .unwrap(),
4813    );
4814
4815    let javascript_language = Arc::new(Language::new(
4816        LanguageConfig {
4817            name: "JavaScript".into(),
4818            brackets: BracketPairConfig {
4819                pairs: vec![
4820                    BracketPair {
4821                        start: "/*".into(),
4822                        end: " */".into(),
4823                        close: true,
4824                        ..Default::default()
4825                    },
4826                    BracketPair {
4827                        start: "{".into(),
4828                        end: "}".into(),
4829                        close: true,
4830                        ..Default::default()
4831                    },
4832                    BracketPair {
4833                        start: "(".into(),
4834                        end: ")".into(),
4835                        close: true,
4836                        ..Default::default()
4837                    },
4838                ],
4839                ..Default::default()
4840            },
4841            autoclose_before: "})]>".into(),
4842            ..Default::default()
4843        },
4844        Some(tree_sitter_typescript::language_tsx()),
4845    ));
4846
4847    cx.language_registry().add(html_language.clone());
4848    cx.language_registry().add(javascript_language.clone());
4849
4850    cx.update_buffer(|buffer, cx| {
4851        buffer.set_language(Some(html_language), cx);
4852    });
4853
4854    cx.set_state(
4855        &r#"
4856            <body>ˇ
4857                <script>
4858                    var x = 1;ˇ
4859                </script>
4860            </body>ˇ
4861        "#
4862        .unindent(),
4863    );
4864
4865    // Precondition: different languages are active at different locations.
4866    cx.update_editor(|editor, cx| {
4867        let snapshot = editor.snapshot(cx);
4868        let cursors = editor.selections.ranges::<usize>(cx);
4869        let languages = cursors
4870            .iter()
4871            .map(|c| snapshot.language_at(c.start).unwrap().name())
4872            .collect::<Vec<_>>();
4873        assert_eq!(
4874            languages,
4875            &["HTML".into(), "JavaScript".into(), "HTML".into()]
4876        );
4877    });
4878
4879    // Angle brackets autoclose in HTML, but not JavaScript.
4880    cx.update_editor(|editor, cx| {
4881        editor.handle_input("<", cx);
4882        editor.handle_input("a", cx);
4883    });
4884    cx.assert_editor_state(
4885        &r#"
4886            <body><aˇ>
4887                <script>
4888                    var x = 1;<aˇ
4889                </script>
4890            </body><aˇ>
4891        "#
4892        .unindent(),
4893    );
4894
4895    // Curly braces and parens autoclose in both HTML and JavaScript.
4896    cx.update_editor(|editor, cx| {
4897        editor.handle_input(" b=", cx);
4898        editor.handle_input("{", cx);
4899        editor.handle_input("c", cx);
4900        editor.handle_input("(", cx);
4901    });
4902    cx.assert_editor_state(
4903        &r#"
4904            <body><a b={c(ˇ)}>
4905                <script>
4906                    var x = 1;<a b={c(ˇ)}
4907                </script>
4908            </body><a b={c(ˇ)}>
4909        "#
4910        .unindent(),
4911    );
4912
4913    // Brackets that were already autoclosed are skipped.
4914    cx.update_editor(|editor, cx| {
4915        editor.handle_input(")", cx);
4916        editor.handle_input("d", cx);
4917        editor.handle_input("}", cx);
4918    });
4919    cx.assert_editor_state(
4920        &r#"
4921            <body><a b={c()d}ˇ>
4922                <script>
4923                    var x = 1;<a b={c()d}ˇ
4924                </script>
4925            </body><a b={c()d}ˇ>
4926        "#
4927        .unindent(),
4928    );
4929    cx.update_editor(|editor, cx| {
4930        editor.handle_input(">", cx);
4931    });
4932    cx.assert_editor_state(
4933        &r#"
4934            <body><a b={c()d}>ˇ
4935                <script>
4936                    var x = 1;<a b={c()d}>ˇ
4937                </script>
4938            </body><a b={c()d}>ˇ
4939        "#
4940        .unindent(),
4941    );
4942
4943    // Reset
4944    cx.set_state(
4945        &r#"
4946            <body>ˇ
4947                <script>
4948                    var x = 1;ˇ
4949                </script>
4950            </body>ˇ
4951        "#
4952        .unindent(),
4953    );
4954
4955    cx.update_editor(|editor, cx| {
4956        editor.handle_input("<", cx);
4957    });
4958    cx.assert_editor_state(
4959        &r#"
4960            <body><ˇ>
4961                <script>
4962                    var x = 1;<ˇ
4963                </script>
4964            </body><ˇ>
4965        "#
4966        .unindent(),
4967    );
4968
4969    // When backspacing, the closing angle brackets are removed.
4970    cx.update_editor(|editor, cx| {
4971        editor.backspace(&Backspace, cx);
4972    });
4973    cx.assert_editor_state(
4974        &r#"
4975            <body>ˇ
4976                <script>
4977                    var x = 1;ˇ
4978                </script>
4979            </body>ˇ
4980        "#
4981        .unindent(),
4982    );
4983
4984    // Block comments autoclose in JavaScript, but not HTML.
4985    cx.update_editor(|editor, cx| {
4986        editor.handle_input("/", cx);
4987        editor.handle_input("*", cx);
4988    });
4989    cx.assert_editor_state(
4990        &r#"
4991            <body>/*ˇ
4992                <script>
4993                    var x = 1;/*ˇ */
4994                </script>
4995            </body>/*ˇ
4996        "#
4997        .unindent(),
4998    );
4999}
5000
5001#[gpui::test]
5002async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
5003    init_test(cx, |_| {});
5004
5005    let mut cx = EditorTestContext::new(cx).await;
5006
5007    let rust_language = Arc::new(
5008        Language::new(
5009            LanguageConfig {
5010                name: "Rust".into(),
5011                brackets: serde_json::from_value(json!([
5012                    { "start": "{", "end": "}", "close": true, "newline": true },
5013                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
5014                ]))
5015                .unwrap(),
5016                autoclose_before: "})]>".into(),
5017                ..Default::default()
5018            },
5019            Some(tree_sitter_rust::language()),
5020        )
5021        .with_override_query("(string_literal) @string")
5022        .unwrap(),
5023    );
5024
5025    cx.language_registry().add(rust_language.clone());
5026    cx.update_buffer(|buffer, cx| {
5027        buffer.set_language(Some(rust_language), cx);
5028    });
5029
5030    cx.set_state(
5031        &r#"
5032            let x = ˇ
5033        "#
5034        .unindent(),
5035    );
5036
5037    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
5038    cx.update_editor(|editor, cx| {
5039        editor.handle_input("\"", cx);
5040    });
5041    cx.assert_editor_state(
5042        &r#"
5043            let x = "ˇ"
5044        "#
5045        .unindent(),
5046    );
5047
5048    // Inserting another quotation mark. The cursor moves across the existing
5049    // automatically-inserted quotation mark.
5050    cx.update_editor(|editor, cx| {
5051        editor.handle_input("\"", cx);
5052    });
5053    cx.assert_editor_state(
5054        &r#"
5055            let x = ""ˇ
5056        "#
5057        .unindent(),
5058    );
5059
5060    // Reset
5061    cx.set_state(
5062        &r#"
5063            let x = ˇ
5064        "#
5065        .unindent(),
5066    );
5067
5068    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
5069    cx.update_editor(|editor, cx| {
5070        editor.handle_input("\"", cx);
5071        editor.handle_input(" ", cx);
5072        editor.move_left(&Default::default(), cx);
5073        editor.handle_input("\\", cx);
5074        editor.handle_input("\"", cx);
5075    });
5076    cx.assert_editor_state(
5077        &r#"
5078            let x = "\"ˇ "
5079        "#
5080        .unindent(),
5081    );
5082
5083    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
5084    // mark. Nothing is inserted.
5085    cx.update_editor(|editor, cx| {
5086        editor.move_right(&Default::default(), cx);
5087        editor.handle_input("\"", cx);
5088    });
5089    cx.assert_editor_state(
5090        &r#"
5091            let x = "\" "ˇ
5092        "#
5093        .unindent(),
5094    );
5095}
5096
5097#[gpui::test]
5098async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
5099    init_test(cx, |_| {});
5100
5101    let language = Arc::new(Language::new(
5102        LanguageConfig {
5103            brackets: BracketPairConfig {
5104                pairs: vec![
5105                    BracketPair {
5106                        start: "{".to_string(),
5107                        end: "}".to_string(),
5108                        close: true,
5109                        newline: true,
5110                    },
5111                    BracketPair {
5112                        start: "/* ".to_string(),
5113                        end: "*/".to_string(),
5114                        close: true,
5115                        ..Default::default()
5116                    },
5117                ],
5118                ..Default::default()
5119            },
5120            ..Default::default()
5121        },
5122        Some(tree_sitter_rust::language()),
5123    ));
5124
5125    let text = r#"
5126        a
5127        b
5128        c
5129    "#
5130    .unindent();
5131
5132    let buffer = cx.new_model(|cx| {
5133        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
5134            .with_language(language, cx)
5135    });
5136    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
5137    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5138    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
5139        .await;
5140
5141    _ = view.update(cx, |view, cx| {
5142        view.change_selections(None, cx, |s| {
5143            s.select_display_ranges([
5144                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
5145                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
5146                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
5147            ])
5148        });
5149
5150        view.handle_input("{", cx);
5151        view.handle_input("{", cx);
5152        view.handle_input("{", cx);
5153        assert_eq!(
5154            view.text(cx),
5155            "
5156                {{{a}}}
5157                {{{b}}}
5158                {{{c}}}
5159            "
5160            .unindent()
5161        );
5162        assert_eq!(
5163            view.selections.display_ranges(cx),
5164            [
5165                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
5166                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
5167                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
5168            ]
5169        );
5170
5171        view.undo(&Undo, cx);
5172        view.undo(&Undo, cx);
5173        view.undo(&Undo, cx);
5174        assert_eq!(
5175            view.text(cx),
5176            "
5177                a
5178                b
5179                c
5180            "
5181            .unindent()
5182        );
5183        assert_eq!(
5184            view.selections.display_ranges(cx),
5185            [
5186                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
5187                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
5188                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
5189            ]
5190        );
5191
5192        // Ensure inserting the first character of a multi-byte bracket pair
5193        // doesn't surround the selections with the bracket.
5194        view.handle_input("/", cx);
5195        assert_eq!(
5196            view.text(cx),
5197            "
5198                /
5199                /
5200                /
5201            "
5202            .unindent()
5203        );
5204        assert_eq!(
5205            view.selections.display_ranges(cx),
5206            [
5207                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
5208                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
5209                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
5210            ]
5211        );
5212
5213        view.undo(&Undo, cx);
5214        assert_eq!(
5215            view.text(cx),
5216            "
5217                a
5218                b
5219                c
5220            "
5221            .unindent()
5222        );
5223        assert_eq!(
5224            view.selections.display_ranges(cx),
5225            [
5226                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
5227                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
5228                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
5229            ]
5230        );
5231
5232        // Ensure inserting the last character of a multi-byte bracket pair
5233        // doesn't surround the selections with the bracket.
5234        view.handle_input("*", cx);
5235        assert_eq!(
5236            view.text(cx),
5237            "
5238                *
5239                *
5240                *
5241            "
5242            .unindent()
5243        );
5244        assert_eq!(
5245            view.selections.display_ranges(cx),
5246            [
5247                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
5248                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
5249                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
5250            ]
5251        );
5252    });
5253}
5254
5255#[gpui::test]
5256async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
5257    init_test(cx, |_| {});
5258
5259    let language = Arc::new(Language::new(
5260        LanguageConfig {
5261            brackets: BracketPairConfig {
5262                pairs: vec![BracketPair {
5263                    start: "{".to_string(),
5264                    end: "}".to_string(),
5265                    close: true,
5266                    newline: true,
5267                }],
5268                ..Default::default()
5269            },
5270            autoclose_before: "}".to_string(),
5271            ..Default::default()
5272        },
5273        Some(tree_sitter_rust::language()),
5274    ));
5275
5276    let text = r#"
5277        a
5278        b
5279        c
5280    "#
5281    .unindent();
5282
5283    let buffer = cx.new_model(|cx| {
5284        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
5285            .with_language(language, cx)
5286    });
5287    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
5288    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5289    editor
5290        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
5291        .await;
5292
5293    _ = editor.update(cx, |editor, cx| {
5294        editor.change_selections(None, cx, |s| {
5295            s.select_ranges([
5296                Point::new(0, 1)..Point::new(0, 1),
5297                Point::new(1, 1)..Point::new(1, 1),
5298                Point::new(2, 1)..Point::new(2, 1),
5299            ])
5300        });
5301
5302        editor.handle_input("{", cx);
5303        editor.handle_input("{", cx);
5304        editor.handle_input("_", cx);
5305        assert_eq!(
5306            editor.text(cx),
5307            "
5308                a{{_}}
5309                b{{_}}
5310                c{{_}}
5311            "
5312            .unindent()
5313        );
5314        assert_eq!(
5315            editor.selections.ranges::<Point>(cx),
5316            [
5317                Point::new(0, 4)..Point::new(0, 4),
5318                Point::new(1, 4)..Point::new(1, 4),
5319                Point::new(2, 4)..Point::new(2, 4)
5320            ]
5321        );
5322
5323        editor.backspace(&Default::default(), cx);
5324        editor.backspace(&Default::default(), cx);
5325        assert_eq!(
5326            editor.text(cx),
5327            "
5328                a{}
5329                b{}
5330                c{}
5331            "
5332            .unindent()
5333        );
5334        assert_eq!(
5335            editor.selections.ranges::<Point>(cx),
5336            [
5337                Point::new(0, 2)..Point::new(0, 2),
5338                Point::new(1, 2)..Point::new(1, 2),
5339                Point::new(2, 2)..Point::new(2, 2)
5340            ]
5341        );
5342
5343        editor.delete_to_previous_word_start(&Default::default(), cx);
5344        assert_eq!(
5345            editor.text(cx),
5346            "
5347                a
5348                b
5349                c
5350            "
5351            .unindent()
5352        );
5353        assert_eq!(
5354            editor.selections.ranges::<Point>(cx),
5355            [
5356                Point::new(0, 1)..Point::new(0, 1),
5357                Point::new(1, 1)..Point::new(1, 1),
5358                Point::new(2, 1)..Point::new(2, 1)
5359            ]
5360        );
5361    });
5362}
5363
5364#[gpui::test]
5365async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
5366    init_test(cx, |settings| {
5367        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
5368    });
5369
5370    let mut cx = EditorTestContext::new(cx).await;
5371
5372    let language = Arc::new(Language::new(
5373        LanguageConfig {
5374            brackets: BracketPairConfig {
5375                pairs: vec![
5376                    BracketPair {
5377                        start: "{".to_string(),
5378                        end: "}".to_string(),
5379                        close: true,
5380                        newline: true,
5381                    },
5382                    BracketPair {
5383                        start: "(".to_string(),
5384                        end: ")".to_string(),
5385                        close: true,
5386                        newline: true,
5387                    },
5388                    BracketPair {
5389                        start: "[".to_string(),
5390                        end: "]".to_string(),
5391                        close: false,
5392                        newline: true,
5393                    },
5394                ],
5395                ..Default::default()
5396            },
5397            autoclose_before: "})]".to_string(),
5398            ..Default::default()
5399        },
5400        Some(tree_sitter_rust::language()),
5401    ));
5402
5403    cx.language_registry().add(language.clone());
5404    cx.update_buffer(|buffer, cx| {
5405        buffer.set_language(Some(language), cx);
5406    });
5407
5408    cx.set_state(
5409        &"
5410            {(ˇ)}
5411            [[ˇ]]
5412            {(ˇ)}
5413        "
5414        .unindent(),
5415    );
5416
5417    cx.update_editor(|view, cx| {
5418        view.backspace(&Default::default(), cx);
5419        view.backspace(&Default::default(), cx);
5420    });
5421
5422    cx.assert_editor_state(
5423        &"
5424            ˇ
5425            ˇ]]
5426            ˇ
5427        "
5428        .unindent(),
5429    );
5430
5431    cx.update_editor(|view, cx| {
5432        view.handle_input("{", cx);
5433        view.handle_input("{", cx);
5434        view.move_right(&MoveRight, cx);
5435        view.move_right(&MoveRight, cx);
5436        view.move_left(&MoveLeft, cx);
5437        view.move_left(&MoveLeft, cx);
5438        view.backspace(&Default::default(), cx);
5439    });
5440
5441    cx.assert_editor_state(
5442        &"
5443            {ˇ}
5444            {ˇ}]]
5445            {ˇ}
5446        "
5447        .unindent(),
5448    );
5449
5450    cx.update_editor(|view, cx| {
5451        view.backspace(&Default::default(), cx);
5452    });
5453
5454    cx.assert_editor_state(
5455        &"
5456            ˇ
5457            ˇ]]
5458            ˇ
5459        "
5460        .unindent(),
5461    );
5462}
5463
5464#[gpui::test]
5465async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
5466    init_test(cx, |_| {});
5467
5468    let language = Arc::new(Language::new(
5469        LanguageConfig::default(),
5470        Some(tree_sitter_rust::language()),
5471    ));
5472
5473    let buffer = cx.new_model(|cx| {
5474        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "")
5475            .with_language(language, cx)
5476    });
5477    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
5478    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5479    editor
5480        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
5481        .await;
5482
5483    _ = editor.update(cx, |editor, cx| {
5484        editor.set_auto_replace_emoji_shortcode(true);
5485
5486        editor.handle_input("Hello ", cx);
5487        editor.handle_input(":wave", cx);
5488        assert_eq!(editor.text(cx), "Hello :wave".unindent());
5489
5490        editor.handle_input(":", cx);
5491        assert_eq!(editor.text(cx), "Hello 👋".unindent());
5492
5493        editor.handle_input(" :smile", cx);
5494        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
5495
5496        editor.handle_input(":", cx);
5497        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
5498
5499        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
5500        editor.handle_input(":wave", cx);
5501        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
5502
5503        editor.handle_input(":", cx);
5504        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
5505
5506        editor.handle_input(":1", cx);
5507        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
5508
5509        editor.handle_input(":", cx);
5510        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
5511
5512        // Ensure shortcode does not get replaced when it is part of a word
5513        editor.handle_input(" Test:wave", cx);
5514        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
5515
5516        editor.handle_input(":", cx);
5517        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
5518
5519        editor.set_auto_replace_emoji_shortcode(false);
5520
5521        // Ensure shortcode does not get replaced when auto replace is off
5522        editor.handle_input(" :wave", cx);
5523        assert_eq!(
5524            editor.text(cx),
5525            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
5526        );
5527
5528        editor.handle_input(":", cx);
5529        assert_eq!(
5530            editor.text(cx),
5531            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
5532        );
5533    });
5534}
5535
5536#[gpui::test]
5537async fn test_snippets(cx: &mut gpui::TestAppContext) {
5538    init_test(cx, |_| {});
5539
5540    let (text, insertion_ranges) = marked_text_ranges(
5541        indoc! {"
5542            a.ˇ b
5543            a.ˇ b
5544            a.ˇ b
5545        "},
5546        false,
5547    );
5548
5549    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
5550    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5551
5552    _ = editor.update(cx, |editor, cx| {
5553        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
5554
5555        editor
5556            .insert_snippet(&insertion_ranges, snippet, cx)
5557            .unwrap();
5558
5559        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
5560            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
5561            assert_eq!(editor.text(cx), expected_text);
5562            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
5563        }
5564
5565        assert(
5566            editor,
5567            cx,
5568            indoc! {"
5569                a.f(«one», two, «three») b
5570                a.f(«one», two, «three») b
5571                a.f(«one», two, «three») b
5572            "},
5573        );
5574
5575        // Can't move earlier than the first tab stop
5576        assert!(!editor.move_to_prev_snippet_tabstop(cx));
5577        assert(
5578            editor,
5579            cx,
5580            indoc! {"
5581                a.f(«one», two, «three») b
5582                a.f(«one», two, «three») b
5583                a.f(«one», two, «three») b
5584            "},
5585        );
5586
5587        assert!(editor.move_to_next_snippet_tabstop(cx));
5588        assert(
5589            editor,
5590            cx,
5591            indoc! {"
5592                a.f(one, «two», three) b
5593                a.f(one, «two», three) b
5594                a.f(one, «two», three) b
5595            "},
5596        );
5597
5598        editor.move_to_prev_snippet_tabstop(cx);
5599        assert(
5600            editor,
5601            cx,
5602            indoc! {"
5603                a.f(«one», two, «three») b
5604                a.f(«one», two, «three») b
5605                a.f(«one», two, «three») b
5606            "},
5607        );
5608
5609        assert!(editor.move_to_next_snippet_tabstop(cx));
5610        assert(
5611            editor,
5612            cx,
5613            indoc! {"
5614                a.f(one, «two», three) b
5615                a.f(one, «two», three) b
5616                a.f(one, «two», three) b
5617            "},
5618        );
5619        assert!(editor.move_to_next_snippet_tabstop(cx));
5620        assert(
5621            editor,
5622            cx,
5623            indoc! {"
5624                a.f(one, two, three)ˇ b
5625                a.f(one, two, three)ˇ b
5626                a.f(one, two, three)ˇ b
5627            "},
5628        );
5629
5630        // As soon as the last tab stop is reached, snippet state is gone
5631        editor.move_to_prev_snippet_tabstop(cx);
5632        assert(
5633            editor,
5634            cx,
5635            indoc! {"
5636                a.f(one, two, three)ˇ b
5637                a.f(one, two, three)ˇ b
5638                a.f(one, two, three)ˇ b
5639            "},
5640        );
5641    });
5642}
5643
5644#[gpui::test]
5645async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
5646    init_test(cx, |_| {});
5647
5648    let fs = FakeFs::new(cx.executor());
5649    fs.insert_file("/file.rs", Default::default()).await;
5650
5651    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5652
5653    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
5654    language_registry.add(rust_lang());
5655    let mut fake_servers = language_registry.register_fake_lsp_adapter(
5656        "Rust",
5657        FakeLspAdapter {
5658            capabilities: lsp::ServerCapabilities {
5659                document_formatting_provider: Some(lsp::OneOf::Left(true)),
5660                ..Default::default()
5661            },
5662            ..Default::default()
5663        },
5664    );
5665
5666    let buffer = project
5667        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
5668        .await
5669        .unwrap();
5670
5671    cx.executor().start_waiting();
5672    let fake_server = fake_servers.next().await.unwrap();
5673
5674    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
5675    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5676    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5677    assert!(cx.read(|cx| editor.is_dirty(cx)));
5678
5679    let save = editor
5680        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5681        .unwrap();
5682    fake_server
5683        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5684            assert_eq!(
5685                params.text_document.uri,
5686                lsp::Url::from_file_path("/file.rs").unwrap()
5687            );
5688            assert_eq!(params.options.tab_size, 4);
5689            Ok(Some(vec![lsp::TextEdit::new(
5690                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
5691                ", ".to_string(),
5692            )]))
5693        })
5694        .next()
5695        .await;
5696    cx.executor().start_waiting();
5697    save.await;
5698
5699    assert_eq!(
5700        editor.update(cx, |editor, cx| editor.text(cx)),
5701        "one, two\nthree\n"
5702    );
5703    assert!(!cx.read(|cx| editor.is_dirty(cx)));
5704
5705    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5706    assert!(cx.read(|cx| editor.is_dirty(cx)));
5707
5708    // Ensure we can still save even if formatting hangs.
5709    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5710        assert_eq!(
5711            params.text_document.uri,
5712            lsp::Url::from_file_path("/file.rs").unwrap()
5713        );
5714        futures::future::pending::<()>().await;
5715        unreachable!()
5716    });
5717    let save = editor
5718        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5719        .unwrap();
5720    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
5721    cx.executor().start_waiting();
5722    save.await;
5723    assert_eq!(
5724        editor.update(cx, |editor, cx| editor.text(cx)),
5725        "one\ntwo\nthree\n"
5726    );
5727    assert!(!cx.read(|cx| editor.is_dirty(cx)));
5728
5729    // For non-dirty buffer, no formatting request should be sent
5730    let save = editor
5731        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5732        .unwrap();
5733    let _pending_format_request = fake_server
5734        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
5735            panic!("Should not be invoked on non-dirty buffer");
5736        })
5737        .next();
5738    cx.executor().start_waiting();
5739    save.await;
5740
5741    // Set rust language override and assert overridden tabsize is sent to language server
5742    update_test_language_settings(cx, |settings| {
5743        settings.languages.insert(
5744            "Rust".into(),
5745            LanguageSettingsContent {
5746                tab_size: NonZeroU32::new(8),
5747                ..Default::default()
5748            },
5749        );
5750    });
5751
5752    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
5753    assert!(cx.read(|cx| editor.is_dirty(cx)));
5754    let save = editor
5755        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5756        .unwrap();
5757    fake_server
5758        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5759            assert_eq!(
5760                params.text_document.uri,
5761                lsp::Url::from_file_path("/file.rs").unwrap()
5762            );
5763            assert_eq!(params.options.tab_size, 8);
5764            Ok(Some(vec![]))
5765        })
5766        .next()
5767        .await;
5768    cx.executor().start_waiting();
5769    save.await;
5770}
5771
5772#[gpui::test]
5773async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
5774    init_test(cx, |_| {});
5775
5776    let cols = 4;
5777    let rows = 10;
5778    let sample_text_1 = sample_text(rows, cols, 'a');
5779    assert_eq!(
5780        sample_text_1,
5781        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
5782    );
5783    let sample_text_2 = sample_text(rows, cols, 'l');
5784    assert_eq!(
5785        sample_text_2,
5786        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
5787    );
5788    let sample_text_3 = sample_text(rows, cols, 'v');
5789    assert_eq!(
5790        sample_text_3,
5791        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
5792    );
5793
5794    let fs = FakeFs::new(cx.executor());
5795    fs.insert_tree(
5796        "/a",
5797        json!({
5798            "main.rs": sample_text_1,
5799            "other.rs": sample_text_2,
5800            "lib.rs": sample_text_3,
5801        }),
5802    )
5803    .await;
5804
5805    let project = Project::test(fs, ["/a".as_ref()], cx).await;
5806    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
5807    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
5808
5809    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
5810    language_registry.add(rust_lang());
5811    let mut fake_servers = language_registry.register_fake_lsp_adapter(
5812        "Rust",
5813        FakeLspAdapter {
5814            capabilities: lsp::ServerCapabilities {
5815                document_formatting_provider: Some(lsp::OneOf::Left(true)),
5816                ..Default::default()
5817            },
5818            ..Default::default()
5819        },
5820    );
5821
5822    let worktree = project.update(cx, |project, _| {
5823        let mut worktrees = project.worktrees().collect::<Vec<_>>();
5824        assert_eq!(worktrees.len(), 1);
5825        worktrees.pop().unwrap()
5826    });
5827    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
5828
5829    let buffer_1 = project
5830        .update(cx, |project, cx| {
5831            project.open_buffer((worktree_id, "main.rs"), cx)
5832        })
5833        .await
5834        .unwrap();
5835    let buffer_2 = project
5836        .update(cx, |project, cx| {
5837            project.open_buffer((worktree_id, "other.rs"), cx)
5838        })
5839        .await
5840        .unwrap();
5841    let buffer_3 = project
5842        .update(cx, |project, cx| {
5843            project.open_buffer((worktree_id, "lib.rs"), cx)
5844        })
5845        .await
5846        .unwrap();
5847
5848    let multi_buffer = cx.new_model(|cx| {
5849        let mut multi_buffer = MultiBuffer::new(0, ReadWrite);
5850        multi_buffer.push_excerpts(
5851            buffer_1.clone(),
5852            [
5853                ExcerptRange {
5854                    context: Point::new(0, 0)..Point::new(3, 0),
5855                    primary: None,
5856                },
5857                ExcerptRange {
5858                    context: Point::new(5, 0)..Point::new(7, 0),
5859                    primary: None,
5860                },
5861                ExcerptRange {
5862                    context: Point::new(9, 0)..Point::new(10, 4),
5863                    primary: None,
5864                },
5865            ],
5866            cx,
5867        );
5868        multi_buffer.push_excerpts(
5869            buffer_2.clone(),
5870            [
5871                ExcerptRange {
5872                    context: Point::new(0, 0)..Point::new(3, 0),
5873                    primary: None,
5874                },
5875                ExcerptRange {
5876                    context: Point::new(5, 0)..Point::new(7, 0),
5877                    primary: None,
5878                },
5879                ExcerptRange {
5880                    context: Point::new(9, 0)..Point::new(10, 4),
5881                    primary: None,
5882                },
5883            ],
5884            cx,
5885        );
5886        multi_buffer.push_excerpts(
5887            buffer_3.clone(),
5888            [
5889                ExcerptRange {
5890                    context: Point::new(0, 0)..Point::new(3, 0),
5891                    primary: None,
5892                },
5893                ExcerptRange {
5894                    context: Point::new(5, 0)..Point::new(7, 0),
5895                    primary: None,
5896                },
5897                ExcerptRange {
5898                    context: Point::new(9, 0)..Point::new(10, 4),
5899                    primary: None,
5900                },
5901            ],
5902            cx,
5903        );
5904        multi_buffer
5905    });
5906    let multi_buffer_editor =
5907        cx.new_view(|cx| Editor::new(EditorMode::Full, multi_buffer, Some(project.clone()), cx));
5908
5909    multi_buffer_editor.update(cx, |editor, cx| {
5910        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
5911        editor.insert("|one|two|three|", cx);
5912    });
5913    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
5914    multi_buffer_editor.update(cx, |editor, cx| {
5915        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
5916            s.select_ranges(Some(60..70))
5917        });
5918        editor.insert("|four|five|six|", cx);
5919    });
5920    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
5921
5922    // First two buffers should be edited, but not the third one.
5923    assert_eq!(
5924        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
5925        "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}",
5926    );
5927    buffer_1.update(cx, |buffer, _| {
5928        assert!(buffer.is_dirty());
5929        assert_eq!(
5930            buffer.text(),
5931            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
5932        )
5933    });
5934    buffer_2.update(cx, |buffer, _| {
5935        assert!(buffer.is_dirty());
5936        assert_eq!(
5937            buffer.text(),
5938            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
5939        )
5940    });
5941    buffer_3.update(cx, |buffer, _| {
5942        assert!(!buffer.is_dirty());
5943        assert_eq!(buffer.text(), sample_text_3,)
5944    });
5945
5946    cx.executor().start_waiting();
5947    let save = multi_buffer_editor
5948        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5949        .unwrap();
5950
5951    let fake_server = fake_servers.next().await.unwrap();
5952    fake_server
5953        .server
5954        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5955            Ok(Some(vec![lsp::TextEdit::new(
5956                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
5957                format!("[{} formatted]", params.text_document.uri),
5958            )]))
5959        })
5960        .detach();
5961    save.await;
5962
5963    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
5964    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
5965    assert_eq!(
5966        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
5967        "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}",
5968    );
5969    buffer_1.update(cx, |buffer, _| {
5970        assert!(!buffer.is_dirty());
5971        assert_eq!(
5972            buffer.text(),
5973            "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
5974        )
5975    });
5976    buffer_2.update(cx, |buffer, _| {
5977        assert!(!buffer.is_dirty());
5978        assert_eq!(
5979            buffer.text(),
5980            "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n",
5981        )
5982    });
5983    buffer_3.update(cx, |buffer, _| {
5984        assert!(!buffer.is_dirty());
5985        assert_eq!(buffer.text(), sample_text_3,)
5986    });
5987}
5988
5989#[gpui::test]
5990async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
5991    init_test(cx, |_| {});
5992
5993    let fs = FakeFs::new(cx.executor());
5994    fs.insert_file("/file.rs", Default::default()).await;
5995
5996    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5997
5998    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
5999    language_registry.add(rust_lang());
6000    let mut fake_servers = language_registry.register_fake_lsp_adapter(
6001        "Rust",
6002        FakeLspAdapter {
6003            capabilities: lsp::ServerCapabilities {
6004                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
6005                ..Default::default()
6006            },
6007            ..Default::default()
6008        },
6009    );
6010
6011    let buffer = project
6012        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
6013        .await
6014        .unwrap();
6015
6016    cx.executor().start_waiting();
6017    let fake_server = fake_servers.next().await.unwrap();
6018
6019    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
6020    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
6021    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
6022    assert!(cx.read(|cx| editor.is_dirty(cx)));
6023
6024    let save = editor
6025        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
6026        .unwrap();
6027    fake_server
6028        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
6029            assert_eq!(
6030                params.text_document.uri,
6031                lsp::Url::from_file_path("/file.rs").unwrap()
6032            );
6033            assert_eq!(params.options.tab_size, 4);
6034            Ok(Some(vec![lsp::TextEdit::new(
6035                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
6036                ", ".to_string(),
6037            )]))
6038        })
6039        .next()
6040        .await;
6041    cx.executor().start_waiting();
6042    save.await;
6043    assert_eq!(
6044        editor.update(cx, |editor, cx| editor.text(cx)),
6045        "one, two\nthree\n"
6046    );
6047    assert!(!cx.read(|cx| editor.is_dirty(cx)));
6048
6049    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
6050    assert!(cx.read(|cx| editor.is_dirty(cx)));
6051
6052    // Ensure we can still save even if formatting hangs.
6053    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
6054        move |params, _| async move {
6055            assert_eq!(
6056                params.text_document.uri,
6057                lsp::Url::from_file_path("/file.rs").unwrap()
6058            );
6059            futures::future::pending::<()>().await;
6060            unreachable!()
6061        },
6062    );
6063    let save = editor
6064        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
6065        .unwrap();
6066    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
6067    cx.executor().start_waiting();
6068    save.await;
6069    assert_eq!(
6070        editor.update(cx, |editor, cx| editor.text(cx)),
6071        "one\ntwo\nthree\n"
6072    );
6073    assert!(!cx.read(|cx| editor.is_dirty(cx)));
6074
6075    // For non-dirty buffer, no formatting request should be sent
6076    let save = editor
6077        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
6078        .unwrap();
6079    let _pending_format_request = fake_server
6080        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
6081            panic!("Should not be invoked on non-dirty buffer");
6082        })
6083        .next();
6084    cx.executor().start_waiting();
6085    save.await;
6086
6087    // Set Rust language override and assert overridden tabsize is sent to language server
6088    update_test_language_settings(cx, |settings| {
6089        settings.languages.insert(
6090            "Rust".into(),
6091            LanguageSettingsContent {
6092                tab_size: NonZeroU32::new(8),
6093                ..Default::default()
6094            },
6095        );
6096    });
6097
6098    editor.update(cx, |editor, cx| editor.set_text("somehting_new\n", cx));
6099    assert!(cx.read(|cx| editor.is_dirty(cx)));
6100    let save = editor
6101        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
6102        .unwrap();
6103    fake_server
6104        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
6105            assert_eq!(
6106                params.text_document.uri,
6107                lsp::Url::from_file_path("/file.rs").unwrap()
6108            );
6109            assert_eq!(params.options.tab_size, 8);
6110            Ok(Some(vec![]))
6111        })
6112        .next()
6113        .await;
6114    cx.executor().start_waiting();
6115    save.await;
6116}
6117
6118#[gpui::test]
6119async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
6120    init_test(cx, |settings| {
6121        settings.defaults.formatter = Some(language_settings::Formatter::LanguageServer)
6122    });
6123
6124    let fs = FakeFs::new(cx.executor());
6125    fs.insert_file("/file.rs", Default::default()).await;
6126
6127    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
6128
6129    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
6130    language_registry.add(Arc::new(Language::new(
6131        LanguageConfig {
6132            name: "Rust".into(),
6133            matcher: LanguageMatcher {
6134                path_suffixes: vec!["rs".to_string()],
6135                ..Default::default()
6136            },
6137            // Enable Prettier formatting for the same buffer, and ensure
6138            // LSP is called instead of Prettier.
6139            prettier_parser_name: Some("test_parser".to_string()),
6140            ..Default::default()
6141        },
6142        Some(tree_sitter_rust::language()),
6143    )));
6144    let mut fake_servers = language_registry.register_fake_lsp_adapter(
6145        "Rust",
6146        FakeLspAdapter {
6147            capabilities: lsp::ServerCapabilities {
6148                document_formatting_provider: Some(lsp::OneOf::Left(true)),
6149                ..Default::default()
6150            },
6151            ..Default::default()
6152        },
6153    );
6154
6155    let buffer = project
6156        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
6157        .await
6158        .unwrap();
6159
6160    cx.executor().start_waiting();
6161    let fake_server = fake_servers.next().await.unwrap();
6162
6163    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
6164    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
6165    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
6166
6167    let format = editor
6168        .update(cx, |editor, cx| {
6169            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
6170        })
6171        .unwrap();
6172    fake_server
6173        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
6174            assert_eq!(
6175                params.text_document.uri,
6176                lsp::Url::from_file_path("/file.rs").unwrap()
6177            );
6178            assert_eq!(params.options.tab_size, 4);
6179            Ok(Some(vec![lsp::TextEdit::new(
6180                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
6181                ", ".to_string(),
6182            )]))
6183        })
6184        .next()
6185        .await;
6186    cx.executor().start_waiting();
6187    format.await;
6188    assert_eq!(
6189        editor.update(cx, |editor, cx| editor.text(cx)),
6190        "one, two\nthree\n"
6191    );
6192
6193    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
6194    // Ensure we don't lock if formatting hangs.
6195    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
6196        assert_eq!(
6197            params.text_document.uri,
6198            lsp::Url::from_file_path("/file.rs").unwrap()
6199        );
6200        futures::future::pending::<()>().await;
6201        unreachable!()
6202    });
6203    let format = editor
6204        .update(cx, |editor, cx| {
6205            editor.perform_format(project, FormatTrigger::Manual, cx)
6206        })
6207        .unwrap();
6208    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
6209    cx.executor().start_waiting();
6210    format.await;
6211    assert_eq!(
6212        editor.update(cx, |editor, cx| editor.text(cx)),
6213        "one\ntwo\nthree\n"
6214    );
6215}
6216
6217#[gpui::test]
6218async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
6219    init_test(cx, |_| {});
6220
6221    let mut cx = EditorLspTestContext::new_rust(
6222        lsp::ServerCapabilities {
6223            document_formatting_provider: Some(lsp::OneOf::Left(true)),
6224            ..Default::default()
6225        },
6226        cx,
6227    )
6228    .await;
6229
6230    cx.set_state(indoc! {"
6231        one.twoˇ
6232    "});
6233
6234    // The format request takes a long time. When it completes, it inserts
6235    // a newline and an indent before the `.`
6236    cx.lsp
6237        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
6238            let executor = cx.background_executor().clone();
6239            async move {
6240                executor.timer(Duration::from_millis(100)).await;
6241                Ok(Some(vec![lsp::TextEdit {
6242                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
6243                    new_text: "\n    ".into(),
6244                }]))
6245            }
6246        });
6247
6248    // Submit a format request.
6249    let format_1 = cx
6250        .update_editor(|editor, cx| editor.format(&Format, cx))
6251        .unwrap();
6252    cx.executor().run_until_parked();
6253
6254    // Submit a second format request.
6255    let format_2 = cx
6256        .update_editor(|editor, cx| editor.format(&Format, cx))
6257        .unwrap();
6258    cx.executor().run_until_parked();
6259
6260    // Wait for both format requests to complete
6261    cx.executor().advance_clock(Duration::from_millis(200));
6262    cx.executor().start_waiting();
6263    format_1.await.unwrap();
6264    cx.executor().start_waiting();
6265    format_2.await.unwrap();
6266
6267    // The formatting edits only happens once.
6268    cx.assert_editor_state(indoc! {"
6269        one
6270            .twoˇ
6271    "});
6272}
6273
6274#[gpui::test]
6275async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
6276    init_test(cx, |settings| {
6277        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
6278    });
6279
6280    let mut cx = EditorLspTestContext::new_rust(
6281        lsp::ServerCapabilities {
6282            document_formatting_provider: Some(lsp::OneOf::Left(true)),
6283            ..Default::default()
6284        },
6285        cx,
6286    )
6287    .await;
6288
6289    // Set up a buffer white some trailing whitespace and no trailing newline.
6290    cx.set_state(
6291        &[
6292            "one ",   //
6293            "twoˇ",   //
6294            "three ", //
6295            "four",   //
6296        ]
6297        .join("\n"),
6298    );
6299
6300    // Submit a format request.
6301    let format = cx
6302        .update_editor(|editor, cx| editor.format(&Format, cx))
6303        .unwrap();
6304
6305    // Record which buffer changes have been sent to the language server
6306    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
6307    cx.lsp
6308        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
6309            let buffer_changes = buffer_changes.clone();
6310            move |params, _| {
6311                buffer_changes.lock().extend(
6312                    params
6313                        .content_changes
6314                        .into_iter()
6315                        .map(|e| (e.range.unwrap(), e.text)),
6316                );
6317            }
6318        });
6319
6320    // Handle formatting requests to the language server.
6321    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
6322        let buffer_changes = buffer_changes.clone();
6323        move |_, _| {
6324            // When formatting is requested, trailing whitespace has already been stripped,
6325            // and the trailing newline has already been added.
6326            assert_eq!(
6327                &buffer_changes.lock()[1..],
6328                &[
6329                    (
6330                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
6331                        "".into()
6332                    ),
6333                    (
6334                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
6335                        "".into()
6336                    ),
6337                    (
6338                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
6339                        "\n".into()
6340                    ),
6341                ]
6342            );
6343
6344            // Insert blank lines between each line of the buffer.
6345            async move {
6346                Ok(Some(vec![
6347                    lsp::TextEdit {
6348                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
6349                        new_text: "\n".into(),
6350                    },
6351                    lsp::TextEdit {
6352                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
6353                        new_text: "\n".into(),
6354                    },
6355                ]))
6356            }
6357        }
6358    });
6359
6360    // After formatting the buffer, the trailing whitespace is stripped,
6361    // a newline is appended, and the edits provided by the language server
6362    // have been applied.
6363    format.await.unwrap();
6364    cx.assert_editor_state(
6365        &[
6366            "one",   //
6367            "",      //
6368            "twoˇ",  //
6369            "",      //
6370            "three", //
6371            "four",  //
6372            "",      //
6373        ]
6374        .join("\n"),
6375    );
6376
6377    // Undoing the formatting undoes the trailing whitespace removal, the
6378    // trailing newline, and the LSP edits.
6379    cx.update_buffer(|buffer, cx| buffer.undo(cx));
6380    cx.assert_editor_state(
6381        &[
6382            "one ",   //
6383            "twoˇ",   //
6384            "three ", //
6385            "four",   //
6386        ]
6387        .join("\n"),
6388    );
6389}
6390
6391#[gpui::test]
6392async fn test_completion(cx: &mut gpui::TestAppContext) {
6393    init_test(cx, |_| {});
6394
6395    let mut cx = EditorLspTestContext::new_rust(
6396        lsp::ServerCapabilities {
6397            completion_provider: Some(lsp::CompletionOptions {
6398                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
6399                resolve_provider: Some(true),
6400                ..Default::default()
6401            }),
6402            ..Default::default()
6403        },
6404        cx,
6405    )
6406    .await;
6407
6408    cx.set_state(indoc! {"
6409        oneˇ
6410        two
6411        three
6412    "});
6413    cx.simulate_keystroke(".");
6414    handle_completion_request(
6415        &mut cx,
6416        indoc! {"
6417            one.|<>
6418            two
6419            three
6420        "},
6421        vec!["first_completion", "second_completion"],
6422    )
6423    .await;
6424    cx.condition(|editor, _| editor.context_menu_visible())
6425        .await;
6426    let apply_additional_edits = cx.update_editor(|editor, cx| {
6427        editor.context_menu_next(&Default::default(), cx);
6428        editor
6429            .confirm_completion(&ConfirmCompletion::default(), cx)
6430            .unwrap()
6431    });
6432    cx.assert_editor_state(indoc! {"
6433        one.second_completionˇ
6434        two
6435        three
6436    "});
6437
6438    handle_resolve_completion_request(
6439        &mut cx,
6440        Some(vec![
6441            (
6442                //This overlaps with the primary completion edit which is
6443                //misbehavior from the LSP spec, test that we filter it out
6444                indoc! {"
6445                    one.second_ˇcompletion
6446                    two
6447                    threeˇ
6448                "},
6449                "overlapping additional edit",
6450            ),
6451            (
6452                indoc! {"
6453                    one.second_completion
6454                    two
6455                    threeˇ
6456                "},
6457                "\nadditional edit",
6458            ),
6459        ]),
6460    )
6461    .await;
6462    apply_additional_edits.await.unwrap();
6463    cx.assert_editor_state(indoc! {"
6464        one.second_completionˇ
6465        two
6466        three
6467        additional edit
6468    "});
6469
6470    cx.set_state(indoc! {"
6471        one.second_completion
6472        twoˇ
6473        threeˇ
6474        additional edit
6475    "});
6476    cx.simulate_keystroke(" ");
6477    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
6478    cx.simulate_keystroke("s");
6479    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
6480
6481    cx.assert_editor_state(indoc! {"
6482        one.second_completion
6483        two sˇ
6484        three sˇ
6485        additional edit
6486    "});
6487    handle_completion_request(
6488        &mut cx,
6489        indoc! {"
6490            one.second_completion
6491            two s
6492            three <s|>
6493            additional edit
6494        "},
6495        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
6496    )
6497    .await;
6498    cx.condition(|editor, _| editor.context_menu_visible())
6499        .await;
6500
6501    cx.simulate_keystroke("i");
6502
6503    handle_completion_request(
6504        &mut cx,
6505        indoc! {"
6506            one.second_completion
6507            two si
6508            three <si|>
6509            additional edit
6510        "},
6511        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
6512    )
6513    .await;
6514    cx.condition(|editor, _| editor.context_menu_visible())
6515        .await;
6516
6517    let apply_additional_edits = cx.update_editor(|editor, cx| {
6518        editor
6519            .confirm_completion(&ConfirmCompletion::default(), cx)
6520            .unwrap()
6521    });
6522    cx.assert_editor_state(indoc! {"
6523        one.second_completion
6524        two sixth_completionˇ
6525        three sixth_completionˇ
6526        additional edit
6527    "});
6528
6529    handle_resolve_completion_request(&mut cx, None).await;
6530    apply_additional_edits.await.unwrap();
6531
6532    _ = cx.update(|cx| {
6533        cx.update_global::<SettingsStore, _>(|settings, cx| {
6534            settings.update_user_settings::<EditorSettings>(cx, |settings| {
6535                settings.show_completions_on_input = Some(false);
6536            });
6537        })
6538    });
6539    cx.set_state("editorˇ");
6540    cx.simulate_keystroke(".");
6541    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
6542    cx.simulate_keystroke("c");
6543    cx.simulate_keystroke("l");
6544    cx.simulate_keystroke("o");
6545    cx.assert_editor_state("editor.cloˇ");
6546    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
6547    cx.update_editor(|editor, cx| {
6548        editor.show_completions(&ShowCompletions, cx);
6549    });
6550    handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
6551    cx.condition(|editor, _| editor.context_menu_visible())
6552        .await;
6553    let apply_additional_edits = cx.update_editor(|editor, cx| {
6554        editor
6555            .confirm_completion(&ConfirmCompletion::default(), cx)
6556            .unwrap()
6557    });
6558    cx.assert_editor_state("editor.closeˇ");
6559    handle_resolve_completion_request(&mut cx, None).await;
6560    apply_additional_edits.await.unwrap();
6561}
6562
6563#[gpui::test]
6564async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
6565    init_test(cx, |_| {});
6566    let mut cx = EditorTestContext::new(cx).await;
6567    let language = Arc::new(Language::new(
6568        LanguageConfig {
6569            line_comments: vec!["// ".into()],
6570            ..Default::default()
6571        },
6572        Some(tree_sitter_rust::language()),
6573    ));
6574    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
6575
6576    // If multiple selections intersect a line, the line is only toggled once.
6577    cx.set_state(indoc! {"
6578        fn a() {
6579            «//b();
6580            ˇ»// «c();
6581            //ˇ»  d();
6582        }
6583    "});
6584
6585    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
6586
6587    cx.assert_editor_state(indoc! {"
6588        fn a() {
6589            «b();
6590            c();
6591            ˇ» d();
6592        }
6593    "});
6594
6595    // The comment prefix is inserted at the same column for every line in a
6596    // selection.
6597    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
6598
6599    cx.assert_editor_state(indoc! {"
6600        fn a() {
6601            // «b();
6602            // c();
6603            ˇ»//  d();
6604        }
6605    "});
6606
6607    // If a selection ends at the beginning of a line, that line is not toggled.
6608    cx.set_selections_state(indoc! {"
6609        fn a() {
6610            // b();
6611            «// c();
6612        ˇ»    //  d();
6613        }
6614    "});
6615
6616    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
6617
6618    cx.assert_editor_state(indoc! {"
6619        fn a() {
6620            // b();
6621            «c();
6622        ˇ»    //  d();
6623        }
6624    "});
6625
6626    // If a selection span a single line and is empty, the line is toggled.
6627    cx.set_state(indoc! {"
6628        fn a() {
6629            a();
6630            b();
6631        ˇ
6632        }
6633    "});
6634
6635    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
6636
6637    cx.assert_editor_state(indoc! {"
6638        fn a() {
6639            a();
6640            b();
6641        //•ˇ
6642        }
6643    "});
6644
6645    // If a selection span multiple lines, empty lines are not toggled.
6646    cx.set_state(indoc! {"
6647        fn a() {
6648            «a();
6649
6650            c();ˇ»
6651        }
6652    "});
6653
6654    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
6655
6656    cx.assert_editor_state(indoc! {"
6657        fn a() {
6658            // «a();
6659
6660            // c();ˇ»
6661        }
6662    "});
6663}
6664
6665#[gpui::test]
6666async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
6667    init_test(cx, |_| {});
6668
6669    let language = Arc::new(Language::new(
6670        LanguageConfig {
6671            line_comments: vec!["// ".into()],
6672            ..Default::default()
6673        },
6674        Some(tree_sitter_rust::language()),
6675    ));
6676
6677    let mut cx = EditorTestContext::new(cx).await;
6678
6679    cx.language_registry().add(language.clone());
6680    cx.update_buffer(|buffer, cx| {
6681        buffer.set_language(Some(language), cx);
6682    });
6683
6684    let toggle_comments = &ToggleComments {
6685        advance_downwards: true,
6686    };
6687
6688    // Single cursor on one line -> advance
6689    // Cursor moves horizontally 3 characters as well on non-blank line
6690    cx.set_state(indoc!(
6691        "fn a() {
6692             ˇdog();
6693             cat();
6694        }"
6695    ));
6696    cx.update_editor(|editor, cx| {
6697        editor.toggle_comments(toggle_comments, cx);
6698    });
6699    cx.assert_editor_state(indoc!(
6700        "fn a() {
6701             // dog();
6702             catˇ();
6703        }"
6704    ));
6705
6706    // Single selection on one line -> don't advance
6707    cx.set_state(indoc!(
6708        "fn a() {
6709             «dog()ˇ»;
6710             cat();
6711        }"
6712    ));
6713    cx.update_editor(|editor, cx| {
6714        editor.toggle_comments(toggle_comments, cx);
6715    });
6716    cx.assert_editor_state(indoc!(
6717        "fn a() {
6718             // «dog()ˇ»;
6719             cat();
6720        }"
6721    ));
6722
6723    // Multiple cursors on one line -> advance
6724    cx.set_state(indoc!(
6725        "fn a() {
6726             ˇdˇog();
6727             cat();
6728        }"
6729    ));
6730    cx.update_editor(|editor, cx| {
6731        editor.toggle_comments(toggle_comments, cx);
6732    });
6733    cx.assert_editor_state(indoc!(
6734        "fn a() {
6735             // dog();
6736             catˇ(ˇ);
6737        }"
6738    ));
6739
6740    // Multiple cursors on one line, with selection -> don't advance
6741    cx.set_state(indoc!(
6742        "fn a() {
6743             ˇdˇog«()ˇ»;
6744             cat();
6745        }"
6746    ));
6747    cx.update_editor(|editor, cx| {
6748        editor.toggle_comments(toggle_comments, cx);
6749    });
6750    cx.assert_editor_state(indoc!(
6751        "fn a() {
6752             // ˇdˇog«()ˇ»;
6753             cat();
6754        }"
6755    ));
6756
6757    // Single cursor on one line -> advance
6758    // Cursor moves to column 0 on blank line
6759    cx.set_state(indoc!(
6760        "fn a() {
6761             ˇdog();
6762
6763             cat();
6764        }"
6765    ));
6766    cx.update_editor(|editor, cx| {
6767        editor.toggle_comments(toggle_comments, cx);
6768    });
6769    cx.assert_editor_state(indoc!(
6770        "fn a() {
6771             // dog();
6772        ˇ
6773             cat();
6774        }"
6775    ));
6776
6777    // Single cursor on one line -> advance
6778    // Cursor starts and ends at column 0
6779    cx.set_state(indoc!(
6780        "fn a() {
6781         ˇ    dog();
6782             cat();
6783        }"
6784    ));
6785    cx.update_editor(|editor, cx| {
6786        editor.toggle_comments(toggle_comments, cx);
6787    });
6788    cx.assert_editor_state(indoc!(
6789        "fn a() {
6790             // dog();
6791         ˇ    cat();
6792        }"
6793    ));
6794}
6795
6796#[gpui::test]
6797async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
6798    init_test(cx, |_| {});
6799
6800    let mut cx = EditorTestContext::new(cx).await;
6801
6802    let html_language = Arc::new(
6803        Language::new(
6804            LanguageConfig {
6805                name: "HTML".into(),
6806                block_comment: Some(("<!-- ".into(), " -->".into())),
6807                ..Default::default()
6808            },
6809            Some(tree_sitter_html::language()),
6810        )
6811        .with_injection_query(
6812            r#"
6813            (script_element
6814                (raw_text) @content
6815                (#set! "language" "javascript"))
6816            "#,
6817        )
6818        .unwrap(),
6819    );
6820
6821    let javascript_language = Arc::new(Language::new(
6822        LanguageConfig {
6823            name: "JavaScript".into(),
6824            line_comments: vec!["// ".into()],
6825            ..Default::default()
6826        },
6827        Some(tree_sitter_typescript::language_tsx()),
6828    ));
6829
6830    cx.language_registry().add(html_language.clone());
6831    cx.language_registry().add(javascript_language.clone());
6832    cx.update_buffer(|buffer, cx| {
6833        buffer.set_language(Some(html_language), cx);
6834    });
6835
6836    // Toggle comments for empty selections
6837    cx.set_state(
6838        &r#"
6839            <p>A</p>ˇ
6840            <p>B</p>ˇ
6841            <p>C</p>ˇ
6842        "#
6843        .unindent(),
6844    );
6845    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6846    cx.assert_editor_state(
6847        &r#"
6848            <!-- <p>A</p>ˇ -->
6849            <!-- <p>B</p>ˇ -->
6850            <!-- <p>C</p>ˇ -->
6851        "#
6852        .unindent(),
6853    );
6854    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6855    cx.assert_editor_state(
6856        &r#"
6857            <p>A</p>ˇ
6858            <p>B</p>ˇ
6859            <p>C</p>ˇ
6860        "#
6861        .unindent(),
6862    );
6863
6864    // Toggle comments for mixture of empty and non-empty selections, where
6865    // multiple selections occupy a given line.
6866    cx.set_state(
6867        &r#"
6868            <p>A«</p>
6869            <p>ˇ»B</p>ˇ
6870            <p>C«</p>
6871            <p>ˇ»D</p>ˇ
6872        "#
6873        .unindent(),
6874    );
6875
6876    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6877    cx.assert_editor_state(
6878        &r#"
6879            <!-- <p>A«</p>
6880            <p>ˇ»B</p>ˇ -->
6881            <!-- <p>C«</p>
6882            <p>ˇ»D</p>ˇ -->
6883        "#
6884        .unindent(),
6885    );
6886    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6887    cx.assert_editor_state(
6888        &r#"
6889            <p>A«</p>
6890            <p>ˇ»B</p>ˇ
6891            <p>C«</p>
6892            <p>ˇ»D</p>ˇ
6893        "#
6894        .unindent(),
6895    );
6896
6897    // Toggle comments when different languages are active for different
6898    // selections.
6899    cx.set_state(
6900        &r#"
6901            ˇ<script>
6902                ˇvar x = new Y();
6903            ˇ</script>
6904        "#
6905        .unindent(),
6906    );
6907    cx.executor().run_until_parked();
6908    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6909    cx.assert_editor_state(
6910        &r#"
6911            <!-- ˇ<script> -->
6912                // ˇvar x = new Y();
6913            <!-- ˇ</script> -->
6914        "#
6915        .unindent(),
6916    );
6917}
6918
6919#[gpui::test]
6920fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
6921    init_test(cx, |_| {});
6922
6923    let buffer = cx.new_model(|cx| {
6924        Buffer::new(
6925            0,
6926            BufferId::new(cx.entity_id().as_u64()).unwrap(),
6927            sample_text(3, 4, 'a'),
6928        )
6929    });
6930    let multibuffer = cx.new_model(|cx| {
6931        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
6932        multibuffer.push_excerpts(
6933            buffer.clone(),
6934            [
6935                ExcerptRange {
6936                    context: Point::new(0, 0)..Point::new(0, 4),
6937                    primary: None,
6938                },
6939                ExcerptRange {
6940                    context: Point::new(1, 0)..Point::new(1, 4),
6941                    primary: None,
6942                },
6943            ],
6944            cx,
6945        );
6946        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
6947        multibuffer
6948    });
6949
6950    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
6951    _ = view.update(cx, |view, cx| {
6952        assert_eq!(view.text(cx), "aaaa\nbbbb");
6953        view.change_selections(None, cx, |s| {
6954            s.select_ranges([
6955                Point::new(0, 0)..Point::new(0, 0),
6956                Point::new(1, 0)..Point::new(1, 0),
6957            ])
6958        });
6959
6960        view.handle_input("X", cx);
6961        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
6962        assert_eq!(
6963            view.selections.ranges(cx),
6964            [
6965                Point::new(0, 1)..Point::new(0, 1),
6966                Point::new(1, 1)..Point::new(1, 1),
6967            ]
6968        );
6969
6970        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
6971        view.change_selections(None, cx, |s| {
6972            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
6973        });
6974        view.backspace(&Default::default(), cx);
6975        assert_eq!(view.text(cx), "Xa\nbbb");
6976        assert_eq!(
6977            view.selections.ranges(cx),
6978            [Point::new(1, 0)..Point::new(1, 0)]
6979        );
6980
6981        view.change_selections(None, cx, |s| {
6982            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
6983        });
6984        view.backspace(&Default::default(), cx);
6985        assert_eq!(view.text(cx), "X\nbb");
6986        assert_eq!(
6987            view.selections.ranges(cx),
6988            [Point::new(0, 1)..Point::new(0, 1)]
6989        );
6990    });
6991}
6992
6993#[gpui::test]
6994fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
6995    init_test(cx, |_| {});
6996
6997    let markers = vec![('[', ']').into(), ('(', ')').into()];
6998    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
6999        indoc! {"
7000            [aaaa
7001            (bbbb]
7002            cccc)",
7003        },
7004        markers.clone(),
7005    );
7006    let excerpt_ranges = markers.into_iter().map(|marker| {
7007        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
7008        ExcerptRange {
7009            context,
7010            primary: None,
7011        }
7012    });
7013    let buffer = cx.new_model(|cx| {
7014        Buffer::new(
7015            0,
7016            BufferId::new(cx.entity_id().as_u64()).unwrap(),
7017            initial_text,
7018        )
7019    });
7020    let multibuffer = cx.new_model(|cx| {
7021        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
7022        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
7023        multibuffer
7024    });
7025
7026    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
7027    _ = view.update(cx, |view, cx| {
7028        let (expected_text, selection_ranges) = marked_text_ranges(
7029            indoc! {"
7030                aaaa
7031                bˇbbb
7032                bˇbbˇb
7033                cccc"
7034            },
7035            true,
7036        );
7037        assert_eq!(view.text(cx), expected_text);
7038        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
7039
7040        view.handle_input("X", cx);
7041
7042        let (expected_text, expected_selections) = marked_text_ranges(
7043            indoc! {"
7044                aaaa
7045                bXˇbbXb
7046                bXˇbbXˇb
7047                cccc"
7048            },
7049            false,
7050        );
7051        assert_eq!(view.text(cx), expected_text);
7052        assert_eq!(view.selections.ranges(cx), expected_selections);
7053
7054        view.newline(&Newline, cx);
7055        let (expected_text, expected_selections) = marked_text_ranges(
7056            indoc! {"
7057                aaaa
7058                bX
7059                ˇbbX
7060                b
7061                bX
7062                ˇbbX
7063                ˇb
7064                cccc"
7065            },
7066            false,
7067        );
7068        assert_eq!(view.text(cx), expected_text);
7069        assert_eq!(view.selections.ranges(cx), expected_selections);
7070    });
7071}
7072
7073#[gpui::test]
7074fn test_refresh_selections(cx: &mut TestAppContext) {
7075    init_test(cx, |_| {});
7076
7077    let buffer = cx.new_model(|cx| {
7078        Buffer::new(
7079            0,
7080            BufferId::new(cx.entity_id().as_u64()).unwrap(),
7081            sample_text(3, 4, 'a'),
7082        )
7083    });
7084    let mut excerpt1_id = None;
7085    let multibuffer = cx.new_model(|cx| {
7086        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
7087        excerpt1_id = multibuffer
7088            .push_excerpts(
7089                buffer.clone(),
7090                [
7091                    ExcerptRange {
7092                        context: Point::new(0, 0)..Point::new(1, 4),
7093                        primary: None,
7094                    },
7095                    ExcerptRange {
7096                        context: Point::new(1, 0)..Point::new(2, 4),
7097                        primary: None,
7098                    },
7099                ],
7100                cx,
7101            )
7102            .into_iter()
7103            .next();
7104        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
7105        multibuffer
7106    });
7107
7108    let editor = cx.add_window(|cx| {
7109        let mut editor = build_editor(multibuffer.clone(), cx);
7110        let snapshot = editor.snapshot(cx);
7111        editor.change_selections(None, cx, |s| {
7112            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
7113        });
7114        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
7115        assert_eq!(
7116            editor.selections.ranges(cx),
7117            [
7118                Point::new(1, 3)..Point::new(1, 3),
7119                Point::new(2, 1)..Point::new(2, 1),
7120            ]
7121        );
7122        editor
7123    });
7124
7125    // Refreshing selections is a no-op when excerpts haven't changed.
7126    _ = editor.update(cx, |editor, cx| {
7127        editor.change_selections(None, cx, |s| s.refresh());
7128        assert_eq!(
7129            editor.selections.ranges(cx),
7130            [
7131                Point::new(1, 3)..Point::new(1, 3),
7132                Point::new(2, 1)..Point::new(2, 1),
7133            ]
7134        );
7135    });
7136
7137    _ = multibuffer.update(cx, |multibuffer, cx| {
7138        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
7139    });
7140    _ = editor.update(cx, |editor, cx| {
7141        // Removing an excerpt causes the first selection to become degenerate.
7142        assert_eq!(
7143            editor.selections.ranges(cx),
7144            [
7145                Point::new(0, 0)..Point::new(0, 0),
7146                Point::new(0, 1)..Point::new(0, 1)
7147            ]
7148        );
7149
7150        // Refreshing selections will relocate the first selection to the original buffer
7151        // location.
7152        editor.change_selections(None, cx, |s| s.refresh());
7153        assert_eq!(
7154            editor.selections.ranges(cx),
7155            [
7156                Point::new(0, 1)..Point::new(0, 1),
7157                Point::new(0, 3)..Point::new(0, 3)
7158            ]
7159        );
7160        assert!(editor.selections.pending_anchor().is_some());
7161    });
7162}
7163
7164#[gpui::test]
7165fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
7166    init_test(cx, |_| {});
7167
7168    let buffer = cx.new_model(|cx| {
7169        Buffer::new(
7170            0,
7171            BufferId::new(cx.entity_id().as_u64()).unwrap(),
7172            sample_text(3, 4, 'a'),
7173        )
7174    });
7175    let mut excerpt1_id = None;
7176    let multibuffer = cx.new_model(|cx| {
7177        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
7178        excerpt1_id = multibuffer
7179            .push_excerpts(
7180                buffer.clone(),
7181                [
7182                    ExcerptRange {
7183                        context: Point::new(0, 0)..Point::new(1, 4),
7184                        primary: None,
7185                    },
7186                    ExcerptRange {
7187                        context: Point::new(1, 0)..Point::new(2, 4),
7188                        primary: None,
7189                    },
7190                ],
7191                cx,
7192            )
7193            .into_iter()
7194            .next();
7195        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
7196        multibuffer
7197    });
7198
7199    let editor = cx.add_window(|cx| {
7200        let mut editor = build_editor(multibuffer.clone(), cx);
7201        let snapshot = editor.snapshot(cx);
7202        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
7203        assert_eq!(
7204            editor.selections.ranges(cx),
7205            [Point::new(1, 3)..Point::new(1, 3)]
7206        );
7207        editor
7208    });
7209
7210    _ = multibuffer.update(cx, |multibuffer, cx| {
7211        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
7212    });
7213    _ = editor.update(cx, |editor, cx| {
7214        assert_eq!(
7215            editor.selections.ranges(cx),
7216            [Point::new(0, 0)..Point::new(0, 0)]
7217        );
7218
7219        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
7220        editor.change_selections(None, cx, |s| s.refresh());
7221        assert_eq!(
7222            editor.selections.ranges(cx),
7223            [Point::new(0, 3)..Point::new(0, 3)]
7224        );
7225        assert!(editor.selections.pending_anchor().is_some());
7226    });
7227}
7228
7229#[gpui::test]
7230async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
7231    init_test(cx, |_| {});
7232
7233    let language = Arc::new(
7234        Language::new(
7235            LanguageConfig {
7236                brackets: BracketPairConfig {
7237                    pairs: vec![
7238                        BracketPair {
7239                            start: "{".to_string(),
7240                            end: "}".to_string(),
7241                            close: true,
7242                            newline: true,
7243                        },
7244                        BracketPair {
7245                            start: "/* ".to_string(),
7246                            end: " */".to_string(),
7247                            close: true,
7248                            newline: true,
7249                        },
7250                    ],
7251                    ..Default::default()
7252                },
7253                ..Default::default()
7254            },
7255            Some(tree_sitter_rust::language()),
7256        )
7257        .with_indents_query("")
7258        .unwrap(),
7259    );
7260
7261    let text = concat!(
7262        "{   }\n",     //
7263        "  x\n",       //
7264        "  /*   */\n", //
7265        "x\n",         //
7266        "{{} }\n",     //
7267    );
7268
7269    let buffer = cx.new_model(|cx| {
7270        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
7271            .with_language(language, cx)
7272    });
7273    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
7274    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
7275    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
7276        .await;
7277
7278    _ = view.update(cx, |view, cx| {
7279        view.change_selections(None, cx, |s| {
7280            s.select_display_ranges([
7281                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
7282                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
7283                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
7284            ])
7285        });
7286        view.newline(&Newline, cx);
7287
7288        assert_eq!(
7289            view.buffer().read(cx).read(cx).text(),
7290            concat!(
7291                "{ \n",    // Suppress rustfmt
7292                "\n",      //
7293                "}\n",     //
7294                "  x\n",   //
7295                "  /* \n", //
7296                "  \n",    //
7297                "  */\n",  //
7298                "x\n",     //
7299                "{{} \n",  //
7300                "}\n",     //
7301            )
7302        );
7303    });
7304}
7305
7306#[gpui::test]
7307fn test_highlighted_ranges(cx: &mut TestAppContext) {
7308    init_test(cx, |_| {});
7309
7310    let editor = cx.add_window(|cx| {
7311        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
7312        build_editor(buffer.clone(), cx)
7313    });
7314
7315    _ = editor.update(cx, |editor, cx| {
7316        struct Type1;
7317        struct Type2;
7318
7319        let buffer = editor.buffer.read(cx).snapshot(cx);
7320
7321        let anchor_range =
7322            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
7323
7324        editor.highlight_background::<Type1>(
7325            &[
7326                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
7327                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
7328                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
7329                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
7330            ],
7331            |_| Hsla::red(),
7332            cx,
7333        );
7334        editor.highlight_background::<Type2>(
7335            &[
7336                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
7337                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
7338                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
7339                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
7340            ],
7341            |_| Hsla::green(),
7342            cx,
7343        );
7344
7345        let snapshot = editor.snapshot(cx);
7346        let mut highlighted_ranges = editor.background_highlights_in_range(
7347            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
7348            &snapshot,
7349            cx.theme().colors(),
7350        );
7351        // Enforce a consistent ordering based on color without relying on the ordering of the
7352        // highlight's `TypeId` which is non-executor.
7353        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
7354        assert_eq!(
7355            highlighted_ranges,
7356            &[
7357                (
7358                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
7359                    Hsla::red(),
7360                ),
7361                (
7362                    DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
7363                    Hsla::red(),
7364                ),
7365                (
7366                    DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
7367                    Hsla::green(),
7368                ),
7369                (
7370                    DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
7371                    Hsla::green(),
7372                ),
7373            ]
7374        );
7375        assert_eq!(
7376            editor.background_highlights_in_range(
7377                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
7378                &snapshot,
7379                cx.theme().colors(),
7380            ),
7381            &[(
7382                DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
7383                Hsla::red(),
7384            )]
7385        );
7386    });
7387}
7388
7389#[gpui::test]
7390async fn test_following(cx: &mut gpui::TestAppContext) {
7391    init_test(cx, |_| {});
7392
7393    let fs = FakeFs::new(cx.executor());
7394    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
7395
7396    let buffer = project.update(cx, |project, cx| {
7397        let buffer = project
7398            .create_buffer(&sample_text(16, 8, 'a'), None, cx)
7399            .unwrap();
7400        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
7401    });
7402    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
7403    let follower = cx.update(|cx| {
7404        cx.open_window(
7405            WindowOptions {
7406                bounds: Some(Bounds::from_corners(
7407                    gpui::Point::new(0.into(), 0.into()),
7408                    gpui::Point::new(10.into(), 80.into()),
7409                )),
7410                ..Default::default()
7411            },
7412            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
7413        )
7414    });
7415
7416    let is_still_following = Rc::new(RefCell::new(true));
7417    let follower_edit_event_count = Rc::new(RefCell::new(0));
7418    let pending_update = Rc::new(RefCell::new(None));
7419    _ = follower.update(cx, {
7420        let update = pending_update.clone();
7421        let is_still_following = is_still_following.clone();
7422        let follower_edit_event_count = follower_edit_event_count.clone();
7423        |_, cx| {
7424            cx.subscribe(
7425                &leader.root_view(cx).unwrap(),
7426                move |_, leader, event, cx| {
7427                    leader
7428                        .read(cx)
7429                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
7430                },
7431            )
7432            .detach();
7433
7434            cx.subscribe(
7435                &follower.root_view(cx).unwrap(),
7436                move |_, _, event: &EditorEvent, _cx| {
7437                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
7438                        *is_still_following.borrow_mut() = false;
7439                    }
7440
7441                    if let EditorEvent::BufferEdited = event {
7442                        *follower_edit_event_count.borrow_mut() += 1;
7443                    }
7444                },
7445            )
7446            .detach();
7447        }
7448    });
7449
7450    // Update the selections only
7451    _ = leader.update(cx, |leader, cx| {
7452        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
7453    });
7454    follower
7455        .update(cx, |follower, cx| {
7456            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
7457        })
7458        .unwrap()
7459        .await
7460        .unwrap();
7461    _ = follower.update(cx, |follower, cx| {
7462        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
7463    });
7464    assert_eq!(*is_still_following.borrow(), true);
7465    assert_eq!(*follower_edit_event_count.borrow(), 0);
7466
7467    // Update the scroll position only
7468    _ = leader.update(cx, |leader, cx| {
7469        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
7470    });
7471    follower
7472        .update(cx, |follower, cx| {
7473            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
7474        })
7475        .unwrap()
7476        .await
7477        .unwrap();
7478    assert_eq!(
7479        follower
7480            .update(cx, |follower, cx| follower.scroll_position(cx))
7481            .unwrap(),
7482        gpui::Point::new(1.5, 3.5)
7483    );
7484    assert_eq!(*is_still_following.borrow(), true);
7485    assert_eq!(*follower_edit_event_count.borrow(), 0);
7486
7487    // Update the selections and scroll position. The follower's scroll position is updated
7488    // via autoscroll, not via the leader's exact scroll position.
7489    _ = leader.update(cx, |leader, cx| {
7490        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
7491        leader.request_autoscroll(Autoscroll::newest(), cx);
7492        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
7493    });
7494    follower
7495        .update(cx, |follower, cx| {
7496            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
7497        })
7498        .unwrap()
7499        .await
7500        .unwrap();
7501    _ = follower.update(cx, |follower, cx| {
7502        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
7503        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
7504    });
7505    assert_eq!(*is_still_following.borrow(), true);
7506
7507    // Creating a pending selection that precedes another selection
7508    _ = leader.update(cx, |leader, cx| {
7509        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
7510        leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
7511    });
7512    follower
7513        .update(cx, |follower, cx| {
7514            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
7515        })
7516        .unwrap()
7517        .await
7518        .unwrap();
7519    _ = follower.update(cx, |follower, cx| {
7520        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
7521    });
7522    assert_eq!(*is_still_following.borrow(), true);
7523
7524    // Extend the pending selection so that it surrounds another selection
7525    _ = leader.update(cx, |leader, cx| {
7526        leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
7527    });
7528    follower
7529        .update(cx, |follower, cx| {
7530            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
7531        })
7532        .unwrap()
7533        .await
7534        .unwrap();
7535    _ = follower.update(cx, |follower, cx| {
7536        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
7537    });
7538
7539    // Scrolling locally breaks the follow
7540    _ = follower.update(cx, |follower, cx| {
7541        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
7542        follower.set_scroll_anchor(
7543            ScrollAnchor {
7544                anchor: top_anchor,
7545                offset: gpui::Point::new(0.0, 0.5),
7546            },
7547            cx,
7548        );
7549    });
7550    assert_eq!(*is_still_following.borrow(), false);
7551}
7552
7553#[gpui::test]
7554async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
7555    init_test(cx, |_| {});
7556
7557    let fs = FakeFs::new(cx.executor());
7558    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
7559    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
7560    let pane = workspace
7561        .update(cx, |workspace, _| workspace.active_pane().clone())
7562        .unwrap();
7563
7564    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
7565
7566    let leader = pane.update(cx, |_, cx| {
7567        let multibuffer = cx.new_model(|_| MultiBuffer::new(0, ReadWrite));
7568        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
7569    });
7570
7571    // Start following the editor when it has no excerpts.
7572    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
7573    let follower_1 = cx
7574        .update_window(*workspace.deref(), |_, cx| {
7575            Editor::from_state_proto(
7576                pane.clone(),
7577                workspace.root_view(cx).unwrap(),
7578                ViewId {
7579                    creator: Default::default(),
7580                    id: 0,
7581                },
7582                &mut state_message,
7583                cx,
7584            )
7585        })
7586        .unwrap()
7587        .unwrap()
7588        .await
7589        .unwrap();
7590
7591    let update_message = Rc::new(RefCell::new(None));
7592    follower_1.update(cx, {
7593        let update = update_message.clone();
7594        |_, cx| {
7595            cx.subscribe(&leader, move |_, leader, event, cx| {
7596                leader
7597                    .read(cx)
7598                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
7599            })
7600            .detach();
7601        }
7602    });
7603
7604    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
7605        (
7606            project
7607                .create_buffer("abc\ndef\nghi\njkl\n", None, cx)
7608                .unwrap(),
7609            project
7610                .create_buffer("mno\npqr\nstu\nvwx\n", None, cx)
7611                .unwrap(),
7612        )
7613    });
7614
7615    // Insert some excerpts.
7616    _ = leader.update(cx, |leader, cx| {
7617        leader.buffer.update(cx, |multibuffer, cx| {
7618            let excerpt_ids = multibuffer.push_excerpts(
7619                buffer_1.clone(),
7620                [
7621                    ExcerptRange {
7622                        context: 1..6,
7623                        primary: None,
7624                    },
7625                    ExcerptRange {
7626                        context: 12..15,
7627                        primary: None,
7628                    },
7629                    ExcerptRange {
7630                        context: 0..3,
7631                        primary: None,
7632                    },
7633                ],
7634                cx,
7635            );
7636            multibuffer.insert_excerpts_after(
7637                excerpt_ids[0],
7638                buffer_2.clone(),
7639                [
7640                    ExcerptRange {
7641                        context: 8..12,
7642                        primary: None,
7643                    },
7644                    ExcerptRange {
7645                        context: 0..6,
7646                        primary: None,
7647                    },
7648                ],
7649                cx,
7650            );
7651        });
7652    });
7653
7654    // Apply the update of adding the excerpts.
7655    follower_1
7656        .update(cx, |follower, cx| {
7657            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
7658        })
7659        .await
7660        .unwrap();
7661    assert_eq!(
7662        follower_1.update(cx, |editor, cx| editor.text(cx)),
7663        leader.update(cx, |editor, cx| editor.text(cx))
7664    );
7665    update_message.borrow_mut().take();
7666
7667    // Start following separately after it already has excerpts.
7668    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
7669    let follower_2 = cx
7670        .update_window(*workspace.deref(), |_, cx| {
7671            Editor::from_state_proto(
7672                pane.clone(),
7673                workspace.root_view(cx).unwrap().clone(),
7674                ViewId {
7675                    creator: Default::default(),
7676                    id: 0,
7677                },
7678                &mut state_message,
7679                cx,
7680            )
7681        })
7682        .unwrap()
7683        .unwrap()
7684        .await
7685        .unwrap();
7686    assert_eq!(
7687        follower_2.update(cx, |editor, cx| editor.text(cx)),
7688        leader.update(cx, |editor, cx| editor.text(cx))
7689    );
7690
7691    // Remove some excerpts.
7692    _ = leader.update(cx, |leader, cx| {
7693        leader.buffer.update(cx, |multibuffer, cx| {
7694            let excerpt_ids = multibuffer.excerpt_ids();
7695            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
7696            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
7697        });
7698    });
7699
7700    // Apply the update of removing the excerpts.
7701    follower_1
7702        .update(cx, |follower, cx| {
7703            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
7704        })
7705        .await
7706        .unwrap();
7707    follower_2
7708        .update(cx, |follower, cx| {
7709            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
7710        })
7711        .await
7712        .unwrap();
7713    update_message.borrow_mut().take();
7714    assert_eq!(
7715        follower_1.update(cx, |editor, cx| editor.text(cx)),
7716        leader.update(cx, |editor, cx| editor.text(cx))
7717    );
7718}
7719
7720#[gpui::test]
7721async fn go_to_prev_overlapping_diagnostic(
7722    executor: BackgroundExecutor,
7723    cx: &mut gpui::TestAppContext,
7724) {
7725    init_test(cx, |_| {});
7726
7727    let mut cx = EditorTestContext::new(cx).await;
7728    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
7729
7730    cx.set_state(indoc! {"
7731        ˇfn func(abc def: i32) -> u32 {
7732        }
7733    "});
7734
7735    _ = cx.update(|cx| {
7736        _ = project.update(cx, |project, cx| {
7737            project
7738                .update_diagnostics(
7739                    LanguageServerId(0),
7740                    lsp::PublishDiagnosticsParams {
7741                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
7742                        version: None,
7743                        diagnostics: vec![
7744                            lsp::Diagnostic {
7745                                range: lsp::Range::new(
7746                                    lsp::Position::new(0, 11),
7747                                    lsp::Position::new(0, 12),
7748                                ),
7749                                severity: Some(lsp::DiagnosticSeverity::ERROR),
7750                                ..Default::default()
7751                            },
7752                            lsp::Diagnostic {
7753                                range: lsp::Range::new(
7754                                    lsp::Position::new(0, 12),
7755                                    lsp::Position::new(0, 15),
7756                                ),
7757                                severity: Some(lsp::DiagnosticSeverity::ERROR),
7758                                ..Default::default()
7759                            },
7760                            lsp::Diagnostic {
7761                                range: lsp::Range::new(
7762                                    lsp::Position::new(0, 25),
7763                                    lsp::Position::new(0, 28),
7764                                ),
7765                                severity: Some(lsp::DiagnosticSeverity::ERROR),
7766                                ..Default::default()
7767                            },
7768                        ],
7769                    },
7770                    &[],
7771                    cx,
7772                )
7773                .unwrap()
7774        });
7775    });
7776
7777    executor.run_until_parked();
7778
7779    cx.update_editor(|editor, cx| {
7780        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
7781    });
7782
7783    cx.assert_editor_state(indoc! {"
7784        fn func(abc def: i32) -> ˇu32 {
7785        }
7786    "});
7787
7788    cx.update_editor(|editor, cx| {
7789        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
7790    });
7791
7792    cx.assert_editor_state(indoc! {"
7793        fn func(abc ˇdef: i32) -> u32 {
7794        }
7795    "});
7796
7797    cx.update_editor(|editor, cx| {
7798        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
7799    });
7800
7801    cx.assert_editor_state(indoc! {"
7802        fn func(abcˇ def: i32) -> u32 {
7803        }
7804    "});
7805
7806    cx.update_editor(|editor, cx| {
7807        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
7808    });
7809
7810    cx.assert_editor_state(indoc! {"
7811        fn func(abc def: i32) -> ˇu32 {
7812        }
7813    "});
7814}
7815
7816#[gpui::test]
7817async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
7818    init_test(cx, |_| {});
7819
7820    let mut cx = EditorTestContext::new(cx).await;
7821
7822    let diff_base = r#"
7823        use some::mod;
7824
7825        const A: u32 = 42;
7826
7827        fn main() {
7828            println!("hello");
7829
7830            println!("world");
7831        }
7832        "#
7833    .unindent();
7834
7835    // Edits are modified, removed, modified, added
7836    cx.set_state(
7837        &r#"
7838        use some::modified;
7839
7840        ˇ
7841        fn main() {
7842            println!("hello there");
7843
7844            println!("around the");
7845            println!("world");
7846        }
7847        "#
7848        .unindent(),
7849    );
7850
7851    cx.set_diff_base(Some(&diff_base));
7852    executor.run_until_parked();
7853
7854    cx.update_editor(|editor, cx| {
7855        //Wrap around the bottom of the buffer
7856        for _ in 0..3 {
7857            editor.go_to_hunk(&GoToHunk, cx);
7858        }
7859    });
7860
7861    cx.assert_editor_state(
7862        &r#"
7863        ˇuse some::modified;
7864
7865
7866        fn main() {
7867            println!("hello there");
7868
7869            println!("around the");
7870            println!("world");
7871        }
7872        "#
7873        .unindent(),
7874    );
7875
7876    cx.update_editor(|editor, cx| {
7877        //Wrap around the top of the buffer
7878        for _ in 0..2 {
7879            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
7880        }
7881    });
7882
7883    cx.assert_editor_state(
7884        &r#"
7885        use some::modified;
7886
7887
7888        fn main() {
7889        ˇ    println!("hello there");
7890
7891            println!("around the");
7892            println!("world");
7893        }
7894        "#
7895        .unindent(),
7896    );
7897
7898    cx.update_editor(|editor, cx| {
7899        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
7900    });
7901
7902    cx.assert_editor_state(
7903        &r#"
7904        use some::modified;
7905
7906        ˇ
7907        fn main() {
7908            println!("hello there");
7909
7910            println!("around the");
7911            println!("world");
7912        }
7913        "#
7914        .unindent(),
7915    );
7916
7917    cx.update_editor(|editor, cx| {
7918        for _ in 0..3 {
7919            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
7920        }
7921    });
7922
7923    cx.assert_editor_state(
7924        &r#"
7925        use some::modified;
7926
7927
7928        fn main() {
7929        ˇ    println!("hello there");
7930
7931            println!("around the");
7932            println!("world");
7933        }
7934        "#
7935        .unindent(),
7936    );
7937
7938    cx.update_editor(|editor, cx| {
7939        editor.fold(&Fold, cx);
7940
7941        //Make sure that the fold only gets one hunk
7942        for _ in 0..4 {
7943            editor.go_to_hunk(&GoToHunk, cx);
7944        }
7945    });
7946
7947    cx.assert_editor_state(
7948        &r#"
7949        ˇuse some::modified;
7950
7951
7952        fn main() {
7953            println!("hello there");
7954
7955            println!("around the");
7956            println!("world");
7957        }
7958        "#
7959        .unindent(),
7960    );
7961}
7962
7963#[test]
7964fn test_split_words() {
7965    fn split(text: &str) -> Vec<&str> {
7966        split_words(text).collect()
7967    }
7968
7969    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
7970    assert_eq!(split("hello_world"), &["hello_", "world"]);
7971    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
7972    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
7973    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
7974    assert_eq!(split("helloworld"), &["helloworld"]);
7975
7976    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
7977}
7978
7979#[gpui::test]
7980async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
7981    init_test(cx, |_| {});
7982
7983    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
7984    let mut assert = |before, after| {
7985        let _state_context = cx.set_state(before);
7986        cx.update_editor(|editor, cx| {
7987            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
7988        });
7989        cx.assert_editor_state(after);
7990    };
7991
7992    // Outside bracket jumps to outside of matching bracket
7993    assert("console.logˇ(var);", "console.log(var)ˇ;");
7994    assert("console.log(var)ˇ;", "console.logˇ(var);");
7995
7996    // Inside bracket jumps to inside of matching bracket
7997    assert("console.log(ˇvar);", "console.log(varˇ);");
7998    assert("console.log(varˇ);", "console.log(ˇvar);");
7999
8000    // When outside a bracket and inside, favor jumping to the inside bracket
8001    assert(
8002        "console.log('foo', [1, 2, 3]ˇ);",
8003        "console.log(ˇ'foo', [1, 2, 3]);",
8004    );
8005    assert(
8006        "console.log(ˇ'foo', [1, 2, 3]);",
8007        "console.log('foo', [1, 2, 3]ˇ);",
8008    );
8009
8010    // Bias forward if two options are equally likely
8011    assert(
8012        "let result = curried_fun()ˇ();",
8013        "let result = curried_fun()()ˇ;",
8014    );
8015
8016    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
8017    assert(
8018        indoc! {"
8019            function test() {
8020                console.log('test')ˇ
8021            }"},
8022        indoc! {"
8023            function test() {
8024                console.logˇ('test')
8025            }"},
8026    );
8027}
8028
8029#[gpui::test]
8030async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
8031    init_test(cx, |_| {});
8032
8033    let fs = FakeFs::new(cx.executor());
8034    fs.insert_tree(
8035        "/a",
8036        json!({
8037            "main.rs": "fn main() { let a = 5; }",
8038            "other.rs": "// Test file",
8039        }),
8040    )
8041    .await;
8042    let project = Project::test(fs, ["/a".as_ref()], cx).await;
8043
8044    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
8045    language_registry.add(Arc::new(Language::new(
8046        LanguageConfig {
8047            name: "Rust".into(),
8048            matcher: LanguageMatcher {
8049                path_suffixes: vec!["rs".to_string()],
8050                ..Default::default()
8051            },
8052            brackets: BracketPairConfig {
8053                pairs: vec![BracketPair {
8054                    start: "{".to_string(),
8055                    end: "}".to_string(),
8056                    close: true,
8057                    newline: true,
8058                }],
8059                disabled_scopes_by_bracket_ix: Vec::new(),
8060            },
8061            ..Default::default()
8062        },
8063        Some(tree_sitter_rust::language()),
8064    )));
8065    let mut fake_servers = language_registry.register_fake_lsp_adapter(
8066        "Rust",
8067        FakeLspAdapter {
8068            capabilities: lsp::ServerCapabilities {
8069                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
8070                    first_trigger_character: "{".to_string(),
8071                    more_trigger_character: None,
8072                }),
8073                ..Default::default()
8074            },
8075            ..Default::default()
8076        },
8077    );
8078
8079    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
8080
8081    let cx = &mut VisualTestContext::from_window(*workspace, cx);
8082
8083    let worktree_id = workspace
8084        .update(cx, |workspace, cx| {
8085            workspace.project().update(cx, |project, cx| {
8086                project.worktrees().next().unwrap().read(cx).id()
8087            })
8088        })
8089        .unwrap();
8090
8091    let buffer = project
8092        .update(cx, |project, cx| {
8093            project.open_local_buffer("/a/main.rs", cx)
8094        })
8095        .await
8096        .unwrap();
8097    cx.executor().run_until_parked();
8098    cx.executor().start_waiting();
8099    let fake_server = fake_servers.next().await.unwrap();
8100    let editor_handle = workspace
8101        .update(cx, |workspace, cx| {
8102            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
8103        })
8104        .unwrap()
8105        .await
8106        .unwrap()
8107        .downcast::<Editor>()
8108        .unwrap();
8109
8110    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
8111        assert_eq!(
8112            params.text_document_position.text_document.uri,
8113            lsp::Url::from_file_path("/a/main.rs").unwrap(),
8114        );
8115        assert_eq!(
8116            params.text_document_position.position,
8117            lsp::Position::new(0, 21),
8118        );
8119
8120        Ok(Some(vec![lsp::TextEdit {
8121            new_text: "]".to_string(),
8122            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
8123        }]))
8124    });
8125
8126    editor_handle.update(cx, |editor, cx| {
8127        editor.focus(cx);
8128        editor.change_selections(None, cx, |s| {
8129            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
8130        });
8131        editor.handle_input("{", cx);
8132    });
8133
8134    cx.executor().run_until_parked();
8135
8136    _ = buffer.update(cx, |buffer, _| {
8137        assert_eq!(
8138            buffer.text(),
8139            "fn main() { let a = {5}; }",
8140            "No extra braces from on type formatting should appear in the buffer"
8141        )
8142    });
8143}
8144
8145#[gpui::test]
8146async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
8147    init_test(cx, |_| {});
8148
8149    let fs = FakeFs::new(cx.executor());
8150    fs.insert_tree(
8151        "/a",
8152        json!({
8153            "main.rs": "fn main() { let a = 5; }",
8154            "other.rs": "// Test file",
8155        }),
8156    )
8157    .await;
8158
8159    let project = Project::test(fs, ["/a".as_ref()], cx).await;
8160
8161    let server_restarts = Arc::new(AtomicUsize::new(0));
8162    let closure_restarts = Arc::clone(&server_restarts);
8163    let language_server_name = "test language server";
8164    let language_name: Arc<str> = "Rust".into();
8165
8166    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
8167    language_registry.add(Arc::new(Language::new(
8168        LanguageConfig {
8169            name: Arc::clone(&language_name),
8170            matcher: LanguageMatcher {
8171                path_suffixes: vec!["rs".to_string()],
8172                ..Default::default()
8173            },
8174            ..Default::default()
8175        },
8176        Some(tree_sitter_rust::language()),
8177    )));
8178    let mut fake_servers = language_registry.register_fake_lsp_adapter(
8179        "Rust",
8180        FakeLspAdapter {
8181            name: language_server_name,
8182            initialization_options: Some(json!({
8183                "testOptionValue": true
8184            })),
8185            initializer: Some(Box::new(move |fake_server| {
8186                let task_restarts = Arc::clone(&closure_restarts);
8187                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
8188                    task_restarts.fetch_add(1, atomic::Ordering::Release);
8189                    futures::future::ready(Ok(()))
8190                });
8191            })),
8192            ..Default::default()
8193        },
8194    );
8195
8196    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
8197    let _buffer = project
8198        .update(cx, |project, cx| {
8199            project.open_local_buffer("/a/main.rs", cx)
8200        })
8201        .await
8202        .unwrap();
8203    let _fake_server = fake_servers.next().await.unwrap();
8204    update_test_language_settings(cx, |language_settings| {
8205        language_settings.languages.insert(
8206            Arc::clone(&language_name),
8207            LanguageSettingsContent {
8208                tab_size: NonZeroU32::new(8),
8209                ..Default::default()
8210            },
8211        );
8212    });
8213    cx.executor().run_until_parked();
8214    assert_eq!(
8215        server_restarts.load(atomic::Ordering::Acquire),
8216        0,
8217        "Should not restart LSP server on an unrelated change"
8218    );
8219
8220    update_test_project_settings(cx, |project_settings| {
8221        project_settings.lsp.insert(
8222            "Some other server name".into(),
8223            LspSettings {
8224                binary: None,
8225                settings: None,
8226                initialization_options: Some(json!({
8227                    "some other init value": false
8228                })),
8229            },
8230        );
8231    });
8232    cx.executor().run_until_parked();
8233    assert_eq!(
8234        server_restarts.load(atomic::Ordering::Acquire),
8235        0,
8236        "Should not restart LSP server on an unrelated LSP settings change"
8237    );
8238
8239    update_test_project_settings(cx, |project_settings| {
8240        project_settings.lsp.insert(
8241            language_server_name.into(),
8242            LspSettings {
8243                binary: None,
8244                settings: None,
8245                initialization_options: Some(json!({
8246                    "anotherInitValue": false
8247                })),
8248            },
8249        );
8250    });
8251    cx.executor().run_until_parked();
8252    assert_eq!(
8253        server_restarts.load(atomic::Ordering::Acquire),
8254        1,
8255        "Should restart LSP server on a related LSP settings change"
8256    );
8257
8258    update_test_project_settings(cx, |project_settings| {
8259        project_settings.lsp.insert(
8260            language_server_name.into(),
8261            LspSettings {
8262                binary: None,
8263                settings: None,
8264                initialization_options: Some(json!({
8265                    "anotherInitValue": false
8266                })),
8267            },
8268        );
8269    });
8270    cx.executor().run_until_parked();
8271    assert_eq!(
8272        server_restarts.load(atomic::Ordering::Acquire),
8273        1,
8274        "Should not restart LSP server on a related LSP settings change that is the same"
8275    );
8276
8277    update_test_project_settings(cx, |project_settings| {
8278        project_settings.lsp.insert(
8279            language_server_name.into(),
8280            LspSettings {
8281                binary: None,
8282                settings: None,
8283                initialization_options: None,
8284            },
8285        );
8286    });
8287    cx.executor().run_until_parked();
8288    assert_eq!(
8289        server_restarts.load(atomic::Ordering::Acquire),
8290        2,
8291        "Should restart LSP server on another related LSP settings change"
8292    );
8293}
8294
8295#[gpui::test]
8296async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
8297    init_test(cx, |_| {});
8298
8299    let mut cx = EditorLspTestContext::new_rust(
8300        lsp::ServerCapabilities {
8301            completion_provider: Some(lsp::CompletionOptions {
8302                trigger_characters: Some(vec![".".to_string()]),
8303                resolve_provider: Some(true),
8304                ..Default::default()
8305            }),
8306            ..Default::default()
8307        },
8308        cx,
8309    )
8310    .await;
8311
8312    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
8313    cx.simulate_keystroke(".");
8314    let completion_item = lsp::CompletionItem {
8315        label: "some".into(),
8316        kind: Some(lsp::CompletionItemKind::SNIPPET),
8317        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
8318        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
8319            kind: lsp::MarkupKind::Markdown,
8320            value: "```rust\nSome(2)\n```".to_string(),
8321        })),
8322        deprecated: Some(false),
8323        sort_text: Some("fffffff2".to_string()),
8324        filter_text: Some("some".to_string()),
8325        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
8326        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
8327            range: lsp::Range {
8328                start: lsp::Position {
8329                    line: 0,
8330                    character: 22,
8331                },
8332                end: lsp::Position {
8333                    line: 0,
8334                    character: 22,
8335                },
8336            },
8337            new_text: "Some(2)".to_string(),
8338        })),
8339        additional_text_edits: Some(vec![lsp::TextEdit {
8340            range: lsp::Range {
8341                start: lsp::Position {
8342                    line: 0,
8343                    character: 20,
8344                },
8345                end: lsp::Position {
8346                    line: 0,
8347                    character: 22,
8348                },
8349            },
8350            new_text: "".to_string(),
8351        }]),
8352        ..Default::default()
8353    };
8354
8355    let closure_completion_item = completion_item.clone();
8356    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
8357        let task_completion_item = closure_completion_item.clone();
8358        async move {
8359            Ok(Some(lsp::CompletionResponse::Array(vec![
8360                task_completion_item,
8361            ])))
8362        }
8363    });
8364
8365    request.next().await;
8366
8367    cx.condition(|editor, _| editor.context_menu_visible())
8368        .await;
8369    let apply_additional_edits = cx.update_editor(|editor, cx| {
8370        editor
8371            .confirm_completion(&ConfirmCompletion::default(), cx)
8372            .unwrap()
8373    });
8374    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
8375
8376    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
8377        let task_completion_item = completion_item.clone();
8378        async move { Ok(task_completion_item) }
8379    })
8380    .next()
8381    .await
8382    .unwrap();
8383    apply_additional_edits.await.unwrap();
8384    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
8385}
8386
8387#[gpui::test]
8388async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
8389    init_test(cx, |_| {});
8390
8391    let mut cx = EditorLspTestContext::new(
8392        Language::new(
8393            LanguageConfig {
8394                matcher: LanguageMatcher {
8395                    path_suffixes: vec!["jsx".into()],
8396                    ..Default::default()
8397                },
8398                overrides: [(
8399                    "element".into(),
8400                    LanguageConfigOverride {
8401                        word_characters: Override::Set(['-'].into_iter().collect()),
8402                        ..Default::default()
8403                    },
8404                )]
8405                .into_iter()
8406                .collect(),
8407                ..Default::default()
8408            },
8409            Some(tree_sitter_typescript::language_tsx()),
8410        )
8411        .with_override_query("(jsx_self_closing_element) @element")
8412        .unwrap(),
8413        lsp::ServerCapabilities {
8414            completion_provider: Some(lsp::CompletionOptions {
8415                trigger_characters: Some(vec![":".to_string()]),
8416                ..Default::default()
8417            }),
8418            ..Default::default()
8419        },
8420        cx,
8421    )
8422    .await;
8423
8424    cx.lsp
8425        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
8426            Ok(Some(lsp::CompletionResponse::Array(vec![
8427                lsp::CompletionItem {
8428                    label: "bg-blue".into(),
8429                    ..Default::default()
8430                },
8431                lsp::CompletionItem {
8432                    label: "bg-red".into(),
8433                    ..Default::default()
8434                },
8435                lsp::CompletionItem {
8436                    label: "bg-yellow".into(),
8437                    ..Default::default()
8438                },
8439            ])))
8440        });
8441
8442    cx.set_state(r#"<p class="bgˇ" />"#);
8443
8444    // Trigger completion when typing a dash, because the dash is an extra
8445    // word character in the 'element' scope, which contains the cursor.
8446    cx.simulate_keystroke("-");
8447    cx.executor().run_until_parked();
8448    cx.update_editor(|editor, _| {
8449        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
8450            assert_eq!(
8451                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
8452                &["bg-red", "bg-blue", "bg-yellow"]
8453            );
8454        } else {
8455            panic!("expected completion menu to be open");
8456        }
8457    });
8458
8459    cx.simulate_keystroke("l");
8460    cx.executor().run_until_parked();
8461    cx.update_editor(|editor, _| {
8462        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
8463            assert_eq!(
8464                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
8465                &["bg-blue", "bg-yellow"]
8466            );
8467        } else {
8468            panic!("expected completion menu to be open");
8469        }
8470    });
8471
8472    // When filtering completions, consider the character after the '-' to
8473    // be the start of a subword.
8474    cx.set_state(r#"<p class="yelˇ" />"#);
8475    cx.simulate_keystroke("l");
8476    cx.executor().run_until_parked();
8477    cx.update_editor(|editor, _| {
8478        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
8479            assert_eq!(
8480                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
8481                &["bg-yellow"]
8482            );
8483        } else {
8484            panic!("expected completion menu to be open");
8485        }
8486    });
8487}
8488
8489#[gpui::test]
8490async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
8491    init_test(cx, |settings| {
8492        settings.defaults.formatter = Some(language_settings::Formatter::Prettier)
8493    });
8494
8495    let fs = FakeFs::new(cx.executor());
8496    fs.insert_file("/file.rs", Default::default()).await;
8497
8498    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
8499    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
8500
8501    language_registry.add(Arc::new(Language::new(
8502        LanguageConfig {
8503            name: "Rust".into(),
8504            matcher: LanguageMatcher {
8505                path_suffixes: vec!["rs".to_string()],
8506                ..Default::default()
8507            },
8508            prettier_parser_name: Some("test_parser".to_string()),
8509            ..Default::default()
8510        },
8511        Some(tree_sitter_rust::language()),
8512    )));
8513
8514    let test_plugin = "test_plugin";
8515    let _ = language_registry.register_fake_lsp_adapter(
8516        "Rust",
8517        FakeLspAdapter {
8518            prettier_plugins: vec![test_plugin],
8519            ..Default::default()
8520        },
8521    );
8522
8523    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
8524    let buffer = project
8525        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
8526        .await
8527        .unwrap();
8528
8529    let buffer_text = "one\ntwo\nthree\n";
8530    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
8531    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
8532    _ = editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
8533
8534    editor
8535        .update(cx, |editor, cx| {
8536            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
8537        })
8538        .unwrap()
8539        .await;
8540    assert_eq!(
8541        editor.update(cx, |editor, cx| editor.text(cx)),
8542        buffer_text.to_string() + prettier_format_suffix,
8543        "Test prettier formatting was not applied to the original buffer text",
8544    );
8545
8546    update_test_language_settings(cx, |settings| {
8547        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
8548    });
8549    let format = editor.update(cx, |editor, cx| {
8550        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
8551    });
8552    format.await.unwrap();
8553    assert_eq!(
8554        editor.update(cx, |editor, cx| editor.text(cx)),
8555        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
8556        "Autoformatting (via test prettier) was not applied to the original buffer text",
8557    );
8558}
8559
8560#[gpui::test]
8561async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
8562    init_test(cx, |_| {});
8563    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
8564    let base_text = indoc! {r#"struct Row;
8565struct Row1;
8566struct Row2;
8567
8568struct Row4;
8569struct Row5;
8570struct Row6;
8571
8572struct Row8;
8573struct Row9;
8574struct Row10;"#};
8575
8576    // When addition hunks are not adjacent to carets, no hunk revert is performed
8577    assert_hunk_revert(
8578        indoc! {r#"struct Row;
8579                   struct Row1;
8580                   struct Row1.1;
8581                   struct Row1.2;
8582                   struct Row2;ˇ
8583
8584                   struct Row4;
8585                   struct Row5;
8586                   struct Row6;
8587
8588                   struct Row8;
8589                   ˇstruct Row9;
8590                   struct Row9.1;
8591                   struct Row9.2;
8592                   struct Row9.3;
8593                   struct Row10;"#},
8594        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
8595        indoc! {r#"struct Row;
8596                   struct Row1;
8597                   struct Row1.1;
8598                   struct Row1.2;
8599                   struct Row2;ˇ
8600
8601                   struct Row4;
8602                   struct Row5;
8603                   struct Row6;
8604
8605                   struct Row8;
8606                   ˇstruct Row9;
8607                   struct Row9.1;
8608                   struct Row9.2;
8609                   struct Row9.3;
8610                   struct Row10;"#},
8611        base_text,
8612        &mut cx,
8613    );
8614    // Same for selections
8615    assert_hunk_revert(
8616        indoc! {r#"struct Row;
8617                   struct Row1;
8618                   struct Row2;
8619                   struct Row2.1;
8620                   struct Row2.2;
8621                   «ˇ
8622                   struct Row4;
8623                   struct» Row5;
8624                   «struct Row6;
8625                   ˇ»
8626                   struct Row9.1;
8627                   struct Row9.2;
8628                   struct Row9.3;
8629                   struct Row8;
8630                   struct Row9;
8631                   struct Row10;"#},
8632        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
8633        indoc! {r#"struct Row;
8634                   struct Row1;
8635                   struct Row2;
8636                   struct Row2.1;
8637                   struct Row2.2;
8638                   «ˇ
8639                   struct Row4;
8640                   struct» Row5;
8641                   «struct Row6;
8642                   ˇ»
8643                   struct Row9.1;
8644                   struct Row9.2;
8645                   struct Row9.3;
8646                   struct Row8;
8647                   struct Row9;
8648                   struct Row10;"#},
8649        base_text,
8650        &mut cx,
8651    );
8652
8653    // When carets and selections intersect the addition hunks, those are reverted.
8654    // Adjacent carets got merged.
8655    assert_hunk_revert(
8656        indoc! {r#"struct Row;
8657                   ˇ// something on the top
8658                   struct Row1;
8659                   struct Row2;
8660                   struct Roˇw3.1;
8661                   struct Row2.2;
8662                   struct Row2.3;ˇ
8663
8664                   struct Row4;
8665                   struct ˇRow5.1;
8666                   struct Row5.2;
8667                   struct «Rowˇ»5.3;
8668                   struct Row5;
8669                   struct Row6;
8670                   ˇ
8671                   struct Row9.1;
8672                   struct «Rowˇ»9.2;
8673                   struct «ˇRow»9.3;
8674                   struct Row8;
8675                   struct Row9;
8676                   «ˇ// something on bottom»
8677                   struct Row10;"#},
8678        vec![
8679            DiffHunkStatus::Added,
8680            DiffHunkStatus::Added,
8681            DiffHunkStatus::Added,
8682            DiffHunkStatus::Added,
8683            DiffHunkStatus::Added,
8684        ],
8685        indoc! {r#"struct Row;
8686                   ˇstruct Row1;
8687                   struct Row2;
8688                   ˇ
8689                   struct Row4;
8690                   ˇstruct Row5;
8691                   struct Row6;
8692                   ˇ
8693                   ˇstruct Row8;
8694                   struct Row9;
8695                   ˇstruct Row10;"#},
8696        base_text,
8697        &mut cx,
8698    );
8699}
8700
8701#[gpui::test]
8702async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
8703    init_test(cx, |_| {});
8704    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
8705    let base_text = indoc! {r#"struct Row;
8706struct Row1;
8707struct Row2;
8708
8709struct Row4;
8710struct Row5;
8711struct Row6;
8712
8713struct Row8;
8714struct Row9;
8715struct Row10;"#};
8716
8717    // Modification hunks behave the same as the addition ones.
8718    assert_hunk_revert(
8719        indoc! {r#"struct Row;
8720                   struct Row1;
8721                   struct Row33;
8722                   ˇ
8723                   struct Row4;
8724                   struct Row5;
8725                   struct Row6;
8726                   ˇ
8727                   struct Row99;
8728                   struct Row9;
8729                   struct Row10;"#},
8730        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
8731        indoc! {r#"struct Row;
8732                   struct Row1;
8733                   struct Row33;
8734                   ˇ
8735                   struct Row4;
8736                   struct Row5;
8737                   struct Row6;
8738                   ˇ
8739                   struct Row99;
8740                   struct Row9;
8741                   struct Row10;"#},
8742        base_text,
8743        &mut cx,
8744    );
8745    assert_hunk_revert(
8746        indoc! {r#"struct Row;
8747                   struct Row1;
8748                   struct Row33;
8749                   «ˇ
8750                   struct Row4;
8751                   struct» Row5;
8752                   «struct Row6;
8753                   ˇ»
8754                   struct Row99;
8755                   struct Row9;
8756                   struct Row10;"#},
8757        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
8758        indoc! {r#"struct Row;
8759                   struct Row1;
8760                   struct Row33;
8761                   «ˇ
8762                   struct Row4;
8763                   struct» Row5;
8764                   «struct Row6;
8765                   ˇ»
8766                   struct Row99;
8767                   struct Row9;
8768                   struct Row10;"#},
8769        base_text,
8770        &mut cx,
8771    );
8772
8773    assert_hunk_revert(
8774        indoc! {r#"ˇstruct Row1.1;
8775                   struct Row1;
8776                   «ˇstr»uct Row22;
8777
8778                   struct ˇRow44;
8779                   struct Row5;
8780                   struct «Rˇ»ow66;ˇ
8781
8782                   «struˇ»ct Row88;
8783                   struct Row9;
8784                   struct Row1011;ˇ"#},
8785        vec![
8786            DiffHunkStatus::Modified,
8787            DiffHunkStatus::Modified,
8788            DiffHunkStatus::Modified,
8789            DiffHunkStatus::Modified,
8790            DiffHunkStatus::Modified,
8791            DiffHunkStatus::Modified,
8792        ],
8793        indoc! {r#"struct Row;
8794                   ˇstruct Row1;
8795                   struct Row2;
8796                   ˇ
8797                   struct Row4;
8798                   ˇstruct Row5;
8799                   struct Row6;
8800                   ˇ
8801                   struct Row8;
8802                   ˇstruct Row9;
8803                   struct Row10;ˇ"#},
8804        base_text,
8805        &mut cx,
8806    );
8807}
8808
8809#[gpui::test]
8810async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
8811    init_test(cx, |_| {});
8812    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
8813    let base_text = indoc! {r#"struct Row;
8814struct Row1;
8815struct Row2;
8816
8817struct Row4;
8818struct Row5;
8819struct Row6;
8820
8821struct Row8;
8822struct Row9;
8823struct Row10;"#};
8824
8825    // Deletion hunks trigger with carets on ajacent rows, so carets and selections have to stay farther to avoid the revert
8826    assert_hunk_revert(
8827        indoc! {r#"struct Row;
8828                   struct Row2;
8829
8830                   ˇstruct Row4;
8831                   struct Row5;
8832                   struct Row6;
8833                   ˇ
8834                   struct Row8;
8835                   struct Row10;"#},
8836        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
8837        indoc! {r#"struct Row;
8838                   struct Row2;
8839
8840                   ˇstruct Row4;
8841                   struct Row5;
8842                   struct Row6;
8843                   ˇ
8844                   struct Row8;
8845                   struct Row10;"#},
8846        base_text,
8847        &mut cx,
8848    );
8849    assert_hunk_revert(
8850        indoc! {r#"struct Row;
8851                   struct Row2;
8852
8853                   «ˇstruct Row4;
8854                   struct» Row5;
8855                   «struct Row6;
8856                   ˇ»
8857                   struct Row8;
8858                   struct Row10;"#},
8859        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
8860        indoc! {r#"struct Row;
8861                   struct Row2;
8862
8863                   «ˇstruct Row4;
8864                   struct» Row5;
8865                   «struct Row6;
8866                   ˇ»
8867                   struct Row8;
8868                   struct Row10;"#},
8869        base_text,
8870        &mut cx,
8871    );
8872
8873    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
8874    assert_hunk_revert(
8875        indoc! {r#"struct Row;
8876                   ˇstruct Row2;
8877
8878                   struct Row4;
8879                   struct Row5;
8880                   struct Row6;
8881
8882                   struct Row8;ˇ
8883                   struct Row10;"#},
8884        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
8885        indoc! {r#"struct Row;
8886                   struct Row1;
8887                   ˇstruct Row2;
8888
8889                   struct Row4;
8890                   struct Row5;
8891                   struct Row6;
8892
8893                   struct Row8;ˇ
8894                   struct Row9;
8895                   struct Row10;"#},
8896        base_text,
8897        &mut cx,
8898    );
8899    assert_hunk_revert(
8900        indoc! {r#"struct Row;
8901                   struct Row2«ˇ;
8902                   struct Row4;
8903                   struct» Row5;
8904                   «struct Row6;
8905
8906                   struct Row8;ˇ»
8907                   struct Row10;"#},
8908        vec![
8909            DiffHunkStatus::Removed,
8910            DiffHunkStatus::Removed,
8911            DiffHunkStatus::Removed,
8912        ],
8913        indoc! {r#"struct Row;
8914                   struct Row1;
8915                   struct Row2«ˇ;
8916
8917                   struct Row4;
8918                   struct» Row5;
8919                   «struct Row6;
8920
8921                   struct Row8;ˇ»
8922                   struct Row9;
8923                   struct Row10;"#},
8924        base_text,
8925        &mut cx,
8926    );
8927}
8928
8929#[gpui::test]
8930async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
8931    init_test(cx, |_| {});
8932
8933    let cols = 4;
8934    let rows = 10;
8935    let sample_text_1 = sample_text(rows, cols, 'a');
8936    assert_eq!(
8937        sample_text_1,
8938        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
8939    );
8940    let sample_text_2 = sample_text(rows, cols, 'l');
8941    assert_eq!(
8942        sample_text_2,
8943        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
8944    );
8945    let sample_text_3 = sample_text(rows, cols, 'v');
8946    assert_eq!(
8947        sample_text_3,
8948        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
8949    );
8950
8951    fn diff_every_buffer_row(
8952        buffer: &Model<Buffer>,
8953        sample_text: String,
8954        cols: usize,
8955        cx: &mut gpui::TestAppContext,
8956    ) {
8957        // revert first character in each row, creating one large diff hunk per buffer
8958        let is_first_char = |offset: usize| offset % cols == 0;
8959        buffer.update(cx, |buffer, cx| {
8960            buffer.set_text(
8961                sample_text
8962                    .chars()
8963                    .enumerate()
8964                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
8965                    .collect::<String>(),
8966                cx,
8967            );
8968            buffer.set_diff_base(Some(sample_text), cx);
8969        });
8970        cx.executor().run_until_parked();
8971    }
8972
8973    let buffer_1 = cx.new_model(|cx| {
8974        Buffer::new(
8975            0,
8976            BufferId::new(cx.entity_id().as_u64()).unwrap(),
8977            sample_text_1.clone(),
8978        )
8979    });
8980    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
8981
8982    let buffer_2 = cx.new_model(|cx| {
8983        Buffer::new(
8984            1,
8985            BufferId::new(cx.entity_id().as_u64() + 1).unwrap(),
8986            sample_text_2.clone(),
8987        )
8988    });
8989    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
8990
8991    let buffer_3 = cx.new_model(|cx| {
8992        Buffer::new(
8993            2,
8994            BufferId::new(cx.entity_id().as_u64() + 2).unwrap(),
8995            sample_text_3.clone(),
8996        )
8997    });
8998    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
8999
9000    let multibuffer = cx.new_model(|cx| {
9001        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
9002        multibuffer.push_excerpts(
9003            buffer_1.clone(),
9004            [
9005                ExcerptRange {
9006                    context: Point::new(0, 0)..Point::new(3, 0),
9007                    primary: None,
9008                },
9009                ExcerptRange {
9010                    context: Point::new(5, 0)..Point::new(7, 0),
9011                    primary: None,
9012                },
9013                ExcerptRange {
9014                    context: Point::new(9, 0)..Point::new(10, 4),
9015                    primary: None,
9016                },
9017            ],
9018            cx,
9019        );
9020        multibuffer.push_excerpts(
9021            buffer_2.clone(),
9022            [
9023                ExcerptRange {
9024                    context: Point::new(0, 0)..Point::new(3, 0),
9025                    primary: None,
9026                },
9027                ExcerptRange {
9028                    context: Point::new(5, 0)..Point::new(7, 0),
9029                    primary: None,
9030                },
9031                ExcerptRange {
9032                    context: Point::new(9, 0)..Point::new(10, 4),
9033                    primary: None,
9034                },
9035            ],
9036            cx,
9037        );
9038        multibuffer.push_excerpts(
9039            buffer_3.clone(),
9040            [
9041                ExcerptRange {
9042                    context: Point::new(0, 0)..Point::new(3, 0),
9043                    primary: None,
9044                },
9045                ExcerptRange {
9046                    context: Point::new(5, 0)..Point::new(7, 0),
9047                    primary: None,
9048                },
9049                ExcerptRange {
9050                    context: Point::new(9, 0)..Point::new(10, 4),
9051                    primary: None,
9052                },
9053            ],
9054            cx,
9055        );
9056        multibuffer
9057    });
9058
9059    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
9060    editor.update(cx, |editor, cx| {
9061        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");
9062        editor.select_all(&SelectAll, cx);
9063        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
9064    });
9065    cx.executor().run_until_parked();
9066    // When all ranges are selected, all buffer hunks are reverted.
9067    editor.update(cx, |editor, cx| {
9068        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");
9069    });
9070    buffer_1.update(cx, |buffer, _| {
9071        assert_eq!(buffer.text(), sample_text_1);
9072    });
9073    buffer_2.update(cx, |buffer, _| {
9074        assert_eq!(buffer.text(), sample_text_2);
9075    });
9076    buffer_3.update(cx, |buffer, _| {
9077        assert_eq!(buffer.text(), sample_text_3);
9078    });
9079
9080    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
9081    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
9082    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
9083    editor.update(cx, |editor, cx| {
9084        editor.change_selections(None, cx, |s| {
9085            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
9086        });
9087        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
9088    });
9089    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
9090    // but not affect buffer_2 and its related excerpts.
9091    editor.update(cx, |editor, cx| {
9092        assert_eq!(
9093            editor.text(cx),
9094            "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"
9095        );
9096    });
9097    buffer_1.update(cx, |buffer, _| {
9098        assert_eq!(buffer.text(), sample_text_1);
9099    });
9100    buffer_2.update(cx, |buffer, _| {
9101        assert_eq!(
9102            buffer.text(),
9103            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
9104        );
9105    });
9106    buffer_3.update(cx, |buffer, _| {
9107        assert_eq!(
9108            buffer.text(),
9109            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
9110        );
9111    });
9112}
9113
9114#[gpui::test]
9115async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
9116    init_test(cx, |_| {});
9117
9118    let cols = 4;
9119    let rows = 10;
9120    let sample_text_1 = sample_text(rows, cols, 'a');
9121    assert_eq!(
9122        sample_text_1,
9123        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
9124    );
9125    let sample_text_2 = sample_text(rows, cols, 'l');
9126    assert_eq!(
9127        sample_text_2,
9128        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
9129    );
9130    let sample_text_3 = sample_text(rows, cols, 'v');
9131    assert_eq!(
9132        sample_text_3,
9133        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
9134    );
9135
9136    let buffer_1 = cx.new_model(|cx| {
9137        Buffer::new(
9138            0,
9139            BufferId::new(cx.entity_id().as_u64()).unwrap(),
9140            sample_text_1.clone(),
9141        )
9142    });
9143
9144    let buffer_2 = cx.new_model(|cx| {
9145        Buffer::new(
9146            1,
9147            BufferId::new(cx.entity_id().as_u64() + 1).unwrap(),
9148            sample_text_2.clone(),
9149        )
9150    });
9151
9152    let buffer_3 = cx.new_model(|cx| {
9153        Buffer::new(
9154            2,
9155            BufferId::new(cx.entity_id().as_u64() + 2).unwrap(),
9156            sample_text_3.clone(),
9157        )
9158    });
9159
9160    let multi_buffer = cx.new_model(|cx| {
9161        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
9162        multibuffer.push_excerpts(
9163            buffer_1.clone(),
9164            [
9165                ExcerptRange {
9166                    context: Point::new(0, 0)..Point::new(3, 0),
9167                    primary: None,
9168                },
9169                ExcerptRange {
9170                    context: Point::new(5, 0)..Point::new(7, 0),
9171                    primary: None,
9172                },
9173                ExcerptRange {
9174                    context: Point::new(9, 0)..Point::new(10, 4),
9175                    primary: None,
9176                },
9177            ],
9178            cx,
9179        );
9180        multibuffer.push_excerpts(
9181            buffer_2.clone(),
9182            [
9183                ExcerptRange {
9184                    context: Point::new(0, 0)..Point::new(3, 0),
9185                    primary: None,
9186                },
9187                ExcerptRange {
9188                    context: Point::new(5, 0)..Point::new(7, 0),
9189                    primary: None,
9190                },
9191                ExcerptRange {
9192                    context: Point::new(9, 0)..Point::new(10, 4),
9193                    primary: None,
9194                },
9195            ],
9196            cx,
9197        );
9198        multibuffer.push_excerpts(
9199            buffer_3.clone(),
9200            [
9201                ExcerptRange {
9202                    context: Point::new(0, 0)..Point::new(3, 0),
9203                    primary: None,
9204                },
9205                ExcerptRange {
9206                    context: Point::new(5, 0)..Point::new(7, 0),
9207                    primary: None,
9208                },
9209                ExcerptRange {
9210                    context: Point::new(9, 0)..Point::new(10, 4),
9211                    primary: None,
9212                },
9213            ],
9214            cx,
9215        );
9216        multibuffer
9217    });
9218
9219    let fs = FakeFs::new(cx.executor());
9220    fs.insert_tree(
9221        "/a",
9222        json!({
9223            "main.rs": sample_text_1,
9224            "other.rs": sample_text_2,
9225            "lib.rs": sample_text_3,
9226        }),
9227    )
9228    .await;
9229    let project = Project::test(fs, ["/a".as_ref()], cx).await;
9230    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
9231    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
9232    let multi_buffer_editor =
9233        cx.new_view(|cx| Editor::new(EditorMode::Full, multi_buffer, Some(project.clone()), cx));
9234    let multibuffer_item_id = workspace
9235        .update(cx, |workspace, cx| {
9236            assert!(
9237                workspace.active_item(cx).is_none(),
9238                "active item should be None before the first item is added"
9239            );
9240            workspace.add_item_to_active_pane(Box::new(multi_buffer_editor.clone()), cx);
9241            let active_item = workspace
9242                .active_item(cx)
9243                .expect("should have an active item after adding the multi buffer");
9244            assert!(
9245                !active_item.is_singleton(cx),
9246                "A multi buffer was expected to active after adding"
9247            );
9248            active_item.item_id()
9249        })
9250        .unwrap();
9251    cx.executor().run_until_parked();
9252
9253    multi_buffer_editor.update(cx, |editor, cx| {
9254        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
9255        editor.open_excerpts(&OpenExcerpts, cx);
9256    });
9257    cx.executor().run_until_parked();
9258    let first_item_id = workspace
9259        .update(cx, |workspace, cx| {
9260            let active_item = workspace
9261                .active_item(cx)
9262                .expect("should have an active item after navigating into the 1st buffer");
9263            let first_item_id = active_item.item_id();
9264            assert_ne!(
9265                first_item_id, multibuffer_item_id,
9266                "Should navigate into the 1st buffer and activate it"
9267            );
9268            assert!(
9269                active_item.is_singleton(cx),
9270                "New active item should be a singleton buffer"
9271            );
9272            assert_eq!(
9273                active_item
9274                    .act_as::<Editor>(cx)
9275                    .expect("should have navigated into an editor for the 1st buffer")
9276                    .read(cx)
9277                    .text(cx),
9278                sample_text_1
9279            );
9280
9281            workspace
9282                .go_back(workspace.active_pane().downgrade(), cx)
9283                .detach_and_log_err(cx);
9284
9285            first_item_id
9286        })
9287        .unwrap();
9288    cx.executor().run_until_parked();
9289    workspace
9290        .update(cx, |workspace, cx| {
9291            let active_item = workspace
9292                .active_item(cx)
9293                .expect("should have an active item after navigating back");
9294            assert_eq!(
9295                active_item.item_id(),
9296                multibuffer_item_id,
9297                "Should navigate back to the multi buffer"
9298            );
9299            assert!(!active_item.is_singleton(cx));
9300        })
9301        .unwrap();
9302
9303    multi_buffer_editor.update(cx, |editor, cx| {
9304        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
9305            s.select_ranges(Some(39..40))
9306        });
9307        editor.open_excerpts(&OpenExcerpts, cx);
9308    });
9309    cx.executor().run_until_parked();
9310    let second_item_id = workspace
9311        .update(cx, |workspace, cx| {
9312            let active_item = workspace
9313                .active_item(cx)
9314                .expect("should have an active item after navigating into the 2nd buffer");
9315            let second_item_id = active_item.item_id();
9316            assert_ne!(
9317                second_item_id, multibuffer_item_id,
9318                "Should navigate away from the multibuffer"
9319            );
9320            assert_ne!(
9321                second_item_id, first_item_id,
9322                "Should navigate into the 2nd buffer and activate it"
9323            );
9324            assert!(
9325                active_item.is_singleton(cx),
9326                "New active item should be a singleton buffer"
9327            );
9328            assert_eq!(
9329                active_item
9330                    .act_as::<Editor>(cx)
9331                    .expect("should have navigated into an editor")
9332                    .read(cx)
9333                    .text(cx),
9334                sample_text_2
9335            );
9336
9337            workspace
9338                .go_back(workspace.active_pane().downgrade(), cx)
9339                .detach_and_log_err(cx);
9340
9341            second_item_id
9342        })
9343        .unwrap();
9344    cx.executor().run_until_parked();
9345    workspace
9346        .update(cx, |workspace, cx| {
9347            let active_item = workspace
9348                .active_item(cx)
9349                .expect("should have an active item after navigating back from the 2nd buffer");
9350            assert_eq!(
9351                active_item.item_id(),
9352                multibuffer_item_id,
9353                "Should navigate back from the 2nd buffer to the multi buffer"
9354            );
9355            assert!(!active_item.is_singleton(cx));
9356        })
9357        .unwrap();
9358
9359    multi_buffer_editor.update(cx, |editor, cx| {
9360        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
9361            s.select_ranges(Some(60..70))
9362        });
9363        editor.open_excerpts(&OpenExcerpts, cx);
9364    });
9365    cx.executor().run_until_parked();
9366    workspace
9367        .update(cx, |workspace, cx| {
9368            let active_item = workspace
9369                .active_item(cx)
9370                .expect("should have an active item after navigating into the 3rd buffer");
9371            let third_item_id = active_item.item_id();
9372            assert_ne!(
9373                third_item_id, multibuffer_item_id,
9374                "Should navigate into the 3rd buffer and activate it"
9375            );
9376            assert_ne!(third_item_id, first_item_id);
9377            assert_ne!(third_item_id, second_item_id);
9378            assert!(
9379                active_item.is_singleton(cx),
9380                "New active item should be a singleton buffer"
9381            );
9382            assert_eq!(
9383                active_item
9384                    .act_as::<Editor>(cx)
9385                    .expect("should have navigated into an editor")
9386                    .read(cx)
9387                    .text(cx),
9388                sample_text_3
9389            );
9390
9391            workspace
9392                .go_back(workspace.active_pane().downgrade(), cx)
9393                .detach_and_log_err(cx);
9394        })
9395        .unwrap();
9396    cx.executor().run_until_parked();
9397    workspace
9398        .update(cx, |workspace, cx| {
9399            let active_item = workspace
9400                .active_item(cx)
9401                .expect("should have an active item after navigating back from the 3rd buffer");
9402            assert_eq!(
9403                active_item.item_id(),
9404                multibuffer_item_id,
9405                "Should navigate back from the 3rd buffer to the multi buffer"
9406            );
9407            assert!(!active_item.is_singleton(cx));
9408        })
9409        .unwrap();
9410}
9411
9412fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
9413    let point = DisplayPoint::new(row as u32, column as u32);
9414    point..point
9415}
9416
9417fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
9418    let (text, ranges) = marked_text_ranges(marked_text, true);
9419    assert_eq!(view.text(cx), text);
9420    assert_eq!(
9421        view.selections.ranges(cx),
9422        ranges,
9423        "Assert selections are {}",
9424        marked_text
9425    );
9426}
9427
9428/// Handle completion request passing a marked string specifying where the completion
9429/// should be triggered from using '|' character, what range should be replaced, and what completions
9430/// should be returned using '<' and '>' to delimit the range
9431pub fn handle_completion_request(
9432    cx: &mut EditorLspTestContext,
9433    marked_string: &str,
9434    completions: Vec<&'static str>,
9435) -> impl Future<Output = ()> {
9436    let complete_from_marker: TextRangeMarker = '|'.into();
9437    let replace_range_marker: TextRangeMarker = ('<', '>').into();
9438    let (_, mut marked_ranges) = marked_text_ranges_by(
9439        marked_string,
9440        vec![complete_from_marker.clone(), replace_range_marker.clone()],
9441    );
9442
9443    let complete_from_position =
9444        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
9445    let replace_range =
9446        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
9447
9448    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
9449        let completions = completions.clone();
9450        async move {
9451            assert_eq!(params.text_document_position.text_document.uri, url.clone());
9452            assert_eq!(
9453                params.text_document_position.position,
9454                complete_from_position
9455            );
9456            Ok(Some(lsp::CompletionResponse::Array(
9457                completions
9458                    .iter()
9459                    .map(|completion_text| lsp::CompletionItem {
9460                        label: completion_text.to_string(),
9461                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
9462                            range: replace_range,
9463                            new_text: completion_text.to_string(),
9464                        })),
9465                        ..Default::default()
9466                    })
9467                    .collect(),
9468            )))
9469        }
9470    });
9471
9472    async move {
9473        request.next().await;
9474    }
9475}
9476
9477fn handle_resolve_completion_request(
9478    cx: &mut EditorLspTestContext,
9479    edits: Option<Vec<(&'static str, &'static str)>>,
9480) -> impl Future<Output = ()> {
9481    let edits = edits.map(|edits| {
9482        edits
9483            .iter()
9484            .map(|(marked_string, new_text)| {
9485                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
9486                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
9487                lsp::TextEdit::new(replace_range, new_text.to_string())
9488            })
9489            .collect::<Vec<_>>()
9490    });
9491
9492    let mut request =
9493        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
9494            let edits = edits.clone();
9495            async move {
9496                Ok(lsp::CompletionItem {
9497                    additional_text_edits: edits,
9498                    ..Default::default()
9499                })
9500            }
9501        });
9502
9503    async move {
9504        request.next().await;
9505    }
9506}
9507
9508pub(crate) fn update_test_language_settings(
9509    cx: &mut TestAppContext,
9510    f: impl Fn(&mut AllLanguageSettingsContent),
9511) {
9512    _ = cx.update(|cx| {
9513        cx.update_global(|store: &mut SettingsStore, cx| {
9514            store.update_user_settings::<AllLanguageSettings>(cx, f);
9515        });
9516    });
9517}
9518
9519pub(crate) fn update_test_project_settings(
9520    cx: &mut TestAppContext,
9521    f: impl Fn(&mut ProjectSettings),
9522) {
9523    _ = cx.update(|cx| {
9524        cx.update_global(|store: &mut SettingsStore, cx| {
9525            store.update_user_settings::<ProjectSettings>(cx, f);
9526        });
9527    });
9528}
9529
9530pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
9531    _ = cx.update(|cx| {
9532        let store = SettingsStore::test(cx);
9533        cx.set_global(store);
9534        theme::init(theme::LoadThemes::JustBase, cx);
9535        release_channel::init("0.0.0", cx);
9536        client::init_settings(cx);
9537        language::init(cx);
9538        Project::init_settings(cx);
9539        workspace::init_settings(cx);
9540        crate::init(cx);
9541    });
9542
9543    update_test_language_settings(cx, f);
9544}
9545
9546pub(crate) fn rust_lang() -> Arc<Language> {
9547    Arc::new(Language::new(
9548        LanguageConfig {
9549            name: "Rust".into(),
9550            matcher: LanguageMatcher {
9551                path_suffixes: vec!["rs".to_string()],
9552                ..Default::default()
9553            },
9554            ..Default::default()
9555        },
9556        Some(tree_sitter_rust::language()),
9557    ))
9558}
9559
9560#[track_caller]
9561fn assert_hunk_revert(
9562    not_reverted_text_with_selections: &str,
9563    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
9564    expected_reverted_text_with_selections: &str,
9565    base_text: &str,
9566    cx: &mut EditorLspTestContext,
9567) {
9568    cx.set_state(not_reverted_text_with_selections);
9569    cx.update_editor(|editor, cx| {
9570        editor
9571            .buffer()
9572            .read(cx)
9573            .as_singleton()
9574            .unwrap()
9575            .update(cx, |buffer, cx| {
9576                buffer.set_diff_base(Some(base_text.to_string()), cx);
9577            });
9578    });
9579    cx.executor().run_until_parked();
9580
9581    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
9582        let snapshot = editor
9583            .buffer()
9584            .read(cx)
9585            .as_singleton()
9586            .unwrap()
9587            .read(cx)
9588            .snapshot();
9589        let reverted_hunk_statuses = snapshot
9590            .git_diff_hunks_in_row_range(0..u32::MAX)
9591            .map(|hunk| hunk.status())
9592            .collect::<Vec<_>>();
9593
9594        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
9595        reverted_hunk_statuses
9596    });
9597    cx.executor().run_until_parked();
9598    cx.assert_editor_state(expected_reverted_text_with_selections);
9599    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
9600}