editor_tests.rs

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