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