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