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