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