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