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(), "//! ".into(), "/// ".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    // If a selection includes multiple comment prefixes, all lines are uncommented.
6665    cx.set_state(indoc! {"
6666        fn a() {
6667            «// a();
6668            /// b();
6669            //! c();ˇ»
6670        }
6671    "});
6672
6673    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
6674
6675    cx.assert_editor_state(indoc! {"
6676        fn a() {
6677            «a();
6678            b();
6679            c();ˇ»
6680        }
6681    "});
6682}
6683
6684#[gpui::test]
6685async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
6686    init_test(cx, |_| {});
6687
6688    let language = Arc::new(Language::new(
6689        LanguageConfig {
6690            line_comments: vec!["// ".into()],
6691            ..Default::default()
6692        },
6693        Some(tree_sitter_rust::language()),
6694    ));
6695
6696    let mut cx = EditorTestContext::new(cx).await;
6697
6698    cx.language_registry().add(language.clone());
6699    cx.update_buffer(|buffer, cx| {
6700        buffer.set_language(Some(language), cx);
6701    });
6702
6703    let toggle_comments = &ToggleComments {
6704        advance_downwards: true,
6705    };
6706
6707    // Single cursor on one line -> advance
6708    // Cursor moves horizontally 3 characters as well on non-blank line
6709    cx.set_state(indoc!(
6710        "fn a() {
6711             ˇdog();
6712             cat();
6713        }"
6714    ));
6715    cx.update_editor(|editor, cx| {
6716        editor.toggle_comments(toggle_comments, cx);
6717    });
6718    cx.assert_editor_state(indoc!(
6719        "fn a() {
6720             // dog();
6721             catˇ();
6722        }"
6723    ));
6724
6725    // Single selection on one line -> don't advance
6726    cx.set_state(indoc!(
6727        "fn a() {
6728             «dog()ˇ»;
6729             cat();
6730        }"
6731    ));
6732    cx.update_editor(|editor, cx| {
6733        editor.toggle_comments(toggle_comments, cx);
6734    });
6735    cx.assert_editor_state(indoc!(
6736        "fn a() {
6737             // «dog()ˇ»;
6738             cat();
6739        }"
6740    ));
6741
6742    // Multiple cursors on one line -> advance
6743    cx.set_state(indoc!(
6744        "fn a() {
6745             ˇdˇog();
6746             cat();
6747        }"
6748    ));
6749    cx.update_editor(|editor, cx| {
6750        editor.toggle_comments(toggle_comments, cx);
6751    });
6752    cx.assert_editor_state(indoc!(
6753        "fn a() {
6754             // dog();
6755             catˇ(ˇ);
6756        }"
6757    ));
6758
6759    // Multiple cursors on one line, with selection -> don't advance
6760    cx.set_state(indoc!(
6761        "fn a() {
6762             ˇdˇog«()ˇ»;
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             // ˇdˇog«()ˇ»;
6772             cat();
6773        }"
6774    ));
6775
6776    // Single cursor on one line -> advance
6777    // Cursor moves to column 0 on blank line
6778    cx.set_state(indoc!(
6779        "fn a() {
6780             ˇdog();
6781
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        ˇ
6792             cat();
6793        }"
6794    ));
6795
6796    // Single cursor on one line -> advance
6797    // Cursor starts and ends at column 0
6798    cx.set_state(indoc!(
6799        "fn a() {
6800         ˇ    dog();
6801             cat();
6802        }"
6803    ));
6804    cx.update_editor(|editor, cx| {
6805        editor.toggle_comments(toggle_comments, cx);
6806    });
6807    cx.assert_editor_state(indoc!(
6808        "fn a() {
6809             // dog();
6810         ˇ    cat();
6811        }"
6812    ));
6813}
6814
6815#[gpui::test]
6816async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
6817    init_test(cx, |_| {});
6818
6819    let mut cx = EditorTestContext::new(cx).await;
6820
6821    let html_language = Arc::new(
6822        Language::new(
6823            LanguageConfig {
6824                name: "HTML".into(),
6825                block_comment: Some(("<!-- ".into(), " -->".into())),
6826                ..Default::default()
6827            },
6828            Some(tree_sitter_html::language()),
6829        )
6830        .with_injection_query(
6831            r#"
6832            (script_element
6833                (raw_text) @content
6834                (#set! "language" "javascript"))
6835            "#,
6836        )
6837        .unwrap(),
6838    );
6839
6840    let javascript_language = Arc::new(Language::new(
6841        LanguageConfig {
6842            name: "JavaScript".into(),
6843            line_comments: vec!["// ".into()],
6844            ..Default::default()
6845        },
6846        Some(tree_sitter_typescript::language_tsx()),
6847    ));
6848
6849    cx.language_registry().add(html_language.clone());
6850    cx.language_registry().add(javascript_language.clone());
6851    cx.update_buffer(|buffer, cx| {
6852        buffer.set_language(Some(html_language), cx);
6853    });
6854
6855    // Toggle comments for empty selections
6856    cx.set_state(
6857        &r#"
6858            <p>A</p>ˇ
6859            <p>B</p>ˇ
6860            <p>C</p>ˇ
6861        "#
6862        .unindent(),
6863    );
6864    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6865    cx.assert_editor_state(
6866        &r#"
6867            <!-- <p>A</p>ˇ -->
6868            <!-- <p>B</p>ˇ -->
6869            <!-- <p>C</p>ˇ -->
6870        "#
6871        .unindent(),
6872    );
6873    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6874    cx.assert_editor_state(
6875        &r#"
6876            <p>A</p>ˇ
6877            <p>B</p>ˇ
6878            <p>C</p>ˇ
6879        "#
6880        .unindent(),
6881    );
6882
6883    // Toggle comments for mixture of empty and non-empty selections, where
6884    // multiple selections occupy a given line.
6885    cx.set_state(
6886        &r#"
6887            <p>A«</p>
6888            <p>ˇ»B</p>ˇ
6889            <p>C«</p>
6890            <p>ˇ»D</p>ˇ
6891        "#
6892        .unindent(),
6893    );
6894
6895    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6896    cx.assert_editor_state(
6897        &r#"
6898            <!-- <p>A«</p>
6899            <p>ˇ»B</p>ˇ -->
6900            <!-- <p>C«</p>
6901            <p>ˇ»D</p>ˇ -->
6902        "#
6903        .unindent(),
6904    );
6905    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6906    cx.assert_editor_state(
6907        &r#"
6908            <p>A«</p>
6909            <p>ˇ»B</p>ˇ
6910            <p>C«</p>
6911            <p>ˇ»D</p>ˇ
6912        "#
6913        .unindent(),
6914    );
6915
6916    // Toggle comments when different languages are active for different
6917    // selections.
6918    cx.set_state(
6919        &r#"
6920            ˇ<script>
6921                ˇvar x = new Y();
6922            ˇ</script>
6923        "#
6924        .unindent(),
6925    );
6926    cx.executor().run_until_parked();
6927    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6928    cx.assert_editor_state(
6929        &r#"
6930            <!-- ˇ<script> -->
6931                // ˇvar x = new Y();
6932            <!-- ˇ</script> -->
6933        "#
6934        .unindent(),
6935    );
6936}
6937
6938#[gpui::test]
6939fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
6940    init_test(cx, |_| {});
6941
6942    let buffer = cx.new_model(|cx| {
6943        Buffer::new(
6944            0,
6945            BufferId::new(cx.entity_id().as_u64()).unwrap(),
6946            sample_text(3, 4, 'a'),
6947        )
6948    });
6949    let multibuffer = cx.new_model(|cx| {
6950        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
6951        multibuffer.push_excerpts(
6952            buffer.clone(),
6953            [
6954                ExcerptRange {
6955                    context: Point::new(0, 0)..Point::new(0, 4),
6956                    primary: None,
6957                },
6958                ExcerptRange {
6959                    context: Point::new(1, 0)..Point::new(1, 4),
6960                    primary: None,
6961                },
6962            ],
6963            cx,
6964        );
6965        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
6966        multibuffer
6967    });
6968
6969    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
6970    _ = view.update(cx, |view, cx| {
6971        assert_eq!(view.text(cx), "aaaa\nbbbb");
6972        view.change_selections(None, cx, |s| {
6973            s.select_ranges([
6974                Point::new(0, 0)..Point::new(0, 0),
6975                Point::new(1, 0)..Point::new(1, 0),
6976            ])
6977        });
6978
6979        view.handle_input("X", cx);
6980        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
6981        assert_eq!(
6982            view.selections.ranges(cx),
6983            [
6984                Point::new(0, 1)..Point::new(0, 1),
6985                Point::new(1, 1)..Point::new(1, 1),
6986            ]
6987        );
6988
6989        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
6990        view.change_selections(None, cx, |s| {
6991            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
6992        });
6993        view.backspace(&Default::default(), cx);
6994        assert_eq!(view.text(cx), "Xa\nbbb");
6995        assert_eq!(
6996            view.selections.ranges(cx),
6997            [Point::new(1, 0)..Point::new(1, 0)]
6998        );
6999
7000        view.change_selections(None, cx, |s| {
7001            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
7002        });
7003        view.backspace(&Default::default(), cx);
7004        assert_eq!(view.text(cx), "X\nbb");
7005        assert_eq!(
7006            view.selections.ranges(cx),
7007            [Point::new(0, 1)..Point::new(0, 1)]
7008        );
7009    });
7010}
7011
7012#[gpui::test]
7013fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
7014    init_test(cx, |_| {});
7015
7016    let markers = vec![('[', ']').into(), ('(', ')').into()];
7017    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
7018        indoc! {"
7019            [aaaa
7020            (bbbb]
7021            cccc)",
7022        },
7023        markers.clone(),
7024    );
7025    let excerpt_ranges = markers.into_iter().map(|marker| {
7026        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
7027        ExcerptRange {
7028            context,
7029            primary: None,
7030        }
7031    });
7032    let buffer = cx.new_model(|cx| {
7033        Buffer::new(
7034            0,
7035            BufferId::new(cx.entity_id().as_u64()).unwrap(),
7036            initial_text,
7037        )
7038    });
7039    let multibuffer = cx.new_model(|cx| {
7040        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
7041        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
7042        multibuffer
7043    });
7044
7045    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
7046    _ = view.update(cx, |view, cx| {
7047        let (expected_text, selection_ranges) = marked_text_ranges(
7048            indoc! {"
7049                aaaa
7050                bˇbbb
7051                bˇbbˇb
7052                cccc"
7053            },
7054            true,
7055        );
7056        assert_eq!(view.text(cx), expected_text);
7057        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
7058
7059        view.handle_input("X", cx);
7060
7061        let (expected_text, expected_selections) = marked_text_ranges(
7062            indoc! {"
7063                aaaa
7064                bXˇbbXb
7065                bXˇbbXˇb
7066                cccc"
7067            },
7068            false,
7069        );
7070        assert_eq!(view.text(cx), expected_text);
7071        assert_eq!(view.selections.ranges(cx), expected_selections);
7072
7073        view.newline(&Newline, cx);
7074        let (expected_text, expected_selections) = marked_text_ranges(
7075            indoc! {"
7076                aaaa
7077                bX
7078                ˇbbX
7079                b
7080                bX
7081                ˇbbX
7082                ˇb
7083                cccc"
7084            },
7085            false,
7086        );
7087        assert_eq!(view.text(cx), expected_text);
7088        assert_eq!(view.selections.ranges(cx), expected_selections);
7089    });
7090}
7091
7092#[gpui::test]
7093fn test_refresh_selections(cx: &mut TestAppContext) {
7094    init_test(cx, |_| {});
7095
7096    let buffer = cx.new_model(|cx| {
7097        Buffer::new(
7098            0,
7099            BufferId::new(cx.entity_id().as_u64()).unwrap(),
7100            sample_text(3, 4, 'a'),
7101        )
7102    });
7103    let mut excerpt1_id = None;
7104    let multibuffer = cx.new_model(|cx| {
7105        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
7106        excerpt1_id = multibuffer
7107            .push_excerpts(
7108                buffer.clone(),
7109                [
7110                    ExcerptRange {
7111                        context: Point::new(0, 0)..Point::new(1, 4),
7112                        primary: None,
7113                    },
7114                    ExcerptRange {
7115                        context: Point::new(1, 0)..Point::new(2, 4),
7116                        primary: None,
7117                    },
7118                ],
7119                cx,
7120            )
7121            .into_iter()
7122            .next();
7123        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
7124        multibuffer
7125    });
7126
7127    let editor = cx.add_window(|cx| {
7128        let mut editor = build_editor(multibuffer.clone(), cx);
7129        let snapshot = editor.snapshot(cx);
7130        editor.change_selections(None, cx, |s| {
7131            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
7132        });
7133        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
7134        assert_eq!(
7135            editor.selections.ranges(cx),
7136            [
7137                Point::new(1, 3)..Point::new(1, 3),
7138                Point::new(2, 1)..Point::new(2, 1),
7139            ]
7140        );
7141        editor
7142    });
7143
7144    // Refreshing selections is a no-op when excerpts haven't changed.
7145    _ = editor.update(cx, |editor, cx| {
7146        editor.change_selections(None, cx, |s| s.refresh());
7147        assert_eq!(
7148            editor.selections.ranges(cx),
7149            [
7150                Point::new(1, 3)..Point::new(1, 3),
7151                Point::new(2, 1)..Point::new(2, 1),
7152            ]
7153        );
7154    });
7155
7156    _ = multibuffer.update(cx, |multibuffer, cx| {
7157        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
7158    });
7159    _ = editor.update(cx, |editor, cx| {
7160        // Removing an excerpt causes the first selection to become degenerate.
7161        assert_eq!(
7162            editor.selections.ranges(cx),
7163            [
7164                Point::new(0, 0)..Point::new(0, 0),
7165                Point::new(0, 1)..Point::new(0, 1)
7166            ]
7167        );
7168
7169        // Refreshing selections will relocate the first selection to the original buffer
7170        // location.
7171        editor.change_selections(None, cx, |s| s.refresh());
7172        assert_eq!(
7173            editor.selections.ranges(cx),
7174            [
7175                Point::new(0, 1)..Point::new(0, 1),
7176                Point::new(0, 3)..Point::new(0, 3)
7177            ]
7178        );
7179        assert!(editor.selections.pending_anchor().is_some());
7180    });
7181}
7182
7183#[gpui::test]
7184fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
7185    init_test(cx, |_| {});
7186
7187    let buffer = cx.new_model(|cx| {
7188        Buffer::new(
7189            0,
7190            BufferId::new(cx.entity_id().as_u64()).unwrap(),
7191            sample_text(3, 4, 'a'),
7192        )
7193    });
7194    let mut excerpt1_id = None;
7195    let multibuffer = cx.new_model(|cx| {
7196        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
7197        excerpt1_id = multibuffer
7198            .push_excerpts(
7199                buffer.clone(),
7200                [
7201                    ExcerptRange {
7202                        context: Point::new(0, 0)..Point::new(1, 4),
7203                        primary: None,
7204                    },
7205                    ExcerptRange {
7206                        context: Point::new(1, 0)..Point::new(2, 4),
7207                        primary: None,
7208                    },
7209                ],
7210                cx,
7211            )
7212            .into_iter()
7213            .next();
7214        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
7215        multibuffer
7216    });
7217
7218    let editor = cx.add_window(|cx| {
7219        let mut editor = build_editor(multibuffer.clone(), cx);
7220        let snapshot = editor.snapshot(cx);
7221        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
7222        assert_eq!(
7223            editor.selections.ranges(cx),
7224            [Point::new(1, 3)..Point::new(1, 3)]
7225        );
7226        editor
7227    });
7228
7229    _ = multibuffer.update(cx, |multibuffer, cx| {
7230        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
7231    });
7232    _ = editor.update(cx, |editor, cx| {
7233        assert_eq!(
7234            editor.selections.ranges(cx),
7235            [Point::new(0, 0)..Point::new(0, 0)]
7236        );
7237
7238        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
7239        editor.change_selections(None, cx, |s| s.refresh());
7240        assert_eq!(
7241            editor.selections.ranges(cx),
7242            [Point::new(0, 3)..Point::new(0, 3)]
7243        );
7244        assert!(editor.selections.pending_anchor().is_some());
7245    });
7246}
7247
7248#[gpui::test]
7249async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
7250    init_test(cx, |_| {});
7251
7252    let language = Arc::new(
7253        Language::new(
7254            LanguageConfig {
7255                brackets: BracketPairConfig {
7256                    pairs: vec![
7257                        BracketPair {
7258                            start: "{".to_string(),
7259                            end: "}".to_string(),
7260                            close: true,
7261                            newline: true,
7262                        },
7263                        BracketPair {
7264                            start: "/* ".to_string(),
7265                            end: " */".to_string(),
7266                            close: true,
7267                            newline: true,
7268                        },
7269                    ],
7270                    ..Default::default()
7271                },
7272                ..Default::default()
7273            },
7274            Some(tree_sitter_rust::language()),
7275        )
7276        .with_indents_query("")
7277        .unwrap(),
7278    );
7279
7280    let text = concat!(
7281        "{   }\n",     //
7282        "  x\n",       //
7283        "  /*   */\n", //
7284        "x\n",         //
7285        "{{} }\n",     //
7286    );
7287
7288    let buffer = cx.new_model(|cx| {
7289        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
7290            .with_language(language, cx)
7291    });
7292    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
7293    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
7294    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
7295        .await;
7296
7297    _ = view.update(cx, |view, cx| {
7298        view.change_selections(None, cx, |s| {
7299            s.select_display_ranges([
7300                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
7301                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
7302                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
7303            ])
7304        });
7305        view.newline(&Newline, cx);
7306
7307        assert_eq!(
7308            view.buffer().read(cx).read(cx).text(),
7309            concat!(
7310                "{ \n",    // Suppress rustfmt
7311                "\n",      //
7312                "}\n",     //
7313                "  x\n",   //
7314                "  /* \n", //
7315                "  \n",    //
7316                "  */\n",  //
7317                "x\n",     //
7318                "{{} \n",  //
7319                "}\n",     //
7320            )
7321        );
7322    });
7323}
7324
7325#[gpui::test]
7326fn test_highlighted_ranges(cx: &mut TestAppContext) {
7327    init_test(cx, |_| {});
7328
7329    let editor = cx.add_window(|cx| {
7330        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
7331        build_editor(buffer.clone(), cx)
7332    });
7333
7334    _ = editor.update(cx, |editor, cx| {
7335        struct Type1;
7336        struct Type2;
7337
7338        let buffer = editor.buffer.read(cx).snapshot(cx);
7339
7340        let anchor_range =
7341            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
7342
7343        editor.highlight_background::<Type1>(
7344            &[
7345                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
7346                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
7347                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
7348                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
7349            ],
7350            |_| Hsla::red(),
7351            cx,
7352        );
7353        editor.highlight_background::<Type2>(
7354            &[
7355                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
7356                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
7357                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
7358                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
7359            ],
7360            |_| Hsla::green(),
7361            cx,
7362        );
7363
7364        let snapshot = editor.snapshot(cx);
7365        let mut highlighted_ranges = editor.background_highlights_in_range(
7366            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
7367            &snapshot,
7368            cx.theme().colors(),
7369        );
7370        // Enforce a consistent ordering based on color without relying on the ordering of the
7371        // highlight's `TypeId` which is non-executor.
7372        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
7373        assert_eq!(
7374            highlighted_ranges,
7375            &[
7376                (
7377                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
7378                    Hsla::red(),
7379                ),
7380                (
7381                    DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
7382                    Hsla::red(),
7383                ),
7384                (
7385                    DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
7386                    Hsla::green(),
7387                ),
7388                (
7389                    DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
7390                    Hsla::green(),
7391                ),
7392            ]
7393        );
7394        assert_eq!(
7395            editor.background_highlights_in_range(
7396                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
7397                &snapshot,
7398                cx.theme().colors(),
7399            ),
7400            &[(
7401                DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
7402                Hsla::red(),
7403            )]
7404        );
7405    });
7406}
7407
7408#[gpui::test]
7409async fn test_following(cx: &mut gpui::TestAppContext) {
7410    init_test(cx, |_| {});
7411
7412    let fs = FakeFs::new(cx.executor());
7413    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
7414
7415    let buffer = project.update(cx, |project, cx| {
7416        let buffer = project
7417            .create_buffer(&sample_text(16, 8, 'a'), None, cx)
7418            .unwrap();
7419        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
7420    });
7421    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
7422    let follower = cx.update(|cx| {
7423        cx.open_window(
7424            WindowOptions {
7425                bounds: Some(Bounds::from_corners(
7426                    gpui::Point::new(0.into(), 0.into()),
7427                    gpui::Point::new(10.into(), 80.into()),
7428                )),
7429                ..Default::default()
7430            },
7431            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
7432        )
7433    });
7434
7435    let is_still_following = Rc::new(RefCell::new(true));
7436    let follower_edit_event_count = Rc::new(RefCell::new(0));
7437    let pending_update = Rc::new(RefCell::new(None));
7438    _ = follower.update(cx, {
7439        let update = pending_update.clone();
7440        let is_still_following = is_still_following.clone();
7441        let follower_edit_event_count = follower_edit_event_count.clone();
7442        |_, cx| {
7443            cx.subscribe(
7444                &leader.root_view(cx).unwrap(),
7445                move |_, leader, event, cx| {
7446                    leader
7447                        .read(cx)
7448                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
7449                },
7450            )
7451            .detach();
7452
7453            cx.subscribe(
7454                &follower.root_view(cx).unwrap(),
7455                move |_, _, event: &EditorEvent, _cx| {
7456                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
7457                        *is_still_following.borrow_mut() = false;
7458                    }
7459
7460                    if let EditorEvent::BufferEdited = event {
7461                        *follower_edit_event_count.borrow_mut() += 1;
7462                    }
7463                },
7464            )
7465            .detach();
7466        }
7467    });
7468
7469    // Update the selections only
7470    _ = leader.update(cx, |leader, cx| {
7471        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
7472    });
7473    follower
7474        .update(cx, |follower, cx| {
7475            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
7476        })
7477        .unwrap()
7478        .await
7479        .unwrap();
7480    _ = follower.update(cx, |follower, cx| {
7481        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
7482    });
7483    assert_eq!(*is_still_following.borrow(), true);
7484    assert_eq!(*follower_edit_event_count.borrow(), 0);
7485
7486    // Update the scroll position only
7487    _ = leader.update(cx, |leader, cx| {
7488        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
7489    });
7490    follower
7491        .update(cx, |follower, cx| {
7492            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
7493        })
7494        .unwrap()
7495        .await
7496        .unwrap();
7497    assert_eq!(
7498        follower
7499            .update(cx, |follower, cx| follower.scroll_position(cx))
7500            .unwrap(),
7501        gpui::Point::new(1.5, 3.5)
7502    );
7503    assert_eq!(*is_still_following.borrow(), true);
7504    assert_eq!(*follower_edit_event_count.borrow(), 0);
7505
7506    // Update the selections and scroll position. The follower's scroll position is updated
7507    // via autoscroll, not via the leader's exact scroll position.
7508    _ = leader.update(cx, |leader, cx| {
7509        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
7510        leader.request_autoscroll(Autoscroll::newest(), cx);
7511        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
7512    });
7513    follower
7514        .update(cx, |follower, cx| {
7515            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
7516        })
7517        .unwrap()
7518        .await
7519        .unwrap();
7520    _ = follower.update(cx, |follower, cx| {
7521        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
7522        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
7523    });
7524    assert_eq!(*is_still_following.borrow(), true);
7525
7526    // Creating a pending selection that precedes another selection
7527    _ = leader.update(cx, |leader, cx| {
7528        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
7529        leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
7530    });
7531    follower
7532        .update(cx, |follower, cx| {
7533            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
7534        })
7535        .unwrap()
7536        .await
7537        .unwrap();
7538    _ = follower.update(cx, |follower, cx| {
7539        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
7540    });
7541    assert_eq!(*is_still_following.borrow(), true);
7542
7543    // Extend the pending selection so that it surrounds another selection
7544    _ = leader.update(cx, |leader, cx| {
7545        leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
7546    });
7547    follower
7548        .update(cx, |follower, cx| {
7549            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
7550        })
7551        .unwrap()
7552        .await
7553        .unwrap();
7554    _ = follower.update(cx, |follower, cx| {
7555        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
7556    });
7557
7558    // Scrolling locally breaks the follow
7559    _ = follower.update(cx, |follower, cx| {
7560        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
7561        follower.set_scroll_anchor(
7562            ScrollAnchor {
7563                anchor: top_anchor,
7564                offset: gpui::Point::new(0.0, 0.5),
7565            },
7566            cx,
7567        );
7568    });
7569    assert_eq!(*is_still_following.borrow(), false);
7570}
7571
7572#[gpui::test]
7573async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
7574    init_test(cx, |_| {});
7575
7576    let fs = FakeFs::new(cx.executor());
7577    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
7578    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
7579    let pane = workspace
7580        .update(cx, |workspace, _| workspace.active_pane().clone())
7581        .unwrap();
7582
7583    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
7584
7585    let leader = pane.update(cx, |_, cx| {
7586        let multibuffer = cx.new_model(|_| MultiBuffer::new(0, ReadWrite));
7587        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
7588    });
7589
7590    // Start following the editor when it has no excerpts.
7591    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
7592    let follower_1 = cx
7593        .update_window(*workspace.deref(), |_, cx| {
7594            Editor::from_state_proto(
7595                pane.clone(),
7596                workspace.root_view(cx).unwrap(),
7597                ViewId {
7598                    creator: Default::default(),
7599                    id: 0,
7600                },
7601                &mut state_message,
7602                cx,
7603            )
7604        })
7605        .unwrap()
7606        .unwrap()
7607        .await
7608        .unwrap();
7609
7610    let update_message = Rc::new(RefCell::new(None));
7611    follower_1.update(cx, {
7612        let update = update_message.clone();
7613        |_, cx| {
7614            cx.subscribe(&leader, move |_, leader, event, cx| {
7615                leader
7616                    .read(cx)
7617                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
7618            })
7619            .detach();
7620        }
7621    });
7622
7623    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
7624        (
7625            project
7626                .create_buffer("abc\ndef\nghi\njkl\n", None, cx)
7627                .unwrap(),
7628            project
7629                .create_buffer("mno\npqr\nstu\nvwx\n", None, cx)
7630                .unwrap(),
7631        )
7632    });
7633
7634    // Insert some excerpts.
7635    _ = leader.update(cx, |leader, cx| {
7636        leader.buffer.update(cx, |multibuffer, cx| {
7637            let excerpt_ids = multibuffer.push_excerpts(
7638                buffer_1.clone(),
7639                [
7640                    ExcerptRange {
7641                        context: 1..6,
7642                        primary: None,
7643                    },
7644                    ExcerptRange {
7645                        context: 12..15,
7646                        primary: None,
7647                    },
7648                    ExcerptRange {
7649                        context: 0..3,
7650                        primary: None,
7651                    },
7652                ],
7653                cx,
7654            );
7655            multibuffer.insert_excerpts_after(
7656                excerpt_ids[0],
7657                buffer_2.clone(),
7658                [
7659                    ExcerptRange {
7660                        context: 8..12,
7661                        primary: None,
7662                    },
7663                    ExcerptRange {
7664                        context: 0..6,
7665                        primary: None,
7666                    },
7667                ],
7668                cx,
7669            );
7670        });
7671    });
7672
7673    // Apply the update of adding the excerpts.
7674    follower_1
7675        .update(cx, |follower, cx| {
7676            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
7677        })
7678        .await
7679        .unwrap();
7680    assert_eq!(
7681        follower_1.update(cx, |editor, cx| editor.text(cx)),
7682        leader.update(cx, |editor, cx| editor.text(cx))
7683    );
7684    update_message.borrow_mut().take();
7685
7686    // Start following separately after it already has excerpts.
7687    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
7688    let follower_2 = cx
7689        .update_window(*workspace.deref(), |_, cx| {
7690            Editor::from_state_proto(
7691                pane.clone(),
7692                workspace.root_view(cx).unwrap().clone(),
7693                ViewId {
7694                    creator: Default::default(),
7695                    id: 0,
7696                },
7697                &mut state_message,
7698                cx,
7699            )
7700        })
7701        .unwrap()
7702        .unwrap()
7703        .await
7704        .unwrap();
7705    assert_eq!(
7706        follower_2.update(cx, |editor, cx| editor.text(cx)),
7707        leader.update(cx, |editor, cx| editor.text(cx))
7708    );
7709
7710    // Remove some excerpts.
7711    _ = leader.update(cx, |leader, cx| {
7712        leader.buffer.update(cx, |multibuffer, cx| {
7713            let excerpt_ids = multibuffer.excerpt_ids();
7714            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
7715            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
7716        });
7717    });
7718
7719    // Apply the update of removing the excerpts.
7720    follower_1
7721        .update(cx, |follower, cx| {
7722            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
7723        })
7724        .await
7725        .unwrap();
7726    follower_2
7727        .update(cx, |follower, cx| {
7728            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
7729        })
7730        .await
7731        .unwrap();
7732    update_message.borrow_mut().take();
7733    assert_eq!(
7734        follower_1.update(cx, |editor, cx| editor.text(cx)),
7735        leader.update(cx, |editor, cx| editor.text(cx))
7736    );
7737}
7738
7739#[gpui::test]
7740async fn go_to_prev_overlapping_diagnostic(
7741    executor: BackgroundExecutor,
7742    cx: &mut gpui::TestAppContext,
7743) {
7744    init_test(cx, |_| {});
7745
7746    let mut cx = EditorTestContext::new(cx).await;
7747    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
7748
7749    cx.set_state(indoc! {"
7750        ˇfn func(abc def: i32) -> u32 {
7751        }
7752    "});
7753
7754    _ = cx.update(|cx| {
7755        _ = project.update(cx, |project, cx| {
7756            project
7757                .update_diagnostics(
7758                    LanguageServerId(0),
7759                    lsp::PublishDiagnosticsParams {
7760                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
7761                        version: None,
7762                        diagnostics: vec![
7763                            lsp::Diagnostic {
7764                                range: lsp::Range::new(
7765                                    lsp::Position::new(0, 11),
7766                                    lsp::Position::new(0, 12),
7767                                ),
7768                                severity: Some(lsp::DiagnosticSeverity::ERROR),
7769                                ..Default::default()
7770                            },
7771                            lsp::Diagnostic {
7772                                range: lsp::Range::new(
7773                                    lsp::Position::new(0, 12),
7774                                    lsp::Position::new(0, 15),
7775                                ),
7776                                severity: Some(lsp::DiagnosticSeverity::ERROR),
7777                                ..Default::default()
7778                            },
7779                            lsp::Diagnostic {
7780                                range: lsp::Range::new(
7781                                    lsp::Position::new(0, 25),
7782                                    lsp::Position::new(0, 28),
7783                                ),
7784                                severity: Some(lsp::DiagnosticSeverity::ERROR),
7785                                ..Default::default()
7786                            },
7787                        ],
7788                    },
7789                    &[],
7790                    cx,
7791                )
7792                .unwrap()
7793        });
7794    });
7795
7796    executor.run_until_parked();
7797
7798    cx.update_editor(|editor, cx| {
7799        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
7800    });
7801
7802    cx.assert_editor_state(indoc! {"
7803        fn func(abc def: i32) -> ˇu32 {
7804        }
7805    "});
7806
7807    cx.update_editor(|editor, cx| {
7808        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
7809    });
7810
7811    cx.assert_editor_state(indoc! {"
7812        fn func(abc ˇdef: i32) -> u32 {
7813        }
7814    "});
7815
7816    cx.update_editor(|editor, cx| {
7817        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
7818    });
7819
7820    cx.assert_editor_state(indoc! {"
7821        fn func(abcˇ def: i32) -> u32 {
7822        }
7823    "});
7824
7825    cx.update_editor(|editor, cx| {
7826        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
7827    });
7828
7829    cx.assert_editor_state(indoc! {"
7830        fn func(abc def: i32) -> ˇu32 {
7831        }
7832    "});
7833}
7834
7835#[gpui::test]
7836async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
7837    init_test(cx, |_| {});
7838
7839    let mut cx = EditorTestContext::new(cx).await;
7840
7841    let diff_base = r#"
7842        use some::mod;
7843
7844        const A: u32 = 42;
7845
7846        fn main() {
7847            println!("hello");
7848
7849            println!("world");
7850        }
7851        "#
7852    .unindent();
7853
7854    // Edits are modified, removed, modified, added
7855    cx.set_state(
7856        &r#"
7857        use some::modified;
7858
7859        ˇ
7860        fn main() {
7861            println!("hello there");
7862
7863            println!("around the");
7864            println!("world");
7865        }
7866        "#
7867        .unindent(),
7868    );
7869
7870    cx.set_diff_base(Some(&diff_base));
7871    executor.run_until_parked();
7872
7873    cx.update_editor(|editor, cx| {
7874        //Wrap around the bottom of the buffer
7875        for _ in 0..3 {
7876            editor.go_to_hunk(&GoToHunk, cx);
7877        }
7878    });
7879
7880    cx.assert_editor_state(
7881        &r#"
7882        ˇuse some::modified;
7883
7884
7885        fn main() {
7886            println!("hello there");
7887
7888            println!("around the");
7889            println!("world");
7890        }
7891        "#
7892        .unindent(),
7893    );
7894
7895    cx.update_editor(|editor, cx| {
7896        //Wrap around the top of the buffer
7897        for _ in 0..2 {
7898            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
7899        }
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        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
7919    });
7920
7921    cx.assert_editor_state(
7922        &r#"
7923        use some::modified;
7924
7925        ˇ
7926        fn main() {
7927            println!("hello there");
7928
7929            println!("around the");
7930            println!("world");
7931        }
7932        "#
7933        .unindent(),
7934    );
7935
7936    cx.update_editor(|editor, cx| {
7937        for _ in 0..3 {
7938            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
7939        }
7940    });
7941
7942    cx.assert_editor_state(
7943        &r#"
7944        use some::modified;
7945
7946
7947        fn main() {
7948        ˇ    println!("hello there");
7949
7950            println!("around the");
7951            println!("world");
7952        }
7953        "#
7954        .unindent(),
7955    );
7956
7957    cx.update_editor(|editor, cx| {
7958        editor.fold(&Fold, cx);
7959
7960        //Make sure that the fold only gets one hunk
7961        for _ in 0..4 {
7962            editor.go_to_hunk(&GoToHunk, cx);
7963        }
7964    });
7965
7966    cx.assert_editor_state(
7967        &r#"
7968        ˇuse some::modified;
7969
7970
7971        fn main() {
7972            println!("hello there");
7973
7974            println!("around the");
7975            println!("world");
7976        }
7977        "#
7978        .unindent(),
7979    );
7980}
7981
7982#[test]
7983fn test_split_words() {
7984    fn split(text: &str) -> Vec<&str> {
7985        split_words(text).collect()
7986    }
7987
7988    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
7989    assert_eq!(split("hello_world"), &["hello_", "world"]);
7990    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
7991    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
7992    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
7993    assert_eq!(split("helloworld"), &["helloworld"]);
7994
7995    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
7996}
7997
7998#[gpui::test]
7999async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
8000    init_test(cx, |_| {});
8001
8002    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
8003    let mut assert = |before, after| {
8004        let _state_context = cx.set_state(before);
8005        cx.update_editor(|editor, cx| {
8006            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
8007        });
8008        cx.assert_editor_state(after);
8009    };
8010
8011    // Outside bracket jumps to outside of matching bracket
8012    assert("console.logˇ(var);", "console.log(var)ˇ;");
8013    assert("console.log(var)ˇ;", "console.logˇ(var);");
8014
8015    // Inside bracket jumps to inside of matching bracket
8016    assert("console.log(ˇvar);", "console.log(varˇ);");
8017    assert("console.log(varˇ);", "console.log(ˇvar);");
8018
8019    // When outside a bracket and inside, favor jumping to the inside bracket
8020    assert(
8021        "console.log('foo', [1, 2, 3]ˇ);",
8022        "console.log(ˇ'foo', [1, 2, 3]);",
8023    );
8024    assert(
8025        "console.log(ˇ'foo', [1, 2, 3]);",
8026        "console.log('foo', [1, 2, 3]ˇ);",
8027    );
8028
8029    // Bias forward if two options are equally likely
8030    assert(
8031        "let result = curried_fun()ˇ();",
8032        "let result = curried_fun()()ˇ;",
8033    );
8034
8035    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
8036    assert(
8037        indoc! {"
8038            function test() {
8039                console.log('test')ˇ
8040            }"},
8041        indoc! {"
8042            function test() {
8043                console.logˇ('test')
8044            }"},
8045    );
8046}
8047
8048#[gpui::test]
8049async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
8050    init_test(cx, |_| {});
8051
8052    let fs = FakeFs::new(cx.executor());
8053    fs.insert_tree(
8054        "/a",
8055        json!({
8056            "main.rs": "fn main() { let a = 5; }",
8057            "other.rs": "// Test file",
8058        }),
8059    )
8060    .await;
8061    let project = Project::test(fs, ["/a".as_ref()], cx).await;
8062
8063    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
8064    language_registry.add(Arc::new(Language::new(
8065        LanguageConfig {
8066            name: "Rust".into(),
8067            matcher: LanguageMatcher {
8068                path_suffixes: vec!["rs".to_string()],
8069                ..Default::default()
8070            },
8071            brackets: BracketPairConfig {
8072                pairs: vec![BracketPair {
8073                    start: "{".to_string(),
8074                    end: "}".to_string(),
8075                    close: true,
8076                    newline: true,
8077                }],
8078                disabled_scopes_by_bracket_ix: Vec::new(),
8079            },
8080            ..Default::default()
8081        },
8082        Some(tree_sitter_rust::language()),
8083    )));
8084    let mut fake_servers = language_registry.register_fake_lsp_adapter(
8085        "Rust",
8086        FakeLspAdapter {
8087            capabilities: lsp::ServerCapabilities {
8088                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
8089                    first_trigger_character: "{".to_string(),
8090                    more_trigger_character: None,
8091                }),
8092                ..Default::default()
8093            },
8094            ..Default::default()
8095        },
8096    );
8097
8098    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
8099
8100    let cx = &mut VisualTestContext::from_window(*workspace, cx);
8101
8102    let worktree_id = workspace
8103        .update(cx, |workspace, cx| {
8104            workspace.project().update(cx, |project, cx| {
8105                project.worktrees().next().unwrap().read(cx).id()
8106            })
8107        })
8108        .unwrap();
8109
8110    let buffer = project
8111        .update(cx, |project, cx| {
8112            project.open_local_buffer("/a/main.rs", cx)
8113        })
8114        .await
8115        .unwrap();
8116    cx.executor().run_until_parked();
8117    cx.executor().start_waiting();
8118    let fake_server = fake_servers.next().await.unwrap();
8119    let editor_handle = workspace
8120        .update(cx, |workspace, cx| {
8121            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
8122        })
8123        .unwrap()
8124        .await
8125        .unwrap()
8126        .downcast::<Editor>()
8127        .unwrap();
8128
8129    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
8130        assert_eq!(
8131            params.text_document_position.text_document.uri,
8132            lsp::Url::from_file_path("/a/main.rs").unwrap(),
8133        );
8134        assert_eq!(
8135            params.text_document_position.position,
8136            lsp::Position::new(0, 21),
8137        );
8138
8139        Ok(Some(vec![lsp::TextEdit {
8140            new_text: "]".to_string(),
8141            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
8142        }]))
8143    });
8144
8145    editor_handle.update(cx, |editor, cx| {
8146        editor.focus(cx);
8147        editor.change_selections(None, cx, |s| {
8148            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
8149        });
8150        editor.handle_input("{", cx);
8151    });
8152
8153    cx.executor().run_until_parked();
8154
8155    _ = buffer.update(cx, |buffer, _| {
8156        assert_eq!(
8157            buffer.text(),
8158            "fn main() { let a = {5}; }",
8159            "No extra braces from on type formatting should appear in the buffer"
8160        )
8161    });
8162}
8163
8164#[gpui::test]
8165async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
8166    init_test(cx, |_| {});
8167
8168    let fs = FakeFs::new(cx.executor());
8169    fs.insert_tree(
8170        "/a",
8171        json!({
8172            "main.rs": "fn main() { let a = 5; }",
8173            "other.rs": "// Test file",
8174        }),
8175    )
8176    .await;
8177
8178    let project = Project::test(fs, ["/a".as_ref()], cx).await;
8179
8180    let server_restarts = Arc::new(AtomicUsize::new(0));
8181    let closure_restarts = Arc::clone(&server_restarts);
8182    let language_server_name = "test language server";
8183    let language_name: Arc<str> = "Rust".into();
8184
8185    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
8186    language_registry.add(Arc::new(Language::new(
8187        LanguageConfig {
8188            name: Arc::clone(&language_name),
8189            matcher: LanguageMatcher {
8190                path_suffixes: vec!["rs".to_string()],
8191                ..Default::default()
8192            },
8193            ..Default::default()
8194        },
8195        Some(tree_sitter_rust::language()),
8196    )));
8197    let mut fake_servers = language_registry.register_fake_lsp_adapter(
8198        "Rust",
8199        FakeLspAdapter {
8200            name: language_server_name,
8201            initialization_options: Some(json!({
8202                "testOptionValue": true
8203            })),
8204            initializer: Some(Box::new(move |fake_server| {
8205                let task_restarts = Arc::clone(&closure_restarts);
8206                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
8207                    task_restarts.fetch_add(1, atomic::Ordering::Release);
8208                    futures::future::ready(Ok(()))
8209                });
8210            })),
8211            ..Default::default()
8212        },
8213    );
8214
8215    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
8216    let _buffer = project
8217        .update(cx, |project, cx| {
8218            project.open_local_buffer("/a/main.rs", cx)
8219        })
8220        .await
8221        .unwrap();
8222    let _fake_server = fake_servers.next().await.unwrap();
8223    update_test_language_settings(cx, |language_settings| {
8224        language_settings.languages.insert(
8225            Arc::clone(&language_name),
8226            LanguageSettingsContent {
8227                tab_size: NonZeroU32::new(8),
8228                ..Default::default()
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 change"
8237    );
8238
8239    update_test_project_settings(cx, |project_settings| {
8240        project_settings.lsp.insert(
8241            "Some other server name".into(),
8242            LspSettings {
8243                binary: None,
8244                settings: None,
8245                initialization_options: Some(json!({
8246                    "some other init value": false
8247                })),
8248            },
8249        );
8250    });
8251    cx.executor().run_until_parked();
8252    assert_eq!(
8253        server_restarts.load(atomic::Ordering::Acquire),
8254        0,
8255        "Should not restart LSP server on an unrelated 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 restart LSP server on a related LSP settings change"
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: Some(json!({
8284                    "anotherInitValue": false
8285                })),
8286            },
8287        );
8288    });
8289    cx.executor().run_until_parked();
8290    assert_eq!(
8291        server_restarts.load(atomic::Ordering::Acquire),
8292        1,
8293        "Should not restart LSP server on a related LSP settings change that is the same"
8294    );
8295
8296    update_test_project_settings(cx, |project_settings| {
8297        project_settings.lsp.insert(
8298            language_server_name.into(),
8299            LspSettings {
8300                binary: None,
8301                settings: None,
8302                initialization_options: None,
8303            },
8304        );
8305    });
8306    cx.executor().run_until_parked();
8307    assert_eq!(
8308        server_restarts.load(atomic::Ordering::Acquire),
8309        2,
8310        "Should restart LSP server on another related LSP settings change"
8311    );
8312}
8313
8314#[gpui::test]
8315async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
8316    init_test(cx, |_| {});
8317
8318    let mut cx = EditorLspTestContext::new_rust(
8319        lsp::ServerCapabilities {
8320            completion_provider: Some(lsp::CompletionOptions {
8321                trigger_characters: Some(vec![".".to_string()]),
8322                resolve_provider: Some(true),
8323                ..Default::default()
8324            }),
8325            ..Default::default()
8326        },
8327        cx,
8328    )
8329    .await;
8330
8331    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
8332    cx.simulate_keystroke(".");
8333    let completion_item = lsp::CompletionItem {
8334        label: "some".into(),
8335        kind: Some(lsp::CompletionItemKind::SNIPPET),
8336        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
8337        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
8338            kind: lsp::MarkupKind::Markdown,
8339            value: "```rust\nSome(2)\n```".to_string(),
8340        })),
8341        deprecated: Some(false),
8342        sort_text: Some("fffffff2".to_string()),
8343        filter_text: Some("some".to_string()),
8344        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
8345        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
8346            range: lsp::Range {
8347                start: lsp::Position {
8348                    line: 0,
8349                    character: 22,
8350                },
8351                end: lsp::Position {
8352                    line: 0,
8353                    character: 22,
8354                },
8355            },
8356            new_text: "Some(2)".to_string(),
8357        })),
8358        additional_text_edits: Some(vec![lsp::TextEdit {
8359            range: lsp::Range {
8360                start: lsp::Position {
8361                    line: 0,
8362                    character: 20,
8363                },
8364                end: lsp::Position {
8365                    line: 0,
8366                    character: 22,
8367                },
8368            },
8369            new_text: "".to_string(),
8370        }]),
8371        ..Default::default()
8372    };
8373
8374    let closure_completion_item = completion_item.clone();
8375    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
8376        let task_completion_item = closure_completion_item.clone();
8377        async move {
8378            Ok(Some(lsp::CompletionResponse::Array(vec![
8379                task_completion_item,
8380            ])))
8381        }
8382    });
8383
8384    request.next().await;
8385
8386    cx.condition(|editor, _| editor.context_menu_visible())
8387        .await;
8388    let apply_additional_edits = cx.update_editor(|editor, cx| {
8389        editor
8390            .confirm_completion(&ConfirmCompletion::default(), cx)
8391            .unwrap()
8392    });
8393    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
8394
8395    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
8396        let task_completion_item = completion_item.clone();
8397        async move { Ok(task_completion_item) }
8398    })
8399    .next()
8400    .await
8401    .unwrap();
8402    apply_additional_edits.await.unwrap();
8403    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
8404}
8405
8406#[gpui::test]
8407async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
8408    init_test(cx, |_| {});
8409
8410    let mut cx = EditorLspTestContext::new(
8411        Language::new(
8412            LanguageConfig {
8413                matcher: LanguageMatcher {
8414                    path_suffixes: vec!["jsx".into()],
8415                    ..Default::default()
8416                },
8417                overrides: [(
8418                    "element".into(),
8419                    LanguageConfigOverride {
8420                        word_characters: Override::Set(['-'].into_iter().collect()),
8421                        ..Default::default()
8422                    },
8423                )]
8424                .into_iter()
8425                .collect(),
8426                ..Default::default()
8427            },
8428            Some(tree_sitter_typescript::language_tsx()),
8429        )
8430        .with_override_query("(jsx_self_closing_element) @element")
8431        .unwrap(),
8432        lsp::ServerCapabilities {
8433            completion_provider: Some(lsp::CompletionOptions {
8434                trigger_characters: Some(vec![":".to_string()]),
8435                ..Default::default()
8436            }),
8437            ..Default::default()
8438        },
8439        cx,
8440    )
8441    .await;
8442
8443    cx.lsp
8444        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
8445            Ok(Some(lsp::CompletionResponse::Array(vec![
8446                lsp::CompletionItem {
8447                    label: "bg-blue".into(),
8448                    ..Default::default()
8449                },
8450                lsp::CompletionItem {
8451                    label: "bg-red".into(),
8452                    ..Default::default()
8453                },
8454                lsp::CompletionItem {
8455                    label: "bg-yellow".into(),
8456                    ..Default::default()
8457                },
8458            ])))
8459        });
8460
8461    cx.set_state(r#"<p class="bgˇ" />"#);
8462
8463    // Trigger completion when typing a dash, because the dash is an extra
8464    // word character in the 'element' scope, which contains the cursor.
8465    cx.simulate_keystroke("-");
8466    cx.executor().run_until_parked();
8467    cx.update_editor(|editor, _| {
8468        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
8469            assert_eq!(
8470                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
8471                &["bg-red", "bg-blue", "bg-yellow"]
8472            );
8473        } else {
8474            panic!("expected completion menu to be open");
8475        }
8476    });
8477
8478    cx.simulate_keystroke("l");
8479    cx.executor().run_until_parked();
8480    cx.update_editor(|editor, _| {
8481        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
8482            assert_eq!(
8483                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
8484                &["bg-blue", "bg-yellow"]
8485            );
8486        } else {
8487            panic!("expected completion menu to be open");
8488        }
8489    });
8490
8491    // When filtering completions, consider the character after the '-' to
8492    // be the start of a subword.
8493    cx.set_state(r#"<p class="yelˇ" />"#);
8494    cx.simulate_keystroke("l");
8495    cx.executor().run_until_parked();
8496    cx.update_editor(|editor, _| {
8497        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
8498            assert_eq!(
8499                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
8500                &["bg-yellow"]
8501            );
8502        } else {
8503            panic!("expected completion menu to be open");
8504        }
8505    });
8506}
8507
8508#[gpui::test]
8509async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
8510    init_test(cx, |settings| {
8511        settings.defaults.formatter = Some(language_settings::Formatter::Prettier)
8512    });
8513
8514    let fs = FakeFs::new(cx.executor());
8515    fs.insert_file("/file.rs", Default::default()).await;
8516
8517    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
8518    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
8519
8520    language_registry.add(Arc::new(Language::new(
8521        LanguageConfig {
8522            name: "Rust".into(),
8523            matcher: LanguageMatcher {
8524                path_suffixes: vec!["rs".to_string()],
8525                ..Default::default()
8526            },
8527            prettier_parser_name: Some("test_parser".to_string()),
8528            ..Default::default()
8529        },
8530        Some(tree_sitter_rust::language()),
8531    )));
8532
8533    let test_plugin = "test_plugin";
8534    let _ = language_registry.register_fake_lsp_adapter(
8535        "Rust",
8536        FakeLspAdapter {
8537            prettier_plugins: vec![test_plugin],
8538            ..Default::default()
8539        },
8540    );
8541
8542    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
8543    let buffer = project
8544        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
8545        .await
8546        .unwrap();
8547
8548    let buffer_text = "one\ntwo\nthree\n";
8549    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
8550    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
8551    _ = editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
8552
8553    editor
8554        .update(cx, |editor, cx| {
8555            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
8556        })
8557        .unwrap()
8558        .await;
8559    assert_eq!(
8560        editor.update(cx, |editor, cx| editor.text(cx)),
8561        buffer_text.to_string() + prettier_format_suffix,
8562        "Test prettier formatting was not applied to the original buffer text",
8563    );
8564
8565    update_test_language_settings(cx, |settings| {
8566        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
8567    });
8568    let format = editor.update(cx, |editor, cx| {
8569        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
8570    });
8571    format.await.unwrap();
8572    assert_eq!(
8573        editor.update(cx, |editor, cx| editor.text(cx)),
8574        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
8575        "Autoformatting (via test prettier) was not applied to the original buffer text",
8576    );
8577}
8578
8579#[gpui::test]
8580async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
8581    init_test(cx, |_| {});
8582    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
8583    let base_text = indoc! {r#"struct Row;
8584struct Row1;
8585struct Row2;
8586
8587struct Row4;
8588struct Row5;
8589struct Row6;
8590
8591struct Row8;
8592struct Row9;
8593struct Row10;"#};
8594
8595    // When addition hunks are not adjacent to carets, no hunk revert is performed
8596    assert_hunk_revert(
8597        indoc! {r#"struct Row;
8598                   struct Row1;
8599                   struct Row1.1;
8600                   struct Row1.2;
8601                   struct Row2;ˇ
8602
8603                   struct Row4;
8604                   struct Row5;
8605                   struct Row6;
8606
8607                   struct Row8;
8608                   ˇstruct Row9;
8609                   struct Row9.1;
8610                   struct Row9.2;
8611                   struct Row9.3;
8612                   struct Row10;"#},
8613        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
8614        indoc! {r#"struct Row;
8615                   struct Row1;
8616                   struct Row1.1;
8617                   struct Row1.2;
8618                   struct Row2;ˇ
8619
8620                   struct Row4;
8621                   struct Row5;
8622                   struct Row6;
8623
8624                   struct Row8;
8625                   ˇstruct Row9;
8626                   struct Row9.1;
8627                   struct Row9.2;
8628                   struct Row9.3;
8629                   struct Row10;"#},
8630        base_text,
8631        &mut cx,
8632    );
8633    // Same for selections
8634    assert_hunk_revert(
8635        indoc! {r#"struct Row;
8636                   struct Row1;
8637                   struct Row2;
8638                   struct Row2.1;
8639                   struct Row2.2;
8640                   «ˇ
8641                   struct Row4;
8642                   struct» Row5;
8643                   «struct Row6;
8644                   ˇ»
8645                   struct Row9.1;
8646                   struct Row9.2;
8647                   struct Row9.3;
8648                   struct Row8;
8649                   struct Row9;
8650                   struct Row10;"#},
8651        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
8652        indoc! {r#"struct Row;
8653                   struct Row1;
8654                   struct Row2;
8655                   struct Row2.1;
8656                   struct Row2.2;
8657                   «ˇ
8658                   struct Row4;
8659                   struct» Row5;
8660                   «struct Row6;
8661                   ˇ»
8662                   struct Row9.1;
8663                   struct Row9.2;
8664                   struct Row9.3;
8665                   struct Row8;
8666                   struct Row9;
8667                   struct Row10;"#},
8668        base_text,
8669        &mut cx,
8670    );
8671
8672    // When carets and selections intersect the addition hunks, those are reverted.
8673    // Adjacent carets got merged.
8674    assert_hunk_revert(
8675        indoc! {r#"struct Row;
8676                   ˇ// something on the top
8677                   struct Row1;
8678                   struct Row2;
8679                   struct Roˇw3.1;
8680                   struct Row2.2;
8681                   struct Row2.3;ˇ
8682
8683                   struct Row4;
8684                   struct ˇRow5.1;
8685                   struct Row5.2;
8686                   struct «Rowˇ»5.3;
8687                   struct Row5;
8688                   struct Row6;
8689                   ˇ
8690                   struct Row9.1;
8691                   struct «Rowˇ»9.2;
8692                   struct «ˇRow»9.3;
8693                   struct Row8;
8694                   struct Row9;
8695                   «ˇ// something on bottom»
8696                   struct Row10;"#},
8697        vec![
8698            DiffHunkStatus::Added,
8699            DiffHunkStatus::Added,
8700            DiffHunkStatus::Added,
8701            DiffHunkStatus::Added,
8702            DiffHunkStatus::Added,
8703        ],
8704        indoc! {r#"struct Row;
8705                   ˇstruct Row1;
8706                   struct Row2;
8707                   ˇ
8708                   struct Row4;
8709                   ˇstruct Row5;
8710                   struct Row6;
8711                   ˇ
8712                   ˇstruct Row8;
8713                   struct Row9;
8714                   ˇstruct Row10;"#},
8715        base_text,
8716        &mut cx,
8717    );
8718}
8719
8720#[gpui::test]
8721async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
8722    init_test(cx, |_| {});
8723    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
8724    let base_text = indoc! {r#"struct Row;
8725struct Row1;
8726struct Row2;
8727
8728struct Row4;
8729struct Row5;
8730struct Row6;
8731
8732struct Row8;
8733struct Row9;
8734struct Row10;"#};
8735
8736    // Modification hunks behave the same as the addition ones.
8737    assert_hunk_revert(
8738        indoc! {r#"struct Row;
8739                   struct Row1;
8740                   struct Row33;
8741                   ˇ
8742                   struct Row4;
8743                   struct Row5;
8744                   struct Row6;
8745                   ˇ
8746                   struct Row99;
8747                   struct Row9;
8748                   struct Row10;"#},
8749        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
8750        indoc! {r#"struct Row;
8751                   struct Row1;
8752                   struct Row33;
8753                   ˇ
8754                   struct Row4;
8755                   struct Row5;
8756                   struct Row6;
8757                   ˇ
8758                   struct Row99;
8759                   struct Row9;
8760                   struct Row10;"#},
8761        base_text,
8762        &mut cx,
8763    );
8764    assert_hunk_revert(
8765        indoc! {r#"struct Row;
8766                   struct Row1;
8767                   struct Row33;
8768                   «ˇ
8769                   struct Row4;
8770                   struct» Row5;
8771                   «struct Row6;
8772                   ˇ»
8773                   struct Row99;
8774                   struct Row9;
8775                   struct Row10;"#},
8776        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
8777        indoc! {r#"struct Row;
8778                   struct Row1;
8779                   struct Row33;
8780                   «ˇ
8781                   struct Row4;
8782                   struct» Row5;
8783                   «struct Row6;
8784                   ˇ»
8785                   struct Row99;
8786                   struct Row9;
8787                   struct Row10;"#},
8788        base_text,
8789        &mut cx,
8790    );
8791
8792    assert_hunk_revert(
8793        indoc! {r#"ˇstruct Row1.1;
8794                   struct Row1;
8795                   «ˇstr»uct Row22;
8796
8797                   struct ˇRow44;
8798                   struct Row5;
8799                   struct «Rˇ»ow66;ˇ
8800
8801                   «struˇ»ct Row88;
8802                   struct Row9;
8803                   struct Row1011;ˇ"#},
8804        vec![
8805            DiffHunkStatus::Modified,
8806            DiffHunkStatus::Modified,
8807            DiffHunkStatus::Modified,
8808            DiffHunkStatus::Modified,
8809            DiffHunkStatus::Modified,
8810            DiffHunkStatus::Modified,
8811        ],
8812        indoc! {r#"struct Row;
8813                   ˇstruct Row1;
8814                   struct Row2;
8815                   ˇ
8816                   struct Row4;
8817                   ˇstruct Row5;
8818                   struct Row6;
8819                   ˇ
8820                   struct Row8;
8821                   ˇstruct Row9;
8822                   struct Row10;ˇ"#},
8823        base_text,
8824        &mut cx,
8825    );
8826}
8827
8828#[gpui::test]
8829async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
8830    init_test(cx, |_| {});
8831    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
8832    let base_text = indoc! {r#"struct Row;
8833struct Row1;
8834struct Row2;
8835
8836struct Row4;
8837struct Row5;
8838struct Row6;
8839
8840struct Row8;
8841struct Row9;
8842struct Row10;"#};
8843
8844    // Deletion hunks trigger with carets on ajacent rows, so carets and selections have to stay farther to avoid the revert
8845    assert_hunk_revert(
8846        indoc! {r#"struct Row;
8847                   struct Row2;
8848
8849                   ˇstruct Row4;
8850                   struct Row5;
8851                   struct Row6;
8852                   ˇ
8853                   struct Row8;
8854                   struct Row10;"#},
8855        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
8856        indoc! {r#"struct Row;
8857                   struct Row2;
8858
8859                   ˇstruct Row4;
8860                   struct Row5;
8861                   struct Row6;
8862                   ˇ
8863                   struct Row8;
8864                   struct Row10;"#},
8865        base_text,
8866        &mut cx,
8867    );
8868    assert_hunk_revert(
8869        indoc! {r#"struct Row;
8870                   struct Row2;
8871
8872                   «ˇstruct Row4;
8873                   struct» Row5;
8874                   «struct Row6;
8875                   ˇ»
8876                   struct Row8;
8877                   struct Row10;"#},
8878        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
8879        indoc! {r#"struct Row;
8880                   struct Row2;
8881
8882                   «ˇstruct Row4;
8883                   struct» Row5;
8884                   «struct Row6;
8885                   ˇ»
8886                   struct Row8;
8887                   struct Row10;"#},
8888        base_text,
8889        &mut cx,
8890    );
8891
8892    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
8893    assert_hunk_revert(
8894        indoc! {r#"struct Row;
8895                   ˇstruct Row2;
8896
8897                   struct Row4;
8898                   struct Row5;
8899                   struct Row6;
8900
8901                   struct Row8;ˇ
8902                   struct Row10;"#},
8903        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
8904        indoc! {r#"struct Row;
8905                   struct Row1;
8906                   ˇstruct Row2;
8907
8908                   struct Row4;
8909                   struct Row5;
8910                   struct Row6;
8911
8912                   struct Row8;ˇ
8913                   struct Row9;
8914                   struct Row10;"#},
8915        base_text,
8916        &mut cx,
8917    );
8918    assert_hunk_revert(
8919        indoc! {r#"struct Row;
8920                   struct Row2«ˇ;
8921                   struct Row4;
8922                   struct» Row5;
8923                   «struct Row6;
8924
8925                   struct Row8;ˇ»
8926                   struct Row10;"#},
8927        vec![
8928            DiffHunkStatus::Removed,
8929            DiffHunkStatus::Removed,
8930            DiffHunkStatus::Removed,
8931        ],
8932        indoc! {r#"struct Row;
8933                   struct Row1;
8934                   struct Row2«ˇ;
8935
8936                   struct Row4;
8937                   struct» Row5;
8938                   «struct Row6;
8939
8940                   struct Row8;ˇ»
8941                   struct Row9;
8942                   struct Row10;"#},
8943        base_text,
8944        &mut cx,
8945    );
8946}
8947
8948#[gpui::test]
8949async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
8950    init_test(cx, |_| {});
8951
8952    let cols = 4;
8953    let rows = 10;
8954    let sample_text_1 = sample_text(rows, cols, 'a');
8955    assert_eq!(
8956        sample_text_1,
8957        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
8958    );
8959    let sample_text_2 = sample_text(rows, cols, 'l');
8960    assert_eq!(
8961        sample_text_2,
8962        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
8963    );
8964    let sample_text_3 = sample_text(rows, cols, 'v');
8965    assert_eq!(
8966        sample_text_3,
8967        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
8968    );
8969
8970    fn diff_every_buffer_row(
8971        buffer: &Model<Buffer>,
8972        sample_text: String,
8973        cols: usize,
8974        cx: &mut gpui::TestAppContext,
8975    ) {
8976        // revert first character in each row, creating one large diff hunk per buffer
8977        let is_first_char = |offset: usize| offset % cols == 0;
8978        buffer.update(cx, |buffer, cx| {
8979            buffer.set_text(
8980                sample_text
8981                    .chars()
8982                    .enumerate()
8983                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
8984                    .collect::<String>(),
8985                cx,
8986            );
8987            buffer.set_diff_base(Some(sample_text), cx);
8988        });
8989        cx.executor().run_until_parked();
8990    }
8991
8992    let buffer_1 = cx.new_model(|cx| {
8993        Buffer::new(
8994            0,
8995            BufferId::new(cx.entity_id().as_u64()).unwrap(),
8996            sample_text_1.clone(),
8997        )
8998    });
8999    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
9000
9001    let buffer_2 = cx.new_model(|cx| {
9002        Buffer::new(
9003            1,
9004            BufferId::new(cx.entity_id().as_u64() + 1).unwrap(),
9005            sample_text_2.clone(),
9006        )
9007    });
9008    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
9009
9010    let buffer_3 = cx.new_model(|cx| {
9011        Buffer::new(
9012            2,
9013            BufferId::new(cx.entity_id().as_u64() + 2).unwrap(),
9014            sample_text_3.clone(),
9015        )
9016    });
9017    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
9018
9019    let multibuffer = cx.new_model(|cx| {
9020        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
9021        multibuffer.push_excerpts(
9022            buffer_1.clone(),
9023            [
9024                ExcerptRange {
9025                    context: Point::new(0, 0)..Point::new(3, 0),
9026                    primary: None,
9027                },
9028                ExcerptRange {
9029                    context: Point::new(5, 0)..Point::new(7, 0),
9030                    primary: None,
9031                },
9032                ExcerptRange {
9033                    context: Point::new(9, 0)..Point::new(10, 4),
9034                    primary: None,
9035                },
9036            ],
9037            cx,
9038        );
9039        multibuffer.push_excerpts(
9040            buffer_2.clone(),
9041            [
9042                ExcerptRange {
9043                    context: Point::new(0, 0)..Point::new(3, 0),
9044                    primary: None,
9045                },
9046                ExcerptRange {
9047                    context: Point::new(5, 0)..Point::new(7, 0),
9048                    primary: None,
9049                },
9050                ExcerptRange {
9051                    context: Point::new(9, 0)..Point::new(10, 4),
9052                    primary: None,
9053                },
9054            ],
9055            cx,
9056        );
9057        multibuffer.push_excerpts(
9058            buffer_3.clone(),
9059            [
9060                ExcerptRange {
9061                    context: Point::new(0, 0)..Point::new(3, 0),
9062                    primary: None,
9063                },
9064                ExcerptRange {
9065                    context: Point::new(5, 0)..Point::new(7, 0),
9066                    primary: None,
9067                },
9068                ExcerptRange {
9069                    context: Point::new(9, 0)..Point::new(10, 4),
9070                    primary: None,
9071                },
9072            ],
9073            cx,
9074        );
9075        multibuffer
9076    });
9077
9078    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
9079    editor.update(cx, |editor, cx| {
9080        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");
9081        editor.select_all(&SelectAll, cx);
9082        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
9083    });
9084    cx.executor().run_until_parked();
9085    // When all ranges are selected, all buffer hunks are reverted.
9086    editor.update(cx, |editor, cx| {
9087        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");
9088    });
9089    buffer_1.update(cx, |buffer, _| {
9090        assert_eq!(buffer.text(), sample_text_1);
9091    });
9092    buffer_2.update(cx, |buffer, _| {
9093        assert_eq!(buffer.text(), sample_text_2);
9094    });
9095    buffer_3.update(cx, |buffer, _| {
9096        assert_eq!(buffer.text(), sample_text_3);
9097    });
9098
9099    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
9100    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
9101    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
9102    editor.update(cx, |editor, cx| {
9103        editor.change_selections(None, cx, |s| {
9104            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
9105        });
9106        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
9107    });
9108    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
9109    // but not affect buffer_2 and its related excerpts.
9110    editor.update(cx, |editor, cx| {
9111        assert_eq!(
9112            editor.text(cx),
9113            "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"
9114        );
9115    });
9116    buffer_1.update(cx, |buffer, _| {
9117        assert_eq!(buffer.text(), sample_text_1);
9118    });
9119    buffer_2.update(cx, |buffer, _| {
9120        assert_eq!(
9121            buffer.text(),
9122            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
9123        );
9124    });
9125    buffer_3.update(cx, |buffer, _| {
9126        assert_eq!(
9127            buffer.text(),
9128            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
9129        );
9130    });
9131}
9132
9133#[gpui::test]
9134async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
9135    init_test(cx, |_| {});
9136
9137    let cols = 4;
9138    let rows = 10;
9139    let sample_text_1 = sample_text(rows, cols, 'a');
9140    assert_eq!(
9141        sample_text_1,
9142        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
9143    );
9144    let sample_text_2 = sample_text(rows, cols, 'l');
9145    assert_eq!(
9146        sample_text_2,
9147        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
9148    );
9149    let sample_text_3 = sample_text(rows, cols, 'v');
9150    assert_eq!(
9151        sample_text_3,
9152        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
9153    );
9154
9155    let buffer_1 = cx.new_model(|cx| {
9156        Buffer::new(
9157            0,
9158            BufferId::new(cx.entity_id().as_u64()).unwrap(),
9159            sample_text_1.clone(),
9160        )
9161    });
9162
9163    let buffer_2 = cx.new_model(|cx| {
9164        Buffer::new(
9165            1,
9166            BufferId::new(cx.entity_id().as_u64() + 1).unwrap(),
9167            sample_text_2.clone(),
9168        )
9169    });
9170
9171    let buffer_3 = cx.new_model(|cx| {
9172        Buffer::new(
9173            2,
9174            BufferId::new(cx.entity_id().as_u64() + 2).unwrap(),
9175            sample_text_3.clone(),
9176        )
9177    });
9178
9179    let multi_buffer = cx.new_model(|cx| {
9180        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
9181        multibuffer.push_excerpts(
9182            buffer_1.clone(),
9183            [
9184                ExcerptRange {
9185                    context: Point::new(0, 0)..Point::new(3, 0),
9186                    primary: None,
9187                },
9188                ExcerptRange {
9189                    context: Point::new(5, 0)..Point::new(7, 0),
9190                    primary: None,
9191                },
9192                ExcerptRange {
9193                    context: Point::new(9, 0)..Point::new(10, 4),
9194                    primary: None,
9195                },
9196            ],
9197            cx,
9198        );
9199        multibuffer.push_excerpts(
9200            buffer_2.clone(),
9201            [
9202                ExcerptRange {
9203                    context: Point::new(0, 0)..Point::new(3, 0),
9204                    primary: None,
9205                },
9206                ExcerptRange {
9207                    context: Point::new(5, 0)..Point::new(7, 0),
9208                    primary: None,
9209                },
9210                ExcerptRange {
9211                    context: Point::new(9, 0)..Point::new(10, 4),
9212                    primary: None,
9213                },
9214            ],
9215            cx,
9216        );
9217        multibuffer.push_excerpts(
9218            buffer_3.clone(),
9219            [
9220                ExcerptRange {
9221                    context: Point::new(0, 0)..Point::new(3, 0),
9222                    primary: None,
9223                },
9224                ExcerptRange {
9225                    context: Point::new(5, 0)..Point::new(7, 0),
9226                    primary: None,
9227                },
9228                ExcerptRange {
9229                    context: Point::new(9, 0)..Point::new(10, 4),
9230                    primary: None,
9231                },
9232            ],
9233            cx,
9234        );
9235        multibuffer
9236    });
9237
9238    let fs = FakeFs::new(cx.executor());
9239    fs.insert_tree(
9240        "/a",
9241        json!({
9242            "main.rs": sample_text_1,
9243            "other.rs": sample_text_2,
9244            "lib.rs": sample_text_3,
9245        }),
9246    )
9247    .await;
9248    let project = Project::test(fs, ["/a".as_ref()], cx).await;
9249    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
9250    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
9251    let multi_buffer_editor =
9252        cx.new_view(|cx| Editor::new(EditorMode::Full, multi_buffer, Some(project.clone()), cx));
9253    let multibuffer_item_id = workspace
9254        .update(cx, |workspace, cx| {
9255            assert!(
9256                workspace.active_item(cx).is_none(),
9257                "active item should be None before the first item is added"
9258            );
9259            workspace.add_item_to_active_pane(Box::new(multi_buffer_editor.clone()), cx);
9260            let active_item = workspace
9261                .active_item(cx)
9262                .expect("should have an active item after adding the multi buffer");
9263            assert!(
9264                !active_item.is_singleton(cx),
9265                "A multi buffer was expected to active after adding"
9266            );
9267            active_item.item_id()
9268        })
9269        .unwrap();
9270    cx.executor().run_until_parked();
9271
9272    multi_buffer_editor.update(cx, |editor, cx| {
9273        editor.change_selections(Some(Autoscroll::Next), cx, |s| s.select_ranges(Some(1..2)));
9274        editor.open_excerpts(&OpenExcerpts, cx);
9275    });
9276    cx.executor().run_until_parked();
9277    let first_item_id = workspace
9278        .update(cx, |workspace, cx| {
9279            let active_item = workspace
9280                .active_item(cx)
9281                .expect("should have an active item after navigating into the 1st buffer");
9282            let first_item_id = active_item.item_id();
9283            assert_ne!(
9284                first_item_id, multibuffer_item_id,
9285                "Should navigate into the 1st buffer and activate it"
9286            );
9287            assert!(
9288                active_item.is_singleton(cx),
9289                "New active item should be a singleton buffer"
9290            );
9291            assert_eq!(
9292                active_item
9293                    .act_as::<Editor>(cx)
9294                    .expect("should have navigated into an editor for the 1st buffer")
9295                    .read(cx)
9296                    .text(cx),
9297                sample_text_1
9298            );
9299
9300            workspace
9301                .go_back(workspace.active_pane().downgrade(), cx)
9302                .detach_and_log_err(cx);
9303
9304            first_item_id
9305        })
9306        .unwrap();
9307    cx.executor().run_until_parked();
9308    workspace
9309        .update(cx, |workspace, cx| {
9310            let active_item = workspace
9311                .active_item(cx)
9312                .expect("should have an active item after navigating back");
9313            assert_eq!(
9314                active_item.item_id(),
9315                multibuffer_item_id,
9316                "Should navigate back to the multi buffer"
9317            );
9318            assert!(!active_item.is_singleton(cx));
9319        })
9320        .unwrap();
9321
9322    multi_buffer_editor.update(cx, |editor, cx| {
9323        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
9324            s.select_ranges(Some(39..40))
9325        });
9326        editor.open_excerpts(&OpenExcerpts, cx);
9327    });
9328    cx.executor().run_until_parked();
9329    let second_item_id = workspace
9330        .update(cx, |workspace, cx| {
9331            let active_item = workspace
9332                .active_item(cx)
9333                .expect("should have an active item after navigating into the 2nd buffer");
9334            let second_item_id = active_item.item_id();
9335            assert_ne!(
9336                second_item_id, multibuffer_item_id,
9337                "Should navigate away from the multibuffer"
9338            );
9339            assert_ne!(
9340                second_item_id, first_item_id,
9341                "Should navigate into the 2nd buffer and activate it"
9342            );
9343            assert!(
9344                active_item.is_singleton(cx),
9345                "New active item should be a singleton buffer"
9346            );
9347            assert_eq!(
9348                active_item
9349                    .act_as::<Editor>(cx)
9350                    .expect("should have navigated into an editor")
9351                    .read(cx)
9352                    .text(cx),
9353                sample_text_2
9354            );
9355
9356            workspace
9357                .go_back(workspace.active_pane().downgrade(), cx)
9358                .detach_and_log_err(cx);
9359
9360            second_item_id
9361        })
9362        .unwrap();
9363    cx.executor().run_until_parked();
9364    workspace
9365        .update(cx, |workspace, cx| {
9366            let active_item = workspace
9367                .active_item(cx)
9368                .expect("should have an active item after navigating back from the 2nd buffer");
9369            assert_eq!(
9370                active_item.item_id(),
9371                multibuffer_item_id,
9372                "Should navigate back from the 2nd buffer to the multi buffer"
9373            );
9374            assert!(!active_item.is_singleton(cx));
9375        })
9376        .unwrap();
9377
9378    multi_buffer_editor.update(cx, |editor, cx| {
9379        editor.change_selections(Some(Autoscroll::Next), cx, |s| {
9380            s.select_ranges(Some(60..70))
9381        });
9382        editor.open_excerpts(&OpenExcerpts, cx);
9383    });
9384    cx.executor().run_until_parked();
9385    workspace
9386        .update(cx, |workspace, cx| {
9387            let active_item = workspace
9388                .active_item(cx)
9389                .expect("should have an active item after navigating into the 3rd buffer");
9390            let third_item_id = active_item.item_id();
9391            assert_ne!(
9392                third_item_id, multibuffer_item_id,
9393                "Should navigate into the 3rd buffer and activate it"
9394            );
9395            assert_ne!(third_item_id, first_item_id);
9396            assert_ne!(third_item_id, second_item_id);
9397            assert!(
9398                active_item.is_singleton(cx),
9399                "New active item should be a singleton buffer"
9400            );
9401            assert_eq!(
9402                active_item
9403                    .act_as::<Editor>(cx)
9404                    .expect("should have navigated into an editor")
9405                    .read(cx)
9406                    .text(cx),
9407                sample_text_3
9408            );
9409
9410            workspace
9411                .go_back(workspace.active_pane().downgrade(), cx)
9412                .detach_and_log_err(cx);
9413        })
9414        .unwrap();
9415    cx.executor().run_until_parked();
9416    workspace
9417        .update(cx, |workspace, cx| {
9418            let active_item = workspace
9419                .active_item(cx)
9420                .expect("should have an active item after navigating back from the 3rd buffer");
9421            assert_eq!(
9422                active_item.item_id(),
9423                multibuffer_item_id,
9424                "Should navigate back from the 3rd buffer to the multi buffer"
9425            );
9426            assert!(!active_item.is_singleton(cx));
9427        })
9428        .unwrap();
9429}
9430
9431fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
9432    let point = DisplayPoint::new(row as u32, column as u32);
9433    point..point
9434}
9435
9436fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
9437    let (text, ranges) = marked_text_ranges(marked_text, true);
9438    assert_eq!(view.text(cx), text);
9439    assert_eq!(
9440        view.selections.ranges(cx),
9441        ranges,
9442        "Assert selections are {}",
9443        marked_text
9444    );
9445}
9446
9447/// Handle completion request passing a marked string specifying where the completion
9448/// should be triggered from using '|' character, what range should be replaced, and what completions
9449/// should be returned using '<' and '>' to delimit the range
9450pub fn handle_completion_request(
9451    cx: &mut EditorLspTestContext,
9452    marked_string: &str,
9453    completions: Vec<&'static str>,
9454) -> impl Future<Output = ()> {
9455    let complete_from_marker: TextRangeMarker = '|'.into();
9456    let replace_range_marker: TextRangeMarker = ('<', '>').into();
9457    let (_, mut marked_ranges) = marked_text_ranges_by(
9458        marked_string,
9459        vec![complete_from_marker.clone(), replace_range_marker.clone()],
9460    );
9461
9462    let complete_from_position =
9463        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
9464    let replace_range =
9465        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
9466
9467    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
9468        let completions = completions.clone();
9469        async move {
9470            assert_eq!(params.text_document_position.text_document.uri, url.clone());
9471            assert_eq!(
9472                params.text_document_position.position,
9473                complete_from_position
9474            );
9475            Ok(Some(lsp::CompletionResponse::Array(
9476                completions
9477                    .iter()
9478                    .map(|completion_text| lsp::CompletionItem {
9479                        label: completion_text.to_string(),
9480                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
9481                            range: replace_range,
9482                            new_text: completion_text.to_string(),
9483                        })),
9484                        ..Default::default()
9485                    })
9486                    .collect(),
9487            )))
9488        }
9489    });
9490
9491    async move {
9492        request.next().await;
9493    }
9494}
9495
9496fn handle_resolve_completion_request(
9497    cx: &mut EditorLspTestContext,
9498    edits: Option<Vec<(&'static str, &'static str)>>,
9499) -> impl Future<Output = ()> {
9500    let edits = edits.map(|edits| {
9501        edits
9502            .iter()
9503            .map(|(marked_string, new_text)| {
9504                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
9505                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
9506                lsp::TextEdit::new(replace_range, new_text.to_string())
9507            })
9508            .collect::<Vec<_>>()
9509    });
9510
9511    let mut request =
9512        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
9513            let edits = edits.clone();
9514            async move {
9515                Ok(lsp::CompletionItem {
9516                    additional_text_edits: edits,
9517                    ..Default::default()
9518                })
9519            }
9520        });
9521
9522    async move {
9523        request.next().await;
9524    }
9525}
9526
9527pub(crate) fn update_test_language_settings(
9528    cx: &mut TestAppContext,
9529    f: impl Fn(&mut AllLanguageSettingsContent),
9530) {
9531    _ = cx.update(|cx| {
9532        cx.update_global(|store: &mut SettingsStore, cx| {
9533            store.update_user_settings::<AllLanguageSettings>(cx, f);
9534        });
9535    });
9536}
9537
9538pub(crate) fn update_test_project_settings(
9539    cx: &mut TestAppContext,
9540    f: impl Fn(&mut ProjectSettings),
9541) {
9542    _ = cx.update(|cx| {
9543        cx.update_global(|store: &mut SettingsStore, cx| {
9544            store.update_user_settings::<ProjectSettings>(cx, f);
9545        });
9546    });
9547}
9548
9549pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
9550    _ = cx.update(|cx| {
9551        let store = SettingsStore::test(cx);
9552        cx.set_global(store);
9553        theme::init(theme::LoadThemes::JustBase, cx);
9554        release_channel::init("0.0.0", cx);
9555        client::init_settings(cx);
9556        language::init(cx);
9557        Project::init_settings(cx);
9558        workspace::init_settings(cx);
9559        crate::init(cx);
9560    });
9561
9562    update_test_language_settings(cx, f);
9563}
9564
9565pub(crate) fn rust_lang() -> Arc<Language> {
9566    Arc::new(Language::new(
9567        LanguageConfig {
9568            name: "Rust".into(),
9569            matcher: LanguageMatcher {
9570                path_suffixes: vec!["rs".to_string()],
9571                ..Default::default()
9572            },
9573            ..Default::default()
9574        },
9575        Some(tree_sitter_rust::language()),
9576    ))
9577}
9578
9579#[track_caller]
9580fn assert_hunk_revert(
9581    not_reverted_text_with_selections: &str,
9582    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
9583    expected_reverted_text_with_selections: &str,
9584    base_text: &str,
9585    cx: &mut EditorLspTestContext,
9586) {
9587    cx.set_state(not_reverted_text_with_selections);
9588    cx.update_editor(|editor, cx| {
9589        editor
9590            .buffer()
9591            .read(cx)
9592            .as_singleton()
9593            .unwrap()
9594            .update(cx, |buffer, cx| {
9595                buffer.set_diff_base(Some(base_text.to_string()), cx);
9596            });
9597    });
9598    cx.executor().run_until_parked();
9599
9600    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
9601        let snapshot = editor
9602            .buffer()
9603            .read(cx)
9604            .as_singleton()
9605            .unwrap()
9606            .read(cx)
9607            .snapshot();
9608        let reverted_hunk_statuses = snapshot
9609            .git_diff_hunks_in_row_range(0..u32::MAX)
9610            .map(|hunk| hunk.status())
9611            .collect::<Vec<_>>();
9612
9613        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
9614        reverted_hunk_statuses
9615    });
9616    cx.executor().run_until_parked();
9617    cx.assert_editor_state(expected_reverted_text_with_selections);
9618    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
9619}