editor_tests.rs

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