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 follower_edit_event_count = Rc::new(RefCell::new(0));
5521    let pending_update = Rc::new(RefCell::new(None));
5522    follower.update(cx, {
5523        let update = pending_update.clone();
5524        let is_still_following = is_still_following.clone();
5525        let follower_edit_event_count = follower_edit_event_count.clone();
5526        |_, cx| {
5527            cx.subscribe(&leader, move |_, leader, event, cx| {
5528                leader
5529                    .read(cx)
5530                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
5531            })
5532            .detach();
5533
5534            cx.subscribe(&follower, move |_, _, event, cx| {
5535                if Editor::should_unfollow_on_event(event, cx) {
5536                    *is_still_following.borrow_mut() = false;
5537                }
5538                if let Event::BufferEdited = event {
5539                    *follower_edit_event_count.borrow_mut() += 1;
5540                }
5541            })
5542            .detach();
5543        }
5544    });
5545
5546    // Update the selections only
5547    leader.update(cx, |leader, cx| {
5548        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
5549    });
5550    follower
5551        .update(cx, |follower, cx| {
5552            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5553        })
5554        .await
5555        .unwrap();
5556    follower.read_with(cx, |follower, cx| {
5557        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
5558    });
5559    assert_eq!(*is_still_following.borrow(), true);
5560    assert_eq!(*follower_edit_event_count.borrow(), 0);
5561
5562    // Update the scroll position only
5563    leader.update(cx, |leader, cx| {
5564        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
5565    });
5566    follower
5567        .update(cx, |follower, cx| {
5568            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5569        })
5570        .await
5571        .unwrap();
5572    assert_eq!(
5573        follower.update(cx, |follower, cx| follower.scroll_position(cx)),
5574        vec2f(1.5, 3.5)
5575    );
5576    assert_eq!(*is_still_following.borrow(), true);
5577    assert_eq!(*follower_edit_event_count.borrow(), 0);
5578
5579    // Update the selections and scroll position. The follower's scroll position is updated
5580    // via autoscroll, not via the leader's exact scroll position.
5581    leader.update(cx, |leader, cx| {
5582        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
5583        leader.request_autoscroll(Autoscroll::newest(), cx);
5584        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
5585    });
5586    follower
5587        .update(cx, |follower, cx| {
5588            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5589        })
5590        .await
5591        .unwrap();
5592    follower.update(cx, |follower, cx| {
5593        assert_eq!(follower.scroll_position(cx), vec2f(1.5, 0.0));
5594        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
5595    });
5596    assert_eq!(*is_still_following.borrow(), true);
5597
5598    // Creating a pending selection that precedes another selection
5599    leader.update(cx, |leader, cx| {
5600        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
5601        leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
5602    });
5603    follower
5604        .update(cx, |follower, cx| {
5605            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5606        })
5607        .await
5608        .unwrap();
5609    follower.read_with(cx, |follower, cx| {
5610        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
5611    });
5612    assert_eq!(*is_still_following.borrow(), true);
5613
5614    // Extend the pending selection so that it surrounds another selection
5615    leader.update(cx, |leader, cx| {
5616        leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
5617    });
5618    follower
5619        .update(cx, |follower, cx| {
5620            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5621        })
5622        .await
5623        .unwrap();
5624    follower.read_with(cx, |follower, cx| {
5625        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
5626    });
5627
5628    // Scrolling locally breaks the follow
5629    follower.update(cx, |follower, cx| {
5630        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
5631        follower.set_scroll_anchor(
5632            ScrollAnchor {
5633                top_anchor,
5634                offset: vec2f(0.0, 0.5),
5635            },
5636            cx,
5637        );
5638    });
5639    assert_eq!(*is_still_following.borrow(), false);
5640}
5641
5642#[gpui::test]
5643async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
5644    init_test(cx, |_| {});
5645
5646    let fs = FakeFs::new(cx.background());
5647    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5648    let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
5649    let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
5650
5651    let leader = pane.update(cx, |_, cx| {
5652        let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
5653        cx.add_view(|cx| build_editor(multibuffer.clone(), cx))
5654    });
5655
5656    // Start following the editor when it has no excerpts.
5657    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
5658    let follower_1 = cx
5659        .update(|cx| {
5660            Editor::from_state_proto(
5661                pane.clone(),
5662                project.clone(),
5663                ViewId {
5664                    creator: Default::default(),
5665                    id: 0,
5666                },
5667                &mut state_message,
5668                cx,
5669            )
5670        })
5671        .unwrap()
5672        .await
5673        .unwrap();
5674
5675    let update_message = Rc::new(RefCell::new(None));
5676    follower_1.update(cx, {
5677        let update = update_message.clone();
5678        |_, cx| {
5679            cx.subscribe(&leader, move |_, leader, event, cx| {
5680                leader
5681                    .read(cx)
5682                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
5683            })
5684            .detach();
5685        }
5686    });
5687
5688    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
5689        (
5690            project
5691                .create_buffer("abc\ndef\nghi\njkl\n", None, cx)
5692                .unwrap(),
5693            project
5694                .create_buffer("mno\npqr\nstu\nvwx\n", None, cx)
5695                .unwrap(),
5696        )
5697    });
5698
5699    // Insert some excerpts.
5700    leader.update(cx, |leader, cx| {
5701        leader.buffer.update(cx, |multibuffer, cx| {
5702            let excerpt_ids = multibuffer.push_excerpts(
5703                buffer_1.clone(),
5704                [
5705                    ExcerptRange {
5706                        context: 1..6,
5707                        primary: None,
5708                    },
5709                    ExcerptRange {
5710                        context: 12..15,
5711                        primary: None,
5712                    },
5713                    ExcerptRange {
5714                        context: 0..3,
5715                        primary: None,
5716                    },
5717                ],
5718                cx,
5719            );
5720            multibuffer.insert_excerpts_after(
5721                excerpt_ids[0],
5722                buffer_2.clone(),
5723                [
5724                    ExcerptRange {
5725                        context: 8..12,
5726                        primary: None,
5727                    },
5728                    ExcerptRange {
5729                        context: 0..6,
5730                        primary: None,
5731                    },
5732                ],
5733                cx,
5734            );
5735        });
5736    });
5737
5738    // Apply the update of adding the excerpts.
5739    follower_1
5740        .update(cx, |follower, cx| {
5741            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
5742        })
5743        .await
5744        .unwrap();
5745    assert_eq!(
5746        follower_1.read_with(cx, |editor, cx| editor.text(cx)),
5747        leader.read_with(cx, |editor, cx| editor.text(cx))
5748    );
5749    update_message.borrow_mut().take();
5750
5751    // Start following separately after it already has excerpts.
5752    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
5753    let follower_2 = cx
5754        .update(|cx| {
5755            Editor::from_state_proto(
5756                pane.clone(),
5757                project.clone(),
5758                ViewId {
5759                    creator: Default::default(),
5760                    id: 0,
5761                },
5762                &mut state_message,
5763                cx,
5764            )
5765        })
5766        .unwrap()
5767        .await
5768        .unwrap();
5769    assert_eq!(
5770        follower_2.read_with(cx, |editor, cx| editor.text(cx)),
5771        leader.read_with(cx, |editor, cx| editor.text(cx))
5772    );
5773
5774    // Remove some excerpts.
5775    leader.update(cx, |leader, cx| {
5776        leader.buffer.update(cx, |multibuffer, cx| {
5777            let excerpt_ids = multibuffer.excerpt_ids();
5778            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
5779            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
5780        });
5781    });
5782
5783    // Apply the update of removing the excerpts.
5784    follower_1
5785        .update(cx, |follower, cx| {
5786            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
5787        })
5788        .await
5789        .unwrap();
5790    follower_2
5791        .update(cx, |follower, cx| {
5792            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
5793        })
5794        .await
5795        .unwrap();
5796    update_message.borrow_mut().take();
5797    assert_eq!(
5798        follower_1.read_with(cx, |editor, cx| editor.text(cx)),
5799        leader.read_with(cx, |editor, cx| editor.text(cx))
5800    );
5801}
5802
5803#[test]
5804fn test_combine_syntax_and_fuzzy_match_highlights() {
5805    let string = "abcdefghijklmnop";
5806    let syntax_ranges = [
5807        (
5808            0..3,
5809            HighlightStyle {
5810                color: Some(Color::red()),
5811                ..Default::default()
5812            },
5813        ),
5814        (
5815            4..8,
5816            HighlightStyle {
5817                color: Some(Color::green()),
5818                ..Default::default()
5819            },
5820        ),
5821    ];
5822    let match_indices = [4, 6, 7, 8];
5823    assert_eq!(
5824        combine_syntax_and_fuzzy_match_highlights(
5825            string,
5826            Default::default(),
5827            syntax_ranges.into_iter(),
5828            &match_indices,
5829        ),
5830        &[
5831            (
5832                0..3,
5833                HighlightStyle {
5834                    color: Some(Color::red()),
5835                    ..Default::default()
5836                },
5837            ),
5838            (
5839                4..5,
5840                HighlightStyle {
5841                    color: Some(Color::green()),
5842                    weight: Some(fonts::Weight::BOLD),
5843                    ..Default::default()
5844                },
5845            ),
5846            (
5847                5..6,
5848                HighlightStyle {
5849                    color: Some(Color::green()),
5850                    ..Default::default()
5851                },
5852            ),
5853            (
5854                6..8,
5855                HighlightStyle {
5856                    color: Some(Color::green()),
5857                    weight: Some(fonts::Weight::BOLD),
5858                    ..Default::default()
5859                },
5860            ),
5861            (
5862                8..9,
5863                HighlightStyle {
5864                    weight: Some(fonts::Weight::BOLD),
5865                    ..Default::default()
5866                },
5867            ),
5868        ]
5869    );
5870}
5871
5872#[gpui::test]
5873async fn go_to_hunk(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
5874    init_test(cx, |_| {});
5875
5876    let mut cx = EditorTestContext::new(cx);
5877
5878    let diff_base = r#"
5879        use some::mod;
5880
5881        const A: u32 = 42;
5882
5883        fn main() {
5884            println!("hello");
5885
5886            println!("world");
5887        }
5888        "#
5889    .unindent();
5890
5891    // Edits are modified, removed, modified, added
5892    cx.set_state(
5893        &r#"
5894        use some::modified;
5895
5896        ˇ
5897        fn main() {
5898            println!("hello there");
5899
5900            println!("around the");
5901            println!("world");
5902        }
5903        "#
5904        .unindent(),
5905    );
5906
5907    cx.set_diff_base(Some(&diff_base));
5908    deterministic.run_until_parked();
5909
5910    cx.update_editor(|editor, cx| {
5911        //Wrap around the bottom of the buffer
5912        for _ in 0..3 {
5913            editor.go_to_hunk(&GoToHunk, cx);
5914        }
5915    });
5916
5917    cx.assert_editor_state(
5918        &r#"
5919        ˇuse some::modified;
5920
5921
5922        fn main() {
5923            println!("hello there");
5924
5925            println!("around the");
5926            println!("world");
5927        }
5928        "#
5929        .unindent(),
5930    );
5931
5932    cx.update_editor(|editor, cx| {
5933        //Wrap around the top of the buffer
5934        for _ in 0..2 {
5935            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
5936        }
5937    });
5938
5939    cx.assert_editor_state(
5940        &r#"
5941        use some::modified;
5942
5943
5944        fn main() {
5945        ˇ    println!("hello there");
5946
5947            println!("around the");
5948            println!("world");
5949        }
5950        "#
5951        .unindent(),
5952    );
5953
5954    cx.update_editor(|editor, cx| {
5955        editor.fold(&Fold, cx);
5956
5957        //Make sure that the fold only gets one hunk
5958        for _ in 0..4 {
5959            editor.go_to_hunk(&GoToHunk, cx);
5960        }
5961    });
5962
5963    cx.assert_editor_state(
5964        &r#"
5965        ˇuse some::modified;
5966
5967
5968        fn main() {
5969            println!("hello there");
5970
5971            println!("around the");
5972            println!("world");
5973        }
5974        "#
5975        .unindent(),
5976    );
5977}
5978
5979#[test]
5980fn test_split_words() {
5981    fn split<'a>(text: &'a str) -> Vec<&'a str> {
5982        split_words(text).collect()
5983    }
5984
5985    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
5986    assert_eq!(split("hello_world"), &["hello_", "world"]);
5987    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
5988    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
5989    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
5990    assert_eq!(split("helloworld"), &["helloworld"]);
5991}
5992
5993#[gpui::test]
5994async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
5995    init_test(cx, |_| {});
5996
5997    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
5998    let mut assert = |before, after| {
5999        let _state_context = cx.set_state(before);
6000        cx.update_editor(|editor, cx| {
6001            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
6002        });
6003        cx.assert_editor_state(after);
6004    };
6005
6006    // Outside bracket jumps to outside of matching bracket
6007    assert("console.logˇ(var);", "console.log(var)ˇ;");
6008    assert("console.log(var)ˇ;", "console.logˇ(var);");
6009
6010    // Inside bracket jumps to inside of matching bracket
6011    assert("console.log(ˇvar);", "console.log(varˇ);");
6012    assert("console.log(varˇ);", "console.log(ˇvar);");
6013
6014    // When outside a bracket and inside, favor jumping to the inside bracket
6015    assert(
6016        "console.log('foo', [1, 2, 3]ˇ);",
6017        "console.log(ˇ'foo', [1, 2, 3]);",
6018    );
6019    assert(
6020        "console.log(ˇ'foo', [1, 2, 3]);",
6021        "console.log('foo', [1, 2, 3]ˇ);",
6022    );
6023
6024    // Bias forward if two options are equally likely
6025    assert(
6026        "let result = curried_fun()ˇ();",
6027        "let result = curried_fun()()ˇ;",
6028    );
6029
6030    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
6031    assert(
6032        indoc! {"
6033            function test() {
6034                console.log('test')ˇ
6035            }"},
6036        indoc! {"
6037            function test() {
6038                console.logˇ('test')
6039            }"},
6040    );
6041}
6042
6043#[gpui::test(iterations = 10)]
6044async fn test_copilot(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
6045    init_test(cx, |_| {});
6046
6047    let (copilot, copilot_lsp) = Copilot::fake(cx);
6048    cx.update(|cx| cx.set_global(copilot));
6049    let mut cx = EditorLspTestContext::new_rust(
6050        lsp::ServerCapabilities {
6051            completion_provider: Some(lsp::CompletionOptions {
6052                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
6053                ..Default::default()
6054            }),
6055            ..Default::default()
6056        },
6057        cx,
6058    )
6059    .await;
6060
6061    // When inserting, ensure autocompletion is favored over Copilot suggestions.
6062    cx.set_state(indoc! {"
6063        oneˇ
6064        two
6065        three
6066    "});
6067    cx.simulate_keystroke(".");
6068    let _ = handle_completion_request(
6069        &mut cx,
6070        indoc! {"
6071            one.|<>
6072            two
6073            three
6074        "},
6075        vec!["completion_a", "completion_b"],
6076    );
6077    handle_copilot_completion_request(
6078        &copilot_lsp,
6079        vec![copilot::request::Completion {
6080            text: "one.copilot1".into(),
6081            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6082            ..Default::default()
6083        }],
6084        vec![],
6085    );
6086    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6087    cx.update_editor(|editor, cx| {
6088        assert!(editor.context_menu_visible());
6089        assert!(!editor.has_active_copilot_suggestion(cx));
6090
6091        // Confirming a completion inserts it and hides the context menu, without showing
6092        // the copilot suggestion afterwards.
6093        editor
6094            .confirm_completion(&Default::default(), cx)
6095            .unwrap()
6096            .detach();
6097        assert!(!editor.context_menu_visible());
6098        assert!(!editor.has_active_copilot_suggestion(cx));
6099        assert_eq!(editor.text(cx), "one.completion_a\ntwo\nthree\n");
6100        assert_eq!(editor.display_text(cx), "one.completion_a\ntwo\nthree\n");
6101    });
6102
6103    // Ensure Copilot suggestions are shown right away if no autocompletion is available.
6104    cx.set_state(indoc! {"
6105        oneˇ
6106        two
6107        three
6108    "});
6109    cx.simulate_keystroke(".");
6110    let _ = handle_completion_request(
6111        &mut cx,
6112        indoc! {"
6113            one.|<>
6114            two
6115            three
6116        "},
6117        vec![],
6118    );
6119    handle_copilot_completion_request(
6120        &copilot_lsp,
6121        vec![copilot::request::Completion {
6122            text: "one.copilot1".into(),
6123            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6124            ..Default::default()
6125        }],
6126        vec![],
6127    );
6128    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6129    cx.update_editor(|editor, cx| {
6130        assert!(!editor.context_menu_visible());
6131        assert!(editor.has_active_copilot_suggestion(cx));
6132        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
6133        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
6134    });
6135
6136    // Reset editor, and ensure autocompletion is still favored over Copilot suggestions.
6137    cx.set_state(indoc! {"
6138        oneˇ
6139        two
6140        three
6141    "});
6142    cx.simulate_keystroke(".");
6143    let _ = handle_completion_request(
6144        &mut cx,
6145        indoc! {"
6146            one.|<>
6147            two
6148            three
6149        "},
6150        vec!["completion_a", "completion_b"],
6151    );
6152    handle_copilot_completion_request(
6153        &copilot_lsp,
6154        vec![copilot::request::Completion {
6155            text: "one.copilot1".into(),
6156            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6157            ..Default::default()
6158        }],
6159        vec![],
6160    );
6161    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6162    cx.update_editor(|editor, cx| {
6163        assert!(editor.context_menu_visible());
6164        assert!(!editor.has_active_copilot_suggestion(cx));
6165
6166        // When hiding the context menu, the Copilot suggestion becomes visible.
6167        editor.hide_context_menu(cx);
6168        assert!(!editor.context_menu_visible());
6169        assert!(editor.has_active_copilot_suggestion(cx));
6170        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
6171        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
6172    });
6173
6174    // Ensure existing completion is interpolated when inserting again.
6175    cx.simulate_keystroke("c");
6176    deterministic.run_until_parked();
6177    cx.update_editor(|editor, cx| {
6178        assert!(!editor.context_menu_visible());
6179        assert!(editor.has_active_copilot_suggestion(cx));
6180        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
6181        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6182    });
6183
6184    // After debouncing, new Copilot completions should be requested.
6185    handle_copilot_completion_request(
6186        &copilot_lsp,
6187        vec![copilot::request::Completion {
6188            text: "one.copilot2".into(),
6189            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 5)),
6190            ..Default::default()
6191        }],
6192        vec![],
6193    );
6194    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6195    cx.update_editor(|editor, cx| {
6196        assert!(!editor.context_menu_visible());
6197        assert!(editor.has_active_copilot_suggestion(cx));
6198        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6199        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6200
6201        // Canceling should remove the active Copilot suggestion.
6202        editor.cancel(&Default::default(), cx);
6203        assert!(!editor.has_active_copilot_suggestion(cx));
6204        assert_eq!(editor.display_text(cx), "one.c\ntwo\nthree\n");
6205        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6206
6207        // After canceling, tabbing shouldn't insert the previously shown suggestion.
6208        editor.tab(&Default::default(), cx);
6209        assert!(!editor.has_active_copilot_suggestion(cx));
6210        assert_eq!(editor.display_text(cx), "one.c   \ntwo\nthree\n");
6211        assert_eq!(editor.text(cx), "one.c   \ntwo\nthree\n");
6212
6213        // When undoing the previously active suggestion is shown again.
6214        editor.undo(&Default::default(), cx);
6215        assert!(editor.has_active_copilot_suggestion(cx));
6216        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6217        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6218    });
6219
6220    // If an edit occurs outside of this editor, the suggestion is still correctly interpolated.
6221    cx.update_buffer(|buffer, cx| buffer.edit([(5..5, "o")], None, cx));
6222    cx.update_editor(|editor, cx| {
6223        assert!(editor.has_active_copilot_suggestion(cx));
6224        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6225        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6226
6227        // Tabbing when there is an active suggestion inserts it.
6228        editor.tab(&Default::default(), cx);
6229        assert!(!editor.has_active_copilot_suggestion(cx));
6230        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6231        assert_eq!(editor.text(cx), "one.copilot2\ntwo\nthree\n");
6232
6233        // When undoing the previously active suggestion is shown again.
6234        editor.undo(&Default::default(), cx);
6235        assert!(editor.has_active_copilot_suggestion(cx));
6236        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6237        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6238
6239        // Hide suggestion.
6240        editor.cancel(&Default::default(), cx);
6241        assert!(!editor.has_active_copilot_suggestion(cx));
6242        assert_eq!(editor.display_text(cx), "one.co\ntwo\nthree\n");
6243        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6244    });
6245
6246    // If an edit occurs outside of this editor but no suggestion is being shown,
6247    // we won't make it visible.
6248    cx.update_buffer(|buffer, cx| buffer.edit([(6..6, "p")], None, cx));
6249    cx.update_editor(|editor, cx| {
6250        assert!(!editor.has_active_copilot_suggestion(cx));
6251        assert_eq!(editor.display_text(cx), "one.cop\ntwo\nthree\n");
6252        assert_eq!(editor.text(cx), "one.cop\ntwo\nthree\n");
6253    });
6254
6255    // Reset the editor to verify how suggestions behave when tabbing on leading indentation.
6256    cx.update_editor(|editor, cx| {
6257        editor.set_text("fn foo() {\n  \n}", cx);
6258        editor.change_selections(None, cx, |s| {
6259            s.select_ranges([Point::new(1, 2)..Point::new(1, 2)])
6260        });
6261    });
6262    handle_copilot_completion_request(
6263        &copilot_lsp,
6264        vec![copilot::request::Completion {
6265            text: "    let x = 4;".into(),
6266            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
6267            ..Default::default()
6268        }],
6269        vec![],
6270    );
6271
6272    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
6273    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6274    cx.update_editor(|editor, cx| {
6275        assert!(editor.has_active_copilot_suggestion(cx));
6276        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6277        assert_eq!(editor.text(cx), "fn foo() {\n  \n}");
6278
6279        // Tabbing inside of leading whitespace inserts indentation without accepting the suggestion.
6280        editor.tab(&Default::default(), cx);
6281        assert!(editor.has_active_copilot_suggestion(cx));
6282        assert_eq!(editor.text(cx), "fn foo() {\n    \n}");
6283        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6284
6285        // Tabbing again accepts the suggestion.
6286        editor.tab(&Default::default(), cx);
6287        assert!(!editor.has_active_copilot_suggestion(cx));
6288        assert_eq!(editor.text(cx), "fn foo() {\n    let x = 4;\n}");
6289        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6290    });
6291}
6292
6293#[gpui::test]
6294async fn test_copilot_completion_invalidation(
6295    deterministic: Arc<Deterministic>,
6296    cx: &mut gpui::TestAppContext,
6297) {
6298    init_test(cx, |_| {});
6299
6300    let (copilot, copilot_lsp) = Copilot::fake(cx);
6301    cx.update(|cx| cx.set_global(copilot));
6302    let mut cx = EditorLspTestContext::new_rust(
6303        lsp::ServerCapabilities {
6304            completion_provider: Some(lsp::CompletionOptions {
6305                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
6306                ..Default::default()
6307            }),
6308            ..Default::default()
6309        },
6310        cx,
6311    )
6312    .await;
6313
6314    cx.set_state(indoc! {"
6315        one
6316        twˇ
6317        three
6318    "});
6319
6320    handle_copilot_completion_request(
6321        &copilot_lsp,
6322        vec![copilot::request::Completion {
6323            text: "two.foo()".into(),
6324            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
6325            ..Default::default()
6326        }],
6327        vec![],
6328    );
6329    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
6330    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6331    cx.update_editor(|editor, cx| {
6332        assert!(editor.has_active_copilot_suggestion(cx));
6333        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6334        assert_eq!(editor.text(cx), "one\ntw\nthree\n");
6335
6336        editor.backspace(&Default::default(), cx);
6337        assert!(editor.has_active_copilot_suggestion(cx));
6338        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6339        assert_eq!(editor.text(cx), "one\nt\nthree\n");
6340
6341        editor.backspace(&Default::default(), cx);
6342        assert!(editor.has_active_copilot_suggestion(cx));
6343        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6344        assert_eq!(editor.text(cx), "one\n\nthree\n");
6345
6346        // Deleting across the original suggestion range invalidates it.
6347        editor.backspace(&Default::default(), cx);
6348        assert!(!editor.has_active_copilot_suggestion(cx));
6349        assert_eq!(editor.display_text(cx), "one\nthree\n");
6350        assert_eq!(editor.text(cx), "one\nthree\n");
6351
6352        // Undoing the deletion restores the suggestion.
6353        editor.undo(&Default::default(), cx);
6354        assert!(editor.has_active_copilot_suggestion(cx));
6355        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6356        assert_eq!(editor.text(cx), "one\n\nthree\n");
6357    });
6358}
6359
6360#[gpui::test]
6361async fn test_copilot_multibuffer(
6362    deterministic: Arc<Deterministic>,
6363    cx: &mut gpui::TestAppContext,
6364) {
6365    init_test(cx, |_| {});
6366
6367    let (copilot, copilot_lsp) = Copilot::fake(cx);
6368    cx.update(|cx| cx.set_global(copilot));
6369
6370    let buffer_1 = cx.add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx));
6371    let buffer_2 = cx.add_model(|cx| Buffer::new(0, "c = 3\nd = 4\n", cx));
6372    let multibuffer = cx.add_model(|cx| {
6373        let mut multibuffer = MultiBuffer::new(0);
6374        multibuffer.push_excerpts(
6375            buffer_1.clone(),
6376            [ExcerptRange {
6377                context: Point::new(0, 0)..Point::new(2, 0),
6378                primary: None,
6379            }],
6380            cx,
6381        );
6382        multibuffer.push_excerpts(
6383            buffer_2.clone(),
6384            [ExcerptRange {
6385                context: Point::new(0, 0)..Point::new(2, 0),
6386                primary: None,
6387            }],
6388            cx,
6389        );
6390        multibuffer
6391    });
6392    let (_, editor) = cx.add_window(|cx| build_editor(multibuffer, cx));
6393
6394    handle_copilot_completion_request(
6395        &copilot_lsp,
6396        vec![copilot::request::Completion {
6397            text: "b = 2 + a".into(),
6398            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 5)),
6399            ..Default::default()
6400        }],
6401        vec![],
6402    );
6403    editor.update(cx, |editor, cx| {
6404        // Ensure copilot suggestions are shown for the first excerpt.
6405        editor.change_selections(None, cx, |s| {
6406            s.select_ranges([Point::new(1, 5)..Point::new(1, 5)])
6407        });
6408        editor.next_copilot_suggestion(&Default::default(), cx);
6409    });
6410    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6411    editor.update(cx, |editor, cx| {
6412        assert!(editor.has_active_copilot_suggestion(cx));
6413        assert_eq!(
6414            editor.display_text(cx),
6415            "\n\na = 1\nb = 2 + a\n\n\n\nc = 3\nd = 4\n"
6416        );
6417        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
6418    });
6419
6420    handle_copilot_completion_request(
6421        &copilot_lsp,
6422        vec![copilot::request::Completion {
6423            text: "d = 4 + c".into(),
6424            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 6)),
6425            ..Default::default()
6426        }],
6427        vec![],
6428    );
6429    editor.update(cx, |editor, cx| {
6430        // Move to another excerpt, ensuring the suggestion gets cleared.
6431        editor.change_selections(None, cx, |s| {
6432            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
6433        });
6434        assert!(!editor.has_active_copilot_suggestion(cx));
6435        assert_eq!(
6436            editor.display_text(cx),
6437            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4\n"
6438        );
6439        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
6440
6441        // Type a character, ensuring we don't even try to interpolate the previous suggestion.
6442        editor.handle_input(" ", cx);
6443        assert!(!editor.has_active_copilot_suggestion(cx));
6444        assert_eq!(
6445            editor.display_text(cx),
6446            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 \n"
6447        );
6448        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
6449    });
6450
6451    // Ensure the new suggestion is displayed when the debounce timeout expires.
6452    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6453    editor.update(cx, |editor, cx| {
6454        assert!(editor.has_active_copilot_suggestion(cx));
6455        assert_eq!(
6456            editor.display_text(cx),
6457            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 + c\n"
6458        );
6459        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
6460    });
6461}
6462
6463#[gpui::test]
6464async fn test_copilot_disabled_globs(
6465    deterministic: Arc<Deterministic>,
6466    cx: &mut gpui::TestAppContext,
6467) {
6468    init_test(cx, |settings| {
6469        settings
6470            .copilot
6471            .get_or_insert(Default::default())
6472            .disabled_globs = Some(vec![".env*".to_string()]);
6473    });
6474
6475    let (copilot, copilot_lsp) = Copilot::fake(cx);
6476    cx.update(|cx| cx.set_global(copilot));
6477
6478    let fs = FakeFs::new(cx.background());
6479    fs.insert_tree(
6480        "/test",
6481        json!({
6482            ".env": "SECRET=something\n",
6483            "README.md": "hello\n"
6484        }),
6485    )
6486    .await;
6487    let project = Project::test(fs, ["/test".as_ref()], cx).await;
6488
6489    let private_buffer = project
6490        .update(cx, |project, cx| {
6491            project.open_local_buffer("/test/.env", cx)
6492        })
6493        .await
6494        .unwrap();
6495    let public_buffer = project
6496        .update(cx, |project, cx| {
6497            project.open_local_buffer("/test/README.md", cx)
6498        })
6499        .await
6500        .unwrap();
6501
6502    let multibuffer = cx.add_model(|cx| {
6503        let mut multibuffer = MultiBuffer::new(0);
6504        multibuffer.push_excerpts(
6505            private_buffer.clone(),
6506            [ExcerptRange {
6507                context: Point::new(0, 0)..Point::new(1, 0),
6508                primary: None,
6509            }],
6510            cx,
6511        );
6512        multibuffer.push_excerpts(
6513            public_buffer.clone(),
6514            [ExcerptRange {
6515                context: Point::new(0, 0)..Point::new(1, 0),
6516                primary: None,
6517            }],
6518            cx,
6519        );
6520        multibuffer
6521    });
6522    let (_, editor) = cx.add_window(|cx| build_editor(multibuffer, cx));
6523
6524    let mut copilot_requests = copilot_lsp
6525        .handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| async move {
6526            Ok(copilot::request::GetCompletionsResult {
6527                completions: vec![copilot::request::Completion {
6528                    text: "next line".into(),
6529                    range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
6530                    ..Default::default()
6531                }],
6532            })
6533        });
6534
6535    editor.update(cx, |editor, cx| {
6536        editor.change_selections(None, cx, |selections| {
6537            selections.select_ranges([Point::new(0, 0)..Point::new(0, 0)])
6538        });
6539        editor.next_copilot_suggestion(&Default::default(), cx);
6540    });
6541
6542    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6543    assert!(copilot_requests.try_next().is_err());
6544
6545    editor.update(cx, |editor, cx| {
6546        editor.change_selections(None, cx, |s| {
6547            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
6548        });
6549        editor.next_copilot_suggestion(&Default::default(), cx);
6550    });
6551
6552    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6553    assert!(copilot_requests.try_next().is_ok());
6554}
6555
6556fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
6557    let point = DisplayPoint::new(row as u32, column as u32);
6558    point..point
6559}
6560
6561fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
6562    let (text, ranges) = marked_text_ranges(marked_text, true);
6563    assert_eq!(view.text(cx), text);
6564    assert_eq!(
6565        view.selections.ranges(cx),
6566        ranges,
6567        "Assert selections are {}",
6568        marked_text
6569    );
6570}
6571
6572/// Handle completion request passing a marked string specifying where the completion
6573/// should be triggered from using '|' character, what range should be replaced, and what completions
6574/// should be returned using '<' and '>' to delimit the range
6575fn handle_completion_request<'a>(
6576    cx: &mut EditorLspTestContext<'a>,
6577    marked_string: &str,
6578    completions: Vec<&'static str>,
6579) -> impl Future<Output = ()> {
6580    let complete_from_marker: TextRangeMarker = '|'.into();
6581    let replace_range_marker: TextRangeMarker = ('<', '>').into();
6582    let (_, mut marked_ranges) = marked_text_ranges_by(
6583        marked_string,
6584        vec![complete_from_marker.clone(), replace_range_marker.clone()],
6585    );
6586
6587    let complete_from_position =
6588        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
6589    let replace_range =
6590        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
6591
6592    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
6593        let completions = completions.clone();
6594        async move {
6595            assert_eq!(params.text_document_position.text_document.uri, url.clone());
6596            assert_eq!(
6597                params.text_document_position.position,
6598                complete_from_position
6599            );
6600            Ok(Some(lsp::CompletionResponse::Array(
6601                completions
6602                    .iter()
6603                    .map(|completion_text| lsp::CompletionItem {
6604                        label: completion_text.to_string(),
6605                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
6606                            range: replace_range,
6607                            new_text: completion_text.to_string(),
6608                        })),
6609                        ..Default::default()
6610                    })
6611                    .collect(),
6612            )))
6613        }
6614    });
6615
6616    async move {
6617        request.next().await;
6618    }
6619}
6620
6621fn handle_resolve_completion_request<'a>(
6622    cx: &mut EditorLspTestContext<'a>,
6623    edits: Option<Vec<(&'static str, &'static str)>>,
6624) -> impl Future<Output = ()> {
6625    let edits = edits.map(|edits| {
6626        edits
6627            .iter()
6628            .map(|(marked_string, new_text)| {
6629                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
6630                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
6631                lsp::TextEdit::new(replace_range, new_text.to_string())
6632            })
6633            .collect::<Vec<_>>()
6634    });
6635
6636    let mut request =
6637        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
6638            let edits = edits.clone();
6639            async move {
6640                Ok(lsp::CompletionItem {
6641                    additional_text_edits: edits,
6642                    ..Default::default()
6643                })
6644            }
6645        });
6646
6647    async move {
6648        request.next().await;
6649    }
6650}
6651
6652fn handle_copilot_completion_request(
6653    lsp: &lsp::FakeLanguageServer,
6654    completions: Vec<copilot::request::Completion>,
6655    completions_cycling: Vec<copilot::request::Completion>,
6656) {
6657    lsp.handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| {
6658        let completions = completions.clone();
6659        async move {
6660            Ok(copilot::request::GetCompletionsResult {
6661                completions: completions.clone(),
6662            })
6663        }
6664    });
6665    lsp.handle_request::<copilot::request::GetCompletionsCycling, _, _>(move |_params, _cx| {
6666        let completions_cycling = completions_cycling.clone();
6667        async move {
6668            Ok(copilot::request::GetCompletionsResult {
6669                completions: completions_cycling.clone(),
6670            })
6671        }
6672    });
6673}
6674
6675pub(crate) fn update_test_settings(
6676    cx: &mut TestAppContext,
6677    f: impl Fn(&mut AllLanguageSettingsContent),
6678) {
6679    cx.update(|cx| {
6680        cx.update_global::<SettingsStore, _, _>(|store, cx| {
6681            store.update_user_settings::<AllLanguageSettings>(cx, f);
6682        });
6683    });
6684}
6685
6686pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
6687    cx.foreground().forbid_parking();
6688
6689    cx.update(|cx| {
6690        cx.set_global(SettingsStore::test(cx));
6691        theme::init((), cx);
6692        client::init_settings(cx);
6693        language::init(cx);
6694        Project::init_settings(cx);
6695        workspace::init_settings(cx);
6696        crate::init(cx);
6697    });
6698
6699    update_test_settings(cx, f);
6700}