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_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
3112    init_test(cx, |_| {});
3113
3114    let language = Arc::new(Language::new(
3115        LanguageConfig::default(),
3116        Some(tree_sitter_rust::language()),
3117    ));
3118
3119    let text = r#"
3120        use mod1::mod2::{mod3, mod4};
3121
3122        fn fn_1(param1: bool, param2: &str) {
3123            let var1 = "text";
3124        }
3125    "#
3126    .unindent();
3127
3128    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3129    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3130    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
3131    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
3132        .await;
3133
3134    view.update(cx, |view, cx| {
3135        view.change_selections(None, cx, |s| {
3136            s.select_display_ranges([
3137                DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3138                DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3139                DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3140            ]);
3141        });
3142        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3143    });
3144    assert_eq!(
3145        view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
3146        &[
3147            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
3148            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3149            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
3150        ]
3151    );
3152
3153    view.update(cx, |view, cx| {
3154        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3155    });
3156    assert_eq!(
3157        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3158        &[
3159            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3160            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
3161        ]
3162    );
3163
3164    view.update(cx, |view, cx| {
3165        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3166    });
3167    assert_eq!(
3168        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3169        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
3170    );
3171
3172    // Trying to expand the selected syntax node one more time has no effect.
3173    view.update(cx, |view, cx| {
3174        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3175    });
3176    assert_eq!(
3177        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3178        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
3179    );
3180
3181    view.update(cx, |view, cx| {
3182        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3183    });
3184    assert_eq!(
3185        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3186        &[
3187            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3188            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
3189        ]
3190    );
3191
3192    view.update(cx, |view, cx| {
3193        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, 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_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3206    });
3207    assert_eq!(
3208        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3209        &[
3210            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3211            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3212            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3213        ]
3214    );
3215
3216    // Trying to shrink the selected syntax node one more time has no effect.
3217    view.update(cx, |view, cx| {
3218        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3219    });
3220    assert_eq!(
3221        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3222        &[
3223            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3224            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3225            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3226        ]
3227    );
3228
3229    // Ensure that we keep expanding the selection if the larger selection starts or ends within
3230    // a fold.
3231    view.update(cx, |view, cx| {
3232        view.fold_ranges(
3233            vec![
3234                Point::new(0, 21)..Point::new(0, 24),
3235                Point::new(3, 20)..Point::new(3, 22),
3236            ],
3237            true,
3238            cx,
3239        );
3240        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3241    });
3242    assert_eq!(
3243        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3244        &[
3245            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3246            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3247            DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
3248        ]
3249    );
3250}
3251
3252#[gpui::test]
3253async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
3254    init_test(cx, |_| {});
3255
3256    let language = Arc::new(
3257        Language::new(
3258            LanguageConfig {
3259                brackets: BracketPairConfig {
3260                    pairs: vec![
3261                        BracketPair {
3262                            start: "{".to_string(),
3263                            end: "}".to_string(),
3264                            close: false,
3265                            newline: true,
3266                        },
3267                        BracketPair {
3268                            start: "(".to_string(),
3269                            end: ")".to_string(),
3270                            close: false,
3271                            newline: true,
3272                        },
3273                    ],
3274                    ..Default::default()
3275                },
3276                ..Default::default()
3277            },
3278            Some(tree_sitter_rust::language()),
3279        )
3280        .with_indents_query(
3281            r#"
3282                (_ "(" ")" @end) @indent
3283                (_ "{" "}" @end) @indent
3284            "#,
3285        )
3286        .unwrap(),
3287    );
3288
3289    let text = "fn a() {}";
3290
3291    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3292    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3293    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3294    editor
3295        .condition(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
3296        .await;
3297
3298    editor.update(cx, |editor, cx| {
3299        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
3300        editor.newline(&Newline, cx);
3301        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
3302        assert_eq!(
3303            editor.selections.ranges(cx),
3304            &[
3305                Point::new(1, 4)..Point::new(1, 4),
3306                Point::new(3, 4)..Point::new(3, 4),
3307                Point::new(5, 0)..Point::new(5, 0)
3308            ]
3309        );
3310    });
3311}
3312
3313#[gpui::test]
3314async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
3315    init_test(cx, |_| {});
3316
3317    let mut cx = EditorTestContext::new(cx).await;
3318
3319    let language = Arc::new(Language::new(
3320        LanguageConfig {
3321            brackets: BracketPairConfig {
3322                pairs: vec![
3323                    BracketPair {
3324                        start: "{".to_string(),
3325                        end: "}".to_string(),
3326                        close: true,
3327                        newline: true,
3328                    },
3329                    BracketPair {
3330                        start: "(".to_string(),
3331                        end: ")".to_string(),
3332                        close: true,
3333                        newline: true,
3334                    },
3335                    BracketPair {
3336                        start: "/*".to_string(),
3337                        end: " */".to_string(),
3338                        close: true,
3339                        newline: true,
3340                    },
3341                    BracketPair {
3342                        start: "[".to_string(),
3343                        end: "]".to_string(),
3344                        close: false,
3345                        newline: true,
3346                    },
3347                    BracketPair {
3348                        start: "\"".to_string(),
3349                        end: "\"".to_string(),
3350                        close: true,
3351                        newline: false,
3352                    },
3353                ],
3354                ..Default::default()
3355            },
3356            autoclose_before: "})]".to_string(),
3357            ..Default::default()
3358        },
3359        Some(tree_sitter_rust::language()),
3360    ));
3361
3362    let registry = Arc::new(LanguageRegistry::test());
3363    registry.add(language.clone());
3364    cx.update_buffer(|buffer, cx| {
3365        buffer.set_language_registry(registry);
3366        buffer.set_language(Some(language), cx);
3367    });
3368
3369    cx.set_state(
3370        &r#"
3371            🏀ˇ
3372            εˇ
3373            ❤️ˇ
3374        "#
3375        .unindent(),
3376    );
3377
3378    // autoclose multiple nested brackets at multiple cursors
3379    cx.update_editor(|view, cx| {
3380        view.handle_input("{", cx);
3381        view.handle_input("{", cx);
3382        view.handle_input("{", cx);
3383    });
3384    cx.assert_editor_state(
3385        &"
3386            🏀{{{ˇ}}}
3387            ε{{{ˇ}}}
3388            ❤️{{{ˇ}}}
3389        "
3390        .unindent(),
3391    );
3392
3393    // insert a different closing bracket
3394    cx.update_editor(|view, cx| {
3395        view.handle_input(")", cx);
3396    });
3397    cx.assert_editor_state(
3398        &"
3399            🏀{{{)ˇ}}}
3400            ε{{{)ˇ}}}
3401            ❤️{{{)ˇ}}}
3402        "
3403        .unindent(),
3404    );
3405
3406    // skip over the auto-closed brackets when typing a closing bracket
3407    cx.update_editor(|view, cx| {
3408        view.move_right(&MoveRight, cx);
3409        view.handle_input("}", cx);
3410        view.handle_input("}", cx);
3411        view.handle_input("}", cx);
3412    });
3413    cx.assert_editor_state(
3414        &"
3415            🏀{{{)}}}}ˇ
3416            ε{{{)}}}}ˇ
3417            ❤️{{{)}}}}ˇ
3418        "
3419        .unindent(),
3420    );
3421
3422    // autoclose multi-character pairs
3423    cx.set_state(
3424        &"
3425            ˇ
3426            ˇ
3427        "
3428        .unindent(),
3429    );
3430    cx.update_editor(|view, cx| {
3431        view.handle_input("/", cx);
3432        view.handle_input("*", cx);
3433    });
3434    cx.assert_editor_state(
3435        &"
3436            /*ˇ */
3437            /*ˇ */
3438        "
3439        .unindent(),
3440    );
3441
3442    // one cursor autocloses a multi-character pair, one cursor
3443    // does not autoclose.
3444    cx.set_state(
3445        &"
34463447            ˇ
3448        "
3449        .unindent(),
3450    );
3451    cx.update_editor(|view, cx| view.handle_input("*", cx));
3452    cx.assert_editor_state(
3453        &"
3454            /*ˇ */
34553456        "
3457        .unindent(),
3458    );
3459
3460    // Don't autoclose if the next character isn't whitespace and isn't
3461    // listed in the language's "autoclose_before" section.
3462    cx.set_state("ˇa b");
3463    cx.update_editor(|view, cx| view.handle_input("{", cx));
3464    cx.assert_editor_state("{ˇa b");
3465
3466    // Don't autoclose if `close` is false for the bracket pair
3467    cx.set_state("ˇ");
3468    cx.update_editor(|view, cx| view.handle_input("[", cx));
3469    cx.assert_editor_state("");
3470
3471    // Surround with brackets if text is selected
3472    cx.set_state("«aˇ» b");
3473    cx.update_editor(|view, cx| view.handle_input("{", cx));
3474    cx.assert_editor_state("{«aˇ»} b");
3475
3476    // Autclose pair where the start and end characters are the same
3477    cx.set_state("");
3478    cx.update_editor(|view, cx| view.handle_input("\"", cx));
3479    cx.assert_editor_state("a\"ˇ\"");
3480    cx.update_editor(|view, cx| view.handle_input("\"", cx));
3481    cx.assert_editor_state("a\"\"ˇ");
3482}
3483
3484#[gpui::test]
3485async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
3486    init_test(cx, |_| {});
3487
3488    let mut cx = EditorTestContext::new(cx).await;
3489
3490    let html_language = Arc::new(
3491        Language::new(
3492            LanguageConfig {
3493                name: "HTML".into(),
3494                brackets: BracketPairConfig {
3495                    pairs: vec![
3496                        BracketPair {
3497                            start: "<".into(),
3498                            end: ">".into(),
3499                            close: true,
3500                            ..Default::default()
3501                        },
3502                        BracketPair {
3503                            start: "{".into(),
3504                            end: "}".into(),
3505                            close: true,
3506                            ..Default::default()
3507                        },
3508                        BracketPair {
3509                            start: "(".into(),
3510                            end: ")".into(),
3511                            close: true,
3512                            ..Default::default()
3513                        },
3514                    ],
3515                    ..Default::default()
3516                },
3517                autoclose_before: "})]>".into(),
3518                ..Default::default()
3519            },
3520            Some(tree_sitter_html::language()),
3521        )
3522        .with_injection_query(
3523            r#"
3524            (script_element
3525                (raw_text) @content
3526                (#set! "language" "javascript"))
3527            "#,
3528        )
3529        .unwrap(),
3530    );
3531
3532    let javascript_language = Arc::new(Language::new(
3533        LanguageConfig {
3534            name: "JavaScript".into(),
3535            brackets: BracketPairConfig {
3536                pairs: vec![
3537                    BracketPair {
3538                        start: "/*".into(),
3539                        end: " */".into(),
3540                        close: true,
3541                        ..Default::default()
3542                    },
3543                    BracketPair {
3544                        start: "{".into(),
3545                        end: "}".into(),
3546                        close: true,
3547                        ..Default::default()
3548                    },
3549                    BracketPair {
3550                        start: "(".into(),
3551                        end: ")".into(),
3552                        close: true,
3553                        ..Default::default()
3554                    },
3555                ],
3556                ..Default::default()
3557            },
3558            autoclose_before: "})]>".into(),
3559            ..Default::default()
3560        },
3561        Some(tree_sitter_javascript::language()),
3562    ));
3563
3564    let registry = Arc::new(LanguageRegistry::test());
3565    registry.add(html_language.clone());
3566    registry.add(javascript_language.clone());
3567
3568    cx.update_buffer(|buffer, cx| {
3569        buffer.set_language_registry(registry);
3570        buffer.set_language(Some(html_language), cx);
3571    });
3572
3573    cx.set_state(
3574        &r#"
3575            <body>ˇ
3576                <script>
3577                    var x = 1;ˇ
3578                </script>
3579            </body>ˇ
3580        "#
3581        .unindent(),
3582    );
3583
3584    // Precondition: different languages are active at different locations.
3585    cx.update_editor(|editor, cx| {
3586        let snapshot = editor.snapshot(cx);
3587        let cursors = editor.selections.ranges::<usize>(cx);
3588        let languages = cursors
3589            .iter()
3590            .map(|c| snapshot.language_at(c.start).unwrap().name())
3591            .collect::<Vec<_>>();
3592        assert_eq!(
3593            languages,
3594            &["HTML".into(), "JavaScript".into(), "HTML".into()]
3595        );
3596    });
3597
3598    // Angle brackets autoclose in HTML, but not JavaScript.
3599    cx.update_editor(|editor, cx| {
3600        editor.handle_input("<", cx);
3601        editor.handle_input("a", cx);
3602    });
3603    cx.assert_editor_state(
3604        &r#"
3605            <body><aˇ>
3606                <script>
3607                    var x = 1;<aˇ
3608                </script>
3609            </body><aˇ>
3610        "#
3611        .unindent(),
3612    );
3613
3614    // Curly braces and parens autoclose in both HTML and JavaScript.
3615    cx.update_editor(|editor, cx| {
3616        editor.handle_input(" b=", cx);
3617        editor.handle_input("{", cx);
3618        editor.handle_input("c", cx);
3619        editor.handle_input("(", cx);
3620    });
3621    cx.assert_editor_state(
3622        &r#"
3623            <body><a b={c(ˇ)}>
3624                <script>
3625                    var x = 1;<a b={c(ˇ)}
3626                </script>
3627            </body><a b={c(ˇ)}>
3628        "#
3629        .unindent(),
3630    );
3631
3632    // Brackets that were already autoclosed are skipped.
3633    cx.update_editor(|editor, cx| {
3634        editor.handle_input(")", cx);
3635        editor.handle_input("d", cx);
3636        editor.handle_input("}", cx);
3637    });
3638    cx.assert_editor_state(
3639        &r#"
3640            <body><a b={c()d}ˇ>
3641                <script>
3642                    var x = 1;<a b={c()d}ˇ
3643                </script>
3644            </body><a b={c()d}ˇ>
3645        "#
3646        .unindent(),
3647    );
3648    cx.update_editor(|editor, cx| {
3649        editor.handle_input(">", cx);
3650    });
3651    cx.assert_editor_state(
3652        &r#"
3653            <body><a b={c()d}>ˇ
3654                <script>
3655                    var x = 1;<a b={c()d}>ˇ
3656                </script>
3657            </body><a b={c()d}>ˇ
3658        "#
3659        .unindent(),
3660    );
3661
3662    // Reset
3663    cx.set_state(
3664        &r#"
3665            <body>ˇ
3666                <script>
3667                    var x = 1;ˇ
3668                </script>
3669            </body>ˇ
3670        "#
3671        .unindent(),
3672    );
3673
3674    cx.update_editor(|editor, cx| {
3675        editor.handle_input("<", cx);
3676    });
3677    cx.assert_editor_state(
3678        &r#"
3679            <body><ˇ>
3680                <script>
3681                    var x = 1;<ˇ
3682                </script>
3683            </body><ˇ>
3684        "#
3685        .unindent(),
3686    );
3687
3688    // When backspacing, the closing angle brackets are removed.
3689    cx.update_editor(|editor, cx| {
3690        editor.backspace(&Backspace, cx);
3691    });
3692    cx.assert_editor_state(
3693        &r#"
3694            <body>ˇ
3695                <script>
3696                    var x = 1;ˇ
3697                </script>
3698            </body>ˇ
3699        "#
3700        .unindent(),
3701    );
3702
3703    // Block comments autoclose in JavaScript, but not HTML.
3704    cx.update_editor(|editor, cx| {
3705        editor.handle_input("/", cx);
3706        editor.handle_input("*", cx);
3707    });
3708    cx.assert_editor_state(
3709        &r#"
3710            <body>/*ˇ
3711                <script>
3712                    var x = 1;/*ˇ */
3713                </script>
3714            </body>/*ˇ
3715        "#
3716        .unindent(),
3717    );
3718}
3719
3720#[gpui::test]
3721async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
3722    init_test(cx, |_| {});
3723
3724    let mut cx = EditorTestContext::new(cx).await;
3725
3726    let rust_language = Arc::new(
3727        Language::new(
3728            LanguageConfig {
3729                name: "Rust".into(),
3730                brackets: serde_json::from_value(json!([
3731                    { "start": "{", "end": "}", "close": true, "newline": true },
3732                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
3733                ]))
3734                .unwrap(),
3735                autoclose_before: "})]>".into(),
3736                ..Default::default()
3737            },
3738            Some(tree_sitter_rust::language()),
3739        )
3740        .with_override_query("(string_literal) @string")
3741        .unwrap(),
3742    );
3743
3744    let registry = Arc::new(LanguageRegistry::test());
3745    registry.add(rust_language.clone());
3746
3747    cx.update_buffer(|buffer, cx| {
3748        buffer.set_language_registry(registry);
3749        buffer.set_language(Some(rust_language), cx);
3750    });
3751
3752    cx.set_state(
3753        &r#"
3754            let x = ˇ
3755        "#
3756        .unindent(),
3757    );
3758
3759    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
3760    cx.update_editor(|editor, cx| {
3761        editor.handle_input("\"", cx);
3762    });
3763    cx.assert_editor_state(
3764        &r#"
3765            let x = "ˇ"
3766        "#
3767        .unindent(),
3768    );
3769
3770    // Inserting another quotation mark. The cursor moves across the existing
3771    // automatically-inserted quotation mark.
3772    cx.update_editor(|editor, cx| {
3773        editor.handle_input("\"", cx);
3774    });
3775    cx.assert_editor_state(
3776        &r#"
3777            let x = ""ˇ
3778        "#
3779        .unindent(),
3780    );
3781
3782    // Reset
3783    cx.set_state(
3784        &r#"
3785            let x = ˇ
3786        "#
3787        .unindent(),
3788    );
3789
3790    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
3791    cx.update_editor(|editor, cx| {
3792        editor.handle_input("\"", cx);
3793        editor.handle_input(" ", cx);
3794        editor.move_left(&Default::default(), cx);
3795        editor.handle_input("\\", cx);
3796        editor.handle_input("\"", cx);
3797    });
3798    cx.assert_editor_state(
3799        &r#"
3800            let x = "\"ˇ "
3801        "#
3802        .unindent(),
3803    );
3804
3805    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
3806    // mark. Nothing is inserted.
3807    cx.update_editor(|editor, cx| {
3808        editor.move_right(&Default::default(), cx);
3809        editor.handle_input("\"", cx);
3810    });
3811    cx.assert_editor_state(
3812        &r#"
3813            let x = "\" "ˇ
3814        "#
3815        .unindent(),
3816    );
3817}
3818
3819#[gpui::test]
3820async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
3821    init_test(cx, |_| {});
3822
3823    let language = Arc::new(Language::new(
3824        LanguageConfig {
3825            brackets: BracketPairConfig {
3826                pairs: vec![
3827                    BracketPair {
3828                        start: "{".to_string(),
3829                        end: "}".to_string(),
3830                        close: true,
3831                        newline: true,
3832                    },
3833                    BracketPair {
3834                        start: "/* ".to_string(),
3835                        end: "*/".to_string(),
3836                        close: true,
3837                        ..Default::default()
3838                    },
3839                ],
3840                ..Default::default()
3841            },
3842            ..Default::default()
3843        },
3844        Some(tree_sitter_rust::language()),
3845    ));
3846
3847    let text = r#"
3848        a
3849        b
3850        c
3851    "#
3852    .unindent();
3853
3854    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3855    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3856    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
3857    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
3858        .await;
3859
3860    view.update(cx, |view, cx| {
3861        view.change_selections(None, cx, |s| {
3862            s.select_display_ranges([
3863                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3864                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
3865                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
3866            ])
3867        });
3868
3869        view.handle_input("{", cx);
3870        view.handle_input("{", cx);
3871        view.handle_input("{", cx);
3872        assert_eq!(
3873            view.text(cx),
3874            "
3875                {{{a}}}
3876                {{{b}}}
3877                {{{c}}}
3878            "
3879            .unindent()
3880        );
3881        assert_eq!(
3882            view.selections.display_ranges(cx),
3883            [
3884                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
3885                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
3886                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
3887            ]
3888        );
3889
3890        view.undo(&Undo, cx);
3891        view.undo(&Undo, cx);
3892        view.undo(&Undo, cx);
3893        assert_eq!(
3894            view.text(cx),
3895            "
3896                a
3897                b
3898                c
3899            "
3900            .unindent()
3901        );
3902        assert_eq!(
3903            view.selections.display_ranges(cx),
3904            [
3905                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3906                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
3907                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
3908            ]
3909        );
3910
3911        // Ensure inserting the first character of a multi-byte bracket pair
3912        // doesn't surround the selections with the bracket.
3913        view.handle_input("/", cx);
3914        assert_eq!(
3915            view.text(cx),
3916            "
3917                /
3918                /
3919                /
3920            "
3921            .unindent()
3922        );
3923        assert_eq!(
3924            view.selections.display_ranges(cx),
3925            [
3926                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
3927                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
3928                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
3929            ]
3930        );
3931
3932        view.undo(&Undo, cx);
3933        assert_eq!(
3934            view.text(cx),
3935            "
3936                a
3937                b
3938                c
3939            "
3940            .unindent()
3941        );
3942        assert_eq!(
3943            view.selections.display_ranges(cx),
3944            [
3945                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3946                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
3947                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
3948            ]
3949        );
3950
3951        // Ensure inserting the last character of a multi-byte bracket pair
3952        // doesn't surround the selections with the bracket.
3953        view.handle_input("*", cx);
3954        assert_eq!(
3955            view.text(cx),
3956            "
3957                *
3958                *
3959                *
3960            "
3961            .unindent()
3962        );
3963        assert_eq!(
3964            view.selections.display_ranges(cx),
3965            [
3966                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
3967                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
3968                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
3969            ]
3970        );
3971    });
3972}
3973
3974#[gpui::test]
3975async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
3976    init_test(cx, |_| {});
3977
3978    let language = Arc::new(Language::new(
3979        LanguageConfig {
3980            brackets: BracketPairConfig {
3981                pairs: vec![BracketPair {
3982                    start: "{".to_string(),
3983                    end: "}".to_string(),
3984                    close: true,
3985                    newline: true,
3986                }],
3987                ..Default::default()
3988            },
3989            autoclose_before: "}".to_string(),
3990            ..Default::default()
3991        },
3992        Some(tree_sitter_rust::language()),
3993    ));
3994
3995    let text = r#"
3996        a
3997        b
3998        c
3999    "#
4000    .unindent();
4001
4002    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
4003    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4004    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4005    editor
4006        .condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
4007        .await;
4008
4009    editor.update(cx, |editor, cx| {
4010        editor.change_selections(None, cx, |s| {
4011            s.select_ranges([
4012                Point::new(0, 1)..Point::new(0, 1),
4013                Point::new(1, 1)..Point::new(1, 1),
4014                Point::new(2, 1)..Point::new(2, 1),
4015            ])
4016        });
4017
4018        editor.handle_input("{", cx);
4019        editor.handle_input("{", cx);
4020        editor.handle_input("_", cx);
4021        assert_eq!(
4022            editor.text(cx),
4023            "
4024                a{{_}}
4025                b{{_}}
4026                c{{_}}
4027            "
4028            .unindent()
4029        );
4030        assert_eq!(
4031            editor.selections.ranges::<Point>(cx),
4032            [
4033                Point::new(0, 4)..Point::new(0, 4),
4034                Point::new(1, 4)..Point::new(1, 4),
4035                Point::new(2, 4)..Point::new(2, 4)
4036            ]
4037        );
4038
4039        editor.backspace(&Default::default(), cx);
4040        editor.backspace(&Default::default(), cx);
4041        assert_eq!(
4042            editor.text(cx),
4043            "
4044                a{}
4045                b{}
4046                c{}
4047            "
4048            .unindent()
4049        );
4050        assert_eq!(
4051            editor.selections.ranges::<Point>(cx),
4052            [
4053                Point::new(0, 2)..Point::new(0, 2),
4054                Point::new(1, 2)..Point::new(1, 2),
4055                Point::new(2, 2)..Point::new(2, 2)
4056            ]
4057        );
4058
4059        editor.delete_to_previous_word_start(&Default::default(), cx);
4060        assert_eq!(
4061            editor.text(cx),
4062            "
4063                a
4064                b
4065                c
4066            "
4067            .unindent()
4068        );
4069        assert_eq!(
4070            editor.selections.ranges::<Point>(cx),
4071            [
4072                Point::new(0, 1)..Point::new(0, 1),
4073                Point::new(1, 1)..Point::new(1, 1),
4074                Point::new(2, 1)..Point::new(2, 1)
4075            ]
4076        );
4077    });
4078}
4079
4080#[gpui::test]
4081async fn test_snippets(cx: &mut gpui::TestAppContext) {
4082    init_test(cx, |_| {});
4083
4084    let (text, insertion_ranges) = marked_text_ranges(
4085        indoc! {"
4086            a.ˇ b
4087            a.ˇ b
4088            a.ˇ b
4089        "},
4090        false,
4091    );
4092
4093    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
4094    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4095
4096    editor.update(cx, |editor, cx| {
4097        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
4098
4099        editor
4100            .insert_snippet(&insertion_ranges, snippet, cx)
4101            .unwrap();
4102
4103        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
4104            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
4105            assert_eq!(editor.text(cx), expected_text);
4106            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
4107        }
4108
4109        assert(
4110            editor,
4111            cx,
4112            indoc! {"
4113                a.f(«one», two, «three») b
4114                a.f(«one», two, «three») b
4115                a.f(«one», two, «three») b
4116            "},
4117        );
4118
4119        // Can't move earlier than the first tab stop
4120        assert!(!editor.move_to_prev_snippet_tabstop(cx));
4121        assert(
4122            editor,
4123            cx,
4124            indoc! {"
4125                a.f(«one», two, «three») b
4126                a.f(«one», two, «three») b
4127                a.f(«one», two, «three») b
4128            "},
4129        );
4130
4131        assert!(editor.move_to_next_snippet_tabstop(cx));
4132        assert(
4133            editor,
4134            cx,
4135            indoc! {"
4136                a.f(one, «two», three) b
4137                a.f(one, «two», three) b
4138                a.f(one, «two», three) b
4139            "},
4140        );
4141
4142        editor.move_to_prev_snippet_tabstop(cx);
4143        assert(
4144            editor,
4145            cx,
4146            indoc! {"
4147                a.f(«one», two, «three») b
4148                a.f(«one», two, «three») b
4149                a.f(«one», two, «three») b
4150            "},
4151        );
4152
4153        assert!(editor.move_to_next_snippet_tabstop(cx));
4154        assert(
4155            editor,
4156            cx,
4157            indoc! {"
4158                a.f(one, «two», three) b
4159                a.f(one, «two», three) b
4160                a.f(one, «two», three) b
4161            "},
4162        );
4163        assert!(editor.move_to_next_snippet_tabstop(cx));
4164        assert(
4165            editor,
4166            cx,
4167            indoc! {"
4168                a.f(one, two, three)ˇ b
4169                a.f(one, two, three)ˇ b
4170                a.f(one, two, three)ˇ b
4171            "},
4172        );
4173
4174        // As soon as the last tab stop is reached, snippet state is gone
4175        editor.move_to_prev_snippet_tabstop(cx);
4176        assert(
4177            editor,
4178            cx,
4179            indoc! {"
4180                a.f(one, two, three)ˇ b
4181                a.f(one, two, three)ˇ b
4182                a.f(one, two, three)ˇ b
4183            "},
4184        );
4185    });
4186}
4187
4188#[gpui::test]
4189async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
4190    init_test(cx, |_| {});
4191
4192    let mut language = Language::new(
4193        LanguageConfig {
4194            name: "Rust".into(),
4195            path_suffixes: vec!["rs".to_string()],
4196            ..Default::default()
4197        },
4198        Some(tree_sitter_rust::language()),
4199    );
4200    let mut fake_servers = language
4201        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4202            capabilities: lsp::ServerCapabilities {
4203                document_formatting_provider: Some(lsp::OneOf::Left(true)),
4204                ..Default::default()
4205            },
4206            ..Default::default()
4207        }))
4208        .await;
4209
4210    let fs = FakeFs::new(cx.background());
4211    fs.insert_file("/file.rs", Default::default()).await;
4212
4213    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4214    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4215    let buffer = project
4216        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4217        .await
4218        .unwrap();
4219
4220    cx.foreground().start_waiting();
4221    let fake_server = fake_servers.next().await.unwrap();
4222
4223    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4224    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4225    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4226    assert!(cx.read(|cx| editor.is_dirty(cx)));
4227
4228    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4229    fake_server
4230        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4231            assert_eq!(
4232                params.text_document.uri,
4233                lsp::Url::from_file_path("/file.rs").unwrap()
4234            );
4235            assert_eq!(params.options.tab_size, 4);
4236            Ok(Some(vec![lsp::TextEdit::new(
4237                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4238                ", ".to_string(),
4239            )]))
4240        })
4241        .next()
4242        .await;
4243    cx.foreground().start_waiting();
4244    save.await.unwrap();
4245    assert_eq!(
4246        editor.read_with(cx, |editor, cx| editor.text(cx)),
4247        "one, two\nthree\n"
4248    );
4249    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4250
4251    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4252    assert!(cx.read(|cx| editor.is_dirty(cx)));
4253
4254    // Ensure we can still save even if formatting hangs.
4255    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4256        assert_eq!(
4257            params.text_document.uri,
4258            lsp::Url::from_file_path("/file.rs").unwrap()
4259        );
4260        futures::future::pending::<()>().await;
4261        unreachable!()
4262    });
4263    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4264    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4265    cx.foreground().start_waiting();
4266    save.await.unwrap();
4267    assert_eq!(
4268        editor.read_with(cx, |editor, cx| editor.text(cx)),
4269        "one\ntwo\nthree\n"
4270    );
4271    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4272
4273    // Set rust language override and assert overriden tabsize is sent to language server
4274    update_test_settings(cx, |settings| {
4275        settings.languages.insert(
4276            "Rust".into(),
4277            LanguageSettingsContent {
4278                tab_size: NonZeroU32::new(8),
4279                ..Default::default()
4280            },
4281        );
4282    });
4283
4284    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4285    fake_server
4286        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4287            assert_eq!(
4288                params.text_document.uri,
4289                lsp::Url::from_file_path("/file.rs").unwrap()
4290            );
4291            assert_eq!(params.options.tab_size, 8);
4292            Ok(Some(vec![]))
4293        })
4294        .next()
4295        .await;
4296    cx.foreground().start_waiting();
4297    save.await.unwrap();
4298}
4299
4300#[gpui::test]
4301async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
4302    init_test(cx, |_| {});
4303
4304    let mut language = Language::new(
4305        LanguageConfig {
4306            name: "Rust".into(),
4307            path_suffixes: vec!["rs".to_string()],
4308            ..Default::default()
4309        },
4310        Some(tree_sitter_rust::language()),
4311    );
4312    let mut fake_servers = language
4313        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4314            capabilities: lsp::ServerCapabilities {
4315                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
4316                ..Default::default()
4317            },
4318            ..Default::default()
4319        }))
4320        .await;
4321
4322    let fs = FakeFs::new(cx.background());
4323    fs.insert_file("/file.rs", Default::default()).await;
4324
4325    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4326    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4327    let buffer = project
4328        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4329        .await
4330        .unwrap();
4331
4332    cx.foreground().start_waiting();
4333    let fake_server = fake_servers.next().await.unwrap();
4334
4335    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4336    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4337    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4338    assert!(cx.read(|cx| editor.is_dirty(cx)));
4339
4340    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4341    fake_server
4342        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
4343            assert_eq!(
4344                params.text_document.uri,
4345                lsp::Url::from_file_path("/file.rs").unwrap()
4346            );
4347            assert_eq!(params.options.tab_size, 4);
4348            Ok(Some(vec![lsp::TextEdit::new(
4349                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4350                ", ".to_string(),
4351            )]))
4352        })
4353        .next()
4354        .await;
4355    cx.foreground().start_waiting();
4356    save.await.unwrap();
4357    assert_eq!(
4358        editor.read_with(cx, |editor, cx| editor.text(cx)),
4359        "one, two\nthree\n"
4360    );
4361    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4362
4363    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4364    assert!(cx.read(|cx| editor.is_dirty(cx)));
4365
4366    // Ensure we can still save even if formatting hangs.
4367    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
4368        move |params, _| async move {
4369            assert_eq!(
4370                params.text_document.uri,
4371                lsp::Url::from_file_path("/file.rs").unwrap()
4372            );
4373            futures::future::pending::<()>().await;
4374            unreachable!()
4375        },
4376    );
4377    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4378    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4379    cx.foreground().start_waiting();
4380    save.await.unwrap();
4381    assert_eq!(
4382        editor.read_with(cx, |editor, cx| editor.text(cx)),
4383        "one\ntwo\nthree\n"
4384    );
4385    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4386
4387    // Set rust language override and assert overriden tabsize is sent to language server
4388    update_test_settings(cx, |settings| {
4389        settings.languages.insert(
4390            "Rust".into(),
4391            LanguageSettingsContent {
4392                tab_size: NonZeroU32::new(8),
4393                ..Default::default()
4394            },
4395        );
4396    });
4397
4398    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4399    fake_server
4400        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
4401            assert_eq!(
4402                params.text_document.uri,
4403                lsp::Url::from_file_path("/file.rs").unwrap()
4404            );
4405            assert_eq!(params.options.tab_size, 8);
4406            Ok(Some(vec![]))
4407        })
4408        .next()
4409        .await;
4410    cx.foreground().start_waiting();
4411    save.await.unwrap();
4412}
4413
4414#[gpui::test]
4415async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
4416    init_test(cx, |_| {});
4417
4418    let mut language = Language::new(
4419        LanguageConfig {
4420            name: "Rust".into(),
4421            path_suffixes: vec!["rs".to_string()],
4422            ..Default::default()
4423        },
4424        Some(tree_sitter_rust::language()),
4425    );
4426    let mut fake_servers = language
4427        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4428            capabilities: lsp::ServerCapabilities {
4429                document_formatting_provider: Some(lsp::OneOf::Left(true)),
4430                ..Default::default()
4431            },
4432            ..Default::default()
4433        }))
4434        .await;
4435
4436    let fs = FakeFs::new(cx.background());
4437    fs.insert_file("/file.rs", Default::default()).await;
4438
4439    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4440    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4441    let buffer = project
4442        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4443        .await
4444        .unwrap();
4445
4446    cx.foreground().start_waiting();
4447    let fake_server = fake_servers.next().await.unwrap();
4448
4449    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4450    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4451    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4452
4453    let format = editor.update(cx, |editor, cx| {
4454        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
4455    });
4456    fake_server
4457        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4458            assert_eq!(
4459                params.text_document.uri,
4460                lsp::Url::from_file_path("/file.rs").unwrap()
4461            );
4462            assert_eq!(params.options.tab_size, 4);
4463            Ok(Some(vec![lsp::TextEdit::new(
4464                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4465                ", ".to_string(),
4466            )]))
4467        })
4468        .next()
4469        .await;
4470    cx.foreground().start_waiting();
4471    format.await.unwrap();
4472    assert_eq!(
4473        editor.read_with(cx, |editor, cx| editor.text(cx)),
4474        "one, two\nthree\n"
4475    );
4476
4477    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4478    // Ensure we don't lock if formatting hangs.
4479    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4480        assert_eq!(
4481            params.text_document.uri,
4482            lsp::Url::from_file_path("/file.rs").unwrap()
4483        );
4484        futures::future::pending::<()>().await;
4485        unreachable!()
4486    });
4487    let format = editor.update(cx, |editor, cx| {
4488        editor.perform_format(project, FormatTrigger::Manual, cx)
4489    });
4490    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4491    cx.foreground().start_waiting();
4492    format.await.unwrap();
4493    assert_eq!(
4494        editor.read_with(cx, |editor, cx| editor.text(cx)),
4495        "one\ntwo\nthree\n"
4496    );
4497}
4498
4499#[gpui::test]
4500async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
4501    init_test(cx, |_| {});
4502
4503    let mut cx = EditorLspTestContext::new_rust(
4504        lsp::ServerCapabilities {
4505            document_formatting_provider: Some(lsp::OneOf::Left(true)),
4506            ..Default::default()
4507        },
4508        cx,
4509    )
4510    .await;
4511
4512    cx.set_state(indoc! {"
4513        one.twoˇ
4514    "});
4515
4516    // The format request takes a long time. When it completes, it inserts
4517    // a newline and an indent before the `.`
4518    cx.lsp
4519        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
4520            let executor = cx.background();
4521            async move {
4522                executor.timer(Duration::from_millis(100)).await;
4523                Ok(Some(vec![lsp::TextEdit {
4524                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
4525                    new_text: "\n    ".into(),
4526                }]))
4527            }
4528        });
4529
4530    // Submit a format request.
4531    let format_1 = cx
4532        .update_editor(|editor, cx| editor.format(&Format, cx))
4533        .unwrap();
4534    cx.foreground().run_until_parked();
4535
4536    // Submit a second format request.
4537    let format_2 = cx
4538        .update_editor(|editor, cx| editor.format(&Format, cx))
4539        .unwrap();
4540    cx.foreground().run_until_parked();
4541
4542    // Wait for both format requests to complete
4543    cx.foreground().advance_clock(Duration::from_millis(200));
4544    cx.foreground().start_waiting();
4545    format_1.await.unwrap();
4546    cx.foreground().start_waiting();
4547    format_2.await.unwrap();
4548
4549    // The formatting edits only happens once.
4550    cx.assert_editor_state(indoc! {"
4551        one
4552            .twoˇ
4553    "});
4554}
4555
4556#[gpui::test]
4557async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
4558    init_test(cx, |_| {});
4559
4560    let mut cx = EditorLspTestContext::new_rust(
4561        lsp::ServerCapabilities {
4562            document_formatting_provider: Some(lsp::OneOf::Left(true)),
4563            ..Default::default()
4564        },
4565        cx,
4566    )
4567    .await;
4568
4569    // Set up a buffer white some trailing whitespace and no trailing newline.
4570    cx.set_state(
4571        &[
4572            "one ",   //
4573            "twoˇ",   //
4574            "three ", //
4575            "four",   //
4576        ]
4577        .join("\n"),
4578    );
4579
4580    // Submit a format request.
4581    let format = cx
4582        .update_editor(|editor, cx| editor.format(&Format, cx))
4583        .unwrap();
4584
4585    // Record which buffer changes have been sent to the language server
4586    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
4587    cx.lsp
4588        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
4589            let buffer_changes = buffer_changes.clone();
4590            move |params, _| {
4591                buffer_changes.lock().extend(
4592                    params
4593                        .content_changes
4594                        .into_iter()
4595                        .map(|e| (e.range.unwrap(), e.text)),
4596                );
4597            }
4598        });
4599
4600    // Handle formatting requests to the language server.
4601    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
4602        let buffer_changes = buffer_changes.clone();
4603        move |_, _| {
4604            // When formatting is requested, trailing whitespace has already been stripped,
4605            // and the trailing newline has already been added.
4606            assert_eq!(
4607                &buffer_changes.lock()[1..],
4608                &[
4609                    (
4610                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
4611                        "".into()
4612                    ),
4613                    (
4614                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
4615                        "".into()
4616                    ),
4617                    (
4618                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
4619                        "\n".into()
4620                    ),
4621                ]
4622            );
4623
4624            // Insert blank lines between each line of the buffer.
4625            async move {
4626                Ok(Some(vec![
4627                    lsp::TextEdit {
4628                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
4629                        new_text: "\n".into(),
4630                    },
4631                    lsp::TextEdit {
4632                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
4633                        new_text: "\n".into(),
4634                    },
4635                ]))
4636            }
4637        }
4638    });
4639
4640    // After formatting the buffer, the trailing whitespace is stripped,
4641    // a newline is appended, and the edits provided by the language server
4642    // have been applied.
4643    format.await.unwrap();
4644    cx.assert_editor_state(
4645        &[
4646            "one",   //
4647            "",      //
4648            "twoˇ",  //
4649            "",      //
4650            "three", //
4651            "four",  //
4652            "",      //
4653        ]
4654        .join("\n"),
4655    );
4656
4657    // Undoing the formatting undoes the trailing whitespace removal, the
4658    // trailing newline, and the LSP edits.
4659    cx.update_buffer(|buffer, cx| buffer.undo(cx));
4660    cx.assert_editor_state(
4661        &[
4662            "one ",   //
4663            "twoˇ",   //
4664            "three ", //
4665            "four",   //
4666        ]
4667        .join("\n"),
4668    );
4669}
4670
4671#[gpui::test]
4672async fn test_completion(cx: &mut gpui::TestAppContext) {
4673    init_test(cx, |_| {});
4674
4675    let mut cx = EditorLspTestContext::new_rust(
4676        lsp::ServerCapabilities {
4677            completion_provider: Some(lsp::CompletionOptions {
4678                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
4679                ..Default::default()
4680            }),
4681            ..Default::default()
4682        },
4683        cx,
4684    )
4685    .await;
4686
4687    cx.set_state(indoc! {"
4688        oneˇ
4689        two
4690        three
4691    "});
4692    cx.simulate_keystroke(".");
4693    handle_completion_request(
4694        &mut cx,
4695        indoc! {"
4696            one.|<>
4697            two
4698            three
4699        "},
4700        vec!["first_completion", "second_completion"],
4701    )
4702    .await;
4703    cx.condition(|editor, _| editor.context_menu_visible())
4704        .await;
4705    let apply_additional_edits = cx.update_editor(|editor, cx| {
4706        editor.move_down(&MoveDown, cx);
4707        editor
4708            .confirm_completion(&ConfirmCompletion::default(), cx)
4709            .unwrap()
4710    });
4711    cx.assert_editor_state(indoc! {"
4712        one.second_completionˇ
4713        two
4714        three
4715    "});
4716
4717    handle_resolve_completion_request(
4718        &mut cx,
4719        Some(vec![
4720            (
4721                //This overlaps with the primary completion edit which is
4722                //misbehavior from the LSP spec, test that we filter it out
4723                indoc! {"
4724                    one.second_ˇcompletion
4725                    two
4726                    threeˇ
4727                "},
4728                "overlapping aditional edit",
4729            ),
4730            (
4731                indoc! {"
4732                    one.second_completion
4733                    two
4734                    threeˇ
4735                "},
4736                "\nadditional edit",
4737            ),
4738        ]),
4739    )
4740    .await;
4741    apply_additional_edits.await.unwrap();
4742    cx.assert_editor_state(indoc! {"
4743        one.second_completionˇ
4744        two
4745        three
4746        additional edit
4747    "});
4748
4749    cx.set_state(indoc! {"
4750        one.second_completion
4751        twoˇ
4752        threeˇ
4753        additional edit
4754    "});
4755    cx.simulate_keystroke(" ");
4756    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4757    cx.simulate_keystroke("s");
4758    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4759
4760    cx.assert_editor_state(indoc! {"
4761        one.second_completion
4762        two sˇ
4763        three sˇ
4764        additional edit
4765    "});
4766    handle_completion_request(
4767        &mut cx,
4768        indoc! {"
4769            one.second_completion
4770            two s
4771            three <s|>
4772            additional edit
4773        "},
4774        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
4775    )
4776    .await;
4777    cx.condition(|editor, _| editor.context_menu_visible())
4778        .await;
4779
4780    cx.simulate_keystroke("i");
4781
4782    handle_completion_request(
4783        &mut cx,
4784        indoc! {"
4785            one.second_completion
4786            two si
4787            three <si|>
4788            additional edit
4789        "},
4790        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
4791    )
4792    .await;
4793    cx.condition(|editor, _| editor.context_menu_visible())
4794        .await;
4795
4796    let apply_additional_edits = cx.update_editor(|editor, cx| {
4797        editor
4798            .confirm_completion(&ConfirmCompletion::default(), cx)
4799            .unwrap()
4800    });
4801    cx.assert_editor_state(indoc! {"
4802        one.second_completion
4803        two sixth_completionˇ
4804        three sixth_completionˇ
4805        additional edit
4806    "});
4807
4808    handle_resolve_completion_request(&mut cx, None).await;
4809    apply_additional_edits.await.unwrap();
4810
4811    cx.update(|cx| {
4812        cx.update_global::<SettingsStore, _, _>(|settings, cx| {
4813            settings.update_user_settings::<EditorSettings>(cx, |settings| {
4814                settings.show_completions_on_input = Some(false);
4815            });
4816        })
4817    });
4818    cx.set_state("editorˇ");
4819    cx.simulate_keystroke(".");
4820    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4821    cx.simulate_keystroke("c");
4822    cx.simulate_keystroke("l");
4823    cx.simulate_keystroke("o");
4824    cx.assert_editor_state("editor.cloˇ");
4825    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4826    cx.update_editor(|editor, cx| {
4827        editor.show_completions(&ShowCompletions, cx);
4828    });
4829    handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
4830    cx.condition(|editor, _| editor.context_menu_visible())
4831        .await;
4832    let apply_additional_edits = cx.update_editor(|editor, cx| {
4833        editor
4834            .confirm_completion(&ConfirmCompletion::default(), cx)
4835            .unwrap()
4836    });
4837    cx.assert_editor_state("editor.closeˇ");
4838    handle_resolve_completion_request(&mut cx, None).await;
4839    apply_additional_edits.await.unwrap();
4840}
4841
4842#[gpui::test]
4843async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
4844    init_test(cx, |_| {});
4845
4846    let language = Arc::new(Language::new(
4847        LanguageConfig {
4848            line_comment: Some("// ".into()),
4849            ..Default::default()
4850        },
4851        Some(tree_sitter_rust::language()),
4852    ));
4853
4854    let text = "
4855        fn a() {
4856            //b();
4857            // c();
4858            //  d();
4859        }
4860    "
4861    .unindent();
4862
4863    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
4864    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4865    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
4866
4867    view.update(cx, |editor, cx| {
4868        // If multiple selections intersect a line, the line is only
4869        // toggled once.
4870        editor.change_selections(None, cx, |s| {
4871            s.select_display_ranges([
4872                DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
4873                DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
4874            ])
4875        });
4876        editor.toggle_comments(&ToggleComments::default(), cx);
4877        assert_eq!(
4878            editor.text(cx),
4879            "
4880                fn a() {
4881                    b();
4882                    c();
4883                     d();
4884                }
4885            "
4886            .unindent()
4887        );
4888
4889        // The comment prefix is inserted at the same column for every line
4890        // in a selection.
4891        editor.change_selections(None, cx, |s| {
4892            s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)])
4893        });
4894        editor.toggle_comments(&ToggleComments::default(), cx);
4895        assert_eq!(
4896            editor.text(cx),
4897            "
4898                fn a() {
4899                    // b();
4900                    // c();
4901                    //  d();
4902                }
4903            "
4904            .unindent()
4905        );
4906
4907        // If a selection ends at the beginning of a line, that line is not toggled.
4908        editor.change_selections(None, cx, |s| {
4909            s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)])
4910        });
4911        editor.toggle_comments(&ToggleComments::default(), cx);
4912        assert_eq!(
4913            editor.text(cx),
4914            "
4915                fn a() {
4916                    // b();
4917                    c();
4918                    //  d();
4919                }
4920            "
4921            .unindent()
4922        );
4923    });
4924}
4925
4926#[gpui::test]
4927async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
4928    init_test(cx, |_| {});
4929
4930    let language = Arc::new(Language::new(
4931        LanguageConfig {
4932            line_comment: Some("// ".into()),
4933            ..Default::default()
4934        },
4935        Some(tree_sitter_rust::language()),
4936    ));
4937
4938    let registry = Arc::new(LanguageRegistry::test());
4939    registry.add(language.clone());
4940
4941    let mut cx = EditorTestContext::new(cx).await;
4942    cx.update_buffer(|buffer, cx| {
4943        buffer.set_language_registry(registry);
4944        buffer.set_language(Some(language), cx);
4945    });
4946
4947    let toggle_comments = &ToggleComments {
4948        advance_downwards: true,
4949    };
4950
4951    // Single cursor on one line -> advance
4952    // Cursor moves horizontally 3 characters as well on non-blank line
4953    cx.set_state(indoc!(
4954        "fn a() {
4955             ˇdog();
4956             cat();
4957        }"
4958    ));
4959    cx.update_editor(|editor, cx| {
4960        editor.toggle_comments(toggle_comments, cx);
4961    });
4962    cx.assert_editor_state(indoc!(
4963        "fn a() {
4964             // dog();
4965             catˇ();
4966        }"
4967    ));
4968
4969    // Single selection on one line -> don't advance
4970    cx.set_state(indoc!(
4971        "fn a() {
4972             «dog()ˇ»;
4973             cat();
4974        }"
4975    ));
4976    cx.update_editor(|editor, cx| {
4977        editor.toggle_comments(toggle_comments, cx);
4978    });
4979    cx.assert_editor_state(indoc!(
4980        "fn a() {
4981             // «dog()ˇ»;
4982             cat();
4983        }"
4984    ));
4985
4986    // Multiple cursors on one line -> advance
4987    cx.set_state(indoc!(
4988        "fn a() {
4989             ˇdˇog();
4990             cat();
4991        }"
4992    ));
4993    cx.update_editor(|editor, cx| {
4994        editor.toggle_comments(toggle_comments, cx);
4995    });
4996    cx.assert_editor_state(indoc!(
4997        "fn a() {
4998             // dog();
4999             catˇ(ˇ);
5000        }"
5001    ));
5002
5003    // Multiple cursors on one line, with selection -> don't advance
5004    cx.set_state(indoc!(
5005        "fn a() {
5006             ˇdˇog«()ˇ»;
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             // ˇdˇog«()ˇ»;
5016             cat();
5017        }"
5018    ));
5019
5020    // Single cursor on one line -> advance
5021    // Cursor moves to column 0 on blank line
5022    cx.set_state(indoc!(
5023        "fn a() {
5024             ˇdog();
5025
5026             cat();
5027        }"
5028    ));
5029    cx.update_editor(|editor, cx| {
5030        editor.toggle_comments(toggle_comments, cx);
5031    });
5032    cx.assert_editor_state(indoc!(
5033        "fn a() {
5034             // dog();
5035        ˇ
5036             cat();
5037        }"
5038    ));
5039
5040    // Single cursor on one line -> advance
5041    // Cursor starts and ends at column 0
5042    cx.set_state(indoc!(
5043        "fn a() {
5044         ˇ    dog();
5045             cat();
5046        }"
5047    ));
5048    cx.update_editor(|editor, cx| {
5049        editor.toggle_comments(toggle_comments, cx);
5050    });
5051    cx.assert_editor_state(indoc!(
5052        "fn a() {
5053             // dog();
5054         ˇ    cat();
5055        }"
5056    ));
5057}
5058
5059#[gpui::test]
5060async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
5061    init_test(cx, |_| {});
5062
5063    let mut cx = EditorTestContext::new(cx).await;
5064
5065    let html_language = Arc::new(
5066        Language::new(
5067            LanguageConfig {
5068                name: "HTML".into(),
5069                block_comment: Some(("<!-- ".into(), " -->".into())),
5070                ..Default::default()
5071            },
5072            Some(tree_sitter_html::language()),
5073        )
5074        .with_injection_query(
5075            r#"
5076            (script_element
5077                (raw_text) @content
5078                (#set! "language" "javascript"))
5079            "#,
5080        )
5081        .unwrap(),
5082    );
5083
5084    let javascript_language = Arc::new(Language::new(
5085        LanguageConfig {
5086            name: "JavaScript".into(),
5087            line_comment: Some("// ".into()),
5088            ..Default::default()
5089        },
5090        Some(tree_sitter_javascript::language()),
5091    ));
5092
5093    let registry = Arc::new(LanguageRegistry::test());
5094    registry.add(html_language.clone());
5095    registry.add(javascript_language.clone());
5096
5097    cx.update_buffer(|buffer, cx| {
5098        buffer.set_language_registry(registry);
5099        buffer.set_language(Some(html_language), cx);
5100    });
5101
5102    // Toggle comments for empty selections
5103    cx.set_state(
5104        &r#"
5105            <p>A</p>ˇ
5106            <p>B</p>ˇ
5107            <p>C</p>ˇ
5108        "#
5109        .unindent(),
5110    );
5111    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5112    cx.assert_editor_state(
5113        &r#"
5114            <!-- <p>A</p>ˇ -->
5115            <!-- <p>B</p>ˇ -->
5116            <!-- <p>C</p>ˇ -->
5117        "#
5118        .unindent(),
5119    );
5120    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5121    cx.assert_editor_state(
5122        &r#"
5123            <p>A</p>ˇ
5124            <p>B</p>ˇ
5125            <p>C</p>ˇ
5126        "#
5127        .unindent(),
5128    );
5129
5130    // Toggle comments for mixture of empty and non-empty selections, where
5131    // multiple selections occupy a given line.
5132    cx.set_state(
5133        &r#"
5134            <p>A«</p>
5135            <p>ˇ»B</p>ˇ
5136            <p>C«</p>
5137            <p>ˇ»D</p>ˇ
5138        "#
5139        .unindent(),
5140    );
5141
5142    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5143    cx.assert_editor_state(
5144        &r#"
5145            <!-- <p>A«</p>
5146            <p>ˇ»B</p>ˇ -->
5147            <!-- <p>C«</p>
5148            <p>ˇ»D</p>ˇ -->
5149        "#
5150        .unindent(),
5151    );
5152    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5153    cx.assert_editor_state(
5154        &r#"
5155            <p>A«</p>
5156            <p>ˇ»B</p>ˇ
5157            <p>C«</p>
5158            <p>ˇ»D</p>ˇ
5159        "#
5160        .unindent(),
5161    );
5162
5163    // Toggle comments when different languages are active for different
5164    // selections.
5165    cx.set_state(
5166        &r#"
5167            ˇ<script>
5168                ˇvar x = new Y();
5169            ˇ</script>
5170        "#
5171        .unindent(),
5172    );
5173    cx.foreground().run_until_parked();
5174    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5175    cx.assert_editor_state(
5176        &r#"
5177            <!-- ˇ<script> -->
5178                // ˇvar x = new Y();
5179            <!-- ˇ</script> -->
5180        "#
5181        .unindent(),
5182    );
5183}
5184
5185#[gpui::test]
5186fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
5187    init_test(cx, |_| {});
5188
5189    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5190    let multibuffer = cx.add_model(|cx| {
5191        let mut multibuffer = MultiBuffer::new(0);
5192        multibuffer.push_excerpts(
5193            buffer.clone(),
5194            [
5195                ExcerptRange {
5196                    context: Point::new(0, 0)..Point::new(0, 4),
5197                    primary: None,
5198                },
5199                ExcerptRange {
5200                    context: Point::new(1, 0)..Point::new(1, 4),
5201                    primary: None,
5202                },
5203            ],
5204            cx,
5205        );
5206        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
5207        multibuffer
5208    });
5209
5210    let (_, view) = cx.add_window(|cx| build_editor(multibuffer, cx));
5211    view.update(cx, |view, cx| {
5212        assert_eq!(view.text(cx), "aaaa\nbbbb");
5213        view.change_selections(None, cx, |s| {
5214            s.select_ranges([
5215                Point::new(0, 0)..Point::new(0, 0),
5216                Point::new(1, 0)..Point::new(1, 0),
5217            ])
5218        });
5219
5220        view.handle_input("X", cx);
5221        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
5222        assert_eq!(
5223            view.selections.ranges(cx),
5224            [
5225                Point::new(0, 1)..Point::new(0, 1),
5226                Point::new(1, 1)..Point::new(1, 1),
5227            ]
5228        )
5229    });
5230}
5231
5232#[gpui::test]
5233fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
5234    init_test(cx, |_| {});
5235
5236    let markers = vec![('[', ']').into(), ('(', ')').into()];
5237    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
5238        indoc! {"
5239            [aaaa
5240            (bbbb]
5241            cccc)",
5242        },
5243        markers.clone(),
5244    );
5245    let excerpt_ranges = markers.into_iter().map(|marker| {
5246        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
5247        ExcerptRange {
5248            context,
5249            primary: None,
5250        }
5251    });
5252    let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
5253    let multibuffer = cx.add_model(|cx| {
5254        let mut multibuffer = MultiBuffer::new(0);
5255        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
5256        multibuffer
5257    });
5258
5259    let (_, view) = cx.add_window(|cx| build_editor(multibuffer, cx));
5260    view.update(cx, |view, cx| {
5261        let (expected_text, selection_ranges) = marked_text_ranges(
5262            indoc! {"
5263                aaaa
5264                bˇbbb
5265                bˇbbˇb
5266                cccc"
5267            },
5268            true,
5269        );
5270        assert_eq!(view.text(cx), expected_text);
5271        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
5272
5273        view.handle_input("X", cx);
5274
5275        let (expected_text, expected_selections) = marked_text_ranges(
5276            indoc! {"
5277                aaaa
5278                bXˇbbXb
5279                bXˇbbXˇb
5280                cccc"
5281            },
5282            false,
5283        );
5284        assert_eq!(view.text(cx), expected_text);
5285        assert_eq!(view.selections.ranges(cx), expected_selections);
5286
5287        view.newline(&Newline, cx);
5288        let (expected_text, expected_selections) = marked_text_ranges(
5289            indoc! {"
5290                aaaa
5291                bX
5292                ˇbbX
5293                b
5294                bX
5295                ˇbbX
5296                ˇb
5297                cccc"
5298            },
5299            false,
5300        );
5301        assert_eq!(view.text(cx), expected_text);
5302        assert_eq!(view.selections.ranges(cx), expected_selections);
5303    });
5304}
5305
5306#[gpui::test]
5307fn test_refresh_selections(cx: &mut TestAppContext) {
5308    init_test(cx, |_| {});
5309
5310    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5311    let mut excerpt1_id = None;
5312    let multibuffer = cx.add_model(|cx| {
5313        let mut multibuffer = MultiBuffer::new(0);
5314        excerpt1_id = multibuffer
5315            .push_excerpts(
5316                buffer.clone(),
5317                [
5318                    ExcerptRange {
5319                        context: Point::new(0, 0)..Point::new(1, 4),
5320                        primary: None,
5321                    },
5322                    ExcerptRange {
5323                        context: Point::new(1, 0)..Point::new(2, 4),
5324                        primary: None,
5325                    },
5326                ],
5327                cx,
5328            )
5329            .into_iter()
5330            .next();
5331        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
5332        multibuffer
5333    });
5334
5335    let (_, editor) = cx.add_window(|cx| {
5336        let mut editor = build_editor(multibuffer.clone(), cx);
5337        let snapshot = editor.snapshot(cx);
5338        editor.change_selections(None, cx, |s| {
5339            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
5340        });
5341        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
5342        assert_eq!(
5343            editor.selections.ranges(cx),
5344            [
5345                Point::new(1, 3)..Point::new(1, 3),
5346                Point::new(2, 1)..Point::new(2, 1),
5347            ]
5348        );
5349        editor
5350    });
5351
5352    // Refreshing selections is a no-op when excerpts haven't changed.
5353    editor.update(cx, |editor, cx| {
5354        editor.change_selections(None, cx, |s| s.refresh());
5355        assert_eq!(
5356            editor.selections.ranges(cx),
5357            [
5358                Point::new(1, 3)..Point::new(1, 3),
5359                Point::new(2, 1)..Point::new(2, 1),
5360            ]
5361        );
5362    });
5363
5364    multibuffer.update(cx, |multibuffer, cx| {
5365        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
5366    });
5367    editor.update(cx, |editor, cx| {
5368        // Removing an excerpt causes the first selection to become degenerate.
5369        assert_eq!(
5370            editor.selections.ranges(cx),
5371            [
5372                Point::new(0, 0)..Point::new(0, 0),
5373                Point::new(0, 1)..Point::new(0, 1)
5374            ]
5375        );
5376
5377        // Refreshing selections will relocate the first selection to the original buffer
5378        // location.
5379        editor.change_selections(None, cx, |s| s.refresh());
5380        assert_eq!(
5381            editor.selections.ranges(cx),
5382            [
5383                Point::new(0, 1)..Point::new(0, 1),
5384                Point::new(0, 3)..Point::new(0, 3)
5385            ]
5386        );
5387        assert!(editor.selections.pending_anchor().is_some());
5388    });
5389}
5390
5391#[gpui::test]
5392fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
5393    init_test(cx, |_| {});
5394
5395    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5396    let mut excerpt1_id = None;
5397    let multibuffer = cx.add_model(|cx| {
5398        let mut multibuffer = MultiBuffer::new(0);
5399        excerpt1_id = multibuffer
5400            .push_excerpts(
5401                buffer.clone(),
5402                [
5403                    ExcerptRange {
5404                        context: Point::new(0, 0)..Point::new(1, 4),
5405                        primary: None,
5406                    },
5407                    ExcerptRange {
5408                        context: Point::new(1, 0)..Point::new(2, 4),
5409                        primary: None,
5410                    },
5411                ],
5412                cx,
5413            )
5414            .into_iter()
5415            .next();
5416        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
5417        multibuffer
5418    });
5419
5420    let (_, editor) = cx.add_window(|cx| {
5421        let mut editor = build_editor(multibuffer.clone(), cx);
5422        let snapshot = editor.snapshot(cx);
5423        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
5424        assert_eq!(
5425            editor.selections.ranges(cx),
5426            [Point::new(1, 3)..Point::new(1, 3)]
5427        );
5428        editor
5429    });
5430
5431    multibuffer.update(cx, |multibuffer, cx| {
5432        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
5433    });
5434    editor.update(cx, |editor, cx| {
5435        assert_eq!(
5436            editor.selections.ranges(cx),
5437            [Point::new(0, 0)..Point::new(0, 0)]
5438        );
5439
5440        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
5441        editor.change_selections(None, cx, |s| s.refresh());
5442        assert_eq!(
5443            editor.selections.ranges(cx),
5444            [Point::new(0, 3)..Point::new(0, 3)]
5445        );
5446        assert!(editor.selections.pending_anchor().is_some());
5447    });
5448}
5449
5450#[gpui::test]
5451async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
5452    init_test(cx, |_| {});
5453
5454    let language = Arc::new(
5455        Language::new(
5456            LanguageConfig {
5457                brackets: BracketPairConfig {
5458                    pairs: vec![
5459                        BracketPair {
5460                            start: "{".to_string(),
5461                            end: "}".to_string(),
5462                            close: true,
5463                            newline: true,
5464                        },
5465                        BracketPair {
5466                            start: "/* ".to_string(),
5467                            end: " */".to_string(),
5468                            close: true,
5469                            newline: true,
5470                        },
5471                    ],
5472                    ..Default::default()
5473                },
5474                ..Default::default()
5475            },
5476            Some(tree_sitter_rust::language()),
5477        )
5478        .with_indents_query("")
5479        .unwrap(),
5480    );
5481
5482    let text = concat!(
5483        "{   }\n",     //
5484        "  x\n",       //
5485        "  /*   */\n", //
5486        "x\n",         //
5487        "{{} }\n",     //
5488    );
5489
5490    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
5491    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
5492    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
5493    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
5494        .await;
5495
5496    view.update(cx, |view, cx| {
5497        view.change_selections(None, cx, |s| {
5498            s.select_display_ranges([
5499                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
5500                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
5501                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
5502            ])
5503        });
5504        view.newline(&Newline, cx);
5505
5506        assert_eq!(
5507            view.buffer().read(cx).read(cx).text(),
5508            concat!(
5509                "{ \n",    // Suppress rustfmt
5510                "\n",      //
5511                "}\n",     //
5512                "  x\n",   //
5513                "  /* \n", //
5514                "  \n",    //
5515                "  */\n",  //
5516                "x\n",     //
5517                "{{} \n",  //
5518                "}\n",     //
5519            )
5520        );
5521    });
5522}
5523
5524#[gpui::test]
5525fn test_highlighted_ranges(cx: &mut TestAppContext) {
5526    init_test(cx, |_| {});
5527
5528    let (_, editor) = cx.add_window(|cx| {
5529        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
5530        build_editor(buffer.clone(), cx)
5531    });
5532
5533    editor.update(cx, |editor, cx| {
5534        struct Type1;
5535        struct Type2;
5536
5537        let buffer = editor.buffer.read(cx).snapshot(cx);
5538
5539        let anchor_range =
5540            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
5541
5542        editor.highlight_background::<Type1>(
5543            vec![
5544                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
5545                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
5546                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
5547                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
5548            ],
5549            |_| Color::red(),
5550            cx,
5551        );
5552        editor.highlight_background::<Type2>(
5553            vec![
5554                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
5555                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
5556                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
5557                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
5558            ],
5559            |_| Color::green(),
5560            cx,
5561        );
5562
5563        let snapshot = editor.snapshot(cx);
5564        let mut highlighted_ranges = editor.background_highlights_in_range(
5565            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
5566            &snapshot,
5567            theme::current(cx).as_ref(),
5568        );
5569        // Enforce a consistent ordering based on color without relying on the ordering of the
5570        // highlight's `TypeId` which is non-deterministic.
5571        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
5572        assert_eq!(
5573            highlighted_ranges,
5574            &[
5575                (
5576                    DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
5577                    Color::green(),
5578                ),
5579                (
5580                    DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
5581                    Color::green(),
5582                ),
5583                (
5584                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
5585                    Color::red(),
5586                ),
5587                (
5588                    DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
5589                    Color::red(),
5590                ),
5591            ]
5592        );
5593        assert_eq!(
5594            editor.background_highlights_in_range(
5595                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
5596                &snapshot,
5597                theme::current(cx).as_ref(),
5598            ),
5599            &[(
5600                DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
5601                Color::red(),
5602            )]
5603        );
5604    });
5605}
5606
5607#[gpui::test]
5608async fn test_following(cx: &mut gpui::TestAppContext) {
5609    init_test(cx, |_| {});
5610
5611    let fs = FakeFs::new(cx.background());
5612    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5613
5614    let buffer = project.update(cx, |project, cx| {
5615        let buffer = project
5616            .create_buffer(&sample_text(16, 8, 'a'), None, cx)
5617            .unwrap();
5618        cx.add_model(|cx| MultiBuffer::singleton(buffer, cx))
5619    });
5620    let (_, leader) = cx.add_window(|cx| build_editor(buffer.clone(), cx));
5621    let (_, follower) = cx.update(|cx| {
5622        cx.add_window(
5623            WindowOptions {
5624                bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
5625                ..Default::default()
5626            },
5627            |cx| build_editor(buffer.clone(), cx),
5628        )
5629    });
5630
5631    let is_still_following = Rc::new(RefCell::new(true));
5632    let follower_edit_event_count = Rc::new(RefCell::new(0));
5633    let pending_update = Rc::new(RefCell::new(None));
5634    follower.update(cx, {
5635        let update = pending_update.clone();
5636        let is_still_following = is_still_following.clone();
5637        let follower_edit_event_count = follower_edit_event_count.clone();
5638        |_, cx| {
5639            cx.subscribe(&leader, move |_, leader, event, cx| {
5640                leader
5641                    .read(cx)
5642                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
5643            })
5644            .detach();
5645
5646            cx.subscribe(&follower, move |_, _, event, cx| {
5647                if Editor::should_unfollow_on_event(event, cx) {
5648                    *is_still_following.borrow_mut() = false;
5649                }
5650                if let Event::BufferEdited = event {
5651                    *follower_edit_event_count.borrow_mut() += 1;
5652                }
5653            })
5654            .detach();
5655        }
5656    });
5657
5658    // Update the selections only
5659    leader.update(cx, |leader, cx| {
5660        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
5661    });
5662    follower
5663        .update(cx, |follower, cx| {
5664            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5665        })
5666        .await
5667        .unwrap();
5668    follower.read_with(cx, |follower, cx| {
5669        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
5670    });
5671    assert_eq!(*is_still_following.borrow(), true);
5672    assert_eq!(*follower_edit_event_count.borrow(), 0);
5673
5674    // Update the scroll position only
5675    leader.update(cx, |leader, cx| {
5676        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
5677    });
5678    follower
5679        .update(cx, |follower, cx| {
5680            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5681        })
5682        .await
5683        .unwrap();
5684    assert_eq!(
5685        follower.update(cx, |follower, cx| follower.scroll_position(cx)),
5686        vec2f(1.5, 3.5)
5687    );
5688    assert_eq!(*is_still_following.borrow(), true);
5689    assert_eq!(*follower_edit_event_count.borrow(), 0);
5690
5691    // Update the selections and scroll position. The follower's scroll position is updated
5692    // via autoscroll, not via the leader's exact scroll position.
5693    leader.update(cx, |leader, cx| {
5694        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
5695        leader.request_autoscroll(Autoscroll::newest(), cx);
5696        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
5697    });
5698    follower
5699        .update(cx, |follower, cx| {
5700            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5701        })
5702        .await
5703        .unwrap();
5704    follower.update(cx, |follower, cx| {
5705        assert_eq!(follower.scroll_position(cx), vec2f(1.5, 0.0));
5706        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
5707    });
5708    assert_eq!(*is_still_following.borrow(), true);
5709
5710    // Creating a pending selection that precedes another selection
5711    leader.update(cx, |leader, cx| {
5712        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
5713        leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
5714    });
5715    follower
5716        .update(cx, |follower, cx| {
5717            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5718        })
5719        .await
5720        .unwrap();
5721    follower.read_with(cx, |follower, cx| {
5722        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
5723    });
5724    assert_eq!(*is_still_following.borrow(), true);
5725
5726    // Extend the pending selection so that it surrounds another selection
5727    leader.update(cx, |leader, cx| {
5728        leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
5729    });
5730    follower
5731        .update(cx, |follower, cx| {
5732            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5733        })
5734        .await
5735        .unwrap();
5736    follower.read_with(cx, |follower, cx| {
5737        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
5738    });
5739
5740    // Scrolling locally breaks the follow
5741    follower.update(cx, |follower, cx| {
5742        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
5743        follower.set_scroll_anchor(
5744            ScrollAnchor {
5745                top_anchor,
5746                offset: vec2f(0.0, 0.5),
5747            },
5748            cx,
5749        );
5750    });
5751    assert_eq!(*is_still_following.borrow(), false);
5752}
5753
5754#[gpui::test]
5755async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
5756    init_test(cx, |_| {});
5757
5758    let fs = FakeFs::new(cx.background());
5759    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5760    let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
5761    let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
5762
5763    let leader = pane.update(cx, |_, cx| {
5764        let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
5765        cx.add_view(|cx| build_editor(multibuffer.clone(), cx))
5766    });
5767
5768    // Start following the editor when it has no excerpts.
5769    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
5770    let follower_1 = cx
5771        .update(|cx| {
5772            Editor::from_state_proto(
5773                pane.clone(),
5774                project.clone(),
5775                ViewId {
5776                    creator: Default::default(),
5777                    id: 0,
5778                },
5779                &mut state_message,
5780                cx,
5781            )
5782        })
5783        .unwrap()
5784        .await
5785        .unwrap();
5786
5787    let update_message = Rc::new(RefCell::new(None));
5788    follower_1.update(cx, {
5789        let update = update_message.clone();
5790        |_, cx| {
5791            cx.subscribe(&leader, move |_, leader, event, cx| {
5792                leader
5793                    .read(cx)
5794                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
5795            })
5796            .detach();
5797        }
5798    });
5799
5800    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
5801        (
5802            project
5803                .create_buffer("abc\ndef\nghi\njkl\n", None, cx)
5804                .unwrap(),
5805            project
5806                .create_buffer("mno\npqr\nstu\nvwx\n", None, cx)
5807                .unwrap(),
5808        )
5809    });
5810
5811    // Insert some excerpts.
5812    leader.update(cx, |leader, cx| {
5813        leader.buffer.update(cx, |multibuffer, cx| {
5814            let excerpt_ids = multibuffer.push_excerpts(
5815                buffer_1.clone(),
5816                [
5817                    ExcerptRange {
5818                        context: 1..6,
5819                        primary: None,
5820                    },
5821                    ExcerptRange {
5822                        context: 12..15,
5823                        primary: None,
5824                    },
5825                    ExcerptRange {
5826                        context: 0..3,
5827                        primary: None,
5828                    },
5829                ],
5830                cx,
5831            );
5832            multibuffer.insert_excerpts_after(
5833                excerpt_ids[0],
5834                buffer_2.clone(),
5835                [
5836                    ExcerptRange {
5837                        context: 8..12,
5838                        primary: None,
5839                    },
5840                    ExcerptRange {
5841                        context: 0..6,
5842                        primary: None,
5843                    },
5844                ],
5845                cx,
5846            );
5847        });
5848    });
5849
5850    // Apply the update of adding the excerpts.
5851    follower_1
5852        .update(cx, |follower, cx| {
5853            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
5854        })
5855        .await
5856        .unwrap();
5857    assert_eq!(
5858        follower_1.read_with(cx, |editor, cx| editor.text(cx)),
5859        leader.read_with(cx, |editor, cx| editor.text(cx))
5860    );
5861    update_message.borrow_mut().take();
5862
5863    // Start following separately after it already has excerpts.
5864    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
5865    let follower_2 = cx
5866        .update(|cx| {
5867            Editor::from_state_proto(
5868                pane.clone(),
5869                project.clone(),
5870                ViewId {
5871                    creator: Default::default(),
5872                    id: 0,
5873                },
5874                &mut state_message,
5875                cx,
5876            )
5877        })
5878        .unwrap()
5879        .await
5880        .unwrap();
5881    assert_eq!(
5882        follower_2.read_with(cx, |editor, cx| editor.text(cx)),
5883        leader.read_with(cx, |editor, cx| editor.text(cx))
5884    );
5885
5886    // Remove some excerpts.
5887    leader.update(cx, |leader, cx| {
5888        leader.buffer.update(cx, |multibuffer, cx| {
5889            let excerpt_ids = multibuffer.excerpt_ids();
5890            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
5891            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
5892        });
5893    });
5894
5895    // Apply the update of removing the excerpts.
5896    follower_1
5897        .update(cx, |follower, cx| {
5898            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
5899        })
5900        .await
5901        .unwrap();
5902    follower_2
5903        .update(cx, |follower, cx| {
5904            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
5905        })
5906        .await
5907        .unwrap();
5908    update_message.borrow_mut().take();
5909    assert_eq!(
5910        follower_1.read_with(cx, |editor, cx| editor.text(cx)),
5911        leader.read_with(cx, |editor, cx| editor.text(cx))
5912    );
5913}
5914
5915#[test]
5916fn test_combine_syntax_and_fuzzy_match_highlights() {
5917    let string = "abcdefghijklmnop";
5918    let syntax_ranges = [
5919        (
5920            0..3,
5921            HighlightStyle {
5922                color: Some(Color::red()),
5923                ..Default::default()
5924            },
5925        ),
5926        (
5927            4..8,
5928            HighlightStyle {
5929                color: Some(Color::green()),
5930                ..Default::default()
5931            },
5932        ),
5933    ];
5934    let match_indices = [4, 6, 7, 8];
5935    assert_eq!(
5936        combine_syntax_and_fuzzy_match_highlights(
5937            string,
5938            Default::default(),
5939            syntax_ranges.into_iter(),
5940            &match_indices,
5941        ),
5942        &[
5943            (
5944                0..3,
5945                HighlightStyle {
5946                    color: Some(Color::red()),
5947                    ..Default::default()
5948                },
5949            ),
5950            (
5951                4..5,
5952                HighlightStyle {
5953                    color: Some(Color::green()),
5954                    weight: Some(fonts::Weight::BOLD),
5955                    ..Default::default()
5956                },
5957            ),
5958            (
5959                5..6,
5960                HighlightStyle {
5961                    color: Some(Color::green()),
5962                    ..Default::default()
5963                },
5964            ),
5965            (
5966                6..8,
5967                HighlightStyle {
5968                    color: Some(Color::green()),
5969                    weight: Some(fonts::Weight::BOLD),
5970                    ..Default::default()
5971                },
5972            ),
5973            (
5974                8..9,
5975                HighlightStyle {
5976                    weight: Some(fonts::Weight::BOLD),
5977                    ..Default::default()
5978                },
5979            ),
5980        ]
5981    );
5982}
5983
5984#[gpui::test]
5985async fn go_to_hunk(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
5986    init_test(cx, |_| {});
5987
5988    let mut cx = EditorTestContext::new(cx).await;
5989
5990    let diff_base = r#"
5991        use some::mod;
5992
5993        const A: u32 = 42;
5994
5995        fn main() {
5996            println!("hello");
5997
5998            println!("world");
5999        }
6000        "#
6001    .unindent();
6002
6003    // Edits are modified, removed, modified, added
6004    cx.set_state(
6005        &r#"
6006        use some::modified;
6007
6008        ˇ
6009        fn main() {
6010            println!("hello there");
6011
6012            println!("around the");
6013            println!("world");
6014        }
6015        "#
6016        .unindent(),
6017    );
6018
6019    cx.set_diff_base(Some(&diff_base));
6020    deterministic.run_until_parked();
6021
6022    cx.update_editor(|editor, cx| {
6023        //Wrap around the bottom of the buffer
6024        for _ in 0..3 {
6025            editor.go_to_hunk(&GoToHunk, cx);
6026        }
6027    });
6028
6029    cx.assert_editor_state(
6030        &r#"
6031        ˇuse some::modified;
6032
6033
6034        fn main() {
6035            println!("hello there");
6036
6037            println!("around the");
6038            println!("world");
6039        }
6040        "#
6041        .unindent(),
6042    );
6043
6044    cx.update_editor(|editor, cx| {
6045        //Wrap around the top of the buffer
6046        for _ in 0..2 {
6047            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
6048        }
6049    });
6050
6051    cx.assert_editor_state(
6052        &r#"
6053        use some::modified;
6054
6055
6056        fn main() {
6057        ˇ    println!("hello there");
6058
6059            println!("around the");
6060            println!("world");
6061        }
6062        "#
6063        .unindent(),
6064    );
6065
6066    cx.update_editor(|editor, cx| {
6067        editor.fold(&Fold, cx);
6068
6069        //Make sure that the fold only gets one hunk
6070        for _ in 0..4 {
6071            editor.go_to_hunk(&GoToHunk, cx);
6072        }
6073    });
6074
6075    cx.assert_editor_state(
6076        &r#"
6077        ˇuse some::modified;
6078
6079
6080        fn main() {
6081            println!("hello there");
6082
6083            println!("around the");
6084            println!("world");
6085        }
6086        "#
6087        .unindent(),
6088    );
6089}
6090
6091#[test]
6092fn test_split_words() {
6093    fn split<'a>(text: &'a str) -> Vec<&'a str> {
6094        split_words(text).collect()
6095    }
6096
6097    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
6098    assert_eq!(split("hello_world"), &["hello_", "world"]);
6099    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
6100    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
6101    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
6102    assert_eq!(split("helloworld"), &["helloworld"]);
6103}
6104
6105#[gpui::test]
6106async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
6107    init_test(cx, |_| {});
6108
6109    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
6110    let mut assert = |before, after| {
6111        let _state_context = cx.set_state(before);
6112        cx.update_editor(|editor, cx| {
6113            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
6114        });
6115        cx.assert_editor_state(after);
6116    };
6117
6118    // Outside bracket jumps to outside of matching bracket
6119    assert("console.logˇ(var);", "console.log(var)ˇ;");
6120    assert("console.log(var)ˇ;", "console.logˇ(var);");
6121
6122    // Inside bracket jumps to inside of matching bracket
6123    assert("console.log(ˇvar);", "console.log(varˇ);");
6124    assert("console.log(varˇ);", "console.log(ˇvar);");
6125
6126    // When outside a bracket and inside, favor jumping to the inside bracket
6127    assert(
6128        "console.log('foo', [1, 2, 3]ˇ);",
6129        "console.log(ˇ'foo', [1, 2, 3]);",
6130    );
6131    assert(
6132        "console.log(ˇ'foo', [1, 2, 3]);",
6133        "console.log('foo', [1, 2, 3]ˇ);",
6134    );
6135
6136    // Bias forward if two options are equally likely
6137    assert(
6138        "let result = curried_fun()ˇ();",
6139        "let result = curried_fun()()ˇ;",
6140    );
6141
6142    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
6143    assert(
6144        indoc! {"
6145            function test() {
6146                console.log('test')ˇ
6147            }"},
6148        indoc! {"
6149            function test() {
6150                console.logˇ('test')
6151            }"},
6152    );
6153}
6154
6155#[gpui::test(iterations = 10)]
6156async fn test_copilot(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
6157    init_test(cx, |_| {});
6158
6159    let (copilot, copilot_lsp) = Copilot::fake(cx);
6160    cx.update(|cx| cx.set_global(copilot));
6161    let mut cx = EditorLspTestContext::new_rust(
6162        lsp::ServerCapabilities {
6163            completion_provider: Some(lsp::CompletionOptions {
6164                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
6165                ..Default::default()
6166            }),
6167            ..Default::default()
6168        },
6169        cx,
6170    )
6171    .await;
6172
6173    // When inserting, ensure autocompletion is favored over Copilot suggestions.
6174    cx.set_state(indoc! {"
6175        oneˇ
6176        two
6177        three
6178    "});
6179    cx.simulate_keystroke(".");
6180    let _ = handle_completion_request(
6181        &mut cx,
6182        indoc! {"
6183            one.|<>
6184            two
6185            three
6186        "},
6187        vec!["completion_a", "completion_b"],
6188    );
6189    handle_copilot_completion_request(
6190        &copilot_lsp,
6191        vec![copilot::request::Completion {
6192            text: "one.copilot1".into(),
6193            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6194            ..Default::default()
6195        }],
6196        vec![],
6197    );
6198    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6199    cx.update_editor(|editor, cx| {
6200        assert!(editor.context_menu_visible());
6201        assert!(!editor.has_active_copilot_suggestion(cx));
6202
6203        // Confirming a completion inserts it and hides the context menu, without showing
6204        // the copilot suggestion afterwards.
6205        editor
6206            .confirm_completion(&Default::default(), cx)
6207            .unwrap()
6208            .detach();
6209        assert!(!editor.context_menu_visible());
6210        assert!(!editor.has_active_copilot_suggestion(cx));
6211        assert_eq!(editor.text(cx), "one.completion_a\ntwo\nthree\n");
6212        assert_eq!(editor.display_text(cx), "one.completion_a\ntwo\nthree\n");
6213    });
6214
6215    // Ensure Copilot suggestions are shown right away if no autocompletion is available.
6216    cx.set_state(indoc! {"
6217        oneˇ
6218        two
6219        three
6220    "});
6221    cx.simulate_keystroke(".");
6222    let _ = handle_completion_request(
6223        &mut cx,
6224        indoc! {"
6225            one.|<>
6226            two
6227            three
6228        "},
6229        vec![],
6230    );
6231    handle_copilot_completion_request(
6232        &copilot_lsp,
6233        vec![copilot::request::Completion {
6234            text: "one.copilot1".into(),
6235            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6236            ..Default::default()
6237        }],
6238        vec![],
6239    );
6240    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6241    cx.update_editor(|editor, cx| {
6242        assert!(!editor.context_menu_visible());
6243        assert!(editor.has_active_copilot_suggestion(cx));
6244        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
6245        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
6246    });
6247
6248    // Reset editor, and ensure autocompletion is still favored over Copilot suggestions.
6249    cx.set_state(indoc! {"
6250        oneˇ
6251        two
6252        three
6253    "});
6254    cx.simulate_keystroke(".");
6255    let _ = handle_completion_request(
6256        &mut cx,
6257        indoc! {"
6258            one.|<>
6259            two
6260            three
6261        "},
6262        vec!["completion_a", "completion_b"],
6263    );
6264    handle_copilot_completion_request(
6265        &copilot_lsp,
6266        vec![copilot::request::Completion {
6267            text: "one.copilot1".into(),
6268            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6269            ..Default::default()
6270        }],
6271        vec![],
6272    );
6273    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6274    cx.update_editor(|editor, cx| {
6275        assert!(editor.context_menu_visible());
6276        assert!(!editor.has_active_copilot_suggestion(cx));
6277
6278        // When hiding the context menu, the Copilot suggestion becomes visible.
6279        editor.hide_context_menu(cx);
6280        assert!(!editor.context_menu_visible());
6281        assert!(editor.has_active_copilot_suggestion(cx));
6282        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
6283        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
6284    });
6285
6286    // Ensure existing completion is interpolated when inserting again.
6287    cx.simulate_keystroke("c");
6288    deterministic.run_until_parked();
6289    cx.update_editor(|editor, cx| {
6290        assert!(!editor.context_menu_visible());
6291        assert!(editor.has_active_copilot_suggestion(cx));
6292        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
6293        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6294    });
6295
6296    // After debouncing, new Copilot completions should be requested.
6297    handle_copilot_completion_request(
6298        &copilot_lsp,
6299        vec![copilot::request::Completion {
6300            text: "one.copilot2".into(),
6301            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 5)),
6302            ..Default::default()
6303        }],
6304        vec![],
6305    );
6306    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6307    cx.update_editor(|editor, cx| {
6308        assert!(!editor.context_menu_visible());
6309        assert!(editor.has_active_copilot_suggestion(cx));
6310        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6311        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6312
6313        // Canceling should remove the active Copilot suggestion.
6314        editor.cancel(&Default::default(), cx);
6315        assert!(!editor.has_active_copilot_suggestion(cx));
6316        assert_eq!(editor.display_text(cx), "one.c\ntwo\nthree\n");
6317        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6318
6319        // After canceling, tabbing shouldn't insert the previously shown suggestion.
6320        editor.tab(&Default::default(), cx);
6321        assert!(!editor.has_active_copilot_suggestion(cx));
6322        assert_eq!(editor.display_text(cx), "one.c   \ntwo\nthree\n");
6323        assert_eq!(editor.text(cx), "one.c   \ntwo\nthree\n");
6324
6325        // When undoing the previously active suggestion is shown again.
6326        editor.undo(&Default::default(), cx);
6327        assert!(editor.has_active_copilot_suggestion(cx));
6328        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6329        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6330    });
6331
6332    // If an edit occurs outside of this editor, the suggestion is still correctly interpolated.
6333    cx.update_buffer(|buffer, cx| buffer.edit([(5..5, "o")], None, cx));
6334    cx.update_editor(|editor, cx| {
6335        assert!(editor.has_active_copilot_suggestion(cx));
6336        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6337        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6338
6339        // Tabbing when there is an active suggestion inserts it.
6340        editor.tab(&Default::default(), cx);
6341        assert!(!editor.has_active_copilot_suggestion(cx));
6342        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6343        assert_eq!(editor.text(cx), "one.copilot2\ntwo\nthree\n");
6344
6345        // When undoing the previously active suggestion is shown again.
6346        editor.undo(&Default::default(), cx);
6347        assert!(editor.has_active_copilot_suggestion(cx));
6348        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6349        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6350
6351        // Hide suggestion.
6352        editor.cancel(&Default::default(), cx);
6353        assert!(!editor.has_active_copilot_suggestion(cx));
6354        assert_eq!(editor.display_text(cx), "one.co\ntwo\nthree\n");
6355        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6356    });
6357
6358    // If an edit occurs outside of this editor but no suggestion is being shown,
6359    // we won't make it visible.
6360    cx.update_buffer(|buffer, cx| buffer.edit([(6..6, "p")], None, cx));
6361    cx.update_editor(|editor, cx| {
6362        assert!(!editor.has_active_copilot_suggestion(cx));
6363        assert_eq!(editor.display_text(cx), "one.cop\ntwo\nthree\n");
6364        assert_eq!(editor.text(cx), "one.cop\ntwo\nthree\n");
6365    });
6366
6367    // Reset the editor to verify how suggestions behave when tabbing on leading indentation.
6368    cx.update_editor(|editor, cx| {
6369        editor.set_text("fn foo() {\n  \n}", cx);
6370        editor.change_selections(None, cx, |s| {
6371            s.select_ranges([Point::new(1, 2)..Point::new(1, 2)])
6372        });
6373    });
6374    handle_copilot_completion_request(
6375        &copilot_lsp,
6376        vec![copilot::request::Completion {
6377            text: "    let x = 4;".into(),
6378            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
6379            ..Default::default()
6380        }],
6381        vec![],
6382    );
6383
6384    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
6385    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6386    cx.update_editor(|editor, cx| {
6387        assert!(editor.has_active_copilot_suggestion(cx));
6388        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6389        assert_eq!(editor.text(cx), "fn foo() {\n  \n}");
6390
6391        // Tabbing inside of leading whitespace inserts indentation without accepting the suggestion.
6392        editor.tab(&Default::default(), cx);
6393        assert!(editor.has_active_copilot_suggestion(cx));
6394        assert_eq!(editor.text(cx), "fn foo() {\n    \n}");
6395        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6396
6397        // Tabbing again accepts the suggestion.
6398        editor.tab(&Default::default(), cx);
6399        assert!(!editor.has_active_copilot_suggestion(cx));
6400        assert_eq!(editor.text(cx), "fn foo() {\n    let x = 4;\n}");
6401        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6402    });
6403}
6404
6405#[gpui::test]
6406async fn test_copilot_completion_invalidation(
6407    deterministic: Arc<Deterministic>,
6408    cx: &mut gpui::TestAppContext,
6409) {
6410    init_test(cx, |_| {});
6411
6412    let (copilot, copilot_lsp) = Copilot::fake(cx);
6413    cx.update(|cx| cx.set_global(copilot));
6414    let mut cx = EditorLspTestContext::new_rust(
6415        lsp::ServerCapabilities {
6416            completion_provider: Some(lsp::CompletionOptions {
6417                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
6418                ..Default::default()
6419            }),
6420            ..Default::default()
6421        },
6422        cx,
6423    )
6424    .await;
6425
6426    cx.set_state(indoc! {"
6427        one
6428        twˇ
6429        three
6430    "});
6431
6432    handle_copilot_completion_request(
6433        &copilot_lsp,
6434        vec![copilot::request::Completion {
6435            text: "two.foo()".into(),
6436            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
6437            ..Default::default()
6438        }],
6439        vec![],
6440    );
6441    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
6442    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6443    cx.update_editor(|editor, cx| {
6444        assert!(editor.has_active_copilot_suggestion(cx));
6445        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6446        assert_eq!(editor.text(cx), "one\ntw\nthree\n");
6447
6448        editor.backspace(&Default::default(), cx);
6449        assert!(editor.has_active_copilot_suggestion(cx));
6450        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6451        assert_eq!(editor.text(cx), "one\nt\nthree\n");
6452
6453        editor.backspace(&Default::default(), cx);
6454        assert!(editor.has_active_copilot_suggestion(cx));
6455        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6456        assert_eq!(editor.text(cx), "one\n\nthree\n");
6457
6458        // Deleting across the original suggestion range invalidates it.
6459        editor.backspace(&Default::default(), cx);
6460        assert!(!editor.has_active_copilot_suggestion(cx));
6461        assert_eq!(editor.display_text(cx), "one\nthree\n");
6462        assert_eq!(editor.text(cx), "one\nthree\n");
6463
6464        // Undoing the deletion restores the suggestion.
6465        editor.undo(&Default::default(), cx);
6466        assert!(editor.has_active_copilot_suggestion(cx));
6467        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6468        assert_eq!(editor.text(cx), "one\n\nthree\n");
6469    });
6470}
6471
6472#[gpui::test]
6473async fn test_copilot_multibuffer(
6474    deterministic: Arc<Deterministic>,
6475    cx: &mut gpui::TestAppContext,
6476) {
6477    init_test(cx, |_| {});
6478
6479    let (copilot, copilot_lsp) = Copilot::fake(cx);
6480    cx.update(|cx| cx.set_global(copilot));
6481
6482    let buffer_1 = cx.add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx));
6483    let buffer_2 = cx.add_model(|cx| Buffer::new(0, "c = 3\nd = 4\n", cx));
6484    let multibuffer = cx.add_model(|cx| {
6485        let mut multibuffer = MultiBuffer::new(0);
6486        multibuffer.push_excerpts(
6487            buffer_1.clone(),
6488            [ExcerptRange {
6489                context: Point::new(0, 0)..Point::new(2, 0),
6490                primary: None,
6491            }],
6492            cx,
6493        );
6494        multibuffer.push_excerpts(
6495            buffer_2.clone(),
6496            [ExcerptRange {
6497                context: Point::new(0, 0)..Point::new(2, 0),
6498                primary: None,
6499            }],
6500            cx,
6501        );
6502        multibuffer
6503    });
6504    let (_, editor) = cx.add_window(|cx| build_editor(multibuffer, cx));
6505
6506    handle_copilot_completion_request(
6507        &copilot_lsp,
6508        vec![copilot::request::Completion {
6509            text: "b = 2 + a".into(),
6510            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 5)),
6511            ..Default::default()
6512        }],
6513        vec![],
6514    );
6515    editor.update(cx, |editor, cx| {
6516        // Ensure copilot suggestions are shown for the first excerpt.
6517        editor.change_selections(None, cx, |s| {
6518            s.select_ranges([Point::new(1, 5)..Point::new(1, 5)])
6519        });
6520        editor.next_copilot_suggestion(&Default::default(), cx);
6521    });
6522    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6523    editor.update(cx, |editor, cx| {
6524        assert!(editor.has_active_copilot_suggestion(cx));
6525        assert_eq!(
6526            editor.display_text(cx),
6527            "\n\na = 1\nb = 2 + a\n\n\n\nc = 3\nd = 4\n"
6528        );
6529        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
6530    });
6531
6532    handle_copilot_completion_request(
6533        &copilot_lsp,
6534        vec![copilot::request::Completion {
6535            text: "d = 4 + c".into(),
6536            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 6)),
6537            ..Default::default()
6538        }],
6539        vec![],
6540    );
6541    editor.update(cx, |editor, cx| {
6542        // Move to another excerpt, ensuring the suggestion gets cleared.
6543        editor.change_selections(None, cx, |s| {
6544            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
6545        });
6546        assert!(!editor.has_active_copilot_suggestion(cx));
6547        assert_eq!(
6548            editor.display_text(cx),
6549            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4\n"
6550        );
6551        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
6552
6553        // Type a character, ensuring we don't even try to interpolate the previous suggestion.
6554        editor.handle_input(" ", cx);
6555        assert!(!editor.has_active_copilot_suggestion(cx));
6556        assert_eq!(
6557            editor.display_text(cx),
6558            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 \n"
6559        );
6560        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
6561    });
6562
6563    // Ensure the new suggestion is displayed when the debounce timeout expires.
6564    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6565    editor.update(cx, |editor, cx| {
6566        assert!(editor.has_active_copilot_suggestion(cx));
6567        assert_eq!(
6568            editor.display_text(cx),
6569            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 + c\n"
6570        );
6571        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
6572    });
6573}
6574
6575#[gpui::test]
6576async fn test_copilot_disabled_globs(
6577    deterministic: Arc<Deterministic>,
6578    cx: &mut gpui::TestAppContext,
6579) {
6580    init_test(cx, |settings| {
6581        settings
6582            .copilot
6583            .get_or_insert(Default::default())
6584            .disabled_globs = Some(vec![".env*".to_string()]);
6585    });
6586
6587    let (copilot, copilot_lsp) = Copilot::fake(cx);
6588    cx.update(|cx| cx.set_global(copilot));
6589
6590    let fs = FakeFs::new(cx.background());
6591    fs.insert_tree(
6592        "/test",
6593        json!({
6594            ".env": "SECRET=something\n",
6595            "README.md": "hello\n"
6596        }),
6597    )
6598    .await;
6599    let project = Project::test(fs, ["/test".as_ref()], cx).await;
6600
6601    let private_buffer = project
6602        .update(cx, |project, cx| {
6603            project.open_local_buffer("/test/.env", cx)
6604        })
6605        .await
6606        .unwrap();
6607    let public_buffer = project
6608        .update(cx, |project, cx| {
6609            project.open_local_buffer("/test/README.md", cx)
6610        })
6611        .await
6612        .unwrap();
6613
6614    let multibuffer = cx.add_model(|cx| {
6615        let mut multibuffer = MultiBuffer::new(0);
6616        multibuffer.push_excerpts(
6617            private_buffer.clone(),
6618            [ExcerptRange {
6619                context: Point::new(0, 0)..Point::new(1, 0),
6620                primary: None,
6621            }],
6622            cx,
6623        );
6624        multibuffer.push_excerpts(
6625            public_buffer.clone(),
6626            [ExcerptRange {
6627                context: Point::new(0, 0)..Point::new(1, 0),
6628                primary: None,
6629            }],
6630            cx,
6631        );
6632        multibuffer
6633    });
6634    let (_, editor) = cx.add_window(|cx| build_editor(multibuffer, cx));
6635
6636    let mut copilot_requests = copilot_lsp
6637        .handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| async move {
6638            Ok(copilot::request::GetCompletionsResult {
6639                completions: vec![copilot::request::Completion {
6640                    text: "next line".into(),
6641                    range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
6642                    ..Default::default()
6643                }],
6644            })
6645        });
6646
6647    editor.update(cx, |editor, cx| {
6648        editor.change_selections(None, cx, |selections| {
6649            selections.select_ranges([Point::new(0, 0)..Point::new(0, 0)])
6650        });
6651        editor.next_copilot_suggestion(&Default::default(), cx);
6652    });
6653
6654    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6655    assert!(copilot_requests.try_next().is_err());
6656
6657    editor.update(cx, |editor, cx| {
6658        editor.change_selections(None, cx, |s| {
6659            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
6660        });
6661        editor.next_copilot_suggestion(&Default::default(), cx);
6662    });
6663
6664    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6665    assert!(copilot_requests.try_next().is_ok());
6666}
6667
6668fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
6669    let point = DisplayPoint::new(row as u32, column as u32);
6670    point..point
6671}
6672
6673fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
6674    let (text, ranges) = marked_text_ranges(marked_text, true);
6675    assert_eq!(view.text(cx), text);
6676    assert_eq!(
6677        view.selections.ranges(cx),
6678        ranges,
6679        "Assert selections are {}",
6680        marked_text
6681    );
6682}
6683
6684/// Handle completion request passing a marked string specifying where the completion
6685/// should be triggered from using '|' character, what range should be replaced, and what completions
6686/// should be returned using '<' and '>' to delimit the range
6687fn handle_completion_request<'a>(
6688    cx: &mut EditorLspTestContext<'a>,
6689    marked_string: &str,
6690    completions: Vec<&'static str>,
6691) -> impl Future<Output = ()> {
6692    let complete_from_marker: TextRangeMarker = '|'.into();
6693    let replace_range_marker: TextRangeMarker = ('<', '>').into();
6694    let (_, mut marked_ranges) = marked_text_ranges_by(
6695        marked_string,
6696        vec![complete_from_marker.clone(), replace_range_marker.clone()],
6697    );
6698
6699    let complete_from_position =
6700        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
6701    let replace_range =
6702        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
6703
6704    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
6705        let completions = completions.clone();
6706        async move {
6707            assert_eq!(params.text_document_position.text_document.uri, url.clone());
6708            assert_eq!(
6709                params.text_document_position.position,
6710                complete_from_position
6711            );
6712            Ok(Some(lsp::CompletionResponse::Array(
6713                completions
6714                    .iter()
6715                    .map(|completion_text| lsp::CompletionItem {
6716                        label: completion_text.to_string(),
6717                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
6718                            range: replace_range,
6719                            new_text: completion_text.to_string(),
6720                        })),
6721                        ..Default::default()
6722                    })
6723                    .collect(),
6724            )))
6725        }
6726    });
6727
6728    async move {
6729        request.next().await;
6730    }
6731}
6732
6733fn handle_resolve_completion_request<'a>(
6734    cx: &mut EditorLspTestContext<'a>,
6735    edits: Option<Vec<(&'static str, &'static str)>>,
6736) -> impl Future<Output = ()> {
6737    let edits = edits.map(|edits| {
6738        edits
6739            .iter()
6740            .map(|(marked_string, new_text)| {
6741                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
6742                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
6743                lsp::TextEdit::new(replace_range, new_text.to_string())
6744            })
6745            .collect::<Vec<_>>()
6746    });
6747
6748    let mut request =
6749        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
6750            let edits = edits.clone();
6751            async move {
6752                Ok(lsp::CompletionItem {
6753                    additional_text_edits: edits,
6754                    ..Default::default()
6755                })
6756            }
6757        });
6758
6759    async move {
6760        request.next().await;
6761    }
6762}
6763
6764fn handle_copilot_completion_request(
6765    lsp: &lsp::FakeLanguageServer,
6766    completions: Vec<copilot::request::Completion>,
6767    completions_cycling: Vec<copilot::request::Completion>,
6768) {
6769    lsp.handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| {
6770        let completions = completions.clone();
6771        async move {
6772            Ok(copilot::request::GetCompletionsResult {
6773                completions: completions.clone(),
6774            })
6775        }
6776    });
6777    lsp.handle_request::<copilot::request::GetCompletionsCycling, _, _>(move |_params, _cx| {
6778        let completions_cycling = completions_cycling.clone();
6779        async move {
6780            Ok(copilot::request::GetCompletionsResult {
6781                completions: completions_cycling.clone(),
6782            })
6783        }
6784    });
6785}
6786
6787pub(crate) fn update_test_settings(
6788    cx: &mut TestAppContext,
6789    f: impl Fn(&mut AllLanguageSettingsContent),
6790) {
6791    cx.update(|cx| {
6792        cx.update_global::<SettingsStore, _, _>(|store, cx| {
6793            store.update_user_settings::<AllLanguageSettings>(cx, f);
6794        });
6795    });
6796}
6797
6798pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
6799    cx.foreground().forbid_parking();
6800
6801    cx.update(|cx| {
6802        cx.set_global(SettingsStore::test(cx));
6803        theme::init((), cx);
6804        client::init_settings(cx);
6805        language::init(cx);
6806        Project::init_settings(cx);
6807        workspace::init_settings(cx);
6808        crate::init(cx);
6809    });
6810
6811    update_test_settings(cx, f);
6812}