editor_tests.rs

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