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]
2504fn test_duplicate_line(cx: &mut TestAppContext) {
2505    init_test(cx, |_| {});
2506
2507    let (_, view) = cx.add_window(|cx| {
2508        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2509        build_editor(buffer, cx)
2510    });
2511    view.update(cx, |view, cx| {
2512        view.change_selections(None, cx, |s| {
2513            s.select_display_ranges([
2514                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2515                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2516                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2517                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2518            ])
2519        });
2520        view.duplicate_line(&DuplicateLine, cx);
2521        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
2522        assert_eq!(
2523            view.selections.display_ranges(cx),
2524            vec![
2525                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
2526                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
2527                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2528                DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
2529            ]
2530        );
2531    });
2532
2533    let (_, view) = cx.add_window(|cx| {
2534        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2535        build_editor(buffer, cx)
2536    });
2537    view.update(cx, |view, cx| {
2538        view.change_selections(None, cx, |s| {
2539            s.select_display_ranges([
2540                DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
2541                DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
2542            ])
2543        });
2544        view.duplicate_line(&DuplicateLine, cx);
2545        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
2546        assert_eq!(
2547            view.selections.display_ranges(cx),
2548            vec![
2549                DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
2550                DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
2551            ]
2552        );
2553    });
2554}
2555
2556#[gpui::test]
2557fn test_move_line_up_down(cx: &mut TestAppContext) {
2558    init_test(cx, |_| {});
2559
2560    let (_, view) = cx.add_window(|cx| {
2561        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
2562        build_editor(buffer, cx)
2563    });
2564    view.update(cx, |view, cx| {
2565        view.fold_ranges(
2566            vec![
2567                Point::new(0, 2)..Point::new(1, 2),
2568                Point::new(2, 3)..Point::new(4, 1),
2569                Point::new(7, 0)..Point::new(8, 4),
2570            ],
2571            true,
2572            cx,
2573        );
2574        view.change_selections(None, cx, |s| {
2575            s.select_display_ranges([
2576                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2577                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2578                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2579                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
2580            ])
2581        });
2582        assert_eq!(
2583            view.display_text(cx),
2584            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
2585        );
2586
2587        view.move_line_up(&MoveLineUp, cx);
2588        assert_eq!(
2589            view.display_text(cx),
2590            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
2591        );
2592        assert_eq!(
2593            view.selections.display_ranges(cx),
2594            vec![
2595                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2596                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2597                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
2598                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
2599            ]
2600        );
2601    });
2602
2603    view.update(cx, |view, cx| {
2604        view.move_line_down(&MoveLineDown, cx);
2605        assert_eq!(
2606            view.display_text(cx),
2607            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
2608        );
2609        assert_eq!(
2610            view.selections.display_ranges(cx),
2611            vec![
2612                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
2613                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2614                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2615                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
2616            ]
2617        );
2618    });
2619
2620    view.update(cx, |view, cx| {
2621        view.move_line_down(&MoveLineDown, cx);
2622        assert_eq!(
2623            view.display_text(cx),
2624            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
2625        );
2626        assert_eq!(
2627            view.selections.display_ranges(cx),
2628            vec![
2629                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2630                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2631                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2632                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
2633            ]
2634        );
2635    });
2636
2637    view.update(cx, |view, cx| {
2638        view.move_line_up(&MoveLineUp, cx);
2639        assert_eq!(
2640            view.display_text(cx),
2641            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
2642        );
2643        assert_eq!(
2644            view.selections.display_ranges(cx),
2645            vec![
2646                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
2647                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2648                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
2649                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
2650            ]
2651        );
2652    });
2653}
2654
2655#[gpui::test]
2656fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
2657    init_test(cx, |_| {});
2658
2659    let (_, editor) = cx.add_window(|cx| {
2660        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
2661        build_editor(buffer, cx)
2662    });
2663    editor.update(cx, |editor, cx| {
2664        let snapshot = editor.buffer.read(cx).snapshot(cx);
2665        editor.insert_blocks(
2666            [BlockProperties {
2667                style: BlockStyle::Fixed,
2668                position: snapshot.anchor_after(Point::new(2, 0)),
2669                disposition: BlockDisposition::Below,
2670                height: 1,
2671                render: Arc::new(|_| Empty::new().into_any()),
2672            }],
2673            Some(Autoscroll::fit()),
2674            cx,
2675        );
2676        editor.change_selections(None, cx, |s| {
2677            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
2678        });
2679        editor.move_line_down(&MoveLineDown, cx);
2680    });
2681}
2682
2683#[gpui::test]
2684fn test_transpose(cx: &mut TestAppContext) {
2685    init_test(cx, |_| {});
2686
2687    _ = cx
2688        .add_window(|cx| {
2689            let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
2690
2691            editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
2692            editor.transpose(&Default::default(), cx);
2693            assert_eq!(editor.text(cx), "bac");
2694            assert_eq!(editor.selections.ranges(cx), [2..2]);
2695
2696            editor.transpose(&Default::default(), cx);
2697            assert_eq!(editor.text(cx), "bca");
2698            assert_eq!(editor.selections.ranges(cx), [3..3]);
2699
2700            editor.transpose(&Default::default(), cx);
2701            assert_eq!(editor.text(cx), "bac");
2702            assert_eq!(editor.selections.ranges(cx), [3..3]);
2703
2704            editor
2705        })
2706        .1;
2707
2708    _ = cx
2709        .add_window(|cx| {
2710            let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
2711
2712            editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
2713            editor.transpose(&Default::default(), cx);
2714            assert_eq!(editor.text(cx), "acb\nde");
2715            assert_eq!(editor.selections.ranges(cx), [3..3]);
2716
2717            editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
2718            editor.transpose(&Default::default(), cx);
2719            assert_eq!(editor.text(cx), "acbd\ne");
2720            assert_eq!(editor.selections.ranges(cx), [5..5]);
2721
2722            editor.transpose(&Default::default(), cx);
2723            assert_eq!(editor.text(cx), "acbde\n");
2724            assert_eq!(editor.selections.ranges(cx), [6..6]);
2725
2726            editor.transpose(&Default::default(), cx);
2727            assert_eq!(editor.text(cx), "acbd\ne");
2728            assert_eq!(editor.selections.ranges(cx), [6..6]);
2729
2730            editor
2731        })
2732        .1;
2733
2734    _ = cx
2735        .add_window(|cx| {
2736            let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
2737
2738            editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
2739            editor.transpose(&Default::default(), cx);
2740            assert_eq!(editor.text(cx), "bacd\ne");
2741            assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
2742
2743            editor.transpose(&Default::default(), cx);
2744            assert_eq!(editor.text(cx), "bcade\n");
2745            assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
2746
2747            editor.transpose(&Default::default(), cx);
2748            assert_eq!(editor.text(cx), "bcda\ne");
2749            assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
2750
2751            editor.transpose(&Default::default(), cx);
2752            assert_eq!(editor.text(cx), "bcade\n");
2753            assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
2754
2755            editor.transpose(&Default::default(), cx);
2756            assert_eq!(editor.text(cx), "bcaed\n");
2757            assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
2758
2759            editor
2760        })
2761        .1;
2762
2763    _ = cx
2764        .add_window(|cx| {
2765            let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
2766
2767            editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
2768            editor.transpose(&Default::default(), cx);
2769            assert_eq!(editor.text(cx), "🏀🍐✋");
2770            assert_eq!(editor.selections.ranges(cx), [8..8]);
2771
2772            editor.transpose(&Default::default(), cx);
2773            assert_eq!(editor.text(cx), "🏀✋🍐");
2774            assert_eq!(editor.selections.ranges(cx), [11..11]);
2775
2776            editor.transpose(&Default::default(), cx);
2777            assert_eq!(editor.text(cx), "🏀🍐✋");
2778            assert_eq!(editor.selections.ranges(cx), [11..11]);
2779
2780            editor
2781        })
2782        .1;
2783}
2784
2785#[gpui::test]
2786async fn test_clipboard(cx: &mut gpui::TestAppContext) {
2787    init_test(cx, |_| {});
2788
2789    let mut cx = EditorTestContext::new(cx).await;
2790
2791    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
2792    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2793    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
2794
2795    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
2796    cx.set_state("two ˇfour ˇsix ˇ");
2797    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2798    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
2799
2800    // Paste again but with only two cursors. Since the number of cursors doesn't
2801    // match the number of slices in the clipboard, the entire clipboard text
2802    // is pasted at each cursor.
2803    cx.set_state("ˇtwo one✅ four three six five ˇ");
2804    cx.update_editor(|e, cx| {
2805        e.handle_input("( ", cx);
2806        e.paste(&Paste, cx);
2807        e.handle_input(") ", cx);
2808    });
2809    cx.assert_editor_state(
2810        &([
2811            "( one✅ ",
2812            "three ",
2813            "five ) ˇtwo one✅ four three six five ( one✅ ",
2814            "three ",
2815            "five ) ˇ",
2816        ]
2817        .join("\n")),
2818    );
2819
2820    // Cut with three selections, one of which is full-line.
2821    cx.set_state(indoc! {"
2822        1«2ˇ»3
2823        4ˇ567
2824        «8ˇ»9"});
2825    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2826    cx.assert_editor_state(indoc! {"
2827        1ˇ3
2828        ˇ9"});
2829
2830    // Paste with three selections, noticing how the copied selection that was full-line
2831    // gets inserted before the second cursor.
2832    cx.set_state(indoc! {"
2833        1ˇ3
28342835        «oˇ»ne"});
2836    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2837    cx.assert_editor_state(indoc! {"
2838        12ˇ3
2839        4567
28402841        8ˇne"});
2842
2843    // Copy with a single cursor only, which writes the whole line into the clipboard.
2844    cx.set_state(indoc! {"
2845        The quick brown
2846        fox juˇmps over
2847        the lazy dog"});
2848    cx.update_editor(|e, cx| e.copy(&Copy, cx));
2849    cx.cx.assert_clipboard_content(Some("fox jumps over\n"));
2850
2851    // Paste with three selections, noticing how the copied full-line selection is inserted
2852    // before the empty selections but replaces the selection that is non-empty.
2853    cx.set_state(indoc! {"
2854        Tˇhe quick brown
2855        «foˇ»x jumps over
2856        tˇhe lazy dog"});
2857    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2858    cx.assert_editor_state(indoc! {"
2859        fox jumps over
2860        Tˇhe quick brown
2861        fox jumps over
2862        ˇx jumps over
2863        fox jumps over
2864        tˇhe lazy dog"});
2865}
2866
2867#[gpui::test]
2868async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
2869    init_test(cx, |_| {});
2870
2871    let mut cx = EditorTestContext::new(cx).await;
2872    let language = Arc::new(Language::new(
2873        LanguageConfig::default(),
2874        Some(tree_sitter_rust::language()),
2875    ));
2876    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
2877
2878    // Cut an indented block, without the leading whitespace.
2879    cx.set_state(indoc! {"
2880        const a: B = (
2881            c(),
2882            «d(
2883                e,
2884                f
2885            )ˇ»
2886        );
2887    "});
2888    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2889    cx.assert_editor_state(indoc! {"
2890        const a: B = (
2891            c(),
2892            ˇ
2893        );
2894    "});
2895
2896    // Paste it at the same position.
2897    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2898    cx.assert_editor_state(indoc! {"
2899        const a: B = (
2900            c(),
2901            d(
2902                e,
2903                f
29042905        );
2906    "});
2907
2908    // Paste it at a line with a lower indent level.
2909    cx.set_state(indoc! {"
2910        ˇ
2911        const a: B = (
2912            c(),
2913        );
2914    "});
2915    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2916    cx.assert_editor_state(indoc! {"
2917        d(
2918            e,
2919            f
29202921        const a: B = (
2922            c(),
2923        );
2924    "});
2925
2926    // Cut an indented block, with the leading whitespace.
2927    cx.set_state(indoc! {"
2928        const a: B = (
2929            c(),
2930        «    d(
2931                e,
2932                f
2933            )
2934        ˇ»);
2935    "});
2936    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2937    cx.assert_editor_state(indoc! {"
2938        const a: B = (
2939            c(),
2940        ˇ);
2941    "});
2942
2943    // Paste it at the same position.
2944    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2945    cx.assert_editor_state(indoc! {"
2946        const a: B = (
2947            c(),
2948            d(
2949                e,
2950                f
2951            )
2952        ˇ);
2953    "});
2954
2955    // Paste it at a line with a higher indent level.
2956    cx.set_state(indoc! {"
2957        const a: B = (
2958            c(),
2959            d(
2960                e,
29612962            )
2963        );
2964    "});
2965    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2966    cx.assert_editor_state(indoc! {"
2967        const a: B = (
2968            c(),
2969            d(
2970                e,
2971                f    d(
2972                    e,
2973                    f
2974                )
2975        ˇ
2976            )
2977        );
2978    "});
2979}
2980
2981#[gpui::test]
2982fn test_select_all(cx: &mut TestAppContext) {
2983    init_test(cx, |_| {});
2984
2985    let (_, view) = cx.add_window(|cx| {
2986        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
2987        build_editor(buffer, cx)
2988    });
2989    view.update(cx, |view, cx| {
2990        view.select_all(&SelectAll, cx);
2991        assert_eq!(
2992            view.selections.display_ranges(cx),
2993            &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
2994        );
2995    });
2996}
2997
2998#[gpui::test]
2999fn test_select_line(cx: &mut TestAppContext) {
3000    init_test(cx, |_| {});
3001
3002    let (_, view) = cx.add_window(|cx| {
3003        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
3004        build_editor(buffer, cx)
3005    });
3006    view.update(cx, |view, cx| {
3007        view.change_selections(None, cx, |s| {
3008            s.select_display_ranges([
3009                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3010                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3011                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
3012                DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
3013            ])
3014        });
3015        view.select_line(&SelectLine, cx);
3016        assert_eq!(
3017            view.selections.display_ranges(cx),
3018            vec![
3019                DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
3020                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
3021            ]
3022        );
3023    });
3024
3025    view.update(cx, |view, cx| {
3026        view.select_line(&SelectLine, cx);
3027        assert_eq!(
3028            view.selections.display_ranges(cx),
3029            vec![
3030                DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
3031                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
3032            ]
3033        );
3034    });
3035
3036    view.update(cx, |view, cx| {
3037        view.select_line(&SelectLine, cx);
3038        assert_eq!(
3039            view.selections.display_ranges(cx),
3040            vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
3041        );
3042    });
3043}
3044
3045#[gpui::test]
3046fn test_split_selection_into_lines(cx: &mut TestAppContext) {
3047    init_test(cx, |_| {});
3048
3049    let (_, view) = cx.add_window(|cx| {
3050        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
3051        build_editor(buffer, cx)
3052    });
3053    view.update(cx, |view, cx| {
3054        view.fold_ranges(
3055            vec![
3056                Point::new(0, 2)..Point::new(1, 2),
3057                Point::new(2, 3)..Point::new(4, 1),
3058                Point::new(7, 0)..Point::new(8, 4),
3059            ],
3060            true,
3061            cx,
3062        );
3063        view.change_selections(None, cx, |s| {
3064            s.select_display_ranges([
3065                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3066                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3067                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
3068                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
3069            ])
3070        });
3071        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
3072    });
3073
3074    view.update(cx, |view, cx| {
3075        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
3076        assert_eq!(
3077            view.display_text(cx),
3078            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
3079        );
3080        assert_eq!(
3081            view.selections.display_ranges(cx),
3082            [
3083                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
3084                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3085                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
3086                DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
3087            ]
3088        );
3089    });
3090
3091    view.update(cx, |view, cx| {
3092        view.change_selections(None, cx, |s| {
3093            s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
3094        });
3095        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
3096        assert_eq!(
3097            view.display_text(cx),
3098            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
3099        );
3100        assert_eq!(
3101            view.selections.display_ranges(cx),
3102            [
3103                DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
3104                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
3105                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
3106                DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
3107                DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
3108                DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
3109                DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
3110                DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
3111            ]
3112        );
3113    });
3114}
3115
3116#[gpui::test]
3117fn test_add_selection_above_below(cx: &mut TestAppContext) {
3118    init_test(cx, |_| {});
3119
3120    let (_, view) = cx.add_window(|cx| {
3121        let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
3122        build_editor(buffer, cx)
3123    });
3124
3125    view.update(cx, |view, cx| {
3126        view.change_selections(None, cx, |s| {
3127            s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
3128        });
3129    });
3130    view.update(cx, |view, cx| {
3131        view.add_selection_above(&AddSelectionAbove, cx);
3132        assert_eq!(
3133            view.selections.display_ranges(cx),
3134            vec![
3135                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
3136                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
3137            ]
3138        );
3139    });
3140
3141    view.update(cx, |view, cx| {
3142        view.add_selection_above(&AddSelectionAbove, cx);
3143        assert_eq!(
3144            view.selections.display_ranges(cx),
3145            vec![
3146                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
3147                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
3148            ]
3149        );
3150    });
3151
3152    view.update(cx, |view, cx| {
3153        view.add_selection_below(&AddSelectionBelow, cx);
3154        assert_eq!(
3155            view.selections.display_ranges(cx),
3156            vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
3157        );
3158
3159        view.undo_selection(&UndoSelection, cx);
3160        assert_eq!(
3161            view.selections.display_ranges(cx),
3162            vec![
3163                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
3164                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
3165            ]
3166        );
3167
3168        view.redo_selection(&RedoSelection, cx);
3169        assert_eq!(
3170            view.selections.display_ranges(cx),
3171            vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
3172        );
3173    });
3174
3175    view.update(cx, |view, cx| {
3176        view.add_selection_below(&AddSelectionBelow, cx);
3177        assert_eq!(
3178            view.selections.display_ranges(cx),
3179            vec![
3180                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
3181                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
3182            ]
3183        );
3184    });
3185
3186    view.update(cx, |view, cx| {
3187        view.add_selection_below(&AddSelectionBelow, cx);
3188        assert_eq!(
3189            view.selections.display_ranges(cx),
3190            vec![
3191                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
3192                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
3193            ]
3194        );
3195    });
3196
3197    view.update(cx, |view, cx| {
3198        view.change_selections(None, cx, |s| {
3199            s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
3200        });
3201    });
3202    view.update(cx, |view, cx| {
3203        view.add_selection_below(&AddSelectionBelow, cx);
3204        assert_eq!(
3205            view.selections.display_ranges(cx),
3206            vec![
3207                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
3208                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
3209            ]
3210        );
3211    });
3212
3213    view.update(cx, |view, cx| {
3214        view.add_selection_below(&AddSelectionBelow, cx);
3215        assert_eq!(
3216            view.selections.display_ranges(cx),
3217            vec![
3218                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
3219                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
3220            ]
3221        );
3222    });
3223
3224    view.update(cx, |view, cx| {
3225        view.add_selection_above(&AddSelectionAbove, cx);
3226        assert_eq!(
3227            view.selections.display_ranges(cx),
3228            vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
3229        );
3230    });
3231
3232    view.update(cx, |view, cx| {
3233        view.add_selection_above(&AddSelectionAbove, cx);
3234        assert_eq!(
3235            view.selections.display_ranges(cx),
3236            vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
3237        );
3238    });
3239
3240    view.update(cx, |view, cx| {
3241        view.change_selections(None, cx, |s| {
3242            s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
3243        });
3244        view.add_selection_below(&AddSelectionBelow, cx);
3245        assert_eq!(
3246            view.selections.display_ranges(cx),
3247            vec![
3248                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
3249                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
3250                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
3251            ]
3252        );
3253    });
3254
3255    view.update(cx, |view, cx| {
3256        view.add_selection_below(&AddSelectionBelow, cx);
3257        assert_eq!(
3258            view.selections.display_ranges(cx),
3259            vec![
3260                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
3261                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
3262                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
3263                DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
3264            ]
3265        );
3266    });
3267
3268    view.update(cx, |view, cx| {
3269        view.add_selection_above(&AddSelectionAbove, cx);
3270        assert_eq!(
3271            view.selections.display_ranges(cx),
3272            vec![
3273                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
3274                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
3275                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
3276            ]
3277        );
3278    });
3279
3280    view.update(cx, |view, cx| {
3281        view.change_selections(None, cx, |s| {
3282            s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
3283        });
3284    });
3285    view.update(cx, |view, cx| {
3286        view.add_selection_above(&AddSelectionAbove, cx);
3287        assert_eq!(
3288            view.selections.display_ranges(cx),
3289            vec![
3290                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
3291                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
3292                DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
3293                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
3294            ]
3295        );
3296    });
3297
3298    view.update(cx, |view, cx| {
3299        view.add_selection_below(&AddSelectionBelow, cx);
3300        assert_eq!(
3301            view.selections.display_ranges(cx),
3302            vec![
3303                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
3304                DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
3305                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
3306            ]
3307        );
3308    });
3309}
3310
3311#[gpui::test]
3312async fn test_select_next(cx: &mut gpui::TestAppContext) {
3313    init_test(cx, |_| {});
3314
3315    let mut cx = EditorTestContext::new(cx).await;
3316    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
3317
3318    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
3319    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3320
3321    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
3322    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
3323
3324    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
3325    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3326
3327    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
3328    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
3329
3330    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
3331    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
3332
3333    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
3334    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
3335}
3336
3337#[gpui::test]
3338async fn test_select_previous(cx: &mut gpui::TestAppContext) {
3339    init_test(cx, |_| {});
3340    {
3341        // `Select previous` without a selection (selects wordwise)
3342        let mut cx = EditorTestContext::new(cx).await;
3343        cx.set_state("abc\nˇabc abc\ndefabc\nabc");
3344
3345        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3346        cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3347
3348        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3349        cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
3350
3351        cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
3352        cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3353
3354        cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
3355        cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
3356
3357        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3358        cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
3359
3360        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3361        cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
3362    }
3363    {
3364        // `Select previous` with a selection
3365        let mut cx = EditorTestContext::new(cx).await;
3366        cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
3367
3368        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3369        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
3370
3371        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3372        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
3373
3374        cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
3375        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
3376
3377        cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
3378        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
3379
3380        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3381        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
3382
3383        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3384        cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
3385    }
3386}
3387
3388#[gpui::test]
3389async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
3390    init_test(cx, |_| {});
3391
3392    let language = Arc::new(Language::new(
3393        LanguageConfig::default(),
3394        Some(tree_sitter_rust::language()),
3395    ));
3396
3397    let text = r#"
3398        use mod1::mod2::{mod3, mod4};
3399
3400        fn fn_1(param1: bool, param2: &str) {
3401            let var1 = "text";
3402        }
3403    "#
3404    .unindent();
3405
3406    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3407    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3408    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
3409    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
3410        .await;
3411
3412    view.update(cx, |view, cx| {
3413        view.change_selections(None, cx, |s| {
3414            s.select_display_ranges([
3415                DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3416                DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3417                DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3418            ]);
3419        });
3420        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3421    });
3422    assert_eq!(
3423        view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
3424        &[
3425            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
3426            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3427            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
3428        ]
3429    );
3430
3431    view.update(cx, |view, cx| {
3432        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3433    });
3434    assert_eq!(
3435        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3436        &[
3437            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3438            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
3439        ]
3440    );
3441
3442    view.update(cx, |view, cx| {
3443        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3444    });
3445    assert_eq!(
3446        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3447        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
3448    );
3449
3450    // Trying to expand the selected syntax node one more time has no effect.
3451    view.update(cx, |view, cx| {
3452        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3453    });
3454    assert_eq!(
3455        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3456        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
3457    );
3458
3459    view.update(cx, |view, cx| {
3460        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3461    });
3462    assert_eq!(
3463        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3464        &[
3465            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3466            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
3467        ]
3468    );
3469
3470    view.update(cx, |view, cx| {
3471        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3472    });
3473    assert_eq!(
3474        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3475        &[
3476            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
3477            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3478            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
3479        ]
3480    );
3481
3482    view.update(cx, |view, cx| {
3483        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3484    });
3485    assert_eq!(
3486        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3487        &[
3488            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3489            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3490            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3491        ]
3492    );
3493
3494    // Trying to shrink the selected syntax node one more time has no effect.
3495    view.update(cx, |view, cx| {
3496        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3497    });
3498    assert_eq!(
3499        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3500        &[
3501            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3502            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3503            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3504        ]
3505    );
3506
3507    // Ensure that we keep expanding the selection if the larger selection starts or ends within
3508    // a fold.
3509    view.update(cx, |view, cx| {
3510        view.fold_ranges(
3511            vec![
3512                Point::new(0, 21)..Point::new(0, 24),
3513                Point::new(3, 20)..Point::new(3, 22),
3514            ],
3515            true,
3516            cx,
3517        );
3518        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3519    });
3520    assert_eq!(
3521        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3522        &[
3523            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3524            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3525            DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
3526        ]
3527    );
3528}
3529
3530#[gpui::test]
3531async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
3532    init_test(cx, |_| {});
3533
3534    let language = Arc::new(
3535        Language::new(
3536            LanguageConfig {
3537                brackets: BracketPairConfig {
3538                    pairs: vec![
3539                        BracketPair {
3540                            start: "{".to_string(),
3541                            end: "}".to_string(),
3542                            close: false,
3543                            newline: true,
3544                        },
3545                        BracketPair {
3546                            start: "(".to_string(),
3547                            end: ")".to_string(),
3548                            close: false,
3549                            newline: true,
3550                        },
3551                    ],
3552                    ..Default::default()
3553                },
3554                ..Default::default()
3555            },
3556            Some(tree_sitter_rust::language()),
3557        )
3558        .with_indents_query(
3559            r#"
3560                (_ "(" ")" @end) @indent
3561                (_ "{" "}" @end) @indent
3562            "#,
3563        )
3564        .unwrap(),
3565    );
3566
3567    let text = "fn a() {}";
3568
3569    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3570    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3571    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3572    editor
3573        .condition(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
3574        .await;
3575
3576    editor.update(cx, |editor, cx| {
3577        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
3578        editor.newline(&Newline, cx);
3579        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
3580        assert_eq!(
3581            editor.selections.ranges(cx),
3582            &[
3583                Point::new(1, 4)..Point::new(1, 4),
3584                Point::new(3, 4)..Point::new(3, 4),
3585                Point::new(5, 0)..Point::new(5, 0)
3586            ]
3587        );
3588    });
3589}
3590
3591#[gpui::test]
3592async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
3593    init_test(cx, |_| {});
3594
3595    let mut cx = EditorTestContext::new(cx).await;
3596
3597    let language = Arc::new(Language::new(
3598        LanguageConfig {
3599            brackets: BracketPairConfig {
3600                pairs: vec![
3601                    BracketPair {
3602                        start: "{".to_string(),
3603                        end: "}".to_string(),
3604                        close: true,
3605                        newline: true,
3606                    },
3607                    BracketPair {
3608                        start: "(".to_string(),
3609                        end: ")".to_string(),
3610                        close: true,
3611                        newline: true,
3612                    },
3613                    BracketPair {
3614                        start: "/*".to_string(),
3615                        end: " */".to_string(),
3616                        close: true,
3617                        newline: true,
3618                    },
3619                    BracketPair {
3620                        start: "[".to_string(),
3621                        end: "]".to_string(),
3622                        close: false,
3623                        newline: true,
3624                    },
3625                    BracketPair {
3626                        start: "\"".to_string(),
3627                        end: "\"".to_string(),
3628                        close: true,
3629                        newline: false,
3630                    },
3631                ],
3632                ..Default::default()
3633            },
3634            autoclose_before: "})]".to_string(),
3635            ..Default::default()
3636        },
3637        Some(tree_sitter_rust::language()),
3638    ));
3639
3640    let registry = Arc::new(LanguageRegistry::test());
3641    registry.add(language.clone());
3642    cx.update_buffer(|buffer, cx| {
3643        buffer.set_language_registry(registry);
3644        buffer.set_language(Some(language), cx);
3645    });
3646
3647    cx.set_state(
3648        &r#"
3649            🏀ˇ
3650            εˇ
3651            ❤️ˇ
3652        "#
3653        .unindent(),
3654    );
3655
3656    // autoclose multiple nested brackets at multiple cursors
3657    cx.update_editor(|view, cx| {
3658        view.handle_input("{", cx);
3659        view.handle_input("{", cx);
3660        view.handle_input("{", cx);
3661    });
3662    cx.assert_editor_state(
3663        &"
3664            🏀{{{ˇ}}}
3665            ε{{{ˇ}}}
3666            ❤️{{{ˇ}}}
3667        "
3668        .unindent(),
3669    );
3670
3671    // insert a different closing bracket
3672    cx.update_editor(|view, cx| {
3673        view.handle_input(")", cx);
3674    });
3675    cx.assert_editor_state(
3676        &"
3677            🏀{{{)ˇ}}}
3678            ε{{{)ˇ}}}
3679            ❤️{{{)ˇ}}}
3680        "
3681        .unindent(),
3682    );
3683
3684    // skip over the auto-closed brackets when typing a closing bracket
3685    cx.update_editor(|view, cx| {
3686        view.move_right(&MoveRight, cx);
3687        view.handle_input("}", cx);
3688        view.handle_input("}", cx);
3689        view.handle_input("}", cx);
3690    });
3691    cx.assert_editor_state(
3692        &"
3693            🏀{{{)}}}}ˇ
3694            ε{{{)}}}}ˇ
3695            ❤️{{{)}}}}ˇ
3696        "
3697        .unindent(),
3698    );
3699
3700    // autoclose multi-character pairs
3701    cx.set_state(
3702        &"
3703            ˇ
3704            ˇ
3705        "
3706        .unindent(),
3707    );
3708    cx.update_editor(|view, cx| {
3709        view.handle_input("/", cx);
3710        view.handle_input("*", cx);
3711    });
3712    cx.assert_editor_state(
3713        &"
3714            /*ˇ */
3715            /*ˇ */
3716        "
3717        .unindent(),
3718    );
3719
3720    // one cursor autocloses a multi-character pair, one cursor
3721    // does not autoclose.
3722    cx.set_state(
3723        &"
37243725            ˇ
3726        "
3727        .unindent(),
3728    );
3729    cx.update_editor(|view, cx| view.handle_input("*", cx));
3730    cx.assert_editor_state(
3731        &"
3732            /*ˇ */
37333734        "
3735        .unindent(),
3736    );
3737
3738    // Don't autoclose if the next character isn't whitespace and isn't
3739    // listed in the language's "autoclose_before" section.
3740    cx.set_state("ˇa b");
3741    cx.update_editor(|view, cx| view.handle_input("{", cx));
3742    cx.assert_editor_state("{ˇa b");
3743
3744    // Don't autoclose if `close` is false for the bracket pair
3745    cx.set_state("ˇ");
3746    cx.update_editor(|view, cx| view.handle_input("[", cx));
3747    cx.assert_editor_state("");
3748
3749    // Surround with brackets if text is selected
3750    cx.set_state("«aˇ» b");
3751    cx.update_editor(|view, cx| view.handle_input("{", cx));
3752    cx.assert_editor_state("{«aˇ»} b");
3753
3754    // Autclose pair where the start and end characters are the same
3755    cx.set_state("");
3756    cx.update_editor(|view, cx| view.handle_input("\"", cx));
3757    cx.assert_editor_state("a\"ˇ\"");
3758    cx.update_editor(|view, cx| view.handle_input("\"", cx));
3759    cx.assert_editor_state("a\"\"ˇ");
3760}
3761
3762#[gpui::test]
3763async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
3764    init_test(cx, |_| {});
3765
3766    let mut cx = EditorTestContext::new(cx).await;
3767
3768    let html_language = Arc::new(
3769        Language::new(
3770            LanguageConfig {
3771                name: "HTML".into(),
3772                brackets: BracketPairConfig {
3773                    pairs: vec![
3774                        BracketPair {
3775                            start: "<".into(),
3776                            end: ">".into(),
3777                            close: true,
3778                            ..Default::default()
3779                        },
3780                        BracketPair {
3781                            start: "{".into(),
3782                            end: "}".into(),
3783                            close: true,
3784                            ..Default::default()
3785                        },
3786                        BracketPair {
3787                            start: "(".into(),
3788                            end: ")".into(),
3789                            close: true,
3790                            ..Default::default()
3791                        },
3792                    ],
3793                    ..Default::default()
3794                },
3795                autoclose_before: "})]>".into(),
3796                ..Default::default()
3797            },
3798            Some(tree_sitter_html::language()),
3799        )
3800        .with_injection_query(
3801            r#"
3802            (script_element
3803                (raw_text) @content
3804                (#set! "language" "javascript"))
3805            "#,
3806        )
3807        .unwrap(),
3808    );
3809
3810    let javascript_language = Arc::new(Language::new(
3811        LanguageConfig {
3812            name: "JavaScript".into(),
3813            brackets: BracketPairConfig {
3814                pairs: vec![
3815                    BracketPair {
3816                        start: "/*".into(),
3817                        end: " */".into(),
3818                        close: true,
3819                        ..Default::default()
3820                    },
3821                    BracketPair {
3822                        start: "{".into(),
3823                        end: "}".into(),
3824                        close: true,
3825                        ..Default::default()
3826                    },
3827                    BracketPair {
3828                        start: "(".into(),
3829                        end: ")".into(),
3830                        close: true,
3831                        ..Default::default()
3832                    },
3833                ],
3834                ..Default::default()
3835            },
3836            autoclose_before: "})]>".into(),
3837            ..Default::default()
3838        },
3839        Some(tree_sitter_typescript::language_tsx()),
3840    ));
3841
3842    let registry = Arc::new(LanguageRegistry::test());
3843    registry.add(html_language.clone());
3844    registry.add(javascript_language.clone());
3845
3846    cx.update_buffer(|buffer, cx| {
3847        buffer.set_language_registry(registry);
3848        buffer.set_language(Some(html_language), cx);
3849    });
3850
3851    cx.set_state(
3852        &r#"
3853            <body>ˇ
3854                <script>
3855                    var x = 1;ˇ
3856                </script>
3857            </body>ˇ
3858        "#
3859        .unindent(),
3860    );
3861
3862    // Precondition: different languages are active at different locations.
3863    cx.update_editor(|editor, cx| {
3864        let snapshot = editor.snapshot(cx);
3865        let cursors = editor.selections.ranges::<usize>(cx);
3866        let languages = cursors
3867            .iter()
3868            .map(|c| snapshot.language_at(c.start).unwrap().name())
3869            .collect::<Vec<_>>();
3870        assert_eq!(
3871            languages,
3872            &["HTML".into(), "JavaScript".into(), "HTML".into()]
3873        );
3874    });
3875
3876    // Angle brackets autoclose in HTML, but not JavaScript.
3877    cx.update_editor(|editor, cx| {
3878        editor.handle_input("<", cx);
3879        editor.handle_input("a", cx);
3880    });
3881    cx.assert_editor_state(
3882        &r#"
3883            <body><aˇ>
3884                <script>
3885                    var x = 1;<aˇ
3886                </script>
3887            </body><aˇ>
3888        "#
3889        .unindent(),
3890    );
3891
3892    // Curly braces and parens autoclose in both HTML and JavaScript.
3893    cx.update_editor(|editor, cx| {
3894        editor.handle_input(" b=", cx);
3895        editor.handle_input("{", cx);
3896        editor.handle_input("c", cx);
3897        editor.handle_input("(", cx);
3898    });
3899    cx.assert_editor_state(
3900        &r#"
3901            <body><a b={c(ˇ)}>
3902                <script>
3903                    var x = 1;<a b={c(ˇ)}
3904                </script>
3905            </body><a b={c(ˇ)}>
3906        "#
3907        .unindent(),
3908    );
3909
3910    // Brackets that were already autoclosed are skipped.
3911    cx.update_editor(|editor, cx| {
3912        editor.handle_input(")", cx);
3913        editor.handle_input("d", cx);
3914        editor.handle_input("}", cx);
3915    });
3916    cx.assert_editor_state(
3917        &r#"
3918            <body><a b={c()d}ˇ>
3919                <script>
3920                    var x = 1;<a b={c()d}ˇ
3921                </script>
3922            </body><a b={c()d}ˇ>
3923        "#
3924        .unindent(),
3925    );
3926    cx.update_editor(|editor, cx| {
3927        editor.handle_input(">", cx);
3928    });
3929    cx.assert_editor_state(
3930        &r#"
3931            <body><a b={c()d}>ˇ
3932                <script>
3933                    var x = 1;<a b={c()d}>ˇ
3934                </script>
3935            </body><a b={c()d}>ˇ
3936        "#
3937        .unindent(),
3938    );
3939
3940    // Reset
3941    cx.set_state(
3942        &r#"
3943            <body>ˇ
3944                <script>
3945                    var x = 1;ˇ
3946                </script>
3947            </body>ˇ
3948        "#
3949        .unindent(),
3950    );
3951
3952    cx.update_editor(|editor, cx| {
3953        editor.handle_input("<", cx);
3954    });
3955    cx.assert_editor_state(
3956        &r#"
3957            <body><ˇ>
3958                <script>
3959                    var x = 1;<ˇ
3960                </script>
3961            </body><ˇ>
3962        "#
3963        .unindent(),
3964    );
3965
3966    // When backspacing, the closing angle brackets are removed.
3967    cx.update_editor(|editor, cx| {
3968        editor.backspace(&Backspace, cx);
3969    });
3970    cx.assert_editor_state(
3971        &r#"
3972            <body>ˇ
3973                <script>
3974                    var x = 1;ˇ
3975                </script>
3976            </body>ˇ
3977        "#
3978        .unindent(),
3979    );
3980
3981    // Block comments autoclose in JavaScript, but not HTML.
3982    cx.update_editor(|editor, cx| {
3983        editor.handle_input("/", cx);
3984        editor.handle_input("*", cx);
3985    });
3986    cx.assert_editor_state(
3987        &r#"
3988            <body>/*ˇ
3989                <script>
3990                    var x = 1;/*ˇ */
3991                </script>
3992            </body>/*ˇ
3993        "#
3994        .unindent(),
3995    );
3996}
3997
3998#[gpui::test]
3999async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
4000    init_test(cx, |_| {});
4001
4002    let mut cx = EditorTestContext::new(cx).await;
4003
4004    let rust_language = Arc::new(
4005        Language::new(
4006            LanguageConfig {
4007                name: "Rust".into(),
4008                brackets: serde_json::from_value(json!([
4009                    { "start": "{", "end": "}", "close": true, "newline": true },
4010                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
4011                ]))
4012                .unwrap(),
4013                autoclose_before: "})]>".into(),
4014                ..Default::default()
4015            },
4016            Some(tree_sitter_rust::language()),
4017        )
4018        .with_override_query("(string_literal) @string")
4019        .unwrap(),
4020    );
4021
4022    let registry = Arc::new(LanguageRegistry::test());
4023    registry.add(rust_language.clone());
4024
4025    cx.update_buffer(|buffer, cx| {
4026        buffer.set_language_registry(registry);
4027        buffer.set_language(Some(rust_language), cx);
4028    });
4029
4030    cx.set_state(
4031        &r#"
4032            let x = ˇ
4033        "#
4034        .unindent(),
4035    );
4036
4037    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
4038    cx.update_editor(|editor, cx| {
4039        editor.handle_input("\"", cx);
4040    });
4041    cx.assert_editor_state(
4042        &r#"
4043            let x = "ˇ"
4044        "#
4045        .unindent(),
4046    );
4047
4048    // Inserting another quotation mark. The cursor moves across the existing
4049    // automatically-inserted quotation mark.
4050    cx.update_editor(|editor, cx| {
4051        editor.handle_input("\"", cx);
4052    });
4053    cx.assert_editor_state(
4054        &r#"
4055            let x = ""ˇ
4056        "#
4057        .unindent(),
4058    );
4059
4060    // Reset
4061    cx.set_state(
4062        &r#"
4063            let x = ˇ
4064        "#
4065        .unindent(),
4066    );
4067
4068    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
4069    cx.update_editor(|editor, cx| {
4070        editor.handle_input("\"", cx);
4071        editor.handle_input(" ", cx);
4072        editor.move_left(&Default::default(), cx);
4073        editor.handle_input("\\", cx);
4074        editor.handle_input("\"", cx);
4075    });
4076    cx.assert_editor_state(
4077        &r#"
4078            let x = "\"ˇ "
4079        "#
4080        .unindent(),
4081    );
4082
4083    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
4084    // mark. Nothing is inserted.
4085    cx.update_editor(|editor, cx| {
4086        editor.move_right(&Default::default(), cx);
4087        editor.handle_input("\"", cx);
4088    });
4089    cx.assert_editor_state(
4090        &r#"
4091            let x = "\" "ˇ
4092        "#
4093        .unindent(),
4094    );
4095}
4096
4097#[gpui::test]
4098async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
4099    init_test(cx, |_| {});
4100
4101    let language = Arc::new(Language::new(
4102        LanguageConfig {
4103            brackets: BracketPairConfig {
4104                pairs: vec![
4105                    BracketPair {
4106                        start: "{".to_string(),
4107                        end: "}".to_string(),
4108                        close: true,
4109                        newline: true,
4110                    },
4111                    BracketPair {
4112                        start: "/* ".to_string(),
4113                        end: "*/".to_string(),
4114                        close: true,
4115                        ..Default::default()
4116                    },
4117                ],
4118                ..Default::default()
4119            },
4120            ..Default::default()
4121        },
4122        Some(tree_sitter_rust::language()),
4123    ));
4124
4125    let text = r#"
4126        a
4127        b
4128        c
4129    "#
4130    .unindent();
4131
4132    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
4133    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4134    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
4135    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
4136        .await;
4137
4138    view.update(cx, |view, cx| {
4139        view.change_selections(None, cx, |s| {
4140            s.select_display_ranges([
4141                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
4142                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
4143                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
4144            ])
4145        });
4146
4147        view.handle_input("{", cx);
4148        view.handle_input("{", cx);
4149        view.handle_input("{", cx);
4150        assert_eq!(
4151            view.text(cx),
4152            "
4153                {{{a}}}
4154                {{{b}}}
4155                {{{c}}}
4156            "
4157            .unindent()
4158        );
4159        assert_eq!(
4160            view.selections.display_ranges(cx),
4161            [
4162                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
4163                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
4164                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
4165            ]
4166        );
4167
4168        view.undo(&Undo, cx);
4169        view.undo(&Undo, cx);
4170        view.undo(&Undo, cx);
4171        assert_eq!(
4172            view.text(cx),
4173            "
4174                a
4175                b
4176                c
4177            "
4178            .unindent()
4179        );
4180        assert_eq!(
4181            view.selections.display_ranges(cx),
4182            [
4183                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
4184                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
4185                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
4186            ]
4187        );
4188
4189        // Ensure inserting the first character of a multi-byte bracket pair
4190        // doesn't surround the selections with the bracket.
4191        view.handle_input("/", cx);
4192        assert_eq!(
4193            view.text(cx),
4194            "
4195                /
4196                /
4197                /
4198            "
4199            .unindent()
4200        );
4201        assert_eq!(
4202            view.selections.display_ranges(cx),
4203            [
4204                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
4205                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
4206                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
4207            ]
4208        );
4209
4210        view.undo(&Undo, cx);
4211        assert_eq!(
4212            view.text(cx),
4213            "
4214                a
4215                b
4216                c
4217            "
4218            .unindent()
4219        );
4220        assert_eq!(
4221            view.selections.display_ranges(cx),
4222            [
4223                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
4224                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
4225                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
4226            ]
4227        );
4228
4229        // Ensure inserting the last character of a multi-byte bracket pair
4230        // doesn't surround the selections with the bracket.
4231        view.handle_input("*", cx);
4232        assert_eq!(
4233            view.text(cx),
4234            "
4235                *
4236                *
4237                *
4238            "
4239            .unindent()
4240        );
4241        assert_eq!(
4242            view.selections.display_ranges(cx),
4243            [
4244                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
4245                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
4246                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
4247            ]
4248        );
4249    });
4250}
4251
4252#[gpui::test]
4253async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
4254    init_test(cx, |_| {});
4255
4256    let language = Arc::new(Language::new(
4257        LanguageConfig {
4258            brackets: BracketPairConfig {
4259                pairs: vec![BracketPair {
4260                    start: "{".to_string(),
4261                    end: "}".to_string(),
4262                    close: true,
4263                    newline: true,
4264                }],
4265                ..Default::default()
4266            },
4267            autoclose_before: "}".to_string(),
4268            ..Default::default()
4269        },
4270        Some(tree_sitter_rust::language()),
4271    ));
4272
4273    let text = r#"
4274        a
4275        b
4276        c
4277    "#
4278    .unindent();
4279
4280    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
4281    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4282    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4283    editor
4284        .condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
4285        .await;
4286
4287    editor.update(cx, |editor, cx| {
4288        editor.change_selections(None, cx, |s| {
4289            s.select_ranges([
4290                Point::new(0, 1)..Point::new(0, 1),
4291                Point::new(1, 1)..Point::new(1, 1),
4292                Point::new(2, 1)..Point::new(2, 1),
4293            ])
4294        });
4295
4296        editor.handle_input("{", cx);
4297        editor.handle_input("{", cx);
4298        editor.handle_input("_", cx);
4299        assert_eq!(
4300            editor.text(cx),
4301            "
4302                a{{_}}
4303                b{{_}}
4304                c{{_}}
4305            "
4306            .unindent()
4307        );
4308        assert_eq!(
4309            editor.selections.ranges::<Point>(cx),
4310            [
4311                Point::new(0, 4)..Point::new(0, 4),
4312                Point::new(1, 4)..Point::new(1, 4),
4313                Point::new(2, 4)..Point::new(2, 4)
4314            ]
4315        );
4316
4317        editor.backspace(&Default::default(), cx);
4318        editor.backspace(&Default::default(), cx);
4319        assert_eq!(
4320            editor.text(cx),
4321            "
4322                a{}
4323                b{}
4324                c{}
4325            "
4326            .unindent()
4327        );
4328        assert_eq!(
4329            editor.selections.ranges::<Point>(cx),
4330            [
4331                Point::new(0, 2)..Point::new(0, 2),
4332                Point::new(1, 2)..Point::new(1, 2),
4333                Point::new(2, 2)..Point::new(2, 2)
4334            ]
4335        );
4336
4337        editor.delete_to_previous_word_start(&Default::default(), cx);
4338        assert_eq!(
4339            editor.text(cx),
4340            "
4341                a
4342                b
4343                c
4344            "
4345            .unindent()
4346        );
4347        assert_eq!(
4348            editor.selections.ranges::<Point>(cx),
4349            [
4350                Point::new(0, 1)..Point::new(0, 1),
4351                Point::new(1, 1)..Point::new(1, 1),
4352                Point::new(2, 1)..Point::new(2, 1)
4353            ]
4354        );
4355    });
4356}
4357
4358#[gpui::test]
4359async fn test_snippets(cx: &mut gpui::TestAppContext) {
4360    init_test(cx, |_| {});
4361
4362    let (text, insertion_ranges) = marked_text_ranges(
4363        indoc! {"
4364            a.ˇ b
4365            a.ˇ b
4366            a.ˇ b
4367        "},
4368        false,
4369    );
4370
4371    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
4372    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4373
4374    editor.update(cx, |editor, cx| {
4375        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
4376
4377        editor
4378            .insert_snippet(&insertion_ranges, snippet, cx)
4379            .unwrap();
4380
4381        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
4382            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
4383            assert_eq!(editor.text(cx), expected_text);
4384            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
4385        }
4386
4387        assert(
4388            editor,
4389            cx,
4390            indoc! {"
4391                a.f(«one», two, «three») b
4392                a.f(«one», two, «three») b
4393                a.f(«one», two, «three») b
4394            "},
4395        );
4396
4397        // Can't move earlier than the first tab stop
4398        assert!(!editor.move_to_prev_snippet_tabstop(cx));
4399        assert(
4400            editor,
4401            cx,
4402            indoc! {"
4403                a.f(«one», two, «three») b
4404                a.f(«one», two, «three») b
4405                a.f(«one», two, «three») b
4406            "},
4407        );
4408
4409        assert!(editor.move_to_next_snippet_tabstop(cx));
4410        assert(
4411            editor,
4412            cx,
4413            indoc! {"
4414                a.f(one, «two», three) b
4415                a.f(one, «two», three) b
4416                a.f(one, «two», three) b
4417            "},
4418        );
4419
4420        editor.move_to_prev_snippet_tabstop(cx);
4421        assert(
4422            editor,
4423            cx,
4424            indoc! {"
4425                a.f(«one», two, «three») b
4426                a.f(«one», two, «three») b
4427                a.f(«one», two, «three») b
4428            "},
4429        );
4430
4431        assert!(editor.move_to_next_snippet_tabstop(cx));
4432        assert(
4433            editor,
4434            cx,
4435            indoc! {"
4436                a.f(one, «two», three) b
4437                a.f(one, «two», three) b
4438                a.f(one, «two», three) b
4439            "},
4440        );
4441        assert!(editor.move_to_next_snippet_tabstop(cx));
4442        assert(
4443            editor,
4444            cx,
4445            indoc! {"
4446                a.f(one, two, three)ˇ b
4447                a.f(one, two, three)ˇ b
4448                a.f(one, two, three)ˇ b
4449            "},
4450        );
4451
4452        // As soon as the last tab stop is reached, snippet state is gone
4453        editor.move_to_prev_snippet_tabstop(cx);
4454        assert(
4455            editor,
4456            cx,
4457            indoc! {"
4458                a.f(one, two, three)ˇ b
4459                a.f(one, two, three)ˇ b
4460                a.f(one, two, three)ˇ b
4461            "},
4462        );
4463    });
4464}
4465
4466#[gpui::test]
4467async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
4468    init_test(cx, |_| {});
4469
4470    let mut language = Language::new(
4471        LanguageConfig {
4472            name: "Rust".into(),
4473            path_suffixes: vec!["rs".to_string()],
4474            ..Default::default()
4475        },
4476        Some(tree_sitter_rust::language()),
4477    );
4478    let mut fake_servers = language
4479        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4480            capabilities: lsp::ServerCapabilities {
4481                document_formatting_provider: Some(lsp::OneOf::Left(true)),
4482                ..Default::default()
4483            },
4484            ..Default::default()
4485        }))
4486        .await;
4487
4488    let fs = FakeFs::new(cx.background());
4489    fs.insert_file("/file.rs", Default::default()).await;
4490
4491    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4492    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4493    let buffer = project
4494        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4495        .await
4496        .unwrap();
4497
4498    cx.foreground().start_waiting();
4499    let fake_server = fake_servers.next().await.unwrap();
4500
4501    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4502    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4503    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4504    assert!(cx.read(|cx| editor.is_dirty(cx)));
4505
4506    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4507    fake_server
4508        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4509            assert_eq!(
4510                params.text_document.uri,
4511                lsp::Url::from_file_path("/file.rs").unwrap()
4512            );
4513            assert_eq!(params.options.tab_size, 4);
4514            Ok(Some(vec![lsp::TextEdit::new(
4515                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4516                ", ".to_string(),
4517            )]))
4518        })
4519        .next()
4520        .await;
4521    cx.foreground().start_waiting();
4522    save.await.unwrap();
4523    assert_eq!(
4524        editor.read_with(cx, |editor, cx| editor.text(cx)),
4525        "one, two\nthree\n"
4526    );
4527    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4528
4529    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4530    assert!(cx.read(|cx| editor.is_dirty(cx)));
4531
4532    // Ensure we can still save even if formatting hangs.
4533    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4534        assert_eq!(
4535            params.text_document.uri,
4536            lsp::Url::from_file_path("/file.rs").unwrap()
4537        );
4538        futures::future::pending::<()>().await;
4539        unreachable!()
4540    });
4541    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4542    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4543    cx.foreground().start_waiting();
4544    save.await.unwrap();
4545    assert_eq!(
4546        editor.read_with(cx, |editor, cx| editor.text(cx)),
4547        "one\ntwo\nthree\n"
4548    );
4549    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4550
4551    // Set rust language override and assert overridden tabsize is sent to language server
4552    update_test_language_settings(cx, |settings| {
4553        settings.languages.insert(
4554            "Rust".into(),
4555            LanguageSettingsContent {
4556                tab_size: NonZeroU32::new(8),
4557                ..Default::default()
4558            },
4559        );
4560    });
4561
4562    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4563    fake_server
4564        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4565            assert_eq!(
4566                params.text_document.uri,
4567                lsp::Url::from_file_path("/file.rs").unwrap()
4568            );
4569            assert_eq!(params.options.tab_size, 8);
4570            Ok(Some(vec![]))
4571        })
4572        .next()
4573        .await;
4574    cx.foreground().start_waiting();
4575    save.await.unwrap();
4576}
4577
4578#[gpui::test]
4579async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
4580    init_test(cx, |_| {});
4581
4582    let mut language = Language::new(
4583        LanguageConfig {
4584            name: "Rust".into(),
4585            path_suffixes: vec!["rs".to_string()],
4586            ..Default::default()
4587        },
4588        Some(tree_sitter_rust::language()),
4589    );
4590    let mut fake_servers = language
4591        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4592            capabilities: lsp::ServerCapabilities {
4593                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
4594                ..Default::default()
4595            },
4596            ..Default::default()
4597        }))
4598        .await;
4599
4600    let fs = FakeFs::new(cx.background());
4601    fs.insert_file("/file.rs", Default::default()).await;
4602
4603    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4604    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4605    let buffer = project
4606        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4607        .await
4608        .unwrap();
4609
4610    cx.foreground().start_waiting();
4611    let fake_server = fake_servers.next().await.unwrap();
4612
4613    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4614    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4615    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4616    assert!(cx.read(|cx| editor.is_dirty(cx)));
4617
4618    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4619    fake_server
4620        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
4621            assert_eq!(
4622                params.text_document.uri,
4623                lsp::Url::from_file_path("/file.rs").unwrap()
4624            );
4625            assert_eq!(params.options.tab_size, 4);
4626            Ok(Some(vec![lsp::TextEdit::new(
4627                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4628                ", ".to_string(),
4629            )]))
4630        })
4631        .next()
4632        .await;
4633    cx.foreground().start_waiting();
4634    save.await.unwrap();
4635    assert_eq!(
4636        editor.read_with(cx, |editor, cx| editor.text(cx)),
4637        "one, two\nthree\n"
4638    );
4639    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4640
4641    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4642    assert!(cx.read(|cx| editor.is_dirty(cx)));
4643
4644    // Ensure we can still save even if formatting hangs.
4645    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
4646        move |params, _| async move {
4647            assert_eq!(
4648                params.text_document.uri,
4649                lsp::Url::from_file_path("/file.rs").unwrap()
4650            );
4651            futures::future::pending::<()>().await;
4652            unreachable!()
4653        },
4654    );
4655    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4656    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4657    cx.foreground().start_waiting();
4658    save.await.unwrap();
4659    assert_eq!(
4660        editor.read_with(cx, |editor, cx| editor.text(cx)),
4661        "one\ntwo\nthree\n"
4662    );
4663    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4664
4665    // Set rust language override and assert overridden tabsize is sent to language server
4666    update_test_language_settings(cx, |settings| {
4667        settings.languages.insert(
4668            "Rust".into(),
4669            LanguageSettingsContent {
4670                tab_size: NonZeroU32::new(8),
4671                ..Default::default()
4672            },
4673        );
4674    });
4675
4676    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4677    fake_server
4678        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
4679            assert_eq!(
4680                params.text_document.uri,
4681                lsp::Url::from_file_path("/file.rs").unwrap()
4682            );
4683            assert_eq!(params.options.tab_size, 8);
4684            Ok(Some(vec![]))
4685        })
4686        .next()
4687        .await;
4688    cx.foreground().start_waiting();
4689    save.await.unwrap();
4690}
4691
4692#[gpui::test]
4693async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
4694    init_test(cx, |_| {});
4695
4696    let mut language = Language::new(
4697        LanguageConfig {
4698            name: "Rust".into(),
4699            path_suffixes: vec!["rs".to_string()],
4700            ..Default::default()
4701        },
4702        Some(tree_sitter_rust::language()),
4703    );
4704    let mut fake_servers = language
4705        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4706            capabilities: lsp::ServerCapabilities {
4707                document_formatting_provider: Some(lsp::OneOf::Left(true)),
4708                ..Default::default()
4709            },
4710            ..Default::default()
4711        }))
4712        .await;
4713
4714    let fs = FakeFs::new(cx.background());
4715    fs.insert_file("/file.rs", Default::default()).await;
4716
4717    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4718    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4719    let buffer = project
4720        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4721        .await
4722        .unwrap();
4723
4724    cx.foreground().start_waiting();
4725    let fake_server = fake_servers.next().await.unwrap();
4726
4727    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4728    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4729    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4730
4731    let format = editor.update(cx, |editor, cx| {
4732        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
4733    });
4734    fake_server
4735        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4736            assert_eq!(
4737                params.text_document.uri,
4738                lsp::Url::from_file_path("/file.rs").unwrap()
4739            );
4740            assert_eq!(params.options.tab_size, 4);
4741            Ok(Some(vec![lsp::TextEdit::new(
4742                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4743                ", ".to_string(),
4744            )]))
4745        })
4746        .next()
4747        .await;
4748    cx.foreground().start_waiting();
4749    format.await.unwrap();
4750    assert_eq!(
4751        editor.read_with(cx, |editor, cx| editor.text(cx)),
4752        "one, two\nthree\n"
4753    );
4754
4755    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4756    // Ensure we don't lock if formatting hangs.
4757    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4758        assert_eq!(
4759            params.text_document.uri,
4760            lsp::Url::from_file_path("/file.rs").unwrap()
4761        );
4762        futures::future::pending::<()>().await;
4763        unreachable!()
4764    });
4765    let format = editor.update(cx, |editor, cx| {
4766        editor.perform_format(project, FormatTrigger::Manual, cx)
4767    });
4768    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4769    cx.foreground().start_waiting();
4770    format.await.unwrap();
4771    assert_eq!(
4772        editor.read_with(cx, |editor, cx| editor.text(cx)),
4773        "one\ntwo\nthree\n"
4774    );
4775}
4776
4777#[gpui::test]
4778async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
4779    init_test(cx, |_| {});
4780
4781    let mut cx = EditorLspTestContext::new_rust(
4782        lsp::ServerCapabilities {
4783            document_formatting_provider: Some(lsp::OneOf::Left(true)),
4784            ..Default::default()
4785        },
4786        cx,
4787    )
4788    .await;
4789
4790    cx.set_state(indoc! {"
4791        one.twoˇ
4792    "});
4793
4794    // The format request takes a long time. When it completes, it inserts
4795    // a newline and an indent before the `.`
4796    cx.lsp
4797        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
4798            let executor = cx.background();
4799            async move {
4800                executor.timer(Duration::from_millis(100)).await;
4801                Ok(Some(vec![lsp::TextEdit {
4802                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
4803                    new_text: "\n    ".into(),
4804                }]))
4805            }
4806        });
4807
4808    // Submit a format request.
4809    let format_1 = cx
4810        .update_editor(|editor, cx| editor.format(&Format, cx))
4811        .unwrap();
4812    cx.foreground().run_until_parked();
4813
4814    // Submit a second format request.
4815    let format_2 = cx
4816        .update_editor(|editor, cx| editor.format(&Format, cx))
4817        .unwrap();
4818    cx.foreground().run_until_parked();
4819
4820    // Wait for both format requests to complete
4821    cx.foreground().advance_clock(Duration::from_millis(200));
4822    cx.foreground().start_waiting();
4823    format_1.await.unwrap();
4824    cx.foreground().start_waiting();
4825    format_2.await.unwrap();
4826
4827    // The formatting edits only happens once.
4828    cx.assert_editor_state(indoc! {"
4829        one
4830            .twoˇ
4831    "});
4832}
4833
4834#[gpui::test]
4835async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
4836    init_test(cx, |_| {});
4837
4838    let mut cx = EditorLspTestContext::new_rust(
4839        lsp::ServerCapabilities {
4840            document_formatting_provider: Some(lsp::OneOf::Left(true)),
4841            ..Default::default()
4842        },
4843        cx,
4844    )
4845    .await;
4846
4847    // Set up a buffer white some trailing whitespace and no trailing newline.
4848    cx.set_state(
4849        &[
4850            "one ",   //
4851            "twoˇ",   //
4852            "three ", //
4853            "four",   //
4854        ]
4855        .join("\n"),
4856    );
4857
4858    // Submit a format request.
4859    let format = cx
4860        .update_editor(|editor, cx| editor.format(&Format, cx))
4861        .unwrap();
4862
4863    // Record which buffer changes have been sent to the language server
4864    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
4865    cx.lsp
4866        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
4867            let buffer_changes = buffer_changes.clone();
4868            move |params, _| {
4869                buffer_changes.lock().extend(
4870                    params
4871                        .content_changes
4872                        .into_iter()
4873                        .map(|e| (e.range.unwrap(), e.text)),
4874                );
4875            }
4876        });
4877
4878    // Handle formatting requests to the language server.
4879    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
4880        let buffer_changes = buffer_changes.clone();
4881        move |_, _| {
4882            // When formatting is requested, trailing whitespace has already been stripped,
4883            // and the trailing newline has already been added.
4884            assert_eq!(
4885                &buffer_changes.lock()[1..],
4886                &[
4887                    (
4888                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
4889                        "".into()
4890                    ),
4891                    (
4892                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
4893                        "".into()
4894                    ),
4895                    (
4896                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
4897                        "\n".into()
4898                    ),
4899                ]
4900            );
4901
4902            // Insert blank lines between each line of the buffer.
4903            async move {
4904                Ok(Some(vec![
4905                    lsp::TextEdit {
4906                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
4907                        new_text: "\n".into(),
4908                    },
4909                    lsp::TextEdit {
4910                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
4911                        new_text: "\n".into(),
4912                    },
4913                ]))
4914            }
4915        }
4916    });
4917
4918    // After formatting the buffer, the trailing whitespace is stripped,
4919    // a newline is appended, and the edits provided by the language server
4920    // have been applied.
4921    format.await.unwrap();
4922    cx.assert_editor_state(
4923        &[
4924            "one",   //
4925            "",      //
4926            "twoˇ",  //
4927            "",      //
4928            "three", //
4929            "four",  //
4930            "",      //
4931        ]
4932        .join("\n"),
4933    );
4934
4935    // Undoing the formatting undoes the trailing whitespace removal, the
4936    // trailing newline, and the LSP edits.
4937    cx.update_buffer(|buffer, cx| buffer.undo(cx));
4938    cx.assert_editor_state(
4939        &[
4940            "one ",   //
4941            "twoˇ",   //
4942            "three ", //
4943            "four",   //
4944        ]
4945        .join("\n"),
4946    );
4947}
4948
4949#[gpui::test]
4950async fn test_completion(cx: &mut gpui::TestAppContext) {
4951    init_test(cx, |_| {});
4952
4953    let mut cx = EditorLspTestContext::new_rust(
4954        lsp::ServerCapabilities {
4955            completion_provider: Some(lsp::CompletionOptions {
4956                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
4957                ..Default::default()
4958            }),
4959            ..Default::default()
4960        },
4961        cx,
4962    )
4963    .await;
4964
4965    cx.set_state(indoc! {"
4966        oneˇ
4967        two
4968        three
4969    "});
4970    cx.simulate_keystroke(".");
4971    handle_completion_request(
4972        &mut cx,
4973        indoc! {"
4974            one.|<>
4975            two
4976            three
4977        "},
4978        vec!["first_completion", "second_completion"],
4979    )
4980    .await;
4981    cx.condition(|editor, _| editor.context_menu_visible())
4982        .await;
4983    let apply_additional_edits = cx.update_editor(|editor, cx| {
4984        editor.move_down(&MoveDown, cx);
4985        editor
4986            .confirm_completion(&ConfirmCompletion::default(), cx)
4987            .unwrap()
4988    });
4989    cx.assert_editor_state(indoc! {"
4990        one.second_completionˇ
4991        two
4992        three
4993    "});
4994
4995    handle_resolve_completion_request(
4996        &mut cx,
4997        Some(vec![
4998            (
4999                //This overlaps with the primary completion edit which is
5000                //misbehavior from the LSP spec, test that we filter it out
5001                indoc! {"
5002                    one.second_ˇcompletion
5003                    two
5004                    threeˇ
5005                "},
5006                "overlapping additional edit",
5007            ),
5008            (
5009                indoc! {"
5010                    one.second_completion
5011                    two
5012                    threeˇ
5013                "},
5014                "\nadditional edit",
5015            ),
5016        ]),
5017    )
5018    .await;
5019    apply_additional_edits.await.unwrap();
5020    cx.assert_editor_state(indoc! {"
5021        one.second_completionˇ
5022        two
5023        three
5024        additional edit
5025    "});
5026
5027    cx.set_state(indoc! {"
5028        one.second_completion
5029        twoˇ
5030        threeˇ
5031        additional edit
5032    "});
5033    cx.simulate_keystroke(" ");
5034    assert!(cx.editor(|e, _| e.context_menu.is_none()));
5035    cx.simulate_keystroke("s");
5036    assert!(cx.editor(|e, _| e.context_menu.is_none()));
5037
5038    cx.assert_editor_state(indoc! {"
5039        one.second_completion
5040        two sˇ
5041        three sˇ
5042        additional edit
5043    "});
5044    handle_completion_request(
5045        &mut cx,
5046        indoc! {"
5047            one.second_completion
5048            two s
5049            three <s|>
5050            additional edit
5051        "},
5052        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
5053    )
5054    .await;
5055    cx.condition(|editor, _| editor.context_menu_visible())
5056        .await;
5057
5058    cx.simulate_keystroke("i");
5059
5060    handle_completion_request(
5061        &mut cx,
5062        indoc! {"
5063            one.second_completion
5064            two si
5065            three <si|>
5066            additional edit
5067        "},
5068        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
5069    )
5070    .await;
5071    cx.condition(|editor, _| editor.context_menu_visible())
5072        .await;
5073
5074    let apply_additional_edits = cx.update_editor(|editor, cx| {
5075        editor
5076            .confirm_completion(&ConfirmCompletion::default(), cx)
5077            .unwrap()
5078    });
5079    cx.assert_editor_state(indoc! {"
5080        one.second_completion
5081        two sixth_completionˇ
5082        three sixth_completionˇ
5083        additional edit
5084    "});
5085
5086    handle_resolve_completion_request(&mut cx, None).await;
5087    apply_additional_edits.await.unwrap();
5088
5089    cx.update(|cx| {
5090        cx.update_global::<SettingsStore, _, _>(|settings, cx| {
5091            settings.update_user_settings::<EditorSettings>(cx, |settings| {
5092                settings.show_completions_on_input = Some(false);
5093            });
5094        })
5095    });
5096    cx.set_state("editorˇ");
5097    cx.simulate_keystroke(".");
5098    assert!(cx.editor(|e, _| e.context_menu.is_none()));
5099    cx.simulate_keystroke("c");
5100    cx.simulate_keystroke("l");
5101    cx.simulate_keystroke("o");
5102    cx.assert_editor_state("editor.cloˇ");
5103    assert!(cx.editor(|e, _| e.context_menu.is_none()));
5104    cx.update_editor(|editor, cx| {
5105        editor.show_completions(&ShowCompletions, cx);
5106    });
5107    handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
5108    cx.condition(|editor, _| editor.context_menu_visible())
5109        .await;
5110    let apply_additional_edits = cx.update_editor(|editor, cx| {
5111        editor
5112            .confirm_completion(&ConfirmCompletion::default(), cx)
5113            .unwrap()
5114    });
5115    cx.assert_editor_state("editor.closeˇ");
5116    handle_resolve_completion_request(&mut cx, None).await;
5117    apply_additional_edits.await.unwrap();
5118}
5119
5120#[gpui::test]
5121async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
5122    init_test(cx, |_| {});
5123    let mut cx = EditorTestContext::new(cx).await;
5124    let language = Arc::new(Language::new(
5125        LanguageConfig {
5126            line_comment: Some("// ".into()),
5127            ..Default::default()
5128        },
5129        Some(tree_sitter_rust::language()),
5130    ));
5131    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
5132
5133    // If multiple selections intersect a line, the line is only toggled once.
5134    cx.set_state(indoc! {"
5135        fn a() {
5136            «//b();
5137            ˇ»// «c();
5138            //ˇ»  d();
5139        }
5140    "});
5141
5142    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5143
5144    cx.assert_editor_state(indoc! {"
5145        fn a() {
5146            «b();
5147            c();
5148            ˇ» d();
5149        }
5150    "});
5151
5152    // The comment prefix is inserted at the same column for every line in a
5153    // selection.
5154    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5155
5156    cx.assert_editor_state(indoc! {"
5157        fn a() {
5158            // «b();
5159            // c();
5160            ˇ»//  d();
5161        }
5162    "});
5163
5164    // If a selection ends at the beginning of a line, that line is not toggled.
5165    cx.set_selections_state(indoc! {"
5166        fn a() {
5167            // b();
5168            «// c();
5169        ˇ»    //  d();
5170        }
5171    "});
5172
5173    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5174
5175    cx.assert_editor_state(indoc! {"
5176        fn a() {
5177            // b();
5178            «c();
5179        ˇ»    //  d();
5180        }
5181    "});
5182
5183    // If a selection span a single line and is empty, the line is toggled.
5184    cx.set_state(indoc! {"
5185        fn a() {
5186            a();
5187            b();
5188        ˇ
5189        }
5190    "});
5191
5192    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5193
5194    cx.assert_editor_state(indoc! {"
5195        fn a() {
5196            a();
5197            b();
5198        //•ˇ
5199        }
5200    "});
5201
5202    // If a selection span multiple lines, empty lines are not toggled.
5203    cx.set_state(indoc! {"
5204        fn a() {
5205            «a();
5206
5207            c();ˇ»
5208        }
5209    "});
5210
5211    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5212
5213    cx.assert_editor_state(indoc! {"
5214        fn a() {
5215            // «a();
5216
5217            // c();ˇ»
5218        }
5219    "});
5220}
5221
5222#[gpui::test]
5223async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
5224    init_test(cx, |_| {});
5225
5226    let language = Arc::new(Language::new(
5227        LanguageConfig {
5228            line_comment: Some("// ".into()),
5229            ..Default::default()
5230        },
5231        Some(tree_sitter_rust::language()),
5232    ));
5233
5234    let registry = Arc::new(LanguageRegistry::test());
5235    registry.add(language.clone());
5236
5237    let mut cx = EditorTestContext::new(cx).await;
5238    cx.update_buffer(|buffer, cx| {
5239        buffer.set_language_registry(registry);
5240        buffer.set_language(Some(language), cx);
5241    });
5242
5243    let toggle_comments = &ToggleComments {
5244        advance_downwards: true,
5245    };
5246
5247    // Single cursor on one line -> advance
5248    // Cursor moves horizontally 3 characters as well on non-blank line
5249    cx.set_state(indoc!(
5250        "fn a() {
5251             ˇdog();
5252             cat();
5253        }"
5254    ));
5255    cx.update_editor(|editor, cx| {
5256        editor.toggle_comments(toggle_comments, cx);
5257    });
5258    cx.assert_editor_state(indoc!(
5259        "fn a() {
5260             // dog();
5261             catˇ();
5262        }"
5263    ));
5264
5265    // Single selection on one line -> don't advance
5266    cx.set_state(indoc!(
5267        "fn a() {
5268             «dog()ˇ»;
5269             cat();
5270        }"
5271    ));
5272    cx.update_editor(|editor, cx| {
5273        editor.toggle_comments(toggle_comments, cx);
5274    });
5275    cx.assert_editor_state(indoc!(
5276        "fn a() {
5277             // «dog()ˇ»;
5278             cat();
5279        }"
5280    ));
5281
5282    // Multiple cursors on one line -> advance
5283    cx.set_state(indoc!(
5284        "fn a() {
5285             ˇdˇog();
5286             cat();
5287        }"
5288    ));
5289    cx.update_editor(|editor, cx| {
5290        editor.toggle_comments(toggle_comments, cx);
5291    });
5292    cx.assert_editor_state(indoc!(
5293        "fn a() {
5294             // dog();
5295             catˇ(ˇ);
5296        }"
5297    ));
5298
5299    // Multiple cursors on one line, with selection -> don't advance
5300    cx.set_state(indoc!(
5301        "fn a() {
5302             ˇdˇog«()ˇ»;
5303             cat();
5304        }"
5305    ));
5306    cx.update_editor(|editor, cx| {
5307        editor.toggle_comments(toggle_comments, cx);
5308    });
5309    cx.assert_editor_state(indoc!(
5310        "fn a() {
5311             // ˇdˇog«()ˇ»;
5312             cat();
5313        }"
5314    ));
5315
5316    // Single cursor on one line -> advance
5317    // Cursor moves to column 0 on blank line
5318    cx.set_state(indoc!(
5319        "fn a() {
5320             ˇdog();
5321
5322             cat();
5323        }"
5324    ));
5325    cx.update_editor(|editor, cx| {
5326        editor.toggle_comments(toggle_comments, cx);
5327    });
5328    cx.assert_editor_state(indoc!(
5329        "fn a() {
5330             // dog();
5331        ˇ
5332             cat();
5333        }"
5334    ));
5335
5336    // Single cursor on one line -> advance
5337    // Cursor starts and ends at column 0
5338    cx.set_state(indoc!(
5339        "fn a() {
5340         ˇ    dog();
5341             cat();
5342        }"
5343    ));
5344    cx.update_editor(|editor, cx| {
5345        editor.toggle_comments(toggle_comments, cx);
5346    });
5347    cx.assert_editor_state(indoc!(
5348        "fn a() {
5349             // dog();
5350         ˇ    cat();
5351        }"
5352    ));
5353}
5354
5355#[gpui::test]
5356async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
5357    init_test(cx, |_| {});
5358
5359    let mut cx = EditorTestContext::new(cx).await;
5360
5361    let html_language = Arc::new(
5362        Language::new(
5363            LanguageConfig {
5364                name: "HTML".into(),
5365                block_comment: Some(("<!-- ".into(), " -->".into())),
5366                ..Default::default()
5367            },
5368            Some(tree_sitter_html::language()),
5369        )
5370        .with_injection_query(
5371            r#"
5372            (script_element
5373                (raw_text) @content
5374                (#set! "language" "javascript"))
5375            "#,
5376        )
5377        .unwrap(),
5378    );
5379
5380    let javascript_language = Arc::new(Language::new(
5381        LanguageConfig {
5382            name: "JavaScript".into(),
5383            line_comment: Some("// ".into()),
5384            ..Default::default()
5385        },
5386        Some(tree_sitter_typescript::language_tsx()),
5387    ));
5388
5389    let registry = Arc::new(LanguageRegistry::test());
5390    registry.add(html_language.clone());
5391    registry.add(javascript_language.clone());
5392
5393    cx.update_buffer(|buffer, cx| {
5394        buffer.set_language_registry(registry);
5395        buffer.set_language(Some(html_language), cx);
5396    });
5397
5398    // Toggle comments for empty selections
5399    cx.set_state(
5400        &r#"
5401            <p>A</p>ˇ
5402            <p>B</p>ˇ
5403            <p>C</p>ˇ
5404        "#
5405        .unindent(),
5406    );
5407    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5408    cx.assert_editor_state(
5409        &r#"
5410            <!-- <p>A</p>ˇ -->
5411            <!-- <p>B</p>ˇ -->
5412            <!-- <p>C</p>ˇ -->
5413        "#
5414        .unindent(),
5415    );
5416    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5417    cx.assert_editor_state(
5418        &r#"
5419            <p>A</p>ˇ
5420            <p>B</p>ˇ
5421            <p>C</p>ˇ
5422        "#
5423        .unindent(),
5424    );
5425
5426    // Toggle comments for mixture of empty and non-empty selections, where
5427    // multiple selections occupy a given line.
5428    cx.set_state(
5429        &r#"
5430            <p>A«</p>
5431            <p>ˇ»B</p>ˇ
5432            <p>C«</p>
5433            <p>ˇ»D</p>ˇ
5434        "#
5435        .unindent(),
5436    );
5437
5438    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5439    cx.assert_editor_state(
5440        &r#"
5441            <!-- <p>A«</p>
5442            <p>ˇ»B</p>ˇ -->
5443            <!-- <p>C«</p>
5444            <p>ˇ»D</p>ˇ -->
5445        "#
5446        .unindent(),
5447    );
5448    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5449    cx.assert_editor_state(
5450        &r#"
5451            <p>A«</p>
5452            <p>ˇ»B</p>ˇ
5453            <p>C«</p>
5454            <p>ˇ»D</p>ˇ
5455        "#
5456        .unindent(),
5457    );
5458
5459    // Toggle comments when different languages are active for different
5460    // selections.
5461    cx.set_state(
5462        &r#"
5463            ˇ<script>
5464                ˇvar x = new Y();
5465            ˇ</script>
5466        "#
5467        .unindent(),
5468    );
5469    cx.foreground().run_until_parked();
5470    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5471    cx.assert_editor_state(
5472        &r#"
5473            <!-- ˇ<script> -->
5474                // ˇvar x = new Y();
5475            <!-- ˇ</script> -->
5476        "#
5477        .unindent(),
5478    );
5479}
5480
5481#[gpui::test]
5482fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
5483    init_test(cx, |_| {});
5484
5485    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5486    let multibuffer = cx.add_model(|cx| {
5487        let mut multibuffer = MultiBuffer::new(0);
5488        multibuffer.push_excerpts(
5489            buffer.clone(),
5490            [
5491                ExcerptRange {
5492                    context: Point::new(0, 0)..Point::new(0, 4),
5493                    primary: None,
5494                },
5495                ExcerptRange {
5496                    context: Point::new(1, 0)..Point::new(1, 4),
5497                    primary: None,
5498                },
5499            ],
5500            cx,
5501        );
5502        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
5503        multibuffer
5504    });
5505
5506    let (_, view) = cx.add_window(|cx| build_editor(multibuffer, cx));
5507    view.update(cx, |view, cx| {
5508        assert_eq!(view.text(cx), "aaaa\nbbbb");
5509        view.change_selections(None, cx, |s| {
5510            s.select_ranges([
5511                Point::new(0, 0)..Point::new(0, 0),
5512                Point::new(1, 0)..Point::new(1, 0),
5513            ])
5514        });
5515
5516        view.handle_input("X", cx);
5517        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
5518        assert_eq!(
5519            view.selections.ranges(cx),
5520            [
5521                Point::new(0, 1)..Point::new(0, 1),
5522                Point::new(1, 1)..Point::new(1, 1),
5523            ]
5524        );
5525
5526        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
5527        view.change_selections(None, cx, |s| {
5528            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
5529        });
5530        view.backspace(&Default::default(), cx);
5531        assert_eq!(view.text(cx), "Xa\nbbb");
5532        assert_eq!(
5533            view.selections.ranges(cx),
5534            [Point::new(1, 0)..Point::new(1, 0)]
5535        );
5536
5537        view.change_selections(None, cx, |s| {
5538            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
5539        });
5540        view.backspace(&Default::default(), cx);
5541        assert_eq!(view.text(cx), "X\nbb");
5542        assert_eq!(
5543            view.selections.ranges(cx),
5544            [Point::new(0, 1)..Point::new(0, 1)]
5545        );
5546    });
5547}
5548
5549#[gpui::test]
5550fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
5551    init_test(cx, |_| {});
5552
5553    let markers = vec![('[', ']').into(), ('(', ')').into()];
5554    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
5555        indoc! {"
5556            [aaaa
5557            (bbbb]
5558            cccc)",
5559        },
5560        markers.clone(),
5561    );
5562    let excerpt_ranges = markers.into_iter().map(|marker| {
5563        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
5564        ExcerptRange {
5565            context,
5566            primary: None,
5567        }
5568    });
5569    let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
5570    let multibuffer = cx.add_model(|cx| {
5571        let mut multibuffer = MultiBuffer::new(0);
5572        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
5573        multibuffer
5574    });
5575
5576    let (_, view) = cx.add_window(|cx| build_editor(multibuffer, cx));
5577    view.update(cx, |view, cx| {
5578        let (expected_text, selection_ranges) = marked_text_ranges(
5579            indoc! {"
5580                aaaa
5581                bˇbbb
5582                bˇbbˇb
5583                cccc"
5584            },
5585            true,
5586        );
5587        assert_eq!(view.text(cx), expected_text);
5588        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
5589
5590        view.handle_input("X", cx);
5591
5592        let (expected_text, expected_selections) = marked_text_ranges(
5593            indoc! {"
5594                aaaa
5595                bXˇbbXb
5596                bXˇbbXˇb
5597                cccc"
5598            },
5599            false,
5600        );
5601        assert_eq!(view.text(cx), expected_text);
5602        assert_eq!(view.selections.ranges(cx), expected_selections);
5603
5604        view.newline(&Newline, cx);
5605        let (expected_text, expected_selections) = marked_text_ranges(
5606            indoc! {"
5607                aaaa
5608                bX
5609                ˇbbX
5610                b
5611                bX
5612                ˇbbX
5613                ˇb
5614                cccc"
5615            },
5616            false,
5617        );
5618        assert_eq!(view.text(cx), expected_text);
5619        assert_eq!(view.selections.ranges(cx), expected_selections);
5620    });
5621}
5622
5623#[gpui::test]
5624fn test_refresh_selections(cx: &mut TestAppContext) {
5625    init_test(cx, |_| {});
5626
5627    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5628    let mut excerpt1_id = None;
5629    let multibuffer = cx.add_model(|cx| {
5630        let mut multibuffer = MultiBuffer::new(0);
5631        excerpt1_id = multibuffer
5632            .push_excerpts(
5633                buffer.clone(),
5634                [
5635                    ExcerptRange {
5636                        context: Point::new(0, 0)..Point::new(1, 4),
5637                        primary: None,
5638                    },
5639                    ExcerptRange {
5640                        context: Point::new(1, 0)..Point::new(2, 4),
5641                        primary: None,
5642                    },
5643                ],
5644                cx,
5645            )
5646            .into_iter()
5647            .next();
5648        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
5649        multibuffer
5650    });
5651
5652    let (_, editor) = cx.add_window(|cx| {
5653        let mut editor = build_editor(multibuffer.clone(), cx);
5654        let snapshot = editor.snapshot(cx);
5655        editor.change_selections(None, cx, |s| {
5656            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
5657        });
5658        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
5659        assert_eq!(
5660            editor.selections.ranges(cx),
5661            [
5662                Point::new(1, 3)..Point::new(1, 3),
5663                Point::new(2, 1)..Point::new(2, 1),
5664            ]
5665        );
5666        editor
5667    });
5668
5669    // Refreshing selections is a no-op when excerpts haven't changed.
5670    editor.update(cx, |editor, cx| {
5671        editor.change_selections(None, cx, |s| s.refresh());
5672        assert_eq!(
5673            editor.selections.ranges(cx),
5674            [
5675                Point::new(1, 3)..Point::new(1, 3),
5676                Point::new(2, 1)..Point::new(2, 1),
5677            ]
5678        );
5679    });
5680
5681    multibuffer.update(cx, |multibuffer, cx| {
5682        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
5683    });
5684    editor.update(cx, |editor, cx| {
5685        // Removing an excerpt causes the first selection to become degenerate.
5686        assert_eq!(
5687            editor.selections.ranges(cx),
5688            [
5689                Point::new(0, 0)..Point::new(0, 0),
5690                Point::new(0, 1)..Point::new(0, 1)
5691            ]
5692        );
5693
5694        // Refreshing selections will relocate the first selection to the original buffer
5695        // location.
5696        editor.change_selections(None, cx, |s| s.refresh());
5697        assert_eq!(
5698            editor.selections.ranges(cx),
5699            [
5700                Point::new(0, 1)..Point::new(0, 1),
5701                Point::new(0, 3)..Point::new(0, 3)
5702            ]
5703        );
5704        assert!(editor.selections.pending_anchor().is_some());
5705    });
5706}
5707
5708#[gpui::test]
5709fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
5710    init_test(cx, |_| {});
5711
5712    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5713    let mut excerpt1_id = None;
5714    let multibuffer = cx.add_model(|cx| {
5715        let mut multibuffer = MultiBuffer::new(0);
5716        excerpt1_id = multibuffer
5717            .push_excerpts(
5718                buffer.clone(),
5719                [
5720                    ExcerptRange {
5721                        context: Point::new(0, 0)..Point::new(1, 4),
5722                        primary: None,
5723                    },
5724                    ExcerptRange {
5725                        context: Point::new(1, 0)..Point::new(2, 4),
5726                        primary: None,
5727                    },
5728                ],
5729                cx,
5730            )
5731            .into_iter()
5732            .next();
5733        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
5734        multibuffer
5735    });
5736
5737    let (_, editor) = cx.add_window(|cx| {
5738        let mut editor = build_editor(multibuffer.clone(), cx);
5739        let snapshot = editor.snapshot(cx);
5740        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
5741        assert_eq!(
5742            editor.selections.ranges(cx),
5743            [Point::new(1, 3)..Point::new(1, 3)]
5744        );
5745        editor
5746    });
5747
5748    multibuffer.update(cx, |multibuffer, cx| {
5749        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
5750    });
5751    editor.update(cx, |editor, cx| {
5752        assert_eq!(
5753            editor.selections.ranges(cx),
5754            [Point::new(0, 0)..Point::new(0, 0)]
5755        );
5756
5757        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
5758        editor.change_selections(None, cx, |s| s.refresh());
5759        assert_eq!(
5760            editor.selections.ranges(cx),
5761            [Point::new(0, 3)..Point::new(0, 3)]
5762        );
5763        assert!(editor.selections.pending_anchor().is_some());
5764    });
5765}
5766
5767#[gpui::test]
5768async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
5769    init_test(cx, |_| {});
5770
5771    let language = Arc::new(
5772        Language::new(
5773            LanguageConfig {
5774                brackets: BracketPairConfig {
5775                    pairs: vec![
5776                        BracketPair {
5777                            start: "{".to_string(),
5778                            end: "}".to_string(),
5779                            close: true,
5780                            newline: true,
5781                        },
5782                        BracketPair {
5783                            start: "/* ".to_string(),
5784                            end: " */".to_string(),
5785                            close: true,
5786                            newline: true,
5787                        },
5788                    ],
5789                    ..Default::default()
5790                },
5791                ..Default::default()
5792            },
5793            Some(tree_sitter_rust::language()),
5794        )
5795        .with_indents_query("")
5796        .unwrap(),
5797    );
5798
5799    let text = concat!(
5800        "{   }\n",     //
5801        "  x\n",       //
5802        "  /*   */\n", //
5803        "x\n",         //
5804        "{{} }\n",     //
5805    );
5806
5807    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
5808    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
5809    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
5810    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
5811        .await;
5812
5813    view.update(cx, |view, cx| {
5814        view.change_selections(None, cx, |s| {
5815            s.select_display_ranges([
5816                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
5817                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
5818                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
5819            ])
5820        });
5821        view.newline(&Newline, cx);
5822
5823        assert_eq!(
5824            view.buffer().read(cx).read(cx).text(),
5825            concat!(
5826                "{ \n",    // Suppress rustfmt
5827                "\n",      //
5828                "}\n",     //
5829                "  x\n",   //
5830                "  /* \n", //
5831                "  \n",    //
5832                "  */\n",  //
5833                "x\n",     //
5834                "{{} \n",  //
5835                "}\n",     //
5836            )
5837        );
5838    });
5839}
5840
5841#[gpui::test]
5842fn test_highlighted_ranges(cx: &mut TestAppContext) {
5843    init_test(cx, |_| {});
5844
5845    let (_, editor) = cx.add_window(|cx| {
5846        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
5847        build_editor(buffer.clone(), cx)
5848    });
5849
5850    editor.update(cx, |editor, cx| {
5851        struct Type1;
5852        struct Type2;
5853
5854        let buffer = editor.buffer.read(cx).snapshot(cx);
5855
5856        let anchor_range =
5857            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
5858
5859        editor.highlight_background::<Type1>(
5860            vec![
5861                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
5862                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
5863                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
5864                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
5865            ],
5866            |_| Color::red(),
5867            cx,
5868        );
5869        editor.highlight_background::<Type2>(
5870            vec![
5871                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
5872                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
5873                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
5874                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
5875            ],
5876            |_| Color::green(),
5877            cx,
5878        );
5879
5880        let snapshot = editor.snapshot(cx);
5881        let mut highlighted_ranges = editor.background_highlights_in_range(
5882            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
5883            &snapshot,
5884            theme::current(cx).as_ref(),
5885        );
5886        // Enforce a consistent ordering based on color without relying on the ordering of the
5887        // highlight's `TypeId` which is non-deterministic.
5888        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
5889        assert_eq!(
5890            highlighted_ranges,
5891            &[
5892                (
5893                    DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
5894                    Color::green(),
5895                ),
5896                (
5897                    DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
5898                    Color::green(),
5899                ),
5900                (
5901                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
5902                    Color::red(),
5903                ),
5904                (
5905                    DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
5906                    Color::red(),
5907                ),
5908            ]
5909        );
5910        assert_eq!(
5911            editor.background_highlights_in_range(
5912                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
5913                &snapshot,
5914                theme::current(cx).as_ref(),
5915            ),
5916            &[(
5917                DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
5918                Color::red(),
5919            )]
5920        );
5921    });
5922}
5923
5924#[gpui::test]
5925async fn test_following(cx: &mut gpui::TestAppContext) {
5926    init_test(cx, |_| {});
5927
5928    let fs = FakeFs::new(cx.background());
5929    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5930
5931    let buffer = project.update(cx, |project, cx| {
5932        let buffer = project
5933            .create_buffer(&sample_text(16, 8, 'a'), None, cx)
5934            .unwrap();
5935        cx.add_model(|cx| MultiBuffer::singleton(buffer, cx))
5936    });
5937    let (_, leader) = cx.add_window(|cx| build_editor(buffer.clone(), cx));
5938    let (_, follower) = cx.update(|cx| {
5939        cx.add_window(
5940            WindowOptions {
5941                bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
5942                ..Default::default()
5943            },
5944            |cx| build_editor(buffer.clone(), cx),
5945        )
5946    });
5947
5948    let is_still_following = Rc::new(RefCell::new(true));
5949    let follower_edit_event_count = Rc::new(RefCell::new(0));
5950    let pending_update = Rc::new(RefCell::new(None));
5951    follower.update(cx, {
5952        let update = pending_update.clone();
5953        let is_still_following = is_still_following.clone();
5954        let follower_edit_event_count = follower_edit_event_count.clone();
5955        |_, cx| {
5956            cx.subscribe(&leader, move |_, leader, event, cx| {
5957                leader
5958                    .read(cx)
5959                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
5960            })
5961            .detach();
5962
5963            cx.subscribe(&follower, move |_, _, event, cx| {
5964                if Editor::should_unfollow_on_event(event, cx) {
5965                    *is_still_following.borrow_mut() = false;
5966                }
5967                if let Event::BufferEdited = event {
5968                    *follower_edit_event_count.borrow_mut() += 1;
5969                }
5970            })
5971            .detach();
5972        }
5973    });
5974
5975    // Update the selections only
5976    leader.update(cx, |leader, cx| {
5977        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
5978    });
5979    follower
5980        .update(cx, |follower, cx| {
5981            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5982        })
5983        .await
5984        .unwrap();
5985    follower.read_with(cx, |follower, cx| {
5986        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
5987    });
5988    assert_eq!(*is_still_following.borrow(), true);
5989    assert_eq!(*follower_edit_event_count.borrow(), 0);
5990
5991    // Update the scroll position only
5992    leader.update(cx, |leader, cx| {
5993        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
5994    });
5995    follower
5996        .update(cx, |follower, cx| {
5997            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5998        })
5999        .await
6000        .unwrap();
6001    assert_eq!(
6002        follower.update(cx, |follower, cx| follower.scroll_position(cx)),
6003        vec2f(1.5, 3.5)
6004    );
6005    assert_eq!(*is_still_following.borrow(), true);
6006    assert_eq!(*follower_edit_event_count.borrow(), 0);
6007
6008    // Update the selections and scroll position. The follower's scroll position is updated
6009    // via autoscroll, not via the leader's exact scroll position.
6010    leader.update(cx, |leader, cx| {
6011        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
6012        leader.request_autoscroll(Autoscroll::newest(), cx);
6013        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
6014    });
6015    follower
6016        .update(cx, |follower, cx| {
6017            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6018        })
6019        .await
6020        .unwrap();
6021    follower.update(cx, |follower, cx| {
6022        assert_eq!(follower.scroll_position(cx), vec2f(1.5, 0.0));
6023        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
6024    });
6025    assert_eq!(*is_still_following.borrow(), true);
6026
6027    // Creating a pending selection that precedes another selection
6028    leader.update(cx, |leader, cx| {
6029        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
6030        leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
6031    });
6032    follower
6033        .update(cx, |follower, cx| {
6034            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6035        })
6036        .await
6037        .unwrap();
6038    follower.read_with(cx, |follower, cx| {
6039        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
6040    });
6041    assert_eq!(*is_still_following.borrow(), true);
6042
6043    // Extend the pending selection so that it surrounds another selection
6044    leader.update(cx, |leader, cx| {
6045        leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
6046    });
6047    follower
6048        .update(cx, |follower, cx| {
6049            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6050        })
6051        .await
6052        .unwrap();
6053    follower.read_with(cx, |follower, cx| {
6054        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
6055    });
6056
6057    // Scrolling locally breaks the follow
6058    follower.update(cx, |follower, cx| {
6059        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
6060        follower.set_scroll_anchor(
6061            ScrollAnchor {
6062                anchor: top_anchor,
6063                offset: vec2f(0.0, 0.5),
6064            },
6065            cx,
6066        );
6067    });
6068    assert_eq!(*is_still_following.borrow(), false);
6069}
6070
6071#[gpui::test]
6072async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
6073    init_test(cx, |_| {});
6074
6075    let fs = FakeFs::new(cx.background());
6076    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
6077    let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
6078    let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
6079
6080    let leader = pane.update(cx, |_, cx| {
6081        let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
6082        cx.add_view(|cx| build_editor(multibuffer.clone(), cx))
6083    });
6084
6085    // Start following the editor when it has no excerpts.
6086    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
6087    let follower_1 = cx
6088        .update(|cx| {
6089            Editor::from_state_proto(
6090                pane.clone(),
6091                project.clone(),
6092                ViewId {
6093                    creator: Default::default(),
6094                    id: 0,
6095                },
6096                &mut state_message,
6097                cx,
6098            )
6099        })
6100        .unwrap()
6101        .await
6102        .unwrap();
6103
6104    let update_message = Rc::new(RefCell::new(None));
6105    follower_1.update(cx, {
6106        let update = update_message.clone();
6107        |_, cx| {
6108            cx.subscribe(&leader, move |_, leader, event, cx| {
6109                leader
6110                    .read(cx)
6111                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
6112            })
6113            .detach();
6114        }
6115    });
6116
6117    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
6118        (
6119            project
6120                .create_buffer("abc\ndef\nghi\njkl\n", None, cx)
6121                .unwrap(),
6122            project
6123                .create_buffer("mno\npqr\nstu\nvwx\n", None, cx)
6124                .unwrap(),
6125        )
6126    });
6127
6128    // Insert some excerpts.
6129    leader.update(cx, |leader, cx| {
6130        leader.buffer.update(cx, |multibuffer, cx| {
6131            let excerpt_ids = multibuffer.push_excerpts(
6132                buffer_1.clone(),
6133                [
6134                    ExcerptRange {
6135                        context: 1..6,
6136                        primary: None,
6137                    },
6138                    ExcerptRange {
6139                        context: 12..15,
6140                        primary: None,
6141                    },
6142                    ExcerptRange {
6143                        context: 0..3,
6144                        primary: None,
6145                    },
6146                ],
6147                cx,
6148            );
6149            multibuffer.insert_excerpts_after(
6150                excerpt_ids[0],
6151                buffer_2.clone(),
6152                [
6153                    ExcerptRange {
6154                        context: 8..12,
6155                        primary: None,
6156                    },
6157                    ExcerptRange {
6158                        context: 0..6,
6159                        primary: None,
6160                    },
6161                ],
6162                cx,
6163            );
6164        });
6165    });
6166
6167    // Apply the update of adding the excerpts.
6168    follower_1
6169        .update(cx, |follower, cx| {
6170            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6171        })
6172        .await
6173        .unwrap();
6174    assert_eq!(
6175        follower_1.read_with(cx, |editor, cx| editor.text(cx)),
6176        leader.read_with(cx, |editor, cx| editor.text(cx))
6177    );
6178    update_message.borrow_mut().take();
6179
6180    // Start following separately after it already has excerpts.
6181    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
6182    let follower_2 = cx
6183        .update(|cx| {
6184            Editor::from_state_proto(
6185                pane.clone(),
6186                project.clone(),
6187                ViewId {
6188                    creator: Default::default(),
6189                    id: 0,
6190                },
6191                &mut state_message,
6192                cx,
6193            )
6194        })
6195        .unwrap()
6196        .await
6197        .unwrap();
6198    assert_eq!(
6199        follower_2.read_with(cx, |editor, cx| editor.text(cx)),
6200        leader.read_with(cx, |editor, cx| editor.text(cx))
6201    );
6202
6203    // Remove some excerpts.
6204    leader.update(cx, |leader, cx| {
6205        leader.buffer.update(cx, |multibuffer, cx| {
6206            let excerpt_ids = multibuffer.excerpt_ids();
6207            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
6208            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
6209        });
6210    });
6211
6212    // Apply the update of removing the excerpts.
6213    follower_1
6214        .update(cx, |follower, cx| {
6215            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6216        })
6217        .await
6218        .unwrap();
6219    follower_2
6220        .update(cx, |follower, cx| {
6221            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6222        })
6223        .await
6224        .unwrap();
6225    update_message.borrow_mut().take();
6226    assert_eq!(
6227        follower_1.read_with(cx, |editor, cx| editor.text(cx)),
6228        leader.read_with(cx, |editor, cx| editor.text(cx))
6229    );
6230}
6231
6232#[test]
6233fn test_combine_syntax_and_fuzzy_match_highlights() {
6234    let string = "abcdefghijklmnop";
6235    let syntax_ranges = [
6236        (
6237            0..3,
6238            HighlightStyle {
6239                color: Some(Color::red()),
6240                ..Default::default()
6241            },
6242        ),
6243        (
6244            4..8,
6245            HighlightStyle {
6246                color: Some(Color::green()),
6247                ..Default::default()
6248            },
6249        ),
6250    ];
6251    let match_indices = [4, 6, 7, 8];
6252    assert_eq!(
6253        combine_syntax_and_fuzzy_match_highlights(
6254            string,
6255            Default::default(),
6256            syntax_ranges.into_iter(),
6257            &match_indices,
6258        ),
6259        &[
6260            (
6261                0..3,
6262                HighlightStyle {
6263                    color: Some(Color::red()),
6264                    ..Default::default()
6265                },
6266            ),
6267            (
6268                4..5,
6269                HighlightStyle {
6270                    color: Some(Color::green()),
6271                    weight: Some(fonts::Weight::BOLD),
6272                    ..Default::default()
6273                },
6274            ),
6275            (
6276                5..6,
6277                HighlightStyle {
6278                    color: Some(Color::green()),
6279                    ..Default::default()
6280                },
6281            ),
6282            (
6283                6..8,
6284                HighlightStyle {
6285                    color: Some(Color::green()),
6286                    weight: Some(fonts::Weight::BOLD),
6287                    ..Default::default()
6288                },
6289            ),
6290            (
6291                8..9,
6292                HighlightStyle {
6293                    weight: Some(fonts::Weight::BOLD),
6294                    ..Default::default()
6295                },
6296            ),
6297        ]
6298    );
6299}
6300
6301#[gpui::test]
6302async fn go_to_hunk(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
6303    init_test(cx, |_| {});
6304
6305    let mut cx = EditorTestContext::new(cx).await;
6306
6307    let diff_base = r#"
6308        use some::mod;
6309
6310        const A: u32 = 42;
6311
6312        fn main() {
6313            println!("hello");
6314
6315            println!("world");
6316        }
6317        "#
6318    .unindent();
6319
6320    // Edits are modified, removed, modified, added
6321    cx.set_state(
6322        &r#"
6323        use some::modified;
6324
6325        ˇ
6326        fn main() {
6327            println!("hello there");
6328
6329            println!("around the");
6330            println!("world");
6331        }
6332        "#
6333        .unindent(),
6334    );
6335
6336    cx.set_diff_base(Some(&diff_base));
6337    deterministic.run_until_parked();
6338
6339    cx.update_editor(|editor, cx| {
6340        //Wrap around the bottom of the buffer
6341        for _ in 0..3 {
6342            editor.go_to_hunk(&GoToHunk, cx);
6343        }
6344    });
6345
6346    cx.assert_editor_state(
6347        &r#"
6348        ˇuse some::modified;
6349
6350
6351        fn main() {
6352            println!("hello there");
6353
6354            println!("around the");
6355            println!("world");
6356        }
6357        "#
6358        .unindent(),
6359    );
6360
6361    cx.update_editor(|editor, cx| {
6362        //Wrap around the top of the buffer
6363        for _ in 0..2 {
6364            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
6365        }
6366    });
6367
6368    cx.assert_editor_state(
6369        &r#"
6370        use some::modified;
6371
6372
6373        fn main() {
6374        ˇ    println!("hello there");
6375
6376            println!("around the");
6377            println!("world");
6378        }
6379        "#
6380        .unindent(),
6381    );
6382
6383    cx.update_editor(|editor, cx| {
6384        editor.fold(&Fold, cx);
6385
6386        //Make sure that the fold only gets one hunk
6387        for _ in 0..4 {
6388            editor.go_to_hunk(&GoToHunk, cx);
6389        }
6390    });
6391
6392    cx.assert_editor_state(
6393        &r#"
6394        ˇuse some::modified;
6395
6396
6397        fn main() {
6398            println!("hello there");
6399
6400            println!("around the");
6401            println!("world");
6402        }
6403        "#
6404        .unindent(),
6405    );
6406}
6407
6408#[test]
6409fn test_split_words() {
6410    fn split<'a>(text: &'a str) -> Vec<&'a str> {
6411        split_words(text).collect()
6412    }
6413
6414    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
6415    assert_eq!(split("hello_world"), &["hello_", "world"]);
6416    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
6417    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
6418    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
6419    assert_eq!(split("helloworld"), &["helloworld"]);
6420}
6421
6422#[gpui::test]
6423async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
6424    init_test(cx, |_| {});
6425
6426    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
6427    let mut assert = |before, after| {
6428        let _state_context = cx.set_state(before);
6429        cx.update_editor(|editor, cx| {
6430            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
6431        });
6432        cx.assert_editor_state(after);
6433    };
6434
6435    // Outside bracket jumps to outside of matching bracket
6436    assert("console.logˇ(var);", "console.log(var)ˇ;");
6437    assert("console.log(var)ˇ;", "console.logˇ(var);");
6438
6439    // Inside bracket jumps to inside of matching bracket
6440    assert("console.log(ˇvar);", "console.log(varˇ);");
6441    assert("console.log(varˇ);", "console.log(ˇvar);");
6442
6443    // When outside a bracket and inside, favor jumping to the inside bracket
6444    assert(
6445        "console.log('foo', [1, 2, 3]ˇ);",
6446        "console.log(ˇ'foo', [1, 2, 3]);",
6447    );
6448    assert(
6449        "console.log(ˇ'foo', [1, 2, 3]);",
6450        "console.log('foo', [1, 2, 3]ˇ);",
6451    );
6452
6453    // Bias forward if two options are equally likely
6454    assert(
6455        "let result = curried_fun()ˇ();",
6456        "let result = curried_fun()()ˇ;",
6457    );
6458
6459    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
6460    assert(
6461        indoc! {"
6462            function test() {
6463                console.log('test')ˇ
6464            }"},
6465        indoc! {"
6466            function test() {
6467                console.logˇ('test')
6468            }"},
6469    );
6470}
6471
6472#[gpui::test(iterations = 10)]
6473async fn test_copilot(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
6474    init_test(cx, |_| {});
6475
6476    let (copilot, copilot_lsp) = Copilot::fake(cx);
6477    cx.update(|cx| cx.set_global(copilot));
6478    let mut cx = EditorLspTestContext::new_rust(
6479        lsp::ServerCapabilities {
6480            completion_provider: Some(lsp::CompletionOptions {
6481                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
6482                ..Default::default()
6483            }),
6484            ..Default::default()
6485        },
6486        cx,
6487    )
6488    .await;
6489
6490    // When inserting, ensure autocompletion is favored over Copilot suggestions.
6491    cx.set_state(indoc! {"
6492        oneˇ
6493        two
6494        three
6495    "});
6496    cx.simulate_keystroke(".");
6497    let _ = handle_completion_request(
6498        &mut cx,
6499        indoc! {"
6500            one.|<>
6501            two
6502            three
6503        "},
6504        vec!["completion_a", "completion_b"],
6505    );
6506    handle_copilot_completion_request(
6507        &copilot_lsp,
6508        vec![copilot::request::Completion {
6509            text: "one.copilot1".into(),
6510            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6511            ..Default::default()
6512        }],
6513        vec![],
6514    );
6515    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6516    cx.update_editor(|editor, cx| {
6517        assert!(editor.context_menu_visible());
6518        assert!(!editor.has_active_copilot_suggestion(cx));
6519
6520        // Confirming a completion inserts it and hides the context menu, without showing
6521        // the copilot suggestion afterwards.
6522        editor
6523            .confirm_completion(&Default::default(), cx)
6524            .unwrap()
6525            .detach();
6526        assert!(!editor.context_menu_visible());
6527        assert!(!editor.has_active_copilot_suggestion(cx));
6528        assert_eq!(editor.text(cx), "one.completion_a\ntwo\nthree\n");
6529        assert_eq!(editor.display_text(cx), "one.completion_a\ntwo\nthree\n");
6530    });
6531
6532    // Ensure Copilot suggestions are shown right away if no autocompletion is available.
6533    cx.set_state(indoc! {"
6534        oneˇ
6535        two
6536        three
6537    "});
6538    cx.simulate_keystroke(".");
6539    let _ = handle_completion_request(
6540        &mut cx,
6541        indoc! {"
6542            one.|<>
6543            two
6544            three
6545        "},
6546        vec![],
6547    );
6548    handle_copilot_completion_request(
6549        &copilot_lsp,
6550        vec![copilot::request::Completion {
6551            text: "one.copilot1".into(),
6552            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6553            ..Default::default()
6554        }],
6555        vec![],
6556    );
6557    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6558    cx.update_editor(|editor, cx| {
6559        assert!(!editor.context_menu_visible());
6560        assert!(editor.has_active_copilot_suggestion(cx));
6561        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
6562        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
6563    });
6564
6565    // Reset editor, and ensure autocompletion is still favored over Copilot suggestions.
6566    cx.set_state(indoc! {"
6567        oneˇ
6568        two
6569        three
6570    "});
6571    cx.simulate_keystroke(".");
6572    let _ = handle_completion_request(
6573        &mut cx,
6574        indoc! {"
6575            one.|<>
6576            two
6577            three
6578        "},
6579        vec!["completion_a", "completion_b"],
6580    );
6581    handle_copilot_completion_request(
6582        &copilot_lsp,
6583        vec![copilot::request::Completion {
6584            text: "one.copilot1".into(),
6585            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6586            ..Default::default()
6587        }],
6588        vec![],
6589    );
6590    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6591    cx.update_editor(|editor, cx| {
6592        assert!(editor.context_menu_visible());
6593        assert!(!editor.has_active_copilot_suggestion(cx));
6594
6595        // When hiding the context menu, the Copilot suggestion becomes visible.
6596        editor.hide_context_menu(cx);
6597        assert!(!editor.context_menu_visible());
6598        assert!(editor.has_active_copilot_suggestion(cx));
6599        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
6600        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
6601    });
6602
6603    // Ensure existing completion is interpolated when inserting again.
6604    cx.simulate_keystroke("c");
6605    deterministic.run_until_parked();
6606    cx.update_editor(|editor, cx| {
6607        assert!(!editor.context_menu_visible());
6608        assert!(editor.has_active_copilot_suggestion(cx));
6609        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
6610        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6611    });
6612
6613    // After debouncing, new Copilot completions should be requested.
6614    handle_copilot_completion_request(
6615        &copilot_lsp,
6616        vec![copilot::request::Completion {
6617            text: "one.copilot2".into(),
6618            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 5)),
6619            ..Default::default()
6620        }],
6621        vec![],
6622    );
6623    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6624    cx.update_editor(|editor, cx| {
6625        assert!(!editor.context_menu_visible());
6626        assert!(editor.has_active_copilot_suggestion(cx));
6627        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6628        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6629
6630        // Canceling should remove the active Copilot suggestion.
6631        editor.cancel(&Default::default(), cx);
6632        assert!(!editor.has_active_copilot_suggestion(cx));
6633        assert_eq!(editor.display_text(cx), "one.c\ntwo\nthree\n");
6634        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6635
6636        // After canceling, tabbing shouldn't insert the previously shown suggestion.
6637        editor.tab(&Default::default(), cx);
6638        assert!(!editor.has_active_copilot_suggestion(cx));
6639        assert_eq!(editor.display_text(cx), "one.c   \ntwo\nthree\n");
6640        assert_eq!(editor.text(cx), "one.c   \ntwo\nthree\n");
6641
6642        // When undoing the previously active suggestion is shown again.
6643        editor.undo(&Default::default(), cx);
6644        assert!(editor.has_active_copilot_suggestion(cx));
6645        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6646        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6647    });
6648
6649    // If an edit occurs outside of this editor, the suggestion is still correctly interpolated.
6650    cx.update_buffer(|buffer, cx| buffer.edit([(5..5, "o")], None, cx));
6651    cx.update_editor(|editor, cx| {
6652        assert!(editor.has_active_copilot_suggestion(cx));
6653        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6654        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6655
6656        // Tabbing when there is an active suggestion inserts it.
6657        editor.tab(&Default::default(), cx);
6658        assert!(!editor.has_active_copilot_suggestion(cx));
6659        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6660        assert_eq!(editor.text(cx), "one.copilot2\ntwo\nthree\n");
6661
6662        // When undoing the previously active suggestion is shown again.
6663        editor.undo(&Default::default(), cx);
6664        assert!(editor.has_active_copilot_suggestion(cx));
6665        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6666        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6667
6668        // Hide suggestion.
6669        editor.cancel(&Default::default(), cx);
6670        assert!(!editor.has_active_copilot_suggestion(cx));
6671        assert_eq!(editor.display_text(cx), "one.co\ntwo\nthree\n");
6672        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6673    });
6674
6675    // If an edit occurs outside of this editor but no suggestion is being shown,
6676    // we won't make it visible.
6677    cx.update_buffer(|buffer, cx| buffer.edit([(6..6, "p")], None, cx));
6678    cx.update_editor(|editor, cx| {
6679        assert!(!editor.has_active_copilot_suggestion(cx));
6680        assert_eq!(editor.display_text(cx), "one.cop\ntwo\nthree\n");
6681        assert_eq!(editor.text(cx), "one.cop\ntwo\nthree\n");
6682    });
6683
6684    // Reset the editor to verify how suggestions behave when tabbing on leading indentation.
6685    cx.update_editor(|editor, cx| {
6686        editor.set_text("fn foo() {\n  \n}", cx);
6687        editor.change_selections(None, cx, |s| {
6688            s.select_ranges([Point::new(1, 2)..Point::new(1, 2)])
6689        });
6690    });
6691    handle_copilot_completion_request(
6692        &copilot_lsp,
6693        vec![copilot::request::Completion {
6694            text: "    let x = 4;".into(),
6695            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
6696            ..Default::default()
6697        }],
6698        vec![],
6699    );
6700
6701    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
6702    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6703    cx.update_editor(|editor, cx| {
6704        assert!(editor.has_active_copilot_suggestion(cx));
6705        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6706        assert_eq!(editor.text(cx), "fn foo() {\n  \n}");
6707
6708        // Tabbing inside of leading whitespace inserts indentation without accepting the suggestion.
6709        editor.tab(&Default::default(), cx);
6710        assert!(editor.has_active_copilot_suggestion(cx));
6711        assert_eq!(editor.text(cx), "fn foo() {\n    \n}");
6712        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6713
6714        // Tabbing again accepts the suggestion.
6715        editor.tab(&Default::default(), cx);
6716        assert!(!editor.has_active_copilot_suggestion(cx));
6717        assert_eq!(editor.text(cx), "fn foo() {\n    let x = 4;\n}");
6718        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6719    });
6720}
6721
6722#[gpui::test]
6723async fn test_copilot_completion_invalidation(
6724    deterministic: Arc<Deterministic>,
6725    cx: &mut gpui::TestAppContext,
6726) {
6727    init_test(cx, |_| {});
6728
6729    let (copilot, copilot_lsp) = Copilot::fake(cx);
6730    cx.update(|cx| cx.set_global(copilot));
6731    let mut cx = EditorLspTestContext::new_rust(
6732        lsp::ServerCapabilities {
6733            completion_provider: Some(lsp::CompletionOptions {
6734                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
6735                ..Default::default()
6736            }),
6737            ..Default::default()
6738        },
6739        cx,
6740    )
6741    .await;
6742
6743    cx.set_state(indoc! {"
6744        one
6745        twˇ
6746        three
6747    "});
6748
6749    handle_copilot_completion_request(
6750        &copilot_lsp,
6751        vec![copilot::request::Completion {
6752            text: "two.foo()".into(),
6753            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
6754            ..Default::default()
6755        }],
6756        vec![],
6757    );
6758    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
6759    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6760    cx.update_editor(|editor, cx| {
6761        assert!(editor.has_active_copilot_suggestion(cx));
6762        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6763        assert_eq!(editor.text(cx), "one\ntw\nthree\n");
6764
6765        editor.backspace(&Default::default(), cx);
6766        assert!(editor.has_active_copilot_suggestion(cx));
6767        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6768        assert_eq!(editor.text(cx), "one\nt\nthree\n");
6769
6770        editor.backspace(&Default::default(), cx);
6771        assert!(editor.has_active_copilot_suggestion(cx));
6772        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6773        assert_eq!(editor.text(cx), "one\n\nthree\n");
6774
6775        // Deleting across the original suggestion range invalidates it.
6776        editor.backspace(&Default::default(), cx);
6777        assert!(!editor.has_active_copilot_suggestion(cx));
6778        assert_eq!(editor.display_text(cx), "one\nthree\n");
6779        assert_eq!(editor.text(cx), "one\nthree\n");
6780
6781        // Undoing the deletion restores the suggestion.
6782        editor.undo(&Default::default(), cx);
6783        assert!(editor.has_active_copilot_suggestion(cx));
6784        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6785        assert_eq!(editor.text(cx), "one\n\nthree\n");
6786    });
6787}
6788
6789#[gpui::test]
6790async fn test_copilot_multibuffer(
6791    deterministic: Arc<Deterministic>,
6792    cx: &mut gpui::TestAppContext,
6793) {
6794    init_test(cx, |_| {});
6795
6796    let (copilot, copilot_lsp) = Copilot::fake(cx);
6797    cx.update(|cx| cx.set_global(copilot));
6798
6799    let buffer_1 = cx.add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx));
6800    let buffer_2 = cx.add_model(|cx| Buffer::new(0, "c = 3\nd = 4\n", cx));
6801    let multibuffer = cx.add_model(|cx| {
6802        let mut multibuffer = MultiBuffer::new(0);
6803        multibuffer.push_excerpts(
6804            buffer_1.clone(),
6805            [ExcerptRange {
6806                context: Point::new(0, 0)..Point::new(2, 0),
6807                primary: None,
6808            }],
6809            cx,
6810        );
6811        multibuffer.push_excerpts(
6812            buffer_2.clone(),
6813            [ExcerptRange {
6814                context: Point::new(0, 0)..Point::new(2, 0),
6815                primary: None,
6816            }],
6817            cx,
6818        );
6819        multibuffer
6820    });
6821    let (_, editor) = cx.add_window(|cx| build_editor(multibuffer, cx));
6822
6823    handle_copilot_completion_request(
6824        &copilot_lsp,
6825        vec![copilot::request::Completion {
6826            text: "b = 2 + a".into(),
6827            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 5)),
6828            ..Default::default()
6829        }],
6830        vec![],
6831    );
6832    editor.update(cx, |editor, cx| {
6833        // Ensure copilot suggestions are shown for the first excerpt.
6834        editor.change_selections(None, cx, |s| {
6835            s.select_ranges([Point::new(1, 5)..Point::new(1, 5)])
6836        });
6837        editor.next_copilot_suggestion(&Default::default(), cx);
6838    });
6839    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6840    editor.update(cx, |editor, cx| {
6841        assert!(editor.has_active_copilot_suggestion(cx));
6842        assert_eq!(
6843            editor.display_text(cx),
6844            "\n\na = 1\nb = 2 + a\n\n\n\nc = 3\nd = 4\n"
6845        );
6846        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
6847    });
6848
6849    handle_copilot_completion_request(
6850        &copilot_lsp,
6851        vec![copilot::request::Completion {
6852            text: "d = 4 + c".into(),
6853            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 6)),
6854            ..Default::default()
6855        }],
6856        vec![],
6857    );
6858    editor.update(cx, |editor, cx| {
6859        // Move to another excerpt, ensuring the suggestion gets cleared.
6860        editor.change_selections(None, cx, |s| {
6861            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
6862        });
6863        assert!(!editor.has_active_copilot_suggestion(cx));
6864        assert_eq!(
6865            editor.display_text(cx),
6866            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4\n"
6867        );
6868        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
6869
6870        // Type a character, ensuring we don't even try to interpolate the previous suggestion.
6871        editor.handle_input(" ", cx);
6872        assert!(!editor.has_active_copilot_suggestion(cx));
6873        assert_eq!(
6874            editor.display_text(cx),
6875            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 \n"
6876        );
6877        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
6878    });
6879
6880    // Ensure the new suggestion is displayed when the debounce timeout expires.
6881    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6882    editor.update(cx, |editor, cx| {
6883        assert!(editor.has_active_copilot_suggestion(cx));
6884        assert_eq!(
6885            editor.display_text(cx),
6886            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 + c\n"
6887        );
6888        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
6889    });
6890}
6891
6892#[gpui::test]
6893async fn test_copilot_disabled_globs(
6894    deterministic: Arc<Deterministic>,
6895    cx: &mut gpui::TestAppContext,
6896) {
6897    init_test(cx, |settings| {
6898        settings
6899            .copilot
6900            .get_or_insert(Default::default())
6901            .disabled_globs = Some(vec![".env*".to_string()]);
6902    });
6903
6904    let (copilot, copilot_lsp) = Copilot::fake(cx);
6905    cx.update(|cx| cx.set_global(copilot));
6906
6907    let fs = FakeFs::new(cx.background());
6908    fs.insert_tree(
6909        "/test",
6910        json!({
6911            ".env": "SECRET=something\n",
6912            "README.md": "hello\n"
6913        }),
6914    )
6915    .await;
6916    let project = Project::test(fs, ["/test".as_ref()], cx).await;
6917
6918    let private_buffer = project
6919        .update(cx, |project, cx| {
6920            project.open_local_buffer("/test/.env", cx)
6921        })
6922        .await
6923        .unwrap();
6924    let public_buffer = project
6925        .update(cx, |project, cx| {
6926            project.open_local_buffer("/test/README.md", cx)
6927        })
6928        .await
6929        .unwrap();
6930
6931    let multibuffer = cx.add_model(|cx| {
6932        let mut multibuffer = MultiBuffer::new(0);
6933        multibuffer.push_excerpts(
6934            private_buffer.clone(),
6935            [ExcerptRange {
6936                context: Point::new(0, 0)..Point::new(1, 0),
6937                primary: None,
6938            }],
6939            cx,
6940        );
6941        multibuffer.push_excerpts(
6942            public_buffer.clone(),
6943            [ExcerptRange {
6944                context: Point::new(0, 0)..Point::new(1, 0),
6945                primary: None,
6946            }],
6947            cx,
6948        );
6949        multibuffer
6950    });
6951    let (_, editor) = cx.add_window(|cx| build_editor(multibuffer, cx));
6952
6953    let mut copilot_requests = copilot_lsp
6954        .handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| async move {
6955            Ok(copilot::request::GetCompletionsResult {
6956                completions: vec![copilot::request::Completion {
6957                    text: "next line".into(),
6958                    range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
6959                    ..Default::default()
6960                }],
6961            })
6962        });
6963
6964    editor.update(cx, |editor, cx| {
6965        editor.change_selections(None, cx, |selections| {
6966            selections.select_ranges([Point::new(0, 0)..Point::new(0, 0)])
6967        });
6968        editor.next_copilot_suggestion(&Default::default(), cx);
6969    });
6970
6971    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6972    assert!(copilot_requests.try_next().is_err());
6973
6974    editor.update(cx, |editor, cx| {
6975        editor.change_selections(None, cx, |s| {
6976            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
6977        });
6978        editor.next_copilot_suggestion(&Default::default(), cx);
6979    });
6980
6981    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6982    assert!(copilot_requests.try_next().is_ok());
6983}
6984
6985#[gpui::test]
6986async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
6987    init_test(cx, |_| {});
6988
6989    let mut language = Language::new(
6990        LanguageConfig {
6991            name: "Rust".into(),
6992            path_suffixes: vec!["rs".to_string()],
6993            brackets: BracketPairConfig {
6994                pairs: vec![BracketPair {
6995                    start: "{".to_string(),
6996                    end: "}".to_string(),
6997                    close: true,
6998                    newline: true,
6999                }],
7000                disabled_scopes_by_bracket_ix: Vec::new(),
7001            },
7002            ..Default::default()
7003        },
7004        Some(tree_sitter_rust::language()),
7005    );
7006    let mut fake_servers = language
7007        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
7008            capabilities: lsp::ServerCapabilities {
7009                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
7010                    first_trigger_character: "{".to_string(),
7011                    more_trigger_character: None,
7012                }),
7013                ..Default::default()
7014            },
7015            ..Default::default()
7016        }))
7017        .await;
7018
7019    let fs = FakeFs::new(cx.background());
7020    fs.insert_tree(
7021        "/a",
7022        json!({
7023            "main.rs": "fn main() { let a = 5; }",
7024            "other.rs": "// Test file",
7025        }),
7026    )
7027    .await;
7028    let project = Project::test(fs, ["/a".as_ref()], cx).await;
7029    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
7030    let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
7031    let worktree_id = workspace.update(cx, |workspace, cx| {
7032        workspace.project().read_with(cx, |project, cx| {
7033            project.worktrees(cx).next().unwrap().read(cx).id()
7034        })
7035    });
7036
7037    let buffer = project
7038        .update(cx, |project, cx| {
7039            project.open_local_buffer("/a/main.rs", cx)
7040        })
7041        .await
7042        .unwrap();
7043    cx.foreground().run_until_parked();
7044    cx.foreground().start_waiting();
7045    let fake_server = fake_servers.next().await.unwrap();
7046    let editor_handle = workspace
7047        .update(cx, |workspace, cx| {
7048            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
7049        })
7050        .await
7051        .unwrap()
7052        .downcast::<Editor>()
7053        .unwrap();
7054
7055    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
7056        assert_eq!(
7057            params.text_document_position.text_document.uri,
7058            lsp::Url::from_file_path("/a/main.rs").unwrap(),
7059        );
7060        assert_eq!(
7061            params.text_document_position.position,
7062            lsp::Position::new(0, 21),
7063        );
7064
7065        Ok(Some(vec![lsp::TextEdit {
7066            new_text: "]".to_string(),
7067            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
7068        }]))
7069    });
7070
7071    editor_handle.update(cx, |editor, cx| {
7072        cx.focus(&editor_handle);
7073        editor.change_selections(None, cx, |s| {
7074            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
7075        });
7076        editor.handle_input("{", cx);
7077    });
7078
7079    cx.foreground().run_until_parked();
7080
7081    buffer.read_with(cx, |buffer, _| {
7082        assert_eq!(
7083            buffer.text(),
7084            "fn main() { let a = {5}; }",
7085            "No extra braces from on type formatting should appear in the buffer"
7086        )
7087    });
7088}
7089
7090#[gpui::test]
7091async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
7092    init_test(cx, |_| {});
7093
7094    let language_name: Arc<str> = "Rust".into();
7095    let mut language = Language::new(
7096        LanguageConfig {
7097            name: Arc::clone(&language_name),
7098            path_suffixes: vec!["rs".to_string()],
7099            ..Default::default()
7100        },
7101        Some(tree_sitter_rust::language()),
7102    );
7103
7104    let server_restarts = Arc::new(AtomicUsize::new(0));
7105    let closure_restarts = Arc::clone(&server_restarts);
7106    let language_server_name = "test language server";
7107    let mut fake_servers = language
7108        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
7109            name: language_server_name,
7110            initialization_options: Some(json!({
7111                "testOptionValue": true
7112            })),
7113            initializer: Some(Box::new(move |fake_server| {
7114                let task_restarts = Arc::clone(&closure_restarts);
7115                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
7116                    task_restarts.fetch_add(1, atomic::Ordering::Release);
7117                    futures::future::ready(Ok(()))
7118                });
7119            })),
7120            ..Default::default()
7121        }))
7122        .await;
7123
7124    let fs = FakeFs::new(cx.background());
7125    fs.insert_tree(
7126        "/a",
7127        json!({
7128            "main.rs": "fn main() { let a = 5; }",
7129            "other.rs": "// Test file",
7130        }),
7131    )
7132    .await;
7133    let project = Project::test(fs, ["/a".as_ref()], cx).await;
7134    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
7135    let (_, _workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
7136    let _buffer = project
7137        .update(cx, |project, cx| {
7138            project.open_local_buffer("/a/main.rs", cx)
7139        })
7140        .await
7141        .unwrap();
7142    let _fake_server = fake_servers.next().await.unwrap();
7143    update_test_language_settings(cx, |language_settings| {
7144        language_settings.languages.insert(
7145            Arc::clone(&language_name),
7146            LanguageSettingsContent {
7147                tab_size: NonZeroU32::new(8),
7148                ..Default::default()
7149            },
7150        );
7151    });
7152    cx.foreground().run_until_parked();
7153    assert_eq!(
7154        server_restarts.load(atomic::Ordering::Acquire),
7155        0,
7156        "Should not restart LSP server on an unrelated change"
7157    );
7158
7159    update_test_project_settings(cx, |project_settings| {
7160        project_settings.lsp.insert(
7161            "Some other server name".into(),
7162            LspSettings {
7163                initialization_options: Some(json!({
7164                    "some other init value": false
7165                })),
7166            },
7167        );
7168    });
7169    cx.foreground().run_until_parked();
7170    assert_eq!(
7171        server_restarts.load(atomic::Ordering::Acquire),
7172        0,
7173        "Should not restart LSP server on an unrelated LSP settings change"
7174    );
7175
7176    update_test_project_settings(cx, |project_settings| {
7177        project_settings.lsp.insert(
7178            language_server_name.into(),
7179            LspSettings {
7180                initialization_options: Some(json!({
7181                    "anotherInitValue": false
7182                })),
7183            },
7184        );
7185    });
7186    cx.foreground().run_until_parked();
7187    assert_eq!(
7188        server_restarts.load(atomic::Ordering::Acquire),
7189        1,
7190        "Should restart LSP server on a related LSP settings change"
7191    );
7192
7193    update_test_project_settings(cx, |project_settings| {
7194        project_settings.lsp.insert(
7195            language_server_name.into(),
7196            LspSettings {
7197                initialization_options: Some(json!({
7198                    "anotherInitValue": false
7199                })),
7200            },
7201        );
7202    });
7203    cx.foreground().run_until_parked();
7204    assert_eq!(
7205        server_restarts.load(atomic::Ordering::Acquire),
7206        1,
7207        "Should not restart LSP server on a related LSP settings change that is the same"
7208    );
7209
7210    update_test_project_settings(cx, |project_settings| {
7211        project_settings.lsp.insert(
7212            language_server_name.into(),
7213            LspSettings {
7214                initialization_options: None,
7215            },
7216        );
7217    });
7218    cx.foreground().run_until_parked();
7219    assert_eq!(
7220        server_restarts.load(atomic::Ordering::Acquire),
7221        2,
7222        "Should restart LSP server on another related LSP settings change"
7223    );
7224}
7225
7226#[gpui::test]
7227async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
7228    init_test(cx, |_| {});
7229
7230    let mut cx = EditorLspTestContext::new_rust(
7231        lsp::ServerCapabilities {
7232            completion_provider: Some(lsp::CompletionOptions {
7233                trigger_characters: Some(vec![".".to_string()]),
7234                ..Default::default()
7235            }),
7236            ..Default::default()
7237        },
7238        cx,
7239    )
7240    .await;
7241
7242    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
7243    cx.simulate_keystroke(".");
7244    let completion_item = lsp::CompletionItem {
7245        label: "some".into(),
7246        kind: Some(lsp::CompletionItemKind::SNIPPET),
7247        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
7248        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
7249            kind: lsp::MarkupKind::Markdown,
7250            value: "```rust\nSome(2)\n```".to_string(),
7251        })),
7252        deprecated: Some(false),
7253        sort_text: Some("fffffff2".to_string()),
7254        filter_text: Some("some".to_string()),
7255        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
7256        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
7257            range: lsp::Range {
7258                start: lsp::Position {
7259                    line: 0,
7260                    character: 22,
7261                },
7262                end: lsp::Position {
7263                    line: 0,
7264                    character: 22,
7265                },
7266            },
7267            new_text: "Some(2)".to_string(),
7268        })),
7269        additional_text_edits: Some(vec![lsp::TextEdit {
7270            range: lsp::Range {
7271                start: lsp::Position {
7272                    line: 0,
7273                    character: 20,
7274                },
7275                end: lsp::Position {
7276                    line: 0,
7277                    character: 22,
7278                },
7279            },
7280            new_text: "".to_string(),
7281        }]),
7282        ..Default::default()
7283    };
7284
7285    let closure_completion_item = completion_item.clone();
7286    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
7287        let task_completion_item = closure_completion_item.clone();
7288        async move {
7289            Ok(Some(lsp::CompletionResponse::Array(vec![
7290                task_completion_item,
7291            ])))
7292        }
7293    });
7294
7295    request.next().await;
7296
7297    cx.condition(|editor, _| editor.context_menu_visible())
7298        .await;
7299    let apply_additional_edits = cx.update_editor(|editor, cx| {
7300        editor
7301            .confirm_completion(&ConfirmCompletion::default(), cx)
7302            .unwrap()
7303    });
7304    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
7305
7306    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
7307        let task_completion_item = completion_item.clone();
7308        async move { Ok(task_completion_item) }
7309    })
7310    .next()
7311    .await
7312    .unwrap();
7313    apply_additional_edits.await.unwrap();
7314    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
7315}
7316
7317fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
7318    let point = DisplayPoint::new(row as u32, column as u32);
7319    point..point
7320}
7321
7322fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
7323    let (text, ranges) = marked_text_ranges(marked_text, true);
7324    assert_eq!(view.text(cx), text);
7325    assert_eq!(
7326        view.selections.ranges(cx),
7327        ranges,
7328        "Assert selections are {}",
7329        marked_text
7330    );
7331}
7332
7333/// Handle completion request passing a marked string specifying where the completion
7334/// should be triggered from using '|' character, what range should be replaced, and what completions
7335/// should be returned using '<' and '>' to delimit the range
7336fn handle_completion_request<'a>(
7337    cx: &mut EditorLspTestContext<'a>,
7338    marked_string: &str,
7339    completions: Vec<&'static str>,
7340) -> impl Future<Output = ()> {
7341    let complete_from_marker: TextRangeMarker = '|'.into();
7342    let replace_range_marker: TextRangeMarker = ('<', '>').into();
7343    let (_, mut marked_ranges) = marked_text_ranges_by(
7344        marked_string,
7345        vec![complete_from_marker.clone(), replace_range_marker.clone()],
7346    );
7347
7348    let complete_from_position =
7349        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
7350    let replace_range =
7351        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
7352
7353    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
7354        let completions = completions.clone();
7355        async move {
7356            assert_eq!(params.text_document_position.text_document.uri, url.clone());
7357            assert_eq!(
7358                params.text_document_position.position,
7359                complete_from_position
7360            );
7361            Ok(Some(lsp::CompletionResponse::Array(
7362                completions
7363                    .iter()
7364                    .map(|completion_text| lsp::CompletionItem {
7365                        label: completion_text.to_string(),
7366                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
7367                            range: replace_range,
7368                            new_text: completion_text.to_string(),
7369                        })),
7370                        ..Default::default()
7371                    })
7372                    .collect(),
7373            )))
7374        }
7375    });
7376
7377    async move {
7378        request.next().await;
7379    }
7380}
7381
7382fn handle_resolve_completion_request<'a>(
7383    cx: &mut EditorLspTestContext<'a>,
7384    edits: Option<Vec<(&'static str, &'static str)>>,
7385) -> impl Future<Output = ()> {
7386    let edits = edits.map(|edits| {
7387        edits
7388            .iter()
7389            .map(|(marked_string, new_text)| {
7390                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
7391                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
7392                lsp::TextEdit::new(replace_range, new_text.to_string())
7393            })
7394            .collect::<Vec<_>>()
7395    });
7396
7397    let mut request =
7398        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
7399            let edits = edits.clone();
7400            async move {
7401                Ok(lsp::CompletionItem {
7402                    additional_text_edits: edits,
7403                    ..Default::default()
7404                })
7405            }
7406        });
7407
7408    async move {
7409        request.next().await;
7410    }
7411}
7412
7413fn handle_copilot_completion_request(
7414    lsp: &lsp::FakeLanguageServer,
7415    completions: Vec<copilot::request::Completion>,
7416    completions_cycling: Vec<copilot::request::Completion>,
7417) {
7418    lsp.handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| {
7419        let completions = completions.clone();
7420        async move {
7421            Ok(copilot::request::GetCompletionsResult {
7422                completions: completions.clone(),
7423            })
7424        }
7425    });
7426    lsp.handle_request::<copilot::request::GetCompletionsCycling, _, _>(move |_params, _cx| {
7427        let completions_cycling = completions_cycling.clone();
7428        async move {
7429            Ok(copilot::request::GetCompletionsResult {
7430                completions: completions_cycling.clone(),
7431            })
7432        }
7433    });
7434}
7435
7436pub(crate) fn update_test_language_settings(
7437    cx: &mut TestAppContext,
7438    f: impl Fn(&mut AllLanguageSettingsContent),
7439) {
7440    cx.update(|cx| {
7441        cx.update_global::<SettingsStore, _, _>(|store, cx| {
7442            store.update_user_settings::<AllLanguageSettings>(cx, f);
7443        });
7444    });
7445}
7446
7447pub(crate) fn update_test_project_settings(
7448    cx: &mut TestAppContext,
7449    f: impl Fn(&mut ProjectSettings),
7450) {
7451    cx.update(|cx| {
7452        cx.update_global::<SettingsStore, _, _>(|store, cx| {
7453            store.update_user_settings::<ProjectSettings>(cx, f);
7454        });
7455    });
7456}
7457
7458pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
7459    cx.foreground().forbid_parking();
7460
7461    cx.update(|cx| {
7462        cx.set_global(SettingsStore::test(cx));
7463        theme::init((), cx);
7464        client::init_settings(cx);
7465        language::init(cx);
7466        Project::init_settings(cx);
7467        workspace::init_settings(cx);
7468        crate::init(cx);
7469    });
7470
7471    update_test_language_settings(cx, f);
7472}