editor_tests.rs

   1use super::*;
   2use crate::test::{
   3    assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
   4    editor_test_context::EditorTestContext, select_ranges,
   5};
   6use drag_and_drop::DragAndDrop;
   7use futures::StreamExt;
   8use gpui::{
   9    executor::Deterministic,
  10    geometry::{rect::RectF, vector::vec2f},
  11    platform::{WindowBounds, WindowOptions},
  12    serde_json, TestAppContext,
  13};
  14use indoc::indoc;
  15use language::{
  16    language_settings::{AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent},
  17    BracketPairConfig, FakeLspAdapter, LanguageConfig, LanguageRegistry, Point,
  18};
  19use parking_lot::Mutex;
  20use project::FakeFs;
  21use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
  22use unindent::Unindent;
  23use util::{
  24    assert_set_eq,
  25    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
  26};
  27use workspace::{
  28    item::{FollowableItem, Item, ItemHandle},
  29    NavigationEntry, ViewId,
  30};
  31
  32#[gpui::test]
  33fn test_edit_events(cx: &mut TestAppContext) {
  34    init_test(cx, |_| {});
  35
  36    let buffer = cx.add_model(|cx| {
  37        let mut buffer = language::Buffer::new(0, "123456", cx);
  38        buffer.set_group_interval(Duration::from_secs(1));
  39        buffer
  40    });
  41
  42    let events = Rc::new(RefCell::new(Vec::new()));
  43    let (_, editor1) = cx.add_window({
  44        let events = events.clone();
  45        |cx| {
  46            cx.subscribe(&cx.handle(), move |_, _, event, _| {
  47                if matches!(
  48                    event,
  49                    Event::Edited | Event::BufferEdited | Event::DirtyChanged
  50                ) {
  51                    events.borrow_mut().push(("editor1", event.clone()));
  52                }
  53            })
  54            .detach();
  55            Editor::for_buffer(buffer.clone(), None, cx)
  56        }
  57    });
  58    let (_, editor2) = cx.add_window({
  59        let events = events.clone();
  60        |cx| {
  61            cx.subscribe(&cx.handle(), move |_, _, event, _| {
  62                if matches!(
  63                    event,
  64                    Event::Edited | Event::BufferEdited | Event::DirtyChanged
  65                ) {
  66                    events.borrow_mut().push(("editor2", event.clone()));
  67                }
  68            })
  69            .detach();
  70            Editor::for_buffer(buffer.clone(), None, cx)
  71        }
  72    });
  73    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  74
  75    // Mutating editor 1 will emit an `Edited` event only for that editor.
  76    editor1.update(cx, |editor, cx| editor.insert("X", cx));
  77    assert_eq!(
  78        mem::take(&mut *events.borrow_mut()),
  79        [
  80            ("editor1", Event::Edited),
  81            ("editor1", Event::BufferEdited),
  82            ("editor2", Event::BufferEdited),
  83            ("editor1", Event::DirtyChanged),
  84            ("editor2", Event::DirtyChanged)
  85        ]
  86    );
  87
  88    // Mutating editor 2 will emit an `Edited` event only for that editor.
  89    editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
  90    assert_eq!(
  91        mem::take(&mut *events.borrow_mut()),
  92        [
  93            ("editor2", Event::Edited),
  94            ("editor1", Event::BufferEdited),
  95            ("editor2", Event::BufferEdited),
  96        ]
  97    );
  98
  99    // Undoing on editor 1 will emit an `Edited` event only for that editor.
 100    editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
 101    assert_eq!(
 102        mem::take(&mut *events.borrow_mut()),
 103        [
 104            ("editor1", Event::Edited),
 105            ("editor1", Event::BufferEdited),
 106            ("editor2", Event::BufferEdited),
 107            ("editor1", Event::DirtyChanged),
 108            ("editor2", Event::DirtyChanged),
 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", Event::Edited),
 118            ("editor1", Event::BufferEdited),
 119            ("editor2", Event::BufferEdited),
 120            ("editor1", Event::DirtyChanged),
 121            ("editor2", Event::DirtyChanged),
 122        ]
 123    );
 124
 125    // Undoing on editor 2 will emit an `Edited` event only for that editor.
 126    editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
 127    assert_eq!(
 128        mem::take(&mut *events.borrow_mut()),
 129        [
 130            ("editor2", Event::Edited),
 131            ("editor1", Event::BufferEdited),
 132            ("editor2", Event::BufferEdited),
 133            ("editor1", Event::DirtyChanged),
 134            ("editor2", Event::DirtyChanged),
 135        ]
 136    );
 137
 138    // Redoing on editor 2 will emit an `Edited` event only for that editor.
 139    editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
 140    assert_eq!(
 141        mem::take(&mut *events.borrow_mut()),
 142        [
 143            ("editor2", Event::Edited),
 144            ("editor1", Event::BufferEdited),
 145            ("editor2", Event::BufferEdited),
 146            ("editor1", Event::DirtyChanged),
 147            ("editor2", Event::DirtyChanged),
 148        ]
 149    );
 150
 151    // No event is emitted when the mutation is a no-op.
 152    editor2.update(cx, |editor, cx| {
 153        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 154
 155        editor.backspace(&Backspace, cx);
 156    });
 157    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
 158}
 159
 160#[gpui::test]
 161fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
 162    init_test(cx, |_| {});
 163
 164    let mut now = Instant::now();
 165    let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
 166    let group_interval = buffer.read_with(cx, |buffer, _| buffer.transaction_group_interval());
 167    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
 168    let (_, editor) = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 169
 170    editor.update(cx, |editor, cx| {
 171        editor.start_transaction_at(now, cx);
 172        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
 173
 174        editor.insert("cd", cx);
 175        editor.end_transaction_at(now, cx);
 176        assert_eq!(editor.text(cx), "12cd56");
 177        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
 178
 179        editor.start_transaction_at(now, cx);
 180        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
 181        editor.insert("e", cx);
 182        editor.end_transaction_at(now, cx);
 183        assert_eq!(editor.text(cx), "12cde6");
 184        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
 185
 186        now += group_interval + Duration::from_millis(1);
 187        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
 188
 189        // Simulate an edit in another editor
 190        buffer.update(cx, |buffer, cx| {
 191            buffer.start_transaction_at(now, cx);
 192            buffer.edit([(0..1, "a")], None, cx);
 193            buffer.edit([(1..1, "b")], None, cx);
 194            buffer.end_transaction_at(now, cx);
 195        });
 196
 197        assert_eq!(editor.text(cx), "ab2cde6");
 198        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
 199
 200        // Last transaction happened past the group interval in a different editor.
 201        // Undo it individually and don't restore selections.
 202        editor.undo(&Undo, cx);
 203        assert_eq!(editor.text(cx), "12cde6");
 204        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
 205
 206        // First two transactions happened within the group interval in this editor.
 207        // Undo them together and restore selections.
 208        editor.undo(&Undo, cx);
 209        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
 210        assert_eq!(editor.text(cx), "123456");
 211        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
 212
 213        // Redo the first two transactions together.
 214        editor.redo(&Redo, cx);
 215        assert_eq!(editor.text(cx), "12cde6");
 216        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
 217
 218        // Redo the last transaction on its own.
 219        editor.redo(&Redo, cx);
 220        assert_eq!(editor.text(cx), "ab2cde6");
 221        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
 222
 223        // Test empty transactions.
 224        editor.start_transaction_at(now, cx);
 225        editor.end_transaction_at(now, cx);
 226        editor.undo(&Undo, cx);
 227        assert_eq!(editor.text(cx), "12cde6");
 228    });
 229}
 230
 231#[gpui::test]
 232fn test_ime_composition(cx: &mut TestAppContext) {
 233    init_test(cx, |_| {});
 234
 235    let buffer = cx.add_model(|cx| {
 236        let mut buffer = language::Buffer::new(0, "abcde", cx);
 237        // Ensure automatic grouping doesn't occur.
 238        buffer.set_group_interval(Duration::ZERO);
 239        buffer
 240    });
 241
 242    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
 243    cx.add_window(|cx| {
 244        let mut editor = build_editor(buffer.clone(), cx);
 245
 246        // Start a new IME composition.
 247        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
 248        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
 249        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
 250        assert_eq!(editor.text(cx), "äbcde");
 251        assert_eq!(
 252            editor.marked_text_ranges(cx),
 253            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
 254        );
 255
 256        // Finalize IME composition.
 257        editor.replace_text_in_range(None, "ā", cx);
 258        assert_eq!(editor.text(cx), "ābcde");
 259        assert_eq!(editor.marked_text_ranges(cx), None);
 260
 261        // IME composition edits are grouped and are undone/redone at once.
 262        editor.undo(&Default::default(), cx);
 263        assert_eq!(editor.text(cx), "abcde");
 264        assert_eq!(editor.marked_text_ranges(cx), None);
 265        editor.redo(&Default::default(), cx);
 266        assert_eq!(editor.text(cx), "ābcde");
 267        assert_eq!(editor.marked_text_ranges(cx), None);
 268
 269        // Start a new IME composition.
 270        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
 271        assert_eq!(
 272            editor.marked_text_ranges(cx),
 273            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
 274        );
 275
 276        // Undoing during an IME composition cancels it.
 277        editor.undo(&Default::default(), cx);
 278        assert_eq!(editor.text(cx), "ābcde");
 279        assert_eq!(editor.marked_text_ranges(cx), None);
 280
 281        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
 282        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
 283        assert_eq!(editor.text(cx), "ābcdè");
 284        assert_eq!(
 285            editor.marked_text_ranges(cx),
 286            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
 287        );
 288
 289        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
 290        editor.replace_text_in_range(Some(4..999), "ę", cx);
 291        assert_eq!(editor.text(cx), "ābcdę");
 292        assert_eq!(editor.marked_text_ranges(cx), None);
 293
 294        // Start a new IME composition with multiple cursors.
 295        editor.change_selections(None, cx, |s| {
 296            s.select_ranges([
 297                OffsetUtf16(1)..OffsetUtf16(1),
 298                OffsetUtf16(3)..OffsetUtf16(3),
 299                OffsetUtf16(5)..OffsetUtf16(5),
 300            ])
 301        });
 302        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
 303        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
 304        assert_eq!(
 305            editor.marked_text_ranges(cx),
 306            Some(vec![
 307                OffsetUtf16(0)..OffsetUtf16(3),
 308                OffsetUtf16(4)..OffsetUtf16(7),
 309                OffsetUtf16(8)..OffsetUtf16(11)
 310            ])
 311        );
 312
 313        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
 314        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
 315        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
 316        assert_eq!(
 317            editor.marked_text_ranges(cx),
 318            Some(vec![
 319                OffsetUtf16(1)..OffsetUtf16(2),
 320                OffsetUtf16(5)..OffsetUtf16(6),
 321                OffsetUtf16(9)..OffsetUtf16(10)
 322            ])
 323        );
 324
 325        // Finalize IME composition with multiple cursors.
 326        editor.replace_text_in_range(Some(9..10), "2", cx);
 327        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
 328        assert_eq!(editor.marked_text_ranges(cx), None);
 329
 330        editor
 331    });
 332}
 333
 334#[gpui::test]
 335fn test_selection_with_mouse(cx: &mut TestAppContext) {
 336    init_test(cx, |_| {});
 337
 338    let (_, editor) = cx.add_window(|cx| {
 339        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
 340        build_editor(buffer, cx)
 341    });
 342    editor.update(cx, |view, cx| {
 343        view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
 344    });
 345    assert_eq!(
 346        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 347        [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
 348    );
 349
 350    editor.update(cx, |view, cx| {
 351        view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
 352    });
 353
 354    assert_eq!(
 355        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 356        [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
 357    );
 358
 359    editor.update(cx, |view, cx| {
 360        view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
 361    });
 362
 363    assert_eq!(
 364        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 365        [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
 366    );
 367
 368    editor.update(cx, |view, cx| {
 369        view.end_selection(cx);
 370        view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
 371    });
 372
 373    assert_eq!(
 374        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 375        [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
 376    );
 377
 378    editor.update(cx, |view, cx| {
 379        view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
 380        view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx);
 381    });
 382
 383    assert_eq!(
 384        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 385        [
 386            DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
 387            DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
 388        ]
 389    );
 390
 391    editor.update(cx, |view, cx| {
 392        view.end_selection(cx);
 393    });
 394
 395    assert_eq!(
 396        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 397        [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
 398    );
 399}
 400
 401#[gpui::test]
 402fn test_canceling_pending_selection(cx: &mut TestAppContext) {
 403    init_test(cx, |_| {});
 404
 405    let (_, view) = cx.add_window(|cx| {
 406        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
 407        build_editor(buffer, cx)
 408    });
 409
 410    view.update(cx, |view, cx| {
 411        view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
 412        assert_eq!(
 413            view.selections.display_ranges(cx),
 414            [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
 415        );
 416    });
 417
 418    view.update(cx, |view, cx| {
 419        view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
 420        assert_eq!(
 421            view.selections.display_ranges(cx),
 422            [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
 423        );
 424    });
 425
 426    view.update(cx, |view, cx| {
 427        view.cancel(&Cancel, cx);
 428        view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
 429        assert_eq!(
 430            view.selections.display_ranges(cx),
 431            [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
 432        );
 433    });
 434}
 435
 436#[gpui::test]
 437fn test_clone(cx: &mut TestAppContext) {
 438    init_test(cx, |_| {});
 439
 440    let (text, selection_ranges) = marked_text_ranges(
 441        indoc! {"
 442            one
 443            two
 444            threeˇ
 445            four
 446            fiveˇ
 447        "},
 448        true,
 449    );
 450
 451    let (_, editor) = cx.add_window(|cx| {
 452        let buffer = MultiBuffer::build_simple(&text, cx);
 453        build_editor(buffer, cx)
 454    });
 455
 456    editor.update(cx, |editor, cx| {
 457        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
 458        editor.fold_ranges(
 459            [
 460                Point::new(1, 0)..Point::new(2, 0),
 461                Point::new(3, 0)..Point::new(4, 0),
 462            ],
 463            true,
 464            cx,
 465        );
 466    });
 467
 468    let (_, cloned_editor) = editor.update(cx, |editor, cx| {
 469        cx.add_window(Default::default(), |cx| editor.clone(cx))
 470    });
 471
 472    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx));
 473    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx));
 474
 475    assert_eq!(
 476        cloned_editor.update(cx, |e, cx| e.display_text(cx)),
 477        editor.update(cx, |e, cx| e.display_text(cx))
 478    );
 479    assert_eq!(
 480        cloned_snapshot
 481            .folds_in_range(0..text.len())
 482            .collect::<Vec<_>>(),
 483        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
 484    );
 485    assert_set_eq!(
 486        cloned_editor.read_with(cx, |editor, cx| editor.selections.ranges::<Point>(cx)),
 487        editor.read_with(cx, |editor, cx| editor.selections.ranges(cx))
 488    );
 489    assert_set_eq!(
 490        cloned_editor.update(cx, |e, cx| e.selections.display_ranges(cx)),
 491        editor.update(cx, |e, cx| e.selections.display_ranges(cx))
 492    );
 493}
 494
 495#[gpui::test]
 496async fn test_navigation_history(cx: &mut TestAppContext) {
 497    init_test(cx, |_| {});
 498
 499    cx.set_global(DragAndDrop::<Workspace>::default());
 500    use workspace::item::Item;
 501
 502    let fs = FakeFs::new(cx.background());
 503    let project = Project::test(fs, [], cx).await;
 504    let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
 505    let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
 506    cx.add_view(window_id, |cx| {
 507        let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
 508        let mut editor = build_editor(buffer.clone(), cx);
 509        let handle = cx.handle();
 510        editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
 511
 512        fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
 513            editor.nav_history.as_mut().unwrap().pop_backward(cx)
 514        }
 515
 516        // Move the cursor a small distance.
 517        // Nothing is added to the navigation history.
 518        editor.change_selections(None, cx, |s| {
 519            s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
 520        });
 521        editor.change_selections(None, cx, |s| {
 522            s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
 523        });
 524        assert!(pop_history(&mut editor, cx).is_none());
 525
 526        // Move the cursor a large distance.
 527        // The history can jump back to the previous position.
 528        editor.change_selections(None, cx, |s| {
 529            s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
 530        });
 531        let nav_entry = pop_history(&mut editor, cx).unwrap();
 532        editor.navigate(nav_entry.data.unwrap(), cx);
 533        assert_eq!(nav_entry.item.id(), cx.view_id());
 534        assert_eq!(
 535            editor.selections.display_ranges(cx),
 536            &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
 537        );
 538        assert!(pop_history(&mut editor, cx).is_none());
 539
 540        // Move the cursor a small distance via the mouse.
 541        // Nothing is added to the navigation history.
 542        editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
 543        editor.end_selection(cx);
 544        assert_eq!(
 545            editor.selections.display_ranges(cx),
 546            &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
 547        );
 548        assert!(pop_history(&mut editor, cx).is_none());
 549
 550        // Move the cursor a large distance via the mouse.
 551        // The history can jump back to the previous position.
 552        editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
 553        editor.end_selection(cx);
 554        assert_eq!(
 555            editor.selections.display_ranges(cx),
 556            &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
 557        );
 558        let nav_entry = pop_history(&mut editor, cx).unwrap();
 559        editor.navigate(nav_entry.data.unwrap(), cx);
 560        assert_eq!(nav_entry.item.id(), cx.view_id());
 561        assert_eq!(
 562            editor.selections.display_ranges(cx),
 563            &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
 564        );
 565        assert!(pop_history(&mut editor, cx).is_none());
 566
 567        // Set scroll position to check later
 568        editor.set_scroll_position(Vector2F::new(5.5, 5.5), cx);
 569        let original_scroll_position = editor.scroll_manager.anchor();
 570
 571        // Jump to the end of the document and adjust scroll
 572        editor.move_to_end(&MoveToEnd, cx);
 573        editor.set_scroll_position(Vector2F::new(-2.5, -0.5), cx);
 574        assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
 575
 576        let nav_entry = pop_history(&mut editor, cx).unwrap();
 577        editor.navigate(nav_entry.data.unwrap(), cx);
 578        assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
 579
 580        // Ensure we don't panic when navigation data contains invalid anchors *and* points.
 581        let mut invalid_anchor = editor.scroll_manager.anchor().top_anchor;
 582        invalid_anchor.text_anchor.buffer_id = Some(999);
 583        let invalid_point = Point::new(9999, 0);
 584        editor.navigate(
 585            Box::new(NavigationData {
 586                cursor_anchor: invalid_anchor,
 587                cursor_position: invalid_point,
 588                scroll_anchor: ScrollAnchor {
 589                    top_anchor: invalid_anchor,
 590                    offset: Default::default(),
 591                },
 592                scroll_top_row: invalid_point.row,
 593            }),
 594            cx,
 595        );
 596        assert_eq!(
 597            editor.selections.display_ranges(cx),
 598            &[editor.max_point(cx)..editor.max_point(cx)]
 599        );
 600        assert_eq!(
 601            editor.scroll_position(cx),
 602            vec2f(0., editor.max_point(cx).row() as f32)
 603        );
 604
 605        editor
 606    });
 607}
 608
 609#[gpui::test]
 610fn test_cancel(cx: &mut TestAppContext) {
 611    init_test(cx, |_| {});
 612
 613    let (_, view) = cx.add_window(|cx| {
 614        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
 615        build_editor(buffer, cx)
 616    });
 617
 618    view.update(cx, |view, cx| {
 619        view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
 620        view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
 621        view.end_selection(cx);
 622
 623        view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
 624        view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx);
 625        view.end_selection(cx);
 626        assert_eq!(
 627            view.selections.display_ranges(cx),
 628            [
 629                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
 630                DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
 631            ]
 632        );
 633    });
 634
 635    view.update(cx, |view, cx| {
 636        view.cancel(&Cancel, cx);
 637        assert_eq!(
 638            view.selections.display_ranges(cx),
 639            [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
 640        );
 641    });
 642
 643    view.update(cx, |view, cx| {
 644        view.cancel(&Cancel, cx);
 645        assert_eq!(
 646            view.selections.display_ranges(cx),
 647            [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
 648        );
 649    });
 650}
 651
 652#[gpui::test]
 653fn test_fold_action(cx: &mut TestAppContext) {
 654    init_test(cx, |_| {});
 655
 656    let (_, view) = cx.add_window(|cx| {
 657        let buffer = MultiBuffer::build_simple(
 658            &"
 659                impl Foo {
 660                    // Hello!
 661
 662                    fn a() {
 663                        1
 664                    }
 665
 666                    fn b() {
 667                        2
 668                    }
 669
 670                    fn c() {
 671                        3
 672                    }
 673                }
 674            "
 675            .unindent(),
 676            cx,
 677        );
 678        build_editor(buffer.clone(), cx)
 679    });
 680
 681    view.update(cx, |view, cx| {
 682        view.change_selections(None, cx, |s| {
 683            s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]);
 684        });
 685        view.fold(&Fold, cx);
 686        assert_eq!(
 687            view.display_text(cx),
 688            "
 689                impl Foo {
 690                    // Hello!
 691
 692                    fn a() {
 693                        1
 694                    }
 695
 696                    fn b() {⋯
 697                    }
 698
 699                    fn c() {⋯
 700                    }
 701                }
 702            "
 703            .unindent(),
 704        );
 705
 706        view.fold(&Fold, cx);
 707        assert_eq!(
 708            view.display_text(cx),
 709            "
 710                impl Foo {⋯
 711                }
 712            "
 713            .unindent(),
 714        );
 715
 716        view.unfold_lines(&UnfoldLines, cx);
 717        assert_eq!(
 718            view.display_text(cx),
 719            "
 720                impl Foo {
 721                    // Hello!
 722
 723                    fn a() {
 724                        1
 725                    }
 726
 727                    fn b() {⋯
 728                    }
 729
 730                    fn c() {⋯
 731                    }
 732                }
 733            "
 734            .unindent(),
 735        );
 736
 737        view.unfold_lines(&UnfoldLines, cx);
 738        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 739    });
 740}
 741
 742#[gpui::test]
 743fn test_move_cursor(cx: &mut TestAppContext) {
 744    init_test(cx, |_| {});
 745
 746    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 747    let (_, view) = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 748
 749    buffer.update(cx, |buffer, cx| {
 750        buffer.edit(
 751            vec![
 752                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 753                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 754            ],
 755            None,
 756            cx,
 757        );
 758    });
 759    view.update(cx, |view, cx| {
 760        assert_eq!(
 761            view.selections.display_ranges(cx),
 762            &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
 763        );
 764
 765        view.move_down(&MoveDown, cx);
 766        assert_eq!(
 767            view.selections.display_ranges(cx),
 768            &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
 769        );
 770
 771        view.move_right(&MoveRight, cx);
 772        assert_eq!(
 773            view.selections.display_ranges(cx),
 774            &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
 775        );
 776
 777        view.move_left(&MoveLeft, cx);
 778        assert_eq!(
 779            view.selections.display_ranges(cx),
 780            &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
 781        );
 782
 783        view.move_up(&MoveUp, cx);
 784        assert_eq!(
 785            view.selections.display_ranges(cx),
 786            &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
 787        );
 788
 789        view.move_to_end(&MoveToEnd, cx);
 790        assert_eq!(
 791            view.selections.display_ranges(cx),
 792            &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
 793        );
 794
 795        view.move_to_beginning(&MoveToBeginning, cx);
 796        assert_eq!(
 797            view.selections.display_ranges(cx),
 798            &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
 799        );
 800
 801        view.change_selections(None, cx, |s| {
 802            s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]);
 803        });
 804        view.select_to_beginning(&SelectToBeginning, cx);
 805        assert_eq!(
 806            view.selections.display_ranges(cx),
 807            &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
 808        );
 809
 810        view.select_to_end(&SelectToEnd, cx);
 811        assert_eq!(
 812            view.selections.display_ranges(cx),
 813            &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
 814        );
 815    });
 816}
 817
 818#[gpui::test]
 819fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 820    init_test(cx, |_| {});
 821
 822    let (_, view) = cx.add_window(|cx| {
 823        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
 824        build_editor(buffer.clone(), cx)
 825    });
 826
 827    assert_eq!('ⓐ'.len_utf8(), 3);
 828    assert_eq!('α'.len_utf8(), 2);
 829
 830    view.update(cx, |view, cx| {
 831        view.fold_ranges(
 832            vec![
 833                Point::new(0, 6)..Point::new(0, 12),
 834                Point::new(1, 2)..Point::new(1, 4),
 835                Point::new(2, 4)..Point::new(2, 8),
 836            ],
 837            true,
 838            cx,
 839        );
 840        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε\n");
 841
 842        view.move_right(&MoveRight, cx);
 843        assert_eq!(
 844            view.selections.display_ranges(cx),
 845            &[empty_range(0, "".len())]
 846        );
 847        view.move_right(&MoveRight, cx);
 848        assert_eq!(
 849            view.selections.display_ranges(cx),
 850            &[empty_range(0, "ⓐⓑ".len())]
 851        );
 852        view.move_right(&MoveRight, cx);
 853        assert_eq!(
 854            view.selections.display_ranges(cx),
 855            &[empty_range(0, "ⓐⓑ⋯".len())]
 856        );
 857
 858        view.move_down(&MoveDown, cx);
 859        assert_eq!(
 860            view.selections.display_ranges(cx),
 861            &[empty_range(1, "ab⋯".len())]
 862        );
 863        view.move_left(&MoveLeft, cx);
 864        assert_eq!(
 865            view.selections.display_ranges(cx),
 866            &[empty_range(1, "ab".len())]
 867        );
 868        view.move_left(&MoveLeft, cx);
 869        assert_eq!(
 870            view.selections.display_ranges(cx),
 871            &[empty_range(1, "a".len())]
 872        );
 873
 874        view.move_down(&MoveDown, cx);
 875        assert_eq!(
 876            view.selections.display_ranges(cx),
 877            &[empty_range(2, "α".len())]
 878        );
 879        view.move_right(&MoveRight, cx);
 880        assert_eq!(
 881            view.selections.display_ranges(cx),
 882            &[empty_range(2, "αβ".len())]
 883        );
 884        view.move_right(&MoveRight, cx);
 885        assert_eq!(
 886            view.selections.display_ranges(cx),
 887            &[empty_range(2, "αβ⋯".len())]
 888        );
 889        view.move_right(&MoveRight, cx);
 890        assert_eq!(
 891            view.selections.display_ranges(cx),
 892            &[empty_range(2, "αβ⋯ε".len())]
 893        );
 894
 895        view.move_up(&MoveUp, cx);
 896        assert_eq!(
 897            view.selections.display_ranges(cx),
 898            &[empty_range(1, "ab⋯e".len())]
 899        );
 900        view.move_up(&MoveUp, cx);
 901        assert_eq!(
 902            view.selections.display_ranges(cx),
 903            &[empty_range(0, "ⓐⓑ⋯ⓔ".len())]
 904        );
 905        view.move_left(&MoveLeft, cx);
 906        assert_eq!(
 907            view.selections.display_ranges(cx),
 908            &[empty_range(0, "ⓐⓑ⋯".len())]
 909        );
 910        view.move_left(&MoveLeft, cx);
 911        assert_eq!(
 912            view.selections.display_ranges(cx),
 913            &[empty_range(0, "ⓐⓑ".len())]
 914        );
 915        view.move_left(&MoveLeft, cx);
 916        assert_eq!(
 917            view.selections.display_ranges(cx),
 918            &[empty_range(0, "".len())]
 919        );
 920    });
 921}
 922
 923#[gpui::test]
 924fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 925    init_test(cx, |_| {});
 926
 927    let (_, view) = cx.add_window(|cx| {
 928        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 929        build_editor(buffer.clone(), cx)
 930    });
 931    view.update(cx, |view, cx| {
 932        view.change_selections(None, cx, |s| {
 933            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 934        });
 935        view.move_down(&MoveDown, cx);
 936        assert_eq!(
 937            view.selections.display_ranges(cx),
 938            &[empty_range(1, "abcd".len())]
 939        );
 940
 941        view.move_down(&MoveDown, cx);
 942        assert_eq!(
 943            view.selections.display_ranges(cx),
 944            &[empty_range(2, "αβγ".len())]
 945        );
 946
 947        view.move_down(&MoveDown, cx);
 948        assert_eq!(
 949            view.selections.display_ranges(cx),
 950            &[empty_range(3, "abcd".len())]
 951        );
 952
 953        view.move_down(&MoveDown, cx);
 954        assert_eq!(
 955            view.selections.display_ranges(cx),
 956            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 957        );
 958
 959        view.move_up(&MoveUp, cx);
 960        assert_eq!(
 961            view.selections.display_ranges(cx),
 962            &[empty_range(3, "abcd".len())]
 963        );
 964
 965        view.move_up(&MoveUp, cx);
 966        assert_eq!(
 967            view.selections.display_ranges(cx),
 968            &[empty_range(2, "αβγ".len())]
 969        );
 970    });
 971}
 972
 973#[gpui::test]
 974fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 975    init_test(cx, |_| {});
 976
 977    let (_, view) = cx.add_window(|cx| {
 978        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 979        build_editor(buffer, cx)
 980    });
 981    view.update(cx, |view, cx| {
 982        view.change_selections(None, cx, |s| {
 983            s.select_display_ranges([
 984                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
 985                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
 986            ]);
 987        });
 988    });
 989
 990    view.update(cx, |view, cx| {
 991        view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
 992        assert_eq!(
 993            view.selections.display_ranges(cx),
 994            &[
 995                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
 996                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
 997            ]
 998        );
 999    });
1000
1001    view.update(cx, |view, cx| {
1002        view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
1003        assert_eq!(
1004            view.selections.display_ranges(cx),
1005            &[
1006                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
1007                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
1008            ]
1009        );
1010    });
1011
1012    view.update(cx, |view, cx| {
1013        view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
1014        assert_eq!(
1015            view.selections.display_ranges(cx),
1016            &[
1017                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
1018                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
1019            ]
1020        );
1021    });
1022
1023    view.update(cx, |view, cx| {
1024        view.move_to_end_of_line(&MoveToEndOfLine, cx);
1025        assert_eq!(
1026            view.selections.display_ranges(cx),
1027            &[
1028                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
1029                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
1030            ]
1031        );
1032    });
1033
1034    // Moving to the end of line again is a no-op.
1035    view.update(cx, |view, cx| {
1036        view.move_to_end_of_line(&MoveToEndOfLine, cx);
1037        assert_eq!(
1038            view.selections.display_ranges(cx),
1039            &[
1040                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
1041                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
1042            ]
1043        );
1044    });
1045
1046    view.update(cx, |view, cx| {
1047        view.move_left(&MoveLeft, cx);
1048        view.select_to_beginning_of_line(
1049            &SelectToBeginningOfLine {
1050                stop_at_soft_wraps: true,
1051            },
1052            cx,
1053        );
1054        assert_eq!(
1055            view.selections.display_ranges(cx),
1056            &[
1057                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
1058                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
1059            ]
1060        );
1061    });
1062
1063    view.update(cx, |view, cx| {
1064        view.select_to_beginning_of_line(
1065            &SelectToBeginningOfLine {
1066                stop_at_soft_wraps: true,
1067            },
1068            cx,
1069        );
1070        assert_eq!(
1071            view.selections.display_ranges(cx),
1072            &[
1073                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
1074                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
1075            ]
1076        );
1077    });
1078
1079    view.update(cx, |view, cx| {
1080        view.select_to_beginning_of_line(
1081            &SelectToBeginningOfLine {
1082                stop_at_soft_wraps: true,
1083            },
1084            cx,
1085        );
1086        assert_eq!(
1087            view.selections.display_ranges(cx),
1088            &[
1089                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
1090                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
1091            ]
1092        );
1093    });
1094
1095    view.update(cx, |view, cx| {
1096        view.select_to_end_of_line(
1097            &SelectToEndOfLine {
1098                stop_at_soft_wraps: true,
1099            },
1100            cx,
1101        );
1102        assert_eq!(
1103            view.selections.display_ranges(cx),
1104            &[
1105                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
1106                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
1107            ]
1108        );
1109    });
1110
1111    view.update(cx, |view, cx| {
1112        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
1113        assert_eq!(view.display_text(cx), "ab\n  de");
1114        assert_eq!(
1115            view.selections.display_ranges(cx),
1116            &[
1117                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
1118                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
1119            ]
1120        );
1121    });
1122
1123    view.update(cx, |view, cx| {
1124        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
1125        assert_eq!(view.display_text(cx), "\n");
1126        assert_eq!(
1127            view.selections.display_ranges(cx),
1128            &[
1129                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
1130                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
1131            ]
1132        );
1133    });
1134}
1135
1136#[gpui::test]
1137fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
1138    init_test(cx, |_| {});
1139
1140    let (_, view) = cx.add_window(|cx| {
1141        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
1142        build_editor(buffer, cx)
1143    });
1144    view.update(cx, |view, cx| {
1145        view.change_selections(None, cx, |s| {
1146            s.select_display_ranges([
1147                DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
1148                DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
1149            ])
1150        });
1151
1152        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1153        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
1154
1155        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1156        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
1157
1158        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1159        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
1160
1161        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1162        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
1163
1164        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1165        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
1166
1167        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1168        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
1169
1170        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1171        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
1172
1173        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1174        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
1175
1176        view.move_right(&MoveRight, cx);
1177        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
1178        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
1179
1180        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
1181        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
1182
1183        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
1184        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
1185    });
1186}
1187
1188#[gpui::test]
1189fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
1190    init_test(cx, |_| {});
1191
1192    let (_, view) = cx.add_window(|cx| {
1193        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
1194        build_editor(buffer, cx)
1195    });
1196
1197    view.update(cx, |view, cx| {
1198        view.set_wrap_width(Some(140.), cx);
1199        assert_eq!(
1200            view.display_text(cx),
1201            "use one::{\n    two::three::\n    four::five\n};"
1202        );
1203
1204        view.change_selections(None, cx, |s| {
1205            s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]);
1206        });
1207
1208        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1209        assert_eq!(
1210            view.selections.display_ranges(cx),
1211            &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
1212        );
1213
1214        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1215        assert_eq!(
1216            view.selections.display_ranges(cx),
1217            &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
1218        );
1219
1220        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1221        assert_eq!(
1222            view.selections.display_ranges(cx),
1223            &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
1224        );
1225
1226        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1227        assert_eq!(
1228            view.selections.display_ranges(cx),
1229            &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
1230        );
1231
1232        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1233        assert_eq!(
1234            view.selections.display_ranges(cx),
1235            &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
1236        );
1237
1238        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1239        assert_eq!(
1240            view.selections.display_ranges(cx),
1241            &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
1242        );
1243    });
1244}
1245
1246#[gpui::test]
1247async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
1248    init_test(cx, |_| {});
1249    let mut cx = EditorTestContext::new(cx);
1250
1251    let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
1252    cx.simulate_window_resize(cx.window_id, vec2f(100., 4. * line_height));
1253
1254    cx.set_state(
1255        &r#"
1256        ˇone
1257        two
1258        threeˇ
1259        four
1260        five
1261        six
1262        seven
1263        eight
1264        nine
1265        ten
1266        "#
1267        .unindent(),
1268    );
1269
1270    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
1271    cx.assert_editor_state(
1272        &r#"
1273        one
1274        two
1275        three
1276        ˇfour
1277        five
1278        sixˇ
1279        seven
1280        eight
1281        nine
1282        ten
1283        "#
1284        .unindent(),
1285    );
1286
1287    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
1288    cx.assert_editor_state(
1289        &r#"
1290        one
1291        two
1292        three
1293        four
1294        five
1295        six
1296        ˇseven
1297        eight
1298        nineˇ
1299        ten
1300        "#
1301        .unindent(),
1302    );
1303
1304    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
1305    cx.assert_editor_state(
1306        &r#"
1307        one
1308        two
1309        three
1310        ˇfour
1311        five
1312        sixˇ
1313        seven
1314        eight
1315        nine
1316        ten
1317        "#
1318        .unindent(),
1319    );
1320
1321    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
1322    cx.assert_editor_state(
1323        &r#"
1324        ˇone
1325        two
1326        threeˇ
1327        four
1328        five
1329        six
1330        seven
1331        eight
1332        nine
1333        ten
1334        "#
1335        .unindent(),
1336    );
1337
1338    // Test select collapsing
1339    cx.update_editor(|editor, cx| {
1340        editor.move_page_down(&MovePageDown::default(), cx);
1341        editor.move_page_down(&MovePageDown::default(), cx);
1342        editor.move_page_down(&MovePageDown::default(), cx);
1343    });
1344    cx.assert_editor_state(
1345        &r#"
1346        one
1347        two
1348        three
1349        four
1350        five
1351        six
1352        seven
1353        eight
1354        nine
1355        ˇten
1356        ˇ"#
1357        .unindent(),
1358    );
1359}
1360
1361#[gpui::test]
1362async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
1363    init_test(cx, |_| {});
1364    let mut cx = EditorTestContext::new(cx);
1365    cx.set_state("one «two threeˇ» four");
1366    cx.update_editor(|editor, cx| {
1367        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
1368        assert_eq!(editor.text(cx), " four");
1369    });
1370}
1371
1372#[gpui::test]
1373fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
1374    init_test(cx, |_| {});
1375
1376    let (_, view) = cx.add_window(|cx| {
1377        let buffer = MultiBuffer::build_simple("one two three four", cx);
1378        build_editor(buffer.clone(), cx)
1379    });
1380
1381    view.update(cx, |view, cx| {
1382        view.change_selections(None, cx, |s| {
1383            s.select_display_ranges([
1384                // an empty selection - the preceding word fragment is deleted
1385                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
1386                // characters selected - they are deleted
1387                DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
1388            ])
1389        });
1390        view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
1391        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
1392    });
1393
1394    view.update(cx, |view, cx| {
1395        view.change_selections(None, cx, |s| {
1396            s.select_display_ranges([
1397                // an empty selection - the following word fragment is deleted
1398                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
1399                // characters selected - they are deleted
1400                DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
1401            ])
1402        });
1403        view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
1404        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
1405    });
1406}
1407
1408#[gpui::test]
1409fn test_newline(cx: &mut TestAppContext) {
1410    init_test(cx, |_| {});
1411
1412    let (_, view) = cx.add_window(|cx| {
1413        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
1414        build_editor(buffer.clone(), cx)
1415    });
1416
1417    view.update(cx, |view, cx| {
1418        view.change_selections(None, cx, |s| {
1419            s.select_display_ranges([
1420                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
1421                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
1422                DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
1423            ])
1424        });
1425
1426        view.newline(&Newline, cx);
1427        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
1428    });
1429}
1430
1431#[gpui::test]
1432fn test_newline_with_old_selections(cx: &mut TestAppContext) {
1433    init_test(cx, |_| {});
1434
1435    let (_, editor) = cx.add_window(|cx| {
1436        let buffer = MultiBuffer::build_simple(
1437            "
1438                a
1439                b(
1440                    X
1441                )
1442                c(
1443                    X
1444                )
1445            "
1446            .unindent()
1447            .as_str(),
1448            cx,
1449        );
1450        let mut editor = build_editor(buffer.clone(), cx);
1451        editor.change_selections(None, cx, |s| {
1452            s.select_ranges([
1453                Point::new(2, 4)..Point::new(2, 5),
1454                Point::new(5, 4)..Point::new(5, 5),
1455            ])
1456        });
1457        editor
1458    });
1459
1460    editor.update(cx, |editor, cx| {
1461        // Edit the buffer directly, deleting ranges surrounding the editor's selections
1462        editor.buffer.update(cx, |buffer, cx| {
1463            buffer.edit(
1464                [
1465                    (Point::new(1, 2)..Point::new(3, 0), ""),
1466                    (Point::new(4, 2)..Point::new(6, 0), ""),
1467                ],
1468                None,
1469                cx,
1470            );
1471            assert_eq!(
1472                buffer.read(cx).text(),
1473                "
1474                    a
1475                    b()
1476                    c()
1477                "
1478                .unindent()
1479            );
1480        });
1481        assert_eq!(
1482            editor.selections.ranges(cx),
1483            &[
1484                Point::new(1, 2)..Point::new(1, 2),
1485                Point::new(2, 2)..Point::new(2, 2),
1486            ],
1487        );
1488
1489        editor.newline(&Newline, cx);
1490        assert_eq!(
1491            editor.text(cx),
1492            "
1493                a
1494                b(
1495                )
1496                c(
1497                )
1498            "
1499            .unindent()
1500        );
1501
1502        // The selections are moved after the inserted newlines
1503        assert_eq!(
1504            editor.selections.ranges(cx),
1505            &[
1506                Point::new(2, 0)..Point::new(2, 0),
1507                Point::new(4, 0)..Point::new(4, 0),
1508            ],
1509        );
1510    });
1511}
1512
1513#[gpui::test]
1514async fn test_newline_above(cx: &mut gpui::TestAppContext) {
1515    init_test(cx, |settings| {
1516        settings.defaults.tab_size = NonZeroU32::new(4)
1517    });
1518
1519    let language = Arc::new(
1520        Language::new(
1521            LanguageConfig::default(),
1522            Some(tree_sitter_rust::language()),
1523        )
1524        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
1525        .unwrap(),
1526    );
1527
1528    let mut cx = EditorTestContext::new(cx);
1529    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1530    cx.set_state(indoc! {"
1531        const a: ˇA = (
15321533                «const_functionˇ»(ˇ),
1534                so«mˇ»et«hˇ»ing_ˇelse,ˇ
15351536        ˇ);ˇ
1537    "});
1538
1539    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
1540    cx.assert_editor_state(indoc! {"
1541        ˇ
1542        const a: A = (
1543            ˇ
1544            (
1545                ˇ
1546                ˇ
1547                const_function(),
1548                ˇ
1549                ˇ
1550                ˇ
1551                ˇ
1552                something_else,
1553                ˇ
1554            )
1555            ˇ
1556            ˇ
1557        );
1558    "});
1559}
1560
1561#[gpui::test]
1562async fn test_newline_below(cx: &mut gpui::TestAppContext) {
1563    init_test(cx, |settings| {
1564        settings.defaults.tab_size = NonZeroU32::new(4)
1565    });
1566
1567    let language = Arc::new(
1568        Language::new(
1569            LanguageConfig::default(),
1570            Some(tree_sitter_rust::language()),
1571        )
1572        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
1573        .unwrap(),
1574    );
1575
1576    let mut cx = EditorTestContext::new(cx);
1577    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1578    cx.set_state(indoc! {"
1579        const a: ˇA = (
15801581                «const_functionˇ»(ˇ),
1582                so«mˇ»et«hˇ»ing_ˇelse,ˇ
15831584        ˇ);ˇ
1585    "});
1586
1587    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
1588    cx.assert_editor_state(indoc! {"
1589        const a: A = (
1590            ˇ
1591            (
1592                ˇ
1593                const_function(),
1594                ˇ
1595                ˇ
1596                something_else,
1597                ˇ
1598                ˇ
1599                ˇ
1600                ˇ
1601            )
1602            ˇ
1603        );
1604        ˇ
1605        ˇ
1606    "});
1607}
1608
1609#[gpui::test]
1610fn test_insert_with_old_selections(cx: &mut TestAppContext) {
1611    init_test(cx, |_| {});
1612
1613    let (_, editor) = cx.add_window(|cx| {
1614        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
1615        let mut editor = build_editor(buffer.clone(), cx);
1616        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
1617        editor
1618    });
1619
1620    editor.update(cx, |editor, cx| {
1621        // Edit the buffer directly, deleting ranges surrounding the editor's selections
1622        editor.buffer.update(cx, |buffer, cx| {
1623            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
1624            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
1625        });
1626        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
1627
1628        editor.insert("Z", cx);
1629        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
1630
1631        // The selections are moved after the inserted characters
1632        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
1633    });
1634}
1635
1636#[gpui::test]
1637async fn test_tab(cx: &mut gpui::TestAppContext) {
1638    init_test(cx, |settings| {
1639        settings.defaults.tab_size = NonZeroU32::new(3)
1640    });
1641
1642    let mut cx = EditorTestContext::new(cx);
1643    cx.set_state(indoc! {"
1644        ˇabˇc
1645        ˇ🏀ˇ🏀ˇefg
16461647    "});
1648    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1649    cx.assert_editor_state(indoc! {"
1650           ˇab ˇc
1651           ˇ🏀  ˇ🏀  ˇefg
1652        d  ˇ
1653    "});
1654
1655    cx.set_state(indoc! {"
1656        a
1657        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
1658    "});
1659    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1660    cx.assert_editor_state(indoc! {"
1661        a
1662           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
1663    "});
1664}
1665
1666#[gpui::test]
1667async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
1668    init_test(cx, |_| {});
1669
1670    let mut cx = EditorTestContext::new(cx);
1671    let language = Arc::new(
1672        Language::new(
1673            LanguageConfig::default(),
1674            Some(tree_sitter_rust::language()),
1675        )
1676        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
1677        .unwrap(),
1678    );
1679    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1680
1681    // cursors that are already at the suggested indent level insert
1682    // a soft tab. cursors that are to the left of the suggested indent
1683    // auto-indent their line.
1684    cx.set_state(indoc! {"
1685        ˇ
1686        const a: B = (
1687            c(
1688                d(
1689        ˇ
1690                )
1691        ˇ
1692        ˇ    )
1693        );
1694    "});
1695    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1696    cx.assert_editor_state(indoc! {"
1697            ˇ
1698        const a: B = (
1699            c(
1700                d(
1701                    ˇ
1702                )
1703                ˇ
1704            ˇ)
1705        );
1706    "});
1707
1708    // handle auto-indent when there are multiple cursors on the same line
1709    cx.set_state(indoc! {"
1710        const a: B = (
1711            c(
1712        ˇ    ˇ
1713        ˇ    )
1714        );
1715    "});
1716    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1717    cx.assert_editor_state(indoc! {"
1718        const a: B = (
1719            c(
1720                ˇ
1721            ˇ)
1722        );
1723    "});
1724}
1725
1726#[gpui::test]
1727async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
1728    init_test(cx, |settings| {
1729        settings.defaults.tab_size = NonZeroU32::new(4)
1730    });
1731
1732    let language = Arc::new(
1733        Language::new(
1734            LanguageConfig::default(),
1735            Some(tree_sitter_rust::language()),
1736        )
1737        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
1738        .unwrap(),
1739    );
1740
1741    let mut cx = EditorTestContext::new(cx);
1742    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1743    cx.set_state(indoc! {"
1744        fn a() {
1745            if b {
1746        \t ˇc
1747            }
1748        }
1749    "});
1750
1751    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1752    cx.assert_editor_state(indoc! {"
1753        fn a() {
1754            if b {
1755                ˇc
1756            }
1757        }
1758    "});
1759}
1760
1761#[gpui::test]
1762async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
1763    init_test(cx, |settings| {
1764        settings.defaults.tab_size = NonZeroU32::new(4);
1765    });
1766
1767    let mut cx = EditorTestContext::new(cx);
1768
1769    cx.set_state(indoc! {"
1770          «oneˇ» «twoˇ»
1771        three
1772         four
1773    "});
1774    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1775    cx.assert_editor_state(indoc! {"
1776            «oneˇ» «twoˇ»
1777        three
1778         four
1779    "});
1780
1781    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1782    cx.assert_editor_state(indoc! {"
1783        «oneˇ» «twoˇ»
1784        three
1785         four
1786    "});
1787
1788    // select across line ending
1789    cx.set_state(indoc! {"
1790        one two
1791        t«hree
1792        ˇ» four
1793    "});
1794    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1795    cx.assert_editor_state(indoc! {"
1796        one two
1797            t«hree
1798        ˇ» four
1799    "});
1800
1801    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1802    cx.assert_editor_state(indoc! {"
1803        one two
1804        t«hree
1805        ˇ» four
1806    "});
1807
1808    // Ensure that indenting/outdenting works when the cursor is at column 0.
1809    cx.set_state(indoc! {"
1810        one two
1811        ˇthree
1812            four
1813    "});
1814    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1815    cx.assert_editor_state(indoc! {"
1816        one two
1817            ˇthree
1818            four
1819    "});
1820
1821    cx.set_state(indoc! {"
1822        one two
1823        ˇ    three
1824            four
1825    "});
1826    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1827    cx.assert_editor_state(indoc! {"
1828        one two
1829        ˇthree
1830            four
1831    "});
1832}
1833
1834#[gpui::test]
1835async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
1836    init_test(cx, |settings| {
1837        settings.defaults.hard_tabs = Some(true);
1838    });
1839
1840    let mut cx = EditorTestContext::new(cx);
1841
1842    // select two ranges on one line
1843    cx.set_state(indoc! {"
1844        «oneˇ» «twoˇ»
1845        three
1846        four
1847    "});
1848    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1849    cx.assert_editor_state(indoc! {"
1850        \t«oneˇ» «twoˇ»
1851        three
1852        four
1853    "});
1854    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1855    cx.assert_editor_state(indoc! {"
1856        \t\t«oneˇ» «twoˇ»
1857        three
1858        four
1859    "});
1860    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1861    cx.assert_editor_state(indoc! {"
1862        \t«oneˇ» «twoˇ»
1863        three
1864        four
1865    "});
1866    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1867    cx.assert_editor_state(indoc! {"
1868        «oneˇ» «twoˇ»
1869        three
1870        four
1871    "});
1872
1873    // select across a line ending
1874    cx.set_state(indoc! {"
1875        one two
1876        t«hree
1877        ˇ»four
1878    "});
1879    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1880    cx.assert_editor_state(indoc! {"
1881        one two
1882        \tt«hree
1883        ˇ»four
1884    "});
1885    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1886    cx.assert_editor_state(indoc! {"
1887        one two
1888        \t\tt«hree
1889        ˇ»four
1890    "});
1891    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1892    cx.assert_editor_state(indoc! {"
1893        one two
1894        \tt«hree
1895        ˇ»four
1896    "});
1897    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1898    cx.assert_editor_state(indoc! {"
1899        one two
1900        t«hree
1901        ˇ»four
1902    "});
1903
1904    // Ensure that indenting/outdenting works when the cursor is at column 0.
1905    cx.set_state(indoc! {"
1906        one two
1907        ˇthree
1908        four
1909    "});
1910    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1911    cx.assert_editor_state(indoc! {"
1912        one two
1913        ˇthree
1914        four
1915    "});
1916    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1917    cx.assert_editor_state(indoc! {"
1918        one two
1919        \tˇthree
1920        four
1921    "});
1922    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1923    cx.assert_editor_state(indoc! {"
1924        one two
1925        ˇthree
1926        four
1927    "});
1928}
1929
1930#[gpui::test]
1931fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
1932    init_test(cx, |settings| {
1933        settings.languages.extend([
1934            (
1935                "TOML".into(),
1936                LanguageSettingsContent {
1937                    tab_size: NonZeroU32::new(2),
1938                    ..Default::default()
1939                },
1940            ),
1941            (
1942                "Rust".into(),
1943                LanguageSettingsContent {
1944                    tab_size: NonZeroU32::new(4),
1945                    ..Default::default()
1946                },
1947            ),
1948        ]);
1949    });
1950
1951    let toml_language = Arc::new(Language::new(
1952        LanguageConfig {
1953            name: "TOML".into(),
1954            ..Default::default()
1955        },
1956        None,
1957    ));
1958    let rust_language = Arc::new(Language::new(
1959        LanguageConfig {
1960            name: "Rust".into(),
1961            ..Default::default()
1962        },
1963        None,
1964    ));
1965
1966    let toml_buffer =
1967        cx.add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx));
1968    let rust_buffer = cx.add_model(|cx| {
1969        Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx)
1970    });
1971    let multibuffer = cx.add_model(|cx| {
1972        let mut multibuffer = MultiBuffer::new(0);
1973        multibuffer.push_excerpts(
1974            toml_buffer.clone(),
1975            [ExcerptRange {
1976                context: Point::new(0, 0)..Point::new(2, 0),
1977                primary: None,
1978            }],
1979            cx,
1980        );
1981        multibuffer.push_excerpts(
1982            rust_buffer.clone(),
1983            [ExcerptRange {
1984                context: Point::new(0, 0)..Point::new(1, 0),
1985                primary: None,
1986            }],
1987            cx,
1988        );
1989        multibuffer
1990    });
1991
1992    cx.add_window(|cx| {
1993        let mut editor = build_editor(multibuffer, cx);
1994
1995        assert_eq!(
1996            editor.text(cx),
1997            indoc! {"
1998                a = 1
1999                b = 2
2000
2001                const c: usize = 3;
2002            "}
2003        );
2004
2005        select_ranges(
2006            &mut editor,
2007            indoc! {"
2008                «aˇ» = 1
2009                b = 2
2010
2011                «const c:ˇ» usize = 3;
2012            "},
2013            cx,
2014        );
2015
2016        editor.tab(&Tab, cx);
2017        assert_text_with_selections(
2018            &mut editor,
2019            indoc! {"
2020                  «aˇ» = 1
2021                b = 2
2022
2023                    «const c:ˇ» usize = 3;
2024            "},
2025            cx,
2026        );
2027        editor.tab_prev(&TabPrev, cx);
2028        assert_text_with_selections(
2029            &mut editor,
2030            indoc! {"
2031                «aˇ» = 1
2032                b = 2
2033
2034                «const c:ˇ» usize = 3;
2035            "},
2036            cx,
2037        );
2038
2039        editor
2040    });
2041}
2042
2043#[gpui::test]
2044async fn test_backspace(cx: &mut gpui::TestAppContext) {
2045    init_test(cx, |_| {});
2046
2047    let mut cx = EditorTestContext::new(cx);
2048
2049    // Basic backspace
2050    cx.set_state(indoc! {"
2051        onˇe two three
2052        fou«rˇ» five six
2053        seven «ˇeight nine
2054        »ten
2055    "});
2056    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2057    cx.assert_editor_state(indoc! {"
2058        oˇe two three
2059        fouˇ five six
2060        seven ˇten
2061    "});
2062
2063    // Test backspace inside and around indents
2064    cx.set_state(indoc! {"
2065        zero
2066            ˇone
2067                ˇtwo
2068            ˇ ˇ ˇ  three
2069        ˇ  ˇ  four
2070    "});
2071    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2072    cx.assert_editor_state(indoc! {"
2073        zero
2074        ˇone
2075            ˇtwo
2076        ˇ  threeˇ  four
2077    "});
2078
2079    // Test backspace with line_mode set to true
2080    cx.update_editor(|e, _| e.selections.line_mode = true);
2081    cx.set_state(indoc! {"
2082        The ˇquick ˇbrown
2083        fox jumps over
2084        the lazy dog
2085        ˇThe qu«ick bˇ»rown"});
2086    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2087    cx.assert_editor_state(indoc! {"
2088        ˇfox jumps over
2089        the lazy dogˇ"});
2090}
2091
2092#[gpui::test]
2093async fn test_delete(cx: &mut gpui::TestAppContext) {
2094    init_test(cx, |_| {});
2095
2096    let mut cx = EditorTestContext::new(cx);
2097    cx.set_state(indoc! {"
2098        onˇe two three
2099        fou«rˇ» five six
2100        seven «ˇeight nine
2101        »ten
2102    "});
2103    cx.update_editor(|e, cx| e.delete(&Delete, cx));
2104    cx.assert_editor_state(indoc! {"
2105        onˇ two three
2106        fouˇ five six
2107        seven ˇten
2108    "});
2109
2110    // Test backspace with line_mode set to true
2111    cx.update_editor(|e, _| e.selections.line_mode = true);
2112    cx.set_state(indoc! {"
2113        The ˇquick ˇbrown
2114        fox «ˇjum»ps over
2115        the lazy dog
2116        ˇThe qu«ick bˇ»rown"});
2117    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2118    cx.assert_editor_state("ˇthe lazy dogˇ");
2119}
2120
2121#[gpui::test]
2122fn test_delete_line(cx: &mut TestAppContext) {
2123    init_test(cx, |_| {});
2124
2125    let (_, view) = cx.add_window(|cx| {
2126        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2127        build_editor(buffer, cx)
2128    });
2129    view.update(cx, |view, cx| {
2130        view.change_selections(None, cx, |s| {
2131            s.select_display_ranges([
2132                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2133                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
2134                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2135            ])
2136        });
2137        view.delete_line(&DeleteLine, cx);
2138        assert_eq!(view.display_text(cx), "ghi");
2139        assert_eq!(
2140            view.selections.display_ranges(cx),
2141            vec![
2142                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
2143                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
2144            ]
2145        );
2146    });
2147
2148    let (_, view) = cx.add_window(|cx| {
2149        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2150        build_editor(buffer, cx)
2151    });
2152    view.update(cx, |view, cx| {
2153        view.change_selections(None, cx, |s| {
2154            s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
2155        });
2156        view.delete_line(&DeleteLine, cx);
2157        assert_eq!(view.display_text(cx), "ghi\n");
2158        assert_eq!(
2159            view.selections.display_ranges(cx),
2160            vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
2161        );
2162    });
2163}
2164
2165#[gpui::test]
2166fn test_duplicate_line(cx: &mut TestAppContext) {
2167    init_test(cx, |_| {});
2168
2169    let (_, view) = cx.add_window(|cx| {
2170        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2171        build_editor(buffer, cx)
2172    });
2173    view.update(cx, |view, cx| {
2174        view.change_selections(None, cx, |s| {
2175            s.select_display_ranges([
2176                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2177                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2178                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2179                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2180            ])
2181        });
2182        view.duplicate_line(&DuplicateLine, cx);
2183        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
2184        assert_eq!(
2185            view.selections.display_ranges(cx),
2186            vec![
2187                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
2188                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
2189                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2190                DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
2191            ]
2192        );
2193    });
2194
2195    let (_, view) = cx.add_window(|cx| {
2196        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2197        build_editor(buffer, cx)
2198    });
2199    view.update(cx, |view, cx| {
2200        view.change_selections(None, cx, |s| {
2201            s.select_display_ranges([
2202                DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
2203                DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
2204            ])
2205        });
2206        view.duplicate_line(&DuplicateLine, cx);
2207        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
2208        assert_eq!(
2209            view.selections.display_ranges(cx),
2210            vec![
2211                DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
2212                DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
2213            ]
2214        );
2215    });
2216}
2217
2218#[gpui::test]
2219fn test_move_line_up_down(cx: &mut TestAppContext) {
2220    init_test(cx, |_| {});
2221
2222    let (_, view) = cx.add_window(|cx| {
2223        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
2224        build_editor(buffer, cx)
2225    });
2226    view.update(cx, |view, cx| {
2227        view.fold_ranges(
2228            vec![
2229                Point::new(0, 2)..Point::new(1, 2),
2230                Point::new(2, 3)..Point::new(4, 1),
2231                Point::new(7, 0)..Point::new(8, 4),
2232            ],
2233            true,
2234            cx,
2235        );
2236        view.change_selections(None, cx, |s| {
2237            s.select_display_ranges([
2238                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2239                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2240                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2241                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
2242            ])
2243        });
2244        assert_eq!(
2245            view.display_text(cx),
2246            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
2247        );
2248
2249        view.move_line_up(&MoveLineUp, cx);
2250        assert_eq!(
2251            view.display_text(cx),
2252            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
2253        );
2254        assert_eq!(
2255            view.selections.display_ranges(cx),
2256            vec![
2257                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2258                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2259                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
2260                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
2261            ]
2262        );
2263    });
2264
2265    view.update(cx, |view, cx| {
2266        view.move_line_down(&MoveLineDown, cx);
2267        assert_eq!(
2268            view.display_text(cx),
2269            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
2270        );
2271        assert_eq!(
2272            view.selections.display_ranges(cx),
2273            vec![
2274                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
2275                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2276                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2277                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
2278            ]
2279        );
2280    });
2281
2282    view.update(cx, |view, cx| {
2283        view.move_line_down(&MoveLineDown, cx);
2284        assert_eq!(
2285            view.display_text(cx),
2286            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
2287        );
2288        assert_eq!(
2289            view.selections.display_ranges(cx),
2290            vec![
2291                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2292                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2293                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2294                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
2295            ]
2296        );
2297    });
2298
2299    view.update(cx, |view, cx| {
2300        view.move_line_up(&MoveLineUp, cx);
2301        assert_eq!(
2302            view.display_text(cx),
2303            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
2304        );
2305        assert_eq!(
2306            view.selections.display_ranges(cx),
2307            vec![
2308                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
2309                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2310                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
2311                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
2312            ]
2313        );
2314    });
2315}
2316
2317#[gpui::test]
2318fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
2319    init_test(cx, |_| {});
2320
2321    let (_, editor) = cx.add_window(|cx| {
2322        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
2323        build_editor(buffer, cx)
2324    });
2325    editor.update(cx, |editor, cx| {
2326        let snapshot = editor.buffer.read(cx).snapshot(cx);
2327        editor.insert_blocks(
2328            [BlockProperties {
2329                style: BlockStyle::Fixed,
2330                position: snapshot.anchor_after(Point::new(2, 0)),
2331                disposition: BlockDisposition::Below,
2332                height: 1,
2333                render: Arc::new(|_| Empty::new().into_any()),
2334            }],
2335            cx,
2336        );
2337        editor.change_selections(None, cx, |s| {
2338            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
2339        });
2340        editor.move_line_down(&MoveLineDown, cx);
2341    });
2342}
2343
2344#[gpui::test]
2345fn test_transpose(cx: &mut TestAppContext) {
2346    init_test(cx, |_| {});
2347
2348    _ = cx
2349        .add_window(|cx| {
2350            let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
2351
2352            editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
2353            editor.transpose(&Default::default(), cx);
2354            assert_eq!(editor.text(cx), "bac");
2355            assert_eq!(editor.selections.ranges(cx), [2..2]);
2356
2357            editor.transpose(&Default::default(), cx);
2358            assert_eq!(editor.text(cx), "bca");
2359            assert_eq!(editor.selections.ranges(cx), [3..3]);
2360
2361            editor.transpose(&Default::default(), cx);
2362            assert_eq!(editor.text(cx), "bac");
2363            assert_eq!(editor.selections.ranges(cx), [3..3]);
2364
2365            editor
2366        })
2367        .1;
2368
2369    _ = cx
2370        .add_window(|cx| {
2371            let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
2372
2373            editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
2374            editor.transpose(&Default::default(), cx);
2375            assert_eq!(editor.text(cx), "acb\nde");
2376            assert_eq!(editor.selections.ranges(cx), [3..3]);
2377
2378            editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
2379            editor.transpose(&Default::default(), cx);
2380            assert_eq!(editor.text(cx), "acbd\ne");
2381            assert_eq!(editor.selections.ranges(cx), [5..5]);
2382
2383            editor.transpose(&Default::default(), cx);
2384            assert_eq!(editor.text(cx), "acbde\n");
2385            assert_eq!(editor.selections.ranges(cx), [6..6]);
2386
2387            editor.transpose(&Default::default(), cx);
2388            assert_eq!(editor.text(cx), "acbd\ne");
2389            assert_eq!(editor.selections.ranges(cx), [6..6]);
2390
2391            editor
2392        })
2393        .1;
2394
2395    _ = cx
2396        .add_window(|cx| {
2397            let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
2398
2399            editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
2400            editor.transpose(&Default::default(), cx);
2401            assert_eq!(editor.text(cx), "bacd\ne");
2402            assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
2403
2404            editor.transpose(&Default::default(), cx);
2405            assert_eq!(editor.text(cx), "bcade\n");
2406            assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
2407
2408            editor.transpose(&Default::default(), cx);
2409            assert_eq!(editor.text(cx), "bcda\ne");
2410            assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
2411
2412            editor.transpose(&Default::default(), cx);
2413            assert_eq!(editor.text(cx), "bcade\n");
2414            assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
2415
2416            editor.transpose(&Default::default(), cx);
2417            assert_eq!(editor.text(cx), "bcaed\n");
2418            assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
2419
2420            editor
2421        })
2422        .1;
2423
2424    _ = cx
2425        .add_window(|cx| {
2426            let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
2427
2428            editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
2429            editor.transpose(&Default::default(), cx);
2430            assert_eq!(editor.text(cx), "🏀🍐✋");
2431            assert_eq!(editor.selections.ranges(cx), [8..8]);
2432
2433            editor.transpose(&Default::default(), cx);
2434            assert_eq!(editor.text(cx), "🏀✋🍐");
2435            assert_eq!(editor.selections.ranges(cx), [11..11]);
2436
2437            editor.transpose(&Default::default(), cx);
2438            assert_eq!(editor.text(cx), "🏀🍐✋");
2439            assert_eq!(editor.selections.ranges(cx), [11..11]);
2440
2441            editor
2442        })
2443        .1;
2444}
2445
2446#[gpui::test]
2447async fn test_clipboard(cx: &mut gpui::TestAppContext) {
2448    init_test(cx, |_| {});
2449
2450    let mut cx = EditorTestContext::new(cx);
2451
2452    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
2453    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2454    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
2455
2456    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
2457    cx.set_state("two ˇfour ˇsix ˇ");
2458    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2459    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
2460
2461    // Paste again but with only two cursors. Since the number of cursors doesn't
2462    // match the number of slices in the clipboard, the entire clipboard text
2463    // is pasted at each cursor.
2464    cx.set_state("ˇtwo one✅ four three six five ˇ");
2465    cx.update_editor(|e, cx| {
2466        e.handle_input("( ", cx);
2467        e.paste(&Paste, cx);
2468        e.handle_input(") ", cx);
2469    });
2470    cx.assert_editor_state(
2471        &([
2472            "( one✅ ",
2473            "three ",
2474            "five ) ˇtwo one✅ four three six five ( one✅ ",
2475            "three ",
2476            "five ) ˇ",
2477        ]
2478        .join("\n")),
2479    );
2480
2481    // Cut with three selections, one of which is full-line.
2482    cx.set_state(indoc! {"
2483        1«2ˇ»3
2484        4ˇ567
2485        «8ˇ»9"});
2486    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2487    cx.assert_editor_state(indoc! {"
2488        1ˇ3
2489        ˇ9"});
2490
2491    // Paste with three selections, noticing how the copied selection that was full-line
2492    // gets inserted before the second cursor.
2493    cx.set_state(indoc! {"
2494        1ˇ3
24952496        «oˇ»ne"});
2497    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2498    cx.assert_editor_state(indoc! {"
2499        12ˇ3
2500        4567
25012502        8ˇne"});
2503
2504    // Copy with a single cursor only, which writes the whole line into the clipboard.
2505    cx.set_state(indoc! {"
2506        The quick brown
2507        fox juˇmps over
2508        the lazy dog"});
2509    cx.update_editor(|e, cx| e.copy(&Copy, cx));
2510    cx.cx.assert_clipboard_content(Some("fox jumps over\n"));
2511
2512    // Paste with three selections, noticing how the copied full-line selection is inserted
2513    // before the empty selections but replaces the selection that is non-empty.
2514    cx.set_state(indoc! {"
2515        Tˇhe quick brown
2516        «foˇ»x jumps over
2517        tˇhe lazy dog"});
2518    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2519    cx.assert_editor_state(indoc! {"
2520        fox jumps over
2521        Tˇhe quick brown
2522        fox jumps over
2523        ˇx jumps over
2524        fox jumps over
2525        tˇhe lazy dog"});
2526}
2527
2528#[gpui::test]
2529async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
2530    init_test(cx, |_| {});
2531
2532    let mut cx = EditorTestContext::new(cx);
2533    let language = Arc::new(Language::new(
2534        LanguageConfig::default(),
2535        Some(tree_sitter_rust::language()),
2536    ));
2537    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
2538
2539    // Cut an indented block, without the leading whitespace.
2540    cx.set_state(indoc! {"
2541        const a: B = (
2542            c(),
2543            «d(
2544                e,
2545                f
2546            )ˇ»
2547        );
2548    "});
2549    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2550    cx.assert_editor_state(indoc! {"
2551        const a: B = (
2552            c(),
2553            ˇ
2554        );
2555    "});
2556
2557    // Paste it at the same position.
2558    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2559    cx.assert_editor_state(indoc! {"
2560        const a: B = (
2561            c(),
2562            d(
2563                e,
2564                f
25652566        );
2567    "});
2568
2569    // Paste it at a line with a lower indent level.
2570    cx.set_state(indoc! {"
2571        ˇ
2572        const a: B = (
2573            c(),
2574        );
2575    "});
2576    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2577    cx.assert_editor_state(indoc! {"
2578        d(
2579            e,
2580            f
25812582        const a: B = (
2583            c(),
2584        );
2585    "});
2586
2587    // Cut an indented block, with the leading whitespace.
2588    cx.set_state(indoc! {"
2589        const a: B = (
2590            c(),
2591        «    d(
2592                e,
2593                f
2594            )
2595        ˇ»);
2596    "});
2597    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2598    cx.assert_editor_state(indoc! {"
2599        const a: B = (
2600            c(),
2601        ˇ);
2602    "});
2603
2604    // Paste it at the same position.
2605    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2606    cx.assert_editor_state(indoc! {"
2607        const a: B = (
2608            c(),
2609            d(
2610                e,
2611                f
2612            )
2613        ˇ);
2614    "});
2615
2616    // Paste it at a line with a higher indent level.
2617    cx.set_state(indoc! {"
2618        const a: B = (
2619            c(),
2620            d(
2621                e,
26222623            )
2624        );
2625    "});
2626    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2627    cx.assert_editor_state(indoc! {"
2628        const a: B = (
2629            c(),
2630            d(
2631                e,
2632                f    d(
2633                    e,
2634                    f
2635                )
2636        ˇ
2637            )
2638        );
2639    "});
2640}
2641
2642#[gpui::test]
2643fn test_select_all(cx: &mut TestAppContext) {
2644    init_test(cx, |_| {});
2645
2646    let (_, view) = cx.add_window(|cx| {
2647        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
2648        build_editor(buffer, cx)
2649    });
2650    view.update(cx, |view, cx| {
2651        view.select_all(&SelectAll, cx);
2652        assert_eq!(
2653            view.selections.display_ranges(cx),
2654            &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
2655        );
2656    });
2657}
2658
2659#[gpui::test]
2660fn test_select_line(cx: &mut TestAppContext) {
2661    init_test(cx, |_| {});
2662
2663    let (_, view) = cx.add_window(|cx| {
2664        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
2665        build_editor(buffer, cx)
2666    });
2667    view.update(cx, |view, cx| {
2668        view.change_selections(None, cx, |s| {
2669            s.select_display_ranges([
2670                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2671                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2672                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2673                DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
2674            ])
2675        });
2676        view.select_line(&SelectLine, cx);
2677        assert_eq!(
2678            view.selections.display_ranges(cx),
2679            vec![
2680                DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
2681                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
2682            ]
2683        );
2684    });
2685
2686    view.update(cx, |view, cx| {
2687        view.select_line(&SelectLine, cx);
2688        assert_eq!(
2689            view.selections.display_ranges(cx),
2690            vec![
2691                DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
2692                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
2693            ]
2694        );
2695    });
2696
2697    view.update(cx, |view, cx| {
2698        view.select_line(&SelectLine, cx);
2699        assert_eq!(
2700            view.selections.display_ranges(cx),
2701            vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
2702        );
2703    });
2704}
2705
2706#[gpui::test]
2707fn test_split_selection_into_lines(cx: &mut TestAppContext) {
2708    init_test(cx, |_| {});
2709
2710    let (_, view) = cx.add_window(|cx| {
2711        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
2712        build_editor(buffer, cx)
2713    });
2714    view.update(cx, |view, cx| {
2715        view.fold_ranges(
2716            vec![
2717                Point::new(0, 2)..Point::new(1, 2),
2718                Point::new(2, 3)..Point::new(4, 1),
2719                Point::new(7, 0)..Point::new(8, 4),
2720            ],
2721            true,
2722            cx,
2723        );
2724        view.change_selections(None, cx, |s| {
2725            s.select_display_ranges([
2726                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2727                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2728                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2729                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
2730            ])
2731        });
2732        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
2733    });
2734
2735    view.update(cx, |view, cx| {
2736        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
2737        assert_eq!(
2738            view.display_text(cx),
2739            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
2740        );
2741        assert_eq!(
2742            view.selections.display_ranges(cx),
2743            [
2744                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2745                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2746                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
2747                DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
2748            ]
2749        );
2750    });
2751
2752    view.update(cx, |view, cx| {
2753        view.change_selections(None, cx, |s| {
2754            s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
2755        });
2756        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
2757        assert_eq!(
2758            view.display_text(cx),
2759            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
2760        );
2761        assert_eq!(
2762            view.selections.display_ranges(cx),
2763            [
2764                DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
2765                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
2766                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
2767                DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
2768                DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
2769                DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
2770                DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
2771                DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
2772            ]
2773        );
2774    });
2775}
2776
2777#[gpui::test]
2778fn test_add_selection_above_below(cx: &mut TestAppContext) {
2779    init_test(cx, |_| {});
2780
2781    let (_, view) = cx.add_window(|cx| {
2782        let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
2783        build_editor(buffer, cx)
2784    });
2785
2786    view.update(cx, |view, cx| {
2787        view.change_selections(None, cx, |s| {
2788            s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
2789        });
2790    });
2791    view.update(cx, |view, cx| {
2792        view.add_selection_above(&AddSelectionAbove, cx);
2793        assert_eq!(
2794            view.selections.display_ranges(cx),
2795            vec![
2796                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
2797                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
2798            ]
2799        );
2800    });
2801
2802    view.update(cx, |view, cx| {
2803        view.add_selection_above(&AddSelectionAbove, cx);
2804        assert_eq!(
2805            view.selections.display_ranges(cx),
2806            vec![
2807                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
2808                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
2809            ]
2810        );
2811    });
2812
2813    view.update(cx, |view, cx| {
2814        view.add_selection_below(&AddSelectionBelow, cx);
2815        assert_eq!(
2816            view.selections.display_ranges(cx),
2817            vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
2818        );
2819
2820        view.undo_selection(&UndoSelection, cx);
2821        assert_eq!(
2822            view.selections.display_ranges(cx),
2823            vec![
2824                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
2825                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
2826            ]
2827        );
2828
2829        view.redo_selection(&RedoSelection, cx);
2830        assert_eq!(
2831            view.selections.display_ranges(cx),
2832            vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
2833        );
2834    });
2835
2836    view.update(cx, |view, cx| {
2837        view.add_selection_below(&AddSelectionBelow, cx);
2838        assert_eq!(
2839            view.selections.display_ranges(cx),
2840            vec![
2841                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
2842                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
2843            ]
2844        );
2845    });
2846
2847    view.update(cx, |view, cx| {
2848        view.add_selection_below(&AddSelectionBelow, cx);
2849        assert_eq!(
2850            view.selections.display_ranges(cx),
2851            vec![
2852                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
2853                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
2854            ]
2855        );
2856    });
2857
2858    view.update(cx, |view, cx| {
2859        view.change_selections(None, cx, |s| {
2860            s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
2861        });
2862    });
2863    view.update(cx, |view, cx| {
2864        view.add_selection_below(&AddSelectionBelow, cx);
2865        assert_eq!(
2866            view.selections.display_ranges(cx),
2867            vec![
2868                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
2869                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
2870            ]
2871        );
2872    });
2873
2874    view.update(cx, |view, cx| {
2875        view.add_selection_below(&AddSelectionBelow, cx);
2876        assert_eq!(
2877            view.selections.display_ranges(cx),
2878            vec![
2879                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
2880                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
2881            ]
2882        );
2883    });
2884
2885    view.update(cx, |view, cx| {
2886        view.add_selection_above(&AddSelectionAbove, cx);
2887        assert_eq!(
2888            view.selections.display_ranges(cx),
2889            vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
2890        );
2891    });
2892
2893    view.update(cx, |view, cx| {
2894        view.add_selection_above(&AddSelectionAbove, cx);
2895        assert_eq!(
2896            view.selections.display_ranges(cx),
2897            vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
2898        );
2899    });
2900
2901    view.update(cx, |view, cx| {
2902        view.change_selections(None, cx, |s| {
2903            s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
2904        });
2905        view.add_selection_below(&AddSelectionBelow, cx);
2906        assert_eq!(
2907            view.selections.display_ranges(cx),
2908            vec![
2909                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
2910                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
2911                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
2912            ]
2913        );
2914    });
2915
2916    view.update(cx, |view, cx| {
2917        view.add_selection_below(&AddSelectionBelow, cx);
2918        assert_eq!(
2919            view.selections.display_ranges(cx),
2920            vec![
2921                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
2922                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
2923                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
2924                DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
2925            ]
2926        );
2927    });
2928
2929    view.update(cx, |view, cx| {
2930        view.add_selection_above(&AddSelectionAbove, cx);
2931        assert_eq!(
2932            view.selections.display_ranges(cx),
2933            vec![
2934                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
2935                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
2936                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
2937            ]
2938        );
2939    });
2940
2941    view.update(cx, |view, cx| {
2942        view.change_selections(None, cx, |s| {
2943            s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
2944        });
2945    });
2946    view.update(cx, |view, cx| {
2947        view.add_selection_above(&AddSelectionAbove, cx);
2948        assert_eq!(
2949            view.selections.display_ranges(cx),
2950            vec![
2951                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
2952                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
2953                DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
2954                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
2955            ]
2956        );
2957    });
2958
2959    view.update(cx, |view, cx| {
2960        view.add_selection_below(&AddSelectionBelow, cx);
2961        assert_eq!(
2962            view.selections.display_ranges(cx),
2963            vec![
2964                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
2965                DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
2966                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
2967            ]
2968        );
2969    });
2970}
2971
2972#[gpui::test]
2973async fn test_select_next(cx: &mut gpui::TestAppContext) {
2974    init_test(cx, |_| {});
2975
2976    let mut cx = EditorTestContext::new(cx);
2977    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
2978
2979    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
2980    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
2981
2982    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
2983    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
2984
2985    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
2986    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
2987
2988    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
2989    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
2990
2991    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
2992    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
2993
2994    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
2995    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
2996}
2997
2998#[gpui::test]
2999async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
3000    init_test(cx, |_| {});
3001
3002    let language = Arc::new(Language::new(
3003        LanguageConfig::default(),
3004        Some(tree_sitter_rust::language()),
3005    ));
3006
3007    let text = r#"
3008        use mod1::mod2::{mod3, mod4};
3009
3010        fn fn_1(param1: bool, param2: &str) {
3011            let var1 = "text";
3012        }
3013    "#
3014    .unindent();
3015
3016    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3017    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3018    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
3019    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
3020        .await;
3021
3022    view.update(cx, |view, cx| {
3023        view.change_selections(None, cx, |s| {
3024            s.select_display_ranges([
3025                DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3026                DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3027                DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3028            ]);
3029        });
3030        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3031    });
3032    assert_eq!(
3033        view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
3034        &[
3035            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
3036            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3037            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
3038        ]
3039    );
3040
3041    view.update(cx, |view, cx| {
3042        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3043    });
3044    assert_eq!(
3045        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3046        &[
3047            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3048            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
3049        ]
3050    );
3051
3052    view.update(cx, |view, cx| {
3053        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3054    });
3055    assert_eq!(
3056        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3057        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
3058    );
3059
3060    // Trying to expand the selected syntax node one more time has no effect.
3061    view.update(cx, |view, cx| {
3062        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3063    });
3064    assert_eq!(
3065        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3066        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
3067    );
3068
3069    view.update(cx, |view, cx| {
3070        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3071    });
3072    assert_eq!(
3073        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3074        &[
3075            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3076            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
3077        ]
3078    );
3079
3080    view.update(cx, |view, cx| {
3081        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3082    });
3083    assert_eq!(
3084        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3085        &[
3086            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
3087            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3088            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
3089        ]
3090    );
3091
3092    view.update(cx, |view, cx| {
3093        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3094    });
3095    assert_eq!(
3096        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3097        &[
3098            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3099            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3100            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3101        ]
3102    );
3103
3104    // Trying to shrink the selected syntax node one more time has no effect.
3105    view.update(cx, |view, cx| {
3106        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3107    });
3108    assert_eq!(
3109        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3110        &[
3111            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3112            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3113            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3114        ]
3115    );
3116
3117    // Ensure that we keep expanding the selection if the larger selection starts or ends within
3118    // a fold.
3119    view.update(cx, |view, cx| {
3120        view.fold_ranges(
3121            vec![
3122                Point::new(0, 21)..Point::new(0, 24),
3123                Point::new(3, 20)..Point::new(3, 22),
3124            ],
3125            true,
3126            cx,
3127        );
3128        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3129    });
3130    assert_eq!(
3131        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3132        &[
3133            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3134            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3135            DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
3136        ]
3137    );
3138}
3139
3140#[gpui::test]
3141async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
3142    init_test(cx, |_| {});
3143
3144    let language = Arc::new(
3145        Language::new(
3146            LanguageConfig {
3147                brackets: BracketPairConfig {
3148                    pairs: vec![
3149                        BracketPair {
3150                            start: "{".to_string(),
3151                            end: "}".to_string(),
3152                            close: false,
3153                            newline: true,
3154                        },
3155                        BracketPair {
3156                            start: "(".to_string(),
3157                            end: ")".to_string(),
3158                            close: false,
3159                            newline: true,
3160                        },
3161                    ],
3162                    ..Default::default()
3163                },
3164                ..Default::default()
3165            },
3166            Some(tree_sitter_rust::language()),
3167        )
3168        .with_indents_query(
3169            r#"
3170                (_ "(" ")" @end) @indent
3171                (_ "{" "}" @end) @indent
3172            "#,
3173        )
3174        .unwrap(),
3175    );
3176
3177    let text = "fn a() {}";
3178
3179    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3180    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3181    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3182    editor
3183        .condition(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
3184        .await;
3185
3186    editor.update(cx, |editor, cx| {
3187        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
3188        editor.newline(&Newline, cx);
3189        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
3190        assert_eq!(
3191            editor.selections.ranges(cx),
3192            &[
3193                Point::new(1, 4)..Point::new(1, 4),
3194                Point::new(3, 4)..Point::new(3, 4),
3195                Point::new(5, 0)..Point::new(5, 0)
3196            ]
3197        );
3198    });
3199}
3200
3201#[gpui::test]
3202async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
3203    init_test(cx, |_| {});
3204
3205    let mut cx = EditorTestContext::new(cx);
3206
3207    let language = Arc::new(Language::new(
3208        LanguageConfig {
3209            brackets: BracketPairConfig {
3210                pairs: vec![
3211                    BracketPair {
3212                        start: "{".to_string(),
3213                        end: "}".to_string(),
3214                        close: true,
3215                        newline: true,
3216                    },
3217                    BracketPair {
3218                        start: "(".to_string(),
3219                        end: ")".to_string(),
3220                        close: true,
3221                        newline: true,
3222                    },
3223                    BracketPair {
3224                        start: "/*".to_string(),
3225                        end: " */".to_string(),
3226                        close: true,
3227                        newline: true,
3228                    },
3229                    BracketPair {
3230                        start: "[".to_string(),
3231                        end: "]".to_string(),
3232                        close: false,
3233                        newline: true,
3234                    },
3235                    BracketPair {
3236                        start: "\"".to_string(),
3237                        end: "\"".to_string(),
3238                        close: true,
3239                        newline: false,
3240                    },
3241                ],
3242                ..Default::default()
3243            },
3244            autoclose_before: "})]".to_string(),
3245            ..Default::default()
3246        },
3247        Some(tree_sitter_rust::language()),
3248    ));
3249
3250    let registry = Arc::new(LanguageRegistry::test());
3251    registry.add(language.clone());
3252    cx.update_buffer(|buffer, cx| {
3253        buffer.set_language_registry(registry);
3254        buffer.set_language(Some(language), cx);
3255    });
3256
3257    cx.set_state(
3258        &r#"
3259            🏀ˇ
3260            εˇ
3261            ❤️ˇ
3262        "#
3263        .unindent(),
3264    );
3265
3266    // autoclose multiple nested brackets at multiple cursors
3267    cx.update_editor(|view, cx| {
3268        view.handle_input("{", cx);
3269        view.handle_input("{", cx);
3270        view.handle_input("{", cx);
3271    });
3272    cx.assert_editor_state(
3273        &"
3274            🏀{{{ˇ}}}
3275            ε{{{ˇ}}}
3276            ❤️{{{ˇ}}}
3277        "
3278        .unindent(),
3279    );
3280
3281    // insert a different closing bracket
3282    cx.update_editor(|view, cx| {
3283        view.handle_input(")", cx);
3284    });
3285    cx.assert_editor_state(
3286        &"
3287            🏀{{{)ˇ}}}
3288            ε{{{)ˇ}}}
3289            ❤️{{{)ˇ}}}
3290        "
3291        .unindent(),
3292    );
3293
3294    // skip over the auto-closed brackets when typing a closing bracket
3295    cx.update_editor(|view, cx| {
3296        view.move_right(&MoveRight, cx);
3297        view.handle_input("}", cx);
3298        view.handle_input("}", cx);
3299        view.handle_input("}", cx);
3300    });
3301    cx.assert_editor_state(
3302        &"
3303            🏀{{{)}}}}ˇ
3304            ε{{{)}}}}ˇ
3305            ❤️{{{)}}}}ˇ
3306        "
3307        .unindent(),
3308    );
3309
3310    // autoclose multi-character pairs
3311    cx.set_state(
3312        &"
3313            ˇ
3314            ˇ
3315        "
3316        .unindent(),
3317    );
3318    cx.update_editor(|view, cx| {
3319        view.handle_input("/", cx);
3320        view.handle_input("*", cx);
3321    });
3322    cx.assert_editor_state(
3323        &"
3324            /*ˇ */
3325            /*ˇ */
3326        "
3327        .unindent(),
3328    );
3329
3330    // one cursor autocloses a multi-character pair, one cursor
3331    // does not autoclose.
3332    cx.set_state(
3333        &"
33343335            ˇ
3336        "
3337        .unindent(),
3338    );
3339    cx.update_editor(|view, cx| view.handle_input("*", cx));
3340    cx.assert_editor_state(
3341        &"
3342            /*ˇ */
33433344        "
3345        .unindent(),
3346    );
3347
3348    // Don't autoclose if the next character isn't whitespace and isn't
3349    // listed in the language's "autoclose_before" section.
3350    cx.set_state("ˇa b");
3351    cx.update_editor(|view, cx| view.handle_input("{", cx));
3352    cx.assert_editor_state("{ˇa b");
3353
3354    // Don't autoclose if `close` is false for the bracket pair
3355    cx.set_state("ˇ");
3356    cx.update_editor(|view, cx| view.handle_input("[", cx));
3357    cx.assert_editor_state("");
3358
3359    // Surround with brackets if text is selected
3360    cx.set_state("«aˇ» b");
3361    cx.update_editor(|view, cx| view.handle_input("{", cx));
3362    cx.assert_editor_state("{«aˇ»} b");
3363
3364    // Autclose pair where the start and end characters are the same
3365    cx.set_state("");
3366    cx.update_editor(|view, cx| view.handle_input("\"", cx));
3367    cx.assert_editor_state("a\"ˇ\"");
3368    cx.update_editor(|view, cx| view.handle_input("\"", cx));
3369    cx.assert_editor_state("a\"\"ˇ");
3370}
3371
3372#[gpui::test]
3373async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
3374    init_test(cx, |_| {});
3375
3376    let mut cx = EditorTestContext::new(cx);
3377
3378    let html_language = Arc::new(
3379        Language::new(
3380            LanguageConfig {
3381                name: "HTML".into(),
3382                brackets: BracketPairConfig {
3383                    pairs: vec![
3384                        BracketPair {
3385                            start: "<".into(),
3386                            end: ">".into(),
3387                            close: true,
3388                            ..Default::default()
3389                        },
3390                        BracketPair {
3391                            start: "{".into(),
3392                            end: "}".into(),
3393                            close: true,
3394                            ..Default::default()
3395                        },
3396                        BracketPair {
3397                            start: "(".into(),
3398                            end: ")".into(),
3399                            close: true,
3400                            ..Default::default()
3401                        },
3402                    ],
3403                    ..Default::default()
3404                },
3405                autoclose_before: "})]>".into(),
3406                ..Default::default()
3407            },
3408            Some(tree_sitter_html::language()),
3409        )
3410        .with_injection_query(
3411            r#"
3412            (script_element
3413                (raw_text) @content
3414                (#set! "language" "javascript"))
3415            "#,
3416        )
3417        .unwrap(),
3418    );
3419
3420    let javascript_language = Arc::new(Language::new(
3421        LanguageConfig {
3422            name: "JavaScript".into(),
3423            brackets: BracketPairConfig {
3424                pairs: vec![
3425                    BracketPair {
3426                        start: "/*".into(),
3427                        end: " */".into(),
3428                        close: true,
3429                        ..Default::default()
3430                    },
3431                    BracketPair {
3432                        start: "{".into(),
3433                        end: "}".into(),
3434                        close: true,
3435                        ..Default::default()
3436                    },
3437                    BracketPair {
3438                        start: "(".into(),
3439                        end: ")".into(),
3440                        close: true,
3441                        ..Default::default()
3442                    },
3443                ],
3444                ..Default::default()
3445            },
3446            autoclose_before: "})]>".into(),
3447            ..Default::default()
3448        },
3449        Some(tree_sitter_javascript::language()),
3450    ));
3451
3452    let registry = Arc::new(LanguageRegistry::test());
3453    registry.add(html_language.clone());
3454    registry.add(javascript_language.clone());
3455
3456    cx.update_buffer(|buffer, cx| {
3457        buffer.set_language_registry(registry);
3458        buffer.set_language(Some(html_language), cx);
3459    });
3460
3461    cx.set_state(
3462        &r#"
3463            <body>ˇ
3464                <script>
3465                    var x = 1;ˇ
3466                </script>
3467            </body>ˇ
3468        "#
3469        .unindent(),
3470    );
3471
3472    // Precondition: different languages are active at different locations.
3473    cx.update_editor(|editor, cx| {
3474        let snapshot = editor.snapshot(cx);
3475        let cursors = editor.selections.ranges::<usize>(cx);
3476        let languages = cursors
3477            .iter()
3478            .map(|c| snapshot.language_at(c.start).unwrap().name())
3479            .collect::<Vec<_>>();
3480        assert_eq!(
3481            languages,
3482            &["HTML".into(), "JavaScript".into(), "HTML".into()]
3483        );
3484    });
3485
3486    // Angle brackets autoclose in HTML, but not JavaScript.
3487    cx.update_editor(|editor, cx| {
3488        editor.handle_input("<", cx);
3489        editor.handle_input("a", cx);
3490    });
3491    cx.assert_editor_state(
3492        &r#"
3493            <body><aˇ>
3494                <script>
3495                    var x = 1;<aˇ
3496                </script>
3497            </body><aˇ>
3498        "#
3499        .unindent(),
3500    );
3501
3502    // Curly braces and parens autoclose in both HTML and JavaScript.
3503    cx.update_editor(|editor, cx| {
3504        editor.handle_input(" b=", cx);
3505        editor.handle_input("{", cx);
3506        editor.handle_input("c", cx);
3507        editor.handle_input("(", cx);
3508    });
3509    cx.assert_editor_state(
3510        &r#"
3511            <body><a b={c(ˇ)}>
3512                <script>
3513                    var x = 1;<a b={c(ˇ)}
3514                </script>
3515            </body><a b={c(ˇ)}>
3516        "#
3517        .unindent(),
3518    );
3519
3520    // Brackets that were already autoclosed are skipped.
3521    cx.update_editor(|editor, cx| {
3522        editor.handle_input(")", cx);
3523        editor.handle_input("d", cx);
3524        editor.handle_input("}", cx);
3525    });
3526    cx.assert_editor_state(
3527        &r#"
3528            <body><a b={c()d}ˇ>
3529                <script>
3530                    var x = 1;<a b={c()d}ˇ
3531                </script>
3532            </body><a b={c()d}ˇ>
3533        "#
3534        .unindent(),
3535    );
3536    cx.update_editor(|editor, cx| {
3537        editor.handle_input(">", cx);
3538    });
3539    cx.assert_editor_state(
3540        &r#"
3541            <body><a b={c()d}>ˇ
3542                <script>
3543                    var x = 1;<a b={c()d}>ˇ
3544                </script>
3545            </body><a b={c()d}>ˇ
3546        "#
3547        .unindent(),
3548    );
3549
3550    // Reset
3551    cx.set_state(
3552        &r#"
3553            <body>ˇ
3554                <script>
3555                    var x = 1;ˇ
3556                </script>
3557            </body>ˇ
3558        "#
3559        .unindent(),
3560    );
3561
3562    cx.update_editor(|editor, cx| {
3563        editor.handle_input("<", cx);
3564    });
3565    cx.assert_editor_state(
3566        &r#"
3567            <body><ˇ>
3568                <script>
3569                    var x = 1;<ˇ
3570                </script>
3571            </body><ˇ>
3572        "#
3573        .unindent(),
3574    );
3575
3576    // When backspacing, the closing angle brackets are removed.
3577    cx.update_editor(|editor, cx| {
3578        editor.backspace(&Backspace, cx);
3579    });
3580    cx.assert_editor_state(
3581        &r#"
3582            <body>ˇ
3583                <script>
3584                    var x = 1;ˇ
3585                </script>
3586            </body>ˇ
3587        "#
3588        .unindent(),
3589    );
3590
3591    // Block comments autoclose in JavaScript, but not HTML.
3592    cx.update_editor(|editor, cx| {
3593        editor.handle_input("/", cx);
3594        editor.handle_input("*", cx);
3595    });
3596    cx.assert_editor_state(
3597        &r#"
3598            <body>/*ˇ
3599                <script>
3600                    var x = 1;/*ˇ */
3601                </script>
3602            </body>/*ˇ
3603        "#
3604        .unindent(),
3605    );
3606}
3607
3608#[gpui::test]
3609async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
3610    init_test(cx, |_| {});
3611
3612    let mut cx = EditorTestContext::new(cx);
3613
3614    let rust_language = Arc::new(
3615        Language::new(
3616            LanguageConfig {
3617                name: "Rust".into(),
3618                brackets: serde_json::from_value(json!([
3619                    { "start": "{", "end": "}", "close": true, "newline": true },
3620                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
3621                ]))
3622                .unwrap(),
3623                autoclose_before: "})]>".into(),
3624                ..Default::default()
3625            },
3626            Some(tree_sitter_rust::language()),
3627        )
3628        .with_override_query("(string_literal) @string")
3629        .unwrap(),
3630    );
3631
3632    let registry = Arc::new(LanguageRegistry::test());
3633    registry.add(rust_language.clone());
3634
3635    cx.update_buffer(|buffer, cx| {
3636        buffer.set_language_registry(registry);
3637        buffer.set_language(Some(rust_language), cx);
3638    });
3639
3640    cx.set_state(
3641        &r#"
3642            let x = ˇ
3643        "#
3644        .unindent(),
3645    );
3646
3647    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
3648    cx.update_editor(|editor, cx| {
3649        editor.handle_input("\"", cx);
3650    });
3651    cx.assert_editor_state(
3652        &r#"
3653            let x = "ˇ"
3654        "#
3655        .unindent(),
3656    );
3657
3658    // Inserting another quotation mark. The cursor moves across the existing
3659    // automatically-inserted quotation mark.
3660    cx.update_editor(|editor, cx| {
3661        editor.handle_input("\"", cx);
3662    });
3663    cx.assert_editor_state(
3664        &r#"
3665            let x = ""ˇ
3666        "#
3667        .unindent(),
3668    );
3669
3670    // Reset
3671    cx.set_state(
3672        &r#"
3673            let x = ˇ
3674        "#
3675        .unindent(),
3676    );
3677
3678    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
3679    cx.update_editor(|editor, cx| {
3680        editor.handle_input("\"", cx);
3681        editor.handle_input(" ", cx);
3682        editor.move_left(&Default::default(), cx);
3683        editor.handle_input("\\", cx);
3684        editor.handle_input("\"", cx);
3685    });
3686    cx.assert_editor_state(
3687        &r#"
3688            let x = "\"ˇ "
3689        "#
3690        .unindent(),
3691    );
3692
3693    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
3694    // mark. Nothing is inserted.
3695    cx.update_editor(|editor, cx| {
3696        editor.move_right(&Default::default(), cx);
3697        editor.handle_input("\"", cx);
3698    });
3699    cx.assert_editor_state(
3700        &r#"
3701            let x = "\" "ˇ
3702        "#
3703        .unindent(),
3704    );
3705}
3706
3707#[gpui::test]
3708async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
3709    init_test(cx, |_| {});
3710
3711    let language = Arc::new(Language::new(
3712        LanguageConfig {
3713            brackets: BracketPairConfig {
3714                pairs: vec![
3715                    BracketPair {
3716                        start: "{".to_string(),
3717                        end: "}".to_string(),
3718                        close: true,
3719                        newline: true,
3720                    },
3721                    BracketPair {
3722                        start: "/* ".to_string(),
3723                        end: "*/".to_string(),
3724                        close: true,
3725                        ..Default::default()
3726                    },
3727                ],
3728                ..Default::default()
3729            },
3730            ..Default::default()
3731        },
3732        Some(tree_sitter_rust::language()),
3733    ));
3734
3735    let text = r#"
3736        a
3737        b
3738        c
3739    "#
3740    .unindent();
3741
3742    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3743    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3744    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
3745    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
3746        .await;
3747
3748    view.update(cx, |view, cx| {
3749        view.change_selections(None, cx, |s| {
3750            s.select_display_ranges([
3751                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3752                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
3753                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
3754            ])
3755        });
3756
3757        view.handle_input("{", cx);
3758        view.handle_input("{", cx);
3759        view.handle_input("{", cx);
3760        assert_eq!(
3761            view.text(cx),
3762            "
3763                {{{a}}}
3764                {{{b}}}
3765                {{{c}}}
3766            "
3767            .unindent()
3768        );
3769        assert_eq!(
3770            view.selections.display_ranges(cx),
3771            [
3772                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
3773                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
3774                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
3775            ]
3776        );
3777
3778        view.undo(&Undo, cx);
3779        view.undo(&Undo, cx);
3780        view.undo(&Undo, cx);
3781        assert_eq!(
3782            view.text(cx),
3783            "
3784                a
3785                b
3786                c
3787            "
3788            .unindent()
3789        );
3790        assert_eq!(
3791            view.selections.display_ranges(cx),
3792            [
3793                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3794                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
3795                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
3796            ]
3797        );
3798
3799        // Ensure inserting the first character of a multi-byte bracket pair
3800        // doesn't surround the selections with the bracket.
3801        view.handle_input("/", cx);
3802        assert_eq!(
3803            view.text(cx),
3804            "
3805                /
3806                /
3807                /
3808            "
3809            .unindent()
3810        );
3811        assert_eq!(
3812            view.selections.display_ranges(cx),
3813            [
3814                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
3815                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
3816                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
3817            ]
3818        );
3819
3820        view.undo(&Undo, cx);
3821        assert_eq!(
3822            view.text(cx),
3823            "
3824                a
3825                b
3826                c
3827            "
3828            .unindent()
3829        );
3830        assert_eq!(
3831            view.selections.display_ranges(cx),
3832            [
3833                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3834                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
3835                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
3836            ]
3837        );
3838
3839        // Ensure inserting the last character of a multi-byte bracket pair
3840        // doesn't surround the selections with the bracket.
3841        view.handle_input("*", cx);
3842        assert_eq!(
3843            view.text(cx),
3844            "
3845                *
3846                *
3847                *
3848            "
3849            .unindent()
3850        );
3851        assert_eq!(
3852            view.selections.display_ranges(cx),
3853            [
3854                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
3855                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
3856                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
3857            ]
3858        );
3859    });
3860}
3861
3862#[gpui::test]
3863async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
3864    init_test(cx, |_| {});
3865
3866    let language = Arc::new(Language::new(
3867        LanguageConfig {
3868            brackets: BracketPairConfig {
3869                pairs: vec![BracketPair {
3870                    start: "{".to_string(),
3871                    end: "}".to_string(),
3872                    close: true,
3873                    newline: true,
3874                }],
3875                ..Default::default()
3876            },
3877            autoclose_before: "}".to_string(),
3878            ..Default::default()
3879        },
3880        Some(tree_sitter_rust::language()),
3881    ));
3882
3883    let text = r#"
3884        a
3885        b
3886        c
3887    "#
3888    .unindent();
3889
3890    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3891    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3892    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3893    editor
3894        .condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
3895        .await;
3896
3897    editor.update(cx, |editor, cx| {
3898        editor.change_selections(None, cx, |s| {
3899            s.select_ranges([
3900                Point::new(0, 1)..Point::new(0, 1),
3901                Point::new(1, 1)..Point::new(1, 1),
3902                Point::new(2, 1)..Point::new(2, 1),
3903            ])
3904        });
3905
3906        editor.handle_input("{", cx);
3907        editor.handle_input("{", cx);
3908        editor.handle_input("_", cx);
3909        assert_eq!(
3910            editor.text(cx),
3911            "
3912                a{{_}}
3913                b{{_}}
3914                c{{_}}
3915            "
3916            .unindent()
3917        );
3918        assert_eq!(
3919            editor.selections.ranges::<Point>(cx),
3920            [
3921                Point::new(0, 4)..Point::new(0, 4),
3922                Point::new(1, 4)..Point::new(1, 4),
3923                Point::new(2, 4)..Point::new(2, 4)
3924            ]
3925        );
3926
3927        editor.backspace(&Default::default(), cx);
3928        editor.backspace(&Default::default(), cx);
3929        assert_eq!(
3930            editor.text(cx),
3931            "
3932                a{}
3933                b{}
3934                c{}
3935            "
3936            .unindent()
3937        );
3938        assert_eq!(
3939            editor.selections.ranges::<Point>(cx),
3940            [
3941                Point::new(0, 2)..Point::new(0, 2),
3942                Point::new(1, 2)..Point::new(1, 2),
3943                Point::new(2, 2)..Point::new(2, 2)
3944            ]
3945        );
3946
3947        editor.delete_to_previous_word_start(&Default::default(), cx);
3948        assert_eq!(
3949            editor.text(cx),
3950            "
3951                a
3952                b
3953                c
3954            "
3955            .unindent()
3956        );
3957        assert_eq!(
3958            editor.selections.ranges::<Point>(cx),
3959            [
3960                Point::new(0, 1)..Point::new(0, 1),
3961                Point::new(1, 1)..Point::new(1, 1),
3962                Point::new(2, 1)..Point::new(2, 1)
3963            ]
3964        );
3965    });
3966}
3967
3968#[gpui::test]
3969async fn test_snippets(cx: &mut gpui::TestAppContext) {
3970    init_test(cx, |_| {});
3971
3972    let (text, insertion_ranges) = marked_text_ranges(
3973        indoc! {"
3974            a.ˇ b
3975            a.ˇ b
3976            a.ˇ b
3977        "},
3978        false,
3979    );
3980
3981    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
3982    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3983
3984    editor.update(cx, |editor, cx| {
3985        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
3986
3987        editor
3988            .insert_snippet(&insertion_ranges, snippet, cx)
3989            .unwrap();
3990
3991        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
3992            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
3993            assert_eq!(editor.text(cx), expected_text);
3994            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
3995        }
3996
3997        assert(
3998            editor,
3999            cx,
4000            indoc! {"
4001                a.f(«one», two, «three») b
4002                a.f(«one», two, «three») b
4003                a.f(«one», two, «three») b
4004            "},
4005        );
4006
4007        // Can't move earlier than the first tab stop
4008        assert!(!editor.move_to_prev_snippet_tabstop(cx));
4009        assert(
4010            editor,
4011            cx,
4012            indoc! {"
4013                a.f(«one», two, «three») b
4014                a.f(«one», two, «three») b
4015                a.f(«one», two, «three») b
4016            "},
4017        );
4018
4019        assert!(editor.move_to_next_snippet_tabstop(cx));
4020        assert(
4021            editor,
4022            cx,
4023            indoc! {"
4024                a.f(one, «two», three) b
4025                a.f(one, «two», three) b
4026                a.f(one, «two», three) b
4027            "},
4028        );
4029
4030        editor.move_to_prev_snippet_tabstop(cx);
4031        assert(
4032            editor,
4033            cx,
4034            indoc! {"
4035                a.f(«one», two, «three») b
4036                a.f(«one», two, «three») b
4037                a.f(«one», two, «three») b
4038            "},
4039        );
4040
4041        assert!(editor.move_to_next_snippet_tabstop(cx));
4042        assert(
4043            editor,
4044            cx,
4045            indoc! {"
4046                a.f(one, «two», three) b
4047                a.f(one, «two», three) b
4048                a.f(one, «two», three) b
4049            "},
4050        );
4051        assert!(editor.move_to_next_snippet_tabstop(cx));
4052        assert(
4053            editor,
4054            cx,
4055            indoc! {"
4056                a.f(one, two, three)ˇ b
4057                a.f(one, two, three)ˇ b
4058                a.f(one, two, three)ˇ b
4059            "},
4060        );
4061
4062        // As soon as the last tab stop is reached, snippet state is gone
4063        editor.move_to_prev_snippet_tabstop(cx);
4064        assert(
4065            editor,
4066            cx,
4067            indoc! {"
4068                a.f(one, two, three)ˇ b
4069                a.f(one, two, three)ˇ b
4070                a.f(one, two, three)ˇ b
4071            "},
4072        );
4073    });
4074}
4075
4076#[gpui::test]
4077async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
4078    init_test(cx, |_| {});
4079
4080    let mut language = Language::new(
4081        LanguageConfig {
4082            name: "Rust".into(),
4083            path_suffixes: vec!["rs".to_string()],
4084            ..Default::default()
4085        },
4086        Some(tree_sitter_rust::language()),
4087    );
4088    let mut fake_servers = language
4089        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4090            capabilities: lsp::ServerCapabilities {
4091                document_formatting_provider: Some(lsp::OneOf::Left(true)),
4092                ..Default::default()
4093            },
4094            ..Default::default()
4095        }))
4096        .await;
4097
4098    let fs = FakeFs::new(cx.background());
4099    fs.insert_file("/file.rs", Default::default()).await;
4100
4101    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4102    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4103    let buffer = project
4104        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4105        .await
4106        .unwrap();
4107
4108    cx.foreground().start_waiting();
4109    let fake_server = fake_servers.next().await.unwrap();
4110
4111    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4112    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4113    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4114    assert!(cx.read(|cx| editor.is_dirty(cx)));
4115
4116    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4117    fake_server
4118        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4119            assert_eq!(
4120                params.text_document.uri,
4121                lsp::Url::from_file_path("/file.rs").unwrap()
4122            );
4123            assert_eq!(params.options.tab_size, 4);
4124            Ok(Some(vec![lsp::TextEdit::new(
4125                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4126                ", ".to_string(),
4127            )]))
4128        })
4129        .next()
4130        .await;
4131    cx.foreground().start_waiting();
4132    save.await.unwrap();
4133    assert_eq!(
4134        editor.read_with(cx, |editor, cx| editor.text(cx)),
4135        "one, two\nthree\n"
4136    );
4137    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4138
4139    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4140    assert!(cx.read(|cx| editor.is_dirty(cx)));
4141
4142    // Ensure we can still save even if formatting hangs.
4143    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4144        assert_eq!(
4145            params.text_document.uri,
4146            lsp::Url::from_file_path("/file.rs").unwrap()
4147        );
4148        futures::future::pending::<()>().await;
4149        unreachable!()
4150    });
4151    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4152    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4153    cx.foreground().start_waiting();
4154    save.await.unwrap();
4155    assert_eq!(
4156        editor.read_with(cx, |editor, cx| editor.text(cx)),
4157        "one\ntwo\nthree\n"
4158    );
4159    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4160
4161    // Set rust language override and assert overriden tabsize is sent to language server
4162    update_test_settings(cx, |settings| {
4163        settings.languages.insert(
4164            "Rust".into(),
4165            LanguageSettingsContent {
4166                tab_size: NonZeroU32::new(8),
4167                ..Default::default()
4168            },
4169        );
4170    });
4171
4172    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4173    fake_server
4174        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4175            assert_eq!(
4176                params.text_document.uri,
4177                lsp::Url::from_file_path("/file.rs").unwrap()
4178            );
4179            assert_eq!(params.options.tab_size, 8);
4180            Ok(Some(vec![]))
4181        })
4182        .next()
4183        .await;
4184    cx.foreground().start_waiting();
4185    save.await.unwrap();
4186}
4187
4188#[gpui::test]
4189async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
4190    init_test(cx, |_| {});
4191
4192    let mut language = Language::new(
4193        LanguageConfig {
4194            name: "Rust".into(),
4195            path_suffixes: vec!["rs".to_string()],
4196            ..Default::default()
4197        },
4198        Some(tree_sitter_rust::language()),
4199    );
4200    let mut fake_servers = language
4201        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4202            capabilities: lsp::ServerCapabilities {
4203                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
4204                ..Default::default()
4205            },
4206            ..Default::default()
4207        }))
4208        .await;
4209
4210    let fs = FakeFs::new(cx.background());
4211    fs.insert_file("/file.rs", Default::default()).await;
4212
4213    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4214    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4215    let buffer = project
4216        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4217        .await
4218        .unwrap();
4219
4220    cx.foreground().start_waiting();
4221    let fake_server = fake_servers.next().await.unwrap();
4222
4223    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4224    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4225    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4226    assert!(cx.read(|cx| editor.is_dirty(cx)));
4227
4228    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4229    fake_server
4230        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
4231            assert_eq!(
4232                params.text_document.uri,
4233                lsp::Url::from_file_path("/file.rs").unwrap()
4234            );
4235            assert_eq!(params.options.tab_size, 4);
4236            Ok(Some(vec![lsp::TextEdit::new(
4237                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4238                ", ".to_string(),
4239            )]))
4240        })
4241        .next()
4242        .await;
4243    cx.foreground().start_waiting();
4244    save.await.unwrap();
4245    assert_eq!(
4246        editor.read_with(cx, |editor, cx| editor.text(cx)),
4247        "one, two\nthree\n"
4248    );
4249    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4250
4251    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4252    assert!(cx.read(|cx| editor.is_dirty(cx)));
4253
4254    // Ensure we can still save even if formatting hangs.
4255    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
4256        move |params, _| async move {
4257            assert_eq!(
4258                params.text_document.uri,
4259                lsp::Url::from_file_path("/file.rs").unwrap()
4260            );
4261            futures::future::pending::<()>().await;
4262            unreachable!()
4263        },
4264    );
4265    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4266    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4267    cx.foreground().start_waiting();
4268    save.await.unwrap();
4269    assert_eq!(
4270        editor.read_with(cx, |editor, cx| editor.text(cx)),
4271        "one\ntwo\nthree\n"
4272    );
4273    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4274
4275    // Set rust language override and assert overriden tabsize is sent to language server
4276    update_test_settings(cx, |settings| {
4277        settings.languages.insert(
4278            "Rust".into(),
4279            LanguageSettingsContent {
4280                tab_size: NonZeroU32::new(8),
4281                ..Default::default()
4282            },
4283        );
4284    });
4285
4286    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4287    fake_server
4288        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
4289            assert_eq!(
4290                params.text_document.uri,
4291                lsp::Url::from_file_path("/file.rs").unwrap()
4292            );
4293            assert_eq!(params.options.tab_size, 8);
4294            Ok(Some(vec![]))
4295        })
4296        .next()
4297        .await;
4298    cx.foreground().start_waiting();
4299    save.await.unwrap();
4300}
4301
4302#[gpui::test]
4303async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
4304    init_test(cx, |_| {});
4305
4306    let mut language = Language::new(
4307        LanguageConfig {
4308            name: "Rust".into(),
4309            path_suffixes: vec!["rs".to_string()],
4310            ..Default::default()
4311        },
4312        Some(tree_sitter_rust::language()),
4313    );
4314    let mut fake_servers = language
4315        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4316            capabilities: lsp::ServerCapabilities {
4317                document_formatting_provider: Some(lsp::OneOf::Left(true)),
4318                ..Default::default()
4319            },
4320            ..Default::default()
4321        }))
4322        .await;
4323
4324    let fs = FakeFs::new(cx.background());
4325    fs.insert_file("/file.rs", Default::default()).await;
4326
4327    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4328    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4329    let buffer = project
4330        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4331        .await
4332        .unwrap();
4333
4334    cx.foreground().start_waiting();
4335    let fake_server = fake_servers.next().await.unwrap();
4336
4337    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4338    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4339    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4340
4341    let format = editor.update(cx, |editor, cx| {
4342        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
4343    });
4344    fake_server
4345        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4346            assert_eq!(
4347                params.text_document.uri,
4348                lsp::Url::from_file_path("/file.rs").unwrap()
4349            );
4350            assert_eq!(params.options.tab_size, 4);
4351            Ok(Some(vec![lsp::TextEdit::new(
4352                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4353                ", ".to_string(),
4354            )]))
4355        })
4356        .next()
4357        .await;
4358    cx.foreground().start_waiting();
4359    format.await.unwrap();
4360    assert_eq!(
4361        editor.read_with(cx, |editor, cx| editor.text(cx)),
4362        "one, two\nthree\n"
4363    );
4364
4365    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4366    // Ensure we don't lock if formatting hangs.
4367    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4368        assert_eq!(
4369            params.text_document.uri,
4370            lsp::Url::from_file_path("/file.rs").unwrap()
4371        );
4372        futures::future::pending::<()>().await;
4373        unreachable!()
4374    });
4375    let format = editor.update(cx, |editor, cx| {
4376        editor.perform_format(project, FormatTrigger::Manual, cx)
4377    });
4378    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4379    cx.foreground().start_waiting();
4380    format.await.unwrap();
4381    assert_eq!(
4382        editor.read_with(cx, |editor, cx| editor.text(cx)),
4383        "one\ntwo\nthree\n"
4384    );
4385}
4386
4387#[gpui::test]
4388async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
4389    init_test(cx, |_| {});
4390
4391    let mut cx = EditorLspTestContext::new_rust(
4392        lsp::ServerCapabilities {
4393            document_formatting_provider: Some(lsp::OneOf::Left(true)),
4394            ..Default::default()
4395        },
4396        cx,
4397    )
4398    .await;
4399
4400    cx.set_state(indoc! {"
4401        one.twoˇ
4402    "});
4403
4404    // The format request takes a long time. When it completes, it inserts
4405    // a newline and an indent before the `.`
4406    cx.lsp
4407        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
4408            let executor = cx.background();
4409            async move {
4410                executor.timer(Duration::from_millis(100)).await;
4411                Ok(Some(vec![lsp::TextEdit {
4412                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
4413                    new_text: "\n    ".into(),
4414                }]))
4415            }
4416        });
4417
4418    // Submit a format request.
4419    let format_1 = cx
4420        .update_editor(|editor, cx| editor.format(&Format, cx))
4421        .unwrap();
4422    cx.foreground().run_until_parked();
4423
4424    // Submit a second format request.
4425    let format_2 = cx
4426        .update_editor(|editor, cx| editor.format(&Format, cx))
4427        .unwrap();
4428    cx.foreground().run_until_parked();
4429
4430    // Wait for both format requests to complete
4431    cx.foreground().advance_clock(Duration::from_millis(200));
4432    cx.foreground().start_waiting();
4433    format_1.await.unwrap();
4434    cx.foreground().start_waiting();
4435    format_2.await.unwrap();
4436
4437    // The formatting edits only happens once.
4438    cx.assert_editor_state(indoc! {"
4439        one
4440            .twoˇ
4441    "});
4442}
4443
4444#[gpui::test]
4445async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
4446    init_test(cx, |_| {});
4447
4448    let mut cx = EditorLspTestContext::new_rust(
4449        lsp::ServerCapabilities {
4450            document_formatting_provider: Some(lsp::OneOf::Left(true)),
4451            ..Default::default()
4452        },
4453        cx,
4454    )
4455    .await;
4456
4457    // Set up a buffer white some trailing whitespace and no trailing newline.
4458    cx.set_state(
4459        &[
4460            "one ",   //
4461            "twoˇ",   //
4462            "three ", //
4463            "four",   //
4464        ]
4465        .join("\n"),
4466    );
4467
4468    // Submit a format request.
4469    let format = cx
4470        .update_editor(|editor, cx| editor.format(&Format, cx))
4471        .unwrap();
4472
4473    // Record which buffer changes have been sent to the language server
4474    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
4475    cx.lsp
4476        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
4477            let buffer_changes = buffer_changes.clone();
4478            move |params, _| {
4479                buffer_changes.lock().extend(
4480                    params
4481                        .content_changes
4482                        .into_iter()
4483                        .map(|e| (e.range.unwrap(), e.text)),
4484                );
4485            }
4486        });
4487
4488    // Handle formatting requests to the language server.
4489    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
4490        let buffer_changes = buffer_changes.clone();
4491        move |_, _| {
4492            // When formatting is requested, trailing whitespace has already been stripped,
4493            // and the trailing newline has already been added.
4494            assert_eq!(
4495                &buffer_changes.lock()[1..],
4496                &[
4497                    (
4498                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
4499                        "".into()
4500                    ),
4501                    (
4502                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
4503                        "".into()
4504                    ),
4505                    (
4506                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
4507                        "\n".into()
4508                    ),
4509                ]
4510            );
4511
4512            // Insert blank lines between each line of the buffer.
4513            async move {
4514                Ok(Some(vec![
4515                    lsp::TextEdit {
4516                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
4517                        new_text: "\n".into(),
4518                    },
4519                    lsp::TextEdit {
4520                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
4521                        new_text: "\n".into(),
4522                    },
4523                ]))
4524            }
4525        }
4526    });
4527
4528    // After formatting the buffer, the trailing whitespace is stripped,
4529    // a newline is appended, and the edits provided by the language server
4530    // have been applied.
4531    format.await.unwrap();
4532    cx.assert_editor_state(
4533        &[
4534            "one",   //
4535            "",      //
4536            "twoˇ",  //
4537            "",      //
4538            "three", //
4539            "four",  //
4540            "",      //
4541        ]
4542        .join("\n"),
4543    );
4544
4545    // Undoing the formatting undoes the trailing whitespace removal, the
4546    // trailing newline, and the LSP edits.
4547    cx.update_buffer(|buffer, cx| buffer.undo(cx));
4548    cx.assert_editor_state(
4549        &[
4550            "one ",   //
4551            "twoˇ",   //
4552            "three ", //
4553            "four",   //
4554        ]
4555        .join("\n"),
4556    );
4557}
4558
4559#[gpui::test]
4560async fn test_completion(cx: &mut gpui::TestAppContext) {
4561    init_test(cx, |_| {});
4562
4563    let mut cx = EditorLspTestContext::new_rust(
4564        lsp::ServerCapabilities {
4565            completion_provider: Some(lsp::CompletionOptions {
4566                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
4567                ..Default::default()
4568            }),
4569            ..Default::default()
4570        },
4571        cx,
4572    )
4573    .await;
4574
4575    cx.set_state(indoc! {"
4576        oneˇ
4577        two
4578        three
4579    "});
4580    cx.simulate_keystroke(".");
4581    handle_completion_request(
4582        &mut cx,
4583        indoc! {"
4584            one.|<>
4585            two
4586            three
4587        "},
4588        vec!["first_completion", "second_completion"],
4589    )
4590    .await;
4591    cx.condition(|editor, _| editor.context_menu_visible())
4592        .await;
4593    let apply_additional_edits = cx.update_editor(|editor, cx| {
4594        editor.move_down(&MoveDown, cx);
4595        editor
4596            .confirm_completion(&ConfirmCompletion::default(), cx)
4597            .unwrap()
4598    });
4599    cx.assert_editor_state(indoc! {"
4600        one.second_completionˇ
4601        two
4602        three
4603    "});
4604
4605    handle_resolve_completion_request(
4606        &mut cx,
4607        Some(vec![
4608            (
4609                //This overlaps with the primary completion edit which is
4610                //misbehavior from the LSP spec, test that we filter it out
4611                indoc! {"
4612                    one.second_ˇcompletion
4613                    two
4614                    threeˇ
4615                "},
4616                "overlapping aditional edit",
4617            ),
4618            (
4619                indoc! {"
4620                    one.second_completion
4621                    two
4622                    threeˇ
4623                "},
4624                "\nadditional edit",
4625            ),
4626        ]),
4627    )
4628    .await;
4629    apply_additional_edits.await.unwrap();
4630    cx.assert_editor_state(indoc! {"
4631        one.second_completionˇ
4632        two
4633        three
4634        additional edit
4635    "});
4636
4637    cx.set_state(indoc! {"
4638        one.second_completion
4639        twoˇ
4640        threeˇ
4641        additional edit
4642    "});
4643    cx.simulate_keystroke(" ");
4644    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4645    cx.simulate_keystroke("s");
4646    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4647
4648    cx.assert_editor_state(indoc! {"
4649        one.second_completion
4650        two sˇ
4651        three sˇ
4652        additional edit
4653    "});
4654    handle_completion_request(
4655        &mut cx,
4656        indoc! {"
4657            one.second_completion
4658            two s
4659            three <s|>
4660            additional edit
4661        "},
4662        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
4663    )
4664    .await;
4665    cx.condition(|editor, _| editor.context_menu_visible())
4666        .await;
4667
4668    cx.simulate_keystroke("i");
4669
4670    handle_completion_request(
4671        &mut cx,
4672        indoc! {"
4673            one.second_completion
4674            two si
4675            three <si|>
4676            additional edit
4677        "},
4678        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
4679    )
4680    .await;
4681    cx.condition(|editor, _| editor.context_menu_visible())
4682        .await;
4683
4684    let apply_additional_edits = cx.update_editor(|editor, cx| {
4685        editor
4686            .confirm_completion(&ConfirmCompletion::default(), cx)
4687            .unwrap()
4688    });
4689    cx.assert_editor_state(indoc! {"
4690        one.second_completion
4691        two sixth_completionˇ
4692        three sixth_completionˇ
4693        additional edit
4694    "});
4695
4696    handle_resolve_completion_request(&mut cx, None).await;
4697    apply_additional_edits.await.unwrap();
4698
4699    cx.update(|cx| {
4700        cx.update_global::<SettingsStore, _, _>(|settings, cx| {
4701            settings.update_user_settings::<EditorSettings>(cx, |settings| {
4702                settings.show_completions_on_input = Some(false);
4703            });
4704        })
4705    });
4706    cx.set_state("editorˇ");
4707    cx.simulate_keystroke(".");
4708    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4709    cx.simulate_keystroke("c");
4710    cx.simulate_keystroke("l");
4711    cx.simulate_keystroke("o");
4712    cx.assert_editor_state("editor.cloˇ");
4713    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4714    cx.update_editor(|editor, cx| {
4715        editor.show_completions(&ShowCompletions, cx);
4716    });
4717    handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
4718    cx.condition(|editor, _| editor.context_menu_visible())
4719        .await;
4720    let apply_additional_edits = cx.update_editor(|editor, cx| {
4721        editor
4722            .confirm_completion(&ConfirmCompletion::default(), cx)
4723            .unwrap()
4724    });
4725    cx.assert_editor_state("editor.closeˇ");
4726    handle_resolve_completion_request(&mut cx, None).await;
4727    apply_additional_edits.await.unwrap();
4728}
4729
4730#[gpui::test]
4731async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
4732    init_test(cx, |_| {});
4733
4734    let language = Arc::new(Language::new(
4735        LanguageConfig {
4736            line_comment: Some("// ".into()),
4737            ..Default::default()
4738        },
4739        Some(tree_sitter_rust::language()),
4740    ));
4741
4742    let text = "
4743        fn a() {
4744            //b();
4745            // c();
4746            //  d();
4747        }
4748    "
4749    .unindent();
4750
4751    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
4752    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4753    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
4754
4755    view.update(cx, |editor, cx| {
4756        // If multiple selections intersect a line, the line is only
4757        // toggled once.
4758        editor.change_selections(None, cx, |s| {
4759            s.select_display_ranges([
4760                DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
4761                DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
4762            ])
4763        });
4764        editor.toggle_comments(&ToggleComments::default(), cx);
4765        assert_eq!(
4766            editor.text(cx),
4767            "
4768                fn a() {
4769                    b();
4770                    c();
4771                     d();
4772                }
4773            "
4774            .unindent()
4775        );
4776
4777        // The comment prefix is inserted at the same column for every line
4778        // in a selection.
4779        editor.change_selections(None, cx, |s| {
4780            s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)])
4781        });
4782        editor.toggle_comments(&ToggleComments::default(), cx);
4783        assert_eq!(
4784            editor.text(cx),
4785            "
4786                fn a() {
4787                    // b();
4788                    // c();
4789                    //  d();
4790                }
4791            "
4792            .unindent()
4793        );
4794
4795        // If a selection ends at the beginning of a line, that line is not toggled.
4796        editor.change_selections(None, cx, |s| {
4797            s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)])
4798        });
4799        editor.toggle_comments(&ToggleComments::default(), cx);
4800        assert_eq!(
4801            editor.text(cx),
4802            "
4803                fn a() {
4804                    // b();
4805                    c();
4806                    //  d();
4807                }
4808            "
4809            .unindent()
4810        );
4811    });
4812}
4813
4814#[gpui::test]
4815async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
4816    init_test(cx, |_| {});
4817
4818    let language = Arc::new(Language::new(
4819        LanguageConfig {
4820            line_comment: Some("// ".into()),
4821            ..Default::default()
4822        },
4823        Some(tree_sitter_rust::language()),
4824    ));
4825
4826    let registry = Arc::new(LanguageRegistry::test());
4827    registry.add(language.clone());
4828
4829    let mut cx = EditorTestContext::new(cx);
4830    cx.update_buffer(|buffer, cx| {
4831        buffer.set_language_registry(registry);
4832        buffer.set_language(Some(language), cx);
4833    });
4834
4835    let toggle_comments = &ToggleComments {
4836        advance_downwards: true,
4837    };
4838
4839    // Single cursor on one line -> advance
4840    // Cursor moves horizontally 3 characters as well on non-blank line
4841    cx.set_state(indoc!(
4842        "fn a() {
4843             ˇdog();
4844             cat();
4845        }"
4846    ));
4847    cx.update_editor(|editor, cx| {
4848        editor.toggle_comments(toggle_comments, cx);
4849    });
4850    cx.assert_editor_state(indoc!(
4851        "fn a() {
4852             // dog();
4853             catˇ();
4854        }"
4855    ));
4856
4857    // Single selection on one line -> don't advance
4858    cx.set_state(indoc!(
4859        "fn a() {
4860             «dog()ˇ»;
4861             cat();
4862        }"
4863    ));
4864    cx.update_editor(|editor, cx| {
4865        editor.toggle_comments(toggle_comments, cx);
4866    });
4867    cx.assert_editor_state(indoc!(
4868        "fn a() {
4869             // «dog()ˇ»;
4870             cat();
4871        }"
4872    ));
4873
4874    // Multiple cursors on one line -> advance
4875    cx.set_state(indoc!(
4876        "fn a() {
4877             ˇdˇog();
4878             cat();
4879        }"
4880    ));
4881    cx.update_editor(|editor, cx| {
4882        editor.toggle_comments(toggle_comments, cx);
4883    });
4884    cx.assert_editor_state(indoc!(
4885        "fn a() {
4886             // dog();
4887             catˇ(ˇ);
4888        }"
4889    ));
4890
4891    // Multiple cursors on one line, with selection -> don't advance
4892    cx.set_state(indoc!(
4893        "fn a() {
4894             ˇdˇog«()ˇ»;
4895             cat();
4896        }"
4897    ));
4898    cx.update_editor(|editor, cx| {
4899        editor.toggle_comments(toggle_comments, cx);
4900    });
4901    cx.assert_editor_state(indoc!(
4902        "fn a() {
4903             // ˇdˇog«()ˇ»;
4904             cat();
4905        }"
4906    ));
4907
4908    // Single cursor on one line -> advance
4909    // Cursor moves to column 0 on blank line
4910    cx.set_state(indoc!(
4911        "fn a() {
4912             ˇdog();
4913
4914             cat();
4915        }"
4916    ));
4917    cx.update_editor(|editor, cx| {
4918        editor.toggle_comments(toggle_comments, cx);
4919    });
4920    cx.assert_editor_state(indoc!(
4921        "fn a() {
4922             // dog();
4923        ˇ
4924             cat();
4925        }"
4926    ));
4927
4928    // Single cursor on one line -> advance
4929    // Cursor starts and ends at column 0
4930    cx.set_state(indoc!(
4931        "fn a() {
4932         ˇ    dog();
4933             cat();
4934        }"
4935    ));
4936    cx.update_editor(|editor, cx| {
4937        editor.toggle_comments(toggle_comments, cx);
4938    });
4939    cx.assert_editor_state(indoc!(
4940        "fn a() {
4941             // dog();
4942         ˇ    cat();
4943        }"
4944    ));
4945}
4946
4947#[gpui::test]
4948async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
4949    init_test(cx, |_| {});
4950
4951    let mut cx = EditorTestContext::new(cx);
4952
4953    let html_language = Arc::new(
4954        Language::new(
4955            LanguageConfig {
4956                name: "HTML".into(),
4957                block_comment: Some(("<!-- ".into(), " -->".into())),
4958                ..Default::default()
4959            },
4960            Some(tree_sitter_html::language()),
4961        )
4962        .with_injection_query(
4963            r#"
4964            (script_element
4965                (raw_text) @content
4966                (#set! "language" "javascript"))
4967            "#,
4968        )
4969        .unwrap(),
4970    );
4971
4972    let javascript_language = Arc::new(Language::new(
4973        LanguageConfig {
4974            name: "JavaScript".into(),
4975            line_comment: Some("// ".into()),
4976            ..Default::default()
4977        },
4978        Some(tree_sitter_javascript::language()),
4979    ));
4980
4981    let registry = Arc::new(LanguageRegistry::test());
4982    registry.add(html_language.clone());
4983    registry.add(javascript_language.clone());
4984
4985    cx.update_buffer(|buffer, cx| {
4986        buffer.set_language_registry(registry);
4987        buffer.set_language(Some(html_language), cx);
4988    });
4989
4990    // Toggle comments for empty selections
4991    cx.set_state(
4992        &r#"
4993            <p>A</p>ˇ
4994            <p>B</p>ˇ
4995            <p>C</p>ˇ
4996        "#
4997        .unindent(),
4998    );
4999    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5000    cx.assert_editor_state(
5001        &r#"
5002            <!-- <p>A</p>ˇ -->
5003            <!-- <p>B</p>ˇ -->
5004            <!-- <p>C</p>ˇ -->
5005        "#
5006        .unindent(),
5007    );
5008    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5009    cx.assert_editor_state(
5010        &r#"
5011            <p>A</p>ˇ
5012            <p>B</p>ˇ
5013            <p>C</p>ˇ
5014        "#
5015        .unindent(),
5016    );
5017
5018    // Toggle comments for mixture of empty and non-empty selections, where
5019    // multiple selections occupy a given line.
5020    cx.set_state(
5021        &r#"
5022            <p>A«</p>
5023            <p>ˇ»B</p>ˇ
5024            <p>C«</p>
5025            <p>ˇ»D</p>ˇ
5026        "#
5027        .unindent(),
5028    );
5029
5030    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5031    cx.assert_editor_state(
5032        &r#"
5033            <!-- <p>A«</p>
5034            <p>ˇ»B</p>ˇ -->
5035            <!-- <p>C«</p>
5036            <p>ˇ»D</p>ˇ -->
5037        "#
5038        .unindent(),
5039    );
5040    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5041    cx.assert_editor_state(
5042        &r#"
5043            <p>A«</p>
5044            <p>ˇ»B</p>ˇ
5045            <p>C«</p>
5046            <p>ˇ»D</p>ˇ
5047        "#
5048        .unindent(),
5049    );
5050
5051    // Toggle comments when different languages are active for different
5052    // selections.
5053    cx.set_state(
5054        &r#"
5055            ˇ<script>
5056                ˇvar x = new Y();
5057            ˇ</script>
5058        "#
5059        .unindent(),
5060    );
5061    cx.foreground().run_until_parked();
5062    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5063    cx.assert_editor_state(
5064        &r#"
5065            <!-- ˇ<script> -->
5066                // ˇvar x = new Y();
5067            <!-- ˇ</script> -->
5068        "#
5069        .unindent(),
5070    );
5071}
5072
5073#[gpui::test]
5074fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
5075    init_test(cx, |_| {});
5076
5077    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5078    let multibuffer = cx.add_model(|cx| {
5079        let mut multibuffer = MultiBuffer::new(0);
5080        multibuffer.push_excerpts(
5081            buffer.clone(),
5082            [
5083                ExcerptRange {
5084                    context: Point::new(0, 0)..Point::new(0, 4),
5085                    primary: None,
5086                },
5087                ExcerptRange {
5088                    context: Point::new(1, 0)..Point::new(1, 4),
5089                    primary: None,
5090                },
5091            ],
5092            cx,
5093        );
5094        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
5095        multibuffer
5096    });
5097
5098    let (_, view) = cx.add_window(|cx| build_editor(multibuffer, cx));
5099    view.update(cx, |view, cx| {
5100        assert_eq!(view.text(cx), "aaaa\nbbbb");
5101        view.change_selections(None, cx, |s| {
5102            s.select_ranges([
5103                Point::new(0, 0)..Point::new(0, 0),
5104                Point::new(1, 0)..Point::new(1, 0),
5105            ])
5106        });
5107
5108        view.handle_input("X", cx);
5109        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
5110        assert_eq!(
5111            view.selections.ranges(cx),
5112            [
5113                Point::new(0, 1)..Point::new(0, 1),
5114                Point::new(1, 1)..Point::new(1, 1),
5115            ]
5116        )
5117    });
5118}
5119
5120#[gpui::test]
5121fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
5122    init_test(cx, |_| {});
5123
5124    let markers = vec![('[', ']').into(), ('(', ')').into()];
5125    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
5126        indoc! {"
5127            [aaaa
5128            (bbbb]
5129            cccc)",
5130        },
5131        markers.clone(),
5132    );
5133    let excerpt_ranges = markers.into_iter().map(|marker| {
5134        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
5135        ExcerptRange {
5136            context,
5137            primary: None,
5138        }
5139    });
5140    let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
5141    let multibuffer = cx.add_model(|cx| {
5142        let mut multibuffer = MultiBuffer::new(0);
5143        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
5144        multibuffer
5145    });
5146
5147    let (_, view) = cx.add_window(|cx| build_editor(multibuffer, cx));
5148    view.update(cx, |view, cx| {
5149        let (expected_text, selection_ranges) = marked_text_ranges(
5150            indoc! {"
5151                aaaa
5152                bˇbbb
5153                bˇbbˇb
5154                cccc"
5155            },
5156            true,
5157        );
5158        assert_eq!(view.text(cx), expected_text);
5159        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
5160
5161        view.handle_input("X", cx);
5162
5163        let (expected_text, expected_selections) = marked_text_ranges(
5164            indoc! {"
5165                aaaa
5166                bXˇbbXb
5167                bXˇbbXˇb
5168                cccc"
5169            },
5170            false,
5171        );
5172        assert_eq!(view.text(cx), expected_text);
5173        assert_eq!(view.selections.ranges(cx), expected_selections);
5174
5175        view.newline(&Newline, cx);
5176        let (expected_text, expected_selections) = marked_text_ranges(
5177            indoc! {"
5178                aaaa
5179                bX
5180                ˇbbX
5181                b
5182                bX
5183                ˇbbX
5184                ˇb
5185                cccc"
5186            },
5187            false,
5188        );
5189        assert_eq!(view.text(cx), expected_text);
5190        assert_eq!(view.selections.ranges(cx), expected_selections);
5191    });
5192}
5193
5194#[gpui::test]
5195fn test_refresh_selections(cx: &mut TestAppContext) {
5196    init_test(cx, |_| {});
5197
5198    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5199    let mut excerpt1_id = None;
5200    let multibuffer = cx.add_model(|cx| {
5201        let mut multibuffer = MultiBuffer::new(0);
5202        excerpt1_id = multibuffer
5203            .push_excerpts(
5204                buffer.clone(),
5205                [
5206                    ExcerptRange {
5207                        context: Point::new(0, 0)..Point::new(1, 4),
5208                        primary: None,
5209                    },
5210                    ExcerptRange {
5211                        context: Point::new(1, 0)..Point::new(2, 4),
5212                        primary: None,
5213                    },
5214                ],
5215                cx,
5216            )
5217            .into_iter()
5218            .next();
5219        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
5220        multibuffer
5221    });
5222
5223    let (_, editor) = cx.add_window(|cx| {
5224        let mut editor = build_editor(multibuffer.clone(), cx);
5225        let snapshot = editor.snapshot(cx);
5226        editor.change_selections(None, cx, |s| {
5227            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
5228        });
5229        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
5230        assert_eq!(
5231            editor.selections.ranges(cx),
5232            [
5233                Point::new(1, 3)..Point::new(1, 3),
5234                Point::new(2, 1)..Point::new(2, 1),
5235            ]
5236        );
5237        editor
5238    });
5239
5240    // Refreshing selections is a no-op when excerpts haven't changed.
5241    editor.update(cx, |editor, cx| {
5242        editor.change_selections(None, cx, |s| s.refresh());
5243        assert_eq!(
5244            editor.selections.ranges(cx),
5245            [
5246                Point::new(1, 3)..Point::new(1, 3),
5247                Point::new(2, 1)..Point::new(2, 1),
5248            ]
5249        );
5250    });
5251
5252    multibuffer.update(cx, |multibuffer, cx| {
5253        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
5254    });
5255    editor.update(cx, |editor, cx| {
5256        // Removing an excerpt causes the first selection to become degenerate.
5257        assert_eq!(
5258            editor.selections.ranges(cx),
5259            [
5260                Point::new(0, 0)..Point::new(0, 0),
5261                Point::new(0, 1)..Point::new(0, 1)
5262            ]
5263        );
5264
5265        // Refreshing selections will relocate the first selection to the original buffer
5266        // location.
5267        editor.change_selections(None, cx, |s| s.refresh());
5268        assert_eq!(
5269            editor.selections.ranges(cx),
5270            [
5271                Point::new(0, 1)..Point::new(0, 1),
5272                Point::new(0, 3)..Point::new(0, 3)
5273            ]
5274        );
5275        assert!(editor.selections.pending_anchor().is_some());
5276    });
5277}
5278
5279#[gpui::test]
5280fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
5281    init_test(cx, |_| {});
5282
5283    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5284    let mut excerpt1_id = None;
5285    let multibuffer = cx.add_model(|cx| {
5286        let mut multibuffer = MultiBuffer::new(0);
5287        excerpt1_id = multibuffer
5288            .push_excerpts(
5289                buffer.clone(),
5290                [
5291                    ExcerptRange {
5292                        context: Point::new(0, 0)..Point::new(1, 4),
5293                        primary: None,
5294                    },
5295                    ExcerptRange {
5296                        context: Point::new(1, 0)..Point::new(2, 4),
5297                        primary: None,
5298                    },
5299                ],
5300                cx,
5301            )
5302            .into_iter()
5303            .next();
5304        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
5305        multibuffer
5306    });
5307
5308    let (_, editor) = cx.add_window(|cx| {
5309        let mut editor = build_editor(multibuffer.clone(), cx);
5310        let snapshot = editor.snapshot(cx);
5311        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
5312        assert_eq!(
5313            editor.selections.ranges(cx),
5314            [Point::new(1, 3)..Point::new(1, 3)]
5315        );
5316        editor
5317    });
5318
5319    multibuffer.update(cx, |multibuffer, cx| {
5320        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
5321    });
5322    editor.update(cx, |editor, cx| {
5323        assert_eq!(
5324            editor.selections.ranges(cx),
5325            [Point::new(0, 0)..Point::new(0, 0)]
5326        );
5327
5328        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
5329        editor.change_selections(None, cx, |s| s.refresh());
5330        assert_eq!(
5331            editor.selections.ranges(cx),
5332            [Point::new(0, 3)..Point::new(0, 3)]
5333        );
5334        assert!(editor.selections.pending_anchor().is_some());
5335    });
5336}
5337
5338#[gpui::test]
5339async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
5340    init_test(cx, |_| {});
5341
5342    let language = Arc::new(
5343        Language::new(
5344            LanguageConfig {
5345                brackets: BracketPairConfig {
5346                    pairs: vec![
5347                        BracketPair {
5348                            start: "{".to_string(),
5349                            end: "}".to_string(),
5350                            close: true,
5351                            newline: true,
5352                        },
5353                        BracketPair {
5354                            start: "/* ".to_string(),
5355                            end: " */".to_string(),
5356                            close: true,
5357                            newline: true,
5358                        },
5359                    ],
5360                    ..Default::default()
5361                },
5362                ..Default::default()
5363            },
5364            Some(tree_sitter_rust::language()),
5365        )
5366        .with_indents_query("")
5367        .unwrap(),
5368    );
5369
5370    let text = concat!(
5371        "{   }\n",     //
5372        "  x\n",       //
5373        "  /*   */\n", //
5374        "x\n",         //
5375        "{{} }\n",     //
5376    );
5377
5378    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
5379    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
5380    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
5381    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
5382        .await;
5383
5384    view.update(cx, |view, cx| {
5385        view.change_selections(None, cx, |s| {
5386            s.select_display_ranges([
5387                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
5388                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
5389                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
5390            ])
5391        });
5392        view.newline(&Newline, cx);
5393
5394        assert_eq!(
5395            view.buffer().read(cx).read(cx).text(),
5396            concat!(
5397                "{ \n",    // Suppress rustfmt
5398                "\n",      //
5399                "}\n",     //
5400                "  x\n",   //
5401                "  /* \n", //
5402                "  \n",    //
5403                "  */\n",  //
5404                "x\n",     //
5405                "{{} \n",  //
5406                "}\n",     //
5407            )
5408        );
5409    });
5410}
5411
5412#[gpui::test]
5413fn test_highlighted_ranges(cx: &mut TestAppContext) {
5414    init_test(cx, |_| {});
5415
5416    let (_, editor) = cx.add_window(|cx| {
5417        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
5418        build_editor(buffer.clone(), cx)
5419    });
5420
5421    editor.update(cx, |editor, cx| {
5422        struct Type1;
5423        struct Type2;
5424
5425        let buffer = editor.buffer.read(cx).snapshot(cx);
5426
5427        let anchor_range =
5428            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
5429
5430        editor.highlight_background::<Type1>(
5431            vec![
5432                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
5433                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
5434                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
5435                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
5436            ],
5437            |_| Color::red(),
5438            cx,
5439        );
5440        editor.highlight_background::<Type2>(
5441            vec![
5442                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
5443                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
5444                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
5445                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
5446            ],
5447            |_| Color::green(),
5448            cx,
5449        );
5450
5451        let snapshot = editor.snapshot(cx);
5452        let mut highlighted_ranges = editor.background_highlights_in_range(
5453            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
5454            &snapshot,
5455            theme::current(cx).as_ref(),
5456        );
5457        // Enforce a consistent ordering based on color without relying on the ordering of the
5458        // highlight's `TypeId` which is non-deterministic.
5459        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
5460        assert_eq!(
5461            highlighted_ranges,
5462            &[
5463                (
5464                    DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
5465                    Color::green(),
5466                ),
5467                (
5468                    DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
5469                    Color::green(),
5470                ),
5471                (
5472                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
5473                    Color::red(),
5474                ),
5475                (
5476                    DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
5477                    Color::red(),
5478                ),
5479            ]
5480        );
5481        assert_eq!(
5482            editor.background_highlights_in_range(
5483                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
5484                &snapshot,
5485                theme::current(cx).as_ref(),
5486            ),
5487            &[(
5488                DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
5489                Color::red(),
5490            )]
5491        );
5492    });
5493}
5494
5495#[gpui::test]
5496async fn test_following(cx: &mut gpui::TestAppContext) {
5497    init_test(cx, |_| {});
5498
5499    let fs = FakeFs::new(cx.background());
5500    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5501
5502    let buffer = project.update(cx, |project, cx| {
5503        let buffer = project
5504            .create_buffer(&sample_text(16, 8, 'a'), None, cx)
5505            .unwrap();
5506        cx.add_model(|cx| MultiBuffer::singleton(buffer, cx))
5507    });
5508    let (_, leader) = cx.add_window(|cx| build_editor(buffer.clone(), cx));
5509    let (_, follower) = cx.update(|cx| {
5510        cx.add_window(
5511            WindowOptions {
5512                bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
5513                ..Default::default()
5514            },
5515            |cx| build_editor(buffer.clone(), cx),
5516        )
5517    });
5518
5519    let is_still_following = Rc::new(RefCell::new(true));
5520    let pending_update = Rc::new(RefCell::new(None));
5521    follower.update(cx, {
5522        let update = pending_update.clone();
5523        let is_still_following = is_still_following.clone();
5524        |_, cx| {
5525            cx.subscribe(&leader, move |_, leader, event, cx| {
5526                leader
5527                    .read(cx)
5528                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
5529            })
5530            .detach();
5531
5532            cx.subscribe(&follower, move |_, _, event, cx| {
5533                if Editor::should_unfollow_on_event(event, cx) {
5534                    *is_still_following.borrow_mut() = false;
5535                }
5536            })
5537            .detach();
5538        }
5539    });
5540
5541    // Update the selections only
5542    leader.update(cx, |leader, cx| {
5543        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
5544    });
5545    follower
5546        .update(cx, |follower, cx| {
5547            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5548        })
5549        .await
5550        .unwrap();
5551    follower.read_with(cx, |follower, cx| {
5552        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
5553    });
5554    assert_eq!(*is_still_following.borrow(), true);
5555
5556    // Update the scroll position only
5557    leader.update(cx, |leader, cx| {
5558        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
5559    });
5560    follower
5561        .update(cx, |follower, cx| {
5562            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5563        })
5564        .await
5565        .unwrap();
5566    assert_eq!(
5567        follower.update(cx, |follower, cx| follower.scroll_position(cx)),
5568        vec2f(1.5, 3.5)
5569    );
5570    assert_eq!(*is_still_following.borrow(), true);
5571
5572    // Update the selections and scroll position. The follower's scroll position is updated
5573    // via autoscroll, not via the leader's exact scroll position.
5574    leader.update(cx, |leader, cx| {
5575        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
5576        leader.request_autoscroll(Autoscroll::newest(), cx);
5577        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
5578    });
5579    follower
5580        .update(cx, |follower, cx| {
5581            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5582        })
5583        .await
5584        .unwrap();
5585    follower.update(cx, |follower, cx| {
5586        assert_eq!(follower.scroll_position(cx), vec2f(1.5, 0.0));
5587        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
5588    });
5589    assert_eq!(*is_still_following.borrow(), true);
5590
5591    // Creating a pending selection that precedes another selection
5592    leader.update(cx, |leader, cx| {
5593        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
5594        leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
5595    });
5596    follower
5597        .update(cx, |follower, cx| {
5598            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5599        })
5600        .await
5601        .unwrap();
5602    follower.read_with(cx, |follower, cx| {
5603        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
5604    });
5605    assert_eq!(*is_still_following.borrow(), true);
5606
5607    // Extend the pending selection so that it surrounds another selection
5608    leader.update(cx, |leader, cx| {
5609        leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
5610    });
5611    follower
5612        .update(cx, |follower, cx| {
5613            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5614        })
5615        .await
5616        .unwrap();
5617    follower.read_with(cx, |follower, cx| {
5618        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
5619    });
5620
5621    // Scrolling locally breaks the follow
5622    follower.update(cx, |follower, cx| {
5623        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
5624        follower.set_scroll_anchor(
5625            ScrollAnchor {
5626                top_anchor,
5627                offset: vec2f(0.0, 0.5),
5628            },
5629            cx,
5630        );
5631    });
5632    assert_eq!(*is_still_following.borrow(), false);
5633}
5634
5635#[gpui::test]
5636async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
5637    init_test(cx, |_| {});
5638
5639    let fs = FakeFs::new(cx.background());
5640    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5641    let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
5642    let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
5643
5644    let leader = pane.update(cx, |_, cx| {
5645        let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
5646        cx.add_view(|cx| build_editor(multibuffer.clone(), cx))
5647    });
5648
5649    // Start following the editor when it has no excerpts.
5650    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
5651    let follower_1 = cx
5652        .update(|cx| {
5653            Editor::from_state_proto(
5654                pane.clone(),
5655                project.clone(),
5656                ViewId {
5657                    creator: Default::default(),
5658                    id: 0,
5659                },
5660                &mut state_message,
5661                cx,
5662            )
5663        })
5664        .unwrap()
5665        .await
5666        .unwrap();
5667
5668    let update_message = Rc::new(RefCell::new(None));
5669    follower_1.update(cx, {
5670        let update = update_message.clone();
5671        |_, cx| {
5672            cx.subscribe(&leader, move |_, leader, event, cx| {
5673                leader
5674                    .read(cx)
5675                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
5676            })
5677            .detach();
5678        }
5679    });
5680
5681    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
5682        (
5683            project
5684                .create_buffer("abc\ndef\nghi\njkl\n", None, cx)
5685                .unwrap(),
5686            project
5687                .create_buffer("mno\npqr\nstu\nvwx\n", None, cx)
5688                .unwrap(),
5689        )
5690    });
5691
5692    // Insert some excerpts.
5693    leader.update(cx, |leader, cx| {
5694        leader.buffer.update(cx, |multibuffer, cx| {
5695            let excerpt_ids = multibuffer.push_excerpts(
5696                buffer_1.clone(),
5697                [
5698                    ExcerptRange {
5699                        context: 1..6,
5700                        primary: None,
5701                    },
5702                    ExcerptRange {
5703                        context: 12..15,
5704                        primary: None,
5705                    },
5706                    ExcerptRange {
5707                        context: 0..3,
5708                        primary: None,
5709                    },
5710                ],
5711                cx,
5712            );
5713            multibuffer.insert_excerpts_after(
5714                excerpt_ids[0],
5715                buffer_2.clone(),
5716                [
5717                    ExcerptRange {
5718                        context: 8..12,
5719                        primary: None,
5720                    },
5721                    ExcerptRange {
5722                        context: 0..6,
5723                        primary: None,
5724                    },
5725                ],
5726                cx,
5727            );
5728        });
5729    });
5730
5731    // Apply the update of adding the excerpts.
5732    follower_1
5733        .update(cx, |follower, cx| {
5734            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
5735        })
5736        .await
5737        .unwrap();
5738    assert_eq!(
5739        follower_1.read_with(cx, |editor, cx| editor.text(cx)),
5740        leader.read_with(cx, |editor, cx| editor.text(cx))
5741    );
5742    update_message.borrow_mut().take();
5743
5744    // Start following separately after it already has excerpts.
5745    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
5746    let follower_2 = cx
5747        .update(|cx| {
5748            Editor::from_state_proto(
5749                pane.clone(),
5750                project.clone(),
5751                ViewId {
5752                    creator: Default::default(),
5753                    id: 0,
5754                },
5755                &mut state_message,
5756                cx,
5757            )
5758        })
5759        .unwrap()
5760        .await
5761        .unwrap();
5762    assert_eq!(
5763        follower_2.read_with(cx, |editor, cx| editor.text(cx)),
5764        leader.read_with(cx, |editor, cx| editor.text(cx))
5765    );
5766
5767    // Remove some excerpts.
5768    leader.update(cx, |leader, cx| {
5769        leader.buffer.update(cx, |multibuffer, cx| {
5770            let excerpt_ids = multibuffer.excerpt_ids();
5771            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
5772            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
5773        });
5774    });
5775
5776    // Apply the update of removing the excerpts.
5777    follower_1
5778        .update(cx, |follower, cx| {
5779            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
5780        })
5781        .await
5782        .unwrap();
5783    follower_2
5784        .update(cx, |follower, cx| {
5785            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
5786        })
5787        .await
5788        .unwrap();
5789    update_message.borrow_mut().take();
5790    assert_eq!(
5791        follower_1.read_with(cx, |editor, cx| editor.text(cx)),
5792        leader.read_with(cx, |editor, cx| editor.text(cx))
5793    );
5794}
5795
5796#[test]
5797fn test_combine_syntax_and_fuzzy_match_highlights() {
5798    let string = "abcdefghijklmnop";
5799    let syntax_ranges = [
5800        (
5801            0..3,
5802            HighlightStyle {
5803                color: Some(Color::red()),
5804                ..Default::default()
5805            },
5806        ),
5807        (
5808            4..8,
5809            HighlightStyle {
5810                color: Some(Color::green()),
5811                ..Default::default()
5812            },
5813        ),
5814    ];
5815    let match_indices = [4, 6, 7, 8];
5816    assert_eq!(
5817        combine_syntax_and_fuzzy_match_highlights(
5818            string,
5819            Default::default(),
5820            syntax_ranges.into_iter(),
5821            &match_indices,
5822        ),
5823        &[
5824            (
5825                0..3,
5826                HighlightStyle {
5827                    color: Some(Color::red()),
5828                    ..Default::default()
5829                },
5830            ),
5831            (
5832                4..5,
5833                HighlightStyle {
5834                    color: Some(Color::green()),
5835                    weight: Some(fonts::Weight::BOLD),
5836                    ..Default::default()
5837                },
5838            ),
5839            (
5840                5..6,
5841                HighlightStyle {
5842                    color: Some(Color::green()),
5843                    ..Default::default()
5844                },
5845            ),
5846            (
5847                6..8,
5848                HighlightStyle {
5849                    color: Some(Color::green()),
5850                    weight: Some(fonts::Weight::BOLD),
5851                    ..Default::default()
5852                },
5853            ),
5854            (
5855                8..9,
5856                HighlightStyle {
5857                    weight: Some(fonts::Weight::BOLD),
5858                    ..Default::default()
5859                },
5860            ),
5861        ]
5862    );
5863}
5864
5865#[gpui::test]
5866async fn go_to_hunk(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
5867    init_test(cx, |_| {});
5868
5869    let mut cx = EditorTestContext::new(cx);
5870
5871    let diff_base = r#"
5872        use some::mod;
5873
5874        const A: u32 = 42;
5875
5876        fn main() {
5877            println!("hello");
5878
5879            println!("world");
5880        }
5881        "#
5882    .unindent();
5883
5884    // Edits are modified, removed, modified, added
5885    cx.set_state(
5886        &r#"
5887        use some::modified;
5888
5889        ˇ
5890        fn main() {
5891            println!("hello there");
5892
5893            println!("around the");
5894            println!("world");
5895        }
5896        "#
5897        .unindent(),
5898    );
5899
5900    cx.set_diff_base(Some(&diff_base));
5901    deterministic.run_until_parked();
5902
5903    cx.update_editor(|editor, cx| {
5904        //Wrap around the bottom of the buffer
5905        for _ in 0..3 {
5906            editor.go_to_hunk(&GoToHunk, cx);
5907        }
5908    });
5909
5910    cx.assert_editor_state(
5911        &r#"
5912        ˇuse some::modified;
5913
5914
5915        fn main() {
5916            println!("hello there");
5917
5918            println!("around the");
5919            println!("world");
5920        }
5921        "#
5922        .unindent(),
5923    );
5924
5925    cx.update_editor(|editor, cx| {
5926        //Wrap around the top of the buffer
5927        for _ in 0..2 {
5928            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
5929        }
5930    });
5931
5932    cx.assert_editor_state(
5933        &r#"
5934        use some::modified;
5935
5936
5937        fn main() {
5938        ˇ    println!("hello there");
5939
5940            println!("around the");
5941            println!("world");
5942        }
5943        "#
5944        .unindent(),
5945    );
5946
5947    cx.update_editor(|editor, cx| {
5948        editor.fold(&Fold, cx);
5949
5950        //Make sure that the fold only gets one hunk
5951        for _ in 0..4 {
5952            editor.go_to_hunk(&GoToHunk, cx);
5953        }
5954    });
5955
5956    cx.assert_editor_state(
5957        &r#"
5958        ˇuse some::modified;
5959
5960
5961        fn main() {
5962            println!("hello there");
5963
5964            println!("around the");
5965            println!("world");
5966        }
5967        "#
5968        .unindent(),
5969    );
5970}
5971
5972#[test]
5973fn test_split_words() {
5974    fn split<'a>(text: &'a str) -> Vec<&'a str> {
5975        split_words(text).collect()
5976    }
5977
5978    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
5979    assert_eq!(split("hello_world"), &["hello_", "world"]);
5980    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
5981    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
5982    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
5983    assert_eq!(split("helloworld"), &["helloworld"]);
5984}
5985
5986#[gpui::test]
5987async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
5988    init_test(cx, |_| {});
5989
5990    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
5991    let mut assert = |before, after| {
5992        let _state_context = cx.set_state(before);
5993        cx.update_editor(|editor, cx| {
5994            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
5995        });
5996        cx.assert_editor_state(after);
5997    };
5998
5999    // Outside bracket jumps to outside of matching bracket
6000    assert("console.logˇ(var);", "console.log(var)ˇ;");
6001    assert("console.log(var)ˇ;", "console.logˇ(var);");
6002
6003    // Inside bracket jumps to inside of matching bracket
6004    assert("console.log(ˇvar);", "console.log(varˇ);");
6005    assert("console.log(varˇ);", "console.log(ˇvar);");
6006
6007    // When outside a bracket and inside, favor jumping to the inside bracket
6008    assert(
6009        "console.log('foo', [1, 2, 3]ˇ);",
6010        "console.log(ˇ'foo', [1, 2, 3]);",
6011    );
6012    assert(
6013        "console.log(ˇ'foo', [1, 2, 3]);",
6014        "console.log('foo', [1, 2, 3]ˇ);",
6015    );
6016
6017    // Bias forward if two options are equally likely
6018    assert(
6019        "let result = curried_fun()ˇ();",
6020        "let result = curried_fun()()ˇ;",
6021    );
6022
6023    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
6024    assert(
6025        indoc! {"
6026            function test() {
6027                console.log('test')ˇ
6028            }"},
6029        indoc! {"
6030            function test() {
6031                console.logˇ('test')
6032            }"},
6033    );
6034}
6035
6036#[gpui::test(iterations = 10)]
6037async fn test_copilot(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
6038    init_test(cx, |_| {});
6039
6040    let (copilot, copilot_lsp) = Copilot::fake(cx);
6041    cx.update(|cx| cx.set_global(copilot));
6042    let mut cx = EditorLspTestContext::new_rust(
6043        lsp::ServerCapabilities {
6044            completion_provider: Some(lsp::CompletionOptions {
6045                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
6046                ..Default::default()
6047            }),
6048            ..Default::default()
6049        },
6050        cx,
6051    )
6052    .await;
6053
6054    // When inserting, ensure autocompletion is favored over Copilot suggestions.
6055    cx.set_state(indoc! {"
6056        oneˇ
6057        two
6058        three
6059    "});
6060    cx.simulate_keystroke(".");
6061    let _ = handle_completion_request(
6062        &mut cx,
6063        indoc! {"
6064            one.|<>
6065            two
6066            three
6067        "},
6068        vec!["completion_a", "completion_b"],
6069    );
6070    handle_copilot_completion_request(
6071        &copilot_lsp,
6072        vec![copilot::request::Completion {
6073            text: "one.copilot1".into(),
6074            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6075            ..Default::default()
6076        }],
6077        vec![],
6078    );
6079    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6080    cx.update_editor(|editor, cx| {
6081        assert!(editor.context_menu_visible());
6082        assert!(!editor.has_active_copilot_suggestion(cx));
6083
6084        // Confirming a completion inserts it and hides the context menu, without showing
6085        // the copilot suggestion afterwards.
6086        editor
6087            .confirm_completion(&Default::default(), cx)
6088            .unwrap()
6089            .detach();
6090        assert!(!editor.context_menu_visible());
6091        assert!(!editor.has_active_copilot_suggestion(cx));
6092        assert_eq!(editor.text(cx), "one.completion_a\ntwo\nthree\n");
6093        assert_eq!(editor.display_text(cx), "one.completion_a\ntwo\nthree\n");
6094    });
6095
6096    // Ensure Copilot suggestions are shown right away if no autocompletion is available.
6097    cx.set_state(indoc! {"
6098        oneˇ
6099        two
6100        three
6101    "});
6102    cx.simulate_keystroke(".");
6103    let _ = handle_completion_request(
6104        &mut cx,
6105        indoc! {"
6106            one.|<>
6107            two
6108            three
6109        "},
6110        vec![],
6111    );
6112    handle_copilot_completion_request(
6113        &copilot_lsp,
6114        vec![copilot::request::Completion {
6115            text: "one.copilot1".into(),
6116            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6117            ..Default::default()
6118        }],
6119        vec![],
6120    );
6121    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6122    cx.update_editor(|editor, cx| {
6123        assert!(!editor.context_menu_visible());
6124        assert!(editor.has_active_copilot_suggestion(cx));
6125        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
6126        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
6127    });
6128
6129    // Reset editor, and ensure autocompletion is still favored over Copilot suggestions.
6130    cx.set_state(indoc! {"
6131        oneˇ
6132        two
6133        three
6134    "});
6135    cx.simulate_keystroke(".");
6136    let _ = handle_completion_request(
6137        &mut cx,
6138        indoc! {"
6139            one.|<>
6140            two
6141            three
6142        "},
6143        vec!["completion_a", "completion_b"],
6144    );
6145    handle_copilot_completion_request(
6146        &copilot_lsp,
6147        vec![copilot::request::Completion {
6148            text: "one.copilot1".into(),
6149            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6150            ..Default::default()
6151        }],
6152        vec![],
6153    );
6154    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6155    cx.update_editor(|editor, cx| {
6156        assert!(editor.context_menu_visible());
6157        assert!(!editor.has_active_copilot_suggestion(cx));
6158
6159        // When hiding the context menu, the Copilot suggestion becomes visible.
6160        editor.hide_context_menu(cx);
6161        assert!(!editor.context_menu_visible());
6162        assert!(editor.has_active_copilot_suggestion(cx));
6163        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
6164        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
6165    });
6166
6167    // Ensure existing completion is interpolated when inserting again.
6168    cx.simulate_keystroke("c");
6169    deterministic.run_until_parked();
6170    cx.update_editor(|editor, cx| {
6171        assert!(!editor.context_menu_visible());
6172        assert!(editor.has_active_copilot_suggestion(cx));
6173        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
6174        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6175    });
6176
6177    // After debouncing, new Copilot completions should be requested.
6178    handle_copilot_completion_request(
6179        &copilot_lsp,
6180        vec![copilot::request::Completion {
6181            text: "one.copilot2".into(),
6182            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 5)),
6183            ..Default::default()
6184        }],
6185        vec![],
6186    );
6187    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6188    cx.update_editor(|editor, cx| {
6189        assert!(!editor.context_menu_visible());
6190        assert!(editor.has_active_copilot_suggestion(cx));
6191        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6192        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6193
6194        // Canceling should remove the active Copilot suggestion.
6195        editor.cancel(&Default::default(), cx);
6196        assert!(!editor.has_active_copilot_suggestion(cx));
6197        assert_eq!(editor.display_text(cx), "one.c\ntwo\nthree\n");
6198        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6199
6200        // After canceling, tabbing shouldn't insert the previously shown suggestion.
6201        editor.tab(&Default::default(), cx);
6202        assert!(!editor.has_active_copilot_suggestion(cx));
6203        assert_eq!(editor.display_text(cx), "one.c   \ntwo\nthree\n");
6204        assert_eq!(editor.text(cx), "one.c   \ntwo\nthree\n");
6205
6206        // When undoing the previously active suggestion is shown again.
6207        editor.undo(&Default::default(), cx);
6208        assert!(editor.has_active_copilot_suggestion(cx));
6209        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6210        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6211    });
6212
6213    // If an edit occurs outside of this editor, the suggestion is still correctly interpolated.
6214    cx.update_buffer(|buffer, cx| buffer.edit([(5..5, "o")], None, cx));
6215    cx.update_editor(|editor, cx| {
6216        assert!(editor.has_active_copilot_suggestion(cx));
6217        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6218        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6219
6220        // Tabbing when there is an active suggestion inserts it.
6221        editor.tab(&Default::default(), cx);
6222        assert!(!editor.has_active_copilot_suggestion(cx));
6223        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6224        assert_eq!(editor.text(cx), "one.copilot2\ntwo\nthree\n");
6225
6226        // When undoing the previously active suggestion is shown again.
6227        editor.undo(&Default::default(), cx);
6228        assert!(editor.has_active_copilot_suggestion(cx));
6229        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6230        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6231
6232        // Hide suggestion.
6233        editor.cancel(&Default::default(), cx);
6234        assert!(!editor.has_active_copilot_suggestion(cx));
6235        assert_eq!(editor.display_text(cx), "one.co\ntwo\nthree\n");
6236        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6237    });
6238
6239    // If an edit occurs outside of this editor but no suggestion is being shown,
6240    // we won't make it visible.
6241    cx.update_buffer(|buffer, cx| buffer.edit([(6..6, "p")], None, cx));
6242    cx.update_editor(|editor, cx| {
6243        assert!(!editor.has_active_copilot_suggestion(cx));
6244        assert_eq!(editor.display_text(cx), "one.cop\ntwo\nthree\n");
6245        assert_eq!(editor.text(cx), "one.cop\ntwo\nthree\n");
6246    });
6247
6248    // Reset the editor to verify how suggestions behave when tabbing on leading indentation.
6249    cx.update_editor(|editor, cx| {
6250        editor.set_text("fn foo() {\n  \n}", cx);
6251        editor.change_selections(None, cx, |s| {
6252            s.select_ranges([Point::new(1, 2)..Point::new(1, 2)])
6253        });
6254    });
6255    handle_copilot_completion_request(
6256        &copilot_lsp,
6257        vec![copilot::request::Completion {
6258            text: "    let x = 4;".into(),
6259            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
6260            ..Default::default()
6261        }],
6262        vec![],
6263    );
6264
6265    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
6266    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6267    cx.update_editor(|editor, cx| {
6268        assert!(editor.has_active_copilot_suggestion(cx));
6269        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6270        assert_eq!(editor.text(cx), "fn foo() {\n  \n}");
6271
6272        // Tabbing inside of leading whitespace inserts indentation without accepting the suggestion.
6273        editor.tab(&Default::default(), cx);
6274        assert!(editor.has_active_copilot_suggestion(cx));
6275        assert_eq!(editor.text(cx), "fn foo() {\n    \n}");
6276        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6277
6278        // Tabbing again accepts the suggestion.
6279        editor.tab(&Default::default(), cx);
6280        assert!(!editor.has_active_copilot_suggestion(cx));
6281        assert_eq!(editor.text(cx), "fn foo() {\n    let x = 4;\n}");
6282        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6283    });
6284}
6285
6286#[gpui::test]
6287async fn test_copilot_completion_invalidation(
6288    deterministic: Arc<Deterministic>,
6289    cx: &mut gpui::TestAppContext,
6290) {
6291    init_test(cx, |_| {});
6292
6293    let (copilot, copilot_lsp) = Copilot::fake(cx);
6294    cx.update(|cx| cx.set_global(copilot));
6295    let mut cx = EditorLspTestContext::new_rust(
6296        lsp::ServerCapabilities {
6297            completion_provider: Some(lsp::CompletionOptions {
6298                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
6299                ..Default::default()
6300            }),
6301            ..Default::default()
6302        },
6303        cx,
6304    )
6305    .await;
6306
6307    cx.set_state(indoc! {"
6308        one
6309        twˇ
6310        three
6311    "});
6312
6313    handle_copilot_completion_request(
6314        &copilot_lsp,
6315        vec![copilot::request::Completion {
6316            text: "two.foo()".into(),
6317            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
6318            ..Default::default()
6319        }],
6320        vec![],
6321    );
6322    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
6323    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6324    cx.update_editor(|editor, cx| {
6325        assert!(editor.has_active_copilot_suggestion(cx));
6326        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6327        assert_eq!(editor.text(cx), "one\ntw\nthree\n");
6328
6329        editor.backspace(&Default::default(), cx);
6330        assert!(editor.has_active_copilot_suggestion(cx));
6331        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6332        assert_eq!(editor.text(cx), "one\nt\nthree\n");
6333
6334        editor.backspace(&Default::default(), cx);
6335        assert!(editor.has_active_copilot_suggestion(cx));
6336        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6337        assert_eq!(editor.text(cx), "one\n\nthree\n");
6338
6339        // Deleting across the original suggestion range invalidates it.
6340        editor.backspace(&Default::default(), cx);
6341        assert!(!editor.has_active_copilot_suggestion(cx));
6342        assert_eq!(editor.display_text(cx), "one\nthree\n");
6343        assert_eq!(editor.text(cx), "one\nthree\n");
6344
6345        // Undoing the deletion restores the suggestion.
6346        editor.undo(&Default::default(), cx);
6347        assert!(editor.has_active_copilot_suggestion(cx));
6348        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6349        assert_eq!(editor.text(cx), "one\n\nthree\n");
6350    });
6351}
6352
6353#[gpui::test]
6354async fn test_copilot_multibuffer(
6355    deterministic: Arc<Deterministic>,
6356    cx: &mut gpui::TestAppContext,
6357) {
6358    init_test(cx, |_| {});
6359
6360    let (copilot, copilot_lsp) = Copilot::fake(cx);
6361    cx.update(|cx| cx.set_global(copilot));
6362
6363    let buffer_1 = cx.add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx));
6364    let buffer_2 = cx.add_model(|cx| Buffer::new(0, "c = 3\nd = 4\n", cx));
6365    let multibuffer = cx.add_model(|cx| {
6366        let mut multibuffer = MultiBuffer::new(0);
6367        multibuffer.push_excerpts(
6368            buffer_1.clone(),
6369            [ExcerptRange {
6370                context: Point::new(0, 0)..Point::new(2, 0),
6371                primary: None,
6372            }],
6373            cx,
6374        );
6375        multibuffer.push_excerpts(
6376            buffer_2.clone(),
6377            [ExcerptRange {
6378                context: Point::new(0, 0)..Point::new(2, 0),
6379                primary: None,
6380            }],
6381            cx,
6382        );
6383        multibuffer
6384    });
6385    let (_, editor) = cx.add_window(|cx| build_editor(multibuffer, cx));
6386
6387    handle_copilot_completion_request(
6388        &copilot_lsp,
6389        vec![copilot::request::Completion {
6390            text: "b = 2 + a".into(),
6391            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 5)),
6392            ..Default::default()
6393        }],
6394        vec![],
6395    );
6396    editor.update(cx, |editor, cx| {
6397        // Ensure copilot suggestions are shown for the first excerpt.
6398        editor.change_selections(None, cx, |s| {
6399            s.select_ranges([Point::new(1, 5)..Point::new(1, 5)])
6400        });
6401        editor.next_copilot_suggestion(&Default::default(), cx);
6402    });
6403    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6404    editor.update(cx, |editor, cx| {
6405        assert!(editor.has_active_copilot_suggestion(cx));
6406        assert_eq!(
6407            editor.display_text(cx),
6408            "\n\na = 1\nb = 2 + a\n\n\n\nc = 3\nd = 4\n"
6409        );
6410        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
6411    });
6412
6413    handle_copilot_completion_request(
6414        &copilot_lsp,
6415        vec![copilot::request::Completion {
6416            text: "d = 4 + c".into(),
6417            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 6)),
6418            ..Default::default()
6419        }],
6420        vec![],
6421    );
6422    editor.update(cx, |editor, cx| {
6423        // Move to another excerpt, ensuring the suggestion gets cleared.
6424        editor.change_selections(None, cx, |s| {
6425            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
6426        });
6427        assert!(!editor.has_active_copilot_suggestion(cx));
6428        assert_eq!(
6429            editor.display_text(cx),
6430            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4\n"
6431        );
6432        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
6433
6434        // Type a character, ensuring we don't even try to interpolate the previous suggestion.
6435        editor.handle_input(" ", cx);
6436        assert!(!editor.has_active_copilot_suggestion(cx));
6437        assert_eq!(
6438            editor.display_text(cx),
6439            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 \n"
6440        );
6441        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
6442    });
6443
6444    // Ensure the new suggestion is displayed when the debounce timeout expires.
6445    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6446    editor.update(cx, |editor, cx| {
6447        assert!(editor.has_active_copilot_suggestion(cx));
6448        assert_eq!(
6449            editor.display_text(cx),
6450            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 + c\n"
6451        );
6452        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
6453    });
6454}
6455
6456#[gpui::test]
6457async fn test_copilot_disabled_globs(
6458    deterministic: Arc<Deterministic>,
6459    cx: &mut gpui::TestAppContext,
6460) {
6461    init_test(cx, |settings| {
6462        settings
6463            .copilot
6464            .get_or_insert(Default::default())
6465            .disabled_globs = Some(vec![".env*".to_string()]);
6466    });
6467
6468    let (copilot, copilot_lsp) = Copilot::fake(cx);
6469    cx.update(|cx| cx.set_global(copilot));
6470
6471    let fs = FakeFs::new(cx.background());
6472    fs.insert_tree(
6473        "/test",
6474        json!({
6475            ".env": "SECRET=something\n",
6476            "README.md": "hello\n"
6477        }),
6478    )
6479    .await;
6480    let project = Project::test(fs, ["/test".as_ref()], cx).await;
6481
6482    let private_buffer = project
6483        .update(cx, |project, cx| {
6484            project.open_local_buffer("/test/.env", cx)
6485        })
6486        .await
6487        .unwrap();
6488    let public_buffer = project
6489        .update(cx, |project, cx| {
6490            project.open_local_buffer("/test/README.md", cx)
6491        })
6492        .await
6493        .unwrap();
6494
6495    let multibuffer = cx.add_model(|cx| {
6496        let mut multibuffer = MultiBuffer::new(0);
6497        multibuffer.push_excerpts(
6498            private_buffer.clone(),
6499            [ExcerptRange {
6500                context: Point::new(0, 0)..Point::new(1, 0),
6501                primary: None,
6502            }],
6503            cx,
6504        );
6505        multibuffer.push_excerpts(
6506            public_buffer.clone(),
6507            [ExcerptRange {
6508                context: Point::new(0, 0)..Point::new(1, 0),
6509                primary: None,
6510            }],
6511            cx,
6512        );
6513        multibuffer
6514    });
6515    let (_, editor) = cx.add_window(|cx| build_editor(multibuffer, cx));
6516
6517    let mut copilot_requests = copilot_lsp
6518        .handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| async move {
6519            Ok(copilot::request::GetCompletionsResult {
6520                completions: vec![copilot::request::Completion {
6521                    text: "next line".into(),
6522                    range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
6523                    ..Default::default()
6524                }],
6525            })
6526        });
6527
6528    editor.update(cx, |editor, cx| {
6529        editor.change_selections(None, cx, |selections| {
6530            selections.select_ranges([Point::new(0, 0)..Point::new(0, 0)])
6531        });
6532        editor.next_copilot_suggestion(&Default::default(), cx);
6533    });
6534
6535    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6536    assert!(copilot_requests.try_next().is_err());
6537
6538    editor.update(cx, |editor, cx| {
6539        editor.change_selections(None, cx, |s| {
6540            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
6541        });
6542        editor.next_copilot_suggestion(&Default::default(), cx);
6543    });
6544
6545    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6546    assert!(copilot_requests.try_next().is_ok());
6547}
6548
6549fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
6550    let point = DisplayPoint::new(row as u32, column as u32);
6551    point..point
6552}
6553
6554fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
6555    let (text, ranges) = marked_text_ranges(marked_text, true);
6556    assert_eq!(view.text(cx), text);
6557    assert_eq!(
6558        view.selections.ranges(cx),
6559        ranges,
6560        "Assert selections are {}",
6561        marked_text
6562    );
6563}
6564
6565/// Handle completion request passing a marked string specifying where the completion
6566/// should be triggered from using '|' character, what range should be replaced, and what completions
6567/// should be returned using '<' and '>' to delimit the range
6568fn handle_completion_request<'a>(
6569    cx: &mut EditorLspTestContext<'a>,
6570    marked_string: &str,
6571    completions: Vec<&'static str>,
6572) -> impl Future<Output = ()> {
6573    let complete_from_marker: TextRangeMarker = '|'.into();
6574    let replace_range_marker: TextRangeMarker = ('<', '>').into();
6575    let (_, mut marked_ranges) = marked_text_ranges_by(
6576        marked_string,
6577        vec![complete_from_marker.clone(), replace_range_marker.clone()],
6578    );
6579
6580    let complete_from_position =
6581        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
6582    let replace_range =
6583        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
6584
6585    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
6586        let completions = completions.clone();
6587        async move {
6588            assert_eq!(params.text_document_position.text_document.uri, url.clone());
6589            assert_eq!(
6590                params.text_document_position.position,
6591                complete_from_position
6592            );
6593            Ok(Some(lsp::CompletionResponse::Array(
6594                completions
6595                    .iter()
6596                    .map(|completion_text| lsp::CompletionItem {
6597                        label: completion_text.to_string(),
6598                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
6599                            range: replace_range,
6600                            new_text: completion_text.to_string(),
6601                        })),
6602                        ..Default::default()
6603                    })
6604                    .collect(),
6605            )))
6606        }
6607    });
6608
6609    async move {
6610        request.next().await;
6611    }
6612}
6613
6614fn handle_resolve_completion_request<'a>(
6615    cx: &mut EditorLspTestContext<'a>,
6616    edits: Option<Vec<(&'static str, &'static str)>>,
6617) -> impl Future<Output = ()> {
6618    let edits = edits.map(|edits| {
6619        edits
6620            .iter()
6621            .map(|(marked_string, new_text)| {
6622                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
6623                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
6624                lsp::TextEdit::new(replace_range, new_text.to_string())
6625            })
6626            .collect::<Vec<_>>()
6627    });
6628
6629    let mut request =
6630        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
6631            let edits = edits.clone();
6632            async move {
6633                Ok(lsp::CompletionItem {
6634                    additional_text_edits: edits,
6635                    ..Default::default()
6636                })
6637            }
6638        });
6639
6640    async move {
6641        request.next().await;
6642    }
6643}
6644
6645fn handle_copilot_completion_request(
6646    lsp: &lsp::FakeLanguageServer,
6647    completions: Vec<copilot::request::Completion>,
6648    completions_cycling: Vec<copilot::request::Completion>,
6649) {
6650    lsp.handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| {
6651        let completions = completions.clone();
6652        async move {
6653            Ok(copilot::request::GetCompletionsResult {
6654                completions: completions.clone(),
6655            })
6656        }
6657    });
6658    lsp.handle_request::<copilot::request::GetCompletionsCycling, _, _>(move |_params, _cx| {
6659        let completions_cycling = completions_cycling.clone();
6660        async move {
6661            Ok(copilot::request::GetCompletionsResult {
6662                completions: completions_cycling.clone(),
6663            })
6664        }
6665    });
6666}
6667
6668pub(crate) fn update_test_settings(
6669    cx: &mut TestAppContext,
6670    f: impl Fn(&mut AllLanguageSettingsContent),
6671) {
6672    cx.update(|cx| {
6673        cx.update_global::<SettingsStore, _, _>(|store, cx| {
6674            store.update_user_settings::<AllLanguageSettings>(cx, f);
6675        });
6676    });
6677}
6678
6679pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
6680    cx.foreground().forbid_parking();
6681
6682    cx.update(|cx| {
6683        cx.set_global(SettingsStore::test(cx));
6684        theme::init((), cx);
6685        client::init_settings(cx);
6686        language::init(cx);
6687        Project::init_settings(cx);
6688        workspace::init_settings(cx);
6689        crate::init(cx);
6690    });
6691
6692    update_test_settings(cx, f);
6693}