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]
2699async fn test_manipulate_text(cx: &mut TestAppContext) {
2700    init_test(cx, |_| {});
2701
2702    let mut cx = EditorTestContext::new(cx).await;
2703
2704    // Test convert_to_upper_case()
2705    cx.set_state(indoc! {"
2706        «hello worldˇ»
2707    "});
2708    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
2709    cx.assert_editor_state(indoc! {"
2710        «HELLO WORLDˇ»
2711    "});
2712
2713    // Test convert_to_lower_case()
2714    cx.set_state(indoc! {"
2715        «HELLO WORLDˇ»
2716    "});
2717    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
2718    cx.assert_editor_state(indoc! {"
2719        «hello worldˇ»
2720    "});
2721
2722    // From here on out, test more complex cases of manipulate_text() with a single driver method: convert_to_upper_case()
2723
2724    // Test no selection case - should affect words cursors are in
2725    // Cursor at beginning, middle, and end of word
2726    cx.set_state(indoc! {"
2727        ˇhello big beauˇtiful worldˇ
2728    "});
2729    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
2730    cx.assert_editor_state(indoc! {"
2731        ˇHELLO big BEAUˇTIFUL WORLDˇ
2732    "});
2733
2734    // Test multiple selections on a single line and across multiple lines
2735    cx.set_state(indoc! {"
2736        «Theˇ» quick «brown
2737        foxˇ» jumps «overˇ»
2738        the «lazyˇ» dog
2739    "});
2740    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
2741    cx.assert_editor_state(indoc! {"
2742        «THEˇ» quick «BROWN
2743        FOXˇ» jumps «OVERˇ»
2744        the «LAZYˇ» dog
2745    "});
2746
2747    // Test case where text length grows
2748    cx.set_state(indoc! {"
2749        «tschüߡ»
2750    "});
2751    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
2752    cx.assert_editor_state(indoc! {"
2753        «TSCHÜSSˇ»
2754    "});
2755}
2756
2757#[gpui::test]
2758fn test_duplicate_line(cx: &mut TestAppContext) {
2759    init_test(cx, |_| {});
2760
2761    let view = cx
2762        .add_window(|cx| {
2763            let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2764            build_editor(buffer, cx)
2765        })
2766        .root(cx);
2767    view.update(cx, |view, cx| {
2768        view.change_selections(None, cx, |s| {
2769            s.select_display_ranges([
2770                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2771                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2772                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2773                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2774            ])
2775        });
2776        view.duplicate_line(&DuplicateLine, cx);
2777        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
2778        assert_eq!(
2779            view.selections.display_ranges(cx),
2780            vec![
2781                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
2782                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
2783                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2784                DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
2785            ]
2786        );
2787    });
2788
2789    let view = cx
2790        .add_window(|cx| {
2791            let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2792            build_editor(buffer, cx)
2793        })
2794        .root(cx);
2795    view.update(cx, |view, cx| {
2796        view.change_selections(None, cx, |s| {
2797            s.select_display_ranges([
2798                DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
2799                DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
2800            ])
2801        });
2802        view.duplicate_line(&DuplicateLine, cx);
2803        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
2804        assert_eq!(
2805            view.selections.display_ranges(cx),
2806            vec![
2807                DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
2808                DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
2809            ]
2810        );
2811    });
2812}
2813
2814#[gpui::test]
2815fn test_move_line_up_down(cx: &mut TestAppContext) {
2816    init_test(cx, |_| {});
2817
2818    let view = cx
2819        .add_window(|cx| {
2820            let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
2821            build_editor(buffer, cx)
2822        })
2823        .root(cx);
2824    view.update(cx, |view, cx| {
2825        view.fold_ranges(
2826            vec![
2827                Point::new(0, 2)..Point::new(1, 2),
2828                Point::new(2, 3)..Point::new(4, 1),
2829                Point::new(7, 0)..Point::new(8, 4),
2830            ],
2831            true,
2832            cx,
2833        );
2834        view.change_selections(None, cx, |s| {
2835            s.select_display_ranges([
2836                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2837                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2838                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2839                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
2840            ])
2841        });
2842        assert_eq!(
2843            view.display_text(cx),
2844            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
2845        );
2846
2847        view.move_line_up(&MoveLineUp, cx);
2848        assert_eq!(
2849            view.display_text(cx),
2850            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
2851        );
2852        assert_eq!(
2853            view.selections.display_ranges(cx),
2854            vec![
2855                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2856                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2857                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
2858                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
2859            ]
2860        );
2861    });
2862
2863    view.update(cx, |view, cx| {
2864        view.move_line_down(&MoveLineDown, cx);
2865        assert_eq!(
2866            view.display_text(cx),
2867            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
2868        );
2869        assert_eq!(
2870            view.selections.display_ranges(cx),
2871            vec![
2872                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
2873                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2874                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2875                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
2876            ]
2877        );
2878    });
2879
2880    view.update(cx, |view, cx| {
2881        view.move_line_down(&MoveLineDown, cx);
2882        assert_eq!(
2883            view.display_text(cx),
2884            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
2885        );
2886        assert_eq!(
2887            view.selections.display_ranges(cx),
2888            vec![
2889                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2890                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2891                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2892                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
2893            ]
2894        );
2895    });
2896
2897    view.update(cx, |view, cx| {
2898        view.move_line_up(&MoveLineUp, cx);
2899        assert_eq!(
2900            view.display_text(cx),
2901            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
2902        );
2903        assert_eq!(
2904            view.selections.display_ranges(cx),
2905            vec![
2906                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
2907                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2908                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
2909                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
2910            ]
2911        );
2912    });
2913}
2914
2915#[gpui::test]
2916fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
2917    init_test(cx, |_| {});
2918
2919    let editor = cx
2920        .add_window(|cx| {
2921            let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
2922            build_editor(buffer, cx)
2923        })
2924        .root(cx);
2925    editor.update(cx, |editor, cx| {
2926        let snapshot = editor.buffer.read(cx).snapshot(cx);
2927        editor.insert_blocks(
2928            [BlockProperties {
2929                style: BlockStyle::Fixed,
2930                position: snapshot.anchor_after(Point::new(2, 0)),
2931                disposition: BlockDisposition::Below,
2932                height: 1,
2933                render: Arc::new(|_| Empty::new().into_any()),
2934            }],
2935            Some(Autoscroll::fit()),
2936            cx,
2937        );
2938        editor.change_selections(None, cx, |s| {
2939            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
2940        });
2941        editor.move_line_down(&MoveLineDown, cx);
2942    });
2943}
2944
2945#[gpui::test]
2946fn test_transpose(cx: &mut TestAppContext) {
2947    init_test(cx, |_| {});
2948
2949    _ = cx.add_window(|cx| {
2950        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
2951
2952        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
2953        editor.transpose(&Default::default(), cx);
2954        assert_eq!(editor.text(cx), "bac");
2955        assert_eq!(editor.selections.ranges(cx), [2..2]);
2956
2957        editor.transpose(&Default::default(), cx);
2958        assert_eq!(editor.text(cx), "bca");
2959        assert_eq!(editor.selections.ranges(cx), [3..3]);
2960
2961        editor.transpose(&Default::default(), cx);
2962        assert_eq!(editor.text(cx), "bac");
2963        assert_eq!(editor.selections.ranges(cx), [3..3]);
2964
2965        editor
2966    });
2967
2968    _ = cx.add_window(|cx| {
2969        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
2970
2971        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
2972        editor.transpose(&Default::default(), cx);
2973        assert_eq!(editor.text(cx), "acb\nde");
2974        assert_eq!(editor.selections.ranges(cx), [3..3]);
2975
2976        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
2977        editor.transpose(&Default::default(), cx);
2978        assert_eq!(editor.text(cx), "acbd\ne");
2979        assert_eq!(editor.selections.ranges(cx), [5..5]);
2980
2981        editor.transpose(&Default::default(), cx);
2982        assert_eq!(editor.text(cx), "acbde\n");
2983        assert_eq!(editor.selections.ranges(cx), [6..6]);
2984
2985        editor.transpose(&Default::default(), cx);
2986        assert_eq!(editor.text(cx), "acbd\ne");
2987        assert_eq!(editor.selections.ranges(cx), [6..6]);
2988
2989        editor
2990    });
2991
2992    _ = cx.add_window(|cx| {
2993        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
2994
2995        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
2996        editor.transpose(&Default::default(), cx);
2997        assert_eq!(editor.text(cx), "bacd\ne");
2998        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
2999
3000        editor.transpose(&Default::default(), cx);
3001        assert_eq!(editor.text(cx), "bcade\n");
3002        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
3003
3004        editor.transpose(&Default::default(), cx);
3005        assert_eq!(editor.text(cx), "bcda\ne");
3006        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
3007
3008        editor.transpose(&Default::default(), cx);
3009        assert_eq!(editor.text(cx), "bcade\n");
3010        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
3011
3012        editor.transpose(&Default::default(), cx);
3013        assert_eq!(editor.text(cx), "bcaed\n");
3014        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
3015
3016        editor
3017    });
3018
3019    _ = cx.add_window(|cx| {
3020        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
3021
3022        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
3023        editor.transpose(&Default::default(), cx);
3024        assert_eq!(editor.text(cx), "🏀🍐✋");
3025        assert_eq!(editor.selections.ranges(cx), [8..8]);
3026
3027        editor.transpose(&Default::default(), cx);
3028        assert_eq!(editor.text(cx), "🏀✋🍐");
3029        assert_eq!(editor.selections.ranges(cx), [11..11]);
3030
3031        editor.transpose(&Default::default(), cx);
3032        assert_eq!(editor.text(cx), "🏀🍐✋");
3033        assert_eq!(editor.selections.ranges(cx), [11..11]);
3034
3035        editor
3036    });
3037}
3038
3039#[gpui::test]
3040async fn test_clipboard(cx: &mut gpui::TestAppContext) {
3041    init_test(cx, |_| {});
3042
3043    let mut cx = EditorTestContext::new(cx).await;
3044
3045    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
3046    cx.update_editor(|e, cx| e.cut(&Cut, cx));
3047    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
3048
3049    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
3050    cx.set_state("two ˇfour ˇsix ˇ");
3051    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3052    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
3053
3054    // Paste again but with only two cursors. Since the number of cursors doesn't
3055    // match the number of slices in the clipboard, the entire clipboard text
3056    // is pasted at each cursor.
3057    cx.set_state("ˇtwo one✅ four three six five ˇ");
3058    cx.update_editor(|e, cx| {
3059        e.handle_input("( ", cx);
3060        e.paste(&Paste, cx);
3061        e.handle_input(") ", cx);
3062    });
3063    cx.assert_editor_state(
3064        &([
3065            "( one✅ ",
3066            "three ",
3067            "five ) ˇtwo one✅ four three six five ( one✅ ",
3068            "three ",
3069            "five ) ˇ",
3070        ]
3071        .join("\n")),
3072    );
3073
3074    // Cut with three selections, one of which is full-line.
3075    cx.set_state(indoc! {"
3076        1«2ˇ»3
3077        4ˇ567
3078        «8ˇ»9"});
3079    cx.update_editor(|e, cx| e.cut(&Cut, cx));
3080    cx.assert_editor_state(indoc! {"
3081        1ˇ3
3082        ˇ9"});
3083
3084    // Paste with three selections, noticing how the copied selection that was full-line
3085    // gets inserted before the second cursor.
3086    cx.set_state(indoc! {"
3087        1ˇ3
30883089        «oˇ»ne"});
3090    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3091    cx.assert_editor_state(indoc! {"
3092        12ˇ3
3093        4567
30943095        8ˇne"});
3096
3097    // Copy with a single cursor only, which writes the whole line into the clipboard.
3098    cx.set_state(indoc! {"
3099        The quick brown
3100        fox juˇmps over
3101        the lazy dog"});
3102    cx.update_editor(|e, cx| e.copy(&Copy, cx));
3103    cx.cx.assert_clipboard_content(Some("fox jumps over\n"));
3104
3105    // Paste with three selections, noticing how the copied full-line selection is inserted
3106    // before the empty selections but replaces the selection that is non-empty.
3107    cx.set_state(indoc! {"
3108        Tˇhe quick brown
3109        «foˇ»x jumps over
3110        tˇhe lazy dog"});
3111    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3112    cx.assert_editor_state(indoc! {"
3113        fox jumps over
3114        Tˇhe quick brown
3115        fox jumps over
3116        ˇx jumps over
3117        fox jumps over
3118        tˇhe lazy dog"});
3119}
3120
3121#[gpui::test]
3122async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
3123    init_test(cx, |_| {});
3124
3125    let mut cx = EditorTestContext::new(cx).await;
3126    let language = Arc::new(Language::new(
3127        LanguageConfig::default(),
3128        Some(tree_sitter_rust::language()),
3129    ));
3130    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
3131
3132    // Cut an indented block, without the leading whitespace.
3133    cx.set_state(indoc! {"
3134        const a: B = (
3135            c(),
3136            «d(
3137                e,
3138                f
3139            )ˇ»
3140        );
3141    "});
3142    cx.update_editor(|e, cx| e.cut(&Cut, cx));
3143    cx.assert_editor_state(indoc! {"
3144        const a: B = (
3145            c(),
3146            ˇ
3147        );
3148    "});
3149
3150    // Paste it at the same position.
3151    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3152    cx.assert_editor_state(indoc! {"
3153        const a: B = (
3154            c(),
3155            d(
3156                e,
3157                f
31583159        );
3160    "});
3161
3162    // Paste it at a line with a lower indent level.
3163    cx.set_state(indoc! {"
3164        ˇ
3165        const a: B = (
3166            c(),
3167        );
3168    "});
3169    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3170    cx.assert_editor_state(indoc! {"
3171        d(
3172            e,
3173            f
31743175        const a: B = (
3176            c(),
3177        );
3178    "});
3179
3180    // Cut an indented block, with the leading whitespace.
3181    cx.set_state(indoc! {"
3182        const a: B = (
3183            c(),
3184        «    d(
3185                e,
3186                f
3187            )
3188        ˇ»);
3189    "});
3190    cx.update_editor(|e, cx| e.cut(&Cut, cx));
3191    cx.assert_editor_state(indoc! {"
3192        const a: B = (
3193            c(),
3194        ˇ);
3195    "});
3196
3197    // Paste it at the same position.
3198    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3199    cx.assert_editor_state(indoc! {"
3200        const a: B = (
3201            c(),
3202            d(
3203                e,
3204                f
3205            )
3206        ˇ);
3207    "});
3208
3209    // Paste it at a line with a higher indent level.
3210    cx.set_state(indoc! {"
3211        const a: B = (
3212            c(),
3213            d(
3214                e,
32153216            )
3217        );
3218    "});
3219    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3220    cx.assert_editor_state(indoc! {"
3221        const a: B = (
3222            c(),
3223            d(
3224                e,
3225                f    d(
3226                    e,
3227                    f
3228                )
3229        ˇ
3230            )
3231        );
3232    "});
3233}
3234
3235#[gpui::test]
3236fn test_select_all(cx: &mut TestAppContext) {
3237    init_test(cx, |_| {});
3238
3239    let view = cx
3240        .add_window(|cx| {
3241            let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
3242            build_editor(buffer, cx)
3243        })
3244        .root(cx);
3245    view.update(cx, |view, cx| {
3246        view.select_all(&SelectAll, cx);
3247        assert_eq!(
3248            view.selections.display_ranges(cx),
3249            &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
3250        );
3251    });
3252}
3253
3254#[gpui::test]
3255fn test_select_line(cx: &mut TestAppContext) {
3256    init_test(cx, |_| {});
3257
3258    let view = cx
3259        .add_window(|cx| {
3260            let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
3261            build_editor(buffer, cx)
3262        })
3263        .root(cx);
3264    view.update(cx, |view, cx| {
3265        view.change_selections(None, cx, |s| {
3266            s.select_display_ranges([
3267                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3268                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3269                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
3270                DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
3271            ])
3272        });
3273        view.select_line(&SelectLine, cx);
3274        assert_eq!(
3275            view.selections.display_ranges(cx),
3276            vec![
3277                DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
3278                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
3279            ]
3280        );
3281    });
3282
3283    view.update(cx, |view, cx| {
3284        view.select_line(&SelectLine, cx);
3285        assert_eq!(
3286            view.selections.display_ranges(cx),
3287            vec![
3288                DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
3289                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
3290            ]
3291        );
3292    });
3293
3294    view.update(cx, |view, cx| {
3295        view.select_line(&SelectLine, cx);
3296        assert_eq!(
3297            view.selections.display_ranges(cx),
3298            vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
3299        );
3300    });
3301}
3302
3303#[gpui::test]
3304fn test_split_selection_into_lines(cx: &mut TestAppContext) {
3305    init_test(cx, |_| {});
3306
3307    let view = cx
3308        .add_window(|cx| {
3309            let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
3310            build_editor(buffer, cx)
3311        })
3312        .root(cx);
3313    view.update(cx, |view, cx| {
3314        view.fold_ranges(
3315            vec![
3316                Point::new(0, 2)..Point::new(1, 2),
3317                Point::new(2, 3)..Point::new(4, 1),
3318                Point::new(7, 0)..Point::new(8, 4),
3319            ],
3320            true,
3321            cx,
3322        );
3323        view.change_selections(None, cx, |s| {
3324            s.select_display_ranges([
3325                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3326                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3327                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
3328                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
3329            ])
3330        });
3331        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
3332    });
3333
3334    view.update(cx, |view, cx| {
3335        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
3336        assert_eq!(
3337            view.display_text(cx),
3338            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
3339        );
3340        assert_eq!(
3341            view.selections.display_ranges(cx),
3342            [
3343                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
3344                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3345                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
3346                DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
3347            ]
3348        );
3349    });
3350
3351    view.update(cx, |view, cx| {
3352        view.change_selections(None, cx, |s| {
3353            s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
3354        });
3355        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
3356        assert_eq!(
3357            view.display_text(cx),
3358            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
3359        );
3360        assert_eq!(
3361            view.selections.display_ranges(cx),
3362            [
3363                DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
3364                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
3365                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
3366                DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
3367                DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
3368                DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
3369                DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
3370                DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
3371            ]
3372        );
3373    });
3374}
3375
3376#[gpui::test]
3377fn test_add_selection_above_below(cx: &mut TestAppContext) {
3378    init_test(cx, |_| {});
3379
3380    let view = cx
3381        .add_window(|cx| {
3382            let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
3383            build_editor(buffer, cx)
3384        })
3385        .root(cx);
3386
3387    view.update(cx, |view, cx| {
3388        view.change_selections(None, cx, |s| {
3389            s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
3390        });
3391    });
3392    view.update(cx, |view, cx| {
3393        view.add_selection_above(&AddSelectionAbove, cx);
3394        assert_eq!(
3395            view.selections.display_ranges(cx),
3396            vec![
3397                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
3398                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
3399            ]
3400        );
3401    });
3402
3403    view.update(cx, |view, cx| {
3404        view.add_selection_above(&AddSelectionAbove, cx);
3405        assert_eq!(
3406            view.selections.display_ranges(cx),
3407            vec![
3408                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
3409                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
3410            ]
3411        );
3412    });
3413
3414    view.update(cx, |view, cx| {
3415        view.add_selection_below(&AddSelectionBelow, cx);
3416        assert_eq!(
3417            view.selections.display_ranges(cx),
3418            vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
3419        );
3420
3421        view.undo_selection(&UndoSelection, cx);
3422        assert_eq!(
3423            view.selections.display_ranges(cx),
3424            vec![
3425                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
3426                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
3427            ]
3428        );
3429
3430        view.redo_selection(&RedoSelection, cx);
3431        assert_eq!(
3432            view.selections.display_ranges(cx),
3433            vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
3434        );
3435    });
3436
3437    view.update(cx, |view, cx| {
3438        view.add_selection_below(&AddSelectionBelow, cx);
3439        assert_eq!(
3440            view.selections.display_ranges(cx),
3441            vec![
3442                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
3443                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
3444            ]
3445        );
3446    });
3447
3448    view.update(cx, |view, cx| {
3449        view.add_selection_below(&AddSelectionBelow, cx);
3450        assert_eq!(
3451            view.selections.display_ranges(cx),
3452            vec![
3453                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
3454                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
3455            ]
3456        );
3457    });
3458
3459    view.update(cx, |view, cx| {
3460        view.change_selections(None, cx, |s| {
3461            s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
3462        });
3463    });
3464    view.update(cx, |view, cx| {
3465        view.add_selection_below(&AddSelectionBelow, cx);
3466        assert_eq!(
3467            view.selections.display_ranges(cx),
3468            vec![
3469                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
3470                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
3471            ]
3472        );
3473    });
3474
3475    view.update(cx, |view, cx| {
3476        view.add_selection_below(&AddSelectionBelow, cx);
3477        assert_eq!(
3478            view.selections.display_ranges(cx),
3479            vec![
3480                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
3481                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
3482            ]
3483        );
3484    });
3485
3486    view.update(cx, |view, cx| {
3487        view.add_selection_above(&AddSelectionAbove, cx);
3488        assert_eq!(
3489            view.selections.display_ranges(cx),
3490            vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
3491        );
3492    });
3493
3494    view.update(cx, |view, cx| {
3495        view.add_selection_above(&AddSelectionAbove, cx);
3496        assert_eq!(
3497            view.selections.display_ranges(cx),
3498            vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
3499        );
3500    });
3501
3502    view.update(cx, |view, cx| {
3503        view.change_selections(None, cx, |s| {
3504            s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
3505        });
3506        view.add_selection_below(&AddSelectionBelow, cx);
3507        assert_eq!(
3508            view.selections.display_ranges(cx),
3509            vec![
3510                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
3511                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
3512                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
3513            ]
3514        );
3515    });
3516
3517    view.update(cx, |view, cx| {
3518        view.add_selection_below(&AddSelectionBelow, cx);
3519        assert_eq!(
3520            view.selections.display_ranges(cx),
3521            vec![
3522                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
3523                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
3524                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
3525                DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
3526            ]
3527        );
3528    });
3529
3530    view.update(cx, |view, cx| {
3531        view.add_selection_above(&AddSelectionAbove, cx);
3532        assert_eq!(
3533            view.selections.display_ranges(cx),
3534            vec![
3535                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
3536                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
3537                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
3538            ]
3539        );
3540    });
3541
3542    view.update(cx, |view, cx| {
3543        view.change_selections(None, cx, |s| {
3544            s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
3545        });
3546    });
3547    view.update(cx, |view, cx| {
3548        view.add_selection_above(&AddSelectionAbove, cx);
3549        assert_eq!(
3550            view.selections.display_ranges(cx),
3551            vec![
3552                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
3553                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
3554                DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
3555                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
3556            ]
3557        );
3558    });
3559
3560    view.update(cx, |view, cx| {
3561        view.add_selection_below(&AddSelectionBelow, cx);
3562        assert_eq!(
3563            view.selections.display_ranges(cx),
3564            vec![
3565                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
3566                DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
3567                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
3568            ]
3569        );
3570    });
3571}
3572
3573#[gpui::test]
3574async fn test_select_next(cx: &mut gpui::TestAppContext) {
3575    init_test(cx, |_| {});
3576
3577    let mut cx = EditorTestContext::new(cx).await;
3578    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
3579
3580    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
3581    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3582
3583    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
3584    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
3585
3586    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
3587    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3588
3589    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
3590    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
3591
3592    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
3593    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
3594
3595    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
3596    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
3597}
3598
3599#[gpui::test]
3600async fn test_select_previous(cx: &mut gpui::TestAppContext) {
3601    init_test(cx, |_| {});
3602    {
3603        // `Select previous` without a selection (selects wordwise)
3604        let mut cx = EditorTestContext::new(cx).await;
3605        cx.set_state("abc\nˇabc abc\ndefabc\nabc");
3606
3607        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3608        cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3609
3610        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3611        cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
3612
3613        cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
3614        cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3615
3616        cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
3617        cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
3618
3619        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3620        cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
3621
3622        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3623        cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
3624    }
3625    {
3626        // `Select previous` with a selection
3627        let mut cx = EditorTestContext::new(cx).await;
3628        cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
3629
3630        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3631        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
3632
3633        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3634        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
3635
3636        cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
3637        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
3638
3639        cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
3640        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
3641
3642        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3643        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
3644
3645        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3646        cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
3647    }
3648}
3649
3650#[gpui::test]
3651async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
3652    init_test(cx, |_| {});
3653
3654    let language = Arc::new(Language::new(
3655        LanguageConfig::default(),
3656        Some(tree_sitter_rust::language()),
3657    ));
3658
3659    let text = r#"
3660        use mod1::mod2::{mod3, mod4};
3661
3662        fn fn_1(param1: bool, param2: &str) {
3663            let var1 = "text";
3664        }
3665    "#
3666    .unindent();
3667
3668    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3669    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3670    let view = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
3671    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
3672        .await;
3673
3674    view.update(cx, |view, cx| {
3675        view.change_selections(None, cx, |s| {
3676            s.select_display_ranges([
3677                DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3678                DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3679                DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3680            ]);
3681        });
3682        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3683    });
3684    assert_eq!(
3685        view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
3686        &[
3687            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
3688            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3689            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
3690        ]
3691    );
3692
3693    view.update(cx, |view, cx| {
3694        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3695    });
3696    assert_eq!(
3697        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3698        &[
3699            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3700            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
3701        ]
3702    );
3703
3704    view.update(cx, |view, cx| {
3705        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3706    });
3707    assert_eq!(
3708        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3709        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
3710    );
3711
3712    // Trying to expand the selected syntax node one more time has no effect.
3713    view.update(cx, |view, cx| {
3714        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3715    });
3716    assert_eq!(
3717        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3718        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
3719    );
3720
3721    view.update(cx, |view, cx| {
3722        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3723    });
3724    assert_eq!(
3725        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3726        &[
3727            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3728            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
3729        ]
3730    );
3731
3732    view.update(cx, |view, cx| {
3733        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3734    });
3735    assert_eq!(
3736        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3737        &[
3738            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
3739            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3740            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
3741        ]
3742    );
3743
3744    view.update(cx, |view, cx| {
3745        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3746    });
3747    assert_eq!(
3748        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3749        &[
3750            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3751            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3752            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3753        ]
3754    );
3755
3756    // Trying to shrink the selected syntax node one more time has no effect.
3757    view.update(cx, |view, cx| {
3758        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3759    });
3760    assert_eq!(
3761        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3762        &[
3763            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3764            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3765            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3766        ]
3767    );
3768
3769    // Ensure that we keep expanding the selection if the larger selection starts or ends within
3770    // a fold.
3771    view.update(cx, |view, cx| {
3772        view.fold_ranges(
3773            vec![
3774                Point::new(0, 21)..Point::new(0, 24),
3775                Point::new(3, 20)..Point::new(3, 22),
3776            ],
3777            true,
3778            cx,
3779        );
3780        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3781    });
3782    assert_eq!(
3783        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3784        &[
3785            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3786            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3787            DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
3788        ]
3789    );
3790}
3791
3792#[gpui::test]
3793async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
3794    init_test(cx, |_| {});
3795
3796    let language = Arc::new(
3797        Language::new(
3798            LanguageConfig {
3799                brackets: BracketPairConfig {
3800                    pairs: vec![
3801                        BracketPair {
3802                            start: "{".to_string(),
3803                            end: "}".to_string(),
3804                            close: false,
3805                            newline: true,
3806                        },
3807                        BracketPair {
3808                            start: "(".to_string(),
3809                            end: ")".to_string(),
3810                            close: false,
3811                            newline: true,
3812                        },
3813                    ],
3814                    ..Default::default()
3815                },
3816                ..Default::default()
3817            },
3818            Some(tree_sitter_rust::language()),
3819        )
3820        .with_indents_query(
3821            r#"
3822                (_ "(" ")" @end) @indent
3823                (_ "{" "}" @end) @indent
3824            "#,
3825        )
3826        .unwrap(),
3827    );
3828
3829    let text = "fn a() {}";
3830
3831    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3832    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3833    let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
3834    editor
3835        .condition(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
3836        .await;
3837
3838    editor.update(cx, |editor, cx| {
3839        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
3840        editor.newline(&Newline, cx);
3841        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
3842        assert_eq!(
3843            editor.selections.ranges(cx),
3844            &[
3845                Point::new(1, 4)..Point::new(1, 4),
3846                Point::new(3, 4)..Point::new(3, 4),
3847                Point::new(5, 0)..Point::new(5, 0)
3848            ]
3849        );
3850    });
3851}
3852
3853#[gpui::test]
3854async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
3855    init_test(cx, |_| {});
3856
3857    let mut cx = EditorTestContext::new(cx).await;
3858
3859    let language = Arc::new(Language::new(
3860        LanguageConfig {
3861            brackets: BracketPairConfig {
3862                pairs: vec![
3863                    BracketPair {
3864                        start: "{".to_string(),
3865                        end: "}".to_string(),
3866                        close: true,
3867                        newline: true,
3868                    },
3869                    BracketPair {
3870                        start: "(".to_string(),
3871                        end: ")".to_string(),
3872                        close: true,
3873                        newline: true,
3874                    },
3875                    BracketPair {
3876                        start: "/*".to_string(),
3877                        end: " */".to_string(),
3878                        close: true,
3879                        newline: true,
3880                    },
3881                    BracketPair {
3882                        start: "[".to_string(),
3883                        end: "]".to_string(),
3884                        close: false,
3885                        newline: true,
3886                    },
3887                    BracketPair {
3888                        start: "\"".to_string(),
3889                        end: "\"".to_string(),
3890                        close: true,
3891                        newline: false,
3892                    },
3893                ],
3894                ..Default::default()
3895            },
3896            autoclose_before: "})]".to_string(),
3897            ..Default::default()
3898        },
3899        Some(tree_sitter_rust::language()),
3900    ));
3901
3902    let registry = Arc::new(LanguageRegistry::test());
3903    registry.add(language.clone());
3904    cx.update_buffer(|buffer, cx| {
3905        buffer.set_language_registry(registry);
3906        buffer.set_language(Some(language), cx);
3907    });
3908
3909    cx.set_state(
3910        &r#"
3911            🏀ˇ
3912            εˇ
3913            ❤️ˇ
3914        "#
3915        .unindent(),
3916    );
3917
3918    // autoclose multiple nested brackets at multiple cursors
3919    cx.update_editor(|view, cx| {
3920        view.handle_input("{", cx);
3921        view.handle_input("{", cx);
3922        view.handle_input("{", cx);
3923    });
3924    cx.assert_editor_state(
3925        &"
3926            🏀{{{ˇ}}}
3927            ε{{{ˇ}}}
3928            ❤️{{{ˇ}}}
3929        "
3930        .unindent(),
3931    );
3932
3933    // insert a different closing bracket
3934    cx.update_editor(|view, cx| {
3935        view.handle_input(")", cx);
3936    });
3937    cx.assert_editor_state(
3938        &"
3939            🏀{{{)ˇ}}}
3940            ε{{{)ˇ}}}
3941            ❤️{{{)ˇ}}}
3942        "
3943        .unindent(),
3944    );
3945
3946    // skip over the auto-closed brackets when typing a closing bracket
3947    cx.update_editor(|view, cx| {
3948        view.move_right(&MoveRight, cx);
3949        view.handle_input("}", cx);
3950        view.handle_input("}", cx);
3951        view.handle_input("}", cx);
3952    });
3953    cx.assert_editor_state(
3954        &"
3955            🏀{{{)}}}}ˇ
3956            ε{{{)}}}}ˇ
3957            ❤️{{{)}}}}ˇ
3958        "
3959        .unindent(),
3960    );
3961
3962    // autoclose multi-character pairs
3963    cx.set_state(
3964        &"
3965            ˇ
3966            ˇ
3967        "
3968        .unindent(),
3969    );
3970    cx.update_editor(|view, cx| {
3971        view.handle_input("/", cx);
3972        view.handle_input("*", cx);
3973    });
3974    cx.assert_editor_state(
3975        &"
3976            /*ˇ */
3977            /*ˇ */
3978        "
3979        .unindent(),
3980    );
3981
3982    // one cursor autocloses a multi-character pair, one cursor
3983    // does not autoclose.
3984    cx.set_state(
3985        &"
39863987            ˇ
3988        "
3989        .unindent(),
3990    );
3991    cx.update_editor(|view, cx| view.handle_input("*", cx));
3992    cx.assert_editor_state(
3993        &"
3994            /*ˇ */
39953996        "
3997        .unindent(),
3998    );
3999
4000    // Don't autoclose if the next character isn't whitespace and isn't
4001    // listed in the language's "autoclose_before" section.
4002    cx.set_state("ˇa b");
4003    cx.update_editor(|view, cx| view.handle_input("{", cx));
4004    cx.assert_editor_state("{ˇa b");
4005
4006    // Don't autoclose if `close` is false for the bracket pair
4007    cx.set_state("ˇ");
4008    cx.update_editor(|view, cx| view.handle_input("[", cx));
4009    cx.assert_editor_state("");
4010
4011    // Surround with brackets if text is selected
4012    cx.set_state("«aˇ» b");
4013    cx.update_editor(|view, cx| view.handle_input("{", cx));
4014    cx.assert_editor_state("{«aˇ»} b");
4015
4016    // Autclose pair where the start and end characters are the same
4017    cx.set_state("");
4018    cx.update_editor(|view, cx| view.handle_input("\"", cx));
4019    cx.assert_editor_state("a\"ˇ\"");
4020    cx.update_editor(|view, cx| view.handle_input("\"", cx));
4021    cx.assert_editor_state("a\"\"ˇ");
4022}
4023
4024#[gpui::test]
4025async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
4026    init_test(cx, |_| {});
4027
4028    let mut cx = EditorTestContext::new(cx).await;
4029
4030    let html_language = Arc::new(
4031        Language::new(
4032            LanguageConfig {
4033                name: "HTML".into(),
4034                brackets: BracketPairConfig {
4035                    pairs: vec![
4036                        BracketPair {
4037                            start: "<".into(),
4038                            end: ">".into(),
4039                            close: true,
4040                            ..Default::default()
4041                        },
4042                        BracketPair {
4043                            start: "{".into(),
4044                            end: "}".into(),
4045                            close: true,
4046                            ..Default::default()
4047                        },
4048                        BracketPair {
4049                            start: "(".into(),
4050                            end: ")".into(),
4051                            close: true,
4052                            ..Default::default()
4053                        },
4054                    ],
4055                    ..Default::default()
4056                },
4057                autoclose_before: "})]>".into(),
4058                ..Default::default()
4059            },
4060            Some(tree_sitter_html::language()),
4061        )
4062        .with_injection_query(
4063            r#"
4064            (script_element
4065                (raw_text) @content
4066                (#set! "language" "javascript"))
4067            "#,
4068        )
4069        .unwrap(),
4070    );
4071
4072    let javascript_language = Arc::new(Language::new(
4073        LanguageConfig {
4074            name: "JavaScript".into(),
4075            brackets: BracketPairConfig {
4076                pairs: vec![
4077                    BracketPair {
4078                        start: "/*".into(),
4079                        end: " */".into(),
4080                        close: true,
4081                        ..Default::default()
4082                    },
4083                    BracketPair {
4084                        start: "{".into(),
4085                        end: "}".into(),
4086                        close: true,
4087                        ..Default::default()
4088                    },
4089                    BracketPair {
4090                        start: "(".into(),
4091                        end: ")".into(),
4092                        close: true,
4093                        ..Default::default()
4094                    },
4095                ],
4096                ..Default::default()
4097            },
4098            autoclose_before: "})]>".into(),
4099            ..Default::default()
4100        },
4101        Some(tree_sitter_typescript::language_tsx()),
4102    ));
4103
4104    let registry = Arc::new(LanguageRegistry::test());
4105    registry.add(html_language.clone());
4106    registry.add(javascript_language.clone());
4107
4108    cx.update_buffer(|buffer, cx| {
4109        buffer.set_language_registry(registry);
4110        buffer.set_language(Some(html_language), cx);
4111    });
4112
4113    cx.set_state(
4114        &r#"
4115            <body>ˇ
4116                <script>
4117                    var x = 1;ˇ
4118                </script>
4119            </body>ˇ
4120        "#
4121        .unindent(),
4122    );
4123
4124    // Precondition: different languages are active at different locations.
4125    cx.update_editor(|editor, cx| {
4126        let snapshot = editor.snapshot(cx);
4127        let cursors = editor.selections.ranges::<usize>(cx);
4128        let languages = cursors
4129            .iter()
4130            .map(|c| snapshot.language_at(c.start).unwrap().name())
4131            .collect::<Vec<_>>();
4132        assert_eq!(
4133            languages,
4134            &["HTML".into(), "JavaScript".into(), "HTML".into()]
4135        );
4136    });
4137
4138    // Angle brackets autoclose in HTML, but not JavaScript.
4139    cx.update_editor(|editor, cx| {
4140        editor.handle_input("<", cx);
4141        editor.handle_input("a", cx);
4142    });
4143    cx.assert_editor_state(
4144        &r#"
4145            <body><aˇ>
4146                <script>
4147                    var x = 1;<aˇ
4148                </script>
4149            </body><aˇ>
4150        "#
4151        .unindent(),
4152    );
4153
4154    // Curly braces and parens autoclose in both HTML and JavaScript.
4155    cx.update_editor(|editor, cx| {
4156        editor.handle_input(" b=", cx);
4157        editor.handle_input("{", cx);
4158        editor.handle_input("c", cx);
4159        editor.handle_input("(", cx);
4160    });
4161    cx.assert_editor_state(
4162        &r#"
4163            <body><a b={c(ˇ)}>
4164                <script>
4165                    var x = 1;<a b={c(ˇ)}
4166                </script>
4167            </body><a b={c(ˇ)}>
4168        "#
4169        .unindent(),
4170    );
4171
4172    // Brackets that were already autoclosed are skipped.
4173    cx.update_editor(|editor, cx| {
4174        editor.handle_input(")", cx);
4175        editor.handle_input("d", cx);
4176        editor.handle_input("}", cx);
4177    });
4178    cx.assert_editor_state(
4179        &r#"
4180            <body><a b={c()d}ˇ>
4181                <script>
4182                    var x = 1;<a b={c()d}ˇ
4183                </script>
4184            </body><a b={c()d}ˇ>
4185        "#
4186        .unindent(),
4187    );
4188    cx.update_editor(|editor, cx| {
4189        editor.handle_input(">", cx);
4190    });
4191    cx.assert_editor_state(
4192        &r#"
4193            <body><a b={c()d}>ˇ
4194                <script>
4195                    var x = 1;<a b={c()d}>ˇ
4196                </script>
4197            </body><a b={c()d}>ˇ
4198        "#
4199        .unindent(),
4200    );
4201
4202    // Reset
4203    cx.set_state(
4204        &r#"
4205            <body>ˇ
4206                <script>
4207                    var x = 1;ˇ
4208                </script>
4209            </body>ˇ
4210        "#
4211        .unindent(),
4212    );
4213
4214    cx.update_editor(|editor, cx| {
4215        editor.handle_input("<", cx);
4216    });
4217    cx.assert_editor_state(
4218        &r#"
4219            <body><ˇ>
4220                <script>
4221                    var x = 1;<ˇ
4222                </script>
4223            </body><ˇ>
4224        "#
4225        .unindent(),
4226    );
4227
4228    // When backspacing, the closing angle brackets are removed.
4229    cx.update_editor(|editor, cx| {
4230        editor.backspace(&Backspace, cx);
4231    });
4232    cx.assert_editor_state(
4233        &r#"
4234            <body>ˇ
4235                <script>
4236                    var x = 1;ˇ
4237                </script>
4238            </body>ˇ
4239        "#
4240        .unindent(),
4241    );
4242
4243    // Block comments autoclose in JavaScript, but not HTML.
4244    cx.update_editor(|editor, cx| {
4245        editor.handle_input("/", cx);
4246        editor.handle_input("*", cx);
4247    });
4248    cx.assert_editor_state(
4249        &r#"
4250            <body>/*ˇ
4251                <script>
4252                    var x = 1;/*ˇ */
4253                </script>
4254            </body>/*ˇ
4255        "#
4256        .unindent(),
4257    );
4258}
4259
4260#[gpui::test]
4261async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
4262    init_test(cx, |_| {});
4263
4264    let mut cx = EditorTestContext::new(cx).await;
4265
4266    let rust_language = Arc::new(
4267        Language::new(
4268            LanguageConfig {
4269                name: "Rust".into(),
4270                brackets: serde_json::from_value(json!([
4271                    { "start": "{", "end": "}", "close": true, "newline": true },
4272                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
4273                ]))
4274                .unwrap(),
4275                autoclose_before: "})]>".into(),
4276                ..Default::default()
4277            },
4278            Some(tree_sitter_rust::language()),
4279        )
4280        .with_override_query("(string_literal) @string")
4281        .unwrap(),
4282    );
4283
4284    let registry = Arc::new(LanguageRegistry::test());
4285    registry.add(rust_language.clone());
4286
4287    cx.update_buffer(|buffer, cx| {
4288        buffer.set_language_registry(registry);
4289        buffer.set_language(Some(rust_language), cx);
4290    });
4291
4292    cx.set_state(
4293        &r#"
4294            let x = ˇ
4295        "#
4296        .unindent(),
4297    );
4298
4299    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
4300    cx.update_editor(|editor, cx| {
4301        editor.handle_input("\"", cx);
4302    });
4303    cx.assert_editor_state(
4304        &r#"
4305            let x = "ˇ"
4306        "#
4307        .unindent(),
4308    );
4309
4310    // Inserting another quotation mark. The cursor moves across the existing
4311    // automatically-inserted quotation mark.
4312    cx.update_editor(|editor, cx| {
4313        editor.handle_input("\"", cx);
4314    });
4315    cx.assert_editor_state(
4316        &r#"
4317            let x = ""ˇ
4318        "#
4319        .unindent(),
4320    );
4321
4322    // Reset
4323    cx.set_state(
4324        &r#"
4325            let x = ˇ
4326        "#
4327        .unindent(),
4328    );
4329
4330    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
4331    cx.update_editor(|editor, cx| {
4332        editor.handle_input("\"", cx);
4333        editor.handle_input(" ", cx);
4334        editor.move_left(&Default::default(), cx);
4335        editor.handle_input("\\", cx);
4336        editor.handle_input("\"", cx);
4337    });
4338    cx.assert_editor_state(
4339        &r#"
4340            let x = "\"ˇ "
4341        "#
4342        .unindent(),
4343    );
4344
4345    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
4346    // mark. Nothing is inserted.
4347    cx.update_editor(|editor, cx| {
4348        editor.move_right(&Default::default(), cx);
4349        editor.handle_input("\"", cx);
4350    });
4351    cx.assert_editor_state(
4352        &r#"
4353            let x = "\" "ˇ
4354        "#
4355        .unindent(),
4356    );
4357}
4358
4359#[gpui::test]
4360async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
4361    init_test(cx, |_| {});
4362
4363    let language = Arc::new(Language::new(
4364        LanguageConfig {
4365            brackets: BracketPairConfig {
4366                pairs: vec![
4367                    BracketPair {
4368                        start: "{".to_string(),
4369                        end: "}".to_string(),
4370                        close: true,
4371                        newline: true,
4372                    },
4373                    BracketPair {
4374                        start: "/* ".to_string(),
4375                        end: "*/".to_string(),
4376                        close: true,
4377                        ..Default::default()
4378                    },
4379                ],
4380                ..Default::default()
4381            },
4382            ..Default::default()
4383        },
4384        Some(tree_sitter_rust::language()),
4385    ));
4386
4387    let text = r#"
4388        a
4389        b
4390        c
4391    "#
4392    .unindent();
4393
4394    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
4395    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4396    let view = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
4397    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
4398        .await;
4399
4400    view.update(cx, |view, cx| {
4401        view.change_selections(None, cx, |s| {
4402            s.select_display_ranges([
4403                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
4404                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
4405                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
4406            ])
4407        });
4408
4409        view.handle_input("{", cx);
4410        view.handle_input("{", cx);
4411        view.handle_input("{", cx);
4412        assert_eq!(
4413            view.text(cx),
4414            "
4415                {{{a}}}
4416                {{{b}}}
4417                {{{c}}}
4418            "
4419            .unindent()
4420        );
4421        assert_eq!(
4422            view.selections.display_ranges(cx),
4423            [
4424                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
4425                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
4426                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
4427            ]
4428        );
4429
4430        view.undo(&Undo, cx);
4431        view.undo(&Undo, cx);
4432        view.undo(&Undo, cx);
4433        assert_eq!(
4434            view.text(cx),
4435            "
4436                a
4437                b
4438                c
4439            "
4440            .unindent()
4441        );
4442        assert_eq!(
4443            view.selections.display_ranges(cx),
4444            [
4445                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
4446                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
4447                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
4448            ]
4449        );
4450
4451        // Ensure inserting the first character of a multi-byte bracket pair
4452        // doesn't surround the selections with the bracket.
4453        view.handle_input("/", cx);
4454        assert_eq!(
4455            view.text(cx),
4456            "
4457                /
4458                /
4459                /
4460            "
4461            .unindent()
4462        );
4463        assert_eq!(
4464            view.selections.display_ranges(cx),
4465            [
4466                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
4467                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
4468                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
4469            ]
4470        );
4471
4472        view.undo(&Undo, cx);
4473        assert_eq!(
4474            view.text(cx),
4475            "
4476                a
4477                b
4478                c
4479            "
4480            .unindent()
4481        );
4482        assert_eq!(
4483            view.selections.display_ranges(cx),
4484            [
4485                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
4486                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
4487                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
4488            ]
4489        );
4490
4491        // Ensure inserting the last character of a multi-byte bracket pair
4492        // doesn't surround the selections with the bracket.
4493        view.handle_input("*", cx);
4494        assert_eq!(
4495            view.text(cx),
4496            "
4497                *
4498                *
4499                *
4500            "
4501            .unindent()
4502        );
4503        assert_eq!(
4504            view.selections.display_ranges(cx),
4505            [
4506                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
4507                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
4508                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
4509            ]
4510        );
4511    });
4512}
4513
4514#[gpui::test]
4515async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
4516    init_test(cx, |_| {});
4517
4518    let language = Arc::new(Language::new(
4519        LanguageConfig {
4520            brackets: BracketPairConfig {
4521                pairs: vec![BracketPair {
4522                    start: "{".to_string(),
4523                    end: "}".to_string(),
4524                    close: true,
4525                    newline: true,
4526                }],
4527                ..Default::default()
4528            },
4529            autoclose_before: "}".to_string(),
4530            ..Default::default()
4531        },
4532        Some(tree_sitter_rust::language()),
4533    ));
4534
4535    let text = r#"
4536        a
4537        b
4538        c
4539    "#
4540    .unindent();
4541
4542    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
4543    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4544    let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
4545    editor
4546        .condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
4547        .await;
4548
4549    editor.update(cx, |editor, cx| {
4550        editor.change_selections(None, cx, |s| {
4551            s.select_ranges([
4552                Point::new(0, 1)..Point::new(0, 1),
4553                Point::new(1, 1)..Point::new(1, 1),
4554                Point::new(2, 1)..Point::new(2, 1),
4555            ])
4556        });
4557
4558        editor.handle_input("{", cx);
4559        editor.handle_input("{", cx);
4560        editor.handle_input("_", cx);
4561        assert_eq!(
4562            editor.text(cx),
4563            "
4564                a{{_}}
4565                b{{_}}
4566                c{{_}}
4567            "
4568            .unindent()
4569        );
4570        assert_eq!(
4571            editor.selections.ranges::<Point>(cx),
4572            [
4573                Point::new(0, 4)..Point::new(0, 4),
4574                Point::new(1, 4)..Point::new(1, 4),
4575                Point::new(2, 4)..Point::new(2, 4)
4576            ]
4577        );
4578
4579        editor.backspace(&Default::default(), cx);
4580        editor.backspace(&Default::default(), cx);
4581        assert_eq!(
4582            editor.text(cx),
4583            "
4584                a{}
4585                b{}
4586                c{}
4587            "
4588            .unindent()
4589        );
4590        assert_eq!(
4591            editor.selections.ranges::<Point>(cx),
4592            [
4593                Point::new(0, 2)..Point::new(0, 2),
4594                Point::new(1, 2)..Point::new(1, 2),
4595                Point::new(2, 2)..Point::new(2, 2)
4596            ]
4597        );
4598
4599        editor.delete_to_previous_word_start(&Default::default(), cx);
4600        assert_eq!(
4601            editor.text(cx),
4602            "
4603                a
4604                b
4605                c
4606            "
4607            .unindent()
4608        );
4609        assert_eq!(
4610            editor.selections.ranges::<Point>(cx),
4611            [
4612                Point::new(0, 1)..Point::new(0, 1),
4613                Point::new(1, 1)..Point::new(1, 1),
4614                Point::new(2, 1)..Point::new(2, 1)
4615            ]
4616        );
4617    });
4618}
4619
4620#[gpui::test]
4621async fn test_snippets(cx: &mut gpui::TestAppContext) {
4622    init_test(cx, |_| {});
4623
4624    let (text, insertion_ranges) = marked_text_ranges(
4625        indoc! {"
4626            a.ˇ b
4627            a.ˇ b
4628            a.ˇ b
4629        "},
4630        false,
4631    );
4632
4633    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
4634    let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
4635
4636    editor.update(cx, |editor, cx| {
4637        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
4638
4639        editor
4640            .insert_snippet(&insertion_ranges, snippet, cx)
4641            .unwrap();
4642
4643        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
4644            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
4645            assert_eq!(editor.text(cx), expected_text);
4646            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
4647        }
4648
4649        assert(
4650            editor,
4651            cx,
4652            indoc! {"
4653                a.f(«one», two, «three») b
4654                a.f(«one», two, «three») b
4655                a.f(«one», two, «three») b
4656            "},
4657        );
4658
4659        // Can't move earlier than the first tab stop
4660        assert!(!editor.move_to_prev_snippet_tabstop(cx));
4661        assert(
4662            editor,
4663            cx,
4664            indoc! {"
4665                a.f(«one», two, «three») b
4666                a.f(«one», two, «three») b
4667                a.f(«one», two, «three») b
4668            "},
4669        );
4670
4671        assert!(editor.move_to_next_snippet_tabstop(cx));
4672        assert(
4673            editor,
4674            cx,
4675            indoc! {"
4676                a.f(one, «two», three) b
4677                a.f(one, «two», three) b
4678                a.f(one, «two», three) b
4679            "},
4680        );
4681
4682        editor.move_to_prev_snippet_tabstop(cx);
4683        assert(
4684            editor,
4685            cx,
4686            indoc! {"
4687                a.f(«one», two, «three») b
4688                a.f(«one», two, «three») b
4689                a.f(«one», two, «three») b
4690            "},
4691        );
4692
4693        assert!(editor.move_to_next_snippet_tabstop(cx));
4694        assert(
4695            editor,
4696            cx,
4697            indoc! {"
4698                a.f(one, «two», three) b
4699                a.f(one, «two», three) b
4700                a.f(one, «two», three) b
4701            "},
4702        );
4703        assert!(editor.move_to_next_snippet_tabstop(cx));
4704        assert(
4705            editor,
4706            cx,
4707            indoc! {"
4708                a.f(one, two, three)ˇ b
4709                a.f(one, two, three)ˇ b
4710                a.f(one, two, three)ˇ b
4711            "},
4712        );
4713
4714        // As soon as the last tab stop is reached, snippet state is gone
4715        editor.move_to_prev_snippet_tabstop(cx);
4716        assert(
4717            editor,
4718            cx,
4719            indoc! {"
4720                a.f(one, two, three)ˇ b
4721                a.f(one, two, three)ˇ b
4722                a.f(one, two, three)ˇ b
4723            "},
4724        );
4725    });
4726}
4727
4728#[gpui::test]
4729async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
4730    init_test(cx, |_| {});
4731
4732    let mut language = Language::new(
4733        LanguageConfig {
4734            name: "Rust".into(),
4735            path_suffixes: vec!["rs".to_string()],
4736            ..Default::default()
4737        },
4738        Some(tree_sitter_rust::language()),
4739    );
4740    let mut fake_servers = language
4741        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4742            capabilities: lsp::ServerCapabilities {
4743                document_formatting_provider: Some(lsp::OneOf::Left(true)),
4744                ..Default::default()
4745            },
4746            ..Default::default()
4747        }))
4748        .await;
4749
4750    let fs = FakeFs::new(cx.background());
4751    fs.insert_file("/file.rs", Default::default()).await;
4752
4753    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4754    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4755    let buffer = project
4756        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4757        .await
4758        .unwrap();
4759
4760    cx.foreground().start_waiting();
4761    let fake_server = fake_servers.next().await.unwrap();
4762
4763    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4764    let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
4765    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4766    assert!(cx.read(|cx| editor.is_dirty(cx)));
4767
4768    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4769    fake_server
4770        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4771            assert_eq!(
4772                params.text_document.uri,
4773                lsp::Url::from_file_path("/file.rs").unwrap()
4774            );
4775            assert_eq!(params.options.tab_size, 4);
4776            Ok(Some(vec![lsp::TextEdit::new(
4777                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4778                ", ".to_string(),
4779            )]))
4780        })
4781        .next()
4782        .await;
4783    cx.foreground().start_waiting();
4784    save.await.unwrap();
4785    assert_eq!(
4786        editor.read_with(cx, |editor, cx| editor.text(cx)),
4787        "one, two\nthree\n"
4788    );
4789    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4790
4791    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4792    assert!(cx.read(|cx| editor.is_dirty(cx)));
4793
4794    // Ensure we can still save even if formatting hangs.
4795    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4796        assert_eq!(
4797            params.text_document.uri,
4798            lsp::Url::from_file_path("/file.rs").unwrap()
4799        );
4800        futures::future::pending::<()>().await;
4801        unreachable!()
4802    });
4803    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4804    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4805    cx.foreground().start_waiting();
4806    save.await.unwrap();
4807    assert_eq!(
4808        editor.read_with(cx, |editor, cx| editor.text(cx)),
4809        "one\ntwo\nthree\n"
4810    );
4811    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4812
4813    // Set rust language override and assert overridden tabsize is sent to language server
4814    update_test_language_settings(cx, |settings| {
4815        settings.languages.insert(
4816            "Rust".into(),
4817            LanguageSettingsContent {
4818                tab_size: NonZeroU32::new(8),
4819                ..Default::default()
4820            },
4821        );
4822    });
4823
4824    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4825    fake_server
4826        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4827            assert_eq!(
4828                params.text_document.uri,
4829                lsp::Url::from_file_path("/file.rs").unwrap()
4830            );
4831            assert_eq!(params.options.tab_size, 8);
4832            Ok(Some(vec![]))
4833        })
4834        .next()
4835        .await;
4836    cx.foreground().start_waiting();
4837    save.await.unwrap();
4838}
4839
4840#[gpui::test]
4841async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
4842    init_test(cx, |_| {});
4843
4844    let mut language = Language::new(
4845        LanguageConfig {
4846            name: "Rust".into(),
4847            path_suffixes: vec!["rs".to_string()],
4848            ..Default::default()
4849        },
4850        Some(tree_sitter_rust::language()),
4851    );
4852    let mut fake_servers = language
4853        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4854            capabilities: lsp::ServerCapabilities {
4855                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
4856                ..Default::default()
4857            },
4858            ..Default::default()
4859        }))
4860        .await;
4861
4862    let fs = FakeFs::new(cx.background());
4863    fs.insert_file("/file.rs", Default::default()).await;
4864
4865    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4866    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4867    let buffer = project
4868        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4869        .await
4870        .unwrap();
4871
4872    cx.foreground().start_waiting();
4873    let fake_server = fake_servers.next().await.unwrap();
4874
4875    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4876    let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
4877    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4878    assert!(cx.read(|cx| editor.is_dirty(cx)));
4879
4880    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4881    fake_server
4882        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
4883            assert_eq!(
4884                params.text_document.uri,
4885                lsp::Url::from_file_path("/file.rs").unwrap()
4886            );
4887            assert_eq!(params.options.tab_size, 4);
4888            Ok(Some(vec![lsp::TextEdit::new(
4889                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4890                ", ".to_string(),
4891            )]))
4892        })
4893        .next()
4894        .await;
4895    cx.foreground().start_waiting();
4896    save.await.unwrap();
4897    assert_eq!(
4898        editor.read_with(cx, |editor, cx| editor.text(cx)),
4899        "one, two\nthree\n"
4900    );
4901    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4902
4903    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4904    assert!(cx.read(|cx| editor.is_dirty(cx)));
4905
4906    // Ensure we can still save even if formatting hangs.
4907    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
4908        move |params, _| async move {
4909            assert_eq!(
4910                params.text_document.uri,
4911                lsp::Url::from_file_path("/file.rs").unwrap()
4912            );
4913            futures::future::pending::<()>().await;
4914            unreachable!()
4915        },
4916    );
4917    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4918    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4919    cx.foreground().start_waiting();
4920    save.await.unwrap();
4921    assert_eq!(
4922        editor.read_with(cx, |editor, cx| editor.text(cx)),
4923        "one\ntwo\nthree\n"
4924    );
4925    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4926
4927    // Set rust language override and assert overridden tabsize is sent to language server
4928    update_test_language_settings(cx, |settings| {
4929        settings.languages.insert(
4930            "Rust".into(),
4931            LanguageSettingsContent {
4932                tab_size: NonZeroU32::new(8),
4933                ..Default::default()
4934            },
4935        );
4936    });
4937
4938    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4939    fake_server
4940        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
4941            assert_eq!(
4942                params.text_document.uri,
4943                lsp::Url::from_file_path("/file.rs").unwrap()
4944            );
4945            assert_eq!(params.options.tab_size, 8);
4946            Ok(Some(vec![]))
4947        })
4948        .next()
4949        .await;
4950    cx.foreground().start_waiting();
4951    save.await.unwrap();
4952}
4953
4954#[gpui::test]
4955async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
4956    init_test(cx, |_| {});
4957
4958    let mut language = Language::new(
4959        LanguageConfig {
4960            name: "Rust".into(),
4961            path_suffixes: vec!["rs".to_string()],
4962            ..Default::default()
4963        },
4964        Some(tree_sitter_rust::language()),
4965    );
4966    let mut fake_servers = language
4967        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4968            capabilities: lsp::ServerCapabilities {
4969                document_formatting_provider: Some(lsp::OneOf::Left(true)),
4970                ..Default::default()
4971            },
4972            ..Default::default()
4973        }))
4974        .await;
4975
4976    let fs = FakeFs::new(cx.background());
4977    fs.insert_file("/file.rs", Default::default()).await;
4978
4979    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4980    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4981    let buffer = project
4982        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4983        .await
4984        .unwrap();
4985
4986    cx.foreground().start_waiting();
4987    let fake_server = fake_servers.next().await.unwrap();
4988
4989    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4990    let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
4991    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4992
4993    let format = editor.update(cx, |editor, cx| {
4994        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
4995    });
4996    fake_server
4997        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4998            assert_eq!(
4999                params.text_document.uri,
5000                lsp::Url::from_file_path("/file.rs").unwrap()
5001            );
5002            assert_eq!(params.options.tab_size, 4);
5003            Ok(Some(vec![lsp::TextEdit::new(
5004                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
5005                ", ".to_string(),
5006            )]))
5007        })
5008        .next()
5009        .await;
5010    cx.foreground().start_waiting();
5011    format.await.unwrap();
5012    assert_eq!(
5013        editor.read_with(cx, |editor, cx| editor.text(cx)),
5014        "one, two\nthree\n"
5015    );
5016
5017    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5018    // Ensure we don't lock if formatting hangs.
5019    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5020        assert_eq!(
5021            params.text_document.uri,
5022            lsp::Url::from_file_path("/file.rs").unwrap()
5023        );
5024        futures::future::pending::<()>().await;
5025        unreachable!()
5026    });
5027    let format = editor.update(cx, |editor, cx| {
5028        editor.perform_format(project, FormatTrigger::Manual, cx)
5029    });
5030    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
5031    cx.foreground().start_waiting();
5032    format.await.unwrap();
5033    assert_eq!(
5034        editor.read_with(cx, |editor, cx| editor.text(cx)),
5035        "one\ntwo\nthree\n"
5036    );
5037}
5038
5039#[gpui::test]
5040async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
5041    init_test(cx, |_| {});
5042
5043    let mut cx = EditorLspTestContext::new_rust(
5044        lsp::ServerCapabilities {
5045            document_formatting_provider: Some(lsp::OneOf::Left(true)),
5046            ..Default::default()
5047        },
5048        cx,
5049    )
5050    .await;
5051
5052    cx.set_state(indoc! {"
5053        one.twoˇ
5054    "});
5055
5056    // The format request takes a long time. When it completes, it inserts
5057    // a newline and an indent before the `.`
5058    cx.lsp
5059        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
5060            let executor = cx.background();
5061            async move {
5062                executor.timer(Duration::from_millis(100)).await;
5063                Ok(Some(vec![lsp::TextEdit {
5064                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
5065                    new_text: "\n    ".into(),
5066                }]))
5067            }
5068        });
5069
5070    // Submit a format request.
5071    let format_1 = cx
5072        .update_editor(|editor, cx| editor.format(&Format, cx))
5073        .unwrap();
5074    cx.foreground().run_until_parked();
5075
5076    // Submit a second format request.
5077    let format_2 = cx
5078        .update_editor(|editor, cx| editor.format(&Format, cx))
5079        .unwrap();
5080    cx.foreground().run_until_parked();
5081
5082    // Wait for both format requests to complete
5083    cx.foreground().advance_clock(Duration::from_millis(200));
5084    cx.foreground().start_waiting();
5085    format_1.await.unwrap();
5086    cx.foreground().start_waiting();
5087    format_2.await.unwrap();
5088
5089    // The formatting edits only happens once.
5090    cx.assert_editor_state(indoc! {"
5091        one
5092            .twoˇ
5093    "});
5094}
5095
5096#[gpui::test]
5097async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
5098    init_test(cx, |_| {});
5099
5100    let mut cx = EditorLspTestContext::new_rust(
5101        lsp::ServerCapabilities {
5102            document_formatting_provider: Some(lsp::OneOf::Left(true)),
5103            ..Default::default()
5104        },
5105        cx,
5106    )
5107    .await;
5108
5109    // Set up a buffer white some trailing whitespace and no trailing newline.
5110    cx.set_state(
5111        &[
5112            "one ",   //
5113            "twoˇ",   //
5114            "three ", //
5115            "four",   //
5116        ]
5117        .join("\n"),
5118    );
5119
5120    // Submit a format request.
5121    let format = cx
5122        .update_editor(|editor, cx| editor.format(&Format, cx))
5123        .unwrap();
5124
5125    // Record which buffer changes have been sent to the language server
5126    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
5127    cx.lsp
5128        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
5129            let buffer_changes = buffer_changes.clone();
5130            move |params, _| {
5131                buffer_changes.lock().extend(
5132                    params
5133                        .content_changes
5134                        .into_iter()
5135                        .map(|e| (e.range.unwrap(), e.text)),
5136                );
5137            }
5138        });
5139
5140    // Handle formatting requests to the language server.
5141    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
5142        let buffer_changes = buffer_changes.clone();
5143        move |_, _| {
5144            // When formatting is requested, trailing whitespace has already been stripped,
5145            // and the trailing newline has already been added.
5146            assert_eq!(
5147                &buffer_changes.lock()[1..],
5148                &[
5149                    (
5150                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
5151                        "".into()
5152                    ),
5153                    (
5154                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
5155                        "".into()
5156                    ),
5157                    (
5158                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
5159                        "\n".into()
5160                    ),
5161                ]
5162            );
5163
5164            // Insert blank lines between each line of the buffer.
5165            async move {
5166                Ok(Some(vec![
5167                    lsp::TextEdit {
5168                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
5169                        new_text: "\n".into(),
5170                    },
5171                    lsp::TextEdit {
5172                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
5173                        new_text: "\n".into(),
5174                    },
5175                ]))
5176            }
5177        }
5178    });
5179
5180    // After formatting the buffer, the trailing whitespace is stripped,
5181    // a newline is appended, and the edits provided by the language server
5182    // have been applied.
5183    format.await.unwrap();
5184    cx.assert_editor_state(
5185        &[
5186            "one",   //
5187            "",      //
5188            "twoˇ",  //
5189            "",      //
5190            "three", //
5191            "four",  //
5192            "",      //
5193        ]
5194        .join("\n"),
5195    );
5196
5197    // Undoing the formatting undoes the trailing whitespace removal, the
5198    // trailing newline, and the LSP edits.
5199    cx.update_buffer(|buffer, cx| buffer.undo(cx));
5200    cx.assert_editor_state(
5201        &[
5202            "one ",   //
5203            "twoˇ",   //
5204            "three ", //
5205            "four",   //
5206        ]
5207        .join("\n"),
5208    );
5209}
5210
5211#[gpui::test]
5212async fn test_completion(cx: &mut gpui::TestAppContext) {
5213    init_test(cx, |_| {});
5214
5215    let mut cx = EditorLspTestContext::new_rust(
5216        lsp::ServerCapabilities {
5217            completion_provider: Some(lsp::CompletionOptions {
5218                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
5219                ..Default::default()
5220            }),
5221            ..Default::default()
5222        },
5223        cx,
5224    )
5225    .await;
5226
5227    cx.set_state(indoc! {"
5228        oneˇ
5229        two
5230        three
5231    "});
5232    cx.simulate_keystroke(".");
5233    handle_completion_request(
5234        &mut cx,
5235        indoc! {"
5236            one.|<>
5237            two
5238            three
5239        "},
5240        vec!["first_completion", "second_completion"],
5241    )
5242    .await;
5243    cx.condition(|editor, _| editor.context_menu_visible())
5244        .await;
5245    let apply_additional_edits = cx.update_editor(|editor, cx| {
5246        editor.move_down(&MoveDown, cx);
5247        editor
5248            .confirm_completion(&ConfirmCompletion::default(), cx)
5249            .unwrap()
5250    });
5251    cx.assert_editor_state(indoc! {"
5252        one.second_completionˇ
5253        two
5254        three
5255    "});
5256
5257    handle_resolve_completion_request(
5258        &mut cx,
5259        Some(vec![
5260            (
5261                //This overlaps with the primary completion edit which is
5262                //misbehavior from the LSP spec, test that we filter it out
5263                indoc! {"
5264                    one.second_ˇcompletion
5265                    two
5266                    threeˇ
5267                "},
5268                "overlapping additional edit",
5269            ),
5270            (
5271                indoc! {"
5272                    one.second_completion
5273                    two
5274                    threeˇ
5275                "},
5276                "\nadditional edit",
5277            ),
5278        ]),
5279    )
5280    .await;
5281    apply_additional_edits.await.unwrap();
5282    cx.assert_editor_state(indoc! {"
5283        one.second_completionˇ
5284        two
5285        three
5286        additional edit
5287    "});
5288
5289    cx.set_state(indoc! {"
5290        one.second_completion
5291        twoˇ
5292        threeˇ
5293        additional edit
5294    "});
5295    cx.simulate_keystroke(" ");
5296    assert!(cx.editor(|e, _| e.context_menu.is_none()));
5297    cx.simulate_keystroke("s");
5298    assert!(cx.editor(|e, _| e.context_menu.is_none()));
5299
5300    cx.assert_editor_state(indoc! {"
5301        one.second_completion
5302        two sˇ
5303        three sˇ
5304        additional edit
5305    "});
5306    handle_completion_request(
5307        &mut cx,
5308        indoc! {"
5309            one.second_completion
5310            two s
5311            three <s|>
5312            additional edit
5313        "},
5314        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
5315    )
5316    .await;
5317    cx.condition(|editor, _| editor.context_menu_visible())
5318        .await;
5319
5320    cx.simulate_keystroke("i");
5321
5322    handle_completion_request(
5323        &mut cx,
5324        indoc! {"
5325            one.second_completion
5326            two si
5327            three <si|>
5328            additional edit
5329        "},
5330        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
5331    )
5332    .await;
5333    cx.condition(|editor, _| editor.context_menu_visible())
5334        .await;
5335
5336    let apply_additional_edits = cx.update_editor(|editor, cx| {
5337        editor
5338            .confirm_completion(&ConfirmCompletion::default(), cx)
5339            .unwrap()
5340    });
5341    cx.assert_editor_state(indoc! {"
5342        one.second_completion
5343        two sixth_completionˇ
5344        three sixth_completionˇ
5345        additional edit
5346    "});
5347
5348    handle_resolve_completion_request(&mut cx, None).await;
5349    apply_additional_edits.await.unwrap();
5350
5351    cx.update(|cx| {
5352        cx.update_global::<SettingsStore, _, _>(|settings, cx| {
5353            settings.update_user_settings::<EditorSettings>(cx, |settings| {
5354                settings.show_completions_on_input = Some(false);
5355            });
5356        })
5357    });
5358    cx.set_state("editorˇ");
5359    cx.simulate_keystroke(".");
5360    assert!(cx.editor(|e, _| e.context_menu.is_none()));
5361    cx.simulate_keystroke("c");
5362    cx.simulate_keystroke("l");
5363    cx.simulate_keystroke("o");
5364    cx.assert_editor_state("editor.cloˇ");
5365    assert!(cx.editor(|e, _| e.context_menu.is_none()));
5366    cx.update_editor(|editor, cx| {
5367        editor.show_completions(&ShowCompletions, cx);
5368    });
5369    handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
5370    cx.condition(|editor, _| editor.context_menu_visible())
5371        .await;
5372    let apply_additional_edits = cx.update_editor(|editor, cx| {
5373        editor
5374            .confirm_completion(&ConfirmCompletion::default(), cx)
5375            .unwrap()
5376    });
5377    cx.assert_editor_state("editor.closeˇ");
5378    handle_resolve_completion_request(&mut cx, None).await;
5379    apply_additional_edits.await.unwrap();
5380}
5381
5382#[gpui::test]
5383async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
5384    init_test(cx, |_| {});
5385    let mut cx = EditorTestContext::new(cx).await;
5386    let language = Arc::new(Language::new(
5387        LanguageConfig {
5388            line_comment: Some("// ".into()),
5389            ..Default::default()
5390        },
5391        Some(tree_sitter_rust::language()),
5392    ));
5393    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
5394
5395    // If multiple selections intersect a line, the line is only toggled once.
5396    cx.set_state(indoc! {"
5397        fn a() {
5398            «//b();
5399            ˇ»// «c();
5400            //ˇ»  d();
5401        }
5402    "});
5403
5404    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5405
5406    cx.assert_editor_state(indoc! {"
5407        fn a() {
5408            «b();
5409            c();
5410            ˇ» d();
5411        }
5412    "});
5413
5414    // The comment prefix is inserted at the same column for every line in a
5415    // selection.
5416    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5417
5418    cx.assert_editor_state(indoc! {"
5419        fn a() {
5420            // «b();
5421            // c();
5422            ˇ»//  d();
5423        }
5424    "});
5425
5426    // If a selection ends at the beginning of a line, that line is not toggled.
5427    cx.set_selections_state(indoc! {"
5428        fn a() {
5429            // b();
5430            «// c();
5431        ˇ»    //  d();
5432        }
5433    "});
5434
5435    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5436
5437    cx.assert_editor_state(indoc! {"
5438        fn a() {
5439            // b();
5440            «c();
5441        ˇ»    //  d();
5442        }
5443    "});
5444
5445    // If a selection span a single line and is empty, the line is toggled.
5446    cx.set_state(indoc! {"
5447        fn a() {
5448            a();
5449            b();
5450        ˇ
5451        }
5452    "});
5453
5454    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5455
5456    cx.assert_editor_state(indoc! {"
5457        fn a() {
5458            a();
5459            b();
5460        //•ˇ
5461        }
5462    "});
5463
5464    // If a selection span multiple lines, empty lines are not toggled.
5465    cx.set_state(indoc! {"
5466        fn a() {
5467            «a();
5468
5469            c();ˇ»
5470        }
5471    "});
5472
5473    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5474
5475    cx.assert_editor_state(indoc! {"
5476        fn a() {
5477            // «a();
5478
5479            // c();ˇ»
5480        }
5481    "});
5482}
5483
5484#[gpui::test]
5485async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
5486    init_test(cx, |_| {});
5487
5488    let language = Arc::new(Language::new(
5489        LanguageConfig {
5490            line_comment: Some("// ".into()),
5491            ..Default::default()
5492        },
5493        Some(tree_sitter_rust::language()),
5494    ));
5495
5496    let registry = Arc::new(LanguageRegistry::test());
5497    registry.add(language.clone());
5498
5499    let mut cx = EditorTestContext::new(cx).await;
5500    cx.update_buffer(|buffer, cx| {
5501        buffer.set_language_registry(registry);
5502        buffer.set_language(Some(language), cx);
5503    });
5504
5505    let toggle_comments = &ToggleComments {
5506        advance_downwards: true,
5507    };
5508
5509    // Single cursor on one line -> advance
5510    // Cursor moves horizontally 3 characters as well on non-blank line
5511    cx.set_state(indoc!(
5512        "fn a() {
5513             ˇdog();
5514             cat();
5515        }"
5516    ));
5517    cx.update_editor(|editor, cx| {
5518        editor.toggle_comments(toggle_comments, cx);
5519    });
5520    cx.assert_editor_state(indoc!(
5521        "fn a() {
5522             // dog();
5523             catˇ();
5524        }"
5525    ));
5526
5527    // Single selection on one line -> don't advance
5528    cx.set_state(indoc!(
5529        "fn a() {
5530             «dog()ˇ»;
5531             cat();
5532        }"
5533    ));
5534    cx.update_editor(|editor, cx| {
5535        editor.toggle_comments(toggle_comments, cx);
5536    });
5537    cx.assert_editor_state(indoc!(
5538        "fn a() {
5539             // «dog()ˇ»;
5540             cat();
5541        }"
5542    ));
5543
5544    // Multiple cursors on one line -> advance
5545    cx.set_state(indoc!(
5546        "fn a() {
5547             ˇdˇog();
5548             cat();
5549        }"
5550    ));
5551    cx.update_editor(|editor, cx| {
5552        editor.toggle_comments(toggle_comments, cx);
5553    });
5554    cx.assert_editor_state(indoc!(
5555        "fn a() {
5556             // dog();
5557             catˇ(ˇ);
5558        }"
5559    ));
5560
5561    // Multiple cursors on one line, with selection -> don't advance
5562    cx.set_state(indoc!(
5563        "fn a() {
5564             ˇdˇog«()ˇ»;
5565             cat();
5566        }"
5567    ));
5568    cx.update_editor(|editor, cx| {
5569        editor.toggle_comments(toggle_comments, cx);
5570    });
5571    cx.assert_editor_state(indoc!(
5572        "fn a() {
5573             // ˇdˇog«()ˇ»;
5574             cat();
5575        }"
5576    ));
5577
5578    // Single cursor on one line -> advance
5579    // Cursor moves to column 0 on blank line
5580    cx.set_state(indoc!(
5581        "fn a() {
5582             ˇdog();
5583
5584             cat();
5585        }"
5586    ));
5587    cx.update_editor(|editor, cx| {
5588        editor.toggle_comments(toggle_comments, cx);
5589    });
5590    cx.assert_editor_state(indoc!(
5591        "fn a() {
5592             // dog();
5593        ˇ
5594             cat();
5595        }"
5596    ));
5597
5598    // Single cursor on one line -> advance
5599    // Cursor starts and ends at column 0
5600    cx.set_state(indoc!(
5601        "fn a() {
5602         ˇ    dog();
5603             cat();
5604        }"
5605    ));
5606    cx.update_editor(|editor, cx| {
5607        editor.toggle_comments(toggle_comments, cx);
5608    });
5609    cx.assert_editor_state(indoc!(
5610        "fn a() {
5611             // dog();
5612         ˇ    cat();
5613        }"
5614    ));
5615}
5616
5617#[gpui::test]
5618async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
5619    init_test(cx, |_| {});
5620
5621    let mut cx = EditorTestContext::new(cx).await;
5622
5623    let html_language = Arc::new(
5624        Language::new(
5625            LanguageConfig {
5626                name: "HTML".into(),
5627                block_comment: Some(("<!-- ".into(), " -->".into())),
5628                ..Default::default()
5629            },
5630            Some(tree_sitter_html::language()),
5631        )
5632        .with_injection_query(
5633            r#"
5634            (script_element
5635                (raw_text) @content
5636                (#set! "language" "javascript"))
5637            "#,
5638        )
5639        .unwrap(),
5640    );
5641
5642    let javascript_language = Arc::new(Language::new(
5643        LanguageConfig {
5644            name: "JavaScript".into(),
5645            line_comment: Some("// ".into()),
5646            ..Default::default()
5647        },
5648        Some(tree_sitter_typescript::language_tsx()),
5649    ));
5650
5651    let registry = Arc::new(LanguageRegistry::test());
5652    registry.add(html_language.clone());
5653    registry.add(javascript_language.clone());
5654
5655    cx.update_buffer(|buffer, cx| {
5656        buffer.set_language_registry(registry);
5657        buffer.set_language(Some(html_language), cx);
5658    });
5659
5660    // Toggle comments for empty selections
5661    cx.set_state(
5662        &r#"
5663            <p>A</p>ˇ
5664            <p>B</p>ˇ
5665            <p>C</p>ˇ
5666        "#
5667        .unindent(),
5668    );
5669    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5670    cx.assert_editor_state(
5671        &r#"
5672            <!-- <p>A</p>ˇ -->
5673            <!-- <p>B</p>ˇ -->
5674            <!-- <p>C</p>ˇ -->
5675        "#
5676        .unindent(),
5677    );
5678    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5679    cx.assert_editor_state(
5680        &r#"
5681            <p>A</p>ˇ
5682            <p>B</p>ˇ
5683            <p>C</p>ˇ
5684        "#
5685        .unindent(),
5686    );
5687
5688    // Toggle comments for mixture of empty and non-empty selections, where
5689    // multiple selections occupy a given line.
5690    cx.set_state(
5691        &r#"
5692            <p>A«</p>
5693            <p>ˇ»B</p>ˇ
5694            <p>C«</p>
5695            <p>ˇ»D</p>ˇ
5696        "#
5697        .unindent(),
5698    );
5699
5700    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5701    cx.assert_editor_state(
5702        &r#"
5703            <!-- <p>A«</p>
5704            <p>ˇ»B</p>ˇ -->
5705            <!-- <p>C«</p>
5706            <p>ˇ»D</p>ˇ -->
5707        "#
5708        .unindent(),
5709    );
5710    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5711    cx.assert_editor_state(
5712        &r#"
5713            <p>A«</p>
5714            <p>ˇ»B</p>ˇ
5715            <p>C«</p>
5716            <p>ˇ»D</p>ˇ
5717        "#
5718        .unindent(),
5719    );
5720
5721    // Toggle comments when different languages are active for different
5722    // selections.
5723    cx.set_state(
5724        &r#"
5725            ˇ<script>
5726                ˇvar x = new Y();
5727            ˇ</script>
5728        "#
5729        .unindent(),
5730    );
5731    cx.foreground().run_until_parked();
5732    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5733    cx.assert_editor_state(
5734        &r#"
5735            <!-- ˇ<script> -->
5736                // ˇvar x = new Y();
5737            <!-- ˇ</script> -->
5738        "#
5739        .unindent(),
5740    );
5741}
5742
5743#[gpui::test]
5744fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
5745    init_test(cx, |_| {});
5746
5747    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5748    let multibuffer = cx.add_model(|cx| {
5749        let mut multibuffer = MultiBuffer::new(0);
5750        multibuffer.push_excerpts(
5751            buffer.clone(),
5752            [
5753                ExcerptRange {
5754                    context: Point::new(0, 0)..Point::new(0, 4),
5755                    primary: None,
5756                },
5757                ExcerptRange {
5758                    context: Point::new(1, 0)..Point::new(1, 4),
5759                    primary: None,
5760                },
5761            ],
5762            cx,
5763        );
5764        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
5765        multibuffer
5766    });
5767
5768    let view = cx.add_window(|cx| build_editor(multibuffer, cx)).root(cx);
5769    view.update(cx, |view, cx| {
5770        assert_eq!(view.text(cx), "aaaa\nbbbb");
5771        view.change_selections(None, cx, |s| {
5772            s.select_ranges([
5773                Point::new(0, 0)..Point::new(0, 0),
5774                Point::new(1, 0)..Point::new(1, 0),
5775            ])
5776        });
5777
5778        view.handle_input("X", cx);
5779        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
5780        assert_eq!(
5781            view.selections.ranges(cx),
5782            [
5783                Point::new(0, 1)..Point::new(0, 1),
5784                Point::new(1, 1)..Point::new(1, 1),
5785            ]
5786        );
5787
5788        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
5789        view.change_selections(None, cx, |s| {
5790            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
5791        });
5792        view.backspace(&Default::default(), cx);
5793        assert_eq!(view.text(cx), "Xa\nbbb");
5794        assert_eq!(
5795            view.selections.ranges(cx),
5796            [Point::new(1, 0)..Point::new(1, 0)]
5797        );
5798
5799        view.change_selections(None, cx, |s| {
5800            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
5801        });
5802        view.backspace(&Default::default(), cx);
5803        assert_eq!(view.text(cx), "X\nbb");
5804        assert_eq!(
5805            view.selections.ranges(cx),
5806            [Point::new(0, 1)..Point::new(0, 1)]
5807        );
5808    });
5809}
5810
5811#[gpui::test]
5812fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
5813    init_test(cx, |_| {});
5814
5815    let markers = vec![('[', ']').into(), ('(', ')').into()];
5816    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
5817        indoc! {"
5818            [aaaa
5819            (bbbb]
5820            cccc)",
5821        },
5822        markers.clone(),
5823    );
5824    let excerpt_ranges = markers.into_iter().map(|marker| {
5825        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
5826        ExcerptRange {
5827            context,
5828            primary: None,
5829        }
5830    });
5831    let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
5832    let multibuffer = cx.add_model(|cx| {
5833        let mut multibuffer = MultiBuffer::new(0);
5834        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
5835        multibuffer
5836    });
5837
5838    let view = cx.add_window(|cx| build_editor(multibuffer, cx)).root(cx);
5839    view.update(cx, |view, cx| {
5840        let (expected_text, selection_ranges) = marked_text_ranges(
5841            indoc! {"
5842                aaaa
5843                bˇbbb
5844                bˇbbˇb
5845                cccc"
5846            },
5847            true,
5848        );
5849        assert_eq!(view.text(cx), expected_text);
5850        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
5851
5852        view.handle_input("X", cx);
5853
5854        let (expected_text, expected_selections) = marked_text_ranges(
5855            indoc! {"
5856                aaaa
5857                bXˇbbXb
5858                bXˇbbXˇb
5859                cccc"
5860            },
5861            false,
5862        );
5863        assert_eq!(view.text(cx), expected_text);
5864        assert_eq!(view.selections.ranges(cx), expected_selections);
5865
5866        view.newline(&Newline, cx);
5867        let (expected_text, expected_selections) = marked_text_ranges(
5868            indoc! {"
5869                aaaa
5870                bX
5871                ˇbbX
5872                b
5873                bX
5874                ˇbbX
5875                ˇb
5876                cccc"
5877            },
5878            false,
5879        );
5880        assert_eq!(view.text(cx), expected_text);
5881        assert_eq!(view.selections.ranges(cx), expected_selections);
5882    });
5883}
5884
5885#[gpui::test]
5886fn test_refresh_selections(cx: &mut TestAppContext) {
5887    init_test(cx, |_| {});
5888
5889    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5890    let mut excerpt1_id = None;
5891    let multibuffer = cx.add_model(|cx| {
5892        let mut multibuffer = MultiBuffer::new(0);
5893        excerpt1_id = multibuffer
5894            .push_excerpts(
5895                buffer.clone(),
5896                [
5897                    ExcerptRange {
5898                        context: Point::new(0, 0)..Point::new(1, 4),
5899                        primary: None,
5900                    },
5901                    ExcerptRange {
5902                        context: Point::new(1, 0)..Point::new(2, 4),
5903                        primary: None,
5904                    },
5905                ],
5906                cx,
5907            )
5908            .into_iter()
5909            .next();
5910        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
5911        multibuffer
5912    });
5913
5914    let editor = cx
5915        .add_window(|cx| {
5916            let mut editor = build_editor(multibuffer.clone(), cx);
5917            let snapshot = editor.snapshot(cx);
5918            editor.change_selections(None, cx, |s| {
5919                s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
5920            });
5921            editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
5922            assert_eq!(
5923                editor.selections.ranges(cx),
5924                [
5925                    Point::new(1, 3)..Point::new(1, 3),
5926                    Point::new(2, 1)..Point::new(2, 1),
5927                ]
5928            );
5929            editor
5930        })
5931        .root(cx);
5932
5933    // Refreshing selections is a no-op when excerpts haven't changed.
5934    editor.update(cx, |editor, cx| {
5935        editor.change_selections(None, cx, |s| s.refresh());
5936        assert_eq!(
5937            editor.selections.ranges(cx),
5938            [
5939                Point::new(1, 3)..Point::new(1, 3),
5940                Point::new(2, 1)..Point::new(2, 1),
5941            ]
5942        );
5943    });
5944
5945    multibuffer.update(cx, |multibuffer, cx| {
5946        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
5947    });
5948    editor.update(cx, |editor, cx| {
5949        // Removing an excerpt causes the first selection to become degenerate.
5950        assert_eq!(
5951            editor.selections.ranges(cx),
5952            [
5953                Point::new(0, 0)..Point::new(0, 0),
5954                Point::new(0, 1)..Point::new(0, 1)
5955            ]
5956        );
5957
5958        // Refreshing selections will relocate the first selection to the original buffer
5959        // location.
5960        editor.change_selections(None, cx, |s| s.refresh());
5961        assert_eq!(
5962            editor.selections.ranges(cx),
5963            [
5964                Point::new(0, 1)..Point::new(0, 1),
5965                Point::new(0, 3)..Point::new(0, 3)
5966            ]
5967        );
5968        assert!(editor.selections.pending_anchor().is_some());
5969    });
5970}
5971
5972#[gpui::test]
5973fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
5974    init_test(cx, |_| {});
5975
5976    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5977    let mut excerpt1_id = None;
5978    let multibuffer = cx.add_model(|cx| {
5979        let mut multibuffer = MultiBuffer::new(0);
5980        excerpt1_id = multibuffer
5981            .push_excerpts(
5982                buffer.clone(),
5983                [
5984                    ExcerptRange {
5985                        context: Point::new(0, 0)..Point::new(1, 4),
5986                        primary: None,
5987                    },
5988                    ExcerptRange {
5989                        context: Point::new(1, 0)..Point::new(2, 4),
5990                        primary: None,
5991                    },
5992                ],
5993                cx,
5994            )
5995            .into_iter()
5996            .next();
5997        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
5998        multibuffer
5999    });
6000
6001    let editor = cx
6002        .add_window(|cx| {
6003            let mut editor = build_editor(multibuffer.clone(), cx);
6004            let snapshot = editor.snapshot(cx);
6005            editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
6006            assert_eq!(
6007                editor.selections.ranges(cx),
6008                [Point::new(1, 3)..Point::new(1, 3)]
6009            );
6010            editor
6011        })
6012        .root(cx);
6013
6014    multibuffer.update(cx, |multibuffer, cx| {
6015        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
6016    });
6017    editor.update(cx, |editor, cx| {
6018        assert_eq!(
6019            editor.selections.ranges(cx),
6020            [Point::new(0, 0)..Point::new(0, 0)]
6021        );
6022
6023        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
6024        editor.change_selections(None, cx, |s| s.refresh());
6025        assert_eq!(
6026            editor.selections.ranges(cx),
6027            [Point::new(0, 3)..Point::new(0, 3)]
6028        );
6029        assert!(editor.selections.pending_anchor().is_some());
6030    });
6031}
6032
6033#[gpui::test]
6034async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
6035    init_test(cx, |_| {});
6036
6037    let language = Arc::new(
6038        Language::new(
6039            LanguageConfig {
6040                brackets: BracketPairConfig {
6041                    pairs: vec![
6042                        BracketPair {
6043                            start: "{".to_string(),
6044                            end: "}".to_string(),
6045                            close: true,
6046                            newline: true,
6047                        },
6048                        BracketPair {
6049                            start: "/* ".to_string(),
6050                            end: " */".to_string(),
6051                            close: true,
6052                            newline: true,
6053                        },
6054                    ],
6055                    ..Default::default()
6056                },
6057                ..Default::default()
6058            },
6059            Some(tree_sitter_rust::language()),
6060        )
6061        .with_indents_query("")
6062        .unwrap(),
6063    );
6064
6065    let text = concat!(
6066        "{   }\n",     //
6067        "  x\n",       //
6068        "  /*   */\n", //
6069        "x\n",         //
6070        "{{} }\n",     //
6071    );
6072
6073    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
6074    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
6075    let view = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
6076    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
6077        .await;
6078
6079    view.update(cx, |view, cx| {
6080        view.change_selections(None, cx, |s| {
6081            s.select_display_ranges([
6082                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
6083                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
6084                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
6085            ])
6086        });
6087        view.newline(&Newline, cx);
6088
6089        assert_eq!(
6090            view.buffer().read(cx).read(cx).text(),
6091            concat!(
6092                "{ \n",    // Suppress rustfmt
6093                "\n",      //
6094                "}\n",     //
6095                "  x\n",   //
6096                "  /* \n", //
6097                "  \n",    //
6098                "  */\n",  //
6099                "x\n",     //
6100                "{{} \n",  //
6101                "}\n",     //
6102            )
6103        );
6104    });
6105}
6106
6107#[gpui::test]
6108fn test_highlighted_ranges(cx: &mut TestAppContext) {
6109    init_test(cx, |_| {});
6110
6111    let editor = cx
6112        .add_window(|cx| {
6113            let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
6114            build_editor(buffer.clone(), cx)
6115        })
6116        .root(cx);
6117
6118    editor.update(cx, |editor, cx| {
6119        struct Type1;
6120        struct Type2;
6121
6122        let buffer = editor.buffer.read(cx).snapshot(cx);
6123
6124        let anchor_range =
6125            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
6126
6127        editor.highlight_background::<Type1>(
6128            vec![
6129                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
6130                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
6131                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
6132                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
6133            ],
6134            |_| Color::red(),
6135            cx,
6136        );
6137        editor.highlight_background::<Type2>(
6138            vec![
6139                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
6140                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
6141                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
6142                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
6143            ],
6144            |_| Color::green(),
6145            cx,
6146        );
6147
6148        let snapshot = editor.snapshot(cx);
6149        let mut highlighted_ranges = editor.background_highlights_in_range(
6150            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
6151            &snapshot,
6152            theme::current(cx).as_ref(),
6153        );
6154        // Enforce a consistent ordering based on color without relying on the ordering of the
6155        // highlight's `TypeId` which is non-deterministic.
6156        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
6157        assert_eq!(
6158            highlighted_ranges,
6159            &[
6160                (
6161                    DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
6162                    Color::green(),
6163                ),
6164                (
6165                    DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
6166                    Color::green(),
6167                ),
6168                (
6169                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
6170                    Color::red(),
6171                ),
6172                (
6173                    DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
6174                    Color::red(),
6175                ),
6176            ]
6177        );
6178        assert_eq!(
6179            editor.background_highlights_in_range(
6180                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
6181                &snapshot,
6182                theme::current(cx).as_ref(),
6183            ),
6184            &[(
6185                DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
6186                Color::red(),
6187            )]
6188        );
6189    });
6190}
6191
6192#[gpui::test]
6193async fn test_following(cx: &mut gpui::TestAppContext) {
6194    init_test(cx, |_| {});
6195
6196    let fs = FakeFs::new(cx.background());
6197    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
6198
6199    let buffer = project.update(cx, |project, cx| {
6200        let buffer = project
6201            .create_buffer(&sample_text(16, 8, 'a'), None, cx)
6202            .unwrap();
6203        cx.add_model(|cx| MultiBuffer::singleton(buffer, cx))
6204    });
6205    let leader = cx
6206        .add_window(|cx| build_editor(buffer.clone(), cx))
6207        .root(cx);
6208    let follower = cx
6209        .update(|cx| {
6210            cx.add_window(
6211                WindowOptions {
6212                    bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
6213                    ..Default::default()
6214                },
6215                |cx| build_editor(buffer.clone(), cx),
6216            )
6217        })
6218        .root(cx);
6219
6220    let is_still_following = Rc::new(RefCell::new(true));
6221    let follower_edit_event_count = Rc::new(RefCell::new(0));
6222    let pending_update = Rc::new(RefCell::new(None));
6223    follower.update(cx, {
6224        let update = pending_update.clone();
6225        let is_still_following = is_still_following.clone();
6226        let follower_edit_event_count = follower_edit_event_count.clone();
6227        |_, cx| {
6228            cx.subscribe(&leader, move |_, leader, event, cx| {
6229                leader
6230                    .read(cx)
6231                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
6232            })
6233            .detach();
6234
6235            cx.subscribe(&follower, move |_, _, event, cx| {
6236                if Editor::should_unfollow_on_event(event, cx) {
6237                    *is_still_following.borrow_mut() = false;
6238                }
6239                if let Event::BufferEdited = event {
6240                    *follower_edit_event_count.borrow_mut() += 1;
6241                }
6242            })
6243            .detach();
6244        }
6245    });
6246
6247    // Update the selections only
6248    leader.update(cx, |leader, cx| {
6249        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
6250    });
6251    follower
6252        .update(cx, |follower, cx| {
6253            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6254        })
6255        .await
6256        .unwrap();
6257    follower.read_with(cx, |follower, cx| {
6258        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
6259    });
6260    assert_eq!(*is_still_following.borrow(), true);
6261    assert_eq!(*follower_edit_event_count.borrow(), 0);
6262
6263    // Update the scroll position only
6264    leader.update(cx, |leader, cx| {
6265        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
6266    });
6267    follower
6268        .update(cx, |follower, cx| {
6269            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6270        })
6271        .await
6272        .unwrap();
6273    assert_eq!(
6274        follower.update(cx, |follower, cx| follower.scroll_position(cx)),
6275        vec2f(1.5, 3.5)
6276    );
6277    assert_eq!(*is_still_following.borrow(), true);
6278    assert_eq!(*follower_edit_event_count.borrow(), 0);
6279
6280    // Update the selections and scroll position. The follower's scroll position is updated
6281    // via autoscroll, not via the leader's exact scroll position.
6282    leader.update(cx, |leader, cx| {
6283        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
6284        leader.request_autoscroll(Autoscroll::newest(), cx);
6285        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
6286    });
6287    follower
6288        .update(cx, |follower, cx| {
6289            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6290        })
6291        .await
6292        .unwrap();
6293    follower.update(cx, |follower, cx| {
6294        assert_eq!(follower.scroll_position(cx), vec2f(1.5, 0.0));
6295        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
6296    });
6297    assert_eq!(*is_still_following.borrow(), true);
6298
6299    // Creating a pending selection that precedes another selection
6300    leader.update(cx, |leader, cx| {
6301        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
6302        leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
6303    });
6304    follower
6305        .update(cx, |follower, cx| {
6306            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6307        })
6308        .await
6309        .unwrap();
6310    follower.read_with(cx, |follower, cx| {
6311        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
6312    });
6313    assert_eq!(*is_still_following.borrow(), true);
6314
6315    // Extend the pending selection so that it surrounds another selection
6316    leader.update(cx, |leader, cx| {
6317        leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
6318    });
6319    follower
6320        .update(cx, |follower, cx| {
6321            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6322        })
6323        .await
6324        .unwrap();
6325    follower.read_with(cx, |follower, cx| {
6326        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
6327    });
6328
6329    // Scrolling locally breaks the follow
6330    follower.update(cx, |follower, cx| {
6331        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
6332        follower.set_scroll_anchor(
6333            ScrollAnchor {
6334                anchor: top_anchor,
6335                offset: vec2f(0.0, 0.5),
6336            },
6337            cx,
6338        );
6339    });
6340    assert_eq!(*is_still_following.borrow(), false);
6341}
6342
6343#[gpui::test]
6344async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
6345    init_test(cx, |_| {});
6346
6347    let fs = FakeFs::new(cx.background());
6348    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
6349    let workspace = cx
6350        .add_window(|cx| Workspace::test_new(project.clone(), cx))
6351        .root(cx);
6352    let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
6353
6354    let leader = pane.update(cx, |_, cx| {
6355        let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
6356        cx.add_view(|cx| build_editor(multibuffer.clone(), cx))
6357    });
6358
6359    // Start following the editor when it has no excerpts.
6360    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
6361    let follower_1 = cx
6362        .update(|cx| {
6363            Editor::from_state_proto(
6364                pane.clone(),
6365                project.clone(),
6366                ViewId {
6367                    creator: Default::default(),
6368                    id: 0,
6369                },
6370                &mut state_message,
6371                cx,
6372            )
6373        })
6374        .unwrap()
6375        .await
6376        .unwrap();
6377
6378    let update_message = Rc::new(RefCell::new(None));
6379    follower_1.update(cx, {
6380        let update = update_message.clone();
6381        |_, cx| {
6382            cx.subscribe(&leader, move |_, leader, event, cx| {
6383                leader
6384                    .read(cx)
6385                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
6386            })
6387            .detach();
6388        }
6389    });
6390
6391    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
6392        (
6393            project
6394                .create_buffer("abc\ndef\nghi\njkl\n", None, cx)
6395                .unwrap(),
6396            project
6397                .create_buffer("mno\npqr\nstu\nvwx\n", None, cx)
6398                .unwrap(),
6399        )
6400    });
6401
6402    // Insert some excerpts.
6403    leader.update(cx, |leader, cx| {
6404        leader.buffer.update(cx, |multibuffer, cx| {
6405            let excerpt_ids = multibuffer.push_excerpts(
6406                buffer_1.clone(),
6407                [
6408                    ExcerptRange {
6409                        context: 1..6,
6410                        primary: None,
6411                    },
6412                    ExcerptRange {
6413                        context: 12..15,
6414                        primary: None,
6415                    },
6416                    ExcerptRange {
6417                        context: 0..3,
6418                        primary: None,
6419                    },
6420                ],
6421                cx,
6422            );
6423            multibuffer.insert_excerpts_after(
6424                excerpt_ids[0],
6425                buffer_2.clone(),
6426                [
6427                    ExcerptRange {
6428                        context: 8..12,
6429                        primary: None,
6430                    },
6431                    ExcerptRange {
6432                        context: 0..6,
6433                        primary: None,
6434                    },
6435                ],
6436                cx,
6437            );
6438        });
6439    });
6440
6441    // Apply the update of adding the excerpts.
6442    follower_1
6443        .update(cx, |follower, cx| {
6444            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6445        })
6446        .await
6447        .unwrap();
6448    assert_eq!(
6449        follower_1.read_with(cx, |editor, cx| editor.text(cx)),
6450        leader.read_with(cx, |editor, cx| editor.text(cx))
6451    );
6452    update_message.borrow_mut().take();
6453
6454    // Start following separately after it already has excerpts.
6455    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
6456    let follower_2 = cx
6457        .update(|cx| {
6458            Editor::from_state_proto(
6459                pane.clone(),
6460                project.clone(),
6461                ViewId {
6462                    creator: Default::default(),
6463                    id: 0,
6464                },
6465                &mut state_message,
6466                cx,
6467            )
6468        })
6469        .unwrap()
6470        .await
6471        .unwrap();
6472    assert_eq!(
6473        follower_2.read_with(cx, |editor, cx| editor.text(cx)),
6474        leader.read_with(cx, |editor, cx| editor.text(cx))
6475    );
6476
6477    // Remove some excerpts.
6478    leader.update(cx, |leader, cx| {
6479        leader.buffer.update(cx, |multibuffer, cx| {
6480            let excerpt_ids = multibuffer.excerpt_ids();
6481            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
6482            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
6483        });
6484    });
6485
6486    // Apply the update of removing the excerpts.
6487    follower_1
6488        .update(cx, |follower, cx| {
6489            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6490        })
6491        .await
6492        .unwrap();
6493    follower_2
6494        .update(cx, |follower, cx| {
6495            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6496        })
6497        .await
6498        .unwrap();
6499    update_message.borrow_mut().take();
6500    assert_eq!(
6501        follower_1.read_with(cx, |editor, cx| editor.text(cx)),
6502        leader.read_with(cx, |editor, cx| editor.text(cx))
6503    );
6504}
6505
6506#[test]
6507fn test_combine_syntax_and_fuzzy_match_highlights() {
6508    let string = "abcdefghijklmnop";
6509    let syntax_ranges = [
6510        (
6511            0..3,
6512            HighlightStyle {
6513                color: Some(Color::red()),
6514                ..Default::default()
6515            },
6516        ),
6517        (
6518            4..8,
6519            HighlightStyle {
6520                color: Some(Color::green()),
6521                ..Default::default()
6522            },
6523        ),
6524    ];
6525    let match_indices = [4, 6, 7, 8];
6526    assert_eq!(
6527        combine_syntax_and_fuzzy_match_highlights(
6528            string,
6529            Default::default(),
6530            syntax_ranges.into_iter(),
6531            &match_indices,
6532        ),
6533        &[
6534            (
6535                0..3,
6536                HighlightStyle {
6537                    color: Some(Color::red()),
6538                    ..Default::default()
6539                },
6540            ),
6541            (
6542                4..5,
6543                HighlightStyle {
6544                    color: Some(Color::green()),
6545                    weight: Some(fonts::Weight::BOLD),
6546                    ..Default::default()
6547                },
6548            ),
6549            (
6550                5..6,
6551                HighlightStyle {
6552                    color: Some(Color::green()),
6553                    ..Default::default()
6554                },
6555            ),
6556            (
6557                6..8,
6558                HighlightStyle {
6559                    color: Some(Color::green()),
6560                    weight: Some(fonts::Weight::BOLD),
6561                    ..Default::default()
6562                },
6563            ),
6564            (
6565                8..9,
6566                HighlightStyle {
6567                    weight: Some(fonts::Weight::BOLD),
6568                    ..Default::default()
6569                },
6570            ),
6571        ]
6572    );
6573}
6574
6575#[gpui::test]
6576async fn go_to_hunk(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
6577    init_test(cx, |_| {});
6578
6579    let mut cx = EditorTestContext::new(cx).await;
6580
6581    let diff_base = r#"
6582        use some::mod;
6583
6584        const A: u32 = 42;
6585
6586        fn main() {
6587            println!("hello");
6588
6589            println!("world");
6590        }
6591        "#
6592    .unindent();
6593
6594    // Edits are modified, removed, modified, added
6595    cx.set_state(
6596        &r#"
6597        use some::modified;
6598
6599        ˇ
6600        fn main() {
6601            println!("hello there");
6602
6603            println!("around the");
6604            println!("world");
6605        }
6606        "#
6607        .unindent(),
6608    );
6609
6610    cx.set_diff_base(Some(&diff_base));
6611    deterministic.run_until_parked();
6612
6613    cx.update_editor(|editor, cx| {
6614        //Wrap around the bottom of the buffer
6615        for _ in 0..3 {
6616            editor.go_to_hunk(&GoToHunk, cx);
6617        }
6618    });
6619
6620    cx.assert_editor_state(
6621        &r#"
6622        ˇuse some::modified;
6623
6624
6625        fn main() {
6626            println!("hello there");
6627
6628            println!("around the");
6629            println!("world");
6630        }
6631        "#
6632        .unindent(),
6633    );
6634
6635    cx.update_editor(|editor, cx| {
6636        //Wrap around the top of the buffer
6637        for _ in 0..2 {
6638            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
6639        }
6640    });
6641
6642    cx.assert_editor_state(
6643        &r#"
6644        use some::modified;
6645
6646
6647        fn main() {
6648        ˇ    println!("hello there");
6649
6650            println!("around the");
6651            println!("world");
6652        }
6653        "#
6654        .unindent(),
6655    );
6656
6657    cx.update_editor(|editor, cx| {
6658        editor.fold(&Fold, cx);
6659
6660        //Make sure that the fold only gets one hunk
6661        for _ in 0..4 {
6662            editor.go_to_hunk(&GoToHunk, cx);
6663        }
6664    });
6665
6666    cx.assert_editor_state(
6667        &r#"
6668        ˇuse some::modified;
6669
6670
6671        fn main() {
6672            println!("hello there");
6673
6674            println!("around the");
6675            println!("world");
6676        }
6677        "#
6678        .unindent(),
6679    );
6680}
6681
6682#[test]
6683fn test_split_words() {
6684    fn split<'a>(text: &'a str) -> Vec<&'a str> {
6685        split_words(text).collect()
6686    }
6687
6688    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
6689    assert_eq!(split("hello_world"), &["hello_", "world"]);
6690    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
6691    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
6692    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
6693    assert_eq!(split("helloworld"), &["helloworld"]);
6694}
6695
6696#[gpui::test]
6697async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
6698    init_test(cx, |_| {});
6699
6700    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
6701    let mut assert = |before, after| {
6702        let _state_context = cx.set_state(before);
6703        cx.update_editor(|editor, cx| {
6704            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
6705        });
6706        cx.assert_editor_state(after);
6707    };
6708
6709    // Outside bracket jumps to outside of matching bracket
6710    assert("console.logˇ(var);", "console.log(var)ˇ;");
6711    assert("console.log(var)ˇ;", "console.logˇ(var);");
6712
6713    // Inside bracket jumps to inside of matching bracket
6714    assert("console.log(ˇvar);", "console.log(varˇ);");
6715    assert("console.log(varˇ);", "console.log(ˇvar);");
6716
6717    // When outside a bracket and inside, favor jumping to the inside bracket
6718    assert(
6719        "console.log('foo', [1, 2, 3]ˇ);",
6720        "console.log(ˇ'foo', [1, 2, 3]);",
6721    );
6722    assert(
6723        "console.log(ˇ'foo', [1, 2, 3]);",
6724        "console.log('foo', [1, 2, 3]ˇ);",
6725    );
6726
6727    // Bias forward if two options are equally likely
6728    assert(
6729        "let result = curried_fun()ˇ();",
6730        "let result = curried_fun()()ˇ;",
6731    );
6732
6733    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
6734    assert(
6735        indoc! {"
6736            function test() {
6737                console.log('test')ˇ
6738            }"},
6739        indoc! {"
6740            function test() {
6741                console.logˇ('test')
6742            }"},
6743    );
6744}
6745
6746#[gpui::test(iterations = 10)]
6747async fn test_copilot(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
6748    init_test(cx, |_| {});
6749
6750    let (copilot, copilot_lsp) = Copilot::fake(cx);
6751    cx.update(|cx| cx.set_global(copilot));
6752    let mut cx = EditorLspTestContext::new_rust(
6753        lsp::ServerCapabilities {
6754            completion_provider: Some(lsp::CompletionOptions {
6755                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
6756                ..Default::default()
6757            }),
6758            ..Default::default()
6759        },
6760        cx,
6761    )
6762    .await;
6763
6764    // When inserting, ensure autocompletion is favored over Copilot suggestions.
6765    cx.set_state(indoc! {"
6766        oneˇ
6767        two
6768        three
6769    "});
6770    cx.simulate_keystroke(".");
6771    let _ = handle_completion_request(
6772        &mut cx,
6773        indoc! {"
6774            one.|<>
6775            two
6776            three
6777        "},
6778        vec!["completion_a", "completion_b"],
6779    );
6780    handle_copilot_completion_request(
6781        &copilot_lsp,
6782        vec![copilot::request::Completion {
6783            text: "one.copilot1".into(),
6784            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6785            ..Default::default()
6786        }],
6787        vec![],
6788    );
6789    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6790    cx.update_editor(|editor, cx| {
6791        assert!(editor.context_menu_visible());
6792        assert!(!editor.has_active_copilot_suggestion(cx));
6793
6794        // Confirming a completion inserts it and hides the context menu, without showing
6795        // the copilot suggestion afterwards.
6796        editor
6797            .confirm_completion(&Default::default(), cx)
6798            .unwrap()
6799            .detach();
6800        assert!(!editor.context_menu_visible());
6801        assert!(!editor.has_active_copilot_suggestion(cx));
6802        assert_eq!(editor.text(cx), "one.completion_a\ntwo\nthree\n");
6803        assert_eq!(editor.display_text(cx), "one.completion_a\ntwo\nthree\n");
6804    });
6805
6806    // Ensure Copilot suggestions are shown right away if no autocompletion is available.
6807    cx.set_state(indoc! {"
6808        oneˇ
6809        two
6810        three
6811    "});
6812    cx.simulate_keystroke(".");
6813    let _ = handle_completion_request(
6814        &mut cx,
6815        indoc! {"
6816            one.|<>
6817            two
6818            three
6819        "},
6820        vec![],
6821    );
6822    handle_copilot_completion_request(
6823        &copilot_lsp,
6824        vec![copilot::request::Completion {
6825            text: "one.copilot1".into(),
6826            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6827            ..Default::default()
6828        }],
6829        vec![],
6830    );
6831    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6832    cx.update_editor(|editor, cx| {
6833        assert!(!editor.context_menu_visible());
6834        assert!(editor.has_active_copilot_suggestion(cx));
6835        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
6836        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
6837    });
6838
6839    // Reset editor, and ensure autocompletion is still favored over Copilot suggestions.
6840    cx.set_state(indoc! {"
6841        oneˇ
6842        two
6843        three
6844    "});
6845    cx.simulate_keystroke(".");
6846    let _ = handle_completion_request(
6847        &mut cx,
6848        indoc! {"
6849            one.|<>
6850            two
6851            three
6852        "},
6853        vec!["completion_a", "completion_b"],
6854    );
6855    handle_copilot_completion_request(
6856        &copilot_lsp,
6857        vec![copilot::request::Completion {
6858            text: "one.copilot1".into(),
6859            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6860            ..Default::default()
6861        }],
6862        vec![],
6863    );
6864    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6865    cx.update_editor(|editor, cx| {
6866        assert!(editor.context_menu_visible());
6867        assert!(!editor.has_active_copilot_suggestion(cx));
6868
6869        // When hiding the context menu, the Copilot suggestion becomes visible.
6870        editor.hide_context_menu(cx);
6871        assert!(!editor.context_menu_visible());
6872        assert!(editor.has_active_copilot_suggestion(cx));
6873        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
6874        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
6875    });
6876
6877    // Ensure existing completion is interpolated when inserting again.
6878    cx.simulate_keystroke("c");
6879    deterministic.run_until_parked();
6880    cx.update_editor(|editor, cx| {
6881        assert!(!editor.context_menu_visible());
6882        assert!(editor.has_active_copilot_suggestion(cx));
6883        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
6884        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6885    });
6886
6887    // After debouncing, new Copilot completions should be requested.
6888    handle_copilot_completion_request(
6889        &copilot_lsp,
6890        vec![copilot::request::Completion {
6891            text: "one.copilot2".into(),
6892            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 5)),
6893            ..Default::default()
6894        }],
6895        vec![],
6896    );
6897    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6898    cx.update_editor(|editor, cx| {
6899        assert!(!editor.context_menu_visible());
6900        assert!(editor.has_active_copilot_suggestion(cx));
6901        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6902        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6903
6904        // Canceling should remove the active Copilot suggestion.
6905        editor.cancel(&Default::default(), cx);
6906        assert!(!editor.has_active_copilot_suggestion(cx));
6907        assert_eq!(editor.display_text(cx), "one.c\ntwo\nthree\n");
6908        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6909
6910        // After canceling, tabbing shouldn't insert the previously shown suggestion.
6911        editor.tab(&Default::default(), cx);
6912        assert!(!editor.has_active_copilot_suggestion(cx));
6913        assert_eq!(editor.display_text(cx), "one.c   \ntwo\nthree\n");
6914        assert_eq!(editor.text(cx), "one.c   \ntwo\nthree\n");
6915
6916        // When undoing the previously active suggestion is shown again.
6917        editor.undo(&Default::default(), cx);
6918        assert!(editor.has_active_copilot_suggestion(cx));
6919        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6920        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6921    });
6922
6923    // If an edit occurs outside of this editor, the suggestion is still correctly interpolated.
6924    cx.update_buffer(|buffer, cx| buffer.edit([(5..5, "o")], None, cx));
6925    cx.update_editor(|editor, cx| {
6926        assert!(editor.has_active_copilot_suggestion(cx));
6927        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6928        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6929
6930        // Tabbing when there is an active suggestion inserts it.
6931        editor.tab(&Default::default(), cx);
6932        assert!(!editor.has_active_copilot_suggestion(cx));
6933        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6934        assert_eq!(editor.text(cx), "one.copilot2\ntwo\nthree\n");
6935
6936        // When undoing the previously active suggestion is shown again.
6937        editor.undo(&Default::default(), cx);
6938        assert!(editor.has_active_copilot_suggestion(cx));
6939        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6940        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6941
6942        // Hide suggestion.
6943        editor.cancel(&Default::default(), cx);
6944        assert!(!editor.has_active_copilot_suggestion(cx));
6945        assert_eq!(editor.display_text(cx), "one.co\ntwo\nthree\n");
6946        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6947    });
6948
6949    // If an edit occurs outside of this editor but no suggestion is being shown,
6950    // we won't make it visible.
6951    cx.update_buffer(|buffer, cx| buffer.edit([(6..6, "p")], None, cx));
6952    cx.update_editor(|editor, cx| {
6953        assert!(!editor.has_active_copilot_suggestion(cx));
6954        assert_eq!(editor.display_text(cx), "one.cop\ntwo\nthree\n");
6955        assert_eq!(editor.text(cx), "one.cop\ntwo\nthree\n");
6956    });
6957
6958    // Reset the editor to verify how suggestions behave when tabbing on leading indentation.
6959    cx.update_editor(|editor, cx| {
6960        editor.set_text("fn foo() {\n  \n}", cx);
6961        editor.change_selections(None, cx, |s| {
6962            s.select_ranges([Point::new(1, 2)..Point::new(1, 2)])
6963        });
6964    });
6965    handle_copilot_completion_request(
6966        &copilot_lsp,
6967        vec![copilot::request::Completion {
6968            text: "    let x = 4;".into(),
6969            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
6970            ..Default::default()
6971        }],
6972        vec![],
6973    );
6974
6975    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
6976    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6977    cx.update_editor(|editor, cx| {
6978        assert!(editor.has_active_copilot_suggestion(cx));
6979        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6980        assert_eq!(editor.text(cx), "fn foo() {\n  \n}");
6981
6982        // Tabbing inside of leading whitespace inserts indentation without accepting the suggestion.
6983        editor.tab(&Default::default(), cx);
6984        assert!(editor.has_active_copilot_suggestion(cx));
6985        assert_eq!(editor.text(cx), "fn foo() {\n    \n}");
6986        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6987
6988        // Tabbing again accepts the suggestion.
6989        editor.tab(&Default::default(), cx);
6990        assert!(!editor.has_active_copilot_suggestion(cx));
6991        assert_eq!(editor.text(cx), "fn foo() {\n    let x = 4;\n}");
6992        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6993    });
6994}
6995
6996#[gpui::test]
6997async fn test_copilot_completion_invalidation(
6998    deterministic: Arc<Deterministic>,
6999    cx: &mut gpui::TestAppContext,
7000) {
7001    init_test(cx, |_| {});
7002
7003    let (copilot, copilot_lsp) = Copilot::fake(cx);
7004    cx.update(|cx| cx.set_global(copilot));
7005    let mut cx = EditorLspTestContext::new_rust(
7006        lsp::ServerCapabilities {
7007            completion_provider: Some(lsp::CompletionOptions {
7008                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
7009                ..Default::default()
7010            }),
7011            ..Default::default()
7012        },
7013        cx,
7014    )
7015    .await;
7016
7017    cx.set_state(indoc! {"
7018        one
7019        twˇ
7020        three
7021    "});
7022
7023    handle_copilot_completion_request(
7024        &copilot_lsp,
7025        vec![copilot::request::Completion {
7026            text: "two.foo()".into(),
7027            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
7028            ..Default::default()
7029        }],
7030        vec![],
7031    );
7032    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
7033    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7034    cx.update_editor(|editor, cx| {
7035        assert!(editor.has_active_copilot_suggestion(cx));
7036        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7037        assert_eq!(editor.text(cx), "one\ntw\nthree\n");
7038
7039        editor.backspace(&Default::default(), cx);
7040        assert!(editor.has_active_copilot_suggestion(cx));
7041        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7042        assert_eq!(editor.text(cx), "one\nt\nthree\n");
7043
7044        editor.backspace(&Default::default(), cx);
7045        assert!(editor.has_active_copilot_suggestion(cx));
7046        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7047        assert_eq!(editor.text(cx), "one\n\nthree\n");
7048
7049        // Deleting across the original suggestion range invalidates it.
7050        editor.backspace(&Default::default(), cx);
7051        assert!(!editor.has_active_copilot_suggestion(cx));
7052        assert_eq!(editor.display_text(cx), "one\nthree\n");
7053        assert_eq!(editor.text(cx), "one\nthree\n");
7054
7055        // Undoing the deletion restores the suggestion.
7056        editor.undo(&Default::default(), cx);
7057        assert!(editor.has_active_copilot_suggestion(cx));
7058        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7059        assert_eq!(editor.text(cx), "one\n\nthree\n");
7060    });
7061}
7062
7063#[gpui::test]
7064async fn test_copilot_multibuffer(
7065    deterministic: Arc<Deterministic>,
7066    cx: &mut gpui::TestAppContext,
7067) {
7068    init_test(cx, |_| {});
7069
7070    let (copilot, copilot_lsp) = Copilot::fake(cx);
7071    cx.update(|cx| cx.set_global(copilot));
7072
7073    let buffer_1 = cx.add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx));
7074    let buffer_2 = cx.add_model(|cx| Buffer::new(0, "c = 3\nd = 4\n", cx));
7075    let multibuffer = cx.add_model(|cx| {
7076        let mut multibuffer = MultiBuffer::new(0);
7077        multibuffer.push_excerpts(
7078            buffer_1.clone(),
7079            [ExcerptRange {
7080                context: Point::new(0, 0)..Point::new(2, 0),
7081                primary: None,
7082            }],
7083            cx,
7084        );
7085        multibuffer.push_excerpts(
7086            buffer_2.clone(),
7087            [ExcerptRange {
7088                context: Point::new(0, 0)..Point::new(2, 0),
7089                primary: None,
7090            }],
7091            cx,
7092        );
7093        multibuffer
7094    });
7095    let editor = cx.add_window(|cx| build_editor(multibuffer, cx)).root(cx);
7096
7097    handle_copilot_completion_request(
7098        &copilot_lsp,
7099        vec![copilot::request::Completion {
7100            text: "b = 2 + a".into(),
7101            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 5)),
7102            ..Default::default()
7103        }],
7104        vec![],
7105    );
7106    editor.update(cx, |editor, cx| {
7107        // Ensure copilot suggestions are shown for the first excerpt.
7108        editor.change_selections(None, cx, |s| {
7109            s.select_ranges([Point::new(1, 5)..Point::new(1, 5)])
7110        });
7111        editor.next_copilot_suggestion(&Default::default(), cx);
7112    });
7113    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7114    editor.update(cx, |editor, cx| {
7115        assert!(editor.has_active_copilot_suggestion(cx));
7116        assert_eq!(
7117            editor.display_text(cx),
7118            "\n\na = 1\nb = 2 + a\n\n\n\nc = 3\nd = 4\n"
7119        );
7120        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
7121    });
7122
7123    handle_copilot_completion_request(
7124        &copilot_lsp,
7125        vec![copilot::request::Completion {
7126            text: "d = 4 + c".into(),
7127            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 6)),
7128            ..Default::default()
7129        }],
7130        vec![],
7131    );
7132    editor.update(cx, |editor, cx| {
7133        // Move to another excerpt, ensuring the suggestion gets cleared.
7134        editor.change_selections(None, cx, |s| {
7135            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
7136        });
7137        assert!(!editor.has_active_copilot_suggestion(cx));
7138        assert_eq!(
7139            editor.display_text(cx),
7140            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4\n"
7141        );
7142        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
7143
7144        // Type a character, ensuring we don't even try to interpolate the previous suggestion.
7145        editor.handle_input(" ", cx);
7146        assert!(!editor.has_active_copilot_suggestion(cx));
7147        assert_eq!(
7148            editor.display_text(cx),
7149            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 \n"
7150        );
7151        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
7152    });
7153
7154    // Ensure the new suggestion is displayed when the debounce timeout expires.
7155    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7156    editor.update(cx, |editor, cx| {
7157        assert!(editor.has_active_copilot_suggestion(cx));
7158        assert_eq!(
7159            editor.display_text(cx),
7160            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 + c\n"
7161        );
7162        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
7163    });
7164}
7165
7166#[gpui::test]
7167async fn test_copilot_disabled_globs(
7168    deterministic: Arc<Deterministic>,
7169    cx: &mut gpui::TestAppContext,
7170) {
7171    init_test(cx, |settings| {
7172        settings
7173            .copilot
7174            .get_or_insert(Default::default())
7175            .disabled_globs = Some(vec![".env*".to_string()]);
7176    });
7177
7178    let (copilot, copilot_lsp) = Copilot::fake(cx);
7179    cx.update(|cx| cx.set_global(copilot));
7180
7181    let fs = FakeFs::new(cx.background());
7182    fs.insert_tree(
7183        "/test",
7184        json!({
7185            ".env": "SECRET=something\n",
7186            "README.md": "hello\n"
7187        }),
7188    )
7189    .await;
7190    let project = Project::test(fs, ["/test".as_ref()], cx).await;
7191
7192    let private_buffer = project
7193        .update(cx, |project, cx| {
7194            project.open_local_buffer("/test/.env", cx)
7195        })
7196        .await
7197        .unwrap();
7198    let public_buffer = project
7199        .update(cx, |project, cx| {
7200            project.open_local_buffer("/test/README.md", cx)
7201        })
7202        .await
7203        .unwrap();
7204
7205    let multibuffer = cx.add_model(|cx| {
7206        let mut multibuffer = MultiBuffer::new(0);
7207        multibuffer.push_excerpts(
7208            private_buffer.clone(),
7209            [ExcerptRange {
7210                context: Point::new(0, 0)..Point::new(1, 0),
7211                primary: None,
7212            }],
7213            cx,
7214        );
7215        multibuffer.push_excerpts(
7216            public_buffer.clone(),
7217            [ExcerptRange {
7218                context: Point::new(0, 0)..Point::new(1, 0),
7219                primary: None,
7220            }],
7221            cx,
7222        );
7223        multibuffer
7224    });
7225    let editor = cx.add_window(|cx| build_editor(multibuffer, cx)).root(cx);
7226
7227    let mut copilot_requests = copilot_lsp
7228        .handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| async move {
7229            Ok(copilot::request::GetCompletionsResult {
7230                completions: vec![copilot::request::Completion {
7231                    text: "next line".into(),
7232                    range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
7233                    ..Default::default()
7234                }],
7235            })
7236        });
7237
7238    editor.update(cx, |editor, cx| {
7239        editor.change_selections(None, cx, |selections| {
7240            selections.select_ranges([Point::new(0, 0)..Point::new(0, 0)])
7241        });
7242        editor.next_copilot_suggestion(&Default::default(), cx);
7243    });
7244
7245    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7246    assert!(copilot_requests.try_next().is_err());
7247
7248    editor.update(cx, |editor, cx| {
7249        editor.change_selections(None, cx, |s| {
7250            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
7251        });
7252        editor.next_copilot_suggestion(&Default::default(), cx);
7253    });
7254
7255    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7256    assert!(copilot_requests.try_next().is_ok());
7257}
7258
7259#[gpui::test]
7260async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
7261    init_test(cx, |_| {});
7262
7263    let mut language = Language::new(
7264        LanguageConfig {
7265            name: "Rust".into(),
7266            path_suffixes: vec!["rs".to_string()],
7267            brackets: BracketPairConfig {
7268                pairs: vec![BracketPair {
7269                    start: "{".to_string(),
7270                    end: "}".to_string(),
7271                    close: true,
7272                    newline: true,
7273                }],
7274                disabled_scopes_by_bracket_ix: Vec::new(),
7275            },
7276            ..Default::default()
7277        },
7278        Some(tree_sitter_rust::language()),
7279    );
7280    let mut fake_servers = language
7281        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
7282            capabilities: lsp::ServerCapabilities {
7283                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
7284                    first_trigger_character: "{".to_string(),
7285                    more_trigger_character: None,
7286                }),
7287                ..Default::default()
7288            },
7289            ..Default::default()
7290        }))
7291        .await;
7292
7293    let fs = FakeFs::new(cx.background());
7294    fs.insert_tree(
7295        "/a",
7296        json!({
7297            "main.rs": "fn main() { let a = 5; }",
7298            "other.rs": "// Test file",
7299        }),
7300    )
7301    .await;
7302    let project = Project::test(fs, ["/a".as_ref()], cx).await;
7303    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
7304    let workspace = cx
7305        .add_window(|cx| Workspace::test_new(project.clone(), cx))
7306        .root(cx);
7307    let worktree_id = workspace.update(cx, |workspace, cx| {
7308        workspace.project().read_with(cx, |project, cx| {
7309            project.worktrees(cx).next().unwrap().read(cx).id()
7310        })
7311    });
7312
7313    let buffer = project
7314        .update(cx, |project, cx| {
7315            project.open_local_buffer("/a/main.rs", cx)
7316        })
7317        .await
7318        .unwrap();
7319    cx.foreground().run_until_parked();
7320    cx.foreground().start_waiting();
7321    let fake_server = fake_servers.next().await.unwrap();
7322    let editor_handle = workspace
7323        .update(cx, |workspace, cx| {
7324            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
7325        })
7326        .await
7327        .unwrap()
7328        .downcast::<Editor>()
7329        .unwrap();
7330
7331    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
7332        assert_eq!(
7333            params.text_document_position.text_document.uri,
7334            lsp::Url::from_file_path("/a/main.rs").unwrap(),
7335        );
7336        assert_eq!(
7337            params.text_document_position.position,
7338            lsp::Position::new(0, 21),
7339        );
7340
7341        Ok(Some(vec![lsp::TextEdit {
7342            new_text: "]".to_string(),
7343            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
7344        }]))
7345    });
7346
7347    editor_handle.update(cx, |editor, cx| {
7348        cx.focus(&editor_handle);
7349        editor.change_selections(None, cx, |s| {
7350            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
7351        });
7352        editor.handle_input("{", cx);
7353    });
7354
7355    cx.foreground().run_until_parked();
7356
7357    buffer.read_with(cx, |buffer, _| {
7358        assert_eq!(
7359            buffer.text(),
7360            "fn main() { let a = {5}; }",
7361            "No extra braces from on type formatting should appear in the buffer"
7362        )
7363    });
7364}
7365
7366#[gpui::test]
7367async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
7368    init_test(cx, |_| {});
7369
7370    let language_name: Arc<str> = "Rust".into();
7371    let mut language = Language::new(
7372        LanguageConfig {
7373            name: Arc::clone(&language_name),
7374            path_suffixes: vec!["rs".to_string()],
7375            ..Default::default()
7376        },
7377        Some(tree_sitter_rust::language()),
7378    );
7379
7380    let server_restarts = Arc::new(AtomicUsize::new(0));
7381    let closure_restarts = Arc::clone(&server_restarts);
7382    let language_server_name = "test language server";
7383    let mut fake_servers = language
7384        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
7385            name: language_server_name,
7386            initialization_options: Some(json!({
7387                "testOptionValue": true
7388            })),
7389            initializer: Some(Box::new(move |fake_server| {
7390                let task_restarts = Arc::clone(&closure_restarts);
7391                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
7392                    task_restarts.fetch_add(1, atomic::Ordering::Release);
7393                    futures::future::ready(Ok(()))
7394                });
7395            })),
7396            ..Default::default()
7397        }))
7398        .await;
7399
7400    let fs = FakeFs::new(cx.background());
7401    fs.insert_tree(
7402        "/a",
7403        json!({
7404            "main.rs": "fn main() { let a = 5; }",
7405            "other.rs": "// Test file",
7406        }),
7407    )
7408    .await;
7409    let project = Project::test(fs, ["/a".as_ref()], cx).await;
7410    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
7411    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
7412    let _buffer = project
7413        .update(cx, |project, cx| {
7414            project.open_local_buffer("/a/main.rs", cx)
7415        })
7416        .await
7417        .unwrap();
7418    let _fake_server = fake_servers.next().await.unwrap();
7419    update_test_language_settings(cx, |language_settings| {
7420        language_settings.languages.insert(
7421            Arc::clone(&language_name),
7422            LanguageSettingsContent {
7423                tab_size: NonZeroU32::new(8),
7424                ..Default::default()
7425            },
7426        );
7427    });
7428    cx.foreground().run_until_parked();
7429    assert_eq!(
7430        server_restarts.load(atomic::Ordering::Acquire),
7431        0,
7432        "Should not restart LSP server on an unrelated change"
7433    );
7434
7435    update_test_project_settings(cx, |project_settings| {
7436        project_settings.lsp.insert(
7437            "Some other server name".into(),
7438            LspSettings {
7439                initialization_options: Some(json!({
7440                    "some other init value": false
7441                })),
7442            },
7443        );
7444    });
7445    cx.foreground().run_until_parked();
7446    assert_eq!(
7447        server_restarts.load(atomic::Ordering::Acquire),
7448        0,
7449        "Should not restart LSP server on an unrelated LSP settings change"
7450    );
7451
7452    update_test_project_settings(cx, |project_settings| {
7453        project_settings.lsp.insert(
7454            language_server_name.into(),
7455            LspSettings {
7456                initialization_options: Some(json!({
7457                    "anotherInitValue": false
7458                })),
7459            },
7460        );
7461    });
7462    cx.foreground().run_until_parked();
7463    assert_eq!(
7464        server_restarts.load(atomic::Ordering::Acquire),
7465        1,
7466        "Should restart LSP server on a related LSP settings change"
7467    );
7468
7469    update_test_project_settings(cx, |project_settings| {
7470        project_settings.lsp.insert(
7471            language_server_name.into(),
7472            LspSettings {
7473                initialization_options: Some(json!({
7474                    "anotherInitValue": false
7475                })),
7476            },
7477        );
7478    });
7479    cx.foreground().run_until_parked();
7480    assert_eq!(
7481        server_restarts.load(atomic::Ordering::Acquire),
7482        1,
7483        "Should not restart LSP server on a related LSP settings change that is the same"
7484    );
7485
7486    update_test_project_settings(cx, |project_settings| {
7487        project_settings.lsp.insert(
7488            language_server_name.into(),
7489            LspSettings {
7490                initialization_options: None,
7491            },
7492        );
7493    });
7494    cx.foreground().run_until_parked();
7495    assert_eq!(
7496        server_restarts.load(atomic::Ordering::Acquire),
7497        2,
7498        "Should restart LSP server on another related LSP settings change"
7499    );
7500}
7501
7502#[gpui::test]
7503async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
7504    init_test(cx, |_| {});
7505
7506    let mut cx = EditorLspTestContext::new_rust(
7507        lsp::ServerCapabilities {
7508            completion_provider: Some(lsp::CompletionOptions {
7509                trigger_characters: Some(vec![".".to_string()]),
7510                ..Default::default()
7511            }),
7512            ..Default::default()
7513        },
7514        cx,
7515    )
7516    .await;
7517
7518    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
7519    cx.simulate_keystroke(".");
7520    let completion_item = lsp::CompletionItem {
7521        label: "some".into(),
7522        kind: Some(lsp::CompletionItemKind::SNIPPET),
7523        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
7524        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
7525            kind: lsp::MarkupKind::Markdown,
7526            value: "```rust\nSome(2)\n```".to_string(),
7527        })),
7528        deprecated: Some(false),
7529        sort_text: Some("fffffff2".to_string()),
7530        filter_text: Some("some".to_string()),
7531        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
7532        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
7533            range: lsp::Range {
7534                start: lsp::Position {
7535                    line: 0,
7536                    character: 22,
7537                },
7538                end: lsp::Position {
7539                    line: 0,
7540                    character: 22,
7541                },
7542            },
7543            new_text: "Some(2)".to_string(),
7544        })),
7545        additional_text_edits: Some(vec![lsp::TextEdit {
7546            range: lsp::Range {
7547                start: lsp::Position {
7548                    line: 0,
7549                    character: 20,
7550                },
7551                end: lsp::Position {
7552                    line: 0,
7553                    character: 22,
7554                },
7555            },
7556            new_text: "".to_string(),
7557        }]),
7558        ..Default::default()
7559    };
7560
7561    let closure_completion_item = completion_item.clone();
7562    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
7563        let task_completion_item = closure_completion_item.clone();
7564        async move {
7565            Ok(Some(lsp::CompletionResponse::Array(vec![
7566                task_completion_item,
7567            ])))
7568        }
7569    });
7570
7571    request.next().await;
7572
7573    cx.condition(|editor, _| editor.context_menu_visible())
7574        .await;
7575    let apply_additional_edits = cx.update_editor(|editor, cx| {
7576        editor
7577            .confirm_completion(&ConfirmCompletion::default(), cx)
7578            .unwrap()
7579    });
7580    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
7581
7582    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
7583        let task_completion_item = completion_item.clone();
7584        async move { Ok(task_completion_item) }
7585    })
7586    .next()
7587    .await
7588    .unwrap();
7589    apply_additional_edits.await.unwrap();
7590    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
7591}
7592
7593fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
7594    let point = DisplayPoint::new(row as u32, column as u32);
7595    point..point
7596}
7597
7598fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
7599    let (text, ranges) = marked_text_ranges(marked_text, true);
7600    assert_eq!(view.text(cx), text);
7601    assert_eq!(
7602        view.selections.ranges(cx),
7603        ranges,
7604        "Assert selections are {}",
7605        marked_text
7606    );
7607}
7608
7609/// Handle completion request passing a marked string specifying where the completion
7610/// should be triggered from using '|' character, what range should be replaced, and what completions
7611/// should be returned using '<' and '>' to delimit the range
7612fn handle_completion_request<'a>(
7613    cx: &mut EditorLspTestContext<'a>,
7614    marked_string: &str,
7615    completions: Vec<&'static str>,
7616) -> impl Future<Output = ()> {
7617    let complete_from_marker: TextRangeMarker = '|'.into();
7618    let replace_range_marker: TextRangeMarker = ('<', '>').into();
7619    let (_, mut marked_ranges) = marked_text_ranges_by(
7620        marked_string,
7621        vec![complete_from_marker.clone(), replace_range_marker.clone()],
7622    );
7623
7624    let complete_from_position =
7625        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
7626    let replace_range =
7627        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
7628
7629    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
7630        let completions = completions.clone();
7631        async move {
7632            assert_eq!(params.text_document_position.text_document.uri, url.clone());
7633            assert_eq!(
7634                params.text_document_position.position,
7635                complete_from_position
7636            );
7637            Ok(Some(lsp::CompletionResponse::Array(
7638                completions
7639                    .iter()
7640                    .map(|completion_text| lsp::CompletionItem {
7641                        label: completion_text.to_string(),
7642                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
7643                            range: replace_range,
7644                            new_text: completion_text.to_string(),
7645                        })),
7646                        ..Default::default()
7647                    })
7648                    .collect(),
7649            )))
7650        }
7651    });
7652
7653    async move {
7654        request.next().await;
7655    }
7656}
7657
7658fn handle_resolve_completion_request<'a>(
7659    cx: &mut EditorLspTestContext<'a>,
7660    edits: Option<Vec<(&'static str, &'static str)>>,
7661) -> impl Future<Output = ()> {
7662    let edits = edits.map(|edits| {
7663        edits
7664            .iter()
7665            .map(|(marked_string, new_text)| {
7666                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
7667                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
7668                lsp::TextEdit::new(replace_range, new_text.to_string())
7669            })
7670            .collect::<Vec<_>>()
7671    });
7672
7673    let mut request =
7674        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
7675            let edits = edits.clone();
7676            async move {
7677                Ok(lsp::CompletionItem {
7678                    additional_text_edits: edits,
7679                    ..Default::default()
7680                })
7681            }
7682        });
7683
7684    async move {
7685        request.next().await;
7686    }
7687}
7688
7689fn handle_copilot_completion_request(
7690    lsp: &lsp::FakeLanguageServer,
7691    completions: Vec<copilot::request::Completion>,
7692    completions_cycling: Vec<copilot::request::Completion>,
7693) {
7694    lsp.handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| {
7695        let completions = completions.clone();
7696        async move {
7697            Ok(copilot::request::GetCompletionsResult {
7698                completions: completions.clone(),
7699            })
7700        }
7701    });
7702    lsp.handle_request::<copilot::request::GetCompletionsCycling, _, _>(move |_params, _cx| {
7703        let completions_cycling = completions_cycling.clone();
7704        async move {
7705            Ok(copilot::request::GetCompletionsResult {
7706                completions: completions_cycling.clone(),
7707            })
7708        }
7709    });
7710}
7711
7712pub(crate) fn update_test_language_settings(
7713    cx: &mut TestAppContext,
7714    f: impl Fn(&mut AllLanguageSettingsContent),
7715) {
7716    cx.update(|cx| {
7717        cx.update_global::<SettingsStore, _, _>(|store, cx| {
7718            store.update_user_settings::<AllLanguageSettings>(cx, f);
7719        });
7720    });
7721}
7722
7723pub(crate) fn update_test_project_settings(
7724    cx: &mut TestAppContext,
7725    f: impl Fn(&mut ProjectSettings),
7726) {
7727    cx.update(|cx| {
7728        cx.update_global::<SettingsStore, _, _>(|store, cx| {
7729            store.update_user_settings::<ProjectSettings>(cx, f);
7730        });
7731    });
7732}
7733
7734pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
7735    cx.foreground().forbid_parking();
7736
7737    cx.update(|cx| {
7738        cx.set_global(SettingsStore::test(cx));
7739        theme::init((), cx);
7740        client::init_settings(cx);
7741        language::init(cx);
7742        Project::init_settings(cx);
7743        workspace::init_settings(cx);
7744        crate::init(cx);
7745    });
7746
7747    update_test_language_settings(cx, f);
7748}