editor_tests.rs

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