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