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