editor_tests.rs

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