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, 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, 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
3157#[gpui::test]
3158fn test_move_line_up_down(cx: &mut TestAppContext) {
3159    init_test(cx, |_| {});
3160
3161    let view = cx.add_window(|cx| {
3162        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
3163        build_editor(buffer, cx)
3164    });
3165    _ = view.update(cx, |view, cx| {
3166        view.fold_ranges(
3167            vec![
3168                Point::new(0, 2)..Point::new(1, 2),
3169                Point::new(2, 3)..Point::new(4, 1),
3170                Point::new(7, 0)..Point::new(8, 4),
3171            ],
3172            true,
3173            cx,
3174        );
3175        view.change_selections(None, cx, |s| {
3176            s.select_display_ranges([
3177                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
3178                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
3179                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
3180                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
3181            ])
3182        });
3183        assert_eq!(
3184            view.display_text(cx),
3185            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
3186        );
3187
3188        view.move_line_up(&MoveLineUp, cx);
3189        assert_eq!(
3190            view.display_text(cx),
3191            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
3192        );
3193        assert_eq!(
3194            view.selections.display_ranges(cx),
3195            vec![
3196                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
3197                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
3198                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
3199                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
3200            ]
3201        );
3202    });
3203
3204    _ = view.update(cx, |view, cx| {
3205        view.move_line_down(&MoveLineDown, cx);
3206        assert_eq!(
3207            view.display_text(cx),
3208            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
3209        );
3210        assert_eq!(
3211            view.selections.display_ranges(cx),
3212            vec![
3213                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
3214                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
3215                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
3216                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
3217            ]
3218        );
3219    });
3220
3221    _ = view.update(cx, |view, cx| {
3222        view.move_line_down(&MoveLineDown, cx);
3223        assert_eq!(
3224            view.display_text(cx),
3225            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
3226        );
3227        assert_eq!(
3228            view.selections.display_ranges(cx),
3229            vec![
3230                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
3231                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
3232                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
3233                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
3234            ]
3235        );
3236    });
3237
3238    _ = view.update(cx, |view, cx| {
3239        view.move_line_up(&MoveLineUp, cx);
3240        assert_eq!(
3241            view.display_text(cx),
3242            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
3243        );
3244        assert_eq!(
3245            view.selections.display_ranges(cx),
3246            vec![
3247                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
3248                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
3249                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
3250                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
3251            ]
3252        );
3253    });
3254}
3255
3256#[gpui::test]
3257fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
3258    init_test(cx, |_| {});
3259
3260    let editor = cx.add_window(|cx| {
3261        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
3262        build_editor(buffer, cx)
3263    });
3264    _ = editor.update(cx, |editor, cx| {
3265        let snapshot = editor.buffer.read(cx).snapshot(cx);
3266        editor.insert_blocks(
3267            [BlockProperties {
3268                style: BlockStyle::Fixed,
3269                position: snapshot.anchor_after(Point::new(2, 0)),
3270                disposition: BlockDisposition::Below,
3271                height: 1,
3272                render: Arc::new(|_| div().into_any()),
3273            }],
3274            Some(Autoscroll::fit()),
3275            cx,
3276        );
3277        editor.change_selections(None, cx, |s| {
3278            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
3279        });
3280        editor.move_line_down(&MoveLineDown, cx);
3281    });
3282}
3283
3284#[gpui::test]
3285fn test_transpose(cx: &mut TestAppContext) {
3286    init_test(cx, |_| {});
3287
3288    _ = cx.add_window(|cx| {
3289        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
3290        editor.set_style(EditorStyle::default(), cx);
3291        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
3292        editor.transpose(&Default::default(), cx);
3293        assert_eq!(editor.text(cx), "bac");
3294        assert_eq!(editor.selections.ranges(cx), [2..2]);
3295
3296        editor.transpose(&Default::default(), cx);
3297        assert_eq!(editor.text(cx), "bca");
3298        assert_eq!(editor.selections.ranges(cx), [3..3]);
3299
3300        editor.transpose(&Default::default(), cx);
3301        assert_eq!(editor.text(cx), "bac");
3302        assert_eq!(editor.selections.ranges(cx), [3..3]);
3303
3304        editor
3305    });
3306
3307    _ = cx.add_window(|cx| {
3308        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
3309        editor.set_style(EditorStyle::default(), cx);
3310        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
3311        editor.transpose(&Default::default(), cx);
3312        assert_eq!(editor.text(cx), "acb\nde");
3313        assert_eq!(editor.selections.ranges(cx), [3..3]);
3314
3315        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
3316        editor.transpose(&Default::default(), cx);
3317        assert_eq!(editor.text(cx), "acbd\ne");
3318        assert_eq!(editor.selections.ranges(cx), [5..5]);
3319
3320        editor.transpose(&Default::default(), cx);
3321        assert_eq!(editor.text(cx), "acbde\n");
3322        assert_eq!(editor.selections.ranges(cx), [6..6]);
3323
3324        editor.transpose(&Default::default(), cx);
3325        assert_eq!(editor.text(cx), "acbd\ne");
3326        assert_eq!(editor.selections.ranges(cx), [6..6]);
3327
3328        editor
3329    });
3330
3331    _ = cx.add_window(|cx| {
3332        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
3333        editor.set_style(EditorStyle::default(), cx);
3334        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
3335        editor.transpose(&Default::default(), cx);
3336        assert_eq!(editor.text(cx), "bacd\ne");
3337        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
3338
3339        editor.transpose(&Default::default(), cx);
3340        assert_eq!(editor.text(cx), "bcade\n");
3341        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
3342
3343        editor.transpose(&Default::default(), cx);
3344        assert_eq!(editor.text(cx), "bcda\ne");
3345        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
3346
3347        editor.transpose(&Default::default(), cx);
3348        assert_eq!(editor.text(cx), "bcade\n");
3349        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
3350
3351        editor.transpose(&Default::default(), cx);
3352        assert_eq!(editor.text(cx), "bcaed\n");
3353        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
3354
3355        editor
3356    });
3357
3358    _ = cx.add_window(|cx| {
3359        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
3360        editor.set_style(EditorStyle::default(), cx);
3361        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
3362        editor.transpose(&Default::default(), cx);
3363        assert_eq!(editor.text(cx), "🏀🍐✋");
3364        assert_eq!(editor.selections.ranges(cx), [8..8]);
3365
3366        editor.transpose(&Default::default(), cx);
3367        assert_eq!(editor.text(cx), "🏀✋🍐");
3368        assert_eq!(editor.selections.ranges(cx), [11..11]);
3369
3370        editor.transpose(&Default::default(), cx);
3371        assert_eq!(editor.text(cx), "🏀🍐✋");
3372        assert_eq!(editor.selections.ranges(cx), [11..11]);
3373
3374        editor
3375    });
3376}
3377
3378#[gpui::test]
3379async fn test_clipboard(cx: &mut gpui::TestAppContext) {
3380    init_test(cx, |_| {});
3381
3382    let mut cx = EditorTestContext::new(cx).await;
3383
3384    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
3385    cx.update_editor(|e, cx| e.cut(&Cut, cx));
3386    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
3387
3388    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
3389    cx.set_state("two ˇfour ˇsix ˇ");
3390    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3391    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
3392
3393    // Paste again but with only two cursors. Since the number of cursors doesn't
3394    // match the number of slices in the clipboard, the entire clipboard text
3395    // is pasted at each cursor.
3396    cx.set_state("ˇtwo one✅ four three six five ˇ");
3397    cx.update_editor(|e, cx| {
3398        e.handle_input("( ", cx);
3399        e.paste(&Paste, cx);
3400        e.handle_input(") ", cx);
3401    });
3402    cx.assert_editor_state(
3403        &([
3404            "( one✅ ",
3405            "three ",
3406            "five ) ˇtwo one✅ four three six five ( one✅ ",
3407            "three ",
3408            "five ) ˇ",
3409        ]
3410        .join("\n")),
3411    );
3412
3413    // Cut with three selections, one of which is full-line.
3414    cx.set_state(indoc! {"
3415        1«2ˇ»3
3416        4ˇ567
3417        «8ˇ»9"});
3418    cx.update_editor(|e, cx| e.cut(&Cut, cx));
3419    cx.assert_editor_state(indoc! {"
3420        1ˇ3
3421        ˇ9"});
3422
3423    // Paste with three selections, noticing how the copied selection that was full-line
3424    // gets inserted before the second cursor.
3425    cx.set_state(indoc! {"
3426        1ˇ3
34273428        «oˇ»ne"});
3429    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3430    cx.assert_editor_state(indoc! {"
3431        12ˇ3
3432        4567
34333434        8ˇne"});
3435
3436    // Copy with a single cursor only, which writes the whole line into the clipboard.
3437    cx.set_state(indoc! {"
3438        The quick brown
3439        fox juˇmps over
3440        the lazy dog"});
3441    cx.update_editor(|e, cx| e.copy(&Copy, cx));
3442    assert_eq!(
3443        cx.read_from_clipboard().map(|item| item.text().to_owned()),
3444        Some("fox jumps over\n".to_owned())
3445    );
3446
3447    // Paste with three selections, noticing how the copied full-line selection is inserted
3448    // before the empty selections but replaces the selection that is non-empty.
3449    cx.set_state(indoc! {"
3450        Tˇhe quick brown
3451        «foˇ»x jumps over
3452        tˇhe lazy dog"});
3453    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3454    cx.assert_editor_state(indoc! {"
3455        fox jumps over
3456        Tˇhe quick brown
3457        fox jumps over
3458        ˇx jumps over
3459        fox jumps over
3460        tˇhe lazy dog"});
3461}
3462
3463#[gpui::test]
3464async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
3465    init_test(cx, |_| {});
3466
3467    let mut cx = EditorTestContext::new(cx).await;
3468    let language = Arc::new(Language::new(
3469        LanguageConfig::default(),
3470        Some(tree_sitter_rust::language()),
3471    ));
3472    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
3473
3474    // Cut an indented block, without the leading whitespace.
3475    cx.set_state(indoc! {"
3476        const a: B = (
3477            c(),
3478            «d(
3479                e,
3480                f
3481            )ˇ»
3482        );
3483    "});
3484    cx.update_editor(|e, cx| e.cut(&Cut, cx));
3485    cx.assert_editor_state(indoc! {"
3486        const a: B = (
3487            c(),
3488            ˇ
3489        );
3490    "});
3491
3492    // Paste it at the same position.
3493    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3494    cx.assert_editor_state(indoc! {"
3495        const a: B = (
3496            c(),
3497            d(
3498                e,
3499                f
35003501        );
3502    "});
3503
3504    // Paste it at a line with a lower indent level.
3505    cx.set_state(indoc! {"
3506        ˇ
3507        const a: B = (
3508            c(),
3509        );
3510    "});
3511    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3512    cx.assert_editor_state(indoc! {"
3513        d(
3514            e,
3515            f
35163517        const a: B = (
3518            c(),
3519        );
3520    "});
3521
3522    // Cut an indented block, with the leading whitespace.
3523    cx.set_state(indoc! {"
3524        const a: B = (
3525            c(),
3526        «    d(
3527                e,
3528                f
3529            )
3530        ˇ»);
3531    "});
3532    cx.update_editor(|e, cx| e.cut(&Cut, cx));
3533    cx.assert_editor_state(indoc! {"
3534        const a: B = (
3535            c(),
3536        ˇ);
3537    "});
3538
3539    // Paste it at the same position.
3540    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3541    cx.assert_editor_state(indoc! {"
3542        const a: B = (
3543            c(),
3544            d(
3545                e,
3546                f
3547            )
3548        ˇ);
3549    "});
3550
3551    // Paste it at a line with a higher indent level.
3552    cx.set_state(indoc! {"
3553        const a: B = (
3554            c(),
3555            d(
3556                e,
35573558            )
3559        );
3560    "});
3561    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3562    cx.assert_editor_state(indoc! {"
3563        const a: B = (
3564            c(),
3565            d(
3566                e,
3567                f    d(
3568                    e,
3569                    f
3570                )
3571        ˇ
3572            )
3573        );
3574    "});
3575}
3576
3577#[gpui::test]
3578fn test_select_all(cx: &mut TestAppContext) {
3579    init_test(cx, |_| {});
3580
3581    let view = cx.add_window(|cx| {
3582        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
3583        build_editor(buffer, cx)
3584    });
3585    _ = view.update(cx, |view, cx| {
3586        view.select_all(&SelectAll, cx);
3587        assert_eq!(
3588            view.selections.display_ranges(cx),
3589            &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
3590        );
3591    });
3592}
3593
3594#[gpui::test]
3595fn test_select_line(cx: &mut TestAppContext) {
3596    init_test(cx, |_| {});
3597
3598    let view = cx.add_window(|cx| {
3599        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
3600        build_editor(buffer, cx)
3601    });
3602    _ = view.update(cx, |view, cx| {
3603        view.change_selections(None, cx, |s| {
3604            s.select_display_ranges([
3605                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3606                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3607                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
3608                DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
3609            ])
3610        });
3611        view.select_line(&SelectLine, cx);
3612        assert_eq!(
3613            view.selections.display_ranges(cx),
3614            vec![
3615                DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
3616                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
3617            ]
3618        );
3619    });
3620
3621    _ = view.update(cx, |view, cx| {
3622        view.select_line(&SelectLine, cx);
3623        assert_eq!(
3624            view.selections.display_ranges(cx),
3625            vec![
3626                DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
3627                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
3628            ]
3629        );
3630    });
3631
3632    _ = view.update(cx, |view, cx| {
3633        view.select_line(&SelectLine, cx);
3634        assert_eq!(
3635            view.selections.display_ranges(cx),
3636            vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
3637        );
3638    });
3639}
3640
3641#[gpui::test]
3642fn test_split_selection_into_lines(cx: &mut TestAppContext) {
3643    init_test(cx, |_| {});
3644
3645    let view = cx.add_window(|cx| {
3646        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
3647        build_editor(buffer, cx)
3648    });
3649    _ = view.update(cx, |view, cx| {
3650        view.fold_ranges(
3651            vec![
3652                Point::new(0, 2)..Point::new(1, 2),
3653                Point::new(2, 3)..Point::new(4, 1),
3654                Point::new(7, 0)..Point::new(8, 4),
3655            ],
3656            true,
3657            cx,
3658        );
3659        view.change_selections(None, cx, |s| {
3660            s.select_display_ranges([
3661                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3662                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3663                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
3664                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
3665            ])
3666        });
3667        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
3668    });
3669
3670    _ = view.update(cx, |view, cx| {
3671        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
3672        assert_eq!(
3673            view.display_text(cx),
3674            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
3675        );
3676        assert_eq!(
3677            view.selections.display_ranges(cx),
3678            [
3679                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
3680                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3681                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
3682                DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
3683            ]
3684        );
3685    });
3686
3687    _ = view.update(cx, |view, cx| {
3688        view.change_selections(None, cx, |s| {
3689            s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
3690        });
3691        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
3692        assert_eq!(
3693            view.display_text(cx),
3694            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
3695        );
3696        assert_eq!(
3697            view.selections.display_ranges(cx),
3698            [
3699                DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
3700                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
3701                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
3702                DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
3703                DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
3704                DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
3705                DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
3706                DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
3707            ]
3708        );
3709    });
3710}
3711
3712#[gpui::test]
3713async fn test_add_selection_above_below(cx: &mut TestAppContext) {
3714    init_test(cx, |_| {});
3715
3716    let mut cx = EditorTestContext::new(cx).await;
3717
3718    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
3719    cx.set_state(indoc!(
3720        r#"abc
3721           defˇghi
3722
3723           jk
3724           nlmo
3725           "#
3726    ));
3727
3728    cx.update_editor(|editor, cx| {
3729        editor.add_selection_above(&Default::default(), cx);
3730    });
3731
3732    cx.assert_editor_state(indoc!(
3733        r#"abcˇ
3734           defˇghi
3735
3736           jk
3737           nlmo
3738           "#
3739    ));
3740
3741    cx.update_editor(|editor, cx| {
3742        editor.add_selection_above(&Default::default(), cx);
3743    });
3744
3745    cx.assert_editor_state(indoc!(
3746        r#"abcˇ
3747            defˇghi
3748
3749            jk
3750            nlmo
3751            "#
3752    ));
3753
3754    cx.update_editor(|view, cx| {
3755        view.add_selection_below(&Default::default(), cx);
3756    });
3757
3758    cx.assert_editor_state(indoc!(
3759        r#"abc
3760           defˇghi
3761
3762           jk
3763           nlmo
3764           "#
3765    ));
3766
3767    cx.update_editor(|view, cx| {
3768        view.undo_selection(&Default::default(), cx);
3769    });
3770
3771    cx.assert_editor_state(indoc!(
3772        r#"abcˇ
3773           defˇghi
3774
3775           jk
3776           nlmo
3777           "#
3778    ));
3779
3780    cx.update_editor(|view, cx| {
3781        view.redo_selection(&Default::default(), cx);
3782    });
3783
3784    cx.assert_editor_state(indoc!(
3785        r#"abc
3786           defˇghi
3787
3788           jk
3789           nlmo
3790           "#
3791    ));
3792
3793    cx.update_editor(|view, cx| {
3794        view.add_selection_below(&Default::default(), cx);
3795    });
3796
3797    cx.assert_editor_state(indoc!(
3798        r#"abc
3799           defˇghi
3800
3801           jk
3802           nlmˇo
3803           "#
3804    ));
3805
3806    cx.update_editor(|view, cx| {
3807        view.add_selection_below(&Default::default(), cx);
3808    });
3809
3810    cx.assert_editor_state(indoc!(
3811        r#"abc
3812           defˇghi
3813
3814           jk
3815           nlmˇo
3816           "#
3817    ));
3818
3819    // change selections
3820    cx.set_state(indoc!(
3821        r#"abc
3822           def«ˇg»hi
3823
3824           jk
3825           nlmo
3826           "#
3827    ));
3828
3829    cx.update_editor(|view, cx| {
3830        view.add_selection_below(&Default::default(), cx);
3831    });
3832
3833    cx.assert_editor_state(indoc!(
3834        r#"abc
3835           def«ˇg»hi
3836
3837           jk
3838           nlm«ˇo»
3839           "#
3840    ));
3841
3842    cx.update_editor(|view, cx| {
3843        view.add_selection_below(&Default::default(), cx);
3844    });
3845
3846    cx.assert_editor_state(indoc!(
3847        r#"abc
3848           def«ˇg»hi
3849
3850           jk
3851           nlm«ˇo»
3852           "#
3853    ));
3854
3855    cx.update_editor(|view, cx| {
3856        view.add_selection_above(&Default::default(), cx);
3857    });
3858
3859    cx.assert_editor_state(indoc!(
3860        r#"abc
3861           def«ˇg»hi
3862
3863           jk
3864           nlmo
3865           "#
3866    ));
3867
3868    cx.update_editor(|view, cx| {
3869        view.add_selection_above(&Default::default(), cx);
3870    });
3871
3872    cx.assert_editor_state(indoc!(
3873        r#"abc
3874           def«ˇg»hi
3875
3876           jk
3877           nlmo
3878           "#
3879    ));
3880
3881    // Change selections again
3882    cx.set_state(indoc!(
3883        r#"a«bc
3884           defgˇ»hi
3885
3886           jk
3887           nlmo
3888           "#
3889    ));
3890
3891    cx.update_editor(|view, cx| {
3892        view.add_selection_below(&Default::default(), cx);
3893    });
3894
3895    cx.assert_editor_state(indoc!(
3896        r#"a«bcˇ»
3897           d«efgˇ»hi
3898
3899           j«kˇ»
3900           nlmo
3901           "#
3902    ));
3903
3904    cx.update_editor(|view, cx| {
3905        view.add_selection_below(&Default::default(), cx);
3906    });
3907    cx.assert_editor_state(indoc!(
3908        r#"a«bcˇ»
3909           d«efgˇ»hi
3910
3911           j«kˇ»
3912           n«lmoˇ»
3913           "#
3914    ));
3915    cx.update_editor(|view, cx| {
3916        view.add_selection_above(&Default::default(), cx);
3917    });
3918
3919    cx.assert_editor_state(indoc!(
3920        r#"a«bcˇ»
3921           d«efgˇ»hi
3922
3923           j«kˇ»
3924           nlmo
3925           "#
3926    ));
3927
3928    // Change selections again
3929    cx.set_state(indoc!(
3930        r#"abc
3931           d«ˇefghi
3932
3933           jk
3934           nlm»o
3935           "#
3936    ));
3937
3938    cx.update_editor(|view, cx| {
3939        view.add_selection_above(&Default::default(), cx);
3940    });
3941
3942    cx.assert_editor_state(indoc!(
3943        r#"a«ˇbc»
3944           d«ˇef»ghi
3945
3946           j«ˇk»
3947           n«ˇlm»o
3948           "#
3949    ));
3950
3951    cx.update_editor(|view, cx| {
3952        view.add_selection_below(&Default::default(), cx);
3953    });
3954
3955    cx.assert_editor_state(indoc!(
3956        r#"abc
3957           d«ˇef»ghi
3958
3959           j«ˇk»
3960           n«ˇlm»o
3961           "#
3962    ));
3963}
3964
3965#[gpui::test]
3966async fn test_select_next(cx: &mut gpui::TestAppContext) {
3967    init_test(cx, |_| {});
3968
3969    let mut cx = EditorTestContext::new(cx).await;
3970    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
3971
3972    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
3973        .unwrap();
3974    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3975
3976    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
3977        .unwrap();
3978    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
3979
3980    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
3981    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3982
3983    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
3984    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
3985
3986    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
3987        .unwrap();
3988    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
3989
3990    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
3991        .unwrap();
3992    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
3993}
3994
3995#[gpui::test]
3996async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
3997    init_test(cx, |_| {});
3998
3999    let mut cx = EditorTestContext::new(cx).await;
4000    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
4001
4002    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
4003        .unwrap();
4004    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
4005}
4006
4007#[gpui::test]
4008async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
4009    init_test(cx, |_| {});
4010
4011    let mut cx = EditorTestContext::new(cx).await;
4012    cx.set_state(
4013        r#"let foo = 2;
4014lˇet foo = 2;
4015let fooˇ = 2;
4016let foo = 2;
4017let foo = ˇ2;"#,
4018    );
4019
4020    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
4021        .unwrap();
4022    cx.assert_editor_state(
4023        r#"let foo = 2;
4024«letˇ» foo = 2;
4025let «fooˇ» = 2;
4026let foo = 2;
4027let foo = «2ˇ»;"#,
4028    );
4029
4030    // noop for multiple selections with different contents
4031    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
4032        .unwrap();
4033    cx.assert_editor_state(
4034        r#"let foo = 2;
4035«letˇ» foo = 2;
4036let «fooˇ» = 2;
4037let foo = 2;
4038let foo = «2ˇ»;"#,
4039    );
4040}
4041
4042#[gpui::test]
4043async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
4044    init_test(cx, |_| {});
4045
4046    let mut cx = EditorTestContext::new(cx).await;
4047    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
4048
4049    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4050        .unwrap();
4051    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
4052
4053    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4054        .unwrap();
4055    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
4056
4057    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
4058    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
4059
4060    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
4061    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
4062
4063    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4064        .unwrap();
4065    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
4066
4067    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4068        .unwrap();
4069    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
4070
4071    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4072        .unwrap();
4073    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
4074}
4075
4076#[gpui::test]
4077async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
4078    init_test(cx, |_| {});
4079
4080    let mut cx = EditorTestContext::new(cx).await;
4081    cx.set_state(
4082        r#"let foo = 2;
4083lˇet foo = 2;
4084let fooˇ = 2;
4085let foo = 2;
4086let foo = ˇ2;"#,
4087    );
4088
4089    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4090        .unwrap();
4091    cx.assert_editor_state(
4092        r#"let foo = 2;
4093«letˇ» foo = 2;
4094let «fooˇ» = 2;
4095let foo = 2;
4096let foo = «2ˇ»;"#,
4097    );
4098
4099    // noop for multiple selections with different contents
4100    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4101        .unwrap();
4102    cx.assert_editor_state(
4103        r#"let foo = 2;
4104«letˇ» foo = 2;
4105let «fooˇ» = 2;
4106let foo = 2;
4107let foo = «2ˇ»;"#,
4108    );
4109}
4110
4111#[gpui::test]
4112async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
4113    init_test(cx, |_| {});
4114
4115    let mut cx = EditorTestContext::new(cx).await;
4116    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
4117
4118    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4119        .unwrap();
4120    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
4121
4122    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4123        .unwrap();
4124    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
4125
4126    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
4127    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
4128
4129    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
4130    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
4131
4132    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4133        .unwrap();
4134    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
4135
4136    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4137        .unwrap();
4138    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
4139}
4140
4141#[gpui::test]
4142async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
4143    init_test(cx, |_| {});
4144
4145    let language = Arc::new(Language::new(
4146        LanguageConfig::default(),
4147        Some(tree_sitter_rust::language()),
4148    ));
4149
4150    let text = r#"
4151        use mod1::mod2::{mod3, mod4};
4152
4153        fn fn_1(param1: bool, param2: &str) {
4154            let var1 = "text";
4155        }
4156    "#
4157    .unindent();
4158
4159    let buffer = cx.new_model(|cx| {
4160        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
4161            .with_language(language, cx)
4162    });
4163    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
4164    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
4165
4166    view.condition::<crate::EditorEvent>(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
4167        .await;
4168
4169    _ = view.update(cx, |view, cx| {
4170        view.change_selections(None, cx, |s| {
4171            s.select_display_ranges([
4172                DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
4173                DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
4174                DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
4175            ]);
4176        });
4177        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
4178    });
4179    assert_eq!(
4180        view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
4181        &[
4182            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
4183            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
4184            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
4185        ]
4186    );
4187
4188    _ = view.update(cx, |view, cx| {
4189        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
4190    });
4191    assert_eq!(
4192        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4193        &[
4194            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
4195            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
4196        ]
4197    );
4198
4199    _ = view.update(cx, |view, cx| {
4200        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
4201    });
4202    assert_eq!(
4203        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4204        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
4205    );
4206
4207    // Trying to expand the selected syntax node one more time has no effect.
4208    _ = view.update(cx, |view, cx| {
4209        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
4210    });
4211    assert_eq!(
4212        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4213        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
4214    );
4215
4216    _ = view.update(cx, |view, cx| {
4217        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
4218    });
4219    assert_eq!(
4220        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4221        &[
4222            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
4223            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
4224        ]
4225    );
4226
4227    _ = view.update(cx, |view, cx| {
4228        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
4229    });
4230    assert_eq!(
4231        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4232        &[
4233            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
4234            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
4235            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
4236        ]
4237    );
4238
4239    _ = view.update(cx, |view, cx| {
4240        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
4241    });
4242    assert_eq!(
4243        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4244        &[
4245            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
4246            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
4247            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
4248        ]
4249    );
4250
4251    // Trying to shrink the selected syntax node one more time has no effect.
4252    _ = view.update(cx, |view, cx| {
4253        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
4254    });
4255    assert_eq!(
4256        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4257        &[
4258            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
4259            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
4260            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
4261        ]
4262    );
4263
4264    // Ensure that we keep expanding the selection if the larger selection starts or ends within
4265    // a fold.
4266    _ = view.update(cx, |view, cx| {
4267        view.fold_ranges(
4268            vec![
4269                Point::new(0, 21)..Point::new(0, 24),
4270                Point::new(3, 20)..Point::new(3, 22),
4271            ],
4272            true,
4273            cx,
4274        );
4275        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
4276    });
4277    assert_eq!(
4278        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4279        &[
4280            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
4281            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
4282            DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
4283        ]
4284    );
4285}
4286
4287#[gpui::test]
4288async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
4289    init_test(cx, |_| {});
4290
4291    let language = Arc::new(
4292        Language::new(
4293            LanguageConfig {
4294                brackets: BracketPairConfig {
4295                    pairs: vec![
4296                        BracketPair {
4297                            start: "{".to_string(),
4298                            end: "}".to_string(),
4299                            close: false,
4300                            newline: true,
4301                        },
4302                        BracketPair {
4303                            start: "(".to_string(),
4304                            end: ")".to_string(),
4305                            close: false,
4306                            newline: true,
4307                        },
4308                    ],
4309                    ..Default::default()
4310                },
4311                ..Default::default()
4312            },
4313            Some(tree_sitter_rust::language()),
4314        )
4315        .with_indents_query(
4316            r#"
4317                (_ "(" ")" @end) @indent
4318                (_ "{" "}" @end) @indent
4319            "#,
4320        )
4321        .unwrap(),
4322    );
4323
4324    let text = "fn a() {}";
4325
4326    let buffer = cx.new_model(|cx| {
4327        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
4328            .with_language(language, cx)
4329    });
4330    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
4331    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
4332    editor
4333        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
4334        .await;
4335
4336    _ = editor.update(cx, |editor, cx| {
4337        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
4338        editor.newline(&Newline, cx);
4339        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
4340        assert_eq!(
4341            editor.selections.ranges(cx),
4342            &[
4343                Point::new(1, 4)..Point::new(1, 4),
4344                Point::new(3, 4)..Point::new(3, 4),
4345                Point::new(5, 0)..Point::new(5, 0)
4346            ]
4347        );
4348    });
4349}
4350
4351#[gpui::test]
4352async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
4353    init_test(cx, |_| {});
4354
4355    let mut cx = EditorTestContext::new(cx).await;
4356
4357    let language = Arc::new(Language::new(
4358        LanguageConfig {
4359            brackets: BracketPairConfig {
4360                pairs: vec![
4361                    BracketPair {
4362                        start: "{".to_string(),
4363                        end: "}".to_string(),
4364                        close: true,
4365                        newline: true,
4366                    },
4367                    BracketPair {
4368                        start: "(".to_string(),
4369                        end: ")".to_string(),
4370                        close: true,
4371                        newline: true,
4372                    },
4373                    BracketPair {
4374                        start: "/*".to_string(),
4375                        end: " */".to_string(),
4376                        close: true,
4377                        newline: true,
4378                    },
4379                    BracketPair {
4380                        start: "[".to_string(),
4381                        end: "]".to_string(),
4382                        close: false,
4383                        newline: true,
4384                    },
4385                    BracketPair {
4386                        start: "\"".to_string(),
4387                        end: "\"".to_string(),
4388                        close: true,
4389                        newline: false,
4390                    },
4391                ],
4392                ..Default::default()
4393            },
4394            autoclose_before: "})]".to_string(),
4395            ..Default::default()
4396        },
4397        Some(tree_sitter_rust::language()),
4398    ));
4399
4400    let registry = Arc::new(LanguageRegistry::test());
4401    registry.add(language.clone());
4402    cx.update_buffer(|buffer, cx| {
4403        buffer.set_language_registry(registry);
4404        buffer.set_language(Some(language), cx);
4405    });
4406
4407    cx.set_state(
4408        &r#"
4409            🏀ˇ
4410            εˇ
4411            ❤️ˇ
4412        "#
4413        .unindent(),
4414    );
4415
4416    // autoclose multiple nested brackets at multiple cursors
4417    cx.update_editor(|view, cx| {
4418        view.handle_input("{", cx);
4419        view.handle_input("{", cx);
4420        view.handle_input("{", cx);
4421    });
4422    cx.assert_editor_state(
4423        &"
4424            🏀{{{ˇ}}}
4425            ε{{{ˇ}}}
4426            ❤️{{{ˇ}}}
4427        "
4428        .unindent(),
4429    );
4430
4431    // insert a different closing bracket
4432    cx.update_editor(|view, cx| {
4433        view.handle_input(")", cx);
4434    });
4435    cx.assert_editor_state(
4436        &"
4437            🏀{{{)ˇ}}}
4438            ε{{{)ˇ}}}
4439            ❤️{{{)ˇ}}}
4440        "
4441        .unindent(),
4442    );
4443
4444    // skip over the auto-closed brackets when typing a closing bracket
4445    cx.update_editor(|view, cx| {
4446        view.move_right(&MoveRight, cx);
4447        view.handle_input("}", cx);
4448        view.handle_input("}", cx);
4449        view.handle_input("}", cx);
4450    });
4451    cx.assert_editor_state(
4452        &"
4453            🏀{{{)}}}}ˇ
4454            ε{{{)}}}}ˇ
4455            ❤️{{{)}}}}ˇ
4456        "
4457        .unindent(),
4458    );
4459
4460    // autoclose multi-character pairs
4461    cx.set_state(
4462        &"
4463            ˇ
4464            ˇ
4465        "
4466        .unindent(),
4467    );
4468    cx.update_editor(|view, cx| {
4469        view.handle_input("/", cx);
4470        view.handle_input("*", cx);
4471    });
4472    cx.assert_editor_state(
4473        &"
4474            /*ˇ */
4475            /*ˇ */
4476        "
4477        .unindent(),
4478    );
4479
4480    // one cursor autocloses a multi-character pair, one cursor
4481    // does not autoclose.
4482    cx.set_state(
4483        &"
44844485            ˇ
4486        "
4487        .unindent(),
4488    );
4489    cx.update_editor(|view, cx| view.handle_input("*", cx));
4490    cx.assert_editor_state(
4491        &"
4492            /*ˇ */
44934494        "
4495        .unindent(),
4496    );
4497
4498    // Don't autoclose if the next character isn't whitespace and isn't
4499    // listed in the language's "autoclose_before" section.
4500    cx.set_state("ˇa b");
4501    cx.update_editor(|view, cx| view.handle_input("{", cx));
4502    cx.assert_editor_state("{ˇa b");
4503
4504    // Don't autoclose if `close` is false for the bracket pair
4505    cx.set_state("ˇ");
4506    cx.update_editor(|view, cx| view.handle_input("[", cx));
4507    cx.assert_editor_state("");
4508
4509    // Surround with brackets if text is selected
4510    cx.set_state("«aˇ» b");
4511    cx.update_editor(|view, cx| view.handle_input("{", cx));
4512    cx.assert_editor_state("{«aˇ»} b");
4513
4514    // Autclose pair where the start and end characters are the same
4515    cx.set_state("");
4516    cx.update_editor(|view, cx| view.handle_input("\"", cx));
4517    cx.assert_editor_state("a\"ˇ\"");
4518    cx.update_editor(|view, cx| view.handle_input("\"", cx));
4519    cx.assert_editor_state("a\"\"ˇ");
4520}
4521
4522#[gpui::test]
4523async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
4524    init_test(cx, |_| {});
4525
4526    let mut cx = EditorTestContext::new(cx).await;
4527
4528    let html_language = Arc::new(
4529        Language::new(
4530            LanguageConfig {
4531                name: "HTML".into(),
4532                brackets: BracketPairConfig {
4533                    pairs: vec![
4534                        BracketPair {
4535                            start: "<".into(),
4536                            end: ">".into(),
4537                            close: true,
4538                            ..Default::default()
4539                        },
4540                        BracketPair {
4541                            start: "{".into(),
4542                            end: "}".into(),
4543                            close: true,
4544                            ..Default::default()
4545                        },
4546                        BracketPair {
4547                            start: "(".into(),
4548                            end: ")".into(),
4549                            close: true,
4550                            ..Default::default()
4551                        },
4552                    ],
4553                    ..Default::default()
4554                },
4555                autoclose_before: "})]>".into(),
4556                ..Default::default()
4557            },
4558            Some(tree_sitter_html::language()),
4559        )
4560        .with_injection_query(
4561            r#"
4562            (script_element
4563                (raw_text) @content
4564                (#set! "language" "javascript"))
4565            "#,
4566        )
4567        .unwrap(),
4568    );
4569
4570    let javascript_language = Arc::new(Language::new(
4571        LanguageConfig {
4572            name: "JavaScript".into(),
4573            brackets: BracketPairConfig {
4574                pairs: vec![
4575                    BracketPair {
4576                        start: "/*".into(),
4577                        end: " */".into(),
4578                        close: true,
4579                        ..Default::default()
4580                    },
4581                    BracketPair {
4582                        start: "{".into(),
4583                        end: "}".into(),
4584                        close: true,
4585                        ..Default::default()
4586                    },
4587                    BracketPair {
4588                        start: "(".into(),
4589                        end: ")".into(),
4590                        close: true,
4591                        ..Default::default()
4592                    },
4593                ],
4594                ..Default::default()
4595            },
4596            autoclose_before: "})]>".into(),
4597            ..Default::default()
4598        },
4599        Some(tree_sitter_typescript::language_tsx()),
4600    ));
4601
4602    let registry = Arc::new(LanguageRegistry::test());
4603    registry.add(html_language.clone());
4604    registry.add(javascript_language.clone());
4605
4606    cx.update_buffer(|buffer, cx| {
4607        buffer.set_language_registry(registry);
4608        buffer.set_language(Some(html_language), cx);
4609    });
4610
4611    cx.set_state(
4612        &r#"
4613            <body>ˇ
4614                <script>
4615                    var x = 1;ˇ
4616                </script>
4617            </body>ˇ
4618        "#
4619        .unindent(),
4620    );
4621
4622    // Precondition: different languages are active at different locations.
4623    cx.update_editor(|editor, cx| {
4624        let snapshot = editor.snapshot(cx);
4625        let cursors = editor.selections.ranges::<usize>(cx);
4626        let languages = cursors
4627            .iter()
4628            .map(|c| snapshot.language_at(c.start).unwrap().name())
4629            .collect::<Vec<_>>();
4630        assert_eq!(
4631            languages,
4632            &["HTML".into(), "JavaScript".into(), "HTML".into()]
4633        );
4634    });
4635
4636    // Angle brackets autoclose in HTML, but not JavaScript.
4637    cx.update_editor(|editor, cx| {
4638        editor.handle_input("<", cx);
4639        editor.handle_input("a", cx);
4640    });
4641    cx.assert_editor_state(
4642        &r#"
4643            <body><aˇ>
4644                <script>
4645                    var x = 1;<aˇ
4646                </script>
4647            </body><aˇ>
4648        "#
4649        .unindent(),
4650    );
4651
4652    // Curly braces and parens autoclose in both HTML and JavaScript.
4653    cx.update_editor(|editor, cx| {
4654        editor.handle_input(" b=", cx);
4655        editor.handle_input("{", cx);
4656        editor.handle_input("c", cx);
4657        editor.handle_input("(", cx);
4658    });
4659    cx.assert_editor_state(
4660        &r#"
4661            <body><a b={c(ˇ)}>
4662                <script>
4663                    var x = 1;<a b={c(ˇ)}
4664                </script>
4665            </body><a b={c(ˇ)}>
4666        "#
4667        .unindent(),
4668    );
4669
4670    // Brackets that were already autoclosed are skipped.
4671    cx.update_editor(|editor, cx| {
4672        editor.handle_input(")", cx);
4673        editor.handle_input("d", cx);
4674        editor.handle_input("}", cx);
4675    });
4676    cx.assert_editor_state(
4677        &r#"
4678            <body><a b={c()d}ˇ>
4679                <script>
4680                    var x = 1;<a b={c()d}ˇ
4681                </script>
4682            </body><a b={c()d}ˇ>
4683        "#
4684        .unindent(),
4685    );
4686    cx.update_editor(|editor, cx| {
4687        editor.handle_input(">", cx);
4688    });
4689    cx.assert_editor_state(
4690        &r#"
4691            <body><a b={c()d}>ˇ
4692                <script>
4693                    var x = 1;<a b={c()d}>ˇ
4694                </script>
4695            </body><a b={c()d}>ˇ
4696        "#
4697        .unindent(),
4698    );
4699
4700    // Reset
4701    cx.set_state(
4702        &r#"
4703            <body>ˇ
4704                <script>
4705                    var x = 1;ˇ
4706                </script>
4707            </body>ˇ
4708        "#
4709        .unindent(),
4710    );
4711
4712    cx.update_editor(|editor, cx| {
4713        editor.handle_input("<", cx);
4714    });
4715    cx.assert_editor_state(
4716        &r#"
4717            <body><ˇ>
4718                <script>
4719                    var x = 1;<ˇ
4720                </script>
4721            </body><ˇ>
4722        "#
4723        .unindent(),
4724    );
4725
4726    // When backspacing, the closing angle brackets are removed.
4727    cx.update_editor(|editor, cx| {
4728        editor.backspace(&Backspace, cx);
4729    });
4730    cx.assert_editor_state(
4731        &r#"
4732            <body>ˇ
4733                <script>
4734                    var x = 1;ˇ
4735                </script>
4736            </body>ˇ
4737        "#
4738        .unindent(),
4739    );
4740
4741    // Block comments autoclose in JavaScript, but not HTML.
4742    cx.update_editor(|editor, cx| {
4743        editor.handle_input("/", cx);
4744        editor.handle_input("*", cx);
4745    });
4746    cx.assert_editor_state(
4747        &r#"
4748            <body>/*ˇ
4749                <script>
4750                    var x = 1;/*ˇ */
4751                </script>
4752            </body>/*ˇ
4753        "#
4754        .unindent(),
4755    );
4756}
4757
4758#[gpui::test]
4759async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
4760    init_test(cx, |_| {});
4761
4762    let mut cx = EditorTestContext::new(cx).await;
4763
4764    let rust_language = Arc::new(
4765        Language::new(
4766            LanguageConfig {
4767                name: "Rust".into(),
4768                brackets: serde_json::from_value(json!([
4769                    { "start": "{", "end": "}", "close": true, "newline": true },
4770                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
4771                ]))
4772                .unwrap(),
4773                autoclose_before: "})]>".into(),
4774                ..Default::default()
4775            },
4776            Some(tree_sitter_rust::language()),
4777        )
4778        .with_override_query("(string_literal) @string")
4779        .unwrap(),
4780    );
4781
4782    let registry = Arc::new(LanguageRegistry::test());
4783    registry.add(rust_language.clone());
4784
4785    cx.update_buffer(|buffer, cx| {
4786        buffer.set_language_registry(registry);
4787        buffer.set_language(Some(rust_language), cx);
4788    });
4789
4790    cx.set_state(
4791        &r#"
4792            let x = ˇ
4793        "#
4794        .unindent(),
4795    );
4796
4797    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
4798    cx.update_editor(|editor, cx| {
4799        editor.handle_input("\"", cx);
4800    });
4801    cx.assert_editor_state(
4802        &r#"
4803            let x = "ˇ"
4804        "#
4805        .unindent(),
4806    );
4807
4808    // Inserting another quotation mark. The cursor moves across the existing
4809    // automatically-inserted quotation mark.
4810    cx.update_editor(|editor, cx| {
4811        editor.handle_input("\"", cx);
4812    });
4813    cx.assert_editor_state(
4814        &r#"
4815            let x = ""ˇ
4816        "#
4817        .unindent(),
4818    );
4819
4820    // Reset
4821    cx.set_state(
4822        &r#"
4823            let x = ˇ
4824        "#
4825        .unindent(),
4826    );
4827
4828    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
4829    cx.update_editor(|editor, cx| {
4830        editor.handle_input("\"", cx);
4831        editor.handle_input(" ", cx);
4832        editor.move_left(&Default::default(), cx);
4833        editor.handle_input("\\", cx);
4834        editor.handle_input("\"", cx);
4835    });
4836    cx.assert_editor_state(
4837        &r#"
4838            let x = "\"ˇ "
4839        "#
4840        .unindent(),
4841    );
4842
4843    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
4844    // mark. Nothing is inserted.
4845    cx.update_editor(|editor, cx| {
4846        editor.move_right(&Default::default(), cx);
4847        editor.handle_input("\"", cx);
4848    });
4849    cx.assert_editor_state(
4850        &r#"
4851            let x = "\" "ˇ
4852        "#
4853        .unindent(),
4854    );
4855}
4856
4857#[gpui::test]
4858async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
4859    init_test(cx, |_| {});
4860
4861    let language = Arc::new(Language::new(
4862        LanguageConfig {
4863            brackets: BracketPairConfig {
4864                pairs: vec![
4865                    BracketPair {
4866                        start: "{".to_string(),
4867                        end: "}".to_string(),
4868                        close: true,
4869                        newline: true,
4870                    },
4871                    BracketPair {
4872                        start: "/* ".to_string(),
4873                        end: "*/".to_string(),
4874                        close: true,
4875                        ..Default::default()
4876                    },
4877                ],
4878                ..Default::default()
4879            },
4880            ..Default::default()
4881        },
4882        Some(tree_sitter_rust::language()),
4883    ));
4884
4885    let text = r#"
4886        a
4887        b
4888        c
4889    "#
4890    .unindent();
4891
4892    let buffer = cx.new_model(|cx| {
4893        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
4894            .with_language(language, cx)
4895    });
4896    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
4897    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
4898    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
4899        .await;
4900
4901    _ = view.update(cx, |view, cx| {
4902        view.change_selections(None, cx, |s| {
4903            s.select_display_ranges([
4904                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
4905                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
4906                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
4907            ])
4908        });
4909
4910        view.handle_input("{", cx);
4911        view.handle_input("{", cx);
4912        view.handle_input("{", cx);
4913        assert_eq!(
4914            view.text(cx),
4915            "
4916                {{{a}}}
4917                {{{b}}}
4918                {{{c}}}
4919            "
4920            .unindent()
4921        );
4922        assert_eq!(
4923            view.selections.display_ranges(cx),
4924            [
4925                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
4926                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
4927                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
4928            ]
4929        );
4930
4931        view.undo(&Undo, cx);
4932        view.undo(&Undo, cx);
4933        view.undo(&Undo, cx);
4934        assert_eq!(
4935            view.text(cx),
4936            "
4937                a
4938                b
4939                c
4940            "
4941            .unindent()
4942        );
4943        assert_eq!(
4944            view.selections.display_ranges(cx),
4945            [
4946                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
4947                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
4948                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
4949            ]
4950        );
4951
4952        // Ensure inserting the first character of a multi-byte bracket pair
4953        // doesn't surround the selections with the bracket.
4954        view.handle_input("/", cx);
4955        assert_eq!(
4956            view.text(cx),
4957            "
4958                /
4959                /
4960                /
4961            "
4962            .unindent()
4963        );
4964        assert_eq!(
4965            view.selections.display_ranges(cx),
4966            [
4967                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
4968                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
4969                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
4970            ]
4971        );
4972
4973        view.undo(&Undo, cx);
4974        assert_eq!(
4975            view.text(cx),
4976            "
4977                a
4978                b
4979                c
4980            "
4981            .unindent()
4982        );
4983        assert_eq!(
4984            view.selections.display_ranges(cx),
4985            [
4986                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
4987                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
4988                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
4989            ]
4990        );
4991
4992        // Ensure inserting the last character of a multi-byte bracket pair
4993        // doesn't surround the selections with the bracket.
4994        view.handle_input("*", cx);
4995        assert_eq!(
4996            view.text(cx),
4997            "
4998                *
4999                *
5000                *
5001            "
5002            .unindent()
5003        );
5004        assert_eq!(
5005            view.selections.display_ranges(cx),
5006            [
5007                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
5008                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
5009                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
5010            ]
5011        );
5012    });
5013}
5014
5015#[gpui::test]
5016async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
5017    init_test(cx, |_| {});
5018
5019    let language = Arc::new(Language::new(
5020        LanguageConfig {
5021            brackets: BracketPairConfig {
5022                pairs: vec![BracketPair {
5023                    start: "{".to_string(),
5024                    end: "}".to_string(),
5025                    close: true,
5026                    newline: true,
5027                }],
5028                ..Default::default()
5029            },
5030            autoclose_before: "}".to_string(),
5031            ..Default::default()
5032        },
5033        Some(tree_sitter_rust::language()),
5034    ));
5035
5036    let text = r#"
5037        a
5038        b
5039        c
5040    "#
5041    .unindent();
5042
5043    let buffer = cx.new_model(|cx| {
5044        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
5045            .with_language(language, cx)
5046    });
5047    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
5048    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5049    editor
5050        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
5051        .await;
5052
5053    _ = editor.update(cx, |editor, cx| {
5054        editor.change_selections(None, cx, |s| {
5055            s.select_ranges([
5056                Point::new(0, 1)..Point::new(0, 1),
5057                Point::new(1, 1)..Point::new(1, 1),
5058                Point::new(2, 1)..Point::new(2, 1),
5059            ])
5060        });
5061
5062        editor.handle_input("{", cx);
5063        editor.handle_input("{", cx);
5064        editor.handle_input("_", cx);
5065        assert_eq!(
5066            editor.text(cx),
5067            "
5068                a{{_}}
5069                b{{_}}
5070                c{{_}}
5071            "
5072            .unindent()
5073        );
5074        assert_eq!(
5075            editor.selections.ranges::<Point>(cx),
5076            [
5077                Point::new(0, 4)..Point::new(0, 4),
5078                Point::new(1, 4)..Point::new(1, 4),
5079                Point::new(2, 4)..Point::new(2, 4)
5080            ]
5081        );
5082
5083        editor.backspace(&Default::default(), cx);
5084        editor.backspace(&Default::default(), cx);
5085        assert_eq!(
5086            editor.text(cx),
5087            "
5088                a{}
5089                b{}
5090                c{}
5091            "
5092            .unindent()
5093        );
5094        assert_eq!(
5095            editor.selections.ranges::<Point>(cx),
5096            [
5097                Point::new(0, 2)..Point::new(0, 2),
5098                Point::new(1, 2)..Point::new(1, 2),
5099                Point::new(2, 2)..Point::new(2, 2)
5100            ]
5101        );
5102
5103        editor.delete_to_previous_word_start(&Default::default(), cx);
5104        assert_eq!(
5105            editor.text(cx),
5106            "
5107                a
5108                b
5109                c
5110            "
5111            .unindent()
5112        );
5113        assert_eq!(
5114            editor.selections.ranges::<Point>(cx),
5115            [
5116                Point::new(0, 1)..Point::new(0, 1),
5117                Point::new(1, 1)..Point::new(1, 1),
5118                Point::new(2, 1)..Point::new(2, 1)
5119            ]
5120        );
5121    });
5122}
5123
5124#[gpui::test]
5125async fn test_snippets(cx: &mut gpui::TestAppContext) {
5126    init_test(cx, |_| {});
5127
5128    let (text, insertion_ranges) = marked_text_ranges(
5129        indoc! {"
5130            a.ˇ b
5131            a.ˇ b
5132            a.ˇ b
5133        "},
5134        false,
5135    );
5136
5137    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
5138    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5139
5140    _ = editor.update(cx, |editor, cx| {
5141        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
5142
5143        editor
5144            .insert_snippet(&insertion_ranges, snippet, cx)
5145            .unwrap();
5146
5147        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
5148            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
5149            assert_eq!(editor.text(cx), expected_text);
5150            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
5151        }
5152
5153        assert(
5154            editor,
5155            cx,
5156            indoc! {"
5157                a.f(«one», two, «three») b
5158                a.f(«one», two, «three») b
5159                a.f(«one», two, «three») b
5160            "},
5161        );
5162
5163        // Can't move earlier than the first tab stop
5164        assert!(!editor.move_to_prev_snippet_tabstop(cx));
5165        assert(
5166            editor,
5167            cx,
5168            indoc! {"
5169                a.f(«one», two, «three») b
5170                a.f(«one», two, «three») b
5171                a.f(«one», two, «three») b
5172            "},
5173        );
5174
5175        assert!(editor.move_to_next_snippet_tabstop(cx));
5176        assert(
5177            editor,
5178            cx,
5179            indoc! {"
5180                a.f(one, «two», three) b
5181                a.f(one, «two», three) b
5182                a.f(one, «two», three) b
5183            "},
5184        );
5185
5186        editor.move_to_prev_snippet_tabstop(cx);
5187        assert(
5188            editor,
5189            cx,
5190            indoc! {"
5191                a.f(«one», two, «three») b
5192                a.f(«one», two, «three») b
5193                a.f(«one», two, «three») b
5194            "},
5195        );
5196
5197        assert!(editor.move_to_next_snippet_tabstop(cx));
5198        assert(
5199            editor,
5200            cx,
5201            indoc! {"
5202                a.f(one, «two», three) b
5203                a.f(one, «two», three) b
5204                a.f(one, «two», three) b
5205            "},
5206        );
5207        assert!(editor.move_to_next_snippet_tabstop(cx));
5208        assert(
5209            editor,
5210            cx,
5211            indoc! {"
5212                a.f(one, two, three)ˇ b
5213                a.f(one, two, three)ˇ b
5214                a.f(one, two, three)ˇ b
5215            "},
5216        );
5217
5218        // As soon as the last tab stop is reached, snippet state is gone
5219        editor.move_to_prev_snippet_tabstop(cx);
5220        assert(
5221            editor,
5222            cx,
5223            indoc! {"
5224                a.f(one, two, three)ˇ b
5225                a.f(one, two, three)ˇ b
5226                a.f(one, two, three)ˇ b
5227            "},
5228        );
5229    });
5230}
5231
5232#[gpui::test]
5233async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
5234    init_test(cx, |_| {});
5235
5236    let fs = FakeFs::new(cx.executor());
5237    fs.insert_file("/file.rs", Default::default()).await;
5238
5239    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5240
5241    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
5242    language_registry.add(rust_lang());
5243    let mut fake_servers = language_registry.register_fake_lsp_adapter(
5244        "Rust",
5245        FakeLspAdapter {
5246            capabilities: lsp::ServerCapabilities {
5247                document_formatting_provider: Some(lsp::OneOf::Left(true)),
5248                ..Default::default()
5249            },
5250            ..Default::default()
5251        },
5252    );
5253
5254    let buffer = project
5255        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
5256        .await
5257        .unwrap();
5258
5259    cx.executor().start_waiting();
5260    let fake_server = fake_servers.next().await.unwrap();
5261
5262    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
5263    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5264    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5265    assert!(cx.read(|cx| editor.is_dirty(cx)));
5266
5267    let save = editor
5268        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5269        .unwrap();
5270    fake_server
5271        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5272            assert_eq!(
5273                params.text_document.uri,
5274                lsp::Url::from_file_path("/file.rs").unwrap()
5275            );
5276            assert_eq!(params.options.tab_size, 4);
5277            Ok(Some(vec![lsp::TextEdit::new(
5278                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
5279                ", ".to_string(),
5280            )]))
5281        })
5282        .next()
5283        .await;
5284    cx.executor().start_waiting();
5285    let _x = save.await;
5286
5287    assert_eq!(
5288        editor.update(cx, |editor, cx| editor.text(cx)),
5289        "one, two\nthree\n"
5290    );
5291    assert!(!cx.read(|cx| editor.is_dirty(cx)));
5292
5293    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5294    assert!(cx.read(|cx| editor.is_dirty(cx)));
5295
5296    // Ensure we can still save even if formatting hangs.
5297    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5298        assert_eq!(
5299            params.text_document.uri,
5300            lsp::Url::from_file_path("/file.rs").unwrap()
5301        );
5302        futures::future::pending::<()>().await;
5303        unreachable!()
5304    });
5305    let save = editor
5306        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5307        .unwrap();
5308    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
5309    cx.executor().start_waiting();
5310    save.await;
5311    assert_eq!(
5312        editor.update(cx, |editor, cx| editor.text(cx)),
5313        "one\ntwo\nthree\n"
5314    );
5315    assert!(!cx.read(|cx| editor.is_dirty(cx)));
5316
5317    // Set rust language override and assert overridden tabsize is sent to language server
5318    update_test_language_settings(cx, |settings| {
5319        settings.languages.insert(
5320            "Rust".into(),
5321            LanguageSettingsContent {
5322                tab_size: NonZeroU32::new(8),
5323                ..Default::default()
5324            },
5325        );
5326    });
5327
5328    let save = editor
5329        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5330        .unwrap();
5331    fake_server
5332        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5333            assert_eq!(
5334                params.text_document.uri,
5335                lsp::Url::from_file_path("/file.rs").unwrap()
5336            );
5337            assert_eq!(params.options.tab_size, 8);
5338            Ok(Some(vec![]))
5339        })
5340        .next()
5341        .await;
5342    cx.executor().start_waiting();
5343    save.await;
5344}
5345
5346#[gpui::test]
5347async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
5348    init_test(cx, |_| {});
5349
5350    let fs = FakeFs::new(cx.executor());
5351    fs.insert_file("/file.rs", Default::default()).await;
5352
5353    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5354
5355    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
5356    language_registry.add(rust_lang());
5357    let mut fake_servers = language_registry.register_fake_lsp_adapter(
5358        "Rust",
5359        FakeLspAdapter {
5360            capabilities: lsp::ServerCapabilities {
5361                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
5362                ..Default::default()
5363            },
5364            ..Default::default()
5365        },
5366    );
5367
5368    let buffer = project
5369        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
5370        .await
5371        .unwrap();
5372
5373    cx.executor().start_waiting();
5374    let fake_server = fake_servers.next().await.unwrap();
5375
5376    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
5377    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5378    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5379    assert!(cx.read(|cx| editor.is_dirty(cx)));
5380
5381    let save = editor
5382        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5383        .unwrap();
5384    fake_server
5385        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
5386            assert_eq!(
5387                params.text_document.uri,
5388                lsp::Url::from_file_path("/file.rs").unwrap()
5389            );
5390            assert_eq!(params.options.tab_size, 4);
5391            Ok(Some(vec![lsp::TextEdit::new(
5392                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
5393                ", ".to_string(),
5394            )]))
5395        })
5396        .next()
5397        .await;
5398    cx.executor().start_waiting();
5399    save.await;
5400    assert_eq!(
5401        editor.update(cx, |editor, cx| editor.text(cx)),
5402        "one, two\nthree\n"
5403    );
5404    assert!(!cx.read(|cx| editor.is_dirty(cx)));
5405
5406    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5407    assert!(cx.read(|cx| editor.is_dirty(cx)));
5408
5409    // Ensure we can still save even if formatting hangs.
5410    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
5411        move |params, _| async move {
5412            assert_eq!(
5413                params.text_document.uri,
5414                lsp::Url::from_file_path("/file.rs").unwrap()
5415            );
5416            futures::future::pending::<()>().await;
5417            unreachable!()
5418        },
5419    );
5420    let save = editor
5421        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5422        .unwrap();
5423    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
5424    cx.executor().start_waiting();
5425    save.await;
5426    assert_eq!(
5427        editor.update(cx, |editor, cx| editor.text(cx)),
5428        "one\ntwo\nthree\n"
5429    );
5430    assert!(!cx.read(|cx| editor.is_dirty(cx)));
5431
5432    // Set rust language override and assert overridden tabsize is sent to language server
5433    update_test_language_settings(cx, |settings| {
5434        settings.languages.insert(
5435            "Rust".into(),
5436            LanguageSettingsContent {
5437                tab_size: NonZeroU32::new(8),
5438                ..Default::default()
5439            },
5440        );
5441    });
5442
5443    let save = editor
5444        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5445        .unwrap();
5446    fake_server
5447        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
5448            assert_eq!(
5449                params.text_document.uri,
5450                lsp::Url::from_file_path("/file.rs").unwrap()
5451            );
5452            assert_eq!(params.options.tab_size, 8);
5453            Ok(Some(vec![]))
5454        })
5455        .next()
5456        .await;
5457    cx.executor().start_waiting();
5458    save.await;
5459}
5460
5461#[gpui::test]
5462async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
5463    init_test(cx, |settings| {
5464        settings.defaults.formatter = Some(language_settings::Formatter::LanguageServer)
5465    });
5466
5467    let fs = FakeFs::new(cx.executor());
5468    fs.insert_file("/file.rs", Default::default()).await;
5469
5470    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5471
5472    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
5473    language_registry.add(Arc::new(Language::new(
5474        LanguageConfig {
5475            name: "Rust".into(),
5476            matcher: LanguageMatcher {
5477                path_suffixes: vec!["rs".to_string()],
5478                ..Default::default()
5479            },
5480            // Enable Prettier formatting for the same buffer, and ensure
5481            // LSP is called instead of Prettier.
5482            prettier_parser_name: Some("test_parser".to_string()),
5483            ..Default::default()
5484        },
5485        Some(tree_sitter_rust::language()),
5486    )));
5487    let mut fake_servers = language_registry.register_fake_lsp_adapter(
5488        "Rust",
5489        FakeLspAdapter {
5490            capabilities: lsp::ServerCapabilities {
5491                document_formatting_provider: Some(lsp::OneOf::Left(true)),
5492                ..Default::default()
5493            },
5494            ..Default::default()
5495        },
5496    );
5497
5498    let buffer = project
5499        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
5500        .await
5501        .unwrap();
5502
5503    cx.executor().start_waiting();
5504    let fake_server = fake_servers.next().await.unwrap();
5505
5506    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
5507    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5508    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5509
5510    let format = editor
5511        .update(cx, |editor, cx| {
5512            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
5513        })
5514        .unwrap();
5515    fake_server
5516        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5517            assert_eq!(
5518                params.text_document.uri,
5519                lsp::Url::from_file_path("/file.rs").unwrap()
5520            );
5521            assert_eq!(params.options.tab_size, 4);
5522            Ok(Some(vec![lsp::TextEdit::new(
5523                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
5524                ", ".to_string(),
5525            )]))
5526        })
5527        .next()
5528        .await;
5529    cx.executor().start_waiting();
5530    format.await;
5531    assert_eq!(
5532        editor.update(cx, |editor, cx| editor.text(cx)),
5533        "one, two\nthree\n"
5534    );
5535
5536    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5537    // Ensure we don't lock if formatting hangs.
5538    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5539        assert_eq!(
5540            params.text_document.uri,
5541            lsp::Url::from_file_path("/file.rs").unwrap()
5542        );
5543        futures::future::pending::<()>().await;
5544        unreachable!()
5545    });
5546    let format = editor
5547        .update(cx, |editor, cx| {
5548            editor.perform_format(project, FormatTrigger::Manual, cx)
5549        })
5550        .unwrap();
5551    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
5552    cx.executor().start_waiting();
5553    format.await;
5554    assert_eq!(
5555        editor.update(cx, |editor, cx| editor.text(cx)),
5556        "one\ntwo\nthree\n"
5557    );
5558}
5559
5560#[gpui::test]
5561async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
5562    init_test(cx, |_| {});
5563
5564    let mut cx = EditorLspTestContext::new_rust(
5565        lsp::ServerCapabilities {
5566            document_formatting_provider: Some(lsp::OneOf::Left(true)),
5567            ..Default::default()
5568        },
5569        cx,
5570    )
5571    .await;
5572
5573    cx.set_state(indoc! {"
5574        one.twoˇ
5575    "});
5576
5577    // The format request takes a long time. When it completes, it inserts
5578    // a newline and an indent before the `.`
5579    cx.lsp
5580        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
5581            let executor = cx.background_executor().clone();
5582            async move {
5583                executor.timer(Duration::from_millis(100)).await;
5584                Ok(Some(vec![lsp::TextEdit {
5585                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
5586                    new_text: "\n    ".into(),
5587                }]))
5588            }
5589        });
5590
5591    // Submit a format request.
5592    let format_1 = cx
5593        .update_editor(|editor, cx| editor.format(&Format, cx))
5594        .unwrap();
5595    cx.executor().run_until_parked();
5596
5597    // Submit a second format request.
5598    let format_2 = cx
5599        .update_editor(|editor, cx| editor.format(&Format, cx))
5600        .unwrap();
5601    cx.executor().run_until_parked();
5602
5603    // Wait for both format requests to complete
5604    cx.executor().advance_clock(Duration::from_millis(200));
5605    cx.executor().start_waiting();
5606    format_1.await.unwrap();
5607    cx.executor().start_waiting();
5608    format_2.await.unwrap();
5609
5610    // The formatting edits only happens once.
5611    cx.assert_editor_state(indoc! {"
5612        one
5613            .twoˇ
5614    "});
5615}
5616
5617#[gpui::test]
5618async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
5619    init_test(cx, |settings| {
5620        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
5621    });
5622
5623    let mut cx = EditorLspTestContext::new_rust(
5624        lsp::ServerCapabilities {
5625            document_formatting_provider: Some(lsp::OneOf::Left(true)),
5626            ..Default::default()
5627        },
5628        cx,
5629    )
5630    .await;
5631
5632    // Set up a buffer white some trailing whitespace and no trailing newline.
5633    cx.set_state(
5634        &[
5635            "one ",   //
5636            "twoˇ",   //
5637            "three ", //
5638            "four",   //
5639        ]
5640        .join("\n"),
5641    );
5642
5643    // Submit a format request.
5644    let format = cx
5645        .update_editor(|editor, cx| editor.format(&Format, cx))
5646        .unwrap();
5647
5648    // Record which buffer changes have been sent to the language server
5649    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
5650    cx.lsp
5651        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
5652            let buffer_changes = buffer_changes.clone();
5653            move |params, _| {
5654                buffer_changes.lock().extend(
5655                    params
5656                        .content_changes
5657                        .into_iter()
5658                        .map(|e| (e.range.unwrap(), e.text)),
5659                );
5660            }
5661        });
5662
5663    // Handle formatting requests to the language server.
5664    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
5665        let buffer_changes = buffer_changes.clone();
5666        move |_, _| {
5667            // When formatting is requested, trailing whitespace has already been stripped,
5668            // and the trailing newline has already been added.
5669            assert_eq!(
5670                &buffer_changes.lock()[1..],
5671                &[
5672                    (
5673                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
5674                        "".into()
5675                    ),
5676                    (
5677                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
5678                        "".into()
5679                    ),
5680                    (
5681                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
5682                        "\n".into()
5683                    ),
5684                ]
5685            );
5686
5687            // Insert blank lines between each line of the buffer.
5688            async move {
5689                Ok(Some(vec![
5690                    lsp::TextEdit {
5691                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
5692                        new_text: "\n".into(),
5693                    },
5694                    lsp::TextEdit {
5695                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
5696                        new_text: "\n".into(),
5697                    },
5698                ]))
5699            }
5700        }
5701    });
5702
5703    // After formatting the buffer, the trailing whitespace is stripped,
5704    // a newline is appended, and the edits provided by the language server
5705    // have been applied.
5706    format.await.unwrap();
5707    cx.assert_editor_state(
5708        &[
5709            "one",   //
5710            "",      //
5711            "twoˇ",  //
5712            "",      //
5713            "three", //
5714            "four",  //
5715            "",      //
5716        ]
5717        .join("\n"),
5718    );
5719
5720    // Undoing the formatting undoes the trailing whitespace removal, the
5721    // trailing newline, and the LSP edits.
5722    cx.update_buffer(|buffer, cx| buffer.undo(cx));
5723    cx.assert_editor_state(
5724        &[
5725            "one ",   //
5726            "twoˇ",   //
5727            "three ", //
5728            "four",   //
5729        ]
5730        .join("\n"),
5731    );
5732}
5733
5734#[gpui::test]
5735async fn test_completion(cx: &mut gpui::TestAppContext) {
5736    init_test(cx, |_| {});
5737
5738    let mut cx = EditorLspTestContext::new_rust(
5739        lsp::ServerCapabilities {
5740            completion_provider: Some(lsp::CompletionOptions {
5741                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
5742                resolve_provider: Some(true),
5743                ..Default::default()
5744            }),
5745            ..Default::default()
5746        },
5747        cx,
5748    )
5749    .await;
5750
5751    cx.set_state(indoc! {"
5752        oneˇ
5753        two
5754        three
5755    "});
5756    cx.simulate_keystroke(".");
5757    handle_completion_request(
5758        &mut cx,
5759        indoc! {"
5760            one.|<>
5761            two
5762            three
5763        "},
5764        vec!["first_completion", "second_completion"],
5765    )
5766    .await;
5767    cx.condition(|editor, _| editor.context_menu_visible())
5768        .await;
5769    let apply_additional_edits = cx.update_editor(|editor, cx| {
5770        editor.context_menu_next(&Default::default(), cx);
5771        editor
5772            .confirm_completion(&ConfirmCompletion::default(), cx)
5773            .unwrap()
5774    });
5775    cx.assert_editor_state(indoc! {"
5776        one.second_completionˇ
5777        two
5778        three
5779    "});
5780
5781    handle_resolve_completion_request(
5782        &mut cx,
5783        Some(vec![
5784            (
5785                //This overlaps with the primary completion edit which is
5786                //misbehavior from the LSP spec, test that we filter it out
5787                indoc! {"
5788                    one.second_ˇcompletion
5789                    two
5790                    threeˇ
5791                "},
5792                "overlapping additional edit",
5793            ),
5794            (
5795                indoc! {"
5796                    one.second_completion
5797                    two
5798                    threeˇ
5799                "},
5800                "\nadditional edit",
5801            ),
5802        ]),
5803    )
5804    .await;
5805    apply_additional_edits.await.unwrap();
5806    cx.assert_editor_state(indoc! {"
5807        one.second_completionˇ
5808        two
5809        three
5810        additional edit
5811    "});
5812
5813    cx.set_state(indoc! {"
5814        one.second_completion
5815        twoˇ
5816        threeˇ
5817        additional edit
5818    "});
5819    cx.simulate_keystroke(" ");
5820    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
5821    cx.simulate_keystroke("s");
5822    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
5823
5824    cx.assert_editor_state(indoc! {"
5825        one.second_completion
5826        two sˇ
5827        three sˇ
5828        additional edit
5829    "});
5830    handle_completion_request(
5831        &mut cx,
5832        indoc! {"
5833            one.second_completion
5834            two s
5835            three <s|>
5836            additional edit
5837        "},
5838        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
5839    )
5840    .await;
5841    cx.condition(|editor, _| editor.context_menu_visible())
5842        .await;
5843
5844    cx.simulate_keystroke("i");
5845
5846    handle_completion_request(
5847        &mut cx,
5848        indoc! {"
5849            one.second_completion
5850            two si
5851            three <si|>
5852            additional edit
5853        "},
5854        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
5855    )
5856    .await;
5857    cx.condition(|editor, _| editor.context_menu_visible())
5858        .await;
5859
5860    let apply_additional_edits = cx.update_editor(|editor, cx| {
5861        editor
5862            .confirm_completion(&ConfirmCompletion::default(), cx)
5863            .unwrap()
5864    });
5865    cx.assert_editor_state(indoc! {"
5866        one.second_completion
5867        two sixth_completionˇ
5868        three sixth_completionˇ
5869        additional edit
5870    "});
5871
5872    handle_resolve_completion_request(&mut cx, None).await;
5873    apply_additional_edits.await.unwrap();
5874
5875    _ = cx.update(|cx| {
5876        cx.update_global::<SettingsStore, _>(|settings, cx| {
5877            settings.update_user_settings::<EditorSettings>(cx, |settings| {
5878                settings.show_completions_on_input = Some(false);
5879            });
5880        })
5881    });
5882    cx.set_state("editorˇ");
5883    cx.simulate_keystroke(".");
5884    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
5885    cx.simulate_keystroke("c");
5886    cx.simulate_keystroke("l");
5887    cx.simulate_keystroke("o");
5888    cx.assert_editor_state("editor.cloˇ");
5889    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
5890    cx.update_editor(|editor, cx| {
5891        editor.show_completions(&ShowCompletions, cx);
5892    });
5893    handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
5894    cx.condition(|editor, _| editor.context_menu_visible())
5895        .await;
5896    let apply_additional_edits = cx.update_editor(|editor, cx| {
5897        editor
5898            .confirm_completion(&ConfirmCompletion::default(), cx)
5899            .unwrap()
5900    });
5901    cx.assert_editor_state("editor.closeˇ");
5902    handle_resolve_completion_request(&mut cx, None).await;
5903    apply_additional_edits.await.unwrap();
5904}
5905
5906#[gpui::test]
5907async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
5908    init_test(cx, |_| {});
5909    let mut cx = EditorTestContext::new(cx).await;
5910    let language = Arc::new(Language::new(
5911        LanguageConfig {
5912            line_comments: vec!["// ".into()],
5913            ..Default::default()
5914        },
5915        Some(tree_sitter_rust::language()),
5916    ));
5917    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
5918
5919    // If multiple selections intersect a line, the line is only toggled once.
5920    cx.set_state(indoc! {"
5921        fn a() {
5922            «//b();
5923            ˇ»// «c();
5924            //ˇ»  d();
5925        }
5926    "});
5927
5928    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5929
5930    cx.assert_editor_state(indoc! {"
5931        fn a() {
5932            «b();
5933            c();
5934            ˇ» d();
5935        }
5936    "});
5937
5938    // The comment prefix is inserted at the same column for every line in a
5939    // selection.
5940    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5941
5942    cx.assert_editor_state(indoc! {"
5943        fn a() {
5944            // «b();
5945            // c();
5946            ˇ»//  d();
5947        }
5948    "});
5949
5950    // If a selection ends at the beginning of a line, that line is not toggled.
5951    cx.set_selections_state(indoc! {"
5952        fn a() {
5953            // b();
5954            «// c();
5955        ˇ»    //  d();
5956        }
5957    "});
5958
5959    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5960
5961    cx.assert_editor_state(indoc! {"
5962        fn a() {
5963            // b();
5964            «c();
5965        ˇ»    //  d();
5966        }
5967    "});
5968
5969    // If a selection span a single line and is empty, the line is toggled.
5970    cx.set_state(indoc! {"
5971        fn a() {
5972            a();
5973            b();
5974        ˇ
5975        }
5976    "});
5977
5978    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5979
5980    cx.assert_editor_state(indoc! {"
5981        fn a() {
5982            a();
5983            b();
5984        //•ˇ
5985        }
5986    "});
5987
5988    // If a selection span multiple lines, empty lines are not toggled.
5989    cx.set_state(indoc! {"
5990        fn a() {
5991            «a();
5992
5993            c();ˇ»
5994        }
5995    "});
5996
5997    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5998
5999    cx.assert_editor_state(indoc! {"
6000        fn a() {
6001            // «a();
6002
6003            // c();ˇ»
6004        }
6005    "});
6006}
6007
6008#[gpui::test]
6009async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
6010    init_test(cx, |_| {});
6011
6012    let language = Arc::new(Language::new(
6013        LanguageConfig {
6014            line_comments: vec!["// ".into()],
6015            ..Default::default()
6016        },
6017        Some(tree_sitter_rust::language()),
6018    ));
6019
6020    let registry = Arc::new(LanguageRegistry::test());
6021    registry.add(language.clone());
6022
6023    let mut cx = EditorTestContext::new(cx).await;
6024    cx.update_buffer(|buffer, cx| {
6025        buffer.set_language_registry(registry);
6026        buffer.set_language(Some(language), cx);
6027    });
6028
6029    let toggle_comments = &ToggleComments {
6030        advance_downwards: true,
6031    };
6032
6033    // Single cursor on one line -> advance
6034    // Cursor moves horizontally 3 characters as well on non-blank line
6035    cx.set_state(indoc!(
6036        "fn a() {
6037             ˇdog();
6038             cat();
6039        }"
6040    ));
6041    cx.update_editor(|editor, cx| {
6042        editor.toggle_comments(toggle_comments, cx);
6043    });
6044    cx.assert_editor_state(indoc!(
6045        "fn a() {
6046             // dog();
6047             catˇ();
6048        }"
6049    ));
6050
6051    // Single selection on one line -> don't advance
6052    cx.set_state(indoc!(
6053        "fn a() {
6054             «dog()ˇ»;
6055             cat();
6056        }"
6057    ));
6058    cx.update_editor(|editor, cx| {
6059        editor.toggle_comments(toggle_comments, cx);
6060    });
6061    cx.assert_editor_state(indoc!(
6062        "fn a() {
6063             // «dog()ˇ»;
6064             cat();
6065        }"
6066    ));
6067
6068    // Multiple cursors on one line -> advance
6069    cx.set_state(indoc!(
6070        "fn a() {
6071             ˇdˇog();
6072             cat();
6073        }"
6074    ));
6075    cx.update_editor(|editor, cx| {
6076        editor.toggle_comments(toggle_comments, cx);
6077    });
6078    cx.assert_editor_state(indoc!(
6079        "fn a() {
6080             // dog();
6081             catˇ(ˇ);
6082        }"
6083    ));
6084
6085    // Multiple cursors on one line, with selection -> don't advance
6086    cx.set_state(indoc!(
6087        "fn a() {
6088             ˇdˇog«()ˇ»;
6089             cat();
6090        }"
6091    ));
6092    cx.update_editor(|editor, cx| {
6093        editor.toggle_comments(toggle_comments, cx);
6094    });
6095    cx.assert_editor_state(indoc!(
6096        "fn a() {
6097             // ˇdˇog«()ˇ»;
6098             cat();
6099        }"
6100    ));
6101
6102    // Single cursor on one line -> advance
6103    // Cursor moves to column 0 on blank line
6104    cx.set_state(indoc!(
6105        "fn a() {
6106             ˇdog();
6107
6108             cat();
6109        }"
6110    ));
6111    cx.update_editor(|editor, cx| {
6112        editor.toggle_comments(toggle_comments, cx);
6113    });
6114    cx.assert_editor_state(indoc!(
6115        "fn a() {
6116             // dog();
6117        ˇ
6118             cat();
6119        }"
6120    ));
6121
6122    // Single cursor on one line -> advance
6123    // Cursor starts and ends at column 0
6124    cx.set_state(indoc!(
6125        "fn a() {
6126         ˇ    dog();
6127             cat();
6128        }"
6129    ));
6130    cx.update_editor(|editor, cx| {
6131        editor.toggle_comments(toggle_comments, cx);
6132    });
6133    cx.assert_editor_state(indoc!(
6134        "fn a() {
6135             // dog();
6136         ˇ    cat();
6137        }"
6138    ));
6139}
6140
6141#[gpui::test]
6142async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
6143    init_test(cx, |_| {});
6144
6145    let mut cx = EditorTestContext::new(cx).await;
6146
6147    let html_language = Arc::new(
6148        Language::new(
6149            LanguageConfig {
6150                name: "HTML".into(),
6151                block_comment: Some(("<!-- ".into(), " -->".into())),
6152                ..Default::default()
6153            },
6154            Some(tree_sitter_html::language()),
6155        )
6156        .with_injection_query(
6157            r#"
6158            (script_element
6159                (raw_text) @content
6160                (#set! "language" "javascript"))
6161            "#,
6162        )
6163        .unwrap(),
6164    );
6165
6166    let javascript_language = Arc::new(Language::new(
6167        LanguageConfig {
6168            name: "JavaScript".into(),
6169            line_comments: vec!["// ".into()],
6170            ..Default::default()
6171        },
6172        Some(tree_sitter_typescript::language_tsx()),
6173    ));
6174
6175    let registry = Arc::new(LanguageRegistry::test());
6176    registry.add(html_language.clone());
6177    registry.add(javascript_language.clone());
6178
6179    cx.update_buffer(|buffer, cx| {
6180        buffer.set_language_registry(registry);
6181        buffer.set_language(Some(html_language), cx);
6182    });
6183
6184    // Toggle comments for empty selections
6185    cx.set_state(
6186        &r#"
6187            <p>A</p>ˇ
6188            <p>B</p>ˇ
6189            <p>C</p>ˇ
6190        "#
6191        .unindent(),
6192    );
6193    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6194    cx.assert_editor_state(
6195        &r#"
6196            <!-- <p>A</p>ˇ -->
6197            <!-- <p>B</p>ˇ -->
6198            <!-- <p>C</p>ˇ -->
6199        "#
6200        .unindent(),
6201    );
6202    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6203    cx.assert_editor_state(
6204        &r#"
6205            <p>A</p>ˇ
6206            <p>B</p>ˇ
6207            <p>C</p>ˇ
6208        "#
6209        .unindent(),
6210    );
6211
6212    // Toggle comments for mixture of empty and non-empty selections, where
6213    // multiple selections occupy a given line.
6214    cx.set_state(
6215        &r#"
6216            <p>A«</p>
6217            <p>ˇ»B</p>ˇ
6218            <p>C«</p>
6219            <p>ˇ»D</p>ˇ
6220        "#
6221        .unindent(),
6222    );
6223
6224    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6225    cx.assert_editor_state(
6226        &r#"
6227            <!-- <p>A«</p>
6228            <p>ˇ»B</p>ˇ -->
6229            <!-- <p>C«</p>
6230            <p>ˇ»D</p>ˇ -->
6231        "#
6232        .unindent(),
6233    );
6234    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6235    cx.assert_editor_state(
6236        &r#"
6237            <p>A«</p>
6238            <p>ˇ»B</p>ˇ
6239            <p>C«</p>
6240            <p>ˇ»D</p>ˇ
6241        "#
6242        .unindent(),
6243    );
6244
6245    // Toggle comments when different languages are active for different
6246    // selections.
6247    cx.set_state(
6248        &r#"
6249            ˇ<script>
6250                ˇvar x = new Y();
6251            ˇ</script>
6252        "#
6253        .unindent(),
6254    );
6255    cx.executor().run_until_parked();
6256    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6257    cx.assert_editor_state(
6258        &r#"
6259            <!-- ˇ<script> -->
6260                // ˇvar x = new Y();
6261            <!-- ˇ</script> -->
6262        "#
6263        .unindent(),
6264    );
6265}
6266
6267#[gpui::test]
6268fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
6269    init_test(cx, |_| {});
6270
6271    let buffer = cx.new_model(|cx| {
6272        Buffer::new(
6273            0,
6274            BufferId::new(cx.entity_id().as_u64()).unwrap(),
6275            sample_text(3, 4, 'a'),
6276        )
6277    });
6278    let multibuffer = cx.new_model(|cx| {
6279        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
6280        multibuffer.push_excerpts(
6281            buffer.clone(),
6282            [
6283                ExcerptRange {
6284                    context: Point::new(0, 0)..Point::new(0, 4),
6285                    primary: None,
6286                },
6287                ExcerptRange {
6288                    context: Point::new(1, 0)..Point::new(1, 4),
6289                    primary: None,
6290                },
6291            ],
6292            cx,
6293        );
6294        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
6295        multibuffer
6296    });
6297
6298    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
6299    _ = view.update(cx, |view, cx| {
6300        assert_eq!(view.text(cx), "aaaa\nbbbb");
6301        view.change_selections(None, cx, |s| {
6302            s.select_ranges([
6303                Point::new(0, 0)..Point::new(0, 0),
6304                Point::new(1, 0)..Point::new(1, 0),
6305            ])
6306        });
6307
6308        view.handle_input("X", cx);
6309        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
6310        assert_eq!(
6311            view.selections.ranges(cx),
6312            [
6313                Point::new(0, 1)..Point::new(0, 1),
6314                Point::new(1, 1)..Point::new(1, 1),
6315            ]
6316        );
6317
6318        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
6319        view.change_selections(None, cx, |s| {
6320            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
6321        });
6322        view.backspace(&Default::default(), cx);
6323        assert_eq!(view.text(cx), "Xa\nbbb");
6324        assert_eq!(
6325            view.selections.ranges(cx),
6326            [Point::new(1, 0)..Point::new(1, 0)]
6327        );
6328
6329        view.change_selections(None, cx, |s| {
6330            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
6331        });
6332        view.backspace(&Default::default(), cx);
6333        assert_eq!(view.text(cx), "X\nbb");
6334        assert_eq!(
6335            view.selections.ranges(cx),
6336            [Point::new(0, 1)..Point::new(0, 1)]
6337        );
6338    });
6339}
6340
6341#[gpui::test]
6342fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
6343    init_test(cx, |_| {});
6344
6345    let markers = vec![('[', ']').into(), ('(', ')').into()];
6346    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
6347        indoc! {"
6348            [aaaa
6349            (bbbb]
6350            cccc)",
6351        },
6352        markers.clone(),
6353    );
6354    let excerpt_ranges = markers.into_iter().map(|marker| {
6355        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
6356        ExcerptRange {
6357            context,
6358            primary: None,
6359        }
6360    });
6361    let buffer = cx.new_model(|cx| {
6362        Buffer::new(
6363            0,
6364            BufferId::new(cx.entity_id().as_u64()).unwrap(),
6365            initial_text,
6366        )
6367    });
6368    let multibuffer = cx.new_model(|cx| {
6369        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
6370        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
6371        multibuffer
6372    });
6373
6374    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
6375    _ = view.update(cx, |view, cx| {
6376        let (expected_text, selection_ranges) = marked_text_ranges(
6377            indoc! {"
6378                aaaa
6379                bˇbbb
6380                bˇbbˇb
6381                cccc"
6382            },
6383            true,
6384        );
6385        assert_eq!(view.text(cx), expected_text);
6386        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
6387
6388        view.handle_input("X", cx);
6389
6390        let (expected_text, expected_selections) = marked_text_ranges(
6391            indoc! {"
6392                aaaa
6393                bXˇbbXb
6394                bXˇbbXˇb
6395                cccc"
6396            },
6397            false,
6398        );
6399        assert_eq!(view.text(cx), expected_text);
6400        assert_eq!(view.selections.ranges(cx), expected_selections);
6401
6402        view.newline(&Newline, cx);
6403        let (expected_text, expected_selections) = marked_text_ranges(
6404            indoc! {"
6405                aaaa
6406                bX
6407                ˇbbX
6408                b
6409                bX
6410                ˇbbX
6411                ˇb
6412                cccc"
6413            },
6414            false,
6415        );
6416        assert_eq!(view.text(cx), expected_text);
6417        assert_eq!(view.selections.ranges(cx), expected_selections);
6418    });
6419}
6420
6421#[gpui::test]
6422fn test_refresh_selections(cx: &mut TestAppContext) {
6423    init_test(cx, |_| {});
6424
6425    let buffer = cx.new_model(|cx| {
6426        Buffer::new(
6427            0,
6428            BufferId::new(cx.entity_id().as_u64()).unwrap(),
6429            sample_text(3, 4, 'a'),
6430        )
6431    });
6432    let mut excerpt1_id = None;
6433    let multibuffer = cx.new_model(|cx| {
6434        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
6435        excerpt1_id = multibuffer
6436            .push_excerpts(
6437                buffer.clone(),
6438                [
6439                    ExcerptRange {
6440                        context: Point::new(0, 0)..Point::new(1, 4),
6441                        primary: None,
6442                    },
6443                    ExcerptRange {
6444                        context: Point::new(1, 0)..Point::new(2, 4),
6445                        primary: None,
6446                    },
6447                ],
6448                cx,
6449            )
6450            .into_iter()
6451            .next();
6452        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
6453        multibuffer
6454    });
6455
6456    let editor = cx.add_window(|cx| {
6457        let mut editor = build_editor(multibuffer.clone(), cx);
6458        let snapshot = editor.snapshot(cx);
6459        editor.change_selections(None, cx, |s| {
6460            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
6461        });
6462        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
6463        assert_eq!(
6464            editor.selections.ranges(cx),
6465            [
6466                Point::new(1, 3)..Point::new(1, 3),
6467                Point::new(2, 1)..Point::new(2, 1),
6468            ]
6469        );
6470        editor
6471    });
6472
6473    // Refreshing selections is a no-op when excerpts haven't changed.
6474    _ = editor.update(cx, |editor, cx| {
6475        editor.change_selections(None, cx, |s| s.refresh());
6476        assert_eq!(
6477            editor.selections.ranges(cx),
6478            [
6479                Point::new(1, 3)..Point::new(1, 3),
6480                Point::new(2, 1)..Point::new(2, 1),
6481            ]
6482        );
6483    });
6484
6485    _ = multibuffer.update(cx, |multibuffer, cx| {
6486        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
6487    });
6488    _ = editor.update(cx, |editor, cx| {
6489        // Removing an excerpt causes the first selection to become degenerate.
6490        assert_eq!(
6491            editor.selections.ranges(cx),
6492            [
6493                Point::new(0, 0)..Point::new(0, 0),
6494                Point::new(0, 1)..Point::new(0, 1)
6495            ]
6496        );
6497
6498        // Refreshing selections will relocate the first selection to the original buffer
6499        // location.
6500        editor.change_selections(None, cx, |s| s.refresh());
6501        assert_eq!(
6502            editor.selections.ranges(cx),
6503            [
6504                Point::new(0, 1)..Point::new(0, 1),
6505                Point::new(0, 3)..Point::new(0, 3)
6506            ]
6507        );
6508        assert!(editor.selections.pending_anchor().is_some());
6509    });
6510}
6511
6512#[gpui::test]
6513fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
6514    init_test(cx, |_| {});
6515
6516    let buffer = cx.new_model(|cx| {
6517        Buffer::new(
6518            0,
6519            BufferId::new(cx.entity_id().as_u64()).unwrap(),
6520            sample_text(3, 4, 'a'),
6521        )
6522    });
6523    let mut excerpt1_id = None;
6524    let multibuffer = cx.new_model(|cx| {
6525        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
6526        excerpt1_id = multibuffer
6527            .push_excerpts(
6528                buffer.clone(),
6529                [
6530                    ExcerptRange {
6531                        context: Point::new(0, 0)..Point::new(1, 4),
6532                        primary: None,
6533                    },
6534                    ExcerptRange {
6535                        context: Point::new(1, 0)..Point::new(2, 4),
6536                        primary: None,
6537                    },
6538                ],
6539                cx,
6540            )
6541            .into_iter()
6542            .next();
6543        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
6544        multibuffer
6545    });
6546
6547    let editor = cx.add_window(|cx| {
6548        let mut editor = build_editor(multibuffer.clone(), cx);
6549        let snapshot = editor.snapshot(cx);
6550        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
6551        assert_eq!(
6552            editor.selections.ranges(cx),
6553            [Point::new(1, 3)..Point::new(1, 3)]
6554        );
6555        editor
6556    });
6557
6558    _ = multibuffer.update(cx, |multibuffer, cx| {
6559        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
6560    });
6561    _ = editor.update(cx, |editor, cx| {
6562        assert_eq!(
6563            editor.selections.ranges(cx),
6564            [Point::new(0, 0)..Point::new(0, 0)]
6565        );
6566
6567        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
6568        editor.change_selections(None, cx, |s| s.refresh());
6569        assert_eq!(
6570            editor.selections.ranges(cx),
6571            [Point::new(0, 3)..Point::new(0, 3)]
6572        );
6573        assert!(editor.selections.pending_anchor().is_some());
6574    });
6575}
6576
6577#[gpui::test]
6578async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
6579    init_test(cx, |_| {});
6580
6581    let language = Arc::new(
6582        Language::new(
6583            LanguageConfig {
6584                brackets: BracketPairConfig {
6585                    pairs: vec![
6586                        BracketPair {
6587                            start: "{".to_string(),
6588                            end: "}".to_string(),
6589                            close: true,
6590                            newline: true,
6591                        },
6592                        BracketPair {
6593                            start: "/* ".to_string(),
6594                            end: " */".to_string(),
6595                            close: true,
6596                            newline: true,
6597                        },
6598                    ],
6599                    ..Default::default()
6600                },
6601                ..Default::default()
6602            },
6603            Some(tree_sitter_rust::language()),
6604        )
6605        .with_indents_query("")
6606        .unwrap(),
6607    );
6608
6609    let text = concat!(
6610        "{   }\n",     //
6611        "  x\n",       //
6612        "  /*   */\n", //
6613        "x\n",         //
6614        "{{} }\n",     //
6615    );
6616
6617    let buffer = cx.new_model(|cx| {
6618        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
6619            .with_language(language, cx)
6620    });
6621    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
6622    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
6623    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
6624        .await;
6625
6626    _ = view.update(cx, |view, cx| {
6627        view.change_selections(None, cx, |s| {
6628            s.select_display_ranges([
6629                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
6630                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
6631                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
6632            ])
6633        });
6634        view.newline(&Newline, cx);
6635
6636        assert_eq!(
6637            view.buffer().read(cx).read(cx).text(),
6638            concat!(
6639                "{ \n",    // Suppress rustfmt
6640                "\n",      //
6641                "}\n",     //
6642                "  x\n",   //
6643                "  /* \n", //
6644                "  \n",    //
6645                "  */\n",  //
6646                "x\n",     //
6647                "{{} \n",  //
6648                "}\n",     //
6649            )
6650        );
6651    });
6652}
6653
6654#[gpui::test]
6655fn test_highlighted_ranges(cx: &mut TestAppContext) {
6656    init_test(cx, |_| {});
6657
6658    let editor = cx.add_window(|cx| {
6659        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
6660        build_editor(buffer.clone(), cx)
6661    });
6662
6663    _ = editor.update(cx, |editor, cx| {
6664        struct Type1;
6665        struct Type2;
6666
6667        let buffer = editor.buffer.read(cx).snapshot(cx);
6668
6669        let anchor_range =
6670            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
6671
6672        editor.highlight_background::<Type1>(
6673            vec![
6674                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
6675                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
6676                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
6677                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
6678            ],
6679            |_| Hsla::red(),
6680            cx,
6681        );
6682        editor.highlight_background::<Type2>(
6683            vec![
6684                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
6685                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
6686                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
6687                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
6688            ],
6689            |_| Hsla::green(),
6690            cx,
6691        );
6692
6693        let snapshot = editor.snapshot(cx);
6694        let mut highlighted_ranges = editor.background_highlights_in_range(
6695            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
6696            &snapshot,
6697            cx.theme().colors(),
6698        );
6699        // Enforce a consistent ordering based on color without relying on the ordering of the
6700        // highlight's `TypeId` which is non-executor.
6701        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
6702        assert_eq!(
6703            highlighted_ranges,
6704            &[
6705                (
6706                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
6707                    Hsla::red(),
6708                ),
6709                (
6710                    DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
6711                    Hsla::red(),
6712                ),
6713                (
6714                    DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
6715                    Hsla::green(),
6716                ),
6717                (
6718                    DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
6719                    Hsla::green(),
6720                ),
6721            ]
6722        );
6723        assert_eq!(
6724            editor.background_highlights_in_range(
6725                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
6726                &snapshot,
6727                cx.theme().colors(),
6728            ),
6729            &[(
6730                DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
6731                Hsla::red(),
6732            )]
6733        );
6734    });
6735}
6736
6737#[gpui::test]
6738async fn test_following(cx: &mut gpui::TestAppContext) {
6739    init_test(cx, |_| {});
6740
6741    let fs = FakeFs::new(cx.executor());
6742    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
6743
6744    let buffer = project.update(cx, |project, cx| {
6745        let buffer = project
6746            .create_buffer(&sample_text(16, 8, 'a'), None, cx)
6747            .unwrap();
6748        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
6749    });
6750    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
6751    let follower = cx.update(|cx| {
6752        cx.open_window(
6753            WindowOptions {
6754                bounds: WindowBounds::Fixed(Bounds::from_corners(
6755                    gpui::Point::new(0_f64.into(), 0_f64.into()),
6756                    gpui::Point::new(10_f64.into(), 80_f64.into()),
6757                )),
6758                ..Default::default()
6759            },
6760            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
6761        )
6762    });
6763
6764    let is_still_following = Rc::new(RefCell::new(true));
6765    let follower_edit_event_count = Rc::new(RefCell::new(0));
6766    let pending_update = Rc::new(RefCell::new(None));
6767    _ = follower.update(cx, {
6768        let update = pending_update.clone();
6769        let is_still_following = is_still_following.clone();
6770        let follower_edit_event_count = follower_edit_event_count.clone();
6771        |_, cx| {
6772            cx.subscribe(
6773                &leader.root_view(cx).unwrap(),
6774                move |_, leader, event, cx| {
6775                    leader
6776                        .read(cx)
6777                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
6778                },
6779            )
6780            .detach();
6781
6782            cx.subscribe(
6783                &follower.root_view(cx).unwrap(),
6784                move |_, _, event: &EditorEvent, _cx| {
6785                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
6786                        *is_still_following.borrow_mut() = false;
6787                    }
6788
6789                    if let EditorEvent::BufferEdited = event {
6790                        *follower_edit_event_count.borrow_mut() += 1;
6791                    }
6792                },
6793            )
6794            .detach();
6795        }
6796    });
6797
6798    // Update the selections only
6799    _ = leader.update(cx, |leader, cx| {
6800        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
6801    });
6802    follower
6803        .update(cx, |follower, cx| {
6804            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6805        })
6806        .unwrap()
6807        .await
6808        .unwrap();
6809    _ = follower.update(cx, |follower, cx| {
6810        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
6811    });
6812    assert_eq!(*is_still_following.borrow(), true);
6813    assert_eq!(*follower_edit_event_count.borrow(), 0);
6814
6815    // Update the scroll position only
6816    _ = leader.update(cx, |leader, cx| {
6817        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
6818    });
6819    follower
6820        .update(cx, |follower, cx| {
6821            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6822        })
6823        .unwrap()
6824        .await
6825        .unwrap();
6826    assert_eq!(
6827        follower
6828            .update(cx, |follower, cx| follower.scroll_position(cx))
6829            .unwrap(),
6830        gpui::Point::new(1.5, 3.5)
6831    );
6832    assert_eq!(*is_still_following.borrow(), true);
6833    assert_eq!(*follower_edit_event_count.borrow(), 0);
6834
6835    // Update the selections and scroll position. The follower's scroll position is updated
6836    // via autoscroll, not via the leader's exact scroll position.
6837    _ = leader.update(cx, |leader, cx| {
6838        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
6839        leader.request_autoscroll(Autoscroll::newest(), cx);
6840        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
6841    });
6842    follower
6843        .update(cx, |follower, cx| {
6844            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6845        })
6846        .unwrap()
6847        .await
6848        .unwrap();
6849    _ = follower.update(cx, |follower, cx| {
6850        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
6851        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
6852    });
6853    assert_eq!(*is_still_following.borrow(), true);
6854
6855    // Creating a pending selection that precedes another selection
6856    _ = leader.update(cx, |leader, cx| {
6857        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
6858        leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
6859    });
6860    follower
6861        .update(cx, |follower, cx| {
6862            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6863        })
6864        .unwrap()
6865        .await
6866        .unwrap();
6867    _ = follower.update(cx, |follower, cx| {
6868        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
6869    });
6870    assert_eq!(*is_still_following.borrow(), true);
6871
6872    // Extend the pending selection so that it surrounds another selection
6873    _ = leader.update(cx, |leader, cx| {
6874        leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
6875    });
6876    follower
6877        .update(cx, |follower, cx| {
6878            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6879        })
6880        .unwrap()
6881        .await
6882        .unwrap();
6883    _ = follower.update(cx, |follower, cx| {
6884        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
6885    });
6886
6887    // Scrolling locally breaks the follow
6888    _ = follower.update(cx, |follower, cx| {
6889        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
6890        follower.set_scroll_anchor(
6891            ScrollAnchor {
6892                anchor: top_anchor,
6893                offset: gpui::Point::new(0.0, 0.5),
6894            },
6895            cx,
6896        );
6897    });
6898    assert_eq!(*is_still_following.borrow(), false);
6899}
6900
6901#[gpui::test]
6902async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
6903    init_test(cx, |_| {});
6904
6905    let fs = FakeFs::new(cx.executor());
6906    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
6907    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
6908    let pane = workspace
6909        .update(cx, |workspace, _| workspace.active_pane().clone())
6910        .unwrap();
6911
6912    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
6913
6914    let leader = pane.update(cx, |_, cx| {
6915        let multibuffer = cx.new_model(|_| MultiBuffer::new(0, ReadWrite));
6916        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
6917    });
6918
6919    // Start following the editor when it has no excerpts.
6920    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
6921    let follower_1 = cx
6922        .update_window(*workspace.deref(), |_, cx| {
6923            Editor::from_state_proto(
6924                pane.clone(),
6925                workspace.root_view(cx).unwrap(),
6926                ViewId {
6927                    creator: Default::default(),
6928                    id: 0,
6929                },
6930                &mut state_message,
6931                cx,
6932            )
6933        })
6934        .unwrap()
6935        .unwrap()
6936        .await
6937        .unwrap();
6938
6939    let update_message = Rc::new(RefCell::new(None));
6940    follower_1.update(cx, {
6941        let update = update_message.clone();
6942        |_, cx| {
6943            cx.subscribe(&leader, move |_, leader, event, cx| {
6944                leader
6945                    .read(cx)
6946                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
6947            })
6948            .detach();
6949        }
6950    });
6951
6952    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
6953        (
6954            project
6955                .create_buffer("abc\ndef\nghi\njkl\n", None, cx)
6956                .unwrap(),
6957            project
6958                .create_buffer("mno\npqr\nstu\nvwx\n", None, cx)
6959                .unwrap(),
6960        )
6961    });
6962
6963    // Insert some excerpts.
6964    _ = leader.update(cx, |leader, cx| {
6965        leader.buffer.update(cx, |multibuffer, cx| {
6966            let excerpt_ids = multibuffer.push_excerpts(
6967                buffer_1.clone(),
6968                [
6969                    ExcerptRange {
6970                        context: 1..6,
6971                        primary: None,
6972                    },
6973                    ExcerptRange {
6974                        context: 12..15,
6975                        primary: None,
6976                    },
6977                    ExcerptRange {
6978                        context: 0..3,
6979                        primary: None,
6980                    },
6981                ],
6982                cx,
6983            );
6984            multibuffer.insert_excerpts_after(
6985                excerpt_ids[0],
6986                buffer_2.clone(),
6987                [
6988                    ExcerptRange {
6989                        context: 8..12,
6990                        primary: None,
6991                    },
6992                    ExcerptRange {
6993                        context: 0..6,
6994                        primary: None,
6995                    },
6996                ],
6997                cx,
6998            );
6999        });
7000    });
7001
7002    // Apply the update of adding the excerpts.
7003    follower_1
7004        .update(cx, |follower, cx| {
7005            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
7006        })
7007        .await
7008        .unwrap();
7009    assert_eq!(
7010        follower_1.update(cx, |editor, cx| editor.text(cx)),
7011        leader.update(cx, |editor, cx| editor.text(cx))
7012    );
7013    update_message.borrow_mut().take();
7014
7015    // Start following separately after it already has excerpts.
7016    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
7017    let follower_2 = cx
7018        .update_window(*workspace.deref(), |_, cx| {
7019            Editor::from_state_proto(
7020                pane.clone(),
7021                workspace.root_view(cx).unwrap().clone(),
7022                ViewId {
7023                    creator: Default::default(),
7024                    id: 0,
7025                },
7026                &mut state_message,
7027                cx,
7028            )
7029        })
7030        .unwrap()
7031        .unwrap()
7032        .await
7033        .unwrap();
7034    assert_eq!(
7035        follower_2.update(cx, |editor, cx| editor.text(cx)),
7036        leader.update(cx, |editor, cx| editor.text(cx))
7037    );
7038
7039    // Remove some excerpts.
7040    _ = leader.update(cx, |leader, cx| {
7041        leader.buffer.update(cx, |multibuffer, cx| {
7042            let excerpt_ids = multibuffer.excerpt_ids();
7043            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
7044            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
7045        });
7046    });
7047
7048    // Apply the update of removing the excerpts.
7049    follower_1
7050        .update(cx, |follower, cx| {
7051            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
7052        })
7053        .await
7054        .unwrap();
7055    follower_2
7056        .update(cx, |follower, cx| {
7057            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
7058        })
7059        .await
7060        .unwrap();
7061    update_message.borrow_mut().take();
7062    assert_eq!(
7063        follower_1.update(cx, |editor, cx| editor.text(cx)),
7064        leader.update(cx, |editor, cx| editor.text(cx))
7065    );
7066}
7067
7068#[gpui::test]
7069async fn go_to_prev_overlapping_diagnostic(
7070    executor: BackgroundExecutor,
7071    cx: &mut gpui::TestAppContext,
7072) {
7073    init_test(cx, |_| {});
7074
7075    let mut cx = EditorTestContext::new(cx).await;
7076    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
7077
7078    cx.set_state(indoc! {"
7079        ˇfn func(abc def: i32) -> u32 {
7080        }
7081    "});
7082
7083    _ = cx.update(|cx| {
7084        _ = project.update(cx, |project, cx| {
7085            project
7086                .update_diagnostics(
7087                    LanguageServerId(0),
7088                    lsp::PublishDiagnosticsParams {
7089                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
7090                        version: None,
7091                        diagnostics: vec![
7092                            lsp::Diagnostic {
7093                                range: lsp::Range::new(
7094                                    lsp::Position::new(0, 11),
7095                                    lsp::Position::new(0, 12),
7096                                ),
7097                                severity: Some(lsp::DiagnosticSeverity::ERROR),
7098                                ..Default::default()
7099                            },
7100                            lsp::Diagnostic {
7101                                range: lsp::Range::new(
7102                                    lsp::Position::new(0, 12),
7103                                    lsp::Position::new(0, 15),
7104                                ),
7105                                severity: Some(lsp::DiagnosticSeverity::ERROR),
7106                                ..Default::default()
7107                            },
7108                            lsp::Diagnostic {
7109                                range: lsp::Range::new(
7110                                    lsp::Position::new(0, 25),
7111                                    lsp::Position::new(0, 28),
7112                                ),
7113                                severity: Some(lsp::DiagnosticSeverity::ERROR),
7114                                ..Default::default()
7115                            },
7116                        ],
7117                    },
7118                    &[],
7119                    cx,
7120                )
7121                .unwrap()
7122        });
7123    });
7124
7125    executor.run_until_parked();
7126
7127    cx.update_editor(|editor, cx| {
7128        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
7129    });
7130
7131    cx.assert_editor_state(indoc! {"
7132        fn func(abc def: i32) -> ˇu32 {
7133        }
7134    "});
7135
7136    cx.update_editor(|editor, cx| {
7137        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
7138    });
7139
7140    cx.assert_editor_state(indoc! {"
7141        fn func(abc ˇdef: i32) -> u32 {
7142        }
7143    "});
7144
7145    cx.update_editor(|editor, cx| {
7146        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
7147    });
7148
7149    cx.assert_editor_state(indoc! {"
7150        fn func(abcˇ def: i32) -> u32 {
7151        }
7152    "});
7153
7154    cx.update_editor(|editor, cx| {
7155        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
7156    });
7157
7158    cx.assert_editor_state(indoc! {"
7159        fn func(abc def: i32) -> ˇu32 {
7160        }
7161    "});
7162}
7163
7164#[gpui::test]
7165async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
7166    init_test(cx, |_| {});
7167
7168    let mut cx = EditorTestContext::new(cx).await;
7169
7170    let diff_base = r#"
7171        use some::mod;
7172
7173        const A: u32 = 42;
7174
7175        fn main() {
7176            println!("hello");
7177
7178            println!("world");
7179        }
7180        "#
7181    .unindent();
7182
7183    // Edits are modified, removed, modified, added
7184    cx.set_state(
7185        &r#"
7186        use some::modified;
7187
7188        ˇ
7189        fn main() {
7190            println!("hello there");
7191
7192            println!("around the");
7193            println!("world");
7194        }
7195        "#
7196        .unindent(),
7197    );
7198
7199    cx.set_diff_base(Some(&diff_base));
7200    executor.run_until_parked();
7201
7202    cx.update_editor(|editor, cx| {
7203        //Wrap around the bottom of the buffer
7204        for _ in 0..3 {
7205            editor.go_to_hunk(&GoToHunk, cx);
7206        }
7207    });
7208
7209    cx.assert_editor_state(
7210        &r#"
7211        ˇuse some::modified;
7212
7213
7214        fn main() {
7215            println!("hello there");
7216
7217            println!("around the");
7218            println!("world");
7219        }
7220        "#
7221        .unindent(),
7222    );
7223
7224    cx.update_editor(|editor, cx| {
7225        //Wrap around the top of the buffer
7226        for _ in 0..2 {
7227            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
7228        }
7229    });
7230
7231    cx.assert_editor_state(
7232        &r#"
7233        use some::modified;
7234
7235
7236        fn main() {
7237        ˇ    println!("hello there");
7238
7239            println!("around the");
7240            println!("world");
7241        }
7242        "#
7243        .unindent(),
7244    );
7245
7246    cx.update_editor(|editor, cx| {
7247        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
7248    });
7249
7250    cx.assert_editor_state(
7251        &r#"
7252        use some::modified;
7253
7254        ˇ
7255        fn main() {
7256            println!("hello there");
7257
7258            println!("around the");
7259            println!("world");
7260        }
7261        "#
7262        .unindent(),
7263    );
7264
7265    cx.update_editor(|editor, cx| {
7266        for _ in 0..3 {
7267            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
7268        }
7269    });
7270
7271    cx.assert_editor_state(
7272        &r#"
7273        use some::modified;
7274
7275
7276        fn main() {
7277        ˇ    println!("hello there");
7278
7279            println!("around the");
7280            println!("world");
7281        }
7282        "#
7283        .unindent(),
7284    );
7285
7286    cx.update_editor(|editor, cx| {
7287        editor.fold(&Fold, cx);
7288
7289        //Make sure that the fold only gets one hunk
7290        for _ in 0..4 {
7291            editor.go_to_hunk(&GoToHunk, cx);
7292        }
7293    });
7294
7295    cx.assert_editor_state(
7296        &r#"
7297        ˇuse some::modified;
7298
7299
7300        fn main() {
7301            println!("hello there");
7302
7303            println!("around the");
7304            println!("world");
7305        }
7306        "#
7307        .unindent(),
7308    );
7309}
7310
7311#[test]
7312fn test_split_words() {
7313    fn split(text: &str) -> Vec<&str> {
7314        split_words(text).collect()
7315    }
7316
7317    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
7318    assert_eq!(split("hello_world"), &["hello_", "world"]);
7319    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
7320    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
7321    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
7322    assert_eq!(split("helloworld"), &["helloworld"]);
7323}
7324
7325#[gpui::test]
7326async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
7327    init_test(cx, |_| {});
7328
7329    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
7330    let mut assert = |before, after| {
7331        let _state_context = cx.set_state(before);
7332        cx.update_editor(|editor, cx| {
7333            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
7334        });
7335        cx.assert_editor_state(after);
7336    };
7337
7338    // Outside bracket jumps to outside of matching bracket
7339    assert("console.logˇ(var);", "console.log(var)ˇ;");
7340    assert("console.log(var)ˇ;", "console.logˇ(var);");
7341
7342    // Inside bracket jumps to inside of matching bracket
7343    assert("console.log(ˇvar);", "console.log(varˇ);");
7344    assert("console.log(varˇ);", "console.log(ˇvar);");
7345
7346    // When outside a bracket and inside, favor jumping to the inside bracket
7347    assert(
7348        "console.log('foo', [1, 2, 3]ˇ);",
7349        "console.log(ˇ'foo', [1, 2, 3]);",
7350    );
7351    assert(
7352        "console.log(ˇ'foo', [1, 2, 3]);",
7353        "console.log('foo', [1, 2, 3]ˇ);",
7354    );
7355
7356    // Bias forward if two options are equally likely
7357    assert(
7358        "let result = curried_fun()ˇ();",
7359        "let result = curried_fun()()ˇ;",
7360    );
7361
7362    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
7363    assert(
7364        indoc! {"
7365            function test() {
7366                console.log('test')ˇ
7367            }"},
7368        indoc! {"
7369            function test() {
7370                console.logˇ('test')
7371            }"},
7372    );
7373}
7374
7375#[gpui::test(iterations = 10)]
7376async fn test_copilot(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
7377    // flaky
7378    init_test(cx, |_| {});
7379
7380    let (copilot, copilot_lsp) = Copilot::fake(cx);
7381    _ = cx.update(|cx| Copilot::set_global(copilot, cx));
7382    let mut cx = EditorLspTestContext::new_rust(
7383        lsp::ServerCapabilities {
7384            completion_provider: Some(lsp::CompletionOptions {
7385                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
7386                ..Default::default()
7387            }),
7388            ..Default::default()
7389        },
7390        cx,
7391    )
7392    .await;
7393
7394    // When inserting, ensure autocompletion is favored over Copilot suggestions.
7395    cx.set_state(indoc! {"
7396        oneˇ
7397        two
7398        three
7399    "});
7400    cx.simulate_keystroke(".");
7401    let _ = handle_completion_request(
7402        &mut cx,
7403        indoc! {"
7404            one.|<>
7405            two
7406            three
7407        "},
7408        vec!["completion_a", "completion_b"],
7409    );
7410    handle_copilot_completion_request(
7411        &copilot_lsp,
7412        vec![copilot::request::Completion {
7413            text: "one.copilot1".into(),
7414            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
7415            ..Default::default()
7416        }],
7417        vec![],
7418    );
7419    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7420    cx.update_editor(|editor, cx| {
7421        assert!(editor.context_menu_visible());
7422        assert!(!editor.has_active_copilot_suggestion(cx));
7423
7424        // Confirming a completion inserts it and hides the context menu, without showing
7425        // the copilot suggestion afterwards.
7426        editor
7427            .confirm_completion(&Default::default(), cx)
7428            .unwrap()
7429            .detach();
7430        assert!(!editor.context_menu_visible());
7431        assert!(!editor.has_active_copilot_suggestion(cx));
7432        assert_eq!(editor.text(cx), "one.completion_a\ntwo\nthree\n");
7433        assert_eq!(editor.display_text(cx), "one.completion_a\ntwo\nthree\n");
7434    });
7435
7436    // Ensure Copilot suggestions are shown right away if no autocompletion is available.
7437    cx.set_state(indoc! {"
7438        oneˇ
7439        two
7440        three
7441    "});
7442    cx.simulate_keystroke(".");
7443    let _ = handle_completion_request(
7444        &mut cx,
7445        indoc! {"
7446            one.|<>
7447            two
7448            three
7449        "},
7450        vec![],
7451    );
7452    handle_copilot_completion_request(
7453        &copilot_lsp,
7454        vec![copilot::request::Completion {
7455            text: "one.copilot1".into(),
7456            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
7457            ..Default::default()
7458        }],
7459        vec![],
7460    );
7461    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7462    cx.update_editor(|editor, cx| {
7463        assert!(!editor.context_menu_visible());
7464        assert!(editor.has_active_copilot_suggestion(cx));
7465        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
7466        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
7467    });
7468
7469    // Reset editor, and ensure autocompletion is still favored over Copilot suggestions.
7470    cx.set_state(indoc! {"
7471        oneˇ
7472        two
7473        three
7474    "});
7475    cx.simulate_keystroke(".");
7476    let _ = handle_completion_request(
7477        &mut cx,
7478        indoc! {"
7479            one.|<>
7480            two
7481            three
7482        "},
7483        vec!["completion_a", "completion_b"],
7484    );
7485    handle_copilot_completion_request(
7486        &copilot_lsp,
7487        vec![copilot::request::Completion {
7488            text: "one.copilot1".into(),
7489            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
7490            ..Default::default()
7491        }],
7492        vec![],
7493    );
7494    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7495    cx.update_editor(|editor, cx| {
7496        assert!(editor.context_menu_visible());
7497        assert!(!editor.has_active_copilot_suggestion(cx));
7498
7499        // When hiding the context menu, the Copilot suggestion becomes visible.
7500        editor.hide_context_menu(cx);
7501        assert!(!editor.context_menu_visible());
7502        assert!(editor.has_active_copilot_suggestion(cx));
7503        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
7504        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
7505    });
7506
7507    // Ensure existing completion is interpolated when inserting again.
7508    cx.simulate_keystroke("c");
7509    executor.run_until_parked();
7510    cx.update_editor(|editor, cx| {
7511        assert!(!editor.context_menu_visible());
7512        assert!(editor.has_active_copilot_suggestion(cx));
7513        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
7514        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7515    });
7516
7517    // After debouncing, new Copilot completions should be requested.
7518    handle_copilot_completion_request(
7519        &copilot_lsp,
7520        vec![copilot::request::Completion {
7521            text: "one.copilot2".into(),
7522            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 5)),
7523            ..Default::default()
7524        }],
7525        vec![],
7526    );
7527    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7528    cx.update_editor(|editor, cx| {
7529        assert!(!editor.context_menu_visible());
7530        assert!(editor.has_active_copilot_suggestion(cx));
7531        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7532        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7533
7534        // Canceling should remove the active Copilot suggestion.
7535        editor.cancel(&Default::default(), cx);
7536        assert!(!editor.has_active_copilot_suggestion(cx));
7537        assert_eq!(editor.display_text(cx), "one.c\ntwo\nthree\n");
7538        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7539
7540        // After canceling, tabbing shouldn't insert the previously shown suggestion.
7541        editor.tab(&Default::default(), cx);
7542        assert!(!editor.has_active_copilot_suggestion(cx));
7543        assert_eq!(editor.display_text(cx), "one.c   \ntwo\nthree\n");
7544        assert_eq!(editor.text(cx), "one.c   \ntwo\nthree\n");
7545
7546        // When undoing the previously active suggestion is shown again.
7547        editor.undo(&Default::default(), cx);
7548        assert!(editor.has_active_copilot_suggestion(cx));
7549        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7550        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7551    });
7552
7553    // If an edit occurs outside of this editor, the suggestion is still correctly interpolated.
7554    cx.update_buffer(|buffer, cx| buffer.edit([(5..5, "o")], None, cx));
7555    cx.update_editor(|editor, cx| {
7556        assert!(editor.has_active_copilot_suggestion(cx));
7557        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7558        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
7559
7560        // Tabbing when there is an active suggestion inserts it.
7561        editor.tab(&Default::default(), cx);
7562        assert!(!editor.has_active_copilot_suggestion(cx));
7563        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7564        assert_eq!(editor.text(cx), "one.copilot2\ntwo\nthree\n");
7565
7566        // When undoing the previously active suggestion is shown again.
7567        editor.undo(&Default::default(), cx);
7568        assert!(editor.has_active_copilot_suggestion(cx));
7569        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7570        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
7571
7572        // Hide suggestion.
7573        editor.cancel(&Default::default(), cx);
7574        assert!(!editor.has_active_copilot_suggestion(cx));
7575        assert_eq!(editor.display_text(cx), "one.co\ntwo\nthree\n");
7576        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
7577    });
7578
7579    // If an edit occurs outside of this editor but no suggestion is being shown,
7580    // we won't make it visible.
7581    cx.update_buffer(|buffer, cx| buffer.edit([(6..6, "p")], None, cx));
7582    cx.update_editor(|editor, cx| {
7583        assert!(!editor.has_active_copilot_suggestion(cx));
7584        assert_eq!(editor.display_text(cx), "one.cop\ntwo\nthree\n");
7585        assert_eq!(editor.text(cx), "one.cop\ntwo\nthree\n");
7586    });
7587
7588    // Reset the editor to verify how suggestions behave when tabbing on leading indentation.
7589    cx.update_editor(|editor, cx| {
7590        editor.set_text("fn foo() {\n  \n}", cx);
7591        editor.change_selections(None, cx, |s| {
7592            s.select_ranges([Point::new(1, 2)..Point::new(1, 2)])
7593        });
7594    });
7595    handle_copilot_completion_request(
7596        &copilot_lsp,
7597        vec![copilot::request::Completion {
7598            text: "    let x = 4;".into(),
7599            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
7600            ..Default::default()
7601        }],
7602        vec![],
7603    );
7604
7605    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
7606    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7607    cx.update_editor(|editor, cx| {
7608        assert!(editor.has_active_copilot_suggestion(cx));
7609        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
7610        assert_eq!(editor.text(cx), "fn foo() {\n  \n}");
7611
7612        // Tabbing inside of leading whitespace inserts indentation without accepting the suggestion.
7613        editor.tab(&Default::default(), cx);
7614        assert!(editor.has_active_copilot_suggestion(cx));
7615        assert_eq!(editor.text(cx), "fn foo() {\n    \n}");
7616        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
7617
7618        // Tabbing again accepts the suggestion.
7619        editor.tab(&Default::default(), cx);
7620        assert!(!editor.has_active_copilot_suggestion(cx));
7621        assert_eq!(editor.text(cx), "fn foo() {\n    let x = 4;\n}");
7622        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
7623    });
7624}
7625
7626#[gpui::test(iterations = 10)]
7627async fn test_accept_partial_copilot_suggestion(
7628    executor: BackgroundExecutor,
7629    cx: &mut gpui::TestAppContext,
7630) {
7631    // flaky
7632    init_test(cx, |_| {});
7633
7634    let (copilot, copilot_lsp) = Copilot::fake(cx);
7635    _ = cx.update(|cx| Copilot::set_global(copilot, cx));
7636    let mut cx = EditorLspTestContext::new_rust(
7637        lsp::ServerCapabilities {
7638            completion_provider: Some(lsp::CompletionOptions {
7639                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
7640                ..Default::default()
7641            }),
7642            ..Default::default()
7643        },
7644        cx,
7645    )
7646    .await;
7647
7648    // Setup the editor with a completion request.
7649    cx.set_state(indoc! {"
7650        oneˇ
7651        two
7652        three
7653    "});
7654    cx.simulate_keystroke(".");
7655    let _ = handle_completion_request(
7656        &mut cx,
7657        indoc! {"
7658            one.|<>
7659            two
7660            three
7661        "},
7662        vec![],
7663    );
7664    handle_copilot_completion_request(
7665        &copilot_lsp,
7666        vec![copilot::request::Completion {
7667            text: "one.copilot1".into(),
7668            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
7669            ..Default::default()
7670        }],
7671        vec![],
7672    );
7673    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7674    cx.update_editor(|editor, cx| {
7675        assert!(editor.has_active_copilot_suggestion(cx));
7676
7677        // Accepting the first word of the suggestion should only accept the first word and still show the rest.
7678        editor.accept_partial_copilot_suggestion(&Default::default(), cx);
7679        assert!(editor.has_active_copilot_suggestion(cx));
7680        assert_eq!(editor.text(cx), "one.copilot\ntwo\nthree\n");
7681        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
7682
7683        // Accepting next word should accept the non-word and copilot suggestion should be gone
7684        editor.accept_partial_copilot_suggestion(&Default::default(), cx);
7685        assert!(!editor.has_active_copilot_suggestion(cx));
7686        assert_eq!(editor.text(cx), "one.copilot1\ntwo\nthree\n");
7687        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
7688    });
7689
7690    // Reset the editor and check non-word and whitespace completion
7691    cx.set_state(indoc! {"
7692        oneˇ
7693        two
7694        three
7695    "});
7696    cx.simulate_keystroke(".");
7697    let _ = handle_completion_request(
7698        &mut cx,
7699        indoc! {"
7700            one.|<>
7701            two
7702            three
7703        "},
7704        vec![],
7705    );
7706    handle_copilot_completion_request(
7707        &copilot_lsp,
7708        vec![copilot::request::Completion {
7709            text: "one.123. copilot\n 456".into(),
7710            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
7711            ..Default::default()
7712        }],
7713        vec![],
7714    );
7715    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7716    cx.update_editor(|editor, cx| {
7717        assert!(editor.has_active_copilot_suggestion(cx));
7718
7719        // Accepting the first word (non-word) of the suggestion should only accept the first word and still show the rest.
7720        editor.accept_partial_copilot_suggestion(&Default::default(), cx);
7721        assert!(editor.has_active_copilot_suggestion(cx));
7722        assert_eq!(editor.text(cx), "one.123. \ntwo\nthree\n");
7723        assert_eq!(
7724            editor.display_text(cx),
7725            "one.123. copilot\n 456\ntwo\nthree\n"
7726        );
7727
7728        // Accepting next word should accept the next word and copilot suggestion should still exist
7729        editor.accept_partial_copilot_suggestion(&Default::default(), cx);
7730        assert!(editor.has_active_copilot_suggestion(cx));
7731        assert_eq!(editor.text(cx), "one.123. copilot\ntwo\nthree\n");
7732        assert_eq!(
7733            editor.display_text(cx),
7734            "one.123. copilot\n 456\ntwo\nthree\n"
7735        );
7736
7737        // Accepting the whitespace should accept the non-word/whitespaces with newline and copilot suggestion should be gone
7738        editor.accept_partial_copilot_suggestion(&Default::default(), cx);
7739        assert!(!editor.has_active_copilot_suggestion(cx));
7740        assert_eq!(editor.text(cx), "one.123. copilot\n 456\ntwo\nthree\n");
7741        assert_eq!(
7742            editor.display_text(cx),
7743            "one.123. copilot\n 456\ntwo\nthree\n"
7744        );
7745    });
7746}
7747
7748#[gpui::test]
7749async fn test_copilot_completion_invalidation(
7750    executor: BackgroundExecutor,
7751    cx: &mut gpui::TestAppContext,
7752) {
7753    init_test(cx, |_| {});
7754
7755    let (copilot, copilot_lsp) = Copilot::fake(cx);
7756    _ = cx.update(|cx| Copilot::set_global(copilot, cx));
7757    let mut cx = EditorLspTestContext::new_rust(
7758        lsp::ServerCapabilities {
7759            completion_provider: Some(lsp::CompletionOptions {
7760                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
7761                ..Default::default()
7762            }),
7763            ..Default::default()
7764        },
7765        cx,
7766    )
7767    .await;
7768
7769    cx.set_state(indoc! {"
7770        one
7771        twˇ
7772        three
7773    "});
7774
7775    handle_copilot_completion_request(
7776        &copilot_lsp,
7777        vec![copilot::request::Completion {
7778            text: "two.foo()".into(),
7779            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
7780            ..Default::default()
7781        }],
7782        vec![],
7783    );
7784    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
7785    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7786    cx.update_editor(|editor, cx| {
7787        assert!(editor.has_active_copilot_suggestion(cx));
7788        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7789        assert_eq!(editor.text(cx), "one\ntw\nthree\n");
7790
7791        editor.backspace(&Default::default(), cx);
7792        assert!(editor.has_active_copilot_suggestion(cx));
7793        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7794        assert_eq!(editor.text(cx), "one\nt\nthree\n");
7795
7796        editor.backspace(&Default::default(), cx);
7797        assert!(editor.has_active_copilot_suggestion(cx));
7798        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7799        assert_eq!(editor.text(cx), "one\n\nthree\n");
7800
7801        // Deleting across the original suggestion range invalidates it.
7802        editor.backspace(&Default::default(), cx);
7803        assert!(!editor.has_active_copilot_suggestion(cx));
7804        assert_eq!(editor.display_text(cx), "one\nthree\n");
7805        assert_eq!(editor.text(cx), "one\nthree\n");
7806
7807        // Undoing the deletion restores the suggestion.
7808        editor.undo(&Default::default(), cx);
7809        assert!(editor.has_active_copilot_suggestion(cx));
7810        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7811        assert_eq!(editor.text(cx), "one\n\nthree\n");
7812    });
7813}
7814
7815#[gpui::test]
7816async fn test_copilot_multibuffer(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
7817    init_test(cx, |_| {});
7818
7819    let (copilot, copilot_lsp) = Copilot::fake(cx);
7820    _ = cx.update(|cx| Copilot::set_global(copilot, cx));
7821
7822    let buffer_1 = cx.new_model(|cx| {
7823        Buffer::new(
7824            0,
7825            BufferId::new(cx.entity_id().as_u64()).unwrap(),
7826            "a = 1\nb = 2\n",
7827        )
7828    });
7829    let buffer_2 = cx.new_model(|cx| {
7830        Buffer::new(
7831            0,
7832            BufferId::new(cx.entity_id().as_u64()).unwrap(),
7833            "c = 3\nd = 4\n",
7834        )
7835    });
7836    let multibuffer = cx.new_model(|cx| {
7837        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
7838        multibuffer.push_excerpts(
7839            buffer_1.clone(),
7840            [ExcerptRange {
7841                context: Point::new(0, 0)..Point::new(2, 0),
7842                primary: None,
7843            }],
7844            cx,
7845        );
7846        multibuffer.push_excerpts(
7847            buffer_2.clone(),
7848            [ExcerptRange {
7849                context: Point::new(0, 0)..Point::new(2, 0),
7850                primary: None,
7851            }],
7852            cx,
7853        );
7854        multibuffer
7855    });
7856    let editor = cx.add_window(|cx| build_editor(multibuffer, cx));
7857
7858    handle_copilot_completion_request(
7859        &copilot_lsp,
7860        vec![copilot::request::Completion {
7861            text: "b = 2 + a".into(),
7862            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 5)),
7863            ..Default::default()
7864        }],
7865        vec![],
7866    );
7867    _ = editor.update(cx, |editor, cx| {
7868        // Ensure copilot suggestions are shown for the first excerpt.
7869        editor.change_selections(None, cx, |s| {
7870            s.select_ranges([Point::new(1, 5)..Point::new(1, 5)])
7871        });
7872        editor.next_copilot_suggestion(&Default::default(), cx);
7873    });
7874    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7875    _ = editor.update(cx, |editor, cx| {
7876        assert!(editor.has_active_copilot_suggestion(cx));
7877        assert_eq!(
7878            editor.display_text(cx),
7879            "\n\na = 1\nb = 2 + a\n\n\n\nc = 3\nd = 4\n"
7880        );
7881        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
7882    });
7883
7884    handle_copilot_completion_request(
7885        &copilot_lsp,
7886        vec![copilot::request::Completion {
7887            text: "d = 4 + c".into(),
7888            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 6)),
7889            ..Default::default()
7890        }],
7891        vec![],
7892    );
7893    _ = editor.update(cx, |editor, cx| {
7894        // Move to another excerpt, ensuring the suggestion gets cleared.
7895        editor.change_selections(None, cx, |s| {
7896            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
7897        });
7898        assert!(!editor.has_active_copilot_suggestion(cx));
7899        assert_eq!(
7900            editor.display_text(cx),
7901            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4\n"
7902        );
7903        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
7904
7905        // Type a character, ensuring we don't even try to interpolate the previous suggestion.
7906        editor.handle_input(" ", cx);
7907        assert!(!editor.has_active_copilot_suggestion(cx));
7908        assert_eq!(
7909            editor.display_text(cx),
7910            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 \n"
7911        );
7912        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
7913    });
7914
7915    // Ensure the new suggestion is displayed when the debounce timeout expires.
7916    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7917    _ = editor.update(cx, |editor, cx| {
7918        assert!(editor.has_active_copilot_suggestion(cx));
7919        assert_eq!(
7920            editor.display_text(cx),
7921            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 + c\n"
7922        );
7923        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
7924    });
7925}
7926
7927#[gpui::test]
7928async fn test_copilot_disabled_globs(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
7929    init_test(cx, |settings| {
7930        settings
7931            .copilot
7932            .get_or_insert(Default::default())
7933            .disabled_globs = Some(vec![".env*".to_string()]);
7934    });
7935
7936    let (copilot, copilot_lsp) = Copilot::fake(cx);
7937    _ = cx.update(|cx| Copilot::set_global(copilot, cx));
7938
7939    let fs = FakeFs::new(cx.executor());
7940    fs.insert_tree(
7941        "/test",
7942        json!({
7943            ".env": "SECRET=something\n",
7944            "README.md": "hello\n"
7945        }),
7946    )
7947    .await;
7948    let project = Project::test(fs, ["/test".as_ref()], cx).await;
7949
7950    let private_buffer = project
7951        .update(cx, |project, cx| {
7952            project.open_local_buffer("/test/.env", cx)
7953        })
7954        .await
7955        .unwrap();
7956    let public_buffer = project
7957        .update(cx, |project, cx| {
7958            project.open_local_buffer("/test/README.md", cx)
7959        })
7960        .await
7961        .unwrap();
7962
7963    let multibuffer = cx.new_model(|cx| {
7964        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
7965        multibuffer.push_excerpts(
7966            private_buffer.clone(),
7967            [ExcerptRange {
7968                context: Point::new(0, 0)..Point::new(1, 0),
7969                primary: None,
7970            }],
7971            cx,
7972        );
7973        multibuffer.push_excerpts(
7974            public_buffer.clone(),
7975            [ExcerptRange {
7976                context: Point::new(0, 0)..Point::new(1, 0),
7977                primary: None,
7978            }],
7979            cx,
7980        );
7981        multibuffer
7982    });
7983    let editor = cx.add_window(|cx| build_editor(multibuffer, cx));
7984
7985    let mut copilot_requests = copilot_lsp
7986        .handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| async move {
7987            Ok(copilot::request::GetCompletionsResult {
7988                completions: vec![copilot::request::Completion {
7989                    text: "next line".into(),
7990                    range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
7991                    ..Default::default()
7992                }],
7993            })
7994        });
7995
7996    _ = editor.update(cx, |editor, cx| {
7997        editor.change_selections(None, cx, |selections| {
7998            selections.select_ranges([Point::new(0, 0)..Point::new(0, 0)])
7999        });
8000        editor.next_copilot_suggestion(&Default::default(), cx);
8001    });
8002
8003    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
8004    assert!(copilot_requests.try_next().is_err());
8005
8006    _ = editor.update(cx, |editor, cx| {
8007        editor.change_selections(None, cx, |s| {
8008            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
8009        });
8010        editor.next_copilot_suggestion(&Default::default(), cx);
8011    });
8012
8013    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
8014    assert!(copilot_requests.try_next().is_ok());
8015}
8016
8017#[gpui::test]
8018async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
8019    init_test(cx, |_| {});
8020
8021    let fs = FakeFs::new(cx.executor());
8022    fs.insert_tree(
8023        "/a",
8024        json!({
8025            "main.rs": "fn main() { let a = 5; }",
8026            "other.rs": "// Test file",
8027        }),
8028    )
8029    .await;
8030    let project = Project::test(fs, ["/a".as_ref()], cx).await;
8031
8032    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
8033    language_registry.add(Arc::new(Language::new(
8034        LanguageConfig {
8035            name: "Rust".into(),
8036            matcher: LanguageMatcher {
8037                path_suffixes: vec!["rs".to_string()],
8038                ..Default::default()
8039            },
8040            brackets: BracketPairConfig {
8041                pairs: vec![BracketPair {
8042                    start: "{".to_string(),
8043                    end: "}".to_string(),
8044                    close: true,
8045                    newline: true,
8046                }],
8047                disabled_scopes_by_bracket_ix: Vec::new(),
8048            },
8049            ..Default::default()
8050        },
8051        Some(tree_sitter_rust::language()),
8052    )));
8053    let mut fake_servers = language_registry.register_fake_lsp_adapter(
8054        "Rust",
8055        FakeLspAdapter {
8056            capabilities: lsp::ServerCapabilities {
8057                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
8058                    first_trigger_character: "{".to_string(),
8059                    more_trigger_character: None,
8060                }),
8061                ..Default::default()
8062            },
8063            ..Default::default()
8064        },
8065    );
8066
8067    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
8068
8069    let cx = &mut VisualTestContext::from_window(*workspace, cx);
8070
8071    let worktree_id = workspace
8072        .update(cx, |workspace, cx| {
8073            workspace.project().update(cx, |project, cx| {
8074                project.worktrees().next().unwrap().read(cx).id()
8075            })
8076        })
8077        .unwrap();
8078
8079    let buffer = project
8080        .update(cx, |project, cx| {
8081            project.open_local_buffer("/a/main.rs", cx)
8082        })
8083        .await
8084        .unwrap();
8085    cx.executor().run_until_parked();
8086    cx.executor().start_waiting();
8087    let fake_server = fake_servers.next().await.unwrap();
8088    let editor_handle = workspace
8089        .update(cx, |workspace, cx| {
8090            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
8091        })
8092        .unwrap()
8093        .await
8094        .unwrap()
8095        .downcast::<Editor>()
8096        .unwrap();
8097
8098    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
8099        assert_eq!(
8100            params.text_document_position.text_document.uri,
8101            lsp::Url::from_file_path("/a/main.rs").unwrap(),
8102        );
8103        assert_eq!(
8104            params.text_document_position.position,
8105            lsp::Position::new(0, 21),
8106        );
8107
8108        Ok(Some(vec![lsp::TextEdit {
8109            new_text: "]".to_string(),
8110            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
8111        }]))
8112    });
8113
8114    editor_handle.update(cx, |editor, cx| {
8115        editor.focus(cx);
8116        editor.change_selections(None, cx, |s| {
8117            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
8118        });
8119        editor.handle_input("{", cx);
8120    });
8121
8122    cx.executor().run_until_parked();
8123
8124    _ = buffer.update(cx, |buffer, _| {
8125        assert_eq!(
8126            buffer.text(),
8127            "fn main() { let a = {5}; }",
8128            "No extra braces from on type formatting should appear in the buffer"
8129        )
8130    });
8131}
8132
8133#[gpui::test]
8134async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
8135    init_test(cx, |_| {});
8136
8137    let fs = FakeFs::new(cx.executor());
8138    fs.insert_tree(
8139        "/a",
8140        json!({
8141            "main.rs": "fn main() { let a = 5; }",
8142            "other.rs": "// Test file",
8143        }),
8144    )
8145    .await;
8146
8147    let project = Project::test(fs, ["/a".as_ref()], cx).await;
8148
8149    let server_restarts = Arc::new(AtomicUsize::new(0));
8150    let closure_restarts = Arc::clone(&server_restarts);
8151    let language_server_name = "test language server";
8152    let language_name: Arc<str> = "Rust".into();
8153
8154    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
8155    language_registry.add(Arc::new(Language::new(
8156        LanguageConfig {
8157            name: Arc::clone(&language_name),
8158            matcher: LanguageMatcher {
8159                path_suffixes: vec!["rs".to_string()],
8160                ..Default::default()
8161            },
8162            ..Default::default()
8163        },
8164        Some(tree_sitter_rust::language()),
8165    )));
8166    let mut fake_servers = language_registry.register_fake_lsp_adapter(
8167        "Rust",
8168        FakeLspAdapter {
8169            name: language_server_name,
8170            initialization_options: Some(json!({
8171                "testOptionValue": true
8172            })),
8173            initializer: Some(Box::new(move |fake_server| {
8174                let task_restarts = Arc::clone(&closure_restarts);
8175                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
8176                    task_restarts.fetch_add(1, atomic::Ordering::Release);
8177                    futures::future::ready(Ok(()))
8178                });
8179            })),
8180            ..Default::default()
8181        },
8182    );
8183
8184    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
8185    let _buffer = project
8186        .update(cx, |project, cx| {
8187            project.open_local_buffer("/a/main.rs", cx)
8188        })
8189        .await
8190        .unwrap();
8191    let _fake_server = fake_servers.next().await.unwrap();
8192    update_test_language_settings(cx, |language_settings| {
8193        language_settings.languages.insert(
8194            Arc::clone(&language_name),
8195            LanguageSettingsContent {
8196                tab_size: NonZeroU32::new(8),
8197                ..Default::default()
8198            },
8199        );
8200    });
8201    cx.executor().run_until_parked();
8202    assert_eq!(
8203        server_restarts.load(atomic::Ordering::Acquire),
8204        0,
8205        "Should not restart LSP server on an unrelated change"
8206    );
8207
8208    update_test_project_settings(cx, |project_settings| {
8209        project_settings.lsp.insert(
8210            "Some other server name".into(),
8211            LspSettings {
8212                settings: None,
8213                initialization_options: Some(json!({
8214                    "some other init value": false
8215                })),
8216            },
8217        );
8218    });
8219    cx.executor().run_until_parked();
8220    assert_eq!(
8221        server_restarts.load(atomic::Ordering::Acquire),
8222        0,
8223        "Should not restart LSP server on an unrelated LSP settings change"
8224    );
8225
8226    update_test_project_settings(cx, |project_settings| {
8227        project_settings.lsp.insert(
8228            language_server_name.into(),
8229            LspSettings {
8230                settings: None,
8231                initialization_options: Some(json!({
8232                    "anotherInitValue": false
8233                })),
8234            },
8235        );
8236    });
8237    cx.executor().run_until_parked();
8238    assert_eq!(
8239        server_restarts.load(atomic::Ordering::Acquire),
8240        1,
8241        "Should restart LSP server on a related LSP settings change"
8242    );
8243
8244    update_test_project_settings(cx, |project_settings| {
8245        project_settings.lsp.insert(
8246            language_server_name.into(),
8247            LspSettings {
8248                settings: None,
8249                initialization_options: Some(json!({
8250                    "anotherInitValue": false
8251                })),
8252            },
8253        );
8254    });
8255    cx.executor().run_until_parked();
8256    assert_eq!(
8257        server_restarts.load(atomic::Ordering::Acquire),
8258        1,
8259        "Should not restart LSP server on a related LSP settings change that is the same"
8260    );
8261
8262    update_test_project_settings(cx, |project_settings| {
8263        project_settings.lsp.insert(
8264            language_server_name.into(),
8265            LspSettings {
8266                settings: None,
8267                initialization_options: None,
8268            },
8269        );
8270    });
8271    cx.executor().run_until_parked();
8272    assert_eq!(
8273        server_restarts.load(atomic::Ordering::Acquire),
8274        2,
8275        "Should restart LSP server on another related LSP settings change"
8276    );
8277}
8278
8279#[gpui::test]
8280async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
8281    init_test(cx, |_| {});
8282
8283    let mut cx = EditorLspTestContext::new_rust(
8284        lsp::ServerCapabilities {
8285            completion_provider: Some(lsp::CompletionOptions {
8286                trigger_characters: Some(vec![".".to_string()]),
8287                resolve_provider: Some(true),
8288                ..Default::default()
8289            }),
8290            ..Default::default()
8291        },
8292        cx,
8293    )
8294    .await;
8295
8296    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
8297    cx.simulate_keystroke(".");
8298    let completion_item = lsp::CompletionItem {
8299        label: "some".into(),
8300        kind: Some(lsp::CompletionItemKind::SNIPPET),
8301        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
8302        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
8303            kind: lsp::MarkupKind::Markdown,
8304            value: "```rust\nSome(2)\n```".to_string(),
8305        })),
8306        deprecated: Some(false),
8307        sort_text: Some("fffffff2".to_string()),
8308        filter_text: Some("some".to_string()),
8309        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
8310        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
8311            range: lsp::Range {
8312                start: lsp::Position {
8313                    line: 0,
8314                    character: 22,
8315                },
8316                end: lsp::Position {
8317                    line: 0,
8318                    character: 22,
8319                },
8320            },
8321            new_text: "Some(2)".to_string(),
8322        })),
8323        additional_text_edits: Some(vec![lsp::TextEdit {
8324            range: lsp::Range {
8325                start: lsp::Position {
8326                    line: 0,
8327                    character: 20,
8328                },
8329                end: lsp::Position {
8330                    line: 0,
8331                    character: 22,
8332                },
8333            },
8334            new_text: "".to_string(),
8335        }]),
8336        ..Default::default()
8337    };
8338
8339    let closure_completion_item = completion_item.clone();
8340    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
8341        let task_completion_item = closure_completion_item.clone();
8342        async move {
8343            Ok(Some(lsp::CompletionResponse::Array(vec![
8344                task_completion_item,
8345            ])))
8346        }
8347    });
8348
8349    request.next().await;
8350
8351    cx.condition(|editor, _| editor.context_menu_visible())
8352        .await;
8353    let apply_additional_edits = cx.update_editor(|editor, cx| {
8354        editor
8355            .confirm_completion(&ConfirmCompletion::default(), cx)
8356            .unwrap()
8357    });
8358    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
8359
8360    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
8361        let task_completion_item = completion_item.clone();
8362        async move { Ok(task_completion_item) }
8363    })
8364    .next()
8365    .await
8366    .unwrap();
8367    apply_additional_edits.await.unwrap();
8368    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
8369}
8370
8371#[gpui::test]
8372async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
8373    init_test(cx, |_| {});
8374
8375    let mut cx = EditorLspTestContext::new(
8376        Language::new(
8377            LanguageConfig {
8378                matcher: LanguageMatcher {
8379                    path_suffixes: vec!["jsx".into()],
8380                    ..Default::default()
8381                },
8382                overrides: [(
8383                    "element".into(),
8384                    LanguageConfigOverride {
8385                        word_characters: Override::Set(['-'].into_iter().collect()),
8386                        ..Default::default()
8387                    },
8388                )]
8389                .into_iter()
8390                .collect(),
8391                ..Default::default()
8392            },
8393            Some(tree_sitter_typescript::language_tsx()),
8394        )
8395        .with_override_query("(jsx_self_closing_element) @element")
8396        .unwrap(),
8397        lsp::ServerCapabilities {
8398            completion_provider: Some(lsp::CompletionOptions {
8399                trigger_characters: Some(vec![":".to_string()]),
8400                ..Default::default()
8401            }),
8402            ..Default::default()
8403        },
8404        cx,
8405    )
8406    .await;
8407
8408    cx.lsp
8409        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
8410            Ok(Some(lsp::CompletionResponse::Array(vec![
8411                lsp::CompletionItem {
8412                    label: "bg-blue".into(),
8413                    ..Default::default()
8414                },
8415                lsp::CompletionItem {
8416                    label: "bg-red".into(),
8417                    ..Default::default()
8418                },
8419                lsp::CompletionItem {
8420                    label: "bg-yellow".into(),
8421                    ..Default::default()
8422                },
8423            ])))
8424        });
8425
8426    cx.set_state(r#"<p class="bgˇ" />"#);
8427
8428    // Trigger completion when typing a dash, because the dash is an extra
8429    // word character in the 'element' scope, which contains the cursor.
8430    cx.simulate_keystroke("-");
8431    cx.executor().run_until_parked();
8432    cx.update_editor(|editor, _| {
8433        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
8434            assert_eq!(
8435                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
8436                &["bg-red", "bg-blue", "bg-yellow"]
8437            );
8438        } else {
8439            panic!("expected completion menu to be open");
8440        }
8441    });
8442
8443    cx.simulate_keystroke("l");
8444    cx.executor().run_until_parked();
8445    cx.update_editor(|editor, _| {
8446        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
8447            assert_eq!(
8448                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
8449                &["bg-blue", "bg-yellow"]
8450            );
8451        } else {
8452            panic!("expected completion menu to be open");
8453        }
8454    });
8455
8456    // When filtering completions, consider the character after the '-' to
8457    // be the start of a subword.
8458    cx.set_state(r#"<p class="yelˇ" />"#);
8459    cx.simulate_keystroke("l");
8460    cx.executor().run_until_parked();
8461    cx.update_editor(|editor, _| {
8462        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
8463            assert_eq!(
8464                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
8465                &["bg-yellow"]
8466            );
8467        } else {
8468            panic!("expected completion menu to be open");
8469        }
8470    });
8471}
8472
8473#[gpui::test]
8474async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
8475    init_test(cx, |settings| {
8476        settings.defaults.formatter = Some(language_settings::Formatter::Prettier)
8477    });
8478
8479    let fs = FakeFs::new(cx.executor());
8480    fs.insert_file("/file.rs", Default::default()).await;
8481
8482    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
8483    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
8484
8485    language_registry.add(Arc::new(Language::new(
8486        LanguageConfig {
8487            name: "Rust".into(),
8488            matcher: LanguageMatcher {
8489                path_suffixes: vec!["rs".to_string()],
8490                ..Default::default()
8491            },
8492            prettier_parser_name: Some("test_parser".to_string()),
8493            ..Default::default()
8494        },
8495        Some(tree_sitter_rust::language()),
8496    )));
8497
8498    let test_plugin = "test_plugin";
8499    let _ = language_registry.register_fake_lsp_adapter(
8500        "Rust",
8501        FakeLspAdapter {
8502            prettier_plugins: vec![test_plugin],
8503            ..Default::default()
8504        },
8505    );
8506
8507    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
8508    let buffer = project
8509        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
8510        .await
8511        .unwrap();
8512
8513    let buffer_text = "one\ntwo\nthree\n";
8514    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
8515    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
8516    _ = editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
8517
8518    editor
8519        .update(cx, |editor, cx| {
8520            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
8521        })
8522        .unwrap()
8523        .await;
8524    assert_eq!(
8525        editor.update(cx, |editor, cx| editor.text(cx)),
8526        buffer_text.to_string() + prettier_format_suffix,
8527        "Test prettier formatting was not applied to the original buffer text",
8528    );
8529
8530    update_test_language_settings(cx, |settings| {
8531        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
8532    });
8533    let format = editor.update(cx, |editor, cx| {
8534        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
8535    });
8536    format.await.unwrap();
8537    assert_eq!(
8538        editor.update(cx, |editor, cx| editor.text(cx)),
8539        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
8540        "Autoformatting (via test prettier) was not applied to the original buffer text",
8541    );
8542}
8543
8544#[gpui::test]
8545async fn test_find_all_references(cx: &mut gpui::TestAppContext) {
8546    init_test(cx, |_| {});
8547
8548    let mut cx = EditorLspTestContext::new_rust(
8549        lsp::ServerCapabilities {
8550            document_formatting_provider: Some(lsp::OneOf::Left(true)),
8551            ..Default::default()
8552        },
8553        cx,
8554    )
8555    .await;
8556
8557    cx.set_state(indoc! {"
8558        fn foo(«paramˇ»: i64) {
8559            println!(param);
8560        }
8561    "});
8562
8563    cx.lsp
8564        .handle_request::<lsp::request::References, _, _>(move |_, _| async move {
8565            Ok(Some(vec![
8566                lsp::Location {
8567                    uri: lsp::Url::from_file_path("/root/dir/file.rs").unwrap(),
8568                    range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 12)),
8569                },
8570                lsp::Location {
8571                    uri: lsp::Url::from_file_path("/root/dir/file.rs").unwrap(),
8572                    range: lsp::Range::new(lsp::Position::new(1, 13), lsp::Position::new(1, 18)),
8573                },
8574            ]))
8575        });
8576
8577    let references = cx
8578        .update_editor(|editor, cx| editor.find_all_references(&FindAllReferences, cx))
8579        .unwrap();
8580
8581    cx.executor().run_until_parked();
8582
8583    cx.executor().start_waiting();
8584    references.await.unwrap();
8585
8586    cx.assert_editor_state(indoc! {"
8587        fn foo(param: i64) {
8588            println!(«paramˇ»);
8589        }
8590    "});
8591
8592    let references = cx
8593        .update_editor(|editor, cx| editor.find_all_references(&FindAllReferences, cx))
8594        .unwrap();
8595
8596    cx.executor().run_until_parked();
8597
8598    cx.executor().start_waiting();
8599    references.await.unwrap();
8600
8601    cx.assert_editor_state(indoc! {"
8602        fn foo(«paramˇ»: i64) {
8603            println!(param);
8604        }
8605    "});
8606
8607    cx.set_state(indoc! {"
8608        fn foo(param: i64) {
8609            let a = param;
8610            let aˇ = param;
8611            let a = param;
8612            println!(param);
8613        }
8614    "});
8615
8616    cx.lsp
8617        .handle_request::<lsp::request::References, _, _>(move |_, _| async move {
8618            Ok(Some(vec![lsp::Location {
8619                uri: lsp::Url::from_file_path("/root/dir/file.rs").unwrap(),
8620                range: lsp::Range::new(lsp::Position::new(2, 8), lsp::Position::new(2, 9)),
8621            }]))
8622        });
8623
8624    let references = cx
8625        .update_editor(|editor, cx| editor.find_all_references(&FindAllReferences, cx))
8626        .unwrap();
8627
8628    cx.executor().run_until_parked();
8629
8630    cx.executor().start_waiting();
8631    references.await.unwrap();
8632
8633    cx.assert_editor_state(indoc! {"
8634        fn foo(param: i64) {
8635            let a = param;
8636            let «aˇ» = param;
8637            let a = param;
8638            println!(param);
8639        }
8640    "});
8641}
8642
8643fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
8644    let point = DisplayPoint::new(row as u32, column as u32);
8645    point..point
8646}
8647
8648fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
8649    let (text, ranges) = marked_text_ranges(marked_text, true);
8650    assert_eq!(view.text(cx), text);
8651    assert_eq!(
8652        view.selections.ranges(cx),
8653        ranges,
8654        "Assert selections are {}",
8655        marked_text
8656    );
8657}
8658
8659/// Handle completion request passing a marked string specifying where the completion
8660/// should be triggered from using '|' character, what range should be replaced, and what completions
8661/// should be returned using '<' and '>' to delimit the range
8662pub fn handle_completion_request(
8663    cx: &mut EditorLspTestContext,
8664    marked_string: &str,
8665    completions: Vec<&'static str>,
8666) -> impl Future<Output = ()> {
8667    let complete_from_marker: TextRangeMarker = '|'.into();
8668    let replace_range_marker: TextRangeMarker = ('<', '>').into();
8669    let (_, mut marked_ranges) = marked_text_ranges_by(
8670        marked_string,
8671        vec![complete_from_marker.clone(), replace_range_marker.clone()],
8672    );
8673
8674    let complete_from_position =
8675        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
8676    let replace_range =
8677        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
8678
8679    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
8680        let completions = completions.clone();
8681        async move {
8682            assert_eq!(params.text_document_position.text_document.uri, url.clone());
8683            assert_eq!(
8684                params.text_document_position.position,
8685                complete_from_position
8686            );
8687            Ok(Some(lsp::CompletionResponse::Array(
8688                completions
8689                    .iter()
8690                    .map(|completion_text| lsp::CompletionItem {
8691                        label: completion_text.to_string(),
8692                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
8693                            range: replace_range,
8694                            new_text: completion_text.to_string(),
8695                        })),
8696                        ..Default::default()
8697                    })
8698                    .collect(),
8699            )))
8700        }
8701    });
8702
8703    async move {
8704        request.next().await;
8705    }
8706}
8707
8708fn handle_resolve_completion_request(
8709    cx: &mut EditorLspTestContext,
8710    edits: Option<Vec<(&'static str, &'static str)>>,
8711) -> impl Future<Output = ()> {
8712    let edits = edits.map(|edits| {
8713        edits
8714            .iter()
8715            .map(|(marked_string, new_text)| {
8716                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
8717                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
8718                lsp::TextEdit::new(replace_range, new_text.to_string())
8719            })
8720            .collect::<Vec<_>>()
8721    });
8722
8723    let mut request =
8724        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
8725            let edits = edits.clone();
8726            async move {
8727                Ok(lsp::CompletionItem {
8728                    additional_text_edits: edits,
8729                    ..Default::default()
8730                })
8731            }
8732        });
8733
8734    async move {
8735        request.next().await;
8736    }
8737}
8738
8739fn handle_copilot_completion_request(
8740    lsp: &lsp::FakeLanguageServer,
8741    completions: Vec<copilot::request::Completion>,
8742    completions_cycling: Vec<copilot::request::Completion>,
8743) {
8744    lsp.handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| {
8745        let completions = completions.clone();
8746        async move {
8747            Ok(copilot::request::GetCompletionsResult {
8748                completions: completions.clone(),
8749            })
8750        }
8751    });
8752    lsp.handle_request::<copilot::request::GetCompletionsCycling, _, _>(move |_params, _cx| {
8753        let completions_cycling = completions_cycling.clone();
8754        async move {
8755            Ok(copilot::request::GetCompletionsResult {
8756                completions: completions_cycling.clone(),
8757            })
8758        }
8759    });
8760}
8761
8762pub(crate) fn update_test_language_settings(
8763    cx: &mut TestAppContext,
8764    f: impl Fn(&mut AllLanguageSettingsContent),
8765) {
8766    _ = cx.update(|cx| {
8767        cx.update_global(|store: &mut SettingsStore, cx| {
8768            store.update_user_settings::<AllLanguageSettings>(cx, f);
8769        });
8770    });
8771}
8772
8773pub(crate) fn update_test_project_settings(
8774    cx: &mut TestAppContext,
8775    f: impl Fn(&mut ProjectSettings),
8776) {
8777    _ = cx.update(|cx| {
8778        cx.update_global(|store: &mut SettingsStore, cx| {
8779            store.update_user_settings::<ProjectSettings>(cx, f);
8780        });
8781    });
8782}
8783
8784pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
8785    _ = cx.update(|cx| {
8786        let store = SettingsStore::test(cx);
8787        cx.set_global(store);
8788        theme::init(theme::LoadThemes::JustBase, cx);
8789        release_channel::init("0.0.0", cx);
8790        client::init_settings(cx);
8791        language::init(cx);
8792        Project::init_settings(cx);
8793        workspace::init_settings(cx);
8794        crate::init(cx);
8795    });
8796
8797    update_test_language_settings(cx, f);
8798}
8799
8800pub(crate) fn rust_lang() -> Arc<Language> {
8801    Arc::new(Language::new(
8802        LanguageConfig {
8803            name: "Rust".into(),
8804            matcher: LanguageMatcher {
8805                path_suffixes: vec!["rs".to_string()],
8806                ..Default::default()
8807            },
8808            ..Default::default()
8809        },
8810        Some(tree_sitter_rust::language()),
8811    ))
8812}