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_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
1248    init_test(cx, |_| {});
1249    let mut cx = EditorTestContext::new(cx).await;
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#"ˇone
1256        two
1257
1258        three
1259        fourˇ
1260        five
1261
1262        six"#
1263            .unindent(),
1264    );
1265
1266    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
1267    cx.assert_editor_state(
1268        &r#"one
1269        two
1270        ˇ
1271        three
1272        four
1273        five
1274        ˇ
1275        six"#
1276            .unindent(),
1277    );
1278
1279    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
1280    cx.assert_editor_state(
1281        &r#"one
1282        two
1283
1284        three
1285        four
1286        five
1287        ˇ
1288        sixˇ"#
1289            .unindent(),
1290    );
1291
1292    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
1293    cx.assert_editor_state(
1294        &r#"ˇone
1295        two
1296
1297        three
1298        four
1299        five
1300
1301        sixˇ"#
1302            .unindent(),
1303    );
1304
1305    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
1306    cx.assert_editor_state(
1307        &r#"ˇone
1308        two
1309        ˇ
1310        three
1311        four
1312        five
1313
1314        six"#
1315            .unindent(),
1316    );
1317
1318    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
1319    cx.assert_editor_state(
1320        &r#"ˇone
1321        two
1322
1323        three
1324        four
1325        five
1326
1327        sixˇ"#
1328            .unindent(),
1329    );
1330
1331    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
1332    cx.assert_editor_state(
1333        &r#"one
1334        two
1335
1336        three
1337        four
1338        five
1339        ˇ
1340        sixˇ"#
1341            .unindent(),
1342    );
1343
1344    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
1345    cx.assert_editor_state(
1346        &r#"one
1347        two
1348        ˇ
1349        three
1350        four
1351        five
1352        ˇ
1353        six"#
1354            .unindent(),
1355    );
1356}
1357
1358#[gpui::test]
1359async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
1360    init_test(cx, |_| {});
1361    let mut cx = EditorTestContext::new(cx).await;
1362
1363    let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
1364    cx.simulate_window_resize(cx.window_id, vec2f(100., 4. * line_height));
1365
1366    cx.set_state(
1367        &r#"
1368        ˇone
1369        two
1370        threeˇ
1371        four
1372        five
1373        six
1374        seven
1375        eight
1376        nine
1377        ten
1378        "#
1379        .unindent(),
1380    );
1381
1382    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
1383    cx.assert_editor_state(
1384        &r#"
1385        one
1386        two
1387        three
1388        ˇfour
1389        five
1390        sixˇ
1391        seven
1392        eight
1393        nine
1394        ten
1395        "#
1396        .unindent(),
1397    );
1398
1399    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
1400    cx.assert_editor_state(
1401        &r#"
1402        one
1403        two
1404        three
1405        four
1406        five
1407        six
1408        ˇseven
1409        eight
1410        nineˇ
1411        ten
1412        "#
1413        .unindent(),
1414    );
1415
1416    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
1417    cx.assert_editor_state(
1418        &r#"
1419        one
1420        two
1421        three
1422        ˇfour
1423        five
1424        sixˇ
1425        seven
1426        eight
1427        nine
1428        ten
1429        "#
1430        .unindent(),
1431    );
1432
1433    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
1434    cx.assert_editor_state(
1435        &r#"
1436        ˇone
1437        two
1438        threeˇ
1439        four
1440        five
1441        six
1442        seven
1443        eight
1444        nine
1445        ten
1446        "#
1447        .unindent(),
1448    );
1449
1450    // Test select collapsing
1451    cx.update_editor(|editor, cx| {
1452        editor.move_page_down(&MovePageDown::default(), cx);
1453        editor.move_page_down(&MovePageDown::default(), cx);
1454        editor.move_page_down(&MovePageDown::default(), cx);
1455    });
1456    cx.assert_editor_state(
1457        &r#"
1458        one
1459        two
1460        three
1461        four
1462        five
1463        six
1464        seven
1465        eight
1466        nine
1467        ˇten
1468        ˇ"#
1469        .unindent(),
1470    );
1471}
1472
1473#[gpui::test]
1474async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
1475    init_test(cx, |_| {});
1476    let mut cx = EditorTestContext::new(cx).await;
1477    cx.set_state("one «two threeˇ» four");
1478    cx.update_editor(|editor, cx| {
1479        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
1480        assert_eq!(editor.text(cx), " four");
1481    });
1482}
1483
1484#[gpui::test]
1485fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
1486    init_test(cx, |_| {});
1487
1488    let (_, view) = cx.add_window(|cx| {
1489        let buffer = MultiBuffer::build_simple("one two three four", cx);
1490        build_editor(buffer.clone(), cx)
1491    });
1492
1493    view.update(cx, |view, cx| {
1494        view.change_selections(None, cx, |s| {
1495            s.select_display_ranges([
1496                // an empty selection - the preceding word fragment is deleted
1497                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
1498                // characters selected - they are deleted
1499                DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
1500            ])
1501        });
1502        view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
1503        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
1504    });
1505
1506    view.update(cx, |view, cx| {
1507        view.change_selections(None, cx, |s| {
1508            s.select_display_ranges([
1509                // an empty selection - the following word fragment is deleted
1510                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
1511                // characters selected - they are deleted
1512                DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
1513            ])
1514        });
1515        view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
1516        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
1517    });
1518}
1519
1520#[gpui::test]
1521fn test_newline(cx: &mut TestAppContext) {
1522    init_test(cx, |_| {});
1523
1524    let (_, view) = cx.add_window(|cx| {
1525        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
1526        build_editor(buffer.clone(), cx)
1527    });
1528
1529    view.update(cx, |view, cx| {
1530        view.change_selections(None, cx, |s| {
1531            s.select_display_ranges([
1532                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
1533                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
1534                DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
1535            ])
1536        });
1537
1538        view.newline(&Newline, cx);
1539        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
1540    });
1541}
1542
1543#[gpui::test]
1544fn test_newline_with_old_selections(cx: &mut TestAppContext) {
1545    init_test(cx, |_| {});
1546
1547    let (_, editor) = cx.add_window(|cx| {
1548        let buffer = MultiBuffer::build_simple(
1549            "
1550                a
1551                b(
1552                    X
1553                )
1554                c(
1555                    X
1556                )
1557            "
1558            .unindent()
1559            .as_str(),
1560            cx,
1561        );
1562        let mut editor = build_editor(buffer.clone(), cx);
1563        editor.change_selections(None, cx, |s| {
1564            s.select_ranges([
1565                Point::new(2, 4)..Point::new(2, 5),
1566                Point::new(5, 4)..Point::new(5, 5),
1567            ])
1568        });
1569        editor
1570    });
1571
1572    editor.update(cx, |editor, cx| {
1573        // Edit the buffer directly, deleting ranges surrounding the editor's selections
1574        editor.buffer.update(cx, |buffer, cx| {
1575            buffer.edit(
1576                [
1577                    (Point::new(1, 2)..Point::new(3, 0), ""),
1578                    (Point::new(4, 2)..Point::new(6, 0), ""),
1579                ],
1580                None,
1581                cx,
1582            );
1583            assert_eq!(
1584                buffer.read(cx).text(),
1585                "
1586                    a
1587                    b()
1588                    c()
1589                "
1590                .unindent()
1591            );
1592        });
1593        assert_eq!(
1594            editor.selections.ranges(cx),
1595            &[
1596                Point::new(1, 2)..Point::new(1, 2),
1597                Point::new(2, 2)..Point::new(2, 2),
1598            ],
1599        );
1600
1601        editor.newline(&Newline, cx);
1602        assert_eq!(
1603            editor.text(cx),
1604            "
1605                a
1606                b(
1607                )
1608                c(
1609                )
1610            "
1611            .unindent()
1612        );
1613
1614        // The selections are moved after the inserted newlines
1615        assert_eq!(
1616            editor.selections.ranges(cx),
1617            &[
1618                Point::new(2, 0)..Point::new(2, 0),
1619                Point::new(4, 0)..Point::new(4, 0),
1620            ],
1621        );
1622    });
1623}
1624
1625#[gpui::test]
1626async fn test_newline_above(cx: &mut gpui::TestAppContext) {
1627    init_test(cx, |settings| {
1628        settings.defaults.tab_size = NonZeroU32::new(4)
1629    });
1630
1631    let language = Arc::new(
1632        Language::new(
1633            LanguageConfig::default(),
1634            Some(tree_sitter_rust::language()),
1635        )
1636        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
1637        .unwrap(),
1638    );
1639
1640    let mut cx = EditorTestContext::new(cx).await;
1641    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1642    cx.set_state(indoc! {"
1643        const a: ˇA = (
16441645                «const_functionˇ»(ˇ),
1646                so«mˇ»et«hˇ»ing_ˇelse,ˇ
16471648        ˇ);ˇ
1649    "});
1650
1651    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
1652    cx.assert_editor_state(indoc! {"
1653        ˇ
1654        const a: A = (
1655            ˇ
1656            (
1657                ˇ
1658                ˇ
1659                const_function(),
1660                ˇ
1661                ˇ
1662                ˇ
1663                ˇ
1664                something_else,
1665                ˇ
1666            )
1667            ˇ
1668            ˇ
1669        );
1670    "});
1671}
1672
1673#[gpui::test]
1674async fn test_newline_below(cx: &mut gpui::TestAppContext) {
1675    init_test(cx, |settings| {
1676        settings.defaults.tab_size = NonZeroU32::new(4)
1677    });
1678
1679    let language = Arc::new(
1680        Language::new(
1681            LanguageConfig::default(),
1682            Some(tree_sitter_rust::language()),
1683        )
1684        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
1685        .unwrap(),
1686    );
1687
1688    let mut cx = EditorTestContext::new(cx).await;
1689    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1690    cx.set_state(indoc! {"
1691        const a: ˇA = (
16921693                «const_functionˇ»(ˇ),
1694                so«mˇ»et«hˇ»ing_ˇelse,ˇ
16951696        ˇ);ˇ
1697    "});
1698
1699    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
1700    cx.assert_editor_state(indoc! {"
1701        const a: A = (
1702            ˇ
1703            (
1704                ˇ
1705                const_function(),
1706                ˇ
1707                ˇ
1708                something_else,
1709                ˇ
1710                ˇ
1711                ˇ
1712                ˇ
1713            )
1714            ˇ
1715        );
1716        ˇ
1717        ˇ
1718    "});
1719}
1720
1721#[gpui::test]
1722fn test_insert_with_old_selections(cx: &mut TestAppContext) {
1723    init_test(cx, |_| {});
1724
1725    let (_, editor) = cx.add_window(|cx| {
1726        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
1727        let mut editor = build_editor(buffer.clone(), cx);
1728        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
1729        editor
1730    });
1731
1732    editor.update(cx, |editor, cx| {
1733        // Edit the buffer directly, deleting ranges surrounding the editor's selections
1734        editor.buffer.update(cx, |buffer, cx| {
1735            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
1736            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
1737        });
1738        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
1739
1740        editor.insert("Z", cx);
1741        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
1742
1743        // The selections are moved after the inserted characters
1744        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
1745    });
1746}
1747
1748#[gpui::test]
1749async fn test_tab(cx: &mut gpui::TestAppContext) {
1750    init_test(cx, |settings| {
1751        settings.defaults.tab_size = NonZeroU32::new(3)
1752    });
1753
1754    let mut cx = EditorTestContext::new(cx).await;
1755    cx.set_state(indoc! {"
1756        ˇabˇc
1757        ˇ🏀ˇ🏀ˇefg
17581759    "});
1760    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1761    cx.assert_editor_state(indoc! {"
1762           ˇab ˇc
1763           ˇ🏀  ˇ🏀  ˇefg
1764        d  ˇ
1765    "});
1766
1767    cx.set_state(indoc! {"
1768        a
1769        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
1770    "});
1771    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1772    cx.assert_editor_state(indoc! {"
1773        a
1774           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
1775    "});
1776}
1777
1778#[gpui::test]
1779async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
1780    init_test(cx, |_| {});
1781
1782    let mut cx = EditorTestContext::new(cx).await;
1783    let language = Arc::new(
1784        Language::new(
1785            LanguageConfig::default(),
1786            Some(tree_sitter_rust::language()),
1787        )
1788        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
1789        .unwrap(),
1790    );
1791    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1792
1793    // cursors that are already at the suggested indent level insert
1794    // a soft tab. cursors that are to the left of the suggested indent
1795    // auto-indent their line.
1796    cx.set_state(indoc! {"
1797        ˇ
1798        const a: B = (
1799            c(
1800                d(
1801        ˇ
1802                )
1803        ˇ
1804        ˇ    )
1805        );
1806    "});
1807    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1808    cx.assert_editor_state(indoc! {"
1809            ˇ
1810        const a: B = (
1811            c(
1812                d(
1813                    ˇ
1814                )
1815                ˇ
1816            ˇ)
1817        );
1818    "});
1819
1820    // handle auto-indent when there are multiple cursors on the same line
1821    cx.set_state(indoc! {"
1822        const a: B = (
1823            c(
1824        ˇ    ˇ
1825        ˇ    )
1826        );
1827    "});
1828    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1829    cx.assert_editor_state(indoc! {"
1830        const a: B = (
1831            c(
1832                ˇ
1833            ˇ)
1834        );
1835    "});
1836}
1837
1838#[gpui::test]
1839async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
1840    init_test(cx, |settings| {
1841        settings.defaults.tab_size = NonZeroU32::new(4)
1842    });
1843
1844    let language = Arc::new(
1845        Language::new(
1846            LanguageConfig::default(),
1847            Some(tree_sitter_rust::language()),
1848        )
1849        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
1850        .unwrap(),
1851    );
1852
1853    let mut cx = EditorTestContext::new(cx).await;
1854    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1855    cx.set_state(indoc! {"
1856        fn a() {
1857            if b {
1858        \t ˇc
1859            }
1860        }
1861    "});
1862
1863    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1864    cx.assert_editor_state(indoc! {"
1865        fn a() {
1866            if b {
1867                ˇc
1868            }
1869        }
1870    "});
1871}
1872
1873#[gpui::test]
1874async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
1875    init_test(cx, |settings| {
1876        settings.defaults.tab_size = NonZeroU32::new(4);
1877    });
1878
1879    let mut cx = EditorTestContext::new(cx).await;
1880
1881    cx.set_state(indoc! {"
1882          «oneˇ» «twoˇ»
1883        three
1884         four
1885    "});
1886    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1887    cx.assert_editor_state(indoc! {"
1888            «oneˇ» «twoˇ»
1889        three
1890         four
1891    "});
1892
1893    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1894    cx.assert_editor_state(indoc! {"
1895        «oneˇ» «twoˇ»
1896        three
1897         four
1898    "});
1899
1900    // select across line ending
1901    cx.set_state(indoc! {"
1902        one two
1903        t«hree
1904        ˇ» four
1905    "});
1906    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1907    cx.assert_editor_state(indoc! {"
1908        one two
1909            t«hree
1910        ˇ» four
1911    "});
1912
1913    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1914    cx.assert_editor_state(indoc! {"
1915        one two
1916        t«hree
1917        ˇ» four
1918    "});
1919
1920    // Ensure that indenting/outdenting works when the cursor is at column 0.
1921    cx.set_state(indoc! {"
1922        one two
1923        ˇthree
1924            four
1925    "});
1926    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1927    cx.assert_editor_state(indoc! {"
1928        one two
1929            ˇthree
1930            four
1931    "});
1932
1933    cx.set_state(indoc! {"
1934        one two
1935        ˇ    three
1936            four
1937    "});
1938    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1939    cx.assert_editor_state(indoc! {"
1940        one two
1941        ˇthree
1942            four
1943    "});
1944}
1945
1946#[gpui::test]
1947async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
1948    init_test(cx, |settings| {
1949        settings.defaults.hard_tabs = Some(true);
1950    });
1951
1952    let mut cx = EditorTestContext::new(cx).await;
1953
1954    // select two ranges on one line
1955    cx.set_state(indoc! {"
1956        «oneˇ» «twoˇ»
1957        three
1958        four
1959    "});
1960    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1961    cx.assert_editor_state(indoc! {"
1962        \t«oneˇ» «twoˇ»
1963        three
1964        four
1965    "});
1966    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1967    cx.assert_editor_state(indoc! {"
1968        \t\t«oneˇ» «twoˇ»
1969        three
1970        four
1971    "});
1972    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1973    cx.assert_editor_state(indoc! {"
1974        \t«oneˇ» «twoˇ»
1975        three
1976        four
1977    "});
1978    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1979    cx.assert_editor_state(indoc! {"
1980        «oneˇ» «twoˇ»
1981        three
1982        four
1983    "});
1984
1985    // select across a line ending
1986    cx.set_state(indoc! {"
1987        one two
1988        t«hree
1989        ˇ»four
1990    "});
1991    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1992    cx.assert_editor_state(indoc! {"
1993        one two
1994        \tt«hree
1995        ˇ»four
1996    "});
1997    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1998    cx.assert_editor_state(indoc! {"
1999        one two
2000        \t\tt«hree
2001        ˇ»four
2002    "});
2003    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2004    cx.assert_editor_state(indoc! {"
2005        one two
2006        \tt«hree
2007        ˇ»four
2008    "});
2009    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2010    cx.assert_editor_state(indoc! {"
2011        one two
2012        t«hree
2013        ˇ»four
2014    "});
2015
2016    // Ensure that indenting/outdenting works when the cursor is at column 0.
2017    cx.set_state(indoc! {"
2018        one two
2019        ˇthree
2020        four
2021    "});
2022    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2023    cx.assert_editor_state(indoc! {"
2024        one two
2025        ˇthree
2026        four
2027    "});
2028    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2029    cx.assert_editor_state(indoc! {"
2030        one two
2031        \tˇthree
2032        four
2033    "});
2034    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2035    cx.assert_editor_state(indoc! {"
2036        one two
2037        ˇthree
2038        four
2039    "});
2040}
2041
2042#[gpui::test]
2043fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
2044    init_test(cx, |settings| {
2045        settings.languages.extend([
2046            (
2047                "TOML".into(),
2048                LanguageSettingsContent {
2049                    tab_size: NonZeroU32::new(2),
2050                    ..Default::default()
2051                },
2052            ),
2053            (
2054                "Rust".into(),
2055                LanguageSettingsContent {
2056                    tab_size: NonZeroU32::new(4),
2057                    ..Default::default()
2058                },
2059            ),
2060        ]);
2061    });
2062
2063    let toml_language = Arc::new(Language::new(
2064        LanguageConfig {
2065            name: "TOML".into(),
2066            ..Default::default()
2067        },
2068        None,
2069    ));
2070    let rust_language = Arc::new(Language::new(
2071        LanguageConfig {
2072            name: "Rust".into(),
2073            ..Default::default()
2074        },
2075        None,
2076    ));
2077
2078    let toml_buffer =
2079        cx.add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx));
2080    let rust_buffer = cx.add_model(|cx| {
2081        Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx)
2082    });
2083    let multibuffer = cx.add_model(|cx| {
2084        let mut multibuffer = MultiBuffer::new(0);
2085        multibuffer.push_excerpts(
2086            toml_buffer.clone(),
2087            [ExcerptRange {
2088                context: Point::new(0, 0)..Point::new(2, 0),
2089                primary: None,
2090            }],
2091            cx,
2092        );
2093        multibuffer.push_excerpts(
2094            rust_buffer.clone(),
2095            [ExcerptRange {
2096                context: Point::new(0, 0)..Point::new(1, 0),
2097                primary: None,
2098            }],
2099            cx,
2100        );
2101        multibuffer
2102    });
2103
2104    cx.add_window(|cx| {
2105        let mut editor = build_editor(multibuffer, cx);
2106
2107        assert_eq!(
2108            editor.text(cx),
2109            indoc! {"
2110                a = 1
2111                b = 2
2112
2113                const c: usize = 3;
2114            "}
2115        );
2116
2117        select_ranges(
2118            &mut editor,
2119            indoc! {"
2120                «aˇ» = 1
2121                b = 2
2122
2123                «const c:ˇ» usize = 3;
2124            "},
2125            cx,
2126        );
2127
2128        editor.tab(&Tab, cx);
2129        assert_text_with_selections(
2130            &mut editor,
2131            indoc! {"
2132                  «aˇ» = 1
2133                b = 2
2134
2135                    «const c:ˇ» usize = 3;
2136            "},
2137            cx,
2138        );
2139        editor.tab_prev(&TabPrev, cx);
2140        assert_text_with_selections(
2141            &mut editor,
2142            indoc! {"
2143                «aˇ» = 1
2144                b = 2
2145
2146                «const c:ˇ» usize = 3;
2147            "},
2148            cx,
2149        );
2150
2151        editor
2152    });
2153}
2154
2155#[gpui::test]
2156async fn test_backspace(cx: &mut gpui::TestAppContext) {
2157    init_test(cx, |_| {});
2158
2159    let mut cx = EditorTestContext::new(cx).await;
2160
2161    // Basic backspace
2162    cx.set_state(indoc! {"
2163        onˇe two three
2164        fou«rˇ» five six
2165        seven «ˇeight nine
2166        »ten
2167    "});
2168    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2169    cx.assert_editor_state(indoc! {"
2170        oˇe two three
2171        fouˇ five six
2172        seven ˇten
2173    "});
2174
2175    // Test backspace inside and around indents
2176    cx.set_state(indoc! {"
2177        zero
2178            ˇone
2179                ˇtwo
2180            ˇ ˇ ˇ  three
2181        ˇ  ˇ  four
2182    "});
2183    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2184    cx.assert_editor_state(indoc! {"
2185        zero
2186        ˇone
2187            ˇtwo
2188        ˇ  threeˇ  four
2189    "});
2190
2191    // Test backspace with line_mode set to true
2192    cx.update_editor(|e, _| e.selections.line_mode = true);
2193    cx.set_state(indoc! {"
2194        The ˇquick ˇbrown
2195        fox jumps over
2196        the lazy dog
2197        ˇThe qu«ick bˇ»rown"});
2198    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2199    cx.assert_editor_state(indoc! {"
2200        ˇfox jumps over
2201        the lazy dogˇ"});
2202}
2203
2204#[gpui::test]
2205async fn test_delete(cx: &mut gpui::TestAppContext) {
2206    init_test(cx, |_| {});
2207
2208    let mut cx = EditorTestContext::new(cx).await;
2209    cx.set_state(indoc! {"
2210        onˇe two three
2211        fou«rˇ» five six
2212        seven «ˇeight nine
2213        »ten
2214    "});
2215    cx.update_editor(|e, cx| e.delete(&Delete, cx));
2216    cx.assert_editor_state(indoc! {"
2217        onˇ two three
2218        fouˇ five six
2219        seven ˇten
2220    "});
2221
2222    // Test backspace with line_mode set to true
2223    cx.update_editor(|e, _| e.selections.line_mode = true);
2224    cx.set_state(indoc! {"
2225        The ˇquick ˇbrown
2226        fox «ˇjum»ps over
2227        the lazy dog
2228        ˇThe qu«ick bˇ»rown"});
2229    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2230    cx.assert_editor_state("ˇthe lazy dogˇ");
2231}
2232
2233#[gpui::test]
2234fn test_delete_line(cx: &mut TestAppContext) {
2235    init_test(cx, |_| {});
2236
2237    let (_, view) = cx.add_window(|cx| {
2238        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2239        build_editor(buffer, cx)
2240    });
2241    view.update(cx, |view, cx| {
2242        view.change_selections(None, cx, |s| {
2243            s.select_display_ranges([
2244                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2245                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
2246                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2247            ])
2248        });
2249        view.delete_line(&DeleteLine, cx);
2250        assert_eq!(view.display_text(cx), "ghi");
2251        assert_eq!(
2252            view.selections.display_ranges(cx),
2253            vec![
2254                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
2255                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
2256            ]
2257        );
2258    });
2259
2260    let (_, view) = cx.add_window(|cx| {
2261        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2262        build_editor(buffer, cx)
2263    });
2264    view.update(cx, |view, cx| {
2265        view.change_selections(None, cx, |s| {
2266            s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
2267        });
2268        view.delete_line(&DeleteLine, cx);
2269        assert_eq!(view.display_text(cx), "ghi\n");
2270        assert_eq!(
2271            view.selections.display_ranges(cx),
2272            vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
2273        );
2274    });
2275}
2276
2277#[gpui::test]
2278fn test_duplicate_line(cx: &mut TestAppContext) {
2279    init_test(cx, |_| {});
2280
2281    let (_, view) = cx.add_window(|cx| {
2282        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2283        build_editor(buffer, cx)
2284    });
2285    view.update(cx, |view, cx| {
2286        view.change_selections(None, cx, |s| {
2287            s.select_display_ranges([
2288                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2289                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2290                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2291                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2292            ])
2293        });
2294        view.duplicate_line(&DuplicateLine, cx);
2295        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
2296        assert_eq!(
2297            view.selections.display_ranges(cx),
2298            vec![
2299                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
2300                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
2301                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2302                DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
2303            ]
2304        );
2305    });
2306
2307    let (_, view) = cx.add_window(|cx| {
2308        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2309        build_editor(buffer, cx)
2310    });
2311    view.update(cx, |view, cx| {
2312        view.change_selections(None, cx, |s| {
2313            s.select_display_ranges([
2314                DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
2315                DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
2316            ])
2317        });
2318        view.duplicate_line(&DuplicateLine, cx);
2319        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
2320        assert_eq!(
2321            view.selections.display_ranges(cx),
2322            vec![
2323                DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
2324                DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
2325            ]
2326        );
2327    });
2328}
2329
2330#[gpui::test]
2331fn test_move_line_up_down(cx: &mut TestAppContext) {
2332    init_test(cx, |_| {});
2333
2334    let (_, view) = cx.add_window(|cx| {
2335        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
2336        build_editor(buffer, cx)
2337    });
2338    view.update(cx, |view, cx| {
2339        view.fold_ranges(
2340            vec![
2341                Point::new(0, 2)..Point::new(1, 2),
2342                Point::new(2, 3)..Point::new(4, 1),
2343                Point::new(7, 0)..Point::new(8, 4),
2344            ],
2345            true,
2346            cx,
2347        );
2348        view.change_selections(None, cx, |s| {
2349            s.select_display_ranges([
2350                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2351                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2352                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2353                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
2354            ])
2355        });
2356        assert_eq!(
2357            view.display_text(cx),
2358            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
2359        );
2360
2361        view.move_line_up(&MoveLineUp, cx);
2362        assert_eq!(
2363            view.display_text(cx),
2364            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
2365        );
2366        assert_eq!(
2367            view.selections.display_ranges(cx),
2368            vec![
2369                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2370                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2371                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
2372                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
2373            ]
2374        );
2375    });
2376
2377    view.update(cx, |view, cx| {
2378        view.move_line_down(&MoveLineDown, cx);
2379        assert_eq!(
2380            view.display_text(cx),
2381            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
2382        );
2383        assert_eq!(
2384            view.selections.display_ranges(cx),
2385            vec![
2386                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
2387                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2388                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2389                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
2390            ]
2391        );
2392    });
2393
2394    view.update(cx, |view, cx| {
2395        view.move_line_down(&MoveLineDown, cx);
2396        assert_eq!(
2397            view.display_text(cx),
2398            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
2399        );
2400        assert_eq!(
2401            view.selections.display_ranges(cx),
2402            vec![
2403                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2404                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2405                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2406                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
2407            ]
2408        );
2409    });
2410
2411    view.update(cx, |view, cx| {
2412        view.move_line_up(&MoveLineUp, cx);
2413        assert_eq!(
2414            view.display_text(cx),
2415            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
2416        );
2417        assert_eq!(
2418            view.selections.display_ranges(cx),
2419            vec![
2420                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
2421                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2422                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
2423                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
2424            ]
2425        );
2426    });
2427}
2428
2429#[gpui::test]
2430fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
2431    init_test(cx, |_| {});
2432
2433    let (_, editor) = cx.add_window(|cx| {
2434        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
2435        build_editor(buffer, cx)
2436    });
2437    editor.update(cx, |editor, cx| {
2438        let snapshot = editor.buffer.read(cx).snapshot(cx);
2439        editor.insert_blocks(
2440            [BlockProperties {
2441                style: BlockStyle::Fixed,
2442                position: snapshot.anchor_after(Point::new(2, 0)),
2443                disposition: BlockDisposition::Below,
2444                height: 1,
2445                render: Arc::new(|_| Empty::new().into_any()),
2446            }],
2447            cx,
2448        );
2449        editor.change_selections(None, cx, |s| {
2450            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
2451        });
2452        editor.move_line_down(&MoveLineDown, cx);
2453    });
2454}
2455
2456#[gpui::test]
2457fn test_transpose(cx: &mut TestAppContext) {
2458    init_test(cx, |_| {});
2459
2460    _ = cx
2461        .add_window(|cx| {
2462            let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
2463
2464            editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
2465            editor.transpose(&Default::default(), cx);
2466            assert_eq!(editor.text(cx), "bac");
2467            assert_eq!(editor.selections.ranges(cx), [2..2]);
2468
2469            editor.transpose(&Default::default(), cx);
2470            assert_eq!(editor.text(cx), "bca");
2471            assert_eq!(editor.selections.ranges(cx), [3..3]);
2472
2473            editor.transpose(&Default::default(), cx);
2474            assert_eq!(editor.text(cx), "bac");
2475            assert_eq!(editor.selections.ranges(cx), [3..3]);
2476
2477            editor
2478        })
2479        .1;
2480
2481    _ = cx
2482        .add_window(|cx| {
2483            let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
2484
2485            editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
2486            editor.transpose(&Default::default(), cx);
2487            assert_eq!(editor.text(cx), "acb\nde");
2488            assert_eq!(editor.selections.ranges(cx), [3..3]);
2489
2490            editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
2491            editor.transpose(&Default::default(), cx);
2492            assert_eq!(editor.text(cx), "acbd\ne");
2493            assert_eq!(editor.selections.ranges(cx), [5..5]);
2494
2495            editor.transpose(&Default::default(), cx);
2496            assert_eq!(editor.text(cx), "acbde\n");
2497            assert_eq!(editor.selections.ranges(cx), [6..6]);
2498
2499            editor.transpose(&Default::default(), cx);
2500            assert_eq!(editor.text(cx), "acbd\ne");
2501            assert_eq!(editor.selections.ranges(cx), [6..6]);
2502
2503            editor
2504        })
2505        .1;
2506
2507    _ = cx
2508        .add_window(|cx| {
2509            let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
2510
2511            editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
2512            editor.transpose(&Default::default(), cx);
2513            assert_eq!(editor.text(cx), "bacd\ne");
2514            assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
2515
2516            editor.transpose(&Default::default(), cx);
2517            assert_eq!(editor.text(cx), "bcade\n");
2518            assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
2519
2520            editor.transpose(&Default::default(), cx);
2521            assert_eq!(editor.text(cx), "bcda\ne");
2522            assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
2523
2524            editor.transpose(&Default::default(), cx);
2525            assert_eq!(editor.text(cx), "bcade\n");
2526            assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
2527
2528            editor.transpose(&Default::default(), cx);
2529            assert_eq!(editor.text(cx), "bcaed\n");
2530            assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
2531
2532            editor
2533        })
2534        .1;
2535
2536    _ = cx
2537        .add_window(|cx| {
2538            let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
2539
2540            editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
2541            editor.transpose(&Default::default(), cx);
2542            assert_eq!(editor.text(cx), "🏀🍐✋");
2543            assert_eq!(editor.selections.ranges(cx), [8..8]);
2544
2545            editor.transpose(&Default::default(), cx);
2546            assert_eq!(editor.text(cx), "🏀✋🍐");
2547            assert_eq!(editor.selections.ranges(cx), [11..11]);
2548
2549            editor.transpose(&Default::default(), cx);
2550            assert_eq!(editor.text(cx), "🏀🍐✋");
2551            assert_eq!(editor.selections.ranges(cx), [11..11]);
2552
2553            editor
2554        })
2555        .1;
2556}
2557
2558#[gpui::test]
2559async fn test_clipboard(cx: &mut gpui::TestAppContext) {
2560    init_test(cx, |_| {});
2561
2562    let mut cx = EditorTestContext::new(cx).await;
2563
2564    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
2565    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2566    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
2567
2568    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
2569    cx.set_state("two ˇfour ˇsix ˇ");
2570    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2571    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
2572
2573    // Paste again but with only two cursors. Since the number of cursors doesn't
2574    // match the number of slices in the clipboard, the entire clipboard text
2575    // is pasted at each cursor.
2576    cx.set_state("ˇtwo one✅ four three six five ˇ");
2577    cx.update_editor(|e, cx| {
2578        e.handle_input("( ", cx);
2579        e.paste(&Paste, cx);
2580        e.handle_input(") ", cx);
2581    });
2582    cx.assert_editor_state(
2583        &([
2584            "( one✅ ",
2585            "three ",
2586            "five ) ˇtwo one✅ four three six five ( one✅ ",
2587            "three ",
2588            "five ) ˇ",
2589        ]
2590        .join("\n")),
2591    );
2592
2593    // Cut with three selections, one of which is full-line.
2594    cx.set_state(indoc! {"
2595        1«2ˇ»3
2596        4ˇ567
2597        «8ˇ»9"});
2598    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2599    cx.assert_editor_state(indoc! {"
2600        1ˇ3
2601        ˇ9"});
2602
2603    // Paste with three selections, noticing how the copied selection that was full-line
2604    // gets inserted before the second cursor.
2605    cx.set_state(indoc! {"
2606        1ˇ3
26072608        «oˇ»ne"});
2609    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2610    cx.assert_editor_state(indoc! {"
2611        12ˇ3
2612        4567
26132614        8ˇne"});
2615
2616    // Copy with a single cursor only, which writes the whole line into the clipboard.
2617    cx.set_state(indoc! {"
2618        The quick brown
2619        fox juˇmps over
2620        the lazy dog"});
2621    cx.update_editor(|e, cx| e.copy(&Copy, cx));
2622    cx.cx.assert_clipboard_content(Some("fox jumps over\n"));
2623
2624    // Paste with three selections, noticing how the copied full-line selection is inserted
2625    // before the empty selections but replaces the selection that is non-empty.
2626    cx.set_state(indoc! {"
2627        Tˇhe quick brown
2628        «foˇ»x jumps over
2629        tˇhe lazy dog"});
2630    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2631    cx.assert_editor_state(indoc! {"
2632        fox jumps over
2633        Tˇhe quick brown
2634        fox jumps over
2635        ˇx jumps over
2636        fox jumps over
2637        tˇhe lazy dog"});
2638}
2639
2640#[gpui::test]
2641async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
2642    init_test(cx, |_| {});
2643
2644    let mut cx = EditorTestContext::new(cx).await;
2645    let language = Arc::new(Language::new(
2646        LanguageConfig::default(),
2647        Some(tree_sitter_rust::language()),
2648    ));
2649    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
2650
2651    // Cut an indented block, without the leading whitespace.
2652    cx.set_state(indoc! {"
2653        const a: B = (
2654            c(),
2655            «d(
2656                e,
2657                f
2658            )ˇ»
2659        );
2660    "});
2661    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2662    cx.assert_editor_state(indoc! {"
2663        const a: B = (
2664            c(),
2665            ˇ
2666        );
2667    "});
2668
2669    // Paste it at the same position.
2670    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2671    cx.assert_editor_state(indoc! {"
2672        const a: B = (
2673            c(),
2674            d(
2675                e,
2676                f
26772678        );
2679    "});
2680
2681    // Paste it at a line with a lower indent level.
2682    cx.set_state(indoc! {"
2683        ˇ
2684        const a: B = (
2685            c(),
2686        );
2687    "});
2688    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2689    cx.assert_editor_state(indoc! {"
2690        d(
2691            e,
2692            f
26932694        const a: B = (
2695            c(),
2696        );
2697    "});
2698
2699    // Cut an indented block, with the leading whitespace.
2700    cx.set_state(indoc! {"
2701        const a: B = (
2702            c(),
2703        «    d(
2704                e,
2705                f
2706            )
2707        ˇ»);
2708    "});
2709    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2710    cx.assert_editor_state(indoc! {"
2711        const a: B = (
2712            c(),
2713        ˇ);
2714    "});
2715
2716    // Paste it at the same position.
2717    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2718    cx.assert_editor_state(indoc! {"
2719        const a: B = (
2720            c(),
2721            d(
2722                e,
2723                f
2724            )
2725        ˇ);
2726    "});
2727
2728    // Paste it at a line with a higher indent level.
2729    cx.set_state(indoc! {"
2730        const a: B = (
2731            c(),
2732            d(
2733                e,
27342735            )
2736        );
2737    "});
2738    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2739    cx.assert_editor_state(indoc! {"
2740        const a: B = (
2741            c(),
2742            d(
2743                e,
2744                f    d(
2745                    e,
2746                    f
2747                )
2748        ˇ
2749            )
2750        );
2751    "});
2752}
2753
2754#[gpui::test]
2755fn test_select_all(cx: &mut TestAppContext) {
2756    init_test(cx, |_| {});
2757
2758    let (_, view) = cx.add_window(|cx| {
2759        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
2760        build_editor(buffer, cx)
2761    });
2762    view.update(cx, |view, cx| {
2763        view.select_all(&SelectAll, cx);
2764        assert_eq!(
2765            view.selections.display_ranges(cx),
2766            &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
2767        );
2768    });
2769}
2770
2771#[gpui::test]
2772fn test_select_line(cx: &mut TestAppContext) {
2773    init_test(cx, |_| {});
2774
2775    let (_, view) = cx.add_window(|cx| {
2776        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
2777        build_editor(buffer, cx)
2778    });
2779    view.update(cx, |view, cx| {
2780        view.change_selections(None, cx, |s| {
2781            s.select_display_ranges([
2782                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2783                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2784                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2785                DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
2786            ])
2787        });
2788        view.select_line(&SelectLine, cx);
2789        assert_eq!(
2790            view.selections.display_ranges(cx),
2791            vec![
2792                DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
2793                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
2794            ]
2795        );
2796    });
2797
2798    view.update(cx, |view, cx| {
2799        view.select_line(&SelectLine, cx);
2800        assert_eq!(
2801            view.selections.display_ranges(cx),
2802            vec![
2803                DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
2804                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
2805            ]
2806        );
2807    });
2808
2809    view.update(cx, |view, cx| {
2810        view.select_line(&SelectLine, cx);
2811        assert_eq!(
2812            view.selections.display_ranges(cx),
2813            vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
2814        );
2815    });
2816}
2817
2818#[gpui::test]
2819fn test_split_selection_into_lines(cx: &mut TestAppContext) {
2820    init_test(cx, |_| {});
2821
2822    let (_, view) = cx.add_window(|cx| {
2823        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
2824        build_editor(buffer, cx)
2825    });
2826    view.update(cx, |view, cx| {
2827        view.fold_ranges(
2828            vec![
2829                Point::new(0, 2)..Point::new(1, 2),
2830                Point::new(2, 3)..Point::new(4, 1),
2831                Point::new(7, 0)..Point::new(8, 4),
2832            ],
2833            true,
2834            cx,
2835        );
2836        view.change_selections(None, cx, |s| {
2837            s.select_display_ranges([
2838                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2839                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2840                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2841                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
2842            ])
2843        });
2844        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
2845    });
2846
2847    view.update(cx, |view, cx| {
2848        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
2849        assert_eq!(
2850            view.display_text(cx),
2851            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
2852        );
2853        assert_eq!(
2854            view.selections.display_ranges(cx),
2855            [
2856                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2857                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2858                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
2859                DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
2860            ]
2861        );
2862    });
2863
2864    view.update(cx, |view, cx| {
2865        view.change_selections(None, cx, |s| {
2866            s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
2867        });
2868        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
2869        assert_eq!(
2870            view.display_text(cx),
2871            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
2872        );
2873        assert_eq!(
2874            view.selections.display_ranges(cx),
2875            [
2876                DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
2877                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
2878                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
2879                DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
2880                DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
2881                DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
2882                DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
2883                DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
2884            ]
2885        );
2886    });
2887}
2888
2889#[gpui::test]
2890fn test_add_selection_above_below(cx: &mut TestAppContext) {
2891    init_test(cx, |_| {});
2892
2893    let (_, view) = cx.add_window(|cx| {
2894        let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
2895        build_editor(buffer, cx)
2896    });
2897
2898    view.update(cx, |view, cx| {
2899        view.change_selections(None, cx, |s| {
2900            s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
2901        });
2902    });
2903    view.update(cx, |view, cx| {
2904        view.add_selection_above(&AddSelectionAbove, cx);
2905        assert_eq!(
2906            view.selections.display_ranges(cx),
2907            vec![
2908                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
2909                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
2910            ]
2911        );
2912    });
2913
2914    view.update(cx, |view, cx| {
2915        view.add_selection_above(&AddSelectionAbove, cx);
2916        assert_eq!(
2917            view.selections.display_ranges(cx),
2918            vec![
2919                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
2920                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
2921            ]
2922        );
2923    });
2924
2925    view.update(cx, |view, cx| {
2926        view.add_selection_below(&AddSelectionBelow, cx);
2927        assert_eq!(
2928            view.selections.display_ranges(cx),
2929            vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
2930        );
2931
2932        view.undo_selection(&UndoSelection, cx);
2933        assert_eq!(
2934            view.selections.display_ranges(cx),
2935            vec![
2936                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
2937                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
2938            ]
2939        );
2940
2941        view.redo_selection(&RedoSelection, cx);
2942        assert_eq!(
2943            view.selections.display_ranges(cx),
2944            vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
2945        );
2946    });
2947
2948    view.update(cx, |view, cx| {
2949        view.add_selection_below(&AddSelectionBelow, cx);
2950        assert_eq!(
2951            view.selections.display_ranges(cx),
2952            vec![
2953                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
2954                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
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, 3),
2965                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
2966            ]
2967        );
2968    });
2969
2970    view.update(cx, |view, cx| {
2971        view.change_selections(None, cx, |s| {
2972            s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
2973        });
2974    });
2975    view.update(cx, |view, cx| {
2976        view.add_selection_below(&AddSelectionBelow, cx);
2977        assert_eq!(
2978            view.selections.display_ranges(cx),
2979            vec![
2980                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
2981                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
2982            ]
2983        );
2984    });
2985
2986    view.update(cx, |view, cx| {
2987        view.add_selection_below(&AddSelectionBelow, cx);
2988        assert_eq!(
2989            view.selections.display_ranges(cx),
2990            vec![
2991                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
2992                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
2993            ]
2994        );
2995    });
2996
2997    view.update(cx, |view, cx| {
2998        view.add_selection_above(&AddSelectionAbove, cx);
2999        assert_eq!(
3000            view.selections.display_ranges(cx),
3001            vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
3002        );
3003    });
3004
3005    view.update(cx, |view, cx| {
3006        view.add_selection_above(&AddSelectionAbove, cx);
3007        assert_eq!(
3008            view.selections.display_ranges(cx),
3009            vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
3010        );
3011    });
3012
3013    view.update(cx, |view, cx| {
3014        view.change_selections(None, cx, |s| {
3015            s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
3016        });
3017        view.add_selection_below(&AddSelectionBelow, cx);
3018        assert_eq!(
3019            view.selections.display_ranges(cx),
3020            vec![
3021                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
3022                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
3023                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
3024            ]
3025        );
3026    });
3027
3028    view.update(cx, |view, cx| {
3029        view.add_selection_below(&AddSelectionBelow, cx);
3030        assert_eq!(
3031            view.selections.display_ranges(cx),
3032            vec![
3033                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
3034                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
3035                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
3036                DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
3037            ]
3038        );
3039    });
3040
3041    view.update(cx, |view, cx| {
3042        view.add_selection_above(&AddSelectionAbove, cx);
3043        assert_eq!(
3044            view.selections.display_ranges(cx),
3045            vec![
3046                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
3047                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
3048                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
3049            ]
3050        );
3051    });
3052
3053    view.update(cx, |view, cx| {
3054        view.change_selections(None, cx, |s| {
3055            s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
3056        });
3057    });
3058    view.update(cx, |view, cx| {
3059        view.add_selection_above(&AddSelectionAbove, cx);
3060        assert_eq!(
3061            view.selections.display_ranges(cx),
3062            vec![
3063                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
3064                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
3065                DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
3066                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
3067            ]
3068        );
3069    });
3070
3071    view.update(cx, |view, cx| {
3072        view.add_selection_below(&AddSelectionBelow, cx);
3073        assert_eq!(
3074            view.selections.display_ranges(cx),
3075            vec![
3076                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
3077                DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
3078                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
3079            ]
3080        );
3081    });
3082}
3083
3084#[gpui::test]
3085async fn test_select_next(cx: &mut gpui::TestAppContext) {
3086    init_test(cx, |_| {});
3087
3088    let mut cx = EditorTestContext::new(cx).await;
3089    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
3090
3091    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
3092    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3093
3094    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
3095    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
3096
3097    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
3098    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3099
3100    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
3101    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
3102
3103    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
3104    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
3105
3106    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
3107    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
3108}
3109
3110#[gpui::test]
3111async fn test_select_previous(cx: &mut gpui::TestAppContext) {
3112    init_test(cx, |_| {});
3113    {
3114        // `Select previous` without a selection (selects wordwise)
3115        let mut cx = EditorTestContext::new(cx).await;
3116        cx.set_state("abc\nˇabc abc\ndefabc\nabc");
3117
3118        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3119        cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3120
3121        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3122        cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
3123
3124        cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
3125        cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3126
3127        cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
3128        cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
3129
3130        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3131        cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
3132
3133        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3134        cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
3135    }
3136    {
3137        // `Select previous` with a selection
3138        let mut cx = EditorTestContext::new(cx).await;
3139        cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
3140
3141        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3142        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
3143
3144        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3145        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
3146
3147        cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
3148        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
3149
3150        cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
3151        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
3152
3153        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3154        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
3155
3156        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3157        cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
3158    }
3159}
3160
3161#[gpui::test]
3162async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
3163    init_test(cx, |_| {});
3164
3165    let language = Arc::new(Language::new(
3166        LanguageConfig::default(),
3167        Some(tree_sitter_rust::language()),
3168    ));
3169
3170    let text = r#"
3171        use mod1::mod2::{mod3, mod4};
3172
3173        fn fn_1(param1: bool, param2: &str) {
3174            let var1 = "text";
3175        }
3176    "#
3177    .unindent();
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 (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
3182    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
3183        .await;
3184
3185    view.update(cx, |view, cx| {
3186        view.change_selections(None, cx, |s| {
3187            s.select_display_ranges([
3188                DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3189                DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3190                DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3191            ]);
3192        });
3193        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3194    });
3195    assert_eq!(
3196        view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
3197        &[
3198            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
3199            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3200            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
3201        ]
3202    );
3203
3204    view.update(cx, |view, cx| {
3205        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3206    });
3207    assert_eq!(
3208        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3209        &[
3210            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3211            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
3212        ]
3213    );
3214
3215    view.update(cx, |view, cx| {
3216        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3217    });
3218    assert_eq!(
3219        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3220        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
3221    );
3222
3223    // Trying to expand the selected syntax node one more time has no effect.
3224    view.update(cx, |view, cx| {
3225        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3226    });
3227    assert_eq!(
3228        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3229        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
3230    );
3231
3232    view.update(cx, |view, cx| {
3233        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3234    });
3235    assert_eq!(
3236        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3237        &[
3238            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3239            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
3240        ]
3241    );
3242
3243    view.update(cx, |view, cx| {
3244        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3245    });
3246    assert_eq!(
3247        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3248        &[
3249            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
3250            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3251            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
3252        ]
3253    );
3254
3255    view.update(cx, |view, cx| {
3256        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3257    });
3258    assert_eq!(
3259        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3260        &[
3261            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3262            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3263            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3264        ]
3265    );
3266
3267    // Trying to shrink the selected syntax node one more time has no effect.
3268    view.update(cx, |view, cx| {
3269        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3270    });
3271    assert_eq!(
3272        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3273        &[
3274            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3275            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3276            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3277        ]
3278    );
3279
3280    // Ensure that we keep expanding the selection if the larger selection starts or ends within
3281    // a fold.
3282    view.update(cx, |view, cx| {
3283        view.fold_ranges(
3284            vec![
3285                Point::new(0, 21)..Point::new(0, 24),
3286                Point::new(3, 20)..Point::new(3, 22),
3287            ],
3288            true,
3289            cx,
3290        );
3291        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3292    });
3293    assert_eq!(
3294        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3295        &[
3296            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3297            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3298            DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
3299        ]
3300    );
3301}
3302
3303#[gpui::test]
3304async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
3305    init_test(cx, |_| {});
3306
3307    let language = Arc::new(
3308        Language::new(
3309            LanguageConfig {
3310                brackets: BracketPairConfig {
3311                    pairs: vec![
3312                        BracketPair {
3313                            start: "{".to_string(),
3314                            end: "}".to_string(),
3315                            close: false,
3316                            newline: true,
3317                        },
3318                        BracketPair {
3319                            start: "(".to_string(),
3320                            end: ")".to_string(),
3321                            close: false,
3322                            newline: true,
3323                        },
3324                    ],
3325                    ..Default::default()
3326                },
3327                ..Default::default()
3328            },
3329            Some(tree_sitter_rust::language()),
3330        )
3331        .with_indents_query(
3332            r#"
3333                (_ "(" ")" @end) @indent
3334                (_ "{" "}" @end) @indent
3335            "#,
3336        )
3337        .unwrap(),
3338    );
3339
3340    let text = "fn a() {}";
3341
3342    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3343    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3344    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3345    editor
3346        .condition(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
3347        .await;
3348
3349    editor.update(cx, |editor, cx| {
3350        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
3351        editor.newline(&Newline, cx);
3352        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
3353        assert_eq!(
3354            editor.selections.ranges(cx),
3355            &[
3356                Point::new(1, 4)..Point::new(1, 4),
3357                Point::new(3, 4)..Point::new(3, 4),
3358                Point::new(5, 0)..Point::new(5, 0)
3359            ]
3360        );
3361    });
3362}
3363
3364#[gpui::test]
3365async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
3366    init_test(cx, |_| {});
3367
3368    let mut cx = EditorTestContext::new(cx).await;
3369
3370    let language = Arc::new(Language::new(
3371        LanguageConfig {
3372            brackets: BracketPairConfig {
3373                pairs: vec![
3374                    BracketPair {
3375                        start: "{".to_string(),
3376                        end: "}".to_string(),
3377                        close: true,
3378                        newline: true,
3379                    },
3380                    BracketPair {
3381                        start: "(".to_string(),
3382                        end: ")".to_string(),
3383                        close: true,
3384                        newline: true,
3385                    },
3386                    BracketPair {
3387                        start: "/*".to_string(),
3388                        end: " */".to_string(),
3389                        close: true,
3390                        newline: true,
3391                    },
3392                    BracketPair {
3393                        start: "[".to_string(),
3394                        end: "]".to_string(),
3395                        close: false,
3396                        newline: true,
3397                    },
3398                    BracketPair {
3399                        start: "\"".to_string(),
3400                        end: "\"".to_string(),
3401                        close: true,
3402                        newline: false,
3403                    },
3404                ],
3405                ..Default::default()
3406            },
3407            autoclose_before: "})]".to_string(),
3408            ..Default::default()
3409        },
3410        Some(tree_sitter_rust::language()),
3411    ));
3412
3413    let registry = Arc::new(LanguageRegistry::test());
3414    registry.add(language.clone());
3415    cx.update_buffer(|buffer, cx| {
3416        buffer.set_language_registry(registry);
3417        buffer.set_language(Some(language), cx);
3418    });
3419
3420    cx.set_state(
3421        &r#"
3422            🏀ˇ
3423            εˇ
3424            ❤️ˇ
3425        "#
3426        .unindent(),
3427    );
3428
3429    // autoclose multiple nested brackets at multiple cursors
3430    cx.update_editor(|view, cx| {
3431        view.handle_input("{", cx);
3432        view.handle_input("{", cx);
3433        view.handle_input("{", cx);
3434    });
3435    cx.assert_editor_state(
3436        &"
3437            🏀{{{ˇ}}}
3438            ε{{{ˇ}}}
3439            ❤️{{{ˇ}}}
3440        "
3441        .unindent(),
3442    );
3443
3444    // insert a different closing bracket
3445    cx.update_editor(|view, cx| {
3446        view.handle_input(")", cx);
3447    });
3448    cx.assert_editor_state(
3449        &"
3450            🏀{{{)ˇ}}}
3451            ε{{{)ˇ}}}
3452            ❤️{{{)ˇ}}}
3453        "
3454        .unindent(),
3455    );
3456
3457    // skip over the auto-closed brackets when typing a closing bracket
3458    cx.update_editor(|view, cx| {
3459        view.move_right(&MoveRight, cx);
3460        view.handle_input("}", cx);
3461        view.handle_input("}", cx);
3462        view.handle_input("}", cx);
3463    });
3464    cx.assert_editor_state(
3465        &"
3466            🏀{{{)}}}}ˇ
3467            ε{{{)}}}}ˇ
3468            ❤️{{{)}}}}ˇ
3469        "
3470        .unindent(),
3471    );
3472
3473    // autoclose multi-character pairs
3474    cx.set_state(
3475        &"
3476            ˇ
3477            ˇ
3478        "
3479        .unindent(),
3480    );
3481    cx.update_editor(|view, cx| {
3482        view.handle_input("/", cx);
3483        view.handle_input("*", cx);
3484    });
3485    cx.assert_editor_state(
3486        &"
3487            /*ˇ */
3488            /*ˇ */
3489        "
3490        .unindent(),
3491    );
3492
3493    // one cursor autocloses a multi-character pair, one cursor
3494    // does not autoclose.
3495    cx.set_state(
3496        &"
34973498            ˇ
3499        "
3500        .unindent(),
3501    );
3502    cx.update_editor(|view, cx| view.handle_input("*", cx));
3503    cx.assert_editor_state(
3504        &"
3505            /*ˇ */
35063507        "
3508        .unindent(),
3509    );
3510
3511    // Don't autoclose if the next character isn't whitespace and isn't
3512    // listed in the language's "autoclose_before" section.
3513    cx.set_state("ˇa b");
3514    cx.update_editor(|view, cx| view.handle_input("{", cx));
3515    cx.assert_editor_state("{ˇa b");
3516
3517    // Don't autoclose if `close` is false for the bracket pair
3518    cx.set_state("ˇ");
3519    cx.update_editor(|view, cx| view.handle_input("[", cx));
3520    cx.assert_editor_state("");
3521
3522    // Surround with brackets if text is selected
3523    cx.set_state("«aˇ» b");
3524    cx.update_editor(|view, cx| view.handle_input("{", cx));
3525    cx.assert_editor_state("{«aˇ»} b");
3526
3527    // Autclose pair where the start and end characters are the same
3528    cx.set_state("");
3529    cx.update_editor(|view, cx| view.handle_input("\"", cx));
3530    cx.assert_editor_state("a\"ˇ\"");
3531    cx.update_editor(|view, cx| view.handle_input("\"", cx));
3532    cx.assert_editor_state("a\"\"ˇ");
3533}
3534
3535#[gpui::test]
3536async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
3537    init_test(cx, |_| {});
3538
3539    let mut cx = EditorTestContext::new(cx).await;
3540
3541    let html_language = Arc::new(
3542        Language::new(
3543            LanguageConfig {
3544                name: "HTML".into(),
3545                brackets: BracketPairConfig {
3546                    pairs: vec![
3547                        BracketPair {
3548                            start: "<".into(),
3549                            end: ">".into(),
3550                            close: true,
3551                            ..Default::default()
3552                        },
3553                        BracketPair {
3554                            start: "{".into(),
3555                            end: "}".into(),
3556                            close: true,
3557                            ..Default::default()
3558                        },
3559                        BracketPair {
3560                            start: "(".into(),
3561                            end: ")".into(),
3562                            close: true,
3563                            ..Default::default()
3564                        },
3565                    ],
3566                    ..Default::default()
3567                },
3568                autoclose_before: "})]>".into(),
3569                ..Default::default()
3570            },
3571            Some(tree_sitter_html::language()),
3572        )
3573        .with_injection_query(
3574            r#"
3575            (script_element
3576                (raw_text) @content
3577                (#set! "language" "javascript"))
3578            "#,
3579        )
3580        .unwrap(),
3581    );
3582
3583    let javascript_language = Arc::new(Language::new(
3584        LanguageConfig {
3585            name: "JavaScript".into(),
3586            brackets: BracketPairConfig {
3587                pairs: vec![
3588                    BracketPair {
3589                        start: "/*".into(),
3590                        end: " */".into(),
3591                        close: true,
3592                        ..Default::default()
3593                    },
3594                    BracketPair {
3595                        start: "{".into(),
3596                        end: "}".into(),
3597                        close: true,
3598                        ..Default::default()
3599                    },
3600                    BracketPair {
3601                        start: "(".into(),
3602                        end: ")".into(),
3603                        close: true,
3604                        ..Default::default()
3605                    },
3606                ],
3607                ..Default::default()
3608            },
3609            autoclose_before: "})]>".into(),
3610            ..Default::default()
3611        },
3612        Some(tree_sitter_javascript::language()),
3613    ));
3614
3615    let registry = Arc::new(LanguageRegistry::test());
3616    registry.add(html_language.clone());
3617    registry.add(javascript_language.clone());
3618
3619    cx.update_buffer(|buffer, cx| {
3620        buffer.set_language_registry(registry);
3621        buffer.set_language(Some(html_language), cx);
3622    });
3623
3624    cx.set_state(
3625        &r#"
3626            <body>ˇ
3627                <script>
3628                    var x = 1;ˇ
3629                </script>
3630            </body>ˇ
3631        "#
3632        .unindent(),
3633    );
3634
3635    // Precondition: different languages are active at different locations.
3636    cx.update_editor(|editor, cx| {
3637        let snapshot = editor.snapshot(cx);
3638        let cursors = editor.selections.ranges::<usize>(cx);
3639        let languages = cursors
3640            .iter()
3641            .map(|c| snapshot.language_at(c.start).unwrap().name())
3642            .collect::<Vec<_>>();
3643        assert_eq!(
3644            languages,
3645            &["HTML".into(), "JavaScript".into(), "HTML".into()]
3646        );
3647    });
3648
3649    // Angle brackets autoclose in HTML, but not JavaScript.
3650    cx.update_editor(|editor, cx| {
3651        editor.handle_input("<", cx);
3652        editor.handle_input("a", cx);
3653    });
3654    cx.assert_editor_state(
3655        &r#"
3656            <body><aˇ>
3657                <script>
3658                    var x = 1;<aˇ
3659                </script>
3660            </body><aˇ>
3661        "#
3662        .unindent(),
3663    );
3664
3665    // Curly braces and parens autoclose in both HTML and JavaScript.
3666    cx.update_editor(|editor, cx| {
3667        editor.handle_input(" b=", cx);
3668        editor.handle_input("{", cx);
3669        editor.handle_input("c", cx);
3670        editor.handle_input("(", cx);
3671    });
3672    cx.assert_editor_state(
3673        &r#"
3674            <body><a b={c(ˇ)}>
3675                <script>
3676                    var x = 1;<a b={c(ˇ)}
3677                </script>
3678            </body><a b={c(ˇ)}>
3679        "#
3680        .unindent(),
3681    );
3682
3683    // Brackets that were already autoclosed are skipped.
3684    cx.update_editor(|editor, cx| {
3685        editor.handle_input(")", cx);
3686        editor.handle_input("d", cx);
3687        editor.handle_input("}", cx);
3688    });
3689    cx.assert_editor_state(
3690        &r#"
3691            <body><a b={c()d}ˇ>
3692                <script>
3693                    var x = 1;<a b={c()d}ˇ
3694                </script>
3695            </body><a b={c()d}ˇ>
3696        "#
3697        .unindent(),
3698    );
3699    cx.update_editor(|editor, cx| {
3700        editor.handle_input(">", cx);
3701    });
3702    cx.assert_editor_state(
3703        &r#"
3704            <body><a b={c()d}>ˇ
3705                <script>
3706                    var x = 1;<a b={c()d}>ˇ
3707                </script>
3708            </body><a b={c()d}>ˇ
3709        "#
3710        .unindent(),
3711    );
3712
3713    // Reset
3714    cx.set_state(
3715        &r#"
3716            <body>ˇ
3717                <script>
3718                    var x = 1;ˇ
3719                </script>
3720            </body>ˇ
3721        "#
3722        .unindent(),
3723    );
3724
3725    cx.update_editor(|editor, cx| {
3726        editor.handle_input("<", cx);
3727    });
3728    cx.assert_editor_state(
3729        &r#"
3730            <body><ˇ>
3731                <script>
3732                    var x = 1;<ˇ
3733                </script>
3734            </body><ˇ>
3735        "#
3736        .unindent(),
3737    );
3738
3739    // When backspacing, the closing angle brackets are removed.
3740    cx.update_editor(|editor, cx| {
3741        editor.backspace(&Backspace, cx);
3742    });
3743    cx.assert_editor_state(
3744        &r#"
3745            <body>ˇ
3746                <script>
3747                    var x = 1;ˇ
3748                </script>
3749            </body>ˇ
3750        "#
3751        .unindent(),
3752    );
3753
3754    // Block comments autoclose in JavaScript, but not HTML.
3755    cx.update_editor(|editor, cx| {
3756        editor.handle_input("/", cx);
3757        editor.handle_input("*", cx);
3758    });
3759    cx.assert_editor_state(
3760        &r#"
3761            <body>/*ˇ
3762                <script>
3763                    var x = 1;/*ˇ */
3764                </script>
3765            </body>/*ˇ
3766        "#
3767        .unindent(),
3768    );
3769}
3770
3771#[gpui::test]
3772async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
3773    init_test(cx, |_| {});
3774
3775    let mut cx = EditorTestContext::new(cx).await;
3776
3777    let rust_language = Arc::new(
3778        Language::new(
3779            LanguageConfig {
3780                name: "Rust".into(),
3781                brackets: serde_json::from_value(json!([
3782                    { "start": "{", "end": "}", "close": true, "newline": true },
3783                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
3784                ]))
3785                .unwrap(),
3786                autoclose_before: "})]>".into(),
3787                ..Default::default()
3788            },
3789            Some(tree_sitter_rust::language()),
3790        )
3791        .with_override_query("(string_literal) @string")
3792        .unwrap(),
3793    );
3794
3795    let registry = Arc::new(LanguageRegistry::test());
3796    registry.add(rust_language.clone());
3797
3798    cx.update_buffer(|buffer, cx| {
3799        buffer.set_language_registry(registry);
3800        buffer.set_language(Some(rust_language), cx);
3801    });
3802
3803    cx.set_state(
3804        &r#"
3805            let x = ˇ
3806        "#
3807        .unindent(),
3808    );
3809
3810    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
3811    cx.update_editor(|editor, cx| {
3812        editor.handle_input("\"", cx);
3813    });
3814    cx.assert_editor_state(
3815        &r#"
3816            let x = "ˇ"
3817        "#
3818        .unindent(),
3819    );
3820
3821    // Inserting another quotation mark. The cursor moves across the existing
3822    // automatically-inserted quotation mark.
3823    cx.update_editor(|editor, cx| {
3824        editor.handle_input("\"", cx);
3825    });
3826    cx.assert_editor_state(
3827        &r#"
3828            let x = ""ˇ
3829        "#
3830        .unindent(),
3831    );
3832
3833    // Reset
3834    cx.set_state(
3835        &r#"
3836            let x = ˇ
3837        "#
3838        .unindent(),
3839    );
3840
3841    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
3842    cx.update_editor(|editor, cx| {
3843        editor.handle_input("\"", cx);
3844        editor.handle_input(" ", cx);
3845        editor.move_left(&Default::default(), cx);
3846        editor.handle_input("\\", cx);
3847        editor.handle_input("\"", cx);
3848    });
3849    cx.assert_editor_state(
3850        &r#"
3851            let x = "\"ˇ "
3852        "#
3853        .unindent(),
3854    );
3855
3856    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
3857    // mark. Nothing is inserted.
3858    cx.update_editor(|editor, cx| {
3859        editor.move_right(&Default::default(), cx);
3860        editor.handle_input("\"", cx);
3861    });
3862    cx.assert_editor_state(
3863        &r#"
3864            let x = "\" "ˇ
3865        "#
3866        .unindent(),
3867    );
3868}
3869
3870#[gpui::test]
3871async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
3872    init_test(cx, |_| {});
3873
3874    let language = Arc::new(Language::new(
3875        LanguageConfig {
3876            brackets: BracketPairConfig {
3877                pairs: vec![
3878                    BracketPair {
3879                        start: "{".to_string(),
3880                        end: "}".to_string(),
3881                        close: true,
3882                        newline: true,
3883                    },
3884                    BracketPair {
3885                        start: "/* ".to_string(),
3886                        end: "*/".to_string(),
3887                        close: true,
3888                        ..Default::default()
3889                    },
3890                ],
3891                ..Default::default()
3892            },
3893            ..Default::default()
3894        },
3895        Some(tree_sitter_rust::language()),
3896    ));
3897
3898    let text = r#"
3899        a
3900        b
3901        c
3902    "#
3903    .unindent();
3904
3905    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3906    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3907    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
3908    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
3909        .await;
3910
3911    view.update(cx, |view, cx| {
3912        view.change_selections(None, cx, |s| {
3913            s.select_display_ranges([
3914                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3915                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
3916                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
3917            ])
3918        });
3919
3920        view.handle_input("{", cx);
3921        view.handle_input("{", cx);
3922        view.handle_input("{", cx);
3923        assert_eq!(
3924            view.text(cx),
3925            "
3926                {{{a}}}
3927                {{{b}}}
3928                {{{c}}}
3929            "
3930            .unindent()
3931        );
3932        assert_eq!(
3933            view.selections.display_ranges(cx),
3934            [
3935                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
3936                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
3937                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
3938            ]
3939        );
3940
3941        view.undo(&Undo, cx);
3942        view.undo(&Undo, cx);
3943        view.undo(&Undo, cx);
3944        assert_eq!(
3945            view.text(cx),
3946            "
3947                a
3948                b
3949                c
3950            "
3951            .unindent()
3952        );
3953        assert_eq!(
3954            view.selections.display_ranges(cx),
3955            [
3956                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3957                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
3958                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
3959            ]
3960        );
3961
3962        // Ensure inserting the first character of a multi-byte bracket pair
3963        // doesn't surround the selections with the bracket.
3964        view.handle_input("/", cx);
3965        assert_eq!(
3966            view.text(cx),
3967            "
3968                /
3969                /
3970                /
3971            "
3972            .unindent()
3973        );
3974        assert_eq!(
3975            view.selections.display_ranges(cx),
3976            [
3977                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
3978                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
3979                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
3980            ]
3981        );
3982
3983        view.undo(&Undo, cx);
3984        assert_eq!(
3985            view.text(cx),
3986            "
3987                a
3988                b
3989                c
3990            "
3991            .unindent()
3992        );
3993        assert_eq!(
3994            view.selections.display_ranges(cx),
3995            [
3996                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3997                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
3998                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
3999            ]
4000        );
4001
4002        // Ensure inserting the last character of a multi-byte bracket pair
4003        // doesn't surround the selections with the bracket.
4004        view.handle_input("*", cx);
4005        assert_eq!(
4006            view.text(cx),
4007            "
4008                *
4009                *
4010                *
4011            "
4012            .unindent()
4013        );
4014        assert_eq!(
4015            view.selections.display_ranges(cx),
4016            [
4017                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
4018                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
4019                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
4020            ]
4021        );
4022    });
4023}
4024
4025#[gpui::test]
4026async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
4027    init_test(cx, |_| {});
4028
4029    let language = Arc::new(Language::new(
4030        LanguageConfig {
4031            brackets: BracketPairConfig {
4032                pairs: vec![BracketPair {
4033                    start: "{".to_string(),
4034                    end: "}".to_string(),
4035                    close: true,
4036                    newline: true,
4037                }],
4038                ..Default::default()
4039            },
4040            autoclose_before: "}".to_string(),
4041            ..Default::default()
4042        },
4043        Some(tree_sitter_rust::language()),
4044    ));
4045
4046    let text = r#"
4047        a
4048        b
4049        c
4050    "#
4051    .unindent();
4052
4053    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
4054    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4055    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4056    editor
4057        .condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
4058        .await;
4059
4060    editor.update(cx, |editor, cx| {
4061        editor.change_selections(None, cx, |s| {
4062            s.select_ranges([
4063                Point::new(0, 1)..Point::new(0, 1),
4064                Point::new(1, 1)..Point::new(1, 1),
4065                Point::new(2, 1)..Point::new(2, 1),
4066            ])
4067        });
4068
4069        editor.handle_input("{", cx);
4070        editor.handle_input("{", cx);
4071        editor.handle_input("_", cx);
4072        assert_eq!(
4073            editor.text(cx),
4074            "
4075                a{{_}}
4076                b{{_}}
4077                c{{_}}
4078            "
4079            .unindent()
4080        );
4081        assert_eq!(
4082            editor.selections.ranges::<Point>(cx),
4083            [
4084                Point::new(0, 4)..Point::new(0, 4),
4085                Point::new(1, 4)..Point::new(1, 4),
4086                Point::new(2, 4)..Point::new(2, 4)
4087            ]
4088        );
4089
4090        editor.backspace(&Default::default(), cx);
4091        editor.backspace(&Default::default(), cx);
4092        assert_eq!(
4093            editor.text(cx),
4094            "
4095                a{}
4096                b{}
4097                c{}
4098            "
4099            .unindent()
4100        );
4101        assert_eq!(
4102            editor.selections.ranges::<Point>(cx),
4103            [
4104                Point::new(0, 2)..Point::new(0, 2),
4105                Point::new(1, 2)..Point::new(1, 2),
4106                Point::new(2, 2)..Point::new(2, 2)
4107            ]
4108        );
4109
4110        editor.delete_to_previous_word_start(&Default::default(), cx);
4111        assert_eq!(
4112            editor.text(cx),
4113            "
4114                a
4115                b
4116                c
4117            "
4118            .unindent()
4119        );
4120        assert_eq!(
4121            editor.selections.ranges::<Point>(cx),
4122            [
4123                Point::new(0, 1)..Point::new(0, 1),
4124                Point::new(1, 1)..Point::new(1, 1),
4125                Point::new(2, 1)..Point::new(2, 1)
4126            ]
4127        );
4128    });
4129}
4130
4131#[gpui::test]
4132async fn test_snippets(cx: &mut gpui::TestAppContext) {
4133    init_test(cx, |_| {});
4134
4135    let (text, insertion_ranges) = marked_text_ranges(
4136        indoc! {"
4137            a.ˇ b
4138            a.ˇ b
4139            a.ˇ b
4140        "},
4141        false,
4142    );
4143
4144    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
4145    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4146
4147    editor.update(cx, |editor, cx| {
4148        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
4149
4150        editor
4151            .insert_snippet(&insertion_ranges, snippet, cx)
4152            .unwrap();
4153
4154        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
4155            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
4156            assert_eq!(editor.text(cx), expected_text);
4157            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
4158        }
4159
4160        assert(
4161            editor,
4162            cx,
4163            indoc! {"
4164                a.f(«one», two, «three») b
4165                a.f(«one», two, «three») b
4166                a.f(«one», two, «three») b
4167            "},
4168        );
4169
4170        // Can't move earlier than the first tab stop
4171        assert!(!editor.move_to_prev_snippet_tabstop(cx));
4172        assert(
4173            editor,
4174            cx,
4175            indoc! {"
4176                a.f(«one», two, «three») b
4177                a.f(«one», two, «three») b
4178                a.f(«one», two, «three») b
4179            "},
4180        );
4181
4182        assert!(editor.move_to_next_snippet_tabstop(cx));
4183        assert(
4184            editor,
4185            cx,
4186            indoc! {"
4187                a.f(one, «two», three) b
4188                a.f(one, «two», three) b
4189                a.f(one, «two», three) b
4190            "},
4191        );
4192
4193        editor.move_to_prev_snippet_tabstop(cx);
4194        assert(
4195            editor,
4196            cx,
4197            indoc! {"
4198                a.f(«one», two, «three») b
4199                a.f(«one», two, «three») b
4200                a.f(«one», two, «three») b
4201            "},
4202        );
4203
4204        assert!(editor.move_to_next_snippet_tabstop(cx));
4205        assert(
4206            editor,
4207            cx,
4208            indoc! {"
4209                a.f(one, «two», three) b
4210                a.f(one, «two», three) b
4211                a.f(one, «two», three) b
4212            "},
4213        );
4214        assert!(editor.move_to_next_snippet_tabstop(cx));
4215        assert(
4216            editor,
4217            cx,
4218            indoc! {"
4219                a.f(one, two, three)ˇ b
4220                a.f(one, two, three)ˇ b
4221                a.f(one, two, three)ˇ b
4222            "},
4223        );
4224
4225        // As soon as the last tab stop is reached, snippet state is gone
4226        editor.move_to_prev_snippet_tabstop(cx);
4227        assert(
4228            editor,
4229            cx,
4230            indoc! {"
4231                a.f(one, two, three)ˇ b
4232                a.f(one, two, three)ˇ b
4233                a.f(one, two, three)ˇ b
4234            "},
4235        );
4236    });
4237}
4238
4239#[gpui::test]
4240async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
4241    init_test(cx, |_| {});
4242
4243    let mut language = Language::new(
4244        LanguageConfig {
4245            name: "Rust".into(),
4246            path_suffixes: vec!["rs".to_string()],
4247            ..Default::default()
4248        },
4249        Some(tree_sitter_rust::language()),
4250    );
4251    let mut fake_servers = language
4252        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4253            capabilities: lsp::ServerCapabilities {
4254                document_formatting_provider: Some(lsp::OneOf::Left(true)),
4255                ..Default::default()
4256            },
4257            ..Default::default()
4258        }))
4259        .await;
4260
4261    let fs = FakeFs::new(cx.background());
4262    fs.insert_file("/file.rs", Default::default()).await;
4263
4264    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4265    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4266    let buffer = project
4267        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4268        .await
4269        .unwrap();
4270
4271    cx.foreground().start_waiting();
4272    let fake_server = fake_servers.next().await.unwrap();
4273
4274    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4275    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4276    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4277    assert!(cx.read(|cx| editor.is_dirty(cx)));
4278
4279    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4280    fake_server
4281        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4282            assert_eq!(
4283                params.text_document.uri,
4284                lsp::Url::from_file_path("/file.rs").unwrap()
4285            );
4286            assert_eq!(params.options.tab_size, 4);
4287            Ok(Some(vec![lsp::TextEdit::new(
4288                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4289                ", ".to_string(),
4290            )]))
4291        })
4292        .next()
4293        .await;
4294    cx.foreground().start_waiting();
4295    save.await.unwrap();
4296    assert_eq!(
4297        editor.read_with(cx, |editor, cx| editor.text(cx)),
4298        "one, two\nthree\n"
4299    );
4300    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4301
4302    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4303    assert!(cx.read(|cx| editor.is_dirty(cx)));
4304
4305    // Ensure we can still save even if formatting hangs.
4306    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4307        assert_eq!(
4308            params.text_document.uri,
4309            lsp::Url::from_file_path("/file.rs").unwrap()
4310        );
4311        futures::future::pending::<()>().await;
4312        unreachable!()
4313    });
4314    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4315    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4316    cx.foreground().start_waiting();
4317    save.await.unwrap();
4318    assert_eq!(
4319        editor.read_with(cx, |editor, cx| editor.text(cx)),
4320        "one\ntwo\nthree\n"
4321    );
4322    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4323
4324    // Set rust language override and assert overriden tabsize is sent to language server
4325    update_test_settings(cx, |settings| {
4326        settings.languages.insert(
4327            "Rust".into(),
4328            LanguageSettingsContent {
4329                tab_size: NonZeroU32::new(8),
4330                ..Default::default()
4331            },
4332        );
4333    });
4334
4335    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4336    fake_server
4337        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4338            assert_eq!(
4339                params.text_document.uri,
4340                lsp::Url::from_file_path("/file.rs").unwrap()
4341            );
4342            assert_eq!(params.options.tab_size, 8);
4343            Ok(Some(vec![]))
4344        })
4345        .next()
4346        .await;
4347    cx.foreground().start_waiting();
4348    save.await.unwrap();
4349}
4350
4351#[gpui::test]
4352async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
4353    init_test(cx, |_| {});
4354
4355    let mut language = Language::new(
4356        LanguageConfig {
4357            name: "Rust".into(),
4358            path_suffixes: vec!["rs".to_string()],
4359            ..Default::default()
4360        },
4361        Some(tree_sitter_rust::language()),
4362    );
4363    let mut fake_servers = language
4364        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4365            capabilities: lsp::ServerCapabilities {
4366                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
4367                ..Default::default()
4368            },
4369            ..Default::default()
4370        }))
4371        .await;
4372
4373    let fs = FakeFs::new(cx.background());
4374    fs.insert_file("/file.rs", Default::default()).await;
4375
4376    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4377    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4378    let buffer = project
4379        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4380        .await
4381        .unwrap();
4382
4383    cx.foreground().start_waiting();
4384    let fake_server = fake_servers.next().await.unwrap();
4385
4386    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4387    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4388    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4389    assert!(cx.read(|cx| editor.is_dirty(cx)));
4390
4391    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4392    fake_server
4393        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
4394            assert_eq!(
4395                params.text_document.uri,
4396                lsp::Url::from_file_path("/file.rs").unwrap()
4397            );
4398            assert_eq!(params.options.tab_size, 4);
4399            Ok(Some(vec![lsp::TextEdit::new(
4400                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4401                ", ".to_string(),
4402            )]))
4403        })
4404        .next()
4405        .await;
4406    cx.foreground().start_waiting();
4407    save.await.unwrap();
4408    assert_eq!(
4409        editor.read_with(cx, |editor, cx| editor.text(cx)),
4410        "one, two\nthree\n"
4411    );
4412    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4413
4414    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4415    assert!(cx.read(|cx| editor.is_dirty(cx)));
4416
4417    // Ensure we can still save even if formatting hangs.
4418    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
4419        move |params, _| async move {
4420            assert_eq!(
4421                params.text_document.uri,
4422                lsp::Url::from_file_path("/file.rs").unwrap()
4423            );
4424            futures::future::pending::<()>().await;
4425            unreachable!()
4426        },
4427    );
4428    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4429    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4430    cx.foreground().start_waiting();
4431    save.await.unwrap();
4432    assert_eq!(
4433        editor.read_with(cx, |editor, cx| editor.text(cx)),
4434        "one\ntwo\nthree\n"
4435    );
4436    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4437
4438    // Set rust language override and assert overriden tabsize is sent to language server
4439    update_test_settings(cx, |settings| {
4440        settings.languages.insert(
4441            "Rust".into(),
4442            LanguageSettingsContent {
4443                tab_size: NonZeroU32::new(8),
4444                ..Default::default()
4445            },
4446        );
4447    });
4448
4449    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4450    fake_server
4451        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
4452            assert_eq!(
4453                params.text_document.uri,
4454                lsp::Url::from_file_path("/file.rs").unwrap()
4455            );
4456            assert_eq!(params.options.tab_size, 8);
4457            Ok(Some(vec![]))
4458        })
4459        .next()
4460        .await;
4461    cx.foreground().start_waiting();
4462    save.await.unwrap();
4463}
4464
4465#[gpui::test]
4466async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
4467    init_test(cx, |_| {});
4468
4469    let mut language = Language::new(
4470        LanguageConfig {
4471            name: "Rust".into(),
4472            path_suffixes: vec!["rs".to_string()],
4473            ..Default::default()
4474        },
4475        Some(tree_sitter_rust::language()),
4476    );
4477    let mut fake_servers = language
4478        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4479            capabilities: lsp::ServerCapabilities {
4480                document_formatting_provider: Some(lsp::OneOf::Left(true)),
4481                ..Default::default()
4482            },
4483            ..Default::default()
4484        }))
4485        .await;
4486
4487    let fs = FakeFs::new(cx.background());
4488    fs.insert_file("/file.rs", Default::default()).await;
4489
4490    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4491    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4492    let buffer = project
4493        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4494        .await
4495        .unwrap();
4496
4497    cx.foreground().start_waiting();
4498    let fake_server = fake_servers.next().await.unwrap();
4499
4500    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4501    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4502    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4503
4504    let format = editor.update(cx, |editor, cx| {
4505        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
4506    });
4507    fake_server
4508        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4509            assert_eq!(
4510                params.text_document.uri,
4511                lsp::Url::from_file_path("/file.rs").unwrap()
4512            );
4513            assert_eq!(params.options.tab_size, 4);
4514            Ok(Some(vec![lsp::TextEdit::new(
4515                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4516                ", ".to_string(),
4517            )]))
4518        })
4519        .next()
4520        .await;
4521    cx.foreground().start_waiting();
4522    format.await.unwrap();
4523    assert_eq!(
4524        editor.read_with(cx, |editor, cx| editor.text(cx)),
4525        "one, two\nthree\n"
4526    );
4527
4528    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4529    // Ensure we don't lock if formatting hangs.
4530    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4531        assert_eq!(
4532            params.text_document.uri,
4533            lsp::Url::from_file_path("/file.rs").unwrap()
4534        );
4535        futures::future::pending::<()>().await;
4536        unreachable!()
4537    });
4538    let format = editor.update(cx, |editor, cx| {
4539        editor.perform_format(project, FormatTrigger::Manual, cx)
4540    });
4541    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4542    cx.foreground().start_waiting();
4543    format.await.unwrap();
4544    assert_eq!(
4545        editor.read_with(cx, |editor, cx| editor.text(cx)),
4546        "one\ntwo\nthree\n"
4547    );
4548}
4549
4550#[gpui::test]
4551async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
4552    init_test(cx, |_| {});
4553
4554    let mut cx = EditorLspTestContext::new_rust(
4555        lsp::ServerCapabilities {
4556            document_formatting_provider: Some(lsp::OneOf::Left(true)),
4557            ..Default::default()
4558        },
4559        cx,
4560    )
4561    .await;
4562
4563    cx.set_state(indoc! {"
4564        one.twoˇ
4565    "});
4566
4567    // The format request takes a long time. When it completes, it inserts
4568    // a newline and an indent before the `.`
4569    cx.lsp
4570        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
4571            let executor = cx.background();
4572            async move {
4573                executor.timer(Duration::from_millis(100)).await;
4574                Ok(Some(vec![lsp::TextEdit {
4575                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
4576                    new_text: "\n    ".into(),
4577                }]))
4578            }
4579        });
4580
4581    // Submit a format request.
4582    let format_1 = cx
4583        .update_editor(|editor, cx| editor.format(&Format, cx))
4584        .unwrap();
4585    cx.foreground().run_until_parked();
4586
4587    // Submit a second format request.
4588    let format_2 = cx
4589        .update_editor(|editor, cx| editor.format(&Format, cx))
4590        .unwrap();
4591    cx.foreground().run_until_parked();
4592
4593    // Wait for both format requests to complete
4594    cx.foreground().advance_clock(Duration::from_millis(200));
4595    cx.foreground().start_waiting();
4596    format_1.await.unwrap();
4597    cx.foreground().start_waiting();
4598    format_2.await.unwrap();
4599
4600    // The formatting edits only happens once.
4601    cx.assert_editor_state(indoc! {"
4602        one
4603            .twoˇ
4604    "});
4605}
4606
4607#[gpui::test]
4608async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
4609    init_test(cx, |_| {});
4610
4611    let mut cx = EditorLspTestContext::new_rust(
4612        lsp::ServerCapabilities {
4613            document_formatting_provider: Some(lsp::OneOf::Left(true)),
4614            ..Default::default()
4615        },
4616        cx,
4617    )
4618    .await;
4619
4620    // Set up a buffer white some trailing whitespace and no trailing newline.
4621    cx.set_state(
4622        &[
4623            "one ",   //
4624            "twoˇ",   //
4625            "three ", //
4626            "four",   //
4627        ]
4628        .join("\n"),
4629    );
4630
4631    // Submit a format request.
4632    let format = cx
4633        .update_editor(|editor, cx| editor.format(&Format, cx))
4634        .unwrap();
4635
4636    // Record which buffer changes have been sent to the language server
4637    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
4638    cx.lsp
4639        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
4640            let buffer_changes = buffer_changes.clone();
4641            move |params, _| {
4642                buffer_changes.lock().extend(
4643                    params
4644                        .content_changes
4645                        .into_iter()
4646                        .map(|e| (e.range.unwrap(), e.text)),
4647                );
4648            }
4649        });
4650
4651    // Handle formatting requests to the language server.
4652    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
4653        let buffer_changes = buffer_changes.clone();
4654        move |_, _| {
4655            // When formatting is requested, trailing whitespace has already been stripped,
4656            // and the trailing newline has already been added.
4657            assert_eq!(
4658                &buffer_changes.lock()[1..],
4659                &[
4660                    (
4661                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
4662                        "".into()
4663                    ),
4664                    (
4665                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
4666                        "".into()
4667                    ),
4668                    (
4669                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
4670                        "\n".into()
4671                    ),
4672                ]
4673            );
4674
4675            // Insert blank lines between each line of the buffer.
4676            async move {
4677                Ok(Some(vec![
4678                    lsp::TextEdit {
4679                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
4680                        new_text: "\n".into(),
4681                    },
4682                    lsp::TextEdit {
4683                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
4684                        new_text: "\n".into(),
4685                    },
4686                ]))
4687            }
4688        }
4689    });
4690
4691    // After formatting the buffer, the trailing whitespace is stripped,
4692    // a newline is appended, and the edits provided by the language server
4693    // have been applied.
4694    format.await.unwrap();
4695    cx.assert_editor_state(
4696        &[
4697            "one",   //
4698            "",      //
4699            "twoˇ",  //
4700            "",      //
4701            "three", //
4702            "four",  //
4703            "",      //
4704        ]
4705        .join("\n"),
4706    );
4707
4708    // Undoing the formatting undoes the trailing whitespace removal, the
4709    // trailing newline, and the LSP edits.
4710    cx.update_buffer(|buffer, cx| buffer.undo(cx));
4711    cx.assert_editor_state(
4712        &[
4713            "one ",   //
4714            "twoˇ",   //
4715            "three ", //
4716            "four",   //
4717        ]
4718        .join("\n"),
4719    );
4720}
4721
4722#[gpui::test]
4723async fn test_completion(cx: &mut gpui::TestAppContext) {
4724    init_test(cx, |_| {});
4725
4726    let mut cx = EditorLspTestContext::new_rust(
4727        lsp::ServerCapabilities {
4728            completion_provider: Some(lsp::CompletionOptions {
4729                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
4730                ..Default::default()
4731            }),
4732            ..Default::default()
4733        },
4734        cx,
4735    )
4736    .await;
4737
4738    cx.set_state(indoc! {"
4739        oneˇ
4740        two
4741        three
4742    "});
4743    cx.simulate_keystroke(".");
4744    handle_completion_request(
4745        &mut cx,
4746        indoc! {"
4747            one.|<>
4748            two
4749            three
4750        "},
4751        vec!["first_completion", "second_completion"],
4752    )
4753    .await;
4754    cx.condition(|editor, _| editor.context_menu_visible())
4755        .await;
4756    let apply_additional_edits = cx.update_editor(|editor, cx| {
4757        editor.move_down(&MoveDown, cx);
4758        editor
4759            .confirm_completion(&ConfirmCompletion::default(), cx)
4760            .unwrap()
4761    });
4762    cx.assert_editor_state(indoc! {"
4763        one.second_completionˇ
4764        two
4765        three
4766    "});
4767
4768    handle_resolve_completion_request(
4769        &mut cx,
4770        Some(vec![
4771            (
4772                //This overlaps with the primary completion edit which is
4773                //misbehavior from the LSP spec, test that we filter it out
4774                indoc! {"
4775                    one.second_ˇcompletion
4776                    two
4777                    threeˇ
4778                "},
4779                "overlapping aditional edit",
4780            ),
4781            (
4782                indoc! {"
4783                    one.second_completion
4784                    two
4785                    threeˇ
4786                "},
4787                "\nadditional edit",
4788            ),
4789        ]),
4790    )
4791    .await;
4792    apply_additional_edits.await.unwrap();
4793    cx.assert_editor_state(indoc! {"
4794        one.second_completionˇ
4795        two
4796        three
4797        additional edit
4798    "});
4799
4800    cx.set_state(indoc! {"
4801        one.second_completion
4802        twoˇ
4803        threeˇ
4804        additional edit
4805    "});
4806    cx.simulate_keystroke(" ");
4807    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4808    cx.simulate_keystroke("s");
4809    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4810
4811    cx.assert_editor_state(indoc! {"
4812        one.second_completion
4813        two sˇ
4814        three sˇ
4815        additional edit
4816    "});
4817    handle_completion_request(
4818        &mut cx,
4819        indoc! {"
4820            one.second_completion
4821            two s
4822            three <s|>
4823            additional edit
4824        "},
4825        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
4826    )
4827    .await;
4828    cx.condition(|editor, _| editor.context_menu_visible())
4829        .await;
4830
4831    cx.simulate_keystroke("i");
4832
4833    handle_completion_request(
4834        &mut cx,
4835        indoc! {"
4836            one.second_completion
4837            two si
4838            three <si|>
4839            additional edit
4840        "},
4841        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
4842    )
4843    .await;
4844    cx.condition(|editor, _| editor.context_menu_visible())
4845        .await;
4846
4847    let apply_additional_edits = cx.update_editor(|editor, cx| {
4848        editor
4849            .confirm_completion(&ConfirmCompletion::default(), cx)
4850            .unwrap()
4851    });
4852    cx.assert_editor_state(indoc! {"
4853        one.second_completion
4854        two sixth_completionˇ
4855        three sixth_completionˇ
4856        additional edit
4857    "});
4858
4859    handle_resolve_completion_request(&mut cx, None).await;
4860    apply_additional_edits.await.unwrap();
4861
4862    cx.update(|cx| {
4863        cx.update_global::<SettingsStore, _, _>(|settings, cx| {
4864            settings.update_user_settings::<EditorSettings>(cx, |settings| {
4865                settings.show_completions_on_input = Some(false);
4866            });
4867        })
4868    });
4869    cx.set_state("editorˇ");
4870    cx.simulate_keystroke(".");
4871    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4872    cx.simulate_keystroke("c");
4873    cx.simulate_keystroke("l");
4874    cx.simulate_keystroke("o");
4875    cx.assert_editor_state("editor.cloˇ");
4876    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4877    cx.update_editor(|editor, cx| {
4878        editor.show_completions(&ShowCompletions, cx);
4879    });
4880    handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
4881    cx.condition(|editor, _| editor.context_menu_visible())
4882        .await;
4883    let apply_additional_edits = cx.update_editor(|editor, cx| {
4884        editor
4885            .confirm_completion(&ConfirmCompletion::default(), cx)
4886            .unwrap()
4887    });
4888    cx.assert_editor_state("editor.closeˇ");
4889    handle_resolve_completion_request(&mut cx, None).await;
4890    apply_additional_edits.await.unwrap();
4891}
4892
4893#[gpui::test]
4894async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
4895    init_test(cx, |_| {});
4896
4897    let language = Arc::new(Language::new(
4898        LanguageConfig {
4899            line_comment: Some("// ".into()),
4900            ..Default::default()
4901        },
4902        Some(tree_sitter_rust::language()),
4903    ));
4904
4905    let text = "
4906        fn a() {
4907            //b();
4908            // c();
4909            //  d();
4910        }
4911    "
4912    .unindent();
4913
4914    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
4915    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4916    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
4917
4918    view.update(cx, |editor, cx| {
4919        // If multiple selections intersect a line, the line is only
4920        // toggled once.
4921        editor.change_selections(None, cx, |s| {
4922            s.select_display_ranges([
4923                DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
4924                DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
4925            ])
4926        });
4927        editor.toggle_comments(&ToggleComments::default(), cx);
4928        assert_eq!(
4929            editor.text(cx),
4930            "
4931                fn a() {
4932                    b();
4933                    c();
4934                     d();
4935                }
4936            "
4937            .unindent()
4938        );
4939
4940        // The comment prefix is inserted at the same column for every line
4941        // in a selection.
4942        editor.change_selections(None, cx, |s| {
4943            s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)])
4944        });
4945        editor.toggle_comments(&ToggleComments::default(), cx);
4946        assert_eq!(
4947            editor.text(cx),
4948            "
4949                fn a() {
4950                    // b();
4951                    // c();
4952                    //  d();
4953                }
4954            "
4955            .unindent()
4956        );
4957
4958        // If a selection ends at the beginning of a line, that line is not toggled.
4959        editor.change_selections(None, cx, |s| {
4960            s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)])
4961        });
4962        editor.toggle_comments(&ToggleComments::default(), cx);
4963        assert_eq!(
4964            editor.text(cx),
4965            "
4966                fn a() {
4967                    // b();
4968                    c();
4969                    //  d();
4970                }
4971            "
4972            .unindent()
4973        );
4974    });
4975}
4976
4977#[gpui::test]
4978async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
4979    init_test(cx, |_| {});
4980
4981    let language = Arc::new(Language::new(
4982        LanguageConfig {
4983            line_comment: Some("// ".into()),
4984            ..Default::default()
4985        },
4986        Some(tree_sitter_rust::language()),
4987    ));
4988
4989    let registry = Arc::new(LanguageRegistry::test());
4990    registry.add(language.clone());
4991
4992    let mut cx = EditorTestContext::new(cx).await;
4993    cx.update_buffer(|buffer, cx| {
4994        buffer.set_language_registry(registry);
4995        buffer.set_language(Some(language), cx);
4996    });
4997
4998    let toggle_comments = &ToggleComments {
4999        advance_downwards: true,
5000    };
5001
5002    // Single cursor on one line -> advance
5003    // Cursor moves horizontally 3 characters as well on non-blank line
5004    cx.set_state(indoc!(
5005        "fn a() {
5006             ˇdog();
5007             cat();
5008        }"
5009    ));
5010    cx.update_editor(|editor, cx| {
5011        editor.toggle_comments(toggle_comments, cx);
5012    });
5013    cx.assert_editor_state(indoc!(
5014        "fn a() {
5015             // dog();
5016             catˇ();
5017        }"
5018    ));
5019
5020    // Single selection on one line -> don't advance
5021    cx.set_state(indoc!(
5022        "fn a() {
5023             «dog()ˇ»;
5024             cat();
5025        }"
5026    ));
5027    cx.update_editor(|editor, cx| {
5028        editor.toggle_comments(toggle_comments, cx);
5029    });
5030    cx.assert_editor_state(indoc!(
5031        "fn a() {
5032             // «dog()ˇ»;
5033             cat();
5034        }"
5035    ));
5036
5037    // Multiple cursors on one line -> advance
5038    cx.set_state(indoc!(
5039        "fn a() {
5040             ˇdˇog();
5041             cat();
5042        }"
5043    ));
5044    cx.update_editor(|editor, cx| {
5045        editor.toggle_comments(toggle_comments, cx);
5046    });
5047    cx.assert_editor_state(indoc!(
5048        "fn a() {
5049             // dog();
5050             catˇ(ˇ);
5051        }"
5052    ));
5053
5054    // Multiple cursors on one line, with selection -> don't advance
5055    cx.set_state(indoc!(
5056        "fn a() {
5057             ˇdˇog«()ˇ»;
5058             cat();
5059        }"
5060    ));
5061    cx.update_editor(|editor, cx| {
5062        editor.toggle_comments(toggle_comments, cx);
5063    });
5064    cx.assert_editor_state(indoc!(
5065        "fn a() {
5066             // ˇdˇog«()ˇ»;
5067             cat();
5068        }"
5069    ));
5070
5071    // Single cursor on one line -> advance
5072    // Cursor moves to column 0 on blank line
5073    cx.set_state(indoc!(
5074        "fn a() {
5075             ˇdog();
5076
5077             cat();
5078        }"
5079    ));
5080    cx.update_editor(|editor, cx| {
5081        editor.toggle_comments(toggle_comments, cx);
5082    });
5083    cx.assert_editor_state(indoc!(
5084        "fn a() {
5085             // dog();
5086        ˇ
5087             cat();
5088        }"
5089    ));
5090
5091    // Single cursor on one line -> advance
5092    // Cursor starts and ends at column 0
5093    cx.set_state(indoc!(
5094        "fn a() {
5095         ˇ    dog();
5096             cat();
5097        }"
5098    ));
5099    cx.update_editor(|editor, cx| {
5100        editor.toggle_comments(toggle_comments, cx);
5101    });
5102    cx.assert_editor_state(indoc!(
5103        "fn a() {
5104             // dog();
5105         ˇ    cat();
5106        }"
5107    ));
5108}
5109
5110#[gpui::test]
5111async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
5112    init_test(cx, |_| {});
5113
5114    let mut cx = EditorTestContext::new(cx).await;
5115
5116    let html_language = Arc::new(
5117        Language::new(
5118            LanguageConfig {
5119                name: "HTML".into(),
5120                block_comment: Some(("<!-- ".into(), " -->".into())),
5121                ..Default::default()
5122            },
5123            Some(tree_sitter_html::language()),
5124        )
5125        .with_injection_query(
5126            r#"
5127            (script_element
5128                (raw_text) @content
5129                (#set! "language" "javascript"))
5130            "#,
5131        )
5132        .unwrap(),
5133    );
5134
5135    let javascript_language = Arc::new(Language::new(
5136        LanguageConfig {
5137            name: "JavaScript".into(),
5138            line_comment: Some("// ".into()),
5139            ..Default::default()
5140        },
5141        Some(tree_sitter_javascript::language()),
5142    ));
5143
5144    let registry = Arc::new(LanguageRegistry::test());
5145    registry.add(html_language.clone());
5146    registry.add(javascript_language.clone());
5147
5148    cx.update_buffer(|buffer, cx| {
5149        buffer.set_language_registry(registry);
5150        buffer.set_language(Some(html_language), cx);
5151    });
5152
5153    // Toggle comments for empty selections
5154    cx.set_state(
5155        &r#"
5156            <p>A</p>ˇ
5157            <p>B</p>ˇ
5158            <p>C</p>ˇ
5159        "#
5160        .unindent(),
5161    );
5162    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5163    cx.assert_editor_state(
5164        &r#"
5165            <!-- <p>A</p>ˇ -->
5166            <!-- <p>B</p>ˇ -->
5167            <!-- <p>C</p>ˇ -->
5168        "#
5169        .unindent(),
5170    );
5171    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5172    cx.assert_editor_state(
5173        &r#"
5174            <p>A</p>ˇ
5175            <p>B</p>ˇ
5176            <p>C</p>ˇ
5177        "#
5178        .unindent(),
5179    );
5180
5181    // Toggle comments for mixture of empty and non-empty selections, where
5182    // multiple selections occupy a given line.
5183    cx.set_state(
5184        &r#"
5185            <p>A«</p>
5186            <p>ˇ»B</p>ˇ
5187            <p>C«</p>
5188            <p>ˇ»D</p>ˇ
5189        "#
5190        .unindent(),
5191    );
5192
5193    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5194    cx.assert_editor_state(
5195        &r#"
5196            <!-- <p>A«</p>
5197            <p>ˇ»B</p>ˇ -->
5198            <!-- <p>C«</p>
5199            <p>ˇ»D</p>ˇ -->
5200        "#
5201        .unindent(),
5202    );
5203    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5204    cx.assert_editor_state(
5205        &r#"
5206            <p>A«</p>
5207            <p>ˇ»B</p>ˇ
5208            <p>C«</p>
5209            <p>ˇ»D</p>ˇ
5210        "#
5211        .unindent(),
5212    );
5213
5214    // Toggle comments when different languages are active for different
5215    // selections.
5216    cx.set_state(
5217        &r#"
5218            ˇ<script>
5219                ˇvar x = new Y();
5220            ˇ</script>
5221        "#
5222        .unindent(),
5223    );
5224    cx.foreground().run_until_parked();
5225    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5226    cx.assert_editor_state(
5227        &r#"
5228            <!-- ˇ<script> -->
5229                // ˇvar x = new Y();
5230            <!-- ˇ</script> -->
5231        "#
5232        .unindent(),
5233    );
5234}
5235
5236#[gpui::test]
5237fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
5238    init_test(cx, |_| {});
5239
5240    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5241    let multibuffer = cx.add_model(|cx| {
5242        let mut multibuffer = MultiBuffer::new(0);
5243        multibuffer.push_excerpts(
5244            buffer.clone(),
5245            [
5246                ExcerptRange {
5247                    context: Point::new(0, 0)..Point::new(0, 4),
5248                    primary: None,
5249                },
5250                ExcerptRange {
5251                    context: Point::new(1, 0)..Point::new(1, 4),
5252                    primary: None,
5253                },
5254            ],
5255            cx,
5256        );
5257        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
5258        multibuffer
5259    });
5260
5261    let (_, view) = cx.add_window(|cx| build_editor(multibuffer, cx));
5262    view.update(cx, |view, cx| {
5263        assert_eq!(view.text(cx), "aaaa\nbbbb");
5264        view.change_selections(None, cx, |s| {
5265            s.select_ranges([
5266                Point::new(0, 0)..Point::new(0, 0),
5267                Point::new(1, 0)..Point::new(1, 0),
5268            ])
5269        });
5270
5271        view.handle_input("X", cx);
5272        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
5273        assert_eq!(
5274            view.selections.ranges(cx),
5275            [
5276                Point::new(0, 1)..Point::new(0, 1),
5277                Point::new(1, 1)..Point::new(1, 1),
5278            ]
5279        )
5280    });
5281}
5282
5283#[gpui::test]
5284fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
5285    init_test(cx, |_| {});
5286
5287    let markers = vec![('[', ']').into(), ('(', ')').into()];
5288    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
5289        indoc! {"
5290            [aaaa
5291            (bbbb]
5292            cccc)",
5293        },
5294        markers.clone(),
5295    );
5296    let excerpt_ranges = markers.into_iter().map(|marker| {
5297        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
5298        ExcerptRange {
5299            context,
5300            primary: None,
5301        }
5302    });
5303    let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
5304    let multibuffer = cx.add_model(|cx| {
5305        let mut multibuffer = MultiBuffer::new(0);
5306        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
5307        multibuffer
5308    });
5309
5310    let (_, view) = cx.add_window(|cx| build_editor(multibuffer, cx));
5311    view.update(cx, |view, cx| {
5312        let (expected_text, selection_ranges) = marked_text_ranges(
5313            indoc! {"
5314                aaaa
5315                bˇbbb
5316                bˇbbˇb
5317                cccc"
5318            },
5319            true,
5320        );
5321        assert_eq!(view.text(cx), expected_text);
5322        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
5323
5324        view.handle_input("X", cx);
5325
5326        let (expected_text, expected_selections) = marked_text_ranges(
5327            indoc! {"
5328                aaaa
5329                bXˇbbXb
5330                bXˇbbXˇb
5331                cccc"
5332            },
5333            false,
5334        );
5335        assert_eq!(view.text(cx), expected_text);
5336        assert_eq!(view.selections.ranges(cx), expected_selections);
5337
5338        view.newline(&Newline, cx);
5339        let (expected_text, expected_selections) = marked_text_ranges(
5340            indoc! {"
5341                aaaa
5342                bX
5343                ˇbbX
5344                b
5345                bX
5346                ˇbbX
5347                ˇb
5348                cccc"
5349            },
5350            false,
5351        );
5352        assert_eq!(view.text(cx), expected_text);
5353        assert_eq!(view.selections.ranges(cx), expected_selections);
5354    });
5355}
5356
5357#[gpui::test]
5358fn test_refresh_selections(cx: &mut TestAppContext) {
5359    init_test(cx, |_| {});
5360
5361    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5362    let mut excerpt1_id = None;
5363    let multibuffer = cx.add_model(|cx| {
5364        let mut multibuffer = MultiBuffer::new(0);
5365        excerpt1_id = multibuffer
5366            .push_excerpts(
5367                buffer.clone(),
5368                [
5369                    ExcerptRange {
5370                        context: Point::new(0, 0)..Point::new(1, 4),
5371                        primary: None,
5372                    },
5373                    ExcerptRange {
5374                        context: Point::new(1, 0)..Point::new(2, 4),
5375                        primary: None,
5376                    },
5377                ],
5378                cx,
5379            )
5380            .into_iter()
5381            .next();
5382        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
5383        multibuffer
5384    });
5385
5386    let (_, editor) = cx.add_window(|cx| {
5387        let mut editor = build_editor(multibuffer.clone(), cx);
5388        let snapshot = editor.snapshot(cx);
5389        editor.change_selections(None, cx, |s| {
5390            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
5391        });
5392        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
5393        assert_eq!(
5394            editor.selections.ranges(cx),
5395            [
5396                Point::new(1, 3)..Point::new(1, 3),
5397                Point::new(2, 1)..Point::new(2, 1),
5398            ]
5399        );
5400        editor
5401    });
5402
5403    // Refreshing selections is a no-op when excerpts haven't changed.
5404    editor.update(cx, |editor, cx| {
5405        editor.change_selections(None, cx, |s| s.refresh());
5406        assert_eq!(
5407            editor.selections.ranges(cx),
5408            [
5409                Point::new(1, 3)..Point::new(1, 3),
5410                Point::new(2, 1)..Point::new(2, 1),
5411            ]
5412        );
5413    });
5414
5415    multibuffer.update(cx, |multibuffer, cx| {
5416        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
5417    });
5418    editor.update(cx, |editor, cx| {
5419        // Removing an excerpt causes the first selection to become degenerate.
5420        assert_eq!(
5421            editor.selections.ranges(cx),
5422            [
5423                Point::new(0, 0)..Point::new(0, 0),
5424                Point::new(0, 1)..Point::new(0, 1)
5425            ]
5426        );
5427
5428        // Refreshing selections will relocate the first selection to the original buffer
5429        // location.
5430        editor.change_selections(None, cx, |s| s.refresh());
5431        assert_eq!(
5432            editor.selections.ranges(cx),
5433            [
5434                Point::new(0, 1)..Point::new(0, 1),
5435                Point::new(0, 3)..Point::new(0, 3)
5436            ]
5437        );
5438        assert!(editor.selections.pending_anchor().is_some());
5439    });
5440}
5441
5442#[gpui::test]
5443fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
5444    init_test(cx, |_| {});
5445
5446    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5447    let mut excerpt1_id = None;
5448    let multibuffer = cx.add_model(|cx| {
5449        let mut multibuffer = MultiBuffer::new(0);
5450        excerpt1_id = multibuffer
5451            .push_excerpts(
5452                buffer.clone(),
5453                [
5454                    ExcerptRange {
5455                        context: Point::new(0, 0)..Point::new(1, 4),
5456                        primary: None,
5457                    },
5458                    ExcerptRange {
5459                        context: Point::new(1, 0)..Point::new(2, 4),
5460                        primary: None,
5461                    },
5462                ],
5463                cx,
5464            )
5465            .into_iter()
5466            .next();
5467        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
5468        multibuffer
5469    });
5470
5471    let (_, editor) = cx.add_window(|cx| {
5472        let mut editor = build_editor(multibuffer.clone(), cx);
5473        let snapshot = editor.snapshot(cx);
5474        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
5475        assert_eq!(
5476            editor.selections.ranges(cx),
5477            [Point::new(1, 3)..Point::new(1, 3)]
5478        );
5479        editor
5480    });
5481
5482    multibuffer.update(cx, |multibuffer, cx| {
5483        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
5484    });
5485    editor.update(cx, |editor, cx| {
5486        assert_eq!(
5487            editor.selections.ranges(cx),
5488            [Point::new(0, 0)..Point::new(0, 0)]
5489        );
5490
5491        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
5492        editor.change_selections(None, cx, |s| s.refresh());
5493        assert_eq!(
5494            editor.selections.ranges(cx),
5495            [Point::new(0, 3)..Point::new(0, 3)]
5496        );
5497        assert!(editor.selections.pending_anchor().is_some());
5498    });
5499}
5500
5501#[gpui::test]
5502async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
5503    init_test(cx, |_| {});
5504
5505    let language = Arc::new(
5506        Language::new(
5507            LanguageConfig {
5508                brackets: BracketPairConfig {
5509                    pairs: vec![
5510                        BracketPair {
5511                            start: "{".to_string(),
5512                            end: "}".to_string(),
5513                            close: true,
5514                            newline: true,
5515                        },
5516                        BracketPair {
5517                            start: "/* ".to_string(),
5518                            end: " */".to_string(),
5519                            close: true,
5520                            newline: true,
5521                        },
5522                    ],
5523                    ..Default::default()
5524                },
5525                ..Default::default()
5526            },
5527            Some(tree_sitter_rust::language()),
5528        )
5529        .with_indents_query("")
5530        .unwrap(),
5531    );
5532
5533    let text = concat!(
5534        "{   }\n",     //
5535        "  x\n",       //
5536        "  /*   */\n", //
5537        "x\n",         //
5538        "{{} }\n",     //
5539    );
5540
5541    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
5542    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
5543    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
5544    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
5545        .await;
5546
5547    view.update(cx, |view, cx| {
5548        view.change_selections(None, cx, |s| {
5549            s.select_display_ranges([
5550                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
5551                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
5552                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
5553            ])
5554        });
5555        view.newline(&Newline, cx);
5556
5557        assert_eq!(
5558            view.buffer().read(cx).read(cx).text(),
5559            concat!(
5560                "{ \n",    // Suppress rustfmt
5561                "\n",      //
5562                "}\n",     //
5563                "  x\n",   //
5564                "  /* \n", //
5565                "  \n",    //
5566                "  */\n",  //
5567                "x\n",     //
5568                "{{} \n",  //
5569                "}\n",     //
5570            )
5571        );
5572    });
5573}
5574
5575#[gpui::test]
5576fn test_highlighted_ranges(cx: &mut TestAppContext) {
5577    init_test(cx, |_| {});
5578
5579    let (_, editor) = cx.add_window(|cx| {
5580        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
5581        build_editor(buffer.clone(), cx)
5582    });
5583
5584    editor.update(cx, |editor, cx| {
5585        struct Type1;
5586        struct Type2;
5587
5588        let buffer = editor.buffer.read(cx).snapshot(cx);
5589
5590        let anchor_range =
5591            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
5592
5593        editor.highlight_background::<Type1>(
5594            vec![
5595                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
5596                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
5597                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
5598                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
5599            ],
5600            |_| Color::red(),
5601            cx,
5602        );
5603        editor.highlight_background::<Type2>(
5604            vec![
5605                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
5606                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
5607                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
5608                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
5609            ],
5610            |_| Color::green(),
5611            cx,
5612        );
5613
5614        let snapshot = editor.snapshot(cx);
5615        let mut highlighted_ranges = editor.background_highlights_in_range(
5616            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
5617            &snapshot,
5618            theme::current(cx).as_ref(),
5619        );
5620        // Enforce a consistent ordering based on color without relying on the ordering of the
5621        // highlight's `TypeId` which is non-deterministic.
5622        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
5623        assert_eq!(
5624            highlighted_ranges,
5625            &[
5626                (
5627                    DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
5628                    Color::green(),
5629                ),
5630                (
5631                    DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
5632                    Color::green(),
5633                ),
5634                (
5635                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
5636                    Color::red(),
5637                ),
5638                (
5639                    DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
5640                    Color::red(),
5641                ),
5642            ]
5643        );
5644        assert_eq!(
5645            editor.background_highlights_in_range(
5646                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
5647                &snapshot,
5648                theme::current(cx).as_ref(),
5649            ),
5650            &[(
5651                DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
5652                Color::red(),
5653            )]
5654        );
5655    });
5656}
5657
5658#[gpui::test]
5659async fn test_following(cx: &mut gpui::TestAppContext) {
5660    init_test(cx, |_| {});
5661
5662    let fs = FakeFs::new(cx.background());
5663    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5664
5665    let buffer = project.update(cx, |project, cx| {
5666        let buffer = project
5667            .create_buffer(&sample_text(16, 8, 'a'), None, cx)
5668            .unwrap();
5669        cx.add_model(|cx| MultiBuffer::singleton(buffer, cx))
5670    });
5671    let (_, leader) = cx.add_window(|cx| build_editor(buffer.clone(), cx));
5672    let (_, follower) = cx.update(|cx| {
5673        cx.add_window(
5674            WindowOptions {
5675                bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
5676                ..Default::default()
5677            },
5678            |cx| build_editor(buffer.clone(), cx),
5679        )
5680    });
5681
5682    let is_still_following = Rc::new(RefCell::new(true));
5683    let follower_edit_event_count = Rc::new(RefCell::new(0));
5684    let pending_update = Rc::new(RefCell::new(None));
5685    follower.update(cx, {
5686        let update = pending_update.clone();
5687        let is_still_following = is_still_following.clone();
5688        let follower_edit_event_count = follower_edit_event_count.clone();
5689        |_, cx| {
5690            cx.subscribe(&leader, move |_, leader, event, cx| {
5691                leader
5692                    .read(cx)
5693                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
5694            })
5695            .detach();
5696
5697            cx.subscribe(&follower, move |_, _, event, cx| {
5698                if Editor::should_unfollow_on_event(event, cx) {
5699                    *is_still_following.borrow_mut() = false;
5700                }
5701                if let Event::BufferEdited = event {
5702                    *follower_edit_event_count.borrow_mut() += 1;
5703                }
5704            })
5705            .detach();
5706        }
5707    });
5708
5709    // Update the selections only
5710    leader.update(cx, |leader, cx| {
5711        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
5712    });
5713    follower
5714        .update(cx, |follower, cx| {
5715            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5716        })
5717        .await
5718        .unwrap();
5719    follower.read_with(cx, |follower, cx| {
5720        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
5721    });
5722    assert_eq!(*is_still_following.borrow(), true);
5723    assert_eq!(*follower_edit_event_count.borrow(), 0);
5724
5725    // Update the scroll position only
5726    leader.update(cx, |leader, cx| {
5727        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
5728    });
5729    follower
5730        .update(cx, |follower, cx| {
5731            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5732        })
5733        .await
5734        .unwrap();
5735    assert_eq!(
5736        follower.update(cx, |follower, cx| follower.scroll_position(cx)),
5737        vec2f(1.5, 3.5)
5738    );
5739    assert_eq!(*is_still_following.borrow(), true);
5740    assert_eq!(*follower_edit_event_count.borrow(), 0);
5741
5742    // Update the selections and scroll position. The follower's scroll position is updated
5743    // via autoscroll, not via the leader's exact scroll position.
5744    leader.update(cx, |leader, cx| {
5745        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
5746        leader.request_autoscroll(Autoscroll::newest(), cx);
5747        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
5748    });
5749    follower
5750        .update(cx, |follower, cx| {
5751            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5752        })
5753        .await
5754        .unwrap();
5755    follower.update(cx, |follower, cx| {
5756        assert_eq!(follower.scroll_position(cx), vec2f(1.5, 0.0));
5757        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
5758    });
5759    assert_eq!(*is_still_following.borrow(), true);
5760
5761    // Creating a pending selection that precedes another selection
5762    leader.update(cx, |leader, cx| {
5763        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
5764        leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
5765    });
5766    follower
5767        .update(cx, |follower, cx| {
5768            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5769        })
5770        .await
5771        .unwrap();
5772    follower.read_with(cx, |follower, cx| {
5773        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
5774    });
5775    assert_eq!(*is_still_following.borrow(), true);
5776
5777    // Extend the pending selection so that it surrounds another selection
5778    leader.update(cx, |leader, cx| {
5779        leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
5780    });
5781    follower
5782        .update(cx, |follower, cx| {
5783            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5784        })
5785        .await
5786        .unwrap();
5787    follower.read_with(cx, |follower, cx| {
5788        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
5789    });
5790
5791    // Scrolling locally breaks the follow
5792    follower.update(cx, |follower, cx| {
5793        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
5794        follower.set_scroll_anchor(
5795            ScrollAnchor {
5796                top_anchor,
5797                offset: vec2f(0.0, 0.5),
5798            },
5799            cx,
5800        );
5801    });
5802    assert_eq!(*is_still_following.borrow(), false);
5803}
5804
5805#[gpui::test]
5806async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
5807    init_test(cx, |_| {});
5808
5809    let fs = FakeFs::new(cx.background());
5810    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5811    let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
5812    let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
5813
5814    let leader = pane.update(cx, |_, cx| {
5815        let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
5816        cx.add_view(|cx| build_editor(multibuffer.clone(), cx))
5817    });
5818
5819    // Start following the editor when it has no excerpts.
5820    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
5821    let follower_1 = cx
5822        .update(|cx| {
5823            Editor::from_state_proto(
5824                pane.clone(),
5825                project.clone(),
5826                ViewId {
5827                    creator: Default::default(),
5828                    id: 0,
5829                },
5830                &mut state_message,
5831                cx,
5832            )
5833        })
5834        .unwrap()
5835        .await
5836        .unwrap();
5837
5838    let update_message = Rc::new(RefCell::new(None));
5839    follower_1.update(cx, {
5840        let update = update_message.clone();
5841        |_, cx| {
5842            cx.subscribe(&leader, move |_, leader, event, cx| {
5843                leader
5844                    .read(cx)
5845                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
5846            })
5847            .detach();
5848        }
5849    });
5850
5851    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
5852        (
5853            project
5854                .create_buffer("abc\ndef\nghi\njkl\n", None, cx)
5855                .unwrap(),
5856            project
5857                .create_buffer("mno\npqr\nstu\nvwx\n", None, cx)
5858                .unwrap(),
5859        )
5860    });
5861
5862    // Insert some excerpts.
5863    leader.update(cx, |leader, cx| {
5864        leader.buffer.update(cx, |multibuffer, cx| {
5865            let excerpt_ids = multibuffer.push_excerpts(
5866                buffer_1.clone(),
5867                [
5868                    ExcerptRange {
5869                        context: 1..6,
5870                        primary: None,
5871                    },
5872                    ExcerptRange {
5873                        context: 12..15,
5874                        primary: None,
5875                    },
5876                    ExcerptRange {
5877                        context: 0..3,
5878                        primary: None,
5879                    },
5880                ],
5881                cx,
5882            );
5883            multibuffer.insert_excerpts_after(
5884                excerpt_ids[0],
5885                buffer_2.clone(),
5886                [
5887                    ExcerptRange {
5888                        context: 8..12,
5889                        primary: None,
5890                    },
5891                    ExcerptRange {
5892                        context: 0..6,
5893                        primary: None,
5894                    },
5895                ],
5896                cx,
5897            );
5898        });
5899    });
5900
5901    // Apply the update of adding the excerpts.
5902    follower_1
5903        .update(cx, |follower, cx| {
5904            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
5905        })
5906        .await
5907        .unwrap();
5908    assert_eq!(
5909        follower_1.read_with(cx, |editor, cx| editor.text(cx)),
5910        leader.read_with(cx, |editor, cx| editor.text(cx))
5911    );
5912    update_message.borrow_mut().take();
5913
5914    // Start following separately after it already has excerpts.
5915    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
5916    let follower_2 = cx
5917        .update(|cx| {
5918            Editor::from_state_proto(
5919                pane.clone(),
5920                project.clone(),
5921                ViewId {
5922                    creator: Default::default(),
5923                    id: 0,
5924                },
5925                &mut state_message,
5926                cx,
5927            )
5928        })
5929        .unwrap()
5930        .await
5931        .unwrap();
5932    assert_eq!(
5933        follower_2.read_with(cx, |editor, cx| editor.text(cx)),
5934        leader.read_with(cx, |editor, cx| editor.text(cx))
5935    );
5936
5937    // Remove some excerpts.
5938    leader.update(cx, |leader, cx| {
5939        leader.buffer.update(cx, |multibuffer, cx| {
5940            let excerpt_ids = multibuffer.excerpt_ids();
5941            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
5942            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
5943        });
5944    });
5945
5946    // Apply the update of removing the excerpts.
5947    follower_1
5948        .update(cx, |follower, cx| {
5949            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
5950        })
5951        .await
5952        .unwrap();
5953    follower_2
5954        .update(cx, |follower, cx| {
5955            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
5956        })
5957        .await
5958        .unwrap();
5959    update_message.borrow_mut().take();
5960    assert_eq!(
5961        follower_1.read_with(cx, |editor, cx| editor.text(cx)),
5962        leader.read_with(cx, |editor, cx| editor.text(cx))
5963    );
5964}
5965
5966#[test]
5967fn test_combine_syntax_and_fuzzy_match_highlights() {
5968    let string = "abcdefghijklmnop";
5969    let syntax_ranges = [
5970        (
5971            0..3,
5972            HighlightStyle {
5973                color: Some(Color::red()),
5974                ..Default::default()
5975            },
5976        ),
5977        (
5978            4..8,
5979            HighlightStyle {
5980                color: Some(Color::green()),
5981                ..Default::default()
5982            },
5983        ),
5984    ];
5985    let match_indices = [4, 6, 7, 8];
5986    assert_eq!(
5987        combine_syntax_and_fuzzy_match_highlights(
5988            string,
5989            Default::default(),
5990            syntax_ranges.into_iter(),
5991            &match_indices,
5992        ),
5993        &[
5994            (
5995                0..3,
5996                HighlightStyle {
5997                    color: Some(Color::red()),
5998                    ..Default::default()
5999                },
6000            ),
6001            (
6002                4..5,
6003                HighlightStyle {
6004                    color: Some(Color::green()),
6005                    weight: Some(fonts::Weight::BOLD),
6006                    ..Default::default()
6007                },
6008            ),
6009            (
6010                5..6,
6011                HighlightStyle {
6012                    color: Some(Color::green()),
6013                    ..Default::default()
6014                },
6015            ),
6016            (
6017                6..8,
6018                HighlightStyle {
6019                    color: Some(Color::green()),
6020                    weight: Some(fonts::Weight::BOLD),
6021                    ..Default::default()
6022                },
6023            ),
6024            (
6025                8..9,
6026                HighlightStyle {
6027                    weight: Some(fonts::Weight::BOLD),
6028                    ..Default::default()
6029                },
6030            ),
6031        ]
6032    );
6033}
6034
6035#[gpui::test]
6036async fn go_to_hunk(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
6037    init_test(cx, |_| {});
6038
6039    let mut cx = EditorTestContext::new(cx).await;
6040
6041    let diff_base = r#"
6042        use some::mod;
6043
6044        const A: u32 = 42;
6045
6046        fn main() {
6047            println!("hello");
6048
6049            println!("world");
6050        }
6051        "#
6052    .unindent();
6053
6054    // Edits are modified, removed, modified, added
6055    cx.set_state(
6056        &r#"
6057        use some::modified;
6058
6059        ˇ
6060        fn main() {
6061            println!("hello there");
6062
6063            println!("around the");
6064            println!("world");
6065        }
6066        "#
6067        .unindent(),
6068    );
6069
6070    cx.set_diff_base(Some(&diff_base));
6071    deterministic.run_until_parked();
6072
6073    cx.update_editor(|editor, cx| {
6074        //Wrap around the bottom of the buffer
6075        for _ in 0..3 {
6076            editor.go_to_hunk(&GoToHunk, cx);
6077        }
6078    });
6079
6080    cx.assert_editor_state(
6081        &r#"
6082        ˇuse some::modified;
6083
6084
6085        fn main() {
6086            println!("hello there");
6087
6088            println!("around the");
6089            println!("world");
6090        }
6091        "#
6092        .unindent(),
6093    );
6094
6095    cx.update_editor(|editor, cx| {
6096        //Wrap around the top of the buffer
6097        for _ in 0..2 {
6098            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
6099        }
6100    });
6101
6102    cx.assert_editor_state(
6103        &r#"
6104        use some::modified;
6105
6106
6107        fn main() {
6108        ˇ    println!("hello there");
6109
6110            println!("around the");
6111            println!("world");
6112        }
6113        "#
6114        .unindent(),
6115    );
6116
6117    cx.update_editor(|editor, cx| {
6118        editor.fold(&Fold, cx);
6119
6120        //Make sure that the fold only gets one hunk
6121        for _ in 0..4 {
6122            editor.go_to_hunk(&GoToHunk, cx);
6123        }
6124    });
6125
6126    cx.assert_editor_state(
6127        &r#"
6128        ˇuse some::modified;
6129
6130
6131        fn main() {
6132            println!("hello there");
6133
6134            println!("around the");
6135            println!("world");
6136        }
6137        "#
6138        .unindent(),
6139    );
6140}
6141
6142#[test]
6143fn test_split_words() {
6144    fn split<'a>(text: &'a str) -> Vec<&'a str> {
6145        split_words(text).collect()
6146    }
6147
6148    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
6149    assert_eq!(split("hello_world"), &["hello_", "world"]);
6150    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
6151    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
6152    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
6153    assert_eq!(split("helloworld"), &["helloworld"]);
6154}
6155
6156#[gpui::test]
6157async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
6158    init_test(cx, |_| {});
6159
6160    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
6161    let mut assert = |before, after| {
6162        let _state_context = cx.set_state(before);
6163        cx.update_editor(|editor, cx| {
6164            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
6165        });
6166        cx.assert_editor_state(after);
6167    };
6168
6169    // Outside bracket jumps to outside of matching bracket
6170    assert("console.logˇ(var);", "console.log(var)ˇ;");
6171    assert("console.log(var)ˇ;", "console.logˇ(var);");
6172
6173    // Inside bracket jumps to inside of matching bracket
6174    assert("console.log(ˇvar);", "console.log(varˇ);");
6175    assert("console.log(varˇ);", "console.log(ˇvar);");
6176
6177    // When outside a bracket and inside, favor jumping to the inside bracket
6178    assert(
6179        "console.log('foo', [1, 2, 3]ˇ);",
6180        "console.log(ˇ'foo', [1, 2, 3]);",
6181    );
6182    assert(
6183        "console.log(ˇ'foo', [1, 2, 3]);",
6184        "console.log('foo', [1, 2, 3]ˇ);",
6185    );
6186
6187    // Bias forward if two options are equally likely
6188    assert(
6189        "let result = curried_fun()ˇ();",
6190        "let result = curried_fun()()ˇ;",
6191    );
6192
6193    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
6194    assert(
6195        indoc! {"
6196            function test() {
6197                console.log('test')ˇ
6198            }"},
6199        indoc! {"
6200            function test() {
6201                console.logˇ('test')
6202            }"},
6203    );
6204}
6205
6206#[gpui::test(iterations = 10)]
6207async fn test_copilot(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
6208    init_test(cx, |_| {});
6209
6210    let (copilot, copilot_lsp) = Copilot::fake(cx);
6211    cx.update(|cx| cx.set_global(copilot));
6212    let mut cx = EditorLspTestContext::new_rust(
6213        lsp::ServerCapabilities {
6214            completion_provider: Some(lsp::CompletionOptions {
6215                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
6216                ..Default::default()
6217            }),
6218            ..Default::default()
6219        },
6220        cx,
6221    )
6222    .await;
6223
6224    // When inserting, ensure autocompletion is favored over Copilot suggestions.
6225    cx.set_state(indoc! {"
6226        oneˇ
6227        two
6228        three
6229    "});
6230    cx.simulate_keystroke(".");
6231    let _ = handle_completion_request(
6232        &mut cx,
6233        indoc! {"
6234            one.|<>
6235            two
6236            three
6237        "},
6238        vec!["completion_a", "completion_b"],
6239    );
6240    handle_copilot_completion_request(
6241        &copilot_lsp,
6242        vec![copilot::request::Completion {
6243            text: "one.copilot1".into(),
6244            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6245            ..Default::default()
6246        }],
6247        vec![],
6248    );
6249    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6250    cx.update_editor(|editor, cx| {
6251        assert!(editor.context_menu_visible());
6252        assert!(!editor.has_active_copilot_suggestion(cx));
6253
6254        // Confirming a completion inserts it and hides the context menu, without showing
6255        // the copilot suggestion afterwards.
6256        editor
6257            .confirm_completion(&Default::default(), cx)
6258            .unwrap()
6259            .detach();
6260        assert!(!editor.context_menu_visible());
6261        assert!(!editor.has_active_copilot_suggestion(cx));
6262        assert_eq!(editor.text(cx), "one.completion_a\ntwo\nthree\n");
6263        assert_eq!(editor.display_text(cx), "one.completion_a\ntwo\nthree\n");
6264    });
6265
6266    // Ensure Copilot suggestions are shown right away if no autocompletion is available.
6267    cx.set_state(indoc! {"
6268        oneˇ
6269        two
6270        three
6271    "});
6272    cx.simulate_keystroke(".");
6273    let _ = handle_completion_request(
6274        &mut cx,
6275        indoc! {"
6276            one.|<>
6277            two
6278            three
6279        "},
6280        vec![],
6281    );
6282    handle_copilot_completion_request(
6283        &copilot_lsp,
6284        vec![copilot::request::Completion {
6285            text: "one.copilot1".into(),
6286            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6287            ..Default::default()
6288        }],
6289        vec![],
6290    );
6291    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6292    cx.update_editor(|editor, cx| {
6293        assert!(!editor.context_menu_visible());
6294        assert!(editor.has_active_copilot_suggestion(cx));
6295        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
6296        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
6297    });
6298
6299    // Reset editor, and ensure autocompletion is still favored over Copilot suggestions.
6300    cx.set_state(indoc! {"
6301        oneˇ
6302        two
6303        three
6304    "});
6305    cx.simulate_keystroke(".");
6306    let _ = handle_completion_request(
6307        &mut cx,
6308        indoc! {"
6309            one.|<>
6310            two
6311            three
6312        "},
6313        vec!["completion_a", "completion_b"],
6314    );
6315    handle_copilot_completion_request(
6316        &copilot_lsp,
6317        vec![copilot::request::Completion {
6318            text: "one.copilot1".into(),
6319            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6320            ..Default::default()
6321        }],
6322        vec![],
6323    );
6324    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6325    cx.update_editor(|editor, cx| {
6326        assert!(editor.context_menu_visible());
6327        assert!(!editor.has_active_copilot_suggestion(cx));
6328
6329        // When hiding the context menu, the Copilot suggestion becomes visible.
6330        editor.hide_context_menu(cx);
6331        assert!(!editor.context_menu_visible());
6332        assert!(editor.has_active_copilot_suggestion(cx));
6333        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
6334        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
6335    });
6336
6337    // Ensure existing completion is interpolated when inserting again.
6338    cx.simulate_keystroke("c");
6339    deterministic.run_until_parked();
6340    cx.update_editor(|editor, cx| {
6341        assert!(!editor.context_menu_visible());
6342        assert!(editor.has_active_copilot_suggestion(cx));
6343        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
6344        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6345    });
6346
6347    // After debouncing, new Copilot completions should be requested.
6348    handle_copilot_completion_request(
6349        &copilot_lsp,
6350        vec![copilot::request::Completion {
6351            text: "one.copilot2".into(),
6352            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 5)),
6353            ..Default::default()
6354        }],
6355        vec![],
6356    );
6357    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6358    cx.update_editor(|editor, cx| {
6359        assert!(!editor.context_menu_visible());
6360        assert!(editor.has_active_copilot_suggestion(cx));
6361        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6362        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6363
6364        // Canceling should remove the active Copilot suggestion.
6365        editor.cancel(&Default::default(), cx);
6366        assert!(!editor.has_active_copilot_suggestion(cx));
6367        assert_eq!(editor.display_text(cx), "one.c\ntwo\nthree\n");
6368        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6369
6370        // After canceling, tabbing shouldn't insert the previously shown suggestion.
6371        editor.tab(&Default::default(), cx);
6372        assert!(!editor.has_active_copilot_suggestion(cx));
6373        assert_eq!(editor.display_text(cx), "one.c   \ntwo\nthree\n");
6374        assert_eq!(editor.text(cx), "one.c   \ntwo\nthree\n");
6375
6376        // When undoing the previously active suggestion is shown again.
6377        editor.undo(&Default::default(), cx);
6378        assert!(editor.has_active_copilot_suggestion(cx));
6379        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6380        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6381    });
6382
6383    // If an edit occurs outside of this editor, the suggestion is still correctly interpolated.
6384    cx.update_buffer(|buffer, cx| buffer.edit([(5..5, "o")], None, cx));
6385    cx.update_editor(|editor, cx| {
6386        assert!(editor.has_active_copilot_suggestion(cx));
6387        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6388        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6389
6390        // Tabbing when there is an active suggestion inserts it.
6391        editor.tab(&Default::default(), cx);
6392        assert!(!editor.has_active_copilot_suggestion(cx));
6393        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6394        assert_eq!(editor.text(cx), "one.copilot2\ntwo\nthree\n");
6395
6396        // When undoing the previously active suggestion is shown again.
6397        editor.undo(&Default::default(), cx);
6398        assert!(editor.has_active_copilot_suggestion(cx));
6399        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6400        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6401
6402        // Hide suggestion.
6403        editor.cancel(&Default::default(), cx);
6404        assert!(!editor.has_active_copilot_suggestion(cx));
6405        assert_eq!(editor.display_text(cx), "one.co\ntwo\nthree\n");
6406        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6407    });
6408
6409    // If an edit occurs outside of this editor but no suggestion is being shown,
6410    // we won't make it visible.
6411    cx.update_buffer(|buffer, cx| buffer.edit([(6..6, "p")], None, cx));
6412    cx.update_editor(|editor, cx| {
6413        assert!(!editor.has_active_copilot_suggestion(cx));
6414        assert_eq!(editor.display_text(cx), "one.cop\ntwo\nthree\n");
6415        assert_eq!(editor.text(cx), "one.cop\ntwo\nthree\n");
6416    });
6417
6418    // Reset the editor to verify how suggestions behave when tabbing on leading indentation.
6419    cx.update_editor(|editor, cx| {
6420        editor.set_text("fn foo() {\n  \n}", cx);
6421        editor.change_selections(None, cx, |s| {
6422            s.select_ranges([Point::new(1, 2)..Point::new(1, 2)])
6423        });
6424    });
6425    handle_copilot_completion_request(
6426        &copilot_lsp,
6427        vec![copilot::request::Completion {
6428            text: "    let x = 4;".into(),
6429            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
6430            ..Default::default()
6431        }],
6432        vec![],
6433    );
6434
6435    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
6436    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6437    cx.update_editor(|editor, cx| {
6438        assert!(editor.has_active_copilot_suggestion(cx));
6439        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6440        assert_eq!(editor.text(cx), "fn foo() {\n  \n}");
6441
6442        // Tabbing inside of leading whitespace inserts indentation without accepting the suggestion.
6443        editor.tab(&Default::default(), cx);
6444        assert!(editor.has_active_copilot_suggestion(cx));
6445        assert_eq!(editor.text(cx), "fn foo() {\n    \n}");
6446        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6447
6448        // Tabbing again accepts the suggestion.
6449        editor.tab(&Default::default(), cx);
6450        assert!(!editor.has_active_copilot_suggestion(cx));
6451        assert_eq!(editor.text(cx), "fn foo() {\n    let x = 4;\n}");
6452        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6453    });
6454}
6455
6456#[gpui::test]
6457async fn test_copilot_completion_invalidation(
6458    deterministic: Arc<Deterministic>,
6459    cx: &mut gpui::TestAppContext,
6460) {
6461    init_test(cx, |_| {});
6462
6463    let (copilot, copilot_lsp) = Copilot::fake(cx);
6464    cx.update(|cx| cx.set_global(copilot));
6465    let mut cx = EditorLspTestContext::new_rust(
6466        lsp::ServerCapabilities {
6467            completion_provider: Some(lsp::CompletionOptions {
6468                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
6469                ..Default::default()
6470            }),
6471            ..Default::default()
6472        },
6473        cx,
6474    )
6475    .await;
6476
6477    cx.set_state(indoc! {"
6478        one
6479        twˇ
6480        three
6481    "});
6482
6483    handle_copilot_completion_request(
6484        &copilot_lsp,
6485        vec![copilot::request::Completion {
6486            text: "two.foo()".into(),
6487            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
6488            ..Default::default()
6489        }],
6490        vec![],
6491    );
6492    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
6493    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6494    cx.update_editor(|editor, cx| {
6495        assert!(editor.has_active_copilot_suggestion(cx));
6496        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6497        assert_eq!(editor.text(cx), "one\ntw\nthree\n");
6498
6499        editor.backspace(&Default::default(), cx);
6500        assert!(editor.has_active_copilot_suggestion(cx));
6501        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6502        assert_eq!(editor.text(cx), "one\nt\nthree\n");
6503
6504        editor.backspace(&Default::default(), cx);
6505        assert!(editor.has_active_copilot_suggestion(cx));
6506        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6507        assert_eq!(editor.text(cx), "one\n\nthree\n");
6508
6509        // Deleting across the original suggestion range invalidates it.
6510        editor.backspace(&Default::default(), cx);
6511        assert!(!editor.has_active_copilot_suggestion(cx));
6512        assert_eq!(editor.display_text(cx), "one\nthree\n");
6513        assert_eq!(editor.text(cx), "one\nthree\n");
6514
6515        // Undoing the deletion restores the suggestion.
6516        editor.undo(&Default::default(), cx);
6517        assert!(editor.has_active_copilot_suggestion(cx));
6518        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6519        assert_eq!(editor.text(cx), "one\n\nthree\n");
6520    });
6521}
6522
6523#[gpui::test]
6524async fn test_copilot_multibuffer(
6525    deterministic: Arc<Deterministic>,
6526    cx: &mut gpui::TestAppContext,
6527) {
6528    init_test(cx, |_| {});
6529
6530    let (copilot, copilot_lsp) = Copilot::fake(cx);
6531    cx.update(|cx| cx.set_global(copilot));
6532
6533    let buffer_1 = cx.add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx));
6534    let buffer_2 = cx.add_model(|cx| Buffer::new(0, "c = 3\nd = 4\n", cx));
6535    let multibuffer = cx.add_model(|cx| {
6536        let mut multibuffer = MultiBuffer::new(0);
6537        multibuffer.push_excerpts(
6538            buffer_1.clone(),
6539            [ExcerptRange {
6540                context: Point::new(0, 0)..Point::new(2, 0),
6541                primary: None,
6542            }],
6543            cx,
6544        );
6545        multibuffer.push_excerpts(
6546            buffer_2.clone(),
6547            [ExcerptRange {
6548                context: Point::new(0, 0)..Point::new(2, 0),
6549                primary: None,
6550            }],
6551            cx,
6552        );
6553        multibuffer
6554    });
6555    let (_, editor) = cx.add_window(|cx| build_editor(multibuffer, cx));
6556
6557    handle_copilot_completion_request(
6558        &copilot_lsp,
6559        vec![copilot::request::Completion {
6560            text: "b = 2 + a".into(),
6561            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 5)),
6562            ..Default::default()
6563        }],
6564        vec![],
6565    );
6566    editor.update(cx, |editor, cx| {
6567        // Ensure copilot suggestions are shown for the first excerpt.
6568        editor.change_selections(None, cx, |s| {
6569            s.select_ranges([Point::new(1, 5)..Point::new(1, 5)])
6570        });
6571        editor.next_copilot_suggestion(&Default::default(), cx);
6572    });
6573    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6574    editor.update(cx, |editor, cx| {
6575        assert!(editor.has_active_copilot_suggestion(cx));
6576        assert_eq!(
6577            editor.display_text(cx),
6578            "\n\na = 1\nb = 2 + a\n\n\n\nc = 3\nd = 4\n"
6579        );
6580        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
6581    });
6582
6583    handle_copilot_completion_request(
6584        &copilot_lsp,
6585        vec![copilot::request::Completion {
6586            text: "d = 4 + c".into(),
6587            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 6)),
6588            ..Default::default()
6589        }],
6590        vec![],
6591    );
6592    editor.update(cx, |editor, cx| {
6593        // Move to another excerpt, ensuring the suggestion gets cleared.
6594        editor.change_selections(None, cx, |s| {
6595            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
6596        });
6597        assert!(!editor.has_active_copilot_suggestion(cx));
6598        assert_eq!(
6599            editor.display_text(cx),
6600            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4\n"
6601        );
6602        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
6603
6604        // Type a character, ensuring we don't even try to interpolate the previous suggestion.
6605        editor.handle_input(" ", cx);
6606        assert!(!editor.has_active_copilot_suggestion(cx));
6607        assert_eq!(
6608            editor.display_text(cx),
6609            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 \n"
6610        );
6611        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
6612    });
6613
6614    // Ensure the new suggestion is displayed when the debounce timeout expires.
6615    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6616    editor.update(cx, |editor, cx| {
6617        assert!(editor.has_active_copilot_suggestion(cx));
6618        assert_eq!(
6619            editor.display_text(cx),
6620            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 + c\n"
6621        );
6622        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
6623    });
6624}
6625
6626#[gpui::test]
6627async fn test_copilot_disabled_globs(
6628    deterministic: Arc<Deterministic>,
6629    cx: &mut gpui::TestAppContext,
6630) {
6631    init_test(cx, |settings| {
6632        settings
6633            .copilot
6634            .get_or_insert(Default::default())
6635            .disabled_globs = Some(vec![".env*".to_string()]);
6636    });
6637
6638    let (copilot, copilot_lsp) = Copilot::fake(cx);
6639    cx.update(|cx| cx.set_global(copilot));
6640
6641    let fs = FakeFs::new(cx.background());
6642    fs.insert_tree(
6643        "/test",
6644        json!({
6645            ".env": "SECRET=something\n",
6646            "README.md": "hello\n"
6647        }),
6648    )
6649    .await;
6650    let project = Project::test(fs, ["/test".as_ref()], cx).await;
6651
6652    let private_buffer = project
6653        .update(cx, |project, cx| {
6654            project.open_local_buffer("/test/.env", cx)
6655        })
6656        .await
6657        .unwrap();
6658    let public_buffer = project
6659        .update(cx, |project, cx| {
6660            project.open_local_buffer("/test/README.md", cx)
6661        })
6662        .await
6663        .unwrap();
6664
6665    let multibuffer = cx.add_model(|cx| {
6666        let mut multibuffer = MultiBuffer::new(0);
6667        multibuffer.push_excerpts(
6668            private_buffer.clone(),
6669            [ExcerptRange {
6670                context: Point::new(0, 0)..Point::new(1, 0),
6671                primary: None,
6672            }],
6673            cx,
6674        );
6675        multibuffer.push_excerpts(
6676            public_buffer.clone(),
6677            [ExcerptRange {
6678                context: Point::new(0, 0)..Point::new(1, 0),
6679                primary: None,
6680            }],
6681            cx,
6682        );
6683        multibuffer
6684    });
6685    let (_, editor) = cx.add_window(|cx| build_editor(multibuffer, cx));
6686
6687    let mut copilot_requests = copilot_lsp
6688        .handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| async move {
6689            Ok(copilot::request::GetCompletionsResult {
6690                completions: vec![copilot::request::Completion {
6691                    text: "next line".into(),
6692                    range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
6693                    ..Default::default()
6694                }],
6695            })
6696        });
6697
6698    editor.update(cx, |editor, cx| {
6699        editor.change_selections(None, cx, |selections| {
6700            selections.select_ranges([Point::new(0, 0)..Point::new(0, 0)])
6701        });
6702        editor.next_copilot_suggestion(&Default::default(), cx);
6703    });
6704
6705    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6706    assert!(copilot_requests.try_next().is_err());
6707
6708    editor.update(cx, |editor, cx| {
6709        editor.change_selections(None, cx, |s| {
6710            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
6711        });
6712        editor.next_copilot_suggestion(&Default::default(), cx);
6713    });
6714
6715    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6716    assert!(copilot_requests.try_next().is_ok());
6717}
6718
6719fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
6720    let point = DisplayPoint::new(row as u32, column as u32);
6721    point..point
6722}
6723
6724fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
6725    let (text, ranges) = marked_text_ranges(marked_text, true);
6726    assert_eq!(view.text(cx), text);
6727    assert_eq!(
6728        view.selections.ranges(cx),
6729        ranges,
6730        "Assert selections are {}",
6731        marked_text
6732    );
6733}
6734
6735/// Handle completion request passing a marked string specifying where the completion
6736/// should be triggered from using '|' character, what range should be replaced, and what completions
6737/// should be returned using '<' and '>' to delimit the range
6738fn handle_completion_request<'a>(
6739    cx: &mut EditorLspTestContext<'a>,
6740    marked_string: &str,
6741    completions: Vec<&'static str>,
6742) -> impl Future<Output = ()> {
6743    let complete_from_marker: TextRangeMarker = '|'.into();
6744    let replace_range_marker: TextRangeMarker = ('<', '>').into();
6745    let (_, mut marked_ranges) = marked_text_ranges_by(
6746        marked_string,
6747        vec![complete_from_marker.clone(), replace_range_marker.clone()],
6748    );
6749
6750    let complete_from_position =
6751        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
6752    let replace_range =
6753        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
6754
6755    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
6756        let completions = completions.clone();
6757        async move {
6758            assert_eq!(params.text_document_position.text_document.uri, url.clone());
6759            assert_eq!(
6760                params.text_document_position.position,
6761                complete_from_position
6762            );
6763            Ok(Some(lsp::CompletionResponse::Array(
6764                completions
6765                    .iter()
6766                    .map(|completion_text| lsp::CompletionItem {
6767                        label: completion_text.to_string(),
6768                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
6769                            range: replace_range,
6770                            new_text: completion_text.to_string(),
6771                        })),
6772                        ..Default::default()
6773                    })
6774                    .collect(),
6775            )))
6776        }
6777    });
6778
6779    async move {
6780        request.next().await;
6781    }
6782}
6783
6784fn handle_resolve_completion_request<'a>(
6785    cx: &mut EditorLspTestContext<'a>,
6786    edits: Option<Vec<(&'static str, &'static str)>>,
6787) -> impl Future<Output = ()> {
6788    let edits = edits.map(|edits| {
6789        edits
6790            .iter()
6791            .map(|(marked_string, new_text)| {
6792                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
6793                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
6794                lsp::TextEdit::new(replace_range, new_text.to_string())
6795            })
6796            .collect::<Vec<_>>()
6797    });
6798
6799    let mut request =
6800        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
6801            let edits = edits.clone();
6802            async move {
6803                Ok(lsp::CompletionItem {
6804                    additional_text_edits: edits,
6805                    ..Default::default()
6806                })
6807            }
6808        });
6809
6810    async move {
6811        request.next().await;
6812    }
6813}
6814
6815fn handle_copilot_completion_request(
6816    lsp: &lsp::FakeLanguageServer,
6817    completions: Vec<copilot::request::Completion>,
6818    completions_cycling: Vec<copilot::request::Completion>,
6819) {
6820    lsp.handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| {
6821        let completions = completions.clone();
6822        async move {
6823            Ok(copilot::request::GetCompletionsResult {
6824                completions: completions.clone(),
6825            })
6826        }
6827    });
6828    lsp.handle_request::<copilot::request::GetCompletionsCycling, _, _>(move |_params, _cx| {
6829        let completions_cycling = completions_cycling.clone();
6830        async move {
6831            Ok(copilot::request::GetCompletionsResult {
6832                completions: completions_cycling.clone(),
6833            })
6834        }
6835    });
6836}
6837
6838pub(crate) fn update_test_settings(
6839    cx: &mut TestAppContext,
6840    f: impl Fn(&mut AllLanguageSettingsContent),
6841) {
6842    cx.update(|cx| {
6843        cx.update_global::<SettingsStore, _, _>(|store, cx| {
6844            store.update_user_settings::<AllLanguageSettings>(cx, f);
6845        });
6846    });
6847}
6848
6849pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
6850    cx.foreground().forbid_parking();
6851
6852    cx.update(|cx| {
6853        cx.set_global(SettingsStore::test(cx));
6854        theme::init((), cx);
6855        client::init_settings(cx);
6856        language::init(cx);
6857        Project::init_settings(cx);
6858        workspace::init_settings(cx);
6859        crate::init(cx);
6860    });
6861
6862    update_test_settings(cx, f);
6863}