editor_tests.rs

   1use super::*;
   2use crate::{
   3    scroll::scroll_amount::ScrollAmount,
   4    test::{
   5        assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
   6        editor_test_context::EditorTestContext, select_ranges,
   7    },
   8    JoinLines,
   9};
  10use drag_and_drop::DragAndDrop;
  11use futures::StreamExt;
  12use gpui::{
  13    executor::Deterministic,
  14    geometry::{rect::RectF, vector::vec2f},
  15    platform::{WindowBounds, WindowOptions},
  16    serde_json::{self, json},
  17    TestAppContext,
  18};
  19use indoc::indoc;
  20use language::{
  21    language_settings::{AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent},
  22    BracketPairConfig, FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageRegistry,
  23    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            // Enable Prettier formatting for the same buffer, and ensure
5088            // LSP is called instead of Prettier.
5089            prettier_parser_name: Some("test_parser".to_string()),
5090            ..Default::default()
5091        },
5092        Some(tree_sitter_rust::language()),
5093    );
5094    let mut fake_servers = language
5095        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
5096            capabilities: lsp::ServerCapabilities {
5097                document_formatting_provider: Some(lsp::OneOf::Left(true)),
5098                ..Default::default()
5099            },
5100            ..Default::default()
5101        }))
5102        .await;
5103
5104    let fs = FakeFs::new(cx.background());
5105    fs.insert_file("/file.rs", Default::default()).await;
5106
5107    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5108    project.update(cx, |project, _| {
5109        project.enable_test_prettier(&[]);
5110        project.languages().add(Arc::new(language));
5111    });
5112    let buffer = project
5113        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
5114        .await
5115        .unwrap();
5116
5117    cx.foreground().start_waiting();
5118    let fake_server = fake_servers.next().await.unwrap();
5119
5120    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
5121    let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
5122    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5123
5124    let format = editor.update(cx, |editor, cx| {
5125        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
5126    });
5127    fake_server
5128        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5129            assert_eq!(
5130                params.text_document.uri,
5131                lsp::Url::from_file_path("/file.rs").unwrap()
5132            );
5133            assert_eq!(params.options.tab_size, 4);
5134            Ok(Some(vec![lsp::TextEdit::new(
5135                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
5136                ", ".to_string(),
5137            )]))
5138        })
5139        .next()
5140        .await;
5141    cx.foreground().start_waiting();
5142    format.await.unwrap();
5143    assert_eq!(
5144        editor.read_with(cx, |editor, cx| editor.text(cx)),
5145        "one, two\nthree\n"
5146    );
5147
5148    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5149    // Ensure we don't lock if formatting hangs.
5150    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5151        assert_eq!(
5152            params.text_document.uri,
5153            lsp::Url::from_file_path("/file.rs").unwrap()
5154        );
5155        futures::future::pending::<()>().await;
5156        unreachable!()
5157    });
5158    let format = editor.update(cx, |editor, cx| {
5159        editor.perform_format(project, FormatTrigger::Manual, cx)
5160    });
5161    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
5162    cx.foreground().start_waiting();
5163    format.await.unwrap();
5164    assert_eq!(
5165        editor.read_with(cx, |editor, cx| editor.text(cx)),
5166        "one\ntwo\nthree\n"
5167    );
5168}
5169
5170#[gpui::test]
5171async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
5172    init_test(cx, |_| {});
5173
5174    let mut cx = EditorLspTestContext::new_rust(
5175        lsp::ServerCapabilities {
5176            document_formatting_provider: Some(lsp::OneOf::Left(true)),
5177            ..Default::default()
5178        },
5179        cx,
5180    )
5181    .await;
5182
5183    cx.set_state(indoc! {"
5184        one.twoˇ
5185    "});
5186
5187    // The format request takes a long time. When it completes, it inserts
5188    // a newline and an indent before the `.`
5189    cx.lsp
5190        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
5191            let executor = cx.background();
5192            async move {
5193                executor.timer(Duration::from_millis(100)).await;
5194                Ok(Some(vec![lsp::TextEdit {
5195                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
5196                    new_text: "\n    ".into(),
5197                }]))
5198            }
5199        });
5200
5201    // Submit a format request.
5202    let format_1 = cx
5203        .update_editor(|editor, cx| editor.format(&Format, cx))
5204        .unwrap();
5205    cx.foreground().run_until_parked();
5206
5207    // Submit a second format request.
5208    let format_2 = cx
5209        .update_editor(|editor, cx| editor.format(&Format, cx))
5210        .unwrap();
5211    cx.foreground().run_until_parked();
5212
5213    // Wait for both format requests to complete
5214    cx.foreground().advance_clock(Duration::from_millis(200));
5215    cx.foreground().start_waiting();
5216    format_1.await.unwrap();
5217    cx.foreground().start_waiting();
5218    format_2.await.unwrap();
5219
5220    // The formatting edits only happens once.
5221    cx.assert_editor_state(indoc! {"
5222        one
5223            .twoˇ
5224    "});
5225}
5226
5227#[gpui::test]
5228async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
5229    init_test(cx, |settings| {
5230        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
5231    });
5232
5233    let mut cx = EditorLspTestContext::new_rust(
5234        lsp::ServerCapabilities {
5235            document_formatting_provider: Some(lsp::OneOf::Left(true)),
5236            ..Default::default()
5237        },
5238        cx,
5239    )
5240    .await;
5241
5242    // Set up a buffer white some trailing whitespace and no trailing newline.
5243    cx.set_state(
5244        &[
5245            "one ",   //
5246            "twoˇ",   //
5247            "three ", //
5248            "four",   //
5249        ]
5250        .join("\n"),
5251    );
5252
5253    // Submit a format request.
5254    let format = cx
5255        .update_editor(|editor, cx| editor.format(&Format, cx))
5256        .unwrap();
5257
5258    // Record which buffer changes have been sent to the language server
5259    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
5260    cx.lsp
5261        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
5262            let buffer_changes = buffer_changes.clone();
5263            move |params, _| {
5264                buffer_changes.lock().extend(
5265                    params
5266                        .content_changes
5267                        .into_iter()
5268                        .map(|e| (e.range.unwrap(), e.text)),
5269                );
5270            }
5271        });
5272
5273    // Handle formatting requests to the language server.
5274    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
5275        let buffer_changes = buffer_changes.clone();
5276        move |_, _| {
5277            // When formatting is requested, trailing whitespace has already been stripped,
5278            // and the trailing newline has already been added.
5279            assert_eq!(
5280                &buffer_changes.lock()[1..],
5281                &[
5282                    (
5283                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
5284                        "".into()
5285                    ),
5286                    (
5287                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
5288                        "".into()
5289                    ),
5290                    (
5291                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
5292                        "\n".into()
5293                    ),
5294                ]
5295            );
5296
5297            // Insert blank lines between each line of the buffer.
5298            async move {
5299                Ok(Some(vec![
5300                    lsp::TextEdit {
5301                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
5302                        new_text: "\n".into(),
5303                    },
5304                    lsp::TextEdit {
5305                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
5306                        new_text: "\n".into(),
5307                    },
5308                ]))
5309            }
5310        }
5311    });
5312
5313    // After formatting the buffer, the trailing whitespace is stripped,
5314    // a newline is appended, and the edits provided by the language server
5315    // have been applied.
5316    format.await.unwrap();
5317    cx.assert_editor_state(
5318        &[
5319            "one",   //
5320            "",      //
5321            "twoˇ",  //
5322            "",      //
5323            "three", //
5324            "four",  //
5325            "",      //
5326        ]
5327        .join("\n"),
5328    );
5329
5330    // Undoing the formatting undoes the trailing whitespace removal, the
5331    // trailing newline, and the LSP edits.
5332    cx.update_buffer(|buffer, cx| buffer.undo(cx));
5333    cx.assert_editor_state(
5334        &[
5335            "one ",   //
5336            "twoˇ",   //
5337            "three ", //
5338            "four",   //
5339        ]
5340        .join("\n"),
5341    );
5342}
5343
5344#[gpui::test]
5345async fn test_completion(cx: &mut gpui::TestAppContext) {
5346    init_test(cx, |_| {});
5347
5348    let mut cx = EditorLspTestContext::new_rust(
5349        lsp::ServerCapabilities {
5350            completion_provider: Some(lsp::CompletionOptions {
5351                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
5352                resolve_provider: Some(true),
5353                ..Default::default()
5354            }),
5355            ..Default::default()
5356        },
5357        cx,
5358    )
5359    .await;
5360
5361    cx.set_state(indoc! {"
5362        oneˇ
5363        two
5364        three
5365    "});
5366    cx.simulate_keystroke(".");
5367    handle_completion_request(
5368        &mut cx,
5369        indoc! {"
5370            one.|<>
5371            two
5372            three
5373        "},
5374        vec!["first_completion", "second_completion"],
5375    )
5376    .await;
5377    cx.condition(|editor, _| editor.context_menu_visible())
5378        .await;
5379    let apply_additional_edits = cx.update_editor(|editor, cx| {
5380        editor.context_menu_next(&Default::default(), cx);
5381        editor
5382            .confirm_completion(&ConfirmCompletion::default(), cx)
5383            .unwrap()
5384    });
5385    cx.assert_editor_state(indoc! {"
5386        one.second_completionˇ
5387        two
5388        three
5389    "});
5390
5391    handle_resolve_completion_request(
5392        &mut cx,
5393        Some(vec![
5394            (
5395                //This overlaps with the primary completion edit which is
5396                //misbehavior from the LSP spec, test that we filter it out
5397                indoc! {"
5398                    one.second_ˇcompletion
5399                    two
5400                    threeˇ
5401                "},
5402                "overlapping additional edit",
5403            ),
5404            (
5405                indoc! {"
5406                    one.second_completion
5407                    two
5408                    threeˇ
5409                "},
5410                "\nadditional edit",
5411            ),
5412        ]),
5413    )
5414    .await;
5415    apply_additional_edits.await.unwrap();
5416    cx.assert_editor_state(indoc! {"
5417        one.second_completionˇ
5418        two
5419        three
5420        additional edit
5421    "});
5422
5423    cx.set_state(indoc! {"
5424        one.second_completion
5425        twoˇ
5426        threeˇ
5427        additional edit
5428    "});
5429    cx.simulate_keystroke(" ");
5430    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
5431    cx.simulate_keystroke("s");
5432    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
5433
5434    cx.assert_editor_state(indoc! {"
5435        one.second_completion
5436        two sˇ
5437        three sˇ
5438        additional edit
5439    "});
5440    handle_completion_request(
5441        &mut cx,
5442        indoc! {"
5443            one.second_completion
5444            two s
5445            three <s|>
5446            additional edit
5447        "},
5448        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
5449    )
5450    .await;
5451    cx.condition(|editor, _| editor.context_menu_visible())
5452        .await;
5453
5454    cx.simulate_keystroke("i");
5455
5456    handle_completion_request(
5457        &mut cx,
5458        indoc! {"
5459            one.second_completion
5460            two si
5461            three <si|>
5462            additional edit
5463        "},
5464        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
5465    )
5466    .await;
5467    cx.condition(|editor, _| editor.context_menu_visible())
5468        .await;
5469
5470    let apply_additional_edits = cx.update_editor(|editor, cx| {
5471        editor
5472            .confirm_completion(&ConfirmCompletion::default(), cx)
5473            .unwrap()
5474    });
5475    cx.assert_editor_state(indoc! {"
5476        one.second_completion
5477        two sixth_completionˇ
5478        three sixth_completionˇ
5479        additional edit
5480    "});
5481
5482    handle_resolve_completion_request(&mut cx, None).await;
5483    apply_additional_edits.await.unwrap();
5484
5485    cx.update(|cx| {
5486        cx.update_global::<SettingsStore, _, _>(|settings, cx| {
5487            settings.update_user_settings::<EditorSettings>(cx, |settings| {
5488                settings.show_completions_on_input = Some(false);
5489            });
5490        })
5491    });
5492    cx.set_state("editorˇ");
5493    cx.simulate_keystroke(".");
5494    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
5495    cx.simulate_keystroke("c");
5496    cx.simulate_keystroke("l");
5497    cx.simulate_keystroke("o");
5498    cx.assert_editor_state("editor.cloˇ");
5499    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
5500    cx.update_editor(|editor, cx| {
5501        editor.show_completions(&ShowCompletions, cx);
5502    });
5503    handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
5504    cx.condition(|editor, _| editor.context_menu_visible())
5505        .await;
5506    let apply_additional_edits = cx.update_editor(|editor, cx| {
5507        editor
5508            .confirm_completion(&ConfirmCompletion::default(), cx)
5509            .unwrap()
5510    });
5511    cx.assert_editor_state("editor.closeˇ");
5512    handle_resolve_completion_request(&mut cx, None).await;
5513    apply_additional_edits.await.unwrap();
5514}
5515
5516#[gpui::test]
5517async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
5518    init_test(cx, |_| {});
5519    let mut cx = EditorTestContext::new(cx).await;
5520    let language = Arc::new(Language::new(
5521        LanguageConfig {
5522            line_comment: Some("// ".into()),
5523            ..Default::default()
5524        },
5525        Some(tree_sitter_rust::language()),
5526    ));
5527    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
5528
5529    // If multiple selections intersect a line, the line is only toggled once.
5530    cx.set_state(indoc! {"
5531        fn a() {
5532            «//b();
5533            ˇ»// «c();
5534            //ˇ»  d();
5535        }
5536    "});
5537
5538    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5539
5540    cx.assert_editor_state(indoc! {"
5541        fn a() {
5542            «b();
5543            c();
5544            ˇ» d();
5545        }
5546    "});
5547
5548    // The comment prefix is inserted at the same column for every line in a
5549    // selection.
5550    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5551
5552    cx.assert_editor_state(indoc! {"
5553        fn a() {
5554            // «b();
5555            // c();
5556            ˇ»//  d();
5557        }
5558    "});
5559
5560    // If a selection ends at the beginning of a line, that line is not toggled.
5561    cx.set_selections_state(indoc! {"
5562        fn a() {
5563            // b();
5564            «// c();
5565        ˇ»    //  d();
5566        }
5567    "});
5568
5569    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5570
5571    cx.assert_editor_state(indoc! {"
5572        fn a() {
5573            // b();
5574            «c();
5575        ˇ»    //  d();
5576        }
5577    "});
5578
5579    // If a selection span a single line and is empty, the line is toggled.
5580    cx.set_state(indoc! {"
5581        fn a() {
5582            a();
5583            b();
5584        ˇ
5585        }
5586    "});
5587
5588    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5589
5590    cx.assert_editor_state(indoc! {"
5591        fn a() {
5592            a();
5593            b();
5594        //•ˇ
5595        }
5596    "});
5597
5598    // If a selection span multiple lines, empty lines are not toggled.
5599    cx.set_state(indoc! {"
5600        fn a() {
5601            «a();
5602
5603            c();ˇ»
5604        }
5605    "});
5606
5607    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5608
5609    cx.assert_editor_state(indoc! {"
5610        fn a() {
5611            // «a();
5612
5613            // c();ˇ»
5614        }
5615    "});
5616}
5617
5618#[gpui::test]
5619async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
5620    init_test(cx, |_| {});
5621
5622    let language = Arc::new(Language::new(
5623        LanguageConfig {
5624            line_comment: Some("// ".into()),
5625            ..Default::default()
5626        },
5627        Some(tree_sitter_rust::language()),
5628    ));
5629
5630    let registry = Arc::new(LanguageRegistry::test());
5631    registry.add(language.clone());
5632
5633    let mut cx = EditorTestContext::new(cx).await;
5634    cx.update_buffer(|buffer, cx| {
5635        buffer.set_language_registry(registry);
5636        buffer.set_language(Some(language), cx);
5637    });
5638
5639    let toggle_comments = &ToggleComments {
5640        advance_downwards: true,
5641    };
5642
5643    // Single cursor on one line -> advance
5644    // Cursor moves horizontally 3 characters as well on non-blank line
5645    cx.set_state(indoc!(
5646        "fn a() {
5647             ˇdog();
5648             cat();
5649        }"
5650    ));
5651    cx.update_editor(|editor, cx| {
5652        editor.toggle_comments(toggle_comments, cx);
5653    });
5654    cx.assert_editor_state(indoc!(
5655        "fn a() {
5656             // dog();
5657             catˇ();
5658        }"
5659    ));
5660
5661    // Single selection on one line -> don't advance
5662    cx.set_state(indoc!(
5663        "fn a() {
5664             «dog()ˇ»;
5665             cat();
5666        }"
5667    ));
5668    cx.update_editor(|editor, cx| {
5669        editor.toggle_comments(toggle_comments, cx);
5670    });
5671    cx.assert_editor_state(indoc!(
5672        "fn a() {
5673             // «dog()ˇ»;
5674             cat();
5675        }"
5676    ));
5677
5678    // Multiple cursors on one line -> advance
5679    cx.set_state(indoc!(
5680        "fn a() {
5681             ˇdˇog();
5682             cat();
5683        }"
5684    ));
5685    cx.update_editor(|editor, cx| {
5686        editor.toggle_comments(toggle_comments, cx);
5687    });
5688    cx.assert_editor_state(indoc!(
5689        "fn a() {
5690             // dog();
5691             catˇ(ˇ);
5692        }"
5693    ));
5694
5695    // Multiple cursors on one line, with selection -> don't advance
5696    cx.set_state(indoc!(
5697        "fn a() {
5698             ˇdˇog«()ˇ»;
5699             cat();
5700        }"
5701    ));
5702    cx.update_editor(|editor, cx| {
5703        editor.toggle_comments(toggle_comments, cx);
5704    });
5705    cx.assert_editor_state(indoc!(
5706        "fn a() {
5707             // ˇdˇog«()ˇ»;
5708             cat();
5709        }"
5710    ));
5711
5712    // Single cursor on one line -> advance
5713    // Cursor moves to column 0 on blank line
5714    cx.set_state(indoc!(
5715        "fn a() {
5716             ˇdog();
5717
5718             cat();
5719        }"
5720    ));
5721    cx.update_editor(|editor, cx| {
5722        editor.toggle_comments(toggle_comments, cx);
5723    });
5724    cx.assert_editor_state(indoc!(
5725        "fn a() {
5726             // dog();
5727        ˇ
5728             cat();
5729        }"
5730    ));
5731
5732    // Single cursor on one line -> advance
5733    // Cursor starts and ends at column 0
5734    cx.set_state(indoc!(
5735        "fn a() {
5736         ˇ    dog();
5737             cat();
5738        }"
5739    ));
5740    cx.update_editor(|editor, cx| {
5741        editor.toggle_comments(toggle_comments, cx);
5742    });
5743    cx.assert_editor_state(indoc!(
5744        "fn a() {
5745             // dog();
5746         ˇ    cat();
5747        }"
5748    ));
5749}
5750
5751#[gpui::test]
5752async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
5753    init_test(cx, |_| {});
5754
5755    let mut cx = EditorTestContext::new(cx).await;
5756
5757    let html_language = Arc::new(
5758        Language::new(
5759            LanguageConfig {
5760                name: "HTML".into(),
5761                block_comment: Some(("<!-- ".into(), " -->".into())),
5762                ..Default::default()
5763            },
5764            Some(tree_sitter_html::language()),
5765        )
5766        .with_injection_query(
5767            r#"
5768            (script_element
5769                (raw_text) @content
5770                (#set! "language" "javascript"))
5771            "#,
5772        )
5773        .unwrap(),
5774    );
5775
5776    let javascript_language = Arc::new(Language::new(
5777        LanguageConfig {
5778            name: "JavaScript".into(),
5779            line_comment: Some("// ".into()),
5780            ..Default::default()
5781        },
5782        Some(tree_sitter_typescript::language_tsx()),
5783    ));
5784
5785    let registry = Arc::new(LanguageRegistry::test());
5786    registry.add(html_language.clone());
5787    registry.add(javascript_language.clone());
5788
5789    cx.update_buffer(|buffer, cx| {
5790        buffer.set_language_registry(registry);
5791        buffer.set_language(Some(html_language), cx);
5792    });
5793
5794    // Toggle comments for empty selections
5795    cx.set_state(
5796        &r#"
5797            <p>A</p>ˇ
5798            <p>B</p>ˇ
5799            <p>C</p>ˇ
5800        "#
5801        .unindent(),
5802    );
5803    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5804    cx.assert_editor_state(
5805        &r#"
5806            <!-- <p>A</p>ˇ -->
5807            <!-- <p>B</p>ˇ -->
5808            <!-- <p>C</p>ˇ -->
5809        "#
5810        .unindent(),
5811    );
5812    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5813    cx.assert_editor_state(
5814        &r#"
5815            <p>A</p>ˇ
5816            <p>B</p>ˇ
5817            <p>C</p>ˇ
5818        "#
5819        .unindent(),
5820    );
5821
5822    // Toggle comments for mixture of empty and non-empty selections, where
5823    // multiple selections occupy a given line.
5824    cx.set_state(
5825        &r#"
5826            <p>A«</p>
5827            <p>ˇ»B</p>ˇ
5828            <p>C«</p>
5829            <p>ˇ»D</p>ˇ
5830        "#
5831        .unindent(),
5832    );
5833
5834    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5835    cx.assert_editor_state(
5836        &r#"
5837            <!-- <p>A«</p>
5838            <p>ˇ»B</p>ˇ -->
5839            <!-- <p>C«</p>
5840            <p>ˇ»D</p>ˇ -->
5841        "#
5842        .unindent(),
5843    );
5844    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5845    cx.assert_editor_state(
5846        &r#"
5847            <p>A«</p>
5848            <p>ˇ»B</p>ˇ
5849            <p>C«</p>
5850            <p>ˇ»D</p>ˇ
5851        "#
5852        .unindent(),
5853    );
5854
5855    // Toggle comments when different languages are active for different
5856    // selections.
5857    cx.set_state(
5858        &r#"
5859            ˇ<script>
5860                ˇvar x = new Y();
5861            ˇ</script>
5862        "#
5863        .unindent(),
5864    );
5865    cx.foreground().run_until_parked();
5866    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5867    cx.assert_editor_state(
5868        &r#"
5869            <!-- ˇ<script> -->
5870                // ˇvar x = new Y();
5871            <!-- ˇ</script> -->
5872        "#
5873        .unindent(),
5874    );
5875}
5876
5877#[gpui::test]
5878fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
5879    init_test(cx, |_| {});
5880
5881    let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, sample_text(3, 4, 'a')));
5882    let multibuffer = cx.add_model(|cx| {
5883        let mut multibuffer = MultiBuffer::new(0);
5884        multibuffer.push_excerpts(
5885            buffer.clone(),
5886            [
5887                ExcerptRange {
5888                    context: Point::new(0, 0)..Point::new(0, 4),
5889                    primary: None,
5890                },
5891                ExcerptRange {
5892                    context: Point::new(1, 0)..Point::new(1, 4),
5893                    primary: None,
5894                },
5895            ],
5896            cx,
5897        );
5898        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
5899        multibuffer
5900    });
5901
5902    let view = cx.add_window(|cx| build_editor(multibuffer, cx)).root(cx);
5903    view.update(cx, |view, cx| {
5904        assert_eq!(view.text(cx), "aaaa\nbbbb");
5905        view.change_selections(None, cx, |s| {
5906            s.select_ranges([
5907                Point::new(0, 0)..Point::new(0, 0),
5908                Point::new(1, 0)..Point::new(1, 0),
5909            ])
5910        });
5911
5912        view.handle_input("X", cx);
5913        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
5914        assert_eq!(
5915            view.selections.ranges(cx),
5916            [
5917                Point::new(0, 1)..Point::new(0, 1),
5918                Point::new(1, 1)..Point::new(1, 1),
5919            ]
5920        );
5921
5922        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
5923        view.change_selections(None, cx, |s| {
5924            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
5925        });
5926        view.backspace(&Default::default(), cx);
5927        assert_eq!(view.text(cx), "Xa\nbbb");
5928        assert_eq!(
5929            view.selections.ranges(cx),
5930            [Point::new(1, 0)..Point::new(1, 0)]
5931        );
5932
5933        view.change_selections(None, cx, |s| {
5934            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
5935        });
5936        view.backspace(&Default::default(), cx);
5937        assert_eq!(view.text(cx), "X\nbb");
5938        assert_eq!(
5939            view.selections.ranges(cx),
5940            [Point::new(0, 1)..Point::new(0, 1)]
5941        );
5942    });
5943}
5944
5945#[gpui::test]
5946fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
5947    init_test(cx, |_| {});
5948
5949    let markers = vec![('[', ']').into(), ('(', ')').into()];
5950    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
5951        indoc! {"
5952            [aaaa
5953            (bbbb]
5954            cccc)",
5955        },
5956        markers.clone(),
5957    );
5958    let excerpt_ranges = markers.into_iter().map(|marker| {
5959        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
5960        ExcerptRange {
5961            context,
5962            primary: None,
5963        }
5964    });
5965    let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, initial_text));
5966    let multibuffer = cx.add_model(|cx| {
5967        let mut multibuffer = MultiBuffer::new(0);
5968        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
5969        multibuffer
5970    });
5971
5972    let view = cx.add_window(|cx| build_editor(multibuffer, cx)).root(cx);
5973    view.update(cx, |view, cx| {
5974        let (expected_text, selection_ranges) = marked_text_ranges(
5975            indoc! {"
5976                aaaa
5977                bˇbbb
5978                bˇbbˇb
5979                cccc"
5980            },
5981            true,
5982        );
5983        assert_eq!(view.text(cx), expected_text);
5984        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
5985
5986        view.handle_input("X", cx);
5987
5988        let (expected_text, expected_selections) = marked_text_ranges(
5989            indoc! {"
5990                aaaa
5991                bXˇbbXb
5992                bXˇbbXˇb
5993                cccc"
5994            },
5995            false,
5996        );
5997        assert_eq!(view.text(cx), expected_text);
5998        assert_eq!(view.selections.ranges(cx), expected_selections);
5999
6000        view.newline(&Newline, cx);
6001        let (expected_text, expected_selections) = marked_text_ranges(
6002            indoc! {"
6003                aaaa
6004                bX
6005                ˇbbX
6006                b
6007                bX
6008                ˇbbX
6009                ˇb
6010                cccc"
6011            },
6012            false,
6013        );
6014        assert_eq!(view.text(cx), expected_text);
6015        assert_eq!(view.selections.ranges(cx), expected_selections);
6016    });
6017}
6018
6019#[gpui::test]
6020fn test_refresh_selections(cx: &mut TestAppContext) {
6021    init_test(cx, |_| {});
6022
6023    let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, sample_text(3, 4, 'a')));
6024    let mut excerpt1_id = None;
6025    let multibuffer = cx.add_model(|cx| {
6026        let mut multibuffer = MultiBuffer::new(0);
6027        excerpt1_id = multibuffer
6028            .push_excerpts(
6029                buffer.clone(),
6030                [
6031                    ExcerptRange {
6032                        context: Point::new(0, 0)..Point::new(1, 4),
6033                        primary: None,
6034                    },
6035                    ExcerptRange {
6036                        context: Point::new(1, 0)..Point::new(2, 4),
6037                        primary: None,
6038                    },
6039                ],
6040                cx,
6041            )
6042            .into_iter()
6043            .next();
6044        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
6045        multibuffer
6046    });
6047
6048    let editor = cx
6049        .add_window(|cx| {
6050            let mut editor = build_editor(multibuffer.clone(), cx);
6051            let snapshot = editor.snapshot(cx);
6052            editor.change_selections(None, cx, |s| {
6053                s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
6054            });
6055            editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
6056            assert_eq!(
6057                editor.selections.ranges(cx),
6058                [
6059                    Point::new(1, 3)..Point::new(1, 3),
6060                    Point::new(2, 1)..Point::new(2, 1),
6061                ]
6062            );
6063            editor
6064        })
6065        .root(cx);
6066
6067    // Refreshing selections is a no-op when excerpts haven't changed.
6068    editor.update(cx, |editor, cx| {
6069        editor.change_selections(None, cx, |s| s.refresh());
6070        assert_eq!(
6071            editor.selections.ranges(cx),
6072            [
6073                Point::new(1, 3)..Point::new(1, 3),
6074                Point::new(2, 1)..Point::new(2, 1),
6075            ]
6076        );
6077    });
6078
6079    multibuffer.update(cx, |multibuffer, cx| {
6080        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
6081    });
6082    editor.update(cx, |editor, cx| {
6083        // Removing an excerpt causes the first selection to become degenerate.
6084        assert_eq!(
6085            editor.selections.ranges(cx),
6086            [
6087                Point::new(0, 0)..Point::new(0, 0),
6088                Point::new(0, 1)..Point::new(0, 1)
6089            ]
6090        );
6091
6092        // Refreshing selections will relocate the first selection to the original buffer
6093        // location.
6094        editor.change_selections(None, cx, |s| s.refresh());
6095        assert_eq!(
6096            editor.selections.ranges(cx),
6097            [
6098                Point::new(0, 1)..Point::new(0, 1),
6099                Point::new(0, 3)..Point::new(0, 3)
6100            ]
6101        );
6102        assert!(editor.selections.pending_anchor().is_some());
6103    });
6104}
6105
6106#[gpui::test]
6107fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
6108    init_test(cx, |_| {});
6109
6110    let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, sample_text(3, 4, 'a')));
6111    let mut excerpt1_id = None;
6112    let multibuffer = cx.add_model(|cx| {
6113        let mut multibuffer = MultiBuffer::new(0);
6114        excerpt1_id = multibuffer
6115            .push_excerpts(
6116                buffer.clone(),
6117                [
6118                    ExcerptRange {
6119                        context: Point::new(0, 0)..Point::new(1, 4),
6120                        primary: None,
6121                    },
6122                    ExcerptRange {
6123                        context: Point::new(1, 0)..Point::new(2, 4),
6124                        primary: None,
6125                    },
6126                ],
6127                cx,
6128            )
6129            .into_iter()
6130            .next();
6131        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
6132        multibuffer
6133    });
6134
6135    let editor = cx
6136        .add_window(|cx| {
6137            let mut editor = build_editor(multibuffer.clone(), cx);
6138            let snapshot = editor.snapshot(cx);
6139            editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
6140            assert_eq!(
6141                editor.selections.ranges(cx),
6142                [Point::new(1, 3)..Point::new(1, 3)]
6143            );
6144            editor
6145        })
6146        .root(cx);
6147
6148    multibuffer.update(cx, |multibuffer, cx| {
6149        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
6150    });
6151    editor.update(cx, |editor, cx| {
6152        assert_eq!(
6153            editor.selections.ranges(cx),
6154            [Point::new(0, 0)..Point::new(0, 0)]
6155        );
6156
6157        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
6158        editor.change_selections(None, cx, |s| s.refresh());
6159        assert_eq!(
6160            editor.selections.ranges(cx),
6161            [Point::new(0, 3)..Point::new(0, 3)]
6162        );
6163        assert!(editor.selections.pending_anchor().is_some());
6164    });
6165}
6166
6167#[gpui::test]
6168async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
6169    init_test(cx, |_| {});
6170
6171    let language = Arc::new(
6172        Language::new(
6173            LanguageConfig {
6174                brackets: BracketPairConfig {
6175                    pairs: vec![
6176                        BracketPair {
6177                            start: "{".to_string(),
6178                            end: "}".to_string(),
6179                            close: true,
6180                            newline: true,
6181                        },
6182                        BracketPair {
6183                            start: "/* ".to_string(),
6184                            end: " */".to_string(),
6185                            close: true,
6186                            newline: true,
6187                        },
6188                    ],
6189                    ..Default::default()
6190                },
6191                ..Default::default()
6192            },
6193            Some(tree_sitter_rust::language()),
6194        )
6195        .with_indents_query("")
6196        .unwrap(),
6197    );
6198
6199    let text = concat!(
6200        "{   }\n",     //
6201        "  x\n",       //
6202        "  /*   */\n", //
6203        "x\n",         //
6204        "{{} }\n",     //
6205    );
6206
6207    let buffer =
6208        cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx));
6209    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
6210    let view = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
6211    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
6212        .await;
6213
6214    view.update(cx, |view, cx| {
6215        view.change_selections(None, cx, |s| {
6216            s.select_display_ranges([
6217                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
6218                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
6219                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
6220            ])
6221        });
6222        view.newline(&Newline, cx);
6223
6224        assert_eq!(
6225            view.buffer().read(cx).read(cx).text(),
6226            concat!(
6227                "{ \n",    // Suppress rustfmt
6228                "\n",      //
6229                "}\n",     //
6230                "  x\n",   //
6231                "  /* \n", //
6232                "  \n",    //
6233                "  */\n",  //
6234                "x\n",     //
6235                "{{} \n",  //
6236                "}\n",     //
6237            )
6238        );
6239    });
6240}
6241
6242#[gpui::test]
6243fn test_highlighted_ranges(cx: &mut TestAppContext) {
6244    init_test(cx, |_| {});
6245
6246    let editor = cx
6247        .add_window(|cx| {
6248            let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
6249            build_editor(buffer.clone(), cx)
6250        })
6251        .root(cx);
6252
6253    editor.update(cx, |editor, cx| {
6254        struct Type1;
6255        struct Type2;
6256
6257        let buffer = editor.buffer.read(cx).snapshot(cx);
6258
6259        let anchor_range =
6260            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
6261
6262        editor.highlight_background::<Type1>(
6263            vec![
6264                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
6265                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
6266                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
6267                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
6268            ],
6269            |_| Color::red(),
6270            cx,
6271        );
6272        editor.highlight_background::<Type2>(
6273            vec![
6274                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
6275                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
6276                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
6277                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
6278            ],
6279            |_| Color::green(),
6280            cx,
6281        );
6282
6283        let snapshot = editor.snapshot(cx);
6284        let mut highlighted_ranges = editor.background_highlights_in_range(
6285            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
6286            &snapshot,
6287            theme::current(cx).as_ref(),
6288        );
6289        // Enforce a consistent ordering based on color without relying on the ordering of the
6290        // highlight's `TypeId` which is non-deterministic.
6291        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
6292        assert_eq!(
6293            highlighted_ranges,
6294            &[
6295                (
6296                    DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
6297                    Color::green(),
6298                ),
6299                (
6300                    DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
6301                    Color::green(),
6302                ),
6303                (
6304                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
6305                    Color::red(),
6306                ),
6307                (
6308                    DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
6309                    Color::red(),
6310                ),
6311            ]
6312        );
6313        assert_eq!(
6314            editor.background_highlights_in_range(
6315                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
6316                &snapshot,
6317                theme::current(cx).as_ref(),
6318            ),
6319            &[(
6320                DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
6321                Color::red(),
6322            )]
6323        );
6324    });
6325}
6326
6327#[gpui::test]
6328async fn test_following(cx: &mut gpui::TestAppContext) {
6329    init_test(cx, |_| {});
6330
6331    let fs = FakeFs::new(cx.background());
6332    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
6333
6334    let buffer = project.update(cx, |project, cx| {
6335        let buffer = project
6336            .create_buffer(&sample_text(16, 8, 'a'), None, cx)
6337            .unwrap();
6338        cx.add_model(|cx| MultiBuffer::singleton(buffer, cx))
6339    });
6340    let leader = cx
6341        .add_window(|cx| build_editor(buffer.clone(), cx))
6342        .root(cx);
6343    let follower = cx
6344        .update(|cx| {
6345            cx.add_window(
6346                WindowOptions {
6347                    bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
6348                    ..Default::default()
6349                },
6350                |cx| build_editor(buffer.clone(), cx),
6351            )
6352        })
6353        .root(cx);
6354
6355    let is_still_following = Rc::new(RefCell::new(true));
6356    let follower_edit_event_count = Rc::new(RefCell::new(0));
6357    let pending_update = Rc::new(RefCell::new(None));
6358    follower.update(cx, {
6359        let update = pending_update.clone();
6360        let is_still_following = is_still_following.clone();
6361        let follower_edit_event_count = follower_edit_event_count.clone();
6362        |_, cx| {
6363            cx.subscribe(&leader, move |_, leader, event, cx| {
6364                leader
6365                    .read(cx)
6366                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
6367            })
6368            .detach();
6369
6370            cx.subscribe(&follower, move |_, _, event, cx| {
6371                if Editor::should_unfollow_on_event(event, cx) {
6372                    *is_still_following.borrow_mut() = false;
6373                }
6374                if let Event::BufferEdited = event {
6375                    *follower_edit_event_count.borrow_mut() += 1;
6376                }
6377            })
6378            .detach();
6379        }
6380    });
6381
6382    // Update the selections only
6383    leader.update(cx, |leader, cx| {
6384        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
6385    });
6386    follower
6387        .update(cx, |follower, cx| {
6388            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6389        })
6390        .await
6391        .unwrap();
6392    follower.read_with(cx, |follower, cx| {
6393        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
6394    });
6395    assert_eq!(*is_still_following.borrow(), true);
6396    assert_eq!(*follower_edit_event_count.borrow(), 0);
6397
6398    // Update the scroll position only
6399    leader.update(cx, |leader, cx| {
6400        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
6401    });
6402    follower
6403        .update(cx, |follower, cx| {
6404            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6405        })
6406        .await
6407        .unwrap();
6408    assert_eq!(
6409        follower.update(cx, |follower, cx| follower.scroll_position(cx)),
6410        vec2f(1.5, 3.5)
6411    );
6412    assert_eq!(*is_still_following.borrow(), true);
6413    assert_eq!(*follower_edit_event_count.borrow(), 0);
6414
6415    // Update the selections and scroll position. The follower's scroll position is updated
6416    // via autoscroll, not via the leader's exact scroll position.
6417    leader.update(cx, |leader, cx| {
6418        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
6419        leader.request_autoscroll(Autoscroll::newest(), cx);
6420        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
6421    });
6422    follower
6423        .update(cx, |follower, cx| {
6424            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6425        })
6426        .await
6427        .unwrap();
6428    follower.update(cx, |follower, cx| {
6429        assert_eq!(follower.scroll_position(cx), vec2f(1.5, 0.0));
6430        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
6431    });
6432    assert_eq!(*is_still_following.borrow(), true);
6433
6434    // Creating a pending selection that precedes another selection
6435    leader.update(cx, |leader, cx| {
6436        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
6437        leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
6438    });
6439    follower
6440        .update(cx, |follower, cx| {
6441            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6442        })
6443        .await
6444        .unwrap();
6445    follower.read_with(cx, |follower, cx| {
6446        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
6447    });
6448    assert_eq!(*is_still_following.borrow(), true);
6449
6450    // Extend the pending selection so that it surrounds another selection
6451    leader.update(cx, |leader, cx| {
6452        leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
6453    });
6454    follower
6455        .update(cx, |follower, cx| {
6456            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6457        })
6458        .await
6459        .unwrap();
6460    follower.read_with(cx, |follower, cx| {
6461        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
6462    });
6463
6464    // Scrolling locally breaks the follow
6465    follower.update(cx, |follower, cx| {
6466        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
6467        follower.set_scroll_anchor(
6468            ScrollAnchor {
6469                anchor: top_anchor,
6470                offset: vec2f(0.0, 0.5),
6471            },
6472            cx,
6473        );
6474    });
6475    assert_eq!(*is_still_following.borrow(), false);
6476}
6477
6478#[gpui::test]
6479async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
6480    init_test(cx, |_| {});
6481
6482    let fs = FakeFs::new(cx.background());
6483    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
6484    let workspace = cx
6485        .add_window(|cx| Workspace::test_new(project.clone(), cx))
6486        .root(cx);
6487    let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
6488
6489    let leader = pane.update(cx, |_, cx| {
6490        let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
6491        cx.add_view(|cx| build_editor(multibuffer.clone(), cx))
6492    });
6493
6494    // Start following the editor when it has no excerpts.
6495    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
6496    let follower_1 = cx
6497        .update(|cx| {
6498            Editor::from_state_proto(
6499                pane.clone(),
6500                workspace.clone(),
6501                ViewId {
6502                    creator: Default::default(),
6503                    id: 0,
6504                },
6505                &mut state_message,
6506                cx,
6507            )
6508        })
6509        .unwrap()
6510        .await
6511        .unwrap();
6512
6513    let update_message = Rc::new(RefCell::new(None));
6514    follower_1.update(cx, {
6515        let update = update_message.clone();
6516        |_, cx| {
6517            cx.subscribe(&leader, move |_, leader, event, cx| {
6518                leader
6519                    .read(cx)
6520                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
6521            })
6522            .detach();
6523        }
6524    });
6525
6526    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
6527        (
6528            project
6529                .create_buffer("abc\ndef\nghi\njkl\n", None, cx)
6530                .unwrap(),
6531            project
6532                .create_buffer("mno\npqr\nstu\nvwx\n", None, cx)
6533                .unwrap(),
6534        )
6535    });
6536
6537    // Insert some excerpts.
6538    leader.update(cx, |leader, cx| {
6539        leader.buffer.update(cx, |multibuffer, cx| {
6540            let excerpt_ids = multibuffer.push_excerpts(
6541                buffer_1.clone(),
6542                [
6543                    ExcerptRange {
6544                        context: 1..6,
6545                        primary: None,
6546                    },
6547                    ExcerptRange {
6548                        context: 12..15,
6549                        primary: None,
6550                    },
6551                    ExcerptRange {
6552                        context: 0..3,
6553                        primary: None,
6554                    },
6555                ],
6556                cx,
6557            );
6558            multibuffer.insert_excerpts_after(
6559                excerpt_ids[0],
6560                buffer_2.clone(),
6561                [
6562                    ExcerptRange {
6563                        context: 8..12,
6564                        primary: None,
6565                    },
6566                    ExcerptRange {
6567                        context: 0..6,
6568                        primary: None,
6569                    },
6570                ],
6571                cx,
6572            );
6573        });
6574    });
6575
6576    // Apply the update of adding the excerpts.
6577    follower_1
6578        .update(cx, |follower, cx| {
6579            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6580        })
6581        .await
6582        .unwrap();
6583    assert_eq!(
6584        follower_1.read_with(cx, |editor, cx| editor.text(cx)),
6585        leader.read_with(cx, |editor, cx| editor.text(cx))
6586    );
6587    update_message.borrow_mut().take();
6588
6589    // Start following separately after it already has excerpts.
6590    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
6591    let follower_2 = cx
6592        .update(|cx| {
6593            Editor::from_state_proto(
6594                pane.clone(),
6595                workspace.clone(),
6596                ViewId {
6597                    creator: Default::default(),
6598                    id: 0,
6599                },
6600                &mut state_message,
6601                cx,
6602            )
6603        })
6604        .unwrap()
6605        .await
6606        .unwrap();
6607    assert_eq!(
6608        follower_2.read_with(cx, |editor, cx| editor.text(cx)),
6609        leader.read_with(cx, |editor, cx| editor.text(cx))
6610    );
6611
6612    // Remove some excerpts.
6613    leader.update(cx, |leader, cx| {
6614        leader.buffer.update(cx, |multibuffer, cx| {
6615            let excerpt_ids = multibuffer.excerpt_ids();
6616            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
6617            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
6618        });
6619    });
6620
6621    // Apply the update of removing the excerpts.
6622    follower_1
6623        .update(cx, |follower, cx| {
6624            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6625        })
6626        .await
6627        .unwrap();
6628    follower_2
6629        .update(cx, |follower, cx| {
6630            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6631        })
6632        .await
6633        .unwrap();
6634    update_message.borrow_mut().take();
6635    assert_eq!(
6636        follower_1.read_with(cx, |editor, cx| editor.text(cx)),
6637        leader.read_with(cx, |editor, cx| editor.text(cx))
6638    );
6639}
6640
6641#[test]
6642fn test_combine_syntax_and_fuzzy_match_highlights() {
6643    let string = "abcdefghijklmnop";
6644    let syntax_ranges = [
6645        (
6646            0..3,
6647            HighlightStyle {
6648                color: Some(Color::red()),
6649                ..Default::default()
6650            },
6651        ),
6652        (
6653            4..8,
6654            HighlightStyle {
6655                color: Some(Color::green()),
6656                ..Default::default()
6657            },
6658        ),
6659    ];
6660    let match_indices = [4, 6, 7, 8];
6661    assert_eq!(
6662        combine_syntax_and_fuzzy_match_highlights(
6663            string,
6664            Default::default(),
6665            syntax_ranges.into_iter(),
6666            &match_indices,
6667        ),
6668        &[
6669            (
6670                0..3,
6671                HighlightStyle {
6672                    color: Some(Color::red()),
6673                    ..Default::default()
6674                },
6675            ),
6676            (
6677                4..5,
6678                HighlightStyle {
6679                    color: Some(Color::green()),
6680                    weight: Some(fonts::Weight::BOLD),
6681                    ..Default::default()
6682                },
6683            ),
6684            (
6685                5..6,
6686                HighlightStyle {
6687                    color: Some(Color::green()),
6688                    ..Default::default()
6689                },
6690            ),
6691            (
6692                6..8,
6693                HighlightStyle {
6694                    color: Some(Color::green()),
6695                    weight: Some(fonts::Weight::BOLD),
6696                    ..Default::default()
6697                },
6698            ),
6699            (
6700                8..9,
6701                HighlightStyle {
6702                    weight: Some(fonts::Weight::BOLD),
6703                    ..Default::default()
6704                },
6705            ),
6706        ]
6707    );
6708}
6709
6710#[gpui::test]
6711async fn go_to_hunk(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
6712    init_test(cx, |_| {});
6713
6714    let mut cx = EditorTestContext::new(cx).await;
6715
6716    let diff_base = r#"
6717        use some::mod;
6718
6719        const A: u32 = 42;
6720
6721        fn main() {
6722            println!("hello");
6723
6724            println!("world");
6725        }
6726        "#
6727    .unindent();
6728
6729    // Edits are modified, removed, modified, added
6730    cx.set_state(
6731        &r#"
6732        use some::modified;
6733
6734        ˇ
6735        fn main() {
6736            println!("hello there");
6737
6738            println!("around the");
6739            println!("world");
6740        }
6741        "#
6742        .unindent(),
6743    );
6744
6745    cx.set_diff_base(Some(&diff_base));
6746    deterministic.run_until_parked();
6747
6748    cx.update_editor(|editor, cx| {
6749        //Wrap around the bottom of the buffer
6750        for _ in 0..3 {
6751            editor.go_to_hunk(&GoToHunk, cx);
6752        }
6753    });
6754
6755    cx.assert_editor_state(
6756        &r#"
6757        ˇuse some::modified;
6758
6759
6760        fn main() {
6761            println!("hello there");
6762
6763            println!("around the");
6764            println!("world");
6765        }
6766        "#
6767        .unindent(),
6768    );
6769
6770    cx.update_editor(|editor, cx| {
6771        //Wrap around the top of the buffer
6772        for _ in 0..2 {
6773            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
6774        }
6775    });
6776
6777    cx.assert_editor_state(
6778        &r#"
6779        use some::modified;
6780
6781
6782        fn main() {
6783        ˇ    println!("hello there");
6784
6785            println!("around the");
6786            println!("world");
6787        }
6788        "#
6789        .unindent(),
6790    );
6791
6792    cx.update_editor(|editor, cx| {
6793        editor.fold(&Fold, cx);
6794
6795        //Make sure that the fold only gets one hunk
6796        for _ in 0..4 {
6797            editor.go_to_hunk(&GoToHunk, cx);
6798        }
6799    });
6800
6801    cx.assert_editor_state(
6802        &r#"
6803        ˇuse some::modified;
6804
6805
6806        fn main() {
6807            println!("hello there");
6808
6809            println!("around the");
6810            println!("world");
6811        }
6812        "#
6813        .unindent(),
6814    );
6815}
6816
6817#[test]
6818fn test_split_words() {
6819    fn split<'a>(text: &'a str) -> Vec<&'a str> {
6820        split_words(text).collect()
6821    }
6822
6823    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
6824    assert_eq!(split("hello_world"), &["hello_", "world"]);
6825    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
6826    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
6827    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
6828    assert_eq!(split("helloworld"), &["helloworld"]);
6829}
6830
6831#[gpui::test]
6832async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
6833    init_test(cx, |_| {});
6834
6835    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
6836    let mut assert = |before, after| {
6837        let _state_context = cx.set_state(before);
6838        cx.update_editor(|editor, cx| {
6839            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
6840        });
6841        cx.assert_editor_state(after);
6842    };
6843
6844    // Outside bracket jumps to outside of matching bracket
6845    assert("console.logˇ(var);", "console.log(var)ˇ;");
6846    assert("console.log(var)ˇ;", "console.logˇ(var);");
6847
6848    // Inside bracket jumps to inside of matching bracket
6849    assert("console.log(ˇvar);", "console.log(varˇ);");
6850    assert("console.log(varˇ);", "console.log(ˇvar);");
6851
6852    // When outside a bracket and inside, favor jumping to the inside bracket
6853    assert(
6854        "console.log('foo', [1, 2, 3]ˇ);",
6855        "console.log(ˇ'foo', [1, 2, 3]);",
6856    );
6857    assert(
6858        "console.log(ˇ'foo', [1, 2, 3]);",
6859        "console.log('foo', [1, 2, 3]ˇ);",
6860    );
6861
6862    // Bias forward if two options are equally likely
6863    assert(
6864        "let result = curried_fun()ˇ();",
6865        "let result = curried_fun()()ˇ;",
6866    );
6867
6868    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
6869    assert(
6870        indoc! {"
6871            function test() {
6872                console.log('test')ˇ
6873            }"},
6874        indoc! {"
6875            function test() {
6876                console.logˇ('test')
6877            }"},
6878    );
6879}
6880
6881#[gpui::test(iterations = 10)]
6882async fn test_copilot(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
6883    init_test(cx, |_| {});
6884
6885    let (copilot, copilot_lsp) = Copilot::fake(cx);
6886    cx.update(|cx| cx.set_global(copilot));
6887    let mut cx = EditorLspTestContext::new_rust(
6888        lsp::ServerCapabilities {
6889            completion_provider: Some(lsp::CompletionOptions {
6890                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
6891                ..Default::default()
6892            }),
6893            ..Default::default()
6894        },
6895        cx,
6896    )
6897    .await;
6898
6899    // When inserting, ensure autocompletion is favored over Copilot suggestions.
6900    cx.set_state(indoc! {"
6901        oneˇ
6902        two
6903        three
6904    "});
6905    cx.simulate_keystroke(".");
6906    let _ = handle_completion_request(
6907        &mut cx,
6908        indoc! {"
6909            one.|<>
6910            two
6911            three
6912        "},
6913        vec!["completion_a", "completion_b"],
6914    );
6915    handle_copilot_completion_request(
6916        &copilot_lsp,
6917        vec![copilot::request::Completion {
6918            text: "one.copilot1".into(),
6919            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6920            ..Default::default()
6921        }],
6922        vec![],
6923    );
6924    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6925    cx.update_editor(|editor, cx| {
6926        assert!(editor.context_menu_visible());
6927        assert!(!editor.has_active_copilot_suggestion(cx));
6928
6929        // Confirming a completion inserts it and hides the context menu, without showing
6930        // the copilot suggestion afterwards.
6931        editor
6932            .confirm_completion(&Default::default(), cx)
6933            .unwrap()
6934            .detach();
6935        assert!(!editor.context_menu_visible());
6936        assert!(!editor.has_active_copilot_suggestion(cx));
6937        assert_eq!(editor.text(cx), "one.completion_a\ntwo\nthree\n");
6938        assert_eq!(editor.display_text(cx), "one.completion_a\ntwo\nthree\n");
6939    });
6940
6941    // Ensure Copilot suggestions are shown right away if no autocompletion is available.
6942    cx.set_state(indoc! {"
6943        oneˇ
6944        two
6945        three
6946    "});
6947    cx.simulate_keystroke(".");
6948    let _ = handle_completion_request(
6949        &mut cx,
6950        indoc! {"
6951            one.|<>
6952            two
6953            three
6954        "},
6955        vec![],
6956    );
6957    handle_copilot_completion_request(
6958        &copilot_lsp,
6959        vec![copilot::request::Completion {
6960            text: "one.copilot1".into(),
6961            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6962            ..Default::default()
6963        }],
6964        vec![],
6965    );
6966    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6967    cx.update_editor(|editor, cx| {
6968        assert!(!editor.context_menu_visible());
6969        assert!(editor.has_active_copilot_suggestion(cx));
6970        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
6971        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
6972    });
6973
6974    // Reset editor, and ensure autocompletion is still favored over Copilot suggestions.
6975    cx.set_state(indoc! {"
6976        oneˇ
6977        two
6978        three
6979    "});
6980    cx.simulate_keystroke(".");
6981    let _ = handle_completion_request(
6982        &mut cx,
6983        indoc! {"
6984            one.|<>
6985            two
6986            three
6987        "},
6988        vec!["completion_a", "completion_b"],
6989    );
6990    handle_copilot_completion_request(
6991        &copilot_lsp,
6992        vec![copilot::request::Completion {
6993            text: "one.copilot1".into(),
6994            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6995            ..Default::default()
6996        }],
6997        vec![],
6998    );
6999    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7000    cx.update_editor(|editor, cx| {
7001        assert!(editor.context_menu_visible());
7002        assert!(!editor.has_active_copilot_suggestion(cx));
7003
7004        // When hiding the context menu, the Copilot suggestion becomes visible.
7005        editor.hide_context_menu(cx);
7006        assert!(!editor.context_menu_visible());
7007        assert!(editor.has_active_copilot_suggestion(cx));
7008        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
7009        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
7010    });
7011
7012    // Ensure existing completion is interpolated when inserting again.
7013    cx.simulate_keystroke("c");
7014    deterministic.run_until_parked();
7015    cx.update_editor(|editor, cx| {
7016        assert!(!editor.context_menu_visible());
7017        assert!(editor.has_active_copilot_suggestion(cx));
7018        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
7019        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7020    });
7021
7022    // After debouncing, new Copilot completions should be requested.
7023    handle_copilot_completion_request(
7024        &copilot_lsp,
7025        vec![copilot::request::Completion {
7026            text: "one.copilot2".into(),
7027            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 5)),
7028            ..Default::default()
7029        }],
7030        vec![],
7031    );
7032    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7033    cx.update_editor(|editor, cx| {
7034        assert!(!editor.context_menu_visible());
7035        assert!(editor.has_active_copilot_suggestion(cx));
7036        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7037        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7038
7039        // Canceling should remove the active Copilot suggestion.
7040        editor.cancel(&Default::default(), cx);
7041        assert!(!editor.has_active_copilot_suggestion(cx));
7042        assert_eq!(editor.display_text(cx), "one.c\ntwo\nthree\n");
7043        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7044
7045        // After canceling, tabbing shouldn't insert the previously shown suggestion.
7046        editor.tab(&Default::default(), cx);
7047        assert!(!editor.has_active_copilot_suggestion(cx));
7048        assert_eq!(editor.display_text(cx), "one.c   \ntwo\nthree\n");
7049        assert_eq!(editor.text(cx), "one.c   \ntwo\nthree\n");
7050
7051        // When undoing the previously active suggestion is shown again.
7052        editor.undo(&Default::default(), cx);
7053        assert!(editor.has_active_copilot_suggestion(cx));
7054        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7055        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7056    });
7057
7058    // If an edit occurs outside of this editor, the suggestion is still correctly interpolated.
7059    cx.update_buffer(|buffer, cx| buffer.edit([(5..5, "o")], None, cx));
7060    cx.update_editor(|editor, cx| {
7061        assert!(editor.has_active_copilot_suggestion(cx));
7062        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7063        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
7064
7065        // Tabbing when there is an active suggestion inserts it.
7066        editor.tab(&Default::default(), cx);
7067        assert!(!editor.has_active_copilot_suggestion(cx));
7068        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7069        assert_eq!(editor.text(cx), "one.copilot2\ntwo\nthree\n");
7070
7071        // When undoing the previously active suggestion is shown again.
7072        editor.undo(&Default::default(), cx);
7073        assert!(editor.has_active_copilot_suggestion(cx));
7074        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7075        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
7076
7077        // Hide suggestion.
7078        editor.cancel(&Default::default(), cx);
7079        assert!(!editor.has_active_copilot_suggestion(cx));
7080        assert_eq!(editor.display_text(cx), "one.co\ntwo\nthree\n");
7081        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
7082    });
7083
7084    // If an edit occurs outside of this editor but no suggestion is being shown,
7085    // we won't make it visible.
7086    cx.update_buffer(|buffer, cx| buffer.edit([(6..6, "p")], None, cx));
7087    cx.update_editor(|editor, cx| {
7088        assert!(!editor.has_active_copilot_suggestion(cx));
7089        assert_eq!(editor.display_text(cx), "one.cop\ntwo\nthree\n");
7090        assert_eq!(editor.text(cx), "one.cop\ntwo\nthree\n");
7091    });
7092
7093    // Reset the editor to verify how suggestions behave when tabbing on leading indentation.
7094    cx.update_editor(|editor, cx| {
7095        editor.set_text("fn foo() {\n  \n}", cx);
7096        editor.change_selections(None, cx, |s| {
7097            s.select_ranges([Point::new(1, 2)..Point::new(1, 2)])
7098        });
7099    });
7100    handle_copilot_completion_request(
7101        &copilot_lsp,
7102        vec![copilot::request::Completion {
7103            text: "    let x = 4;".into(),
7104            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
7105            ..Default::default()
7106        }],
7107        vec![],
7108    );
7109
7110    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
7111    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7112    cx.update_editor(|editor, cx| {
7113        assert!(editor.has_active_copilot_suggestion(cx));
7114        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
7115        assert_eq!(editor.text(cx), "fn foo() {\n  \n}");
7116
7117        // Tabbing inside of leading whitespace inserts indentation without accepting the suggestion.
7118        editor.tab(&Default::default(), cx);
7119        assert!(editor.has_active_copilot_suggestion(cx));
7120        assert_eq!(editor.text(cx), "fn foo() {\n    \n}");
7121        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
7122
7123        // Tabbing again accepts the suggestion.
7124        editor.tab(&Default::default(), cx);
7125        assert!(!editor.has_active_copilot_suggestion(cx));
7126        assert_eq!(editor.text(cx), "fn foo() {\n    let x = 4;\n}");
7127        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
7128    });
7129}
7130
7131#[gpui::test]
7132async fn test_copilot_completion_invalidation(
7133    deterministic: Arc<Deterministic>,
7134    cx: &mut gpui::TestAppContext,
7135) {
7136    init_test(cx, |_| {});
7137
7138    let (copilot, copilot_lsp) = Copilot::fake(cx);
7139    cx.update(|cx| cx.set_global(copilot));
7140    let mut cx = EditorLspTestContext::new_rust(
7141        lsp::ServerCapabilities {
7142            completion_provider: Some(lsp::CompletionOptions {
7143                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
7144                ..Default::default()
7145            }),
7146            ..Default::default()
7147        },
7148        cx,
7149    )
7150    .await;
7151
7152    cx.set_state(indoc! {"
7153        one
7154        twˇ
7155        three
7156    "});
7157
7158    handle_copilot_completion_request(
7159        &copilot_lsp,
7160        vec![copilot::request::Completion {
7161            text: "two.foo()".into(),
7162            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
7163            ..Default::default()
7164        }],
7165        vec![],
7166    );
7167    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
7168    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7169    cx.update_editor(|editor, cx| {
7170        assert!(editor.has_active_copilot_suggestion(cx));
7171        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7172        assert_eq!(editor.text(cx), "one\ntw\nthree\n");
7173
7174        editor.backspace(&Default::default(), cx);
7175        assert!(editor.has_active_copilot_suggestion(cx));
7176        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7177        assert_eq!(editor.text(cx), "one\nt\nthree\n");
7178
7179        editor.backspace(&Default::default(), cx);
7180        assert!(editor.has_active_copilot_suggestion(cx));
7181        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7182        assert_eq!(editor.text(cx), "one\n\nthree\n");
7183
7184        // Deleting across the original suggestion range invalidates it.
7185        editor.backspace(&Default::default(), cx);
7186        assert!(!editor.has_active_copilot_suggestion(cx));
7187        assert_eq!(editor.display_text(cx), "one\nthree\n");
7188        assert_eq!(editor.text(cx), "one\nthree\n");
7189
7190        // Undoing the deletion restores the suggestion.
7191        editor.undo(&Default::default(), cx);
7192        assert!(editor.has_active_copilot_suggestion(cx));
7193        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7194        assert_eq!(editor.text(cx), "one\n\nthree\n");
7195    });
7196}
7197
7198#[gpui::test]
7199async fn test_copilot_multibuffer(
7200    deterministic: Arc<Deterministic>,
7201    cx: &mut gpui::TestAppContext,
7202) {
7203    init_test(cx, |_| {});
7204
7205    let (copilot, copilot_lsp) = Copilot::fake(cx);
7206    cx.update(|cx| cx.set_global(copilot));
7207
7208    let buffer_1 = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, "a = 1\nb = 2\n"));
7209    let buffer_2 = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, "c = 3\nd = 4\n"));
7210    let multibuffer = cx.add_model(|cx| {
7211        let mut multibuffer = MultiBuffer::new(0);
7212        multibuffer.push_excerpts(
7213            buffer_1.clone(),
7214            [ExcerptRange {
7215                context: Point::new(0, 0)..Point::new(2, 0),
7216                primary: None,
7217            }],
7218            cx,
7219        );
7220        multibuffer.push_excerpts(
7221            buffer_2.clone(),
7222            [ExcerptRange {
7223                context: Point::new(0, 0)..Point::new(2, 0),
7224                primary: None,
7225            }],
7226            cx,
7227        );
7228        multibuffer
7229    });
7230    let editor = cx.add_window(|cx| build_editor(multibuffer, cx)).root(cx);
7231
7232    handle_copilot_completion_request(
7233        &copilot_lsp,
7234        vec![copilot::request::Completion {
7235            text: "b = 2 + a".into(),
7236            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 5)),
7237            ..Default::default()
7238        }],
7239        vec![],
7240    );
7241    editor.update(cx, |editor, cx| {
7242        // Ensure copilot suggestions are shown for the first excerpt.
7243        editor.change_selections(None, cx, |s| {
7244            s.select_ranges([Point::new(1, 5)..Point::new(1, 5)])
7245        });
7246        editor.next_copilot_suggestion(&Default::default(), cx);
7247    });
7248    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7249    editor.update(cx, |editor, cx| {
7250        assert!(editor.has_active_copilot_suggestion(cx));
7251        assert_eq!(
7252            editor.display_text(cx),
7253            "\n\na = 1\nb = 2 + a\n\n\n\nc = 3\nd = 4\n"
7254        );
7255        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
7256    });
7257
7258    handle_copilot_completion_request(
7259        &copilot_lsp,
7260        vec![copilot::request::Completion {
7261            text: "d = 4 + c".into(),
7262            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 6)),
7263            ..Default::default()
7264        }],
7265        vec![],
7266    );
7267    editor.update(cx, |editor, cx| {
7268        // Move to another excerpt, ensuring the suggestion gets cleared.
7269        editor.change_selections(None, cx, |s| {
7270            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
7271        });
7272        assert!(!editor.has_active_copilot_suggestion(cx));
7273        assert_eq!(
7274            editor.display_text(cx),
7275            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4\n"
7276        );
7277        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
7278
7279        // Type a character, ensuring we don't even try to interpolate the previous suggestion.
7280        editor.handle_input(" ", cx);
7281        assert!(!editor.has_active_copilot_suggestion(cx));
7282        assert_eq!(
7283            editor.display_text(cx),
7284            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 \n"
7285        );
7286        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
7287    });
7288
7289    // Ensure the new suggestion is displayed when the debounce timeout expires.
7290    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7291    editor.update(cx, |editor, cx| {
7292        assert!(editor.has_active_copilot_suggestion(cx));
7293        assert_eq!(
7294            editor.display_text(cx),
7295            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 + c\n"
7296        );
7297        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
7298    });
7299}
7300
7301#[gpui::test]
7302async fn test_copilot_disabled_globs(
7303    deterministic: Arc<Deterministic>,
7304    cx: &mut gpui::TestAppContext,
7305) {
7306    init_test(cx, |settings| {
7307        settings
7308            .copilot
7309            .get_or_insert(Default::default())
7310            .disabled_globs = Some(vec![".env*".to_string()]);
7311    });
7312
7313    let (copilot, copilot_lsp) = Copilot::fake(cx);
7314    cx.update(|cx| cx.set_global(copilot));
7315
7316    let fs = FakeFs::new(cx.background());
7317    fs.insert_tree(
7318        "/test",
7319        json!({
7320            ".env": "SECRET=something\n",
7321            "README.md": "hello\n"
7322        }),
7323    )
7324    .await;
7325    let project = Project::test(fs, ["/test".as_ref()], cx).await;
7326
7327    let private_buffer = project
7328        .update(cx, |project, cx| {
7329            project.open_local_buffer("/test/.env", cx)
7330        })
7331        .await
7332        .unwrap();
7333    let public_buffer = project
7334        .update(cx, |project, cx| {
7335            project.open_local_buffer("/test/README.md", cx)
7336        })
7337        .await
7338        .unwrap();
7339
7340    let multibuffer = cx.add_model(|cx| {
7341        let mut multibuffer = MultiBuffer::new(0);
7342        multibuffer.push_excerpts(
7343            private_buffer.clone(),
7344            [ExcerptRange {
7345                context: Point::new(0, 0)..Point::new(1, 0),
7346                primary: None,
7347            }],
7348            cx,
7349        );
7350        multibuffer.push_excerpts(
7351            public_buffer.clone(),
7352            [ExcerptRange {
7353                context: Point::new(0, 0)..Point::new(1, 0),
7354                primary: None,
7355            }],
7356            cx,
7357        );
7358        multibuffer
7359    });
7360    let editor = cx.add_window(|cx| build_editor(multibuffer, cx)).root(cx);
7361
7362    let mut copilot_requests = copilot_lsp
7363        .handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| async move {
7364            Ok(copilot::request::GetCompletionsResult {
7365                completions: vec![copilot::request::Completion {
7366                    text: "next line".into(),
7367                    range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
7368                    ..Default::default()
7369                }],
7370            })
7371        });
7372
7373    editor.update(cx, |editor, cx| {
7374        editor.change_selections(None, cx, |selections| {
7375            selections.select_ranges([Point::new(0, 0)..Point::new(0, 0)])
7376        });
7377        editor.next_copilot_suggestion(&Default::default(), cx);
7378    });
7379
7380    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7381    assert!(copilot_requests.try_next().is_err());
7382
7383    editor.update(cx, |editor, cx| {
7384        editor.change_selections(None, cx, |s| {
7385            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
7386        });
7387        editor.next_copilot_suggestion(&Default::default(), cx);
7388    });
7389
7390    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7391    assert!(copilot_requests.try_next().is_ok());
7392}
7393
7394#[gpui::test]
7395async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
7396    init_test(cx, |_| {});
7397
7398    let mut language = Language::new(
7399        LanguageConfig {
7400            name: "Rust".into(),
7401            path_suffixes: vec!["rs".to_string()],
7402            brackets: BracketPairConfig {
7403                pairs: vec![BracketPair {
7404                    start: "{".to_string(),
7405                    end: "}".to_string(),
7406                    close: true,
7407                    newline: true,
7408                }],
7409                disabled_scopes_by_bracket_ix: Vec::new(),
7410            },
7411            ..Default::default()
7412        },
7413        Some(tree_sitter_rust::language()),
7414    );
7415    let mut fake_servers = language
7416        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
7417            capabilities: lsp::ServerCapabilities {
7418                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
7419                    first_trigger_character: "{".to_string(),
7420                    more_trigger_character: None,
7421                }),
7422                ..Default::default()
7423            },
7424            ..Default::default()
7425        }))
7426        .await;
7427
7428    let fs = FakeFs::new(cx.background());
7429    fs.insert_tree(
7430        "/a",
7431        json!({
7432            "main.rs": "fn main() { let a = 5; }",
7433            "other.rs": "// Test file",
7434        }),
7435    )
7436    .await;
7437    let project = Project::test(fs, ["/a".as_ref()], cx).await;
7438    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
7439    let workspace = cx
7440        .add_window(|cx| Workspace::test_new(project.clone(), cx))
7441        .root(cx);
7442    let worktree_id = workspace.update(cx, |workspace, cx| {
7443        workspace.project().read_with(cx, |project, cx| {
7444            project.worktrees(cx).next().unwrap().read(cx).id()
7445        })
7446    });
7447
7448    let buffer = project
7449        .update(cx, |project, cx| {
7450            project.open_local_buffer("/a/main.rs", cx)
7451        })
7452        .await
7453        .unwrap();
7454    cx.foreground().run_until_parked();
7455    cx.foreground().start_waiting();
7456    let fake_server = fake_servers.next().await.unwrap();
7457    let editor_handle = workspace
7458        .update(cx, |workspace, cx| {
7459            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
7460        })
7461        .await
7462        .unwrap()
7463        .downcast::<Editor>()
7464        .unwrap();
7465
7466    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
7467        assert_eq!(
7468            params.text_document_position.text_document.uri,
7469            lsp::Url::from_file_path("/a/main.rs").unwrap(),
7470        );
7471        assert_eq!(
7472            params.text_document_position.position,
7473            lsp::Position::new(0, 21),
7474        );
7475
7476        Ok(Some(vec![lsp::TextEdit {
7477            new_text: "]".to_string(),
7478            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
7479        }]))
7480    });
7481
7482    editor_handle.update(cx, |editor, cx| {
7483        cx.focus(&editor_handle);
7484        editor.change_selections(None, cx, |s| {
7485            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
7486        });
7487        editor.handle_input("{", cx);
7488    });
7489
7490    cx.foreground().run_until_parked();
7491
7492    buffer.read_with(cx, |buffer, _| {
7493        assert_eq!(
7494            buffer.text(),
7495            "fn main() { let a = {5}; }",
7496            "No extra braces from on type formatting should appear in the buffer"
7497        )
7498    });
7499}
7500
7501#[gpui::test]
7502async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
7503    init_test(cx, |_| {});
7504
7505    let language_name: Arc<str> = "Rust".into();
7506    let mut language = Language::new(
7507        LanguageConfig {
7508            name: Arc::clone(&language_name),
7509            path_suffixes: vec!["rs".to_string()],
7510            ..Default::default()
7511        },
7512        Some(tree_sitter_rust::language()),
7513    );
7514
7515    let server_restarts = Arc::new(AtomicUsize::new(0));
7516    let closure_restarts = Arc::clone(&server_restarts);
7517    let language_server_name = "test language server";
7518    let mut fake_servers = language
7519        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
7520            name: language_server_name,
7521            initialization_options: Some(json!({
7522                "testOptionValue": true
7523            })),
7524            initializer: Some(Box::new(move |fake_server| {
7525                let task_restarts = Arc::clone(&closure_restarts);
7526                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
7527                    task_restarts.fetch_add(1, atomic::Ordering::Release);
7528                    futures::future::ready(Ok(()))
7529                });
7530            })),
7531            ..Default::default()
7532        }))
7533        .await;
7534
7535    let fs = FakeFs::new(cx.background());
7536    fs.insert_tree(
7537        "/a",
7538        json!({
7539            "main.rs": "fn main() { let a = 5; }",
7540            "other.rs": "// Test file",
7541        }),
7542    )
7543    .await;
7544    let project = Project::test(fs, ["/a".as_ref()], cx).await;
7545    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
7546    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
7547    let _buffer = project
7548        .update(cx, |project, cx| {
7549            project.open_local_buffer("/a/main.rs", cx)
7550        })
7551        .await
7552        .unwrap();
7553    let _fake_server = fake_servers.next().await.unwrap();
7554    update_test_language_settings(cx, |language_settings| {
7555        language_settings.languages.insert(
7556            Arc::clone(&language_name),
7557            LanguageSettingsContent {
7558                tab_size: NonZeroU32::new(8),
7559                ..Default::default()
7560            },
7561        );
7562    });
7563    cx.foreground().run_until_parked();
7564    assert_eq!(
7565        server_restarts.load(atomic::Ordering::Acquire),
7566        0,
7567        "Should not restart LSP server on an unrelated change"
7568    );
7569
7570    update_test_project_settings(cx, |project_settings| {
7571        project_settings.lsp.insert(
7572            "Some other server name".into(),
7573            LspSettings {
7574                initialization_options: Some(json!({
7575                    "some other init value": false
7576                })),
7577            },
7578        );
7579    });
7580    cx.foreground().run_until_parked();
7581    assert_eq!(
7582        server_restarts.load(atomic::Ordering::Acquire),
7583        0,
7584        "Should not restart LSP server on an unrelated LSP settings change"
7585    );
7586
7587    update_test_project_settings(cx, |project_settings| {
7588        project_settings.lsp.insert(
7589            language_server_name.into(),
7590            LspSettings {
7591                initialization_options: Some(json!({
7592                    "anotherInitValue": false
7593                })),
7594            },
7595        );
7596    });
7597    cx.foreground().run_until_parked();
7598    assert_eq!(
7599        server_restarts.load(atomic::Ordering::Acquire),
7600        1,
7601        "Should restart LSP server on a related LSP settings change"
7602    );
7603
7604    update_test_project_settings(cx, |project_settings| {
7605        project_settings.lsp.insert(
7606            language_server_name.into(),
7607            LspSettings {
7608                initialization_options: Some(json!({
7609                    "anotherInitValue": false
7610                })),
7611            },
7612        );
7613    });
7614    cx.foreground().run_until_parked();
7615    assert_eq!(
7616        server_restarts.load(atomic::Ordering::Acquire),
7617        1,
7618        "Should not restart LSP server on a related LSP settings change that is the same"
7619    );
7620
7621    update_test_project_settings(cx, |project_settings| {
7622        project_settings.lsp.insert(
7623            language_server_name.into(),
7624            LspSettings {
7625                initialization_options: None,
7626            },
7627        );
7628    });
7629    cx.foreground().run_until_parked();
7630    assert_eq!(
7631        server_restarts.load(atomic::Ordering::Acquire),
7632        2,
7633        "Should restart LSP server on another related LSP settings change"
7634    );
7635}
7636
7637#[gpui::test]
7638async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
7639    init_test(cx, |_| {});
7640
7641    let mut cx = EditorLspTestContext::new_rust(
7642        lsp::ServerCapabilities {
7643            completion_provider: Some(lsp::CompletionOptions {
7644                trigger_characters: Some(vec![".".to_string()]),
7645                resolve_provider: Some(true),
7646                ..Default::default()
7647            }),
7648            ..Default::default()
7649        },
7650        cx,
7651    )
7652    .await;
7653
7654    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
7655    cx.simulate_keystroke(".");
7656    let completion_item = lsp::CompletionItem {
7657        label: "some".into(),
7658        kind: Some(lsp::CompletionItemKind::SNIPPET),
7659        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
7660        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
7661            kind: lsp::MarkupKind::Markdown,
7662            value: "```rust\nSome(2)\n```".to_string(),
7663        })),
7664        deprecated: Some(false),
7665        sort_text: Some("fffffff2".to_string()),
7666        filter_text: Some("some".to_string()),
7667        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
7668        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
7669            range: lsp::Range {
7670                start: lsp::Position {
7671                    line: 0,
7672                    character: 22,
7673                },
7674                end: lsp::Position {
7675                    line: 0,
7676                    character: 22,
7677                },
7678            },
7679            new_text: "Some(2)".to_string(),
7680        })),
7681        additional_text_edits: Some(vec![lsp::TextEdit {
7682            range: lsp::Range {
7683                start: lsp::Position {
7684                    line: 0,
7685                    character: 20,
7686                },
7687                end: lsp::Position {
7688                    line: 0,
7689                    character: 22,
7690                },
7691            },
7692            new_text: "".to_string(),
7693        }]),
7694        ..Default::default()
7695    };
7696
7697    let closure_completion_item = completion_item.clone();
7698    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
7699        let task_completion_item = closure_completion_item.clone();
7700        async move {
7701            Ok(Some(lsp::CompletionResponse::Array(vec![
7702                task_completion_item,
7703            ])))
7704        }
7705    });
7706
7707    request.next().await;
7708
7709    cx.condition(|editor, _| editor.context_menu_visible())
7710        .await;
7711    let apply_additional_edits = cx.update_editor(|editor, cx| {
7712        editor
7713            .confirm_completion(&ConfirmCompletion::default(), cx)
7714            .unwrap()
7715    });
7716    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
7717
7718    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
7719        let task_completion_item = completion_item.clone();
7720        async move { Ok(task_completion_item) }
7721    })
7722    .next()
7723    .await
7724    .unwrap();
7725    apply_additional_edits.await.unwrap();
7726    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
7727}
7728
7729#[gpui::test]
7730async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
7731    init_test(cx, |_| {});
7732
7733    let mut cx = EditorLspTestContext::new(
7734        Language::new(
7735            LanguageConfig {
7736                path_suffixes: vec!["jsx".into()],
7737                overrides: [(
7738                    "element".into(),
7739                    LanguageConfigOverride {
7740                        word_characters: Override::Set(['-'].into_iter().collect()),
7741                        ..Default::default()
7742                    },
7743                )]
7744                .into_iter()
7745                .collect(),
7746                ..Default::default()
7747            },
7748            Some(tree_sitter_typescript::language_tsx()),
7749        )
7750        .with_override_query("(jsx_self_closing_element) @element")
7751        .unwrap(),
7752        lsp::ServerCapabilities {
7753            completion_provider: Some(lsp::CompletionOptions {
7754                trigger_characters: Some(vec![":".to_string()]),
7755                ..Default::default()
7756            }),
7757            ..Default::default()
7758        },
7759        cx,
7760    )
7761    .await;
7762
7763    cx.lsp
7764        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
7765            Ok(Some(lsp::CompletionResponse::Array(vec![
7766                lsp::CompletionItem {
7767                    label: "bg-blue".into(),
7768                    ..Default::default()
7769                },
7770                lsp::CompletionItem {
7771                    label: "bg-red".into(),
7772                    ..Default::default()
7773                },
7774                lsp::CompletionItem {
7775                    label: "bg-yellow".into(),
7776                    ..Default::default()
7777                },
7778            ])))
7779        });
7780
7781    cx.set_state(r#"<p class="bgˇ" />"#);
7782
7783    // Trigger completion when typing a dash, because the dash is an extra
7784    // word character in the 'element' scope, which contains the cursor.
7785    cx.simulate_keystroke("-");
7786    cx.foreground().run_until_parked();
7787    cx.update_editor(|editor, _| {
7788        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
7789            assert_eq!(
7790                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
7791                &["bg-red", "bg-blue", "bg-yellow"]
7792            );
7793        } else {
7794            panic!("expected completion menu to be open");
7795        }
7796    });
7797
7798    cx.simulate_keystroke("l");
7799    cx.foreground().run_until_parked();
7800    cx.update_editor(|editor, _| {
7801        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
7802            assert_eq!(
7803                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
7804                &["bg-blue", "bg-yellow"]
7805            );
7806        } else {
7807            panic!("expected completion menu to be open");
7808        }
7809    });
7810
7811    // When filtering completions, consider the character after the '-' to
7812    // be the start of a subword.
7813    cx.set_state(r#"<p class="yelˇ" />"#);
7814    cx.simulate_keystroke("l");
7815    cx.foreground().run_until_parked();
7816    cx.update_editor(|editor, _| {
7817        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
7818            assert_eq!(
7819                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
7820                &["bg-yellow"]
7821            );
7822        } else {
7823            panic!("expected completion menu to be open");
7824        }
7825    });
7826}
7827
7828#[gpui::test]
7829async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
7830    init_test(cx, |settings| {
7831        settings.defaults.formatter = Some(language_settings::Formatter::Prettier)
7832    });
7833
7834    let mut language = Language::new(
7835        LanguageConfig {
7836            name: "Rust".into(),
7837            path_suffixes: vec!["rs".to_string()],
7838            prettier_parser_name: Some("test_parser".to_string()),
7839            ..Default::default()
7840        },
7841        Some(tree_sitter_rust::language()),
7842    );
7843
7844    let test_plugin = "test_plugin";
7845    let _ = language
7846        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
7847            prettier_plugins: vec![test_plugin],
7848            ..Default::default()
7849        }))
7850        .await;
7851
7852    let fs = FakeFs::new(cx.background());
7853    fs.insert_file("/file.rs", Default::default()).await;
7854
7855    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
7856    let prettier_format_suffix = project.update(cx, |project, _| {
7857        let suffix = project.enable_test_prettier(&[test_plugin]);
7858        project.languages().add(Arc::new(language));
7859        suffix
7860    });
7861    let buffer = project
7862        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
7863        .await
7864        .unwrap();
7865
7866    let buffer_text = "one\ntwo\nthree\n";
7867    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
7868    let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
7869    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
7870
7871    let format = editor.update(cx, |editor, cx| {
7872        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
7873    });
7874    format.await.unwrap();
7875    assert_eq!(
7876        editor.read_with(cx, |editor, cx| editor.text(cx)),
7877        buffer_text.to_string() + prettier_format_suffix,
7878        "Test prettier formatting was not applied to the original buffer text",
7879    );
7880
7881    update_test_language_settings(cx, |settings| {
7882        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
7883    });
7884    let format = editor.update(cx, |editor, cx| {
7885        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
7886    });
7887    format.await.unwrap();
7888    assert_eq!(
7889        editor.read_with(cx, |editor, cx| editor.text(cx)),
7890        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
7891        "Autoformatting (via test prettier) was not applied to the original buffer text",
7892    );
7893}
7894
7895fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
7896    let point = DisplayPoint::new(row as u32, column as u32);
7897    point..point
7898}
7899
7900fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
7901    let (text, ranges) = marked_text_ranges(marked_text, true);
7902    assert_eq!(view.text(cx), text);
7903    assert_eq!(
7904        view.selections.ranges(cx),
7905        ranges,
7906        "Assert selections are {}",
7907        marked_text
7908    );
7909}
7910
7911/// Handle completion request passing a marked string specifying where the completion
7912/// should be triggered from using '|' character, what range should be replaced, and what completions
7913/// should be returned using '<' and '>' to delimit the range
7914pub fn handle_completion_request<'a>(
7915    cx: &mut EditorLspTestContext<'a>,
7916    marked_string: &str,
7917    completions: Vec<&'static str>,
7918) -> impl Future<Output = ()> {
7919    let complete_from_marker: TextRangeMarker = '|'.into();
7920    let replace_range_marker: TextRangeMarker = ('<', '>').into();
7921    let (_, mut marked_ranges) = marked_text_ranges_by(
7922        marked_string,
7923        vec![complete_from_marker.clone(), replace_range_marker.clone()],
7924    );
7925
7926    let complete_from_position =
7927        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
7928    let replace_range =
7929        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
7930
7931    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
7932        let completions = completions.clone();
7933        async move {
7934            assert_eq!(params.text_document_position.text_document.uri, url.clone());
7935            assert_eq!(
7936                params.text_document_position.position,
7937                complete_from_position
7938            );
7939            Ok(Some(lsp::CompletionResponse::Array(
7940                completions
7941                    .iter()
7942                    .map(|completion_text| lsp::CompletionItem {
7943                        label: completion_text.to_string(),
7944                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
7945                            range: replace_range,
7946                            new_text: completion_text.to_string(),
7947                        })),
7948                        ..Default::default()
7949                    })
7950                    .collect(),
7951            )))
7952        }
7953    });
7954
7955    async move {
7956        request.next().await;
7957    }
7958}
7959
7960fn handle_resolve_completion_request<'a>(
7961    cx: &mut EditorLspTestContext<'a>,
7962    edits: Option<Vec<(&'static str, &'static str)>>,
7963) -> impl Future<Output = ()> {
7964    let edits = edits.map(|edits| {
7965        edits
7966            .iter()
7967            .map(|(marked_string, new_text)| {
7968                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
7969                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
7970                lsp::TextEdit::new(replace_range, new_text.to_string())
7971            })
7972            .collect::<Vec<_>>()
7973    });
7974
7975    let mut request =
7976        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
7977            let edits = edits.clone();
7978            async move {
7979                Ok(lsp::CompletionItem {
7980                    additional_text_edits: edits,
7981                    ..Default::default()
7982                })
7983            }
7984        });
7985
7986    async move {
7987        request.next().await;
7988    }
7989}
7990
7991fn handle_copilot_completion_request(
7992    lsp: &lsp::FakeLanguageServer,
7993    completions: Vec<copilot::request::Completion>,
7994    completions_cycling: Vec<copilot::request::Completion>,
7995) {
7996    lsp.handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| {
7997        let completions = completions.clone();
7998        async move {
7999            Ok(copilot::request::GetCompletionsResult {
8000                completions: completions.clone(),
8001            })
8002        }
8003    });
8004    lsp.handle_request::<copilot::request::GetCompletionsCycling, _, _>(move |_params, _cx| {
8005        let completions_cycling = completions_cycling.clone();
8006        async move {
8007            Ok(copilot::request::GetCompletionsResult {
8008                completions: completions_cycling.clone(),
8009            })
8010        }
8011    });
8012}
8013
8014pub(crate) fn update_test_language_settings(
8015    cx: &mut TestAppContext,
8016    f: impl Fn(&mut AllLanguageSettingsContent),
8017) {
8018    cx.update(|cx| {
8019        cx.update_global::<SettingsStore, _, _>(|store, cx| {
8020            store.update_user_settings::<AllLanguageSettings>(cx, f);
8021        });
8022    });
8023}
8024
8025pub(crate) fn update_test_project_settings(
8026    cx: &mut TestAppContext,
8027    f: impl Fn(&mut ProjectSettings),
8028) {
8029    cx.update(|cx| {
8030        cx.update_global::<SettingsStore, _, _>(|store, cx| {
8031            store.update_user_settings::<ProjectSettings>(cx, f);
8032        });
8033    });
8034}
8035
8036pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
8037    cx.foreground().forbid_parking();
8038
8039    cx.update(|cx| {
8040        cx.set_global(SettingsStore::test(cx));
8041        theme::init((), cx);
8042        client::init_settings(cx);
8043        language::init(cx);
8044        Project::init_settings(cx);
8045        workspace::init_settings(cx);
8046        crate::init(cx);
8047    });
8048
8049    update_test_language_settings(cx, f);
8050}