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