editor_tests.rs

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