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