editor_tests.rs

   1use super::*;
   2use crate::test::{
   3    assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
   4    editor_test_context::EditorTestContext, select_ranges,
   5};
   6use drag_and_drop::DragAndDrop;
   7use futures::StreamExt;
   8use gpui::{
   9    executor::Deterministic,
  10    geometry::{rect::RectF, vector::vec2f},
  11    platform::{WindowBounds, WindowOptions},
  12    serde_json,
  13};
  14use indoc::indoc;
  15use language::{BracketPairConfig, FakeLspAdapter, LanguageConfig, LanguageRegistry, Point};
  16use parking_lot::Mutex;
  17use project::FakeFs;
  18use settings::EditorSettings;
  19use std::{cell::RefCell, rc::Rc, time::Instant};
  20use unindent::Unindent;
  21use util::{
  22    assert_set_eq,
  23    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
  24};
  25use workspace::{
  26    item::{FollowableItem, ItemHandle},
  27    NavigationEntry, Pane, ViewId,
  28};
  29
  30#[gpui::test]
  31fn test_edit_events(cx: &mut MutableAppContext) {
  32    cx.set_global(Settings::test(cx));
  33    let buffer = cx.add_model(|cx| {
  34        let mut buffer = language::Buffer::new(0, "123456", cx);
  35        buffer.set_group_interval(Duration::from_secs(1));
  36        buffer
  37    });
  38
  39    let events = Rc::new(RefCell::new(Vec::new()));
  40    let (_, editor1) = cx.add_window(Default::default(), {
  41        let events = events.clone();
  42        |cx| {
  43            cx.subscribe(&cx.handle(), move |_, _, event, _| {
  44                if matches!(
  45                    event,
  46                    Event::Edited | Event::BufferEdited | Event::DirtyChanged
  47                ) {
  48                    events.borrow_mut().push(("editor1", event.clone()));
  49                }
  50            })
  51            .detach();
  52            Editor::for_buffer(buffer.clone(), None, cx)
  53        }
  54    });
  55    let (_, editor2) = cx.add_window(Default::default(), {
  56        let events = events.clone();
  57        |cx| {
  58            cx.subscribe(&cx.handle(), move |_, _, event, _| {
  59                if matches!(
  60                    event,
  61                    Event::Edited | Event::BufferEdited | Event::DirtyChanged
  62                ) {
  63                    events.borrow_mut().push(("editor2", event.clone()));
  64                }
  65            })
  66            .detach();
  67            Editor::for_buffer(buffer.clone(), None, cx)
  68        }
  69    });
  70    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  71
  72    // Mutating editor 1 will emit an `Edited` event only for that editor.
  73    editor1.update(cx, |editor, cx| editor.insert("X", cx));
  74    assert_eq!(
  75        mem::take(&mut *events.borrow_mut()),
  76        [
  77            ("editor1", Event::Edited),
  78            ("editor1", Event::BufferEdited),
  79            ("editor2", Event::BufferEdited),
  80            ("editor1", Event::DirtyChanged),
  81            ("editor2", Event::DirtyChanged)
  82        ]
  83    );
  84
  85    // Mutating editor 2 will emit an `Edited` event only for that editor.
  86    editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
  87    assert_eq!(
  88        mem::take(&mut *events.borrow_mut()),
  89        [
  90            ("editor2", Event::Edited),
  91            ("editor1", Event::BufferEdited),
  92            ("editor2", Event::BufferEdited),
  93        ]
  94    );
  95
  96    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  97    editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
  98    assert_eq!(
  99        mem::take(&mut *events.borrow_mut()),
 100        [
 101            ("editor1", Event::Edited),
 102            ("editor1", Event::BufferEdited),
 103            ("editor2", Event::BufferEdited),
 104            ("editor1", Event::DirtyChanged),
 105            ("editor2", Event::DirtyChanged),
 106        ]
 107    );
 108
 109    // Redoing on editor 1 will emit an `Edited` event only for that editor.
 110    editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
 111    assert_eq!(
 112        mem::take(&mut *events.borrow_mut()),
 113        [
 114            ("editor1", Event::Edited),
 115            ("editor1", Event::BufferEdited),
 116            ("editor2", Event::BufferEdited),
 117            ("editor1", Event::DirtyChanged),
 118            ("editor2", Event::DirtyChanged),
 119        ]
 120    );
 121
 122    // Undoing on editor 2 will emit an `Edited` event only for that editor.
 123    editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
 124    assert_eq!(
 125        mem::take(&mut *events.borrow_mut()),
 126        [
 127            ("editor2", Event::Edited),
 128            ("editor1", Event::BufferEdited),
 129            ("editor2", Event::BufferEdited),
 130            ("editor1", Event::DirtyChanged),
 131            ("editor2", Event::DirtyChanged),
 132        ]
 133    );
 134
 135    // Redoing on editor 2 will emit an `Edited` event only for that editor.
 136    editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
 137    assert_eq!(
 138        mem::take(&mut *events.borrow_mut()),
 139        [
 140            ("editor2", Event::Edited),
 141            ("editor1", Event::BufferEdited),
 142            ("editor2", Event::BufferEdited),
 143            ("editor1", Event::DirtyChanged),
 144            ("editor2", Event::DirtyChanged),
 145        ]
 146    );
 147
 148    // No event is emitted when the mutation is a no-op.
 149    editor2.update(cx, |editor, cx| {
 150        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 151
 152        editor.backspace(&Backspace, cx);
 153    });
 154    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
 155}
 156
 157#[gpui::test]
 158fn test_undo_redo_with_selection_restoration(cx: &mut MutableAppContext) {
 159    cx.set_global(Settings::test(cx));
 160    let mut now = Instant::now();
 161    let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
 162    let group_interval = buffer.read(cx).transaction_group_interval();
 163    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
 164    let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
 165
 166    editor.update(cx, |editor, cx| {
 167        editor.start_transaction_at(now, cx);
 168        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
 169
 170        editor.insert("cd", cx);
 171        editor.end_transaction_at(now, cx);
 172        assert_eq!(editor.text(cx), "12cd56");
 173        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
 174
 175        editor.start_transaction_at(now, cx);
 176        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
 177        editor.insert("e", cx);
 178        editor.end_transaction_at(now, cx);
 179        assert_eq!(editor.text(cx), "12cde6");
 180        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
 181
 182        now += group_interval + Duration::from_millis(1);
 183        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
 184
 185        // Simulate an edit in another editor
 186        buffer.update(cx, |buffer, cx| {
 187            buffer.start_transaction_at(now, cx);
 188            buffer.edit([(0..1, "a")], None, cx);
 189            buffer.edit([(1..1, "b")], None, cx);
 190            buffer.end_transaction_at(now, cx);
 191        });
 192
 193        assert_eq!(editor.text(cx), "ab2cde6");
 194        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
 195
 196        // Last transaction happened past the group interval in a different editor.
 197        // Undo it individually and don't restore selections.
 198        editor.undo(&Undo, cx);
 199        assert_eq!(editor.text(cx), "12cde6");
 200        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
 201
 202        // First two transactions happened within the group interval in this editor.
 203        // Undo them together and restore selections.
 204        editor.undo(&Undo, cx);
 205        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
 206        assert_eq!(editor.text(cx), "123456");
 207        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
 208
 209        // Redo the first two transactions together.
 210        editor.redo(&Redo, cx);
 211        assert_eq!(editor.text(cx), "12cde6");
 212        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
 213
 214        // Redo the last transaction on its own.
 215        editor.redo(&Redo, cx);
 216        assert_eq!(editor.text(cx), "ab2cde6");
 217        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
 218
 219        // Test empty transactions.
 220        editor.start_transaction_at(now, cx);
 221        editor.end_transaction_at(now, cx);
 222        editor.undo(&Undo, cx);
 223        assert_eq!(editor.text(cx), "12cde6");
 224    });
 225}
 226
 227#[gpui::test]
 228fn test_ime_composition(cx: &mut MutableAppContext) {
 229    cx.set_global(Settings::test(cx));
 230    let buffer = cx.add_model(|cx| {
 231        let mut buffer = language::Buffer::new(0, "abcde", cx);
 232        // Ensure automatic grouping doesn't occur.
 233        buffer.set_group_interval(Duration::ZERO);
 234        buffer
 235    });
 236
 237    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
 238    cx.add_window(Default::default(), |cx| {
 239        let mut editor = build_editor(buffer.clone(), cx);
 240
 241        // Start a new IME composition.
 242        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
 243        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
 244        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
 245        assert_eq!(editor.text(cx), "äbcde");
 246        assert_eq!(
 247            editor.marked_text_ranges(cx),
 248            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
 249        );
 250
 251        // Finalize IME composition.
 252        editor.replace_text_in_range(None, "ā", cx);
 253        assert_eq!(editor.text(cx), "ābcde");
 254        assert_eq!(editor.marked_text_ranges(cx), None);
 255
 256        // IME composition edits are grouped and are undone/redone at once.
 257        editor.undo(&Default::default(), cx);
 258        assert_eq!(editor.text(cx), "abcde");
 259        assert_eq!(editor.marked_text_ranges(cx), None);
 260        editor.redo(&Default::default(), cx);
 261        assert_eq!(editor.text(cx), "ābcde");
 262        assert_eq!(editor.marked_text_ranges(cx), None);
 263
 264        // Start a new IME composition.
 265        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
 266        assert_eq!(
 267            editor.marked_text_ranges(cx),
 268            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
 269        );
 270
 271        // Undoing during an IME composition cancels it.
 272        editor.undo(&Default::default(), cx);
 273        assert_eq!(editor.text(cx), "ābcde");
 274        assert_eq!(editor.marked_text_ranges(cx), None);
 275
 276        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
 277        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
 278        assert_eq!(editor.text(cx), "ābcdè");
 279        assert_eq!(
 280            editor.marked_text_ranges(cx),
 281            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
 282        );
 283
 284        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
 285        editor.replace_text_in_range(Some(4..999), "ę", cx);
 286        assert_eq!(editor.text(cx), "ābcdę");
 287        assert_eq!(editor.marked_text_ranges(cx), None);
 288
 289        // Start a new IME composition with multiple cursors.
 290        editor.change_selections(None, cx, |s| {
 291            s.select_ranges([
 292                OffsetUtf16(1)..OffsetUtf16(1),
 293                OffsetUtf16(3)..OffsetUtf16(3),
 294                OffsetUtf16(5)..OffsetUtf16(5),
 295            ])
 296        });
 297        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
 298        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
 299        assert_eq!(
 300            editor.marked_text_ranges(cx),
 301            Some(vec![
 302                OffsetUtf16(0)..OffsetUtf16(3),
 303                OffsetUtf16(4)..OffsetUtf16(7),
 304                OffsetUtf16(8)..OffsetUtf16(11)
 305            ])
 306        );
 307
 308        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
 309        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
 310        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
 311        assert_eq!(
 312            editor.marked_text_ranges(cx),
 313            Some(vec![
 314                OffsetUtf16(1)..OffsetUtf16(2),
 315                OffsetUtf16(5)..OffsetUtf16(6),
 316                OffsetUtf16(9)..OffsetUtf16(10)
 317            ])
 318        );
 319
 320        // Finalize IME composition with multiple cursors.
 321        editor.replace_text_in_range(Some(9..10), "2", cx);
 322        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
 323        assert_eq!(editor.marked_text_ranges(cx), None);
 324
 325        editor
 326    });
 327}
 328
 329#[gpui::test]
 330fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) {
 331    cx.set_global(Settings::test(cx));
 332
 333    let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
 334    let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
 335    editor.update(cx, |view, cx| {
 336        view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
 337    });
 338    assert_eq!(
 339        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 340        [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
 341    );
 342
 343    editor.update(cx, |view, cx| {
 344        view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
 345    });
 346
 347    assert_eq!(
 348        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 349        [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
 350    );
 351
 352    editor.update(cx, |view, cx| {
 353        view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
 354    });
 355
 356    assert_eq!(
 357        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 358        [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
 359    );
 360
 361    editor.update(cx, |view, cx| {
 362        view.end_selection(cx);
 363        view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
 364    });
 365
 366    assert_eq!(
 367        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 368        [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
 369    );
 370
 371    editor.update(cx, |view, cx| {
 372        view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
 373        view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx);
 374    });
 375
 376    assert_eq!(
 377        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 378        [
 379            DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
 380            DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
 381        ]
 382    );
 383
 384    editor.update(cx, |view, cx| {
 385        view.end_selection(cx);
 386    });
 387
 388    assert_eq!(
 389        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 390        [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
 391    );
 392}
 393
 394#[gpui::test]
 395fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) {
 396    cx.set_global(Settings::test(cx));
 397    let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
 398    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
 399
 400    view.update(cx, |view, cx| {
 401        view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
 402        assert_eq!(
 403            view.selections.display_ranges(cx),
 404            [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
 405        );
 406    });
 407
 408    view.update(cx, |view, cx| {
 409        view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
 410        assert_eq!(
 411            view.selections.display_ranges(cx),
 412            [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
 413        );
 414    });
 415
 416    view.update(cx, |view, cx| {
 417        view.cancel(&Cancel, cx);
 418        view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
 419        assert_eq!(
 420            view.selections.display_ranges(cx),
 421            [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
 422        );
 423    });
 424}
 425
 426#[gpui::test]
 427fn test_clone(cx: &mut gpui::MutableAppContext) {
 428    let (text, selection_ranges) = marked_text_ranges(
 429        indoc! {"
 430            one
 431            two
 432            threeˇ
 433            four
 434            fiveˇ
 435        "},
 436        true,
 437    );
 438    cx.set_global(Settings::test(cx));
 439    let buffer = MultiBuffer::build_simple(&text, cx);
 440
 441    let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
 442
 443    editor.update(cx, |editor, cx| {
 444        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
 445        editor.fold_ranges(
 446            [
 447                Point::new(1, 0)..Point::new(2, 0),
 448                Point::new(3, 0)..Point::new(4, 0),
 449            ],
 450            true,
 451            cx,
 452        );
 453    });
 454
 455    let (_, cloned_editor) = editor.update(cx, |editor, cx| {
 456        cx.add_window(Default::default(), |cx| editor.clone(cx))
 457    });
 458
 459    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx));
 460    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx));
 461
 462    assert_eq!(
 463        cloned_editor.update(cx, |e, cx| e.display_text(cx)),
 464        editor.update(cx, |e, cx| e.display_text(cx))
 465    );
 466    assert_eq!(
 467        cloned_snapshot
 468            .folds_in_range(0..text.len())
 469            .collect::<Vec<_>>(),
 470        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
 471    );
 472    assert_set_eq!(
 473        cloned_editor.read(cx).selections.ranges::<Point>(cx),
 474        editor.read(cx).selections.ranges(cx)
 475    );
 476    assert_set_eq!(
 477        cloned_editor.update(cx, |e, cx| e.selections.display_ranges(cx)),
 478        editor.update(cx, |e, cx| e.selections.display_ranges(cx))
 479    );
 480}
 481
 482#[gpui::test]
 483fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
 484    cx.set_global(Settings::test(cx));
 485    cx.set_global(DragAndDrop::<Workspace>::default());
 486    use workspace::item::Item;
 487    let (_, pane) = cx.add_window(Default::default(), |cx| {
 488        Pane::new(0, None, || unimplemented!(), cx)
 489    });
 490    let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
 491
 492    cx.add_view(&pane, |cx| {
 493        let mut editor = build_editor(buffer.clone(), cx);
 494        let handle = cx.handle();
 495        editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
 496
 497        fn pop_history(editor: &mut Editor, cx: &mut MutableAppContext) -> Option<NavigationEntry> {
 498            editor.nav_history.as_mut().unwrap().pop_backward(cx)
 499        }
 500
 501        // Move the cursor a small distance.
 502        // Nothing is added to the navigation history.
 503        editor.change_selections(None, cx, |s| {
 504            s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
 505        });
 506        editor.change_selections(None, cx, |s| {
 507            s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
 508        });
 509        assert!(pop_history(&mut editor, cx).is_none());
 510
 511        // Move the cursor a large distance.
 512        // The history can jump back to the previous position.
 513        editor.change_selections(None, cx, |s| {
 514            s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
 515        });
 516        let nav_entry = pop_history(&mut editor, cx).unwrap();
 517        editor.navigate(nav_entry.data.unwrap(), cx);
 518        assert_eq!(nav_entry.item.id(), cx.view_id());
 519        assert_eq!(
 520            editor.selections.display_ranges(cx),
 521            &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
 522        );
 523        assert!(pop_history(&mut editor, cx).is_none());
 524
 525        // Move the cursor a small distance via the mouse.
 526        // Nothing is added to the navigation history.
 527        editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
 528        editor.end_selection(cx);
 529        assert_eq!(
 530            editor.selections.display_ranges(cx),
 531            &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
 532        );
 533        assert!(pop_history(&mut editor, cx).is_none());
 534
 535        // Move the cursor a large distance via the mouse.
 536        // The history can jump back to the previous position.
 537        editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
 538        editor.end_selection(cx);
 539        assert_eq!(
 540            editor.selections.display_ranges(cx),
 541            &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
 542        );
 543        let nav_entry = pop_history(&mut editor, cx).unwrap();
 544        editor.navigate(nav_entry.data.unwrap(), cx);
 545        assert_eq!(nav_entry.item.id(), cx.view_id());
 546        assert_eq!(
 547            editor.selections.display_ranges(cx),
 548            &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
 549        );
 550        assert!(pop_history(&mut editor, cx).is_none());
 551
 552        // Set scroll position to check later
 553        editor.set_scroll_position(Vector2F::new(5.5, 5.5), cx);
 554        let original_scroll_position = editor.scroll_manager.anchor();
 555
 556        // Jump to the end of the document and adjust scroll
 557        editor.move_to_end(&MoveToEnd, cx);
 558        editor.set_scroll_position(Vector2F::new(-2.5, -0.5), cx);
 559        assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
 560
 561        let nav_entry = pop_history(&mut editor, cx).unwrap();
 562        editor.navigate(nav_entry.data.unwrap(), cx);
 563        assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
 564
 565        // Ensure we don't panic when navigation data contains invalid anchors *and* points.
 566        let mut invalid_anchor = editor.scroll_manager.anchor().top_anchor;
 567        invalid_anchor.text_anchor.buffer_id = Some(999);
 568        let invalid_point = Point::new(9999, 0);
 569        editor.navigate(
 570            Box::new(NavigationData {
 571                cursor_anchor: invalid_anchor,
 572                cursor_position: invalid_point,
 573                scroll_anchor: ScrollAnchor {
 574                    top_anchor: invalid_anchor,
 575                    offset: Default::default(),
 576                },
 577                scroll_top_row: invalid_point.row,
 578            }),
 579            cx,
 580        );
 581        assert_eq!(
 582            editor.selections.display_ranges(cx),
 583            &[editor.max_point(cx)..editor.max_point(cx)]
 584        );
 585        assert_eq!(
 586            editor.scroll_position(cx),
 587            vec2f(0., editor.max_point(cx).row() as f32)
 588        );
 589
 590        editor
 591    });
 592}
 593
 594#[gpui::test]
 595fn test_cancel(cx: &mut gpui::MutableAppContext) {
 596    cx.set_global(Settings::test(cx));
 597    let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
 598    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
 599
 600    view.update(cx, |view, cx| {
 601        view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
 602        view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
 603        view.end_selection(cx);
 604
 605        view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
 606        view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx);
 607        view.end_selection(cx);
 608        assert_eq!(
 609            view.selections.display_ranges(cx),
 610            [
 611                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
 612                DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
 613            ]
 614        );
 615    });
 616
 617    view.update(cx, |view, cx| {
 618        view.cancel(&Cancel, cx);
 619        assert_eq!(
 620            view.selections.display_ranges(cx),
 621            [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
 622        );
 623    });
 624
 625    view.update(cx, |view, cx| {
 626        view.cancel(&Cancel, cx);
 627        assert_eq!(
 628            view.selections.display_ranges(cx),
 629            [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
 630        );
 631    });
 632}
 633
 634#[gpui::test]
 635fn test_fold(cx: &mut gpui::MutableAppContext) {
 636    cx.set_global(Settings::test(cx));
 637    let buffer = MultiBuffer::build_simple(
 638        &"
 639            impl Foo {
 640                // Hello!
 641
 642                fn a() {
 643                    1
 644                }
 645
 646                fn b() {
 647                    2
 648                }
 649
 650                fn c() {
 651                    3
 652                }
 653            }
 654        "
 655        .unindent(),
 656        cx,
 657    );
 658    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
 659
 660    view.update(cx, |view, cx| {
 661        view.change_selections(None, cx, |s| {
 662            s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]);
 663        });
 664        view.fold(&Fold, cx);
 665        assert_eq!(
 666            view.display_text(cx),
 667            "
 668                impl Foo {
 669                    // Hello!
 670
 671                    fn a() {
 672                        1
 673                    }
 674
 675                    fn b() {⋯
 676                    }
 677
 678                    fn c() {⋯
 679                    }
 680                }
 681            "
 682            .unindent(),
 683        );
 684
 685        view.fold(&Fold, cx);
 686        assert_eq!(
 687            view.display_text(cx),
 688            "
 689                impl Foo {⋯
 690                }
 691            "
 692            .unindent(),
 693        );
 694
 695        view.unfold_lines(&UnfoldLines, cx);
 696        assert_eq!(
 697            view.display_text(cx),
 698            "
 699                impl Foo {
 700                    // Hello!
 701
 702                    fn a() {
 703                        1
 704                    }
 705
 706                    fn b() {⋯
 707                    }
 708
 709                    fn c() {⋯
 710                    }
 711                }
 712            "
 713            .unindent(),
 714        );
 715
 716        view.unfold_lines(&UnfoldLines, cx);
 717        assert_eq!(view.display_text(cx), buffer.read(cx).read(cx).text());
 718    });
 719}
 720
 721#[gpui::test]
 722fn test_move_cursor(cx: &mut gpui::MutableAppContext) {
 723    cx.set_global(Settings::test(cx));
 724    let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
 725    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
 726
 727    buffer.update(cx, |buffer, cx| {
 728        buffer.edit(
 729            vec![
 730                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 731                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 732            ],
 733            None,
 734            cx,
 735        );
 736    });
 737
 738    view.update(cx, |view, cx| {
 739        assert_eq!(
 740            view.selections.display_ranges(cx),
 741            &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
 742        );
 743
 744        view.move_down(&MoveDown, cx);
 745        assert_eq!(
 746            view.selections.display_ranges(cx),
 747            &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
 748        );
 749
 750        view.move_right(&MoveRight, cx);
 751        assert_eq!(
 752            view.selections.display_ranges(cx),
 753            &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
 754        );
 755
 756        view.move_left(&MoveLeft, cx);
 757        assert_eq!(
 758            view.selections.display_ranges(cx),
 759            &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
 760        );
 761
 762        view.move_up(&MoveUp, cx);
 763        assert_eq!(
 764            view.selections.display_ranges(cx),
 765            &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
 766        );
 767
 768        view.move_to_end(&MoveToEnd, cx);
 769        assert_eq!(
 770            view.selections.display_ranges(cx),
 771            &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
 772        );
 773
 774        view.move_to_beginning(&MoveToBeginning, cx);
 775        assert_eq!(
 776            view.selections.display_ranges(cx),
 777            &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
 778        );
 779
 780        view.change_selections(None, cx, |s| {
 781            s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]);
 782        });
 783        view.select_to_beginning(&SelectToBeginning, cx);
 784        assert_eq!(
 785            view.selections.display_ranges(cx),
 786            &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
 787        );
 788
 789        view.select_to_end(&SelectToEnd, cx);
 790        assert_eq!(
 791            view.selections.display_ranges(cx),
 792            &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
 793        );
 794    });
 795}
 796
 797#[gpui::test]
 798fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) {
 799    cx.set_global(Settings::test(cx));
 800    let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
 801    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
 802
 803    assert_eq!('ⓐ'.len_utf8(), 3);
 804    assert_eq!('α'.len_utf8(), 2);
 805
 806    view.update(cx, |view, cx| {
 807        view.fold_ranges(
 808            vec![
 809                Point::new(0, 6)..Point::new(0, 12),
 810                Point::new(1, 2)..Point::new(1, 4),
 811                Point::new(2, 4)..Point::new(2, 8),
 812            ],
 813            true,
 814            cx,
 815        );
 816        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε\n");
 817
 818        view.move_right(&MoveRight, cx);
 819        assert_eq!(
 820            view.selections.display_ranges(cx),
 821            &[empty_range(0, "".len())]
 822        );
 823        view.move_right(&MoveRight, cx);
 824        assert_eq!(
 825            view.selections.display_ranges(cx),
 826            &[empty_range(0, "ⓐⓑ".len())]
 827        );
 828        view.move_right(&MoveRight, cx);
 829        assert_eq!(
 830            view.selections.display_ranges(cx),
 831            &[empty_range(0, "ⓐⓑ⋯".len())]
 832        );
 833
 834        view.move_down(&MoveDown, cx);
 835        assert_eq!(
 836            view.selections.display_ranges(cx),
 837            &[empty_range(1, "ab⋯".len())]
 838        );
 839        view.move_left(&MoveLeft, cx);
 840        assert_eq!(
 841            view.selections.display_ranges(cx),
 842            &[empty_range(1, "ab".len())]
 843        );
 844        view.move_left(&MoveLeft, cx);
 845        assert_eq!(
 846            view.selections.display_ranges(cx),
 847            &[empty_range(1, "a".len())]
 848        );
 849
 850        view.move_down(&MoveDown, cx);
 851        assert_eq!(
 852            view.selections.display_ranges(cx),
 853            &[empty_range(2, "α".len())]
 854        );
 855        view.move_right(&MoveRight, cx);
 856        assert_eq!(
 857            view.selections.display_ranges(cx),
 858            &[empty_range(2, "αβ".len())]
 859        );
 860        view.move_right(&MoveRight, cx);
 861        assert_eq!(
 862            view.selections.display_ranges(cx),
 863            &[empty_range(2, "αβ⋯".len())]
 864        );
 865        view.move_right(&MoveRight, cx);
 866        assert_eq!(
 867            view.selections.display_ranges(cx),
 868            &[empty_range(2, "αβ⋯ε".len())]
 869        );
 870
 871        view.move_up(&MoveUp, cx);
 872        assert_eq!(
 873            view.selections.display_ranges(cx),
 874            &[empty_range(1, "ab⋯e".len())]
 875        );
 876        view.move_up(&MoveUp, cx);
 877        assert_eq!(
 878            view.selections.display_ranges(cx),
 879            &[empty_range(0, "ⓐⓑ⋯ⓔ".len())]
 880        );
 881        view.move_left(&MoveLeft, cx);
 882        assert_eq!(
 883            view.selections.display_ranges(cx),
 884            &[empty_range(0, "ⓐⓑ⋯".len())]
 885        );
 886        view.move_left(&MoveLeft, cx);
 887        assert_eq!(
 888            view.selections.display_ranges(cx),
 889            &[empty_range(0, "ⓐⓑ".len())]
 890        );
 891        view.move_left(&MoveLeft, cx);
 892        assert_eq!(
 893            view.selections.display_ranges(cx),
 894            &[empty_range(0, "".len())]
 895        );
 896    });
 897}
 898
 899#[gpui::test]
 900fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) {
 901    cx.set_global(Settings::test(cx));
 902    let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 903    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
 904    view.update(cx, |view, cx| {
 905        view.change_selections(None, cx, |s| {
 906            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 907        });
 908        view.move_down(&MoveDown, cx);
 909        assert_eq!(
 910            view.selections.display_ranges(cx),
 911            &[empty_range(1, "abcd".len())]
 912        );
 913
 914        view.move_down(&MoveDown, cx);
 915        assert_eq!(
 916            view.selections.display_ranges(cx),
 917            &[empty_range(2, "αβγ".len())]
 918        );
 919
 920        view.move_down(&MoveDown, cx);
 921        assert_eq!(
 922            view.selections.display_ranges(cx),
 923            &[empty_range(3, "abcd".len())]
 924        );
 925
 926        view.move_down(&MoveDown, cx);
 927        assert_eq!(
 928            view.selections.display_ranges(cx),
 929            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 930        );
 931
 932        view.move_up(&MoveUp, cx);
 933        assert_eq!(
 934            view.selections.display_ranges(cx),
 935            &[empty_range(3, "abcd".len())]
 936        );
 937
 938        view.move_up(&MoveUp, cx);
 939        assert_eq!(
 940            view.selections.display_ranges(cx),
 941            &[empty_range(2, "αβγ".len())]
 942        );
 943    });
 944}
 945
 946#[gpui::test]
 947fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) {
 948    cx.set_global(Settings::test(cx));
 949    let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 950    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
 951    view.update(cx, |view, cx| {
 952        view.change_selections(None, cx, |s| {
 953            s.select_display_ranges([
 954                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
 955                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
 956            ]);
 957        });
 958    });
 959
 960    view.update(cx, |view, cx| {
 961        view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
 962        assert_eq!(
 963            view.selections.display_ranges(cx),
 964            &[
 965                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
 966                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
 967            ]
 968        );
 969    });
 970
 971    view.update(cx, |view, cx| {
 972        view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
 973        assert_eq!(
 974            view.selections.display_ranges(cx),
 975            &[
 976                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
 977                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
 978            ]
 979        );
 980    });
 981
 982    view.update(cx, |view, cx| {
 983        view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
 984        assert_eq!(
 985            view.selections.display_ranges(cx),
 986            &[
 987                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
 988                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
 989            ]
 990        );
 991    });
 992
 993    view.update(cx, |view, cx| {
 994        view.move_to_end_of_line(&MoveToEndOfLine, cx);
 995        assert_eq!(
 996            view.selections.display_ranges(cx),
 997            &[
 998                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
 999                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
1000            ]
1001        );
1002    });
1003
1004    // Moving to the end of line again is a no-op.
1005    view.update(cx, |view, cx| {
1006        view.move_to_end_of_line(&MoveToEndOfLine, cx);
1007        assert_eq!(
1008            view.selections.display_ranges(cx),
1009            &[
1010                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
1011                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
1012            ]
1013        );
1014    });
1015
1016    view.update(cx, |view, cx| {
1017        view.move_left(&MoveLeft, cx);
1018        view.select_to_beginning_of_line(
1019            &SelectToBeginningOfLine {
1020                stop_at_soft_wraps: true,
1021            },
1022            cx,
1023        );
1024        assert_eq!(
1025            view.selections.display_ranges(cx),
1026            &[
1027                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
1028                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
1029            ]
1030        );
1031    });
1032
1033    view.update(cx, |view, cx| {
1034        view.select_to_beginning_of_line(
1035            &SelectToBeginningOfLine {
1036                stop_at_soft_wraps: true,
1037            },
1038            cx,
1039        );
1040        assert_eq!(
1041            view.selections.display_ranges(cx),
1042            &[
1043                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
1044                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
1045            ]
1046        );
1047    });
1048
1049    view.update(cx, |view, cx| {
1050        view.select_to_beginning_of_line(
1051            &SelectToBeginningOfLine {
1052                stop_at_soft_wraps: true,
1053            },
1054            cx,
1055        );
1056        assert_eq!(
1057            view.selections.display_ranges(cx),
1058            &[
1059                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
1060                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
1061            ]
1062        );
1063    });
1064
1065    view.update(cx, |view, cx| {
1066        view.select_to_end_of_line(
1067            &SelectToEndOfLine {
1068                stop_at_soft_wraps: true,
1069            },
1070            cx,
1071        );
1072        assert_eq!(
1073            view.selections.display_ranges(cx),
1074            &[
1075                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
1076                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
1077            ]
1078        );
1079    });
1080
1081    view.update(cx, |view, cx| {
1082        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
1083        assert_eq!(view.display_text(cx), "ab\n  de");
1084        assert_eq!(
1085            view.selections.display_ranges(cx),
1086            &[
1087                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
1088                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
1089            ]
1090        );
1091    });
1092
1093    view.update(cx, |view, cx| {
1094        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
1095        assert_eq!(view.display_text(cx), "\n");
1096        assert_eq!(
1097            view.selections.display_ranges(cx),
1098            &[
1099                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
1100                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
1101            ]
1102        );
1103    });
1104}
1105
1106#[gpui::test]
1107fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) {
1108    cx.set_global(Settings::test(cx));
1109    let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
1110    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
1111    view.update(cx, |view, cx| {
1112        view.change_selections(None, cx, |s| {
1113            s.select_display_ranges([
1114                DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
1115                DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
1116            ])
1117        });
1118
1119        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1120        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
1121
1122        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1123        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
1124
1125        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1126        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
1127
1128        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1129        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
1130
1131        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1132        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
1133
1134        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1135        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
1136
1137        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1138        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
1139
1140        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1141        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
1142
1143        view.move_right(&MoveRight, cx);
1144        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
1145        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
1146
1147        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
1148        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
1149
1150        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
1151        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
1152    });
1153}
1154
1155#[gpui::test]
1156fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
1157    cx.set_global(Settings::test(cx));
1158    let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
1159    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
1160
1161    view.update(cx, |view, cx| {
1162        view.set_wrap_width(Some(140.), cx);
1163        assert_eq!(
1164            view.display_text(cx),
1165            "use one::{\n    two::three::\n    four::five\n};"
1166        );
1167
1168        view.change_selections(None, cx, |s| {
1169            s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]);
1170        });
1171
1172        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1173        assert_eq!(
1174            view.selections.display_ranges(cx),
1175            &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
1176        );
1177
1178        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1179        assert_eq!(
1180            view.selections.display_ranges(cx),
1181            &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
1182        );
1183
1184        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1185        assert_eq!(
1186            view.selections.display_ranges(cx),
1187            &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
1188        );
1189
1190        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1191        assert_eq!(
1192            view.selections.display_ranges(cx),
1193            &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
1194        );
1195
1196        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1197        assert_eq!(
1198            view.selections.display_ranges(cx),
1199            &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
1200        );
1201
1202        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1203        assert_eq!(
1204            view.selections.display_ranges(cx),
1205            &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
1206        );
1207    });
1208}
1209
1210#[gpui::test]
1211async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
1212    let mut cx = EditorTestContext::new(cx);
1213
1214    let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
1215    cx.simulate_window_resize(cx.window_id, vec2f(100., 4. * line_height));
1216
1217    cx.set_state(
1218        &r#"
1219        ˇone
1220        two
1221        threeˇ
1222        four
1223        five
1224        six
1225        seven
1226        eight
1227        nine
1228        ten
1229        "#
1230        .unindent(),
1231    );
1232
1233    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
1234    cx.assert_editor_state(
1235        &r#"
1236        one
1237        two
1238        three
1239        ˇfour
1240        five
1241        sixˇ
1242        seven
1243        eight
1244        nine
1245        ten
1246        "#
1247        .unindent(),
1248    );
1249
1250    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
1251    cx.assert_editor_state(
1252        &r#"
1253        one
1254        two
1255        three
1256        four
1257        five
1258        six
1259        ˇseven
1260        eight
1261        nineˇ
1262        ten
1263        "#
1264        .unindent(),
1265    );
1266
1267    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
1268    cx.assert_editor_state(
1269        &r#"
1270        one
1271        two
1272        three
1273        ˇfour
1274        five
1275        sixˇ
1276        seven
1277        eight
1278        nine
1279        ten
1280        "#
1281        .unindent(),
1282    );
1283
1284    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
1285    cx.assert_editor_state(
1286        &r#"
1287        ˇone
1288        two
1289        threeˇ
1290        four
1291        five
1292        six
1293        seven
1294        eight
1295        nine
1296        ten
1297        "#
1298        .unindent(),
1299    );
1300
1301    // Test select collapsing
1302    cx.update_editor(|editor, cx| {
1303        editor.move_page_down(&MovePageDown::default(), cx);
1304        editor.move_page_down(&MovePageDown::default(), cx);
1305        editor.move_page_down(&MovePageDown::default(), cx);
1306    });
1307    cx.assert_editor_state(
1308        &r#"
1309        one
1310        two
1311        three
1312        four
1313        five
1314        six
1315        seven
1316        eight
1317        nine
1318        ˇten
1319        ˇ"#
1320        .unindent(),
1321    );
1322}
1323
1324#[gpui::test]
1325async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
1326    let mut cx = EditorTestContext::new(cx);
1327    cx.set_state("one «two threeˇ» four");
1328    cx.update_editor(|editor, cx| {
1329        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
1330        assert_eq!(editor.text(cx), " four");
1331    });
1332}
1333
1334#[gpui::test]
1335fn test_delete_to_word_boundary(cx: &mut gpui::MutableAppContext) {
1336    cx.set_global(Settings::test(cx));
1337    let buffer = MultiBuffer::build_simple("one two three four", cx);
1338    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
1339
1340    view.update(cx, |view, cx| {
1341        view.change_selections(None, cx, |s| {
1342            s.select_display_ranges([
1343                // an empty selection - the preceding word fragment is deleted
1344                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
1345                // characters selected - they are deleted
1346                DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
1347            ])
1348        });
1349        view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
1350    });
1351
1352    assert_eq!(buffer.read(cx).read(cx).text(), "e two te four");
1353
1354    view.update(cx, |view, cx| {
1355        view.change_selections(None, cx, |s| {
1356            s.select_display_ranges([
1357                // an empty selection - the following word fragment is deleted
1358                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
1359                // characters selected - they are deleted
1360                DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
1361            ])
1362        });
1363        view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
1364    });
1365
1366    assert_eq!(buffer.read(cx).read(cx).text(), "e t te our");
1367}
1368
1369#[gpui::test]
1370fn test_newline(cx: &mut gpui::MutableAppContext) {
1371    cx.set_global(Settings::test(cx));
1372    let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
1373    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
1374
1375    view.update(cx, |view, cx| {
1376        view.change_selections(None, cx, |s| {
1377            s.select_display_ranges([
1378                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
1379                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
1380                DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
1381            ])
1382        });
1383
1384        view.newline(&Newline, cx);
1385        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
1386    });
1387}
1388
1389#[gpui::test]
1390fn test_newline_with_old_selections(cx: &mut gpui::MutableAppContext) {
1391    cx.set_global(Settings::test(cx));
1392    let buffer = MultiBuffer::build_simple(
1393        "
1394            a
1395            b(
1396                X
1397            )
1398            c(
1399                X
1400            )
1401        "
1402        .unindent()
1403        .as_str(),
1404        cx,
1405    );
1406
1407    let (_, editor) = cx.add_window(Default::default(), |cx| {
1408        let mut editor = build_editor(buffer.clone(), cx);
1409        editor.change_selections(None, cx, |s| {
1410            s.select_ranges([
1411                Point::new(2, 4)..Point::new(2, 5),
1412                Point::new(5, 4)..Point::new(5, 5),
1413            ])
1414        });
1415        editor
1416    });
1417
1418    // Edit the buffer directly, deleting ranges surrounding the editor's selections
1419    buffer.update(cx, |buffer, cx| {
1420        buffer.edit(
1421            [
1422                (Point::new(1, 2)..Point::new(3, 0), ""),
1423                (Point::new(4, 2)..Point::new(6, 0), ""),
1424            ],
1425            None,
1426            cx,
1427        );
1428        assert_eq!(
1429            buffer.read(cx).text(),
1430            "
1431                a
1432                b()
1433                c()
1434            "
1435            .unindent()
1436        );
1437    });
1438
1439    editor.update(cx, |editor, cx| {
1440        assert_eq!(
1441            editor.selections.ranges(cx),
1442            &[
1443                Point::new(1, 2)..Point::new(1, 2),
1444                Point::new(2, 2)..Point::new(2, 2),
1445            ],
1446        );
1447
1448        editor.newline(&Newline, cx);
1449        assert_eq!(
1450            editor.text(cx),
1451            "
1452                a
1453                b(
1454                )
1455                c(
1456                )
1457            "
1458            .unindent()
1459        );
1460
1461        // The selections are moved after the inserted newlines
1462        assert_eq!(
1463            editor.selections.ranges(cx),
1464            &[
1465                Point::new(2, 0)..Point::new(2, 0),
1466                Point::new(4, 0)..Point::new(4, 0),
1467            ],
1468        );
1469    });
1470}
1471
1472#[gpui::test]
1473async fn test_newline_below(cx: &mut gpui::TestAppContext) {
1474    let mut cx = EditorTestContext::new(cx);
1475    cx.update(|cx| {
1476        cx.update_global::<Settings, _, _>(|settings, _| {
1477            settings.editor_overrides.tab_size = Some(NonZeroU32::new(4).unwrap());
1478        });
1479    });
1480
1481    let language = Arc::new(
1482        Language::new(
1483            LanguageConfig::default(),
1484            Some(tree_sitter_rust::language()),
1485        )
1486        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
1487        .unwrap(),
1488    );
1489    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1490
1491    cx.set_state(indoc! {"
1492        const a: ˇA = (
14931494                «const_functionˇ»(ˇ),
1495                so«mˇ»et«hˇ»ing_ˇelse,ˇ
14961497        ˇ);ˇ
1498    "});
1499    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
1500    cx.assert_editor_state(indoc! {"
1501        const a: A = (
1502            ˇ
1503            (
1504                ˇ
1505                const_function(),
1506                ˇ
1507                ˇ
1508                something_else,
1509                ˇ
1510                ˇ
1511                ˇ
1512                ˇ
1513            )
1514            ˇ
1515        );
1516        ˇ
1517        ˇ
1518    "});
1519}
1520
1521#[gpui::test]
1522fn test_insert_with_old_selections(cx: &mut gpui::MutableAppContext) {
1523    cx.set_global(Settings::test(cx));
1524    let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
1525    let (_, editor) = cx.add_window(Default::default(), |cx| {
1526        let mut editor = build_editor(buffer.clone(), cx);
1527        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
1528        editor
1529    });
1530
1531    // Edit the buffer directly, deleting ranges surrounding the editor's selections
1532    buffer.update(cx, |buffer, cx| {
1533        buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
1534        assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
1535    });
1536
1537    editor.update(cx, |editor, cx| {
1538        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
1539
1540        editor.insert("Z", cx);
1541        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
1542
1543        // The selections are moved after the inserted characters
1544        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
1545    });
1546}
1547
1548#[gpui::test]
1549async fn test_tab(cx: &mut gpui::TestAppContext) {
1550    let mut cx = EditorTestContext::new(cx);
1551    cx.update(|cx| {
1552        cx.update_global::<Settings, _, _>(|settings, _| {
1553            settings.editor_overrides.tab_size = Some(NonZeroU32::new(3).unwrap());
1554        });
1555    });
1556    cx.set_state(indoc! {"
1557        ˇabˇc
1558        ˇ🏀ˇ🏀ˇefg
15591560    "});
1561    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1562    cx.assert_editor_state(indoc! {"
1563           ˇab ˇc
1564           ˇ🏀  ˇ🏀  ˇefg
1565        d  ˇ
1566    "});
1567
1568    cx.set_state(indoc! {"
1569        a
1570        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
1571    "});
1572    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1573    cx.assert_editor_state(indoc! {"
1574        a
1575           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
1576    "});
1577}
1578
1579#[gpui::test]
1580async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
1581    let mut cx = EditorTestContext::new(cx);
1582    let language = Arc::new(
1583        Language::new(
1584            LanguageConfig::default(),
1585            Some(tree_sitter_rust::language()),
1586        )
1587        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
1588        .unwrap(),
1589    );
1590    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1591
1592    // cursors that are already at the suggested indent level insert
1593    // a soft tab. cursors that are to the left of the suggested indent
1594    // auto-indent their line.
1595    cx.set_state(indoc! {"
1596        ˇ
1597        const a: B = (
1598            c(
1599                d(
1600        ˇ
1601                )
1602        ˇ
1603        ˇ    )
1604        );
1605    "});
1606    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1607    cx.assert_editor_state(indoc! {"
1608            ˇ
1609        const a: B = (
1610            c(
1611                d(
1612                    ˇ
1613                )
1614                ˇ
1615            ˇ)
1616        );
1617    "});
1618
1619    // handle auto-indent when there are multiple cursors on the same line
1620    cx.set_state(indoc! {"
1621        const a: B = (
1622            c(
1623        ˇ    ˇ
1624        ˇ    )
1625        );
1626    "});
1627    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1628    cx.assert_editor_state(indoc! {"
1629        const a: B = (
1630            c(
1631                ˇ
1632            ˇ)
1633        );
1634    "});
1635}
1636
1637#[gpui::test]
1638async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
1639    let mut cx = EditorTestContext::new(cx);
1640    let language = Arc::new(
1641        Language::new(
1642            LanguageConfig::default(),
1643            Some(tree_sitter_rust::language()),
1644        )
1645        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
1646        .unwrap(),
1647    );
1648    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1649
1650    cx.update(|cx| {
1651        cx.update_global::<Settings, _, _>(|settings, _| {
1652            settings.editor_overrides.tab_size = Some(4.try_into().unwrap());
1653        });
1654    });
1655
1656    cx.set_state(indoc! {"
1657        fn a() {
1658            if b {
1659        \t ˇc
1660            }
1661        }
1662    "});
1663
1664    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1665    cx.assert_editor_state(indoc! {"
1666        fn a() {
1667            if b {
1668                ˇc
1669            }
1670        }
1671    "});
1672}
1673
1674#[gpui::test]
1675async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
1676    let mut cx = EditorTestContext::new(cx);
1677
1678    cx.set_state(indoc! {"
1679          «oneˇ» «twoˇ»
1680        three
1681         four
1682    "});
1683    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1684    cx.assert_editor_state(indoc! {"
1685            «oneˇ» «twoˇ»
1686        three
1687         four
1688    "});
1689
1690    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1691    cx.assert_editor_state(indoc! {"
1692        «oneˇ» «twoˇ»
1693        three
1694         four
1695    "});
1696
1697    // select across line ending
1698    cx.set_state(indoc! {"
1699        one two
1700        t«hree
1701        ˇ» four
1702    "});
1703    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1704    cx.assert_editor_state(indoc! {"
1705        one two
1706            t«hree
1707        ˇ» four
1708    "});
1709
1710    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1711    cx.assert_editor_state(indoc! {"
1712        one two
1713        t«hree
1714        ˇ» four
1715    "});
1716
1717    // Ensure that indenting/outdenting works when the cursor is at column 0.
1718    cx.set_state(indoc! {"
1719        one two
1720        ˇthree
1721            four
1722    "});
1723    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1724    cx.assert_editor_state(indoc! {"
1725        one two
1726            ˇthree
1727            four
1728    "});
1729
1730    cx.set_state(indoc! {"
1731        one two
1732        ˇ    three
1733            four
1734    "});
1735    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1736    cx.assert_editor_state(indoc! {"
1737        one two
1738        ˇthree
1739            four
1740    "});
1741}
1742
1743#[gpui::test]
1744async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
1745    let mut cx = EditorTestContext::new(cx);
1746    cx.update(|cx| {
1747        cx.update_global::<Settings, _, _>(|settings, _| {
1748            settings.editor_overrides.hard_tabs = Some(true);
1749        });
1750    });
1751
1752    // select two ranges on one line
1753    cx.set_state(indoc! {"
1754        «oneˇ» «twoˇ»
1755        three
1756        four
1757    "});
1758    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1759    cx.assert_editor_state(indoc! {"
1760        \t«oneˇ» «twoˇ»
1761        three
1762        four
1763    "});
1764    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1765    cx.assert_editor_state(indoc! {"
1766        \t\t«oneˇ» «twoˇ»
1767        three
1768        four
1769    "});
1770    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1771    cx.assert_editor_state(indoc! {"
1772        \t«oneˇ» «twoˇ»
1773        three
1774        four
1775    "});
1776    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1777    cx.assert_editor_state(indoc! {"
1778        «oneˇ» «twoˇ»
1779        three
1780        four
1781    "});
1782
1783    // select across a line ending
1784    cx.set_state(indoc! {"
1785        one two
1786        t«hree
1787        ˇ»four
1788    "});
1789    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1790    cx.assert_editor_state(indoc! {"
1791        one two
1792        \tt«hree
1793        ˇ»four
1794    "});
1795    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1796    cx.assert_editor_state(indoc! {"
1797        one two
1798        \t\tt«hree
1799        ˇ»four
1800    "});
1801    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1802    cx.assert_editor_state(indoc! {"
1803        one two
1804        \tt«hree
1805        ˇ»four
1806    "});
1807    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1808    cx.assert_editor_state(indoc! {"
1809        one two
1810        t«hree
1811        ˇ»four
1812    "});
1813
1814    // Ensure that indenting/outdenting works when the cursor is at column 0.
1815    cx.set_state(indoc! {"
1816        one two
1817        ˇthree
1818        four
1819    "});
1820    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1821    cx.assert_editor_state(indoc! {"
1822        one two
1823        ˇthree
1824        four
1825    "});
1826    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1827    cx.assert_editor_state(indoc! {"
1828        one two
1829        \tˇthree
1830        four
1831    "});
1832    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1833    cx.assert_editor_state(indoc! {"
1834        one two
1835        ˇthree
1836        four
1837    "});
1838}
1839
1840#[gpui::test]
1841fn test_indent_outdent_with_excerpts(cx: &mut gpui::MutableAppContext) {
1842    cx.set_global(
1843        Settings::test(cx)
1844            .with_language_defaults(
1845                "TOML",
1846                EditorSettings {
1847                    tab_size: Some(2.try_into().unwrap()),
1848                    ..Default::default()
1849                },
1850            )
1851            .with_language_defaults(
1852                "Rust",
1853                EditorSettings {
1854                    tab_size: Some(4.try_into().unwrap()),
1855                    ..Default::default()
1856                },
1857            ),
1858    );
1859    let toml_language = Arc::new(Language::new(
1860        LanguageConfig {
1861            name: "TOML".into(),
1862            ..Default::default()
1863        },
1864        None,
1865    ));
1866    let rust_language = Arc::new(Language::new(
1867        LanguageConfig {
1868            name: "Rust".into(),
1869            ..Default::default()
1870        },
1871        None,
1872    ));
1873
1874    let toml_buffer =
1875        cx.add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx));
1876    let rust_buffer = cx.add_model(|cx| {
1877        Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx)
1878    });
1879    let multibuffer = cx.add_model(|cx| {
1880        let mut multibuffer = MultiBuffer::new(0);
1881        multibuffer.push_excerpts(
1882            toml_buffer.clone(),
1883            [ExcerptRange {
1884                context: Point::new(0, 0)..Point::new(2, 0),
1885                primary: None,
1886            }],
1887            cx,
1888        );
1889        multibuffer.push_excerpts(
1890            rust_buffer.clone(),
1891            [ExcerptRange {
1892                context: Point::new(0, 0)..Point::new(1, 0),
1893                primary: None,
1894            }],
1895            cx,
1896        );
1897        multibuffer
1898    });
1899
1900    cx.add_window(Default::default(), |cx| {
1901        let mut editor = build_editor(multibuffer, cx);
1902
1903        assert_eq!(
1904            editor.text(cx),
1905            indoc! {"
1906                a = 1
1907                b = 2
1908
1909                const c: usize = 3;
1910            "}
1911        );
1912
1913        select_ranges(
1914            &mut editor,
1915            indoc! {"
1916                «aˇ» = 1
1917                b = 2
1918
1919                «const c:ˇ» usize = 3;
1920            "},
1921            cx,
1922        );
1923
1924        editor.tab(&Tab, cx);
1925        assert_text_with_selections(
1926            &mut editor,
1927            indoc! {"
1928                  «aˇ» = 1
1929                b = 2
1930
1931                    «const c:ˇ» usize = 3;
1932            "},
1933            cx,
1934        );
1935        editor.tab_prev(&TabPrev, cx);
1936        assert_text_with_selections(
1937            &mut editor,
1938            indoc! {"
1939                «aˇ» = 1
1940                b = 2
1941
1942                «const c:ˇ» usize = 3;
1943            "},
1944            cx,
1945        );
1946
1947        editor
1948    });
1949}
1950
1951#[gpui::test]
1952async fn test_backspace(cx: &mut gpui::TestAppContext) {
1953    let mut cx = EditorTestContext::new(cx);
1954
1955    // Basic backspace
1956    cx.set_state(indoc! {"
1957        onˇe two three
1958        fou«rˇ» five six
1959        seven «ˇeight nine
1960        »ten
1961    "});
1962    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
1963    cx.assert_editor_state(indoc! {"
1964        oˇe two three
1965        fouˇ five six
1966        seven ˇten
1967    "});
1968
1969    // Test backspace inside and around indents
1970    cx.set_state(indoc! {"
1971        zero
1972            ˇone
1973                ˇtwo
1974            ˇ ˇ ˇ  three
1975        ˇ  ˇ  four
1976    "});
1977    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
1978    cx.assert_editor_state(indoc! {"
1979        zero
1980        ˇone
1981            ˇtwo
1982        ˇ  threeˇ  four
1983    "});
1984
1985    // Test backspace with line_mode set to true
1986    cx.update_editor(|e, _| e.selections.line_mode = true);
1987    cx.set_state(indoc! {"
1988        The ˇquick ˇbrown
1989        fox jumps over
1990        the lazy dog
1991        ˇThe qu«ick bˇ»rown"});
1992    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
1993    cx.assert_editor_state(indoc! {"
1994        ˇfox jumps over
1995        the lazy dogˇ"});
1996}
1997
1998#[gpui::test]
1999async fn test_delete(cx: &mut gpui::TestAppContext) {
2000    let mut cx = EditorTestContext::new(cx);
2001
2002    cx.set_state(indoc! {"
2003        onˇe two three
2004        fou«rˇ» five six
2005        seven «ˇeight nine
2006        »ten
2007    "});
2008    cx.update_editor(|e, cx| e.delete(&Delete, cx));
2009    cx.assert_editor_state(indoc! {"
2010        onˇ two three
2011        fouˇ five six
2012        seven ˇten
2013    "});
2014
2015    // Test backspace with line_mode set to true
2016    cx.update_editor(|e, _| e.selections.line_mode = true);
2017    cx.set_state(indoc! {"
2018        The ˇquick ˇbrown
2019        fox «ˇjum»ps over
2020        the lazy dog
2021        ˇThe qu«ick bˇ»rown"});
2022    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2023    cx.assert_editor_state("ˇthe lazy dogˇ");
2024}
2025
2026#[gpui::test]
2027fn test_delete_line(cx: &mut gpui::MutableAppContext) {
2028    cx.set_global(Settings::test(cx));
2029    let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2030    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2031    view.update(cx, |view, cx| {
2032        view.change_selections(None, cx, |s| {
2033            s.select_display_ranges([
2034                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2035                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
2036                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2037            ])
2038        });
2039        view.delete_line(&DeleteLine, cx);
2040        assert_eq!(view.display_text(cx), "ghi");
2041        assert_eq!(
2042            view.selections.display_ranges(cx),
2043            vec![
2044                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
2045                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
2046            ]
2047        );
2048    });
2049
2050    cx.set_global(Settings::test(cx));
2051    let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2052    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2053    view.update(cx, |view, cx| {
2054        view.change_selections(None, cx, |s| {
2055            s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
2056        });
2057        view.delete_line(&DeleteLine, cx);
2058        assert_eq!(view.display_text(cx), "ghi\n");
2059        assert_eq!(
2060            view.selections.display_ranges(cx),
2061            vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
2062        );
2063    });
2064}
2065
2066#[gpui::test]
2067fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
2068    cx.set_global(Settings::test(cx));
2069    let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2070    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2071    view.update(cx, |view, cx| {
2072        view.change_selections(None, cx, |s| {
2073            s.select_display_ranges([
2074                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2075                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2076                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2077                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2078            ])
2079        });
2080        view.duplicate_line(&DuplicateLine, cx);
2081        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
2082        assert_eq!(
2083            view.selections.display_ranges(cx),
2084            vec![
2085                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
2086                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
2087                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2088                DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
2089            ]
2090        );
2091    });
2092
2093    let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2094    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2095    view.update(cx, |view, cx| {
2096        view.change_selections(None, cx, |s| {
2097            s.select_display_ranges([
2098                DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
2099                DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
2100            ])
2101        });
2102        view.duplicate_line(&DuplicateLine, cx);
2103        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
2104        assert_eq!(
2105            view.selections.display_ranges(cx),
2106            vec![
2107                DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
2108                DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
2109            ]
2110        );
2111    });
2112}
2113
2114#[gpui::test]
2115fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
2116    cx.set_global(Settings::test(cx));
2117    let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
2118    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2119    view.update(cx, |view, cx| {
2120        view.fold_ranges(
2121            vec![
2122                Point::new(0, 2)..Point::new(1, 2),
2123                Point::new(2, 3)..Point::new(4, 1),
2124                Point::new(7, 0)..Point::new(8, 4),
2125            ],
2126            true,
2127            cx,
2128        );
2129        view.change_selections(None, cx, |s| {
2130            s.select_display_ranges([
2131                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2132                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2133                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2134                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
2135            ])
2136        });
2137        assert_eq!(
2138            view.display_text(cx),
2139            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
2140        );
2141
2142        view.move_line_up(&MoveLineUp, cx);
2143        assert_eq!(
2144            view.display_text(cx),
2145            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
2146        );
2147        assert_eq!(
2148            view.selections.display_ranges(cx),
2149            vec![
2150                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2151                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2152                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
2153                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
2154            ]
2155        );
2156    });
2157
2158    view.update(cx, |view, cx| {
2159        view.move_line_down(&MoveLineDown, cx);
2160        assert_eq!(
2161            view.display_text(cx),
2162            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
2163        );
2164        assert_eq!(
2165            view.selections.display_ranges(cx),
2166            vec![
2167                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
2168                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2169                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2170                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
2171            ]
2172        );
2173    });
2174
2175    view.update(cx, |view, cx| {
2176        view.move_line_down(&MoveLineDown, cx);
2177        assert_eq!(
2178            view.display_text(cx),
2179            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
2180        );
2181        assert_eq!(
2182            view.selections.display_ranges(cx),
2183            vec![
2184                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2185                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2186                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2187                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
2188            ]
2189        );
2190    });
2191
2192    view.update(cx, |view, cx| {
2193        view.move_line_up(&MoveLineUp, cx);
2194        assert_eq!(
2195            view.display_text(cx),
2196            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
2197        );
2198        assert_eq!(
2199            view.selections.display_ranges(cx),
2200            vec![
2201                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
2202                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2203                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
2204                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
2205            ]
2206        );
2207    });
2208}
2209
2210#[gpui::test]
2211fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) {
2212    cx.set_global(Settings::test(cx));
2213    let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
2214    let snapshot = buffer.read(cx).snapshot(cx);
2215    let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2216    editor.update(cx, |editor, cx| {
2217        editor.insert_blocks(
2218            [BlockProperties {
2219                style: BlockStyle::Fixed,
2220                position: snapshot.anchor_after(Point::new(2, 0)),
2221                disposition: BlockDisposition::Below,
2222                height: 1,
2223                render: Arc::new(|_| Empty::new().boxed()),
2224            }],
2225            cx,
2226        );
2227        editor.change_selections(None, cx, |s| {
2228            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
2229        });
2230        editor.move_line_down(&MoveLineDown, cx);
2231    });
2232}
2233
2234#[gpui::test]
2235fn test_transpose(cx: &mut gpui::MutableAppContext) {
2236    cx.set_global(Settings::test(cx));
2237
2238    _ = cx
2239        .add_window(Default::default(), |cx| {
2240            let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
2241
2242            editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
2243            editor.transpose(&Default::default(), cx);
2244            assert_eq!(editor.text(cx), "bac");
2245            assert_eq!(editor.selections.ranges(cx), [2..2]);
2246
2247            editor.transpose(&Default::default(), cx);
2248            assert_eq!(editor.text(cx), "bca");
2249            assert_eq!(editor.selections.ranges(cx), [3..3]);
2250
2251            editor.transpose(&Default::default(), cx);
2252            assert_eq!(editor.text(cx), "bac");
2253            assert_eq!(editor.selections.ranges(cx), [3..3]);
2254
2255            editor
2256        })
2257        .1;
2258
2259    _ = cx
2260        .add_window(Default::default(), |cx| {
2261            let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
2262
2263            editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
2264            editor.transpose(&Default::default(), cx);
2265            assert_eq!(editor.text(cx), "acb\nde");
2266            assert_eq!(editor.selections.ranges(cx), [3..3]);
2267
2268            editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
2269            editor.transpose(&Default::default(), cx);
2270            assert_eq!(editor.text(cx), "acbd\ne");
2271            assert_eq!(editor.selections.ranges(cx), [5..5]);
2272
2273            editor.transpose(&Default::default(), cx);
2274            assert_eq!(editor.text(cx), "acbde\n");
2275            assert_eq!(editor.selections.ranges(cx), [6..6]);
2276
2277            editor.transpose(&Default::default(), cx);
2278            assert_eq!(editor.text(cx), "acbd\ne");
2279            assert_eq!(editor.selections.ranges(cx), [6..6]);
2280
2281            editor
2282        })
2283        .1;
2284
2285    _ = cx
2286        .add_window(Default::default(), |cx| {
2287            let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
2288
2289            editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
2290            editor.transpose(&Default::default(), cx);
2291            assert_eq!(editor.text(cx), "bacd\ne");
2292            assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
2293
2294            editor.transpose(&Default::default(), cx);
2295            assert_eq!(editor.text(cx), "bcade\n");
2296            assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
2297
2298            editor.transpose(&Default::default(), cx);
2299            assert_eq!(editor.text(cx), "bcda\ne");
2300            assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
2301
2302            editor.transpose(&Default::default(), cx);
2303            assert_eq!(editor.text(cx), "bcade\n");
2304            assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
2305
2306            editor.transpose(&Default::default(), cx);
2307            assert_eq!(editor.text(cx), "bcaed\n");
2308            assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
2309
2310            editor
2311        })
2312        .1;
2313
2314    _ = cx
2315        .add_window(Default::default(), |cx| {
2316            let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
2317
2318            editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
2319            editor.transpose(&Default::default(), cx);
2320            assert_eq!(editor.text(cx), "🏀🍐✋");
2321            assert_eq!(editor.selections.ranges(cx), [8..8]);
2322
2323            editor.transpose(&Default::default(), cx);
2324            assert_eq!(editor.text(cx), "🏀✋🍐");
2325            assert_eq!(editor.selections.ranges(cx), [11..11]);
2326
2327            editor.transpose(&Default::default(), cx);
2328            assert_eq!(editor.text(cx), "🏀🍐✋");
2329            assert_eq!(editor.selections.ranges(cx), [11..11]);
2330
2331            editor
2332        })
2333        .1;
2334}
2335
2336#[gpui::test]
2337async fn test_clipboard(cx: &mut gpui::TestAppContext) {
2338    let mut cx = EditorTestContext::new(cx);
2339
2340    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
2341    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2342    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
2343
2344    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
2345    cx.set_state("two ˇfour ˇsix ˇ");
2346    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2347    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
2348
2349    // Paste again but with only two cursors. Since the number of cursors doesn't
2350    // match the number of slices in the clipboard, the entire clipboard text
2351    // is pasted at each cursor.
2352    cx.set_state("ˇtwo one✅ four three six five ˇ");
2353    cx.update_editor(|e, cx| {
2354        e.handle_input("( ", cx);
2355        e.paste(&Paste, cx);
2356        e.handle_input(") ", cx);
2357    });
2358    cx.assert_editor_state(indoc! {"
2359        ( one✅
2360        three
2361        five ) ˇtwo one✅ four three six five ( one✅
2362        three
2363        five ) ˇ"});
2364
2365    // Cut with three selections, one of which is full-line.
2366    cx.set_state(indoc! {"
2367        1«2ˇ»3
2368        4ˇ567
2369        «8ˇ»9"});
2370    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2371    cx.assert_editor_state(indoc! {"
2372        1ˇ3
2373        ˇ9"});
2374
2375    // Paste with three selections, noticing how the copied selection that was full-line
2376    // gets inserted before the second cursor.
2377    cx.set_state(indoc! {"
2378        1ˇ3
23792380        «oˇ»ne"});
2381    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2382    cx.assert_editor_state(indoc! {"
2383        12ˇ3
2384        4567
23852386        8ˇne"});
2387
2388    // Copy with a single cursor only, which writes the whole line into the clipboard.
2389    cx.set_state(indoc! {"
2390        The quick brown
2391        fox juˇmps over
2392        the lazy dog"});
2393    cx.update_editor(|e, cx| e.copy(&Copy, cx));
2394    cx.cx.assert_clipboard_content(Some("fox jumps over\n"));
2395
2396    // Paste with three selections, noticing how the copied full-line selection is inserted
2397    // before the empty selections but replaces the selection that is non-empty.
2398    cx.set_state(indoc! {"
2399        Tˇhe quick brown
2400        «foˇ»x jumps over
2401        tˇhe lazy dog"});
2402    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2403    cx.assert_editor_state(indoc! {"
2404        fox jumps over
2405        Tˇhe quick brown
2406        fox jumps over
2407        ˇx jumps over
2408        fox jumps over
2409        tˇhe lazy dog"});
2410}
2411
2412#[gpui::test]
2413async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
2414    let mut cx = EditorTestContext::new(cx);
2415    let language = Arc::new(Language::new(
2416        LanguageConfig::default(),
2417        Some(tree_sitter_rust::language()),
2418    ));
2419    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
2420
2421    // Cut an indented block, without the leading whitespace.
2422    cx.set_state(indoc! {"
2423        const a: B = (
2424            c(),
2425            «d(
2426                e,
2427                f
2428            )ˇ»
2429        );
2430    "});
2431    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2432    cx.assert_editor_state(indoc! {"
2433        const a: B = (
2434            c(),
2435            ˇ
2436        );
2437    "});
2438
2439    // Paste it at the same position.
2440    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2441    cx.assert_editor_state(indoc! {"
2442        const a: B = (
2443            c(),
2444            d(
2445                e,
2446                f
24472448        );
2449    "});
2450
2451    // Paste it at a line with a lower indent level.
2452    cx.set_state(indoc! {"
2453        ˇ
2454        const a: B = (
2455            c(),
2456        );
2457    "});
2458    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2459    cx.assert_editor_state(indoc! {"
2460        d(
2461            e,
2462            f
24632464        const a: B = (
2465            c(),
2466        );
2467    "});
2468
2469    // Cut an indented block, with the leading whitespace.
2470    cx.set_state(indoc! {"
2471        const a: B = (
2472            c(),
2473        «    d(
2474                e,
2475                f
2476            )
2477        ˇ»);
2478    "});
2479    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2480    cx.assert_editor_state(indoc! {"
2481        const a: B = (
2482            c(),
2483        ˇ);
2484    "});
2485
2486    // Paste it at the same position.
2487    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2488    cx.assert_editor_state(indoc! {"
2489        const a: B = (
2490            c(),
2491            d(
2492                e,
2493                f
2494            )
2495        ˇ);
2496    "});
2497
2498    // Paste it at a line with a higher indent level.
2499    cx.set_state(indoc! {"
2500        const a: B = (
2501            c(),
2502            d(
2503                e,
25042505            )
2506        );
2507    "});
2508    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2509    cx.assert_editor_state(indoc! {"
2510        const a: B = (
2511            c(),
2512            d(
2513                e,
2514                f    d(
2515                    e,
2516                    f
2517                )
2518        ˇ
2519            )
2520        );
2521    "});
2522}
2523
2524#[gpui::test]
2525fn test_select_all(cx: &mut gpui::MutableAppContext) {
2526    cx.set_global(Settings::test(cx));
2527    let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
2528    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2529    view.update(cx, |view, cx| {
2530        view.select_all(&SelectAll, cx);
2531        assert_eq!(
2532            view.selections.display_ranges(cx),
2533            &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
2534        );
2535    });
2536}
2537
2538#[gpui::test]
2539fn test_select_line(cx: &mut gpui::MutableAppContext) {
2540    cx.set_global(Settings::test(cx));
2541    let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
2542    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2543    view.update(cx, |view, cx| {
2544        view.change_selections(None, cx, |s| {
2545            s.select_display_ranges([
2546                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2547                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2548                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2549                DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
2550            ])
2551        });
2552        view.select_line(&SelectLine, cx);
2553        assert_eq!(
2554            view.selections.display_ranges(cx),
2555            vec![
2556                DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
2557                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
2558            ]
2559        );
2560    });
2561
2562    view.update(cx, |view, cx| {
2563        view.select_line(&SelectLine, cx);
2564        assert_eq!(
2565            view.selections.display_ranges(cx),
2566            vec![
2567                DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
2568                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
2569            ]
2570        );
2571    });
2572
2573    view.update(cx, |view, cx| {
2574        view.select_line(&SelectLine, cx);
2575        assert_eq!(
2576            view.selections.display_ranges(cx),
2577            vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
2578        );
2579    });
2580}
2581
2582#[gpui::test]
2583fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
2584    cx.set_global(Settings::test(cx));
2585    let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
2586    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2587    view.update(cx, |view, cx| {
2588        view.fold_ranges(
2589            vec![
2590                Point::new(0, 2)..Point::new(1, 2),
2591                Point::new(2, 3)..Point::new(4, 1),
2592                Point::new(7, 0)..Point::new(8, 4),
2593            ],
2594            true,
2595            cx,
2596        );
2597        view.change_selections(None, cx, |s| {
2598            s.select_display_ranges([
2599                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2600                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2601                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2602                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
2603            ])
2604        });
2605        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
2606    });
2607
2608    view.update(cx, |view, cx| {
2609        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
2610        assert_eq!(
2611            view.display_text(cx),
2612            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
2613        );
2614        assert_eq!(
2615            view.selections.display_ranges(cx),
2616            [
2617                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2618                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2619                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
2620                DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
2621            ]
2622        );
2623    });
2624
2625    view.update(cx, |view, cx| {
2626        view.change_selections(None, cx, |s| {
2627            s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
2628        });
2629        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
2630        assert_eq!(
2631            view.display_text(cx),
2632            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
2633        );
2634        assert_eq!(
2635            view.selections.display_ranges(cx),
2636            [
2637                DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
2638                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
2639                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
2640                DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
2641                DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
2642                DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
2643                DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
2644                DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
2645            ]
2646        );
2647    });
2648}
2649
2650#[gpui::test]
2651fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
2652    cx.set_global(Settings::test(cx));
2653    let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
2654    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2655
2656    view.update(cx, |view, cx| {
2657        view.change_selections(None, cx, |s| {
2658            s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
2659        });
2660    });
2661    view.update(cx, |view, cx| {
2662        view.add_selection_above(&AddSelectionAbove, cx);
2663        assert_eq!(
2664            view.selections.display_ranges(cx),
2665            vec![
2666                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
2667                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
2668            ]
2669        );
2670    });
2671
2672    view.update(cx, |view, cx| {
2673        view.add_selection_above(&AddSelectionAbove, cx);
2674        assert_eq!(
2675            view.selections.display_ranges(cx),
2676            vec![
2677                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
2678                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
2679            ]
2680        );
2681    });
2682
2683    view.update(cx, |view, cx| {
2684        view.add_selection_below(&AddSelectionBelow, cx);
2685        assert_eq!(
2686            view.selections.display_ranges(cx),
2687            vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
2688        );
2689
2690        view.undo_selection(&UndoSelection, cx);
2691        assert_eq!(
2692            view.selections.display_ranges(cx),
2693            vec![
2694                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
2695                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
2696            ]
2697        );
2698
2699        view.redo_selection(&RedoSelection, cx);
2700        assert_eq!(
2701            view.selections.display_ranges(cx),
2702            vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
2703        );
2704    });
2705
2706    view.update(cx, |view, cx| {
2707        view.add_selection_below(&AddSelectionBelow, cx);
2708        assert_eq!(
2709            view.selections.display_ranges(cx),
2710            vec![
2711                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
2712                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
2713            ]
2714        );
2715    });
2716
2717    view.update(cx, |view, cx| {
2718        view.add_selection_below(&AddSelectionBelow, cx);
2719        assert_eq!(
2720            view.selections.display_ranges(cx),
2721            vec![
2722                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
2723                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
2724            ]
2725        );
2726    });
2727
2728    view.update(cx, |view, cx| {
2729        view.change_selections(None, cx, |s| {
2730            s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
2731        });
2732    });
2733    view.update(cx, |view, cx| {
2734        view.add_selection_below(&AddSelectionBelow, cx);
2735        assert_eq!(
2736            view.selections.display_ranges(cx),
2737            vec![
2738                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
2739                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
2740            ]
2741        );
2742    });
2743
2744    view.update(cx, |view, cx| {
2745        view.add_selection_below(&AddSelectionBelow, cx);
2746        assert_eq!(
2747            view.selections.display_ranges(cx),
2748            vec![
2749                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
2750                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
2751            ]
2752        );
2753    });
2754
2755    view.update(cx, |view, cx| {
2756        view.add_selection_above(&AddSelectionAbove, cx);
2757        assert_eq!(
2758            view.selections.display_ranges(cx),
2759            vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
2760        );
2761    });
2762
2763    view.update(cx, |view, cx| {
2764        view.add_selection_above(&AddSelectionAbove, cx);
2765        assert_eq!(
2766            view.selections.display_ranges(cx),
2767            vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
2768        );
2769    });
2770
2771    view.update(cx, |view, cx| {
2772        view.change_selections(None, cx, |s| {
2773            s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
2774        });
2775        view.add_selection_below(&AddSelectionBelow, cx);
2776        assert_eq!(
2777            view.selections.display_ranges(cx),
2778            vec![
2779                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
2780                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
2781                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
2782            ]
2783        );
2784    });
2785
2786    view.update(cx, |view, cx| {
2787        view.add_selection_below(&AddSelectionBelow, cx);
2788        assert_eq!(
2789            view.selections.display_ranges(cx),
2790            vec![
2791                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
2792                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
2793                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
2794                DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
2795            ]
2796        );
2797    });
2798
2799    view.update(cx, |view, cx| {
2800        view.add_selection_above(&AddSelectionAbove, cx);
2801        assert_eq!(
2802            view.selections.display_ranges(cx),
2803            vec![
2804                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
2805                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
2806                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
2807            ]
2808        );
2809    });
2810
2811    view.update(cx, |view, cx| {
2812        view.change_selections(None, cx, |s| {
2813            s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
2814        });
2815    });
2816    view.update(cx, |view, cx| {
2817        view.add_selection_above(&AddSelectionAbove, cx);
2818        assert_eq!(
2819            view.selections.display_ranges(cx),
2820            vec![
2821                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
2822                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
2823                DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
2824                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
2825            ]
2826        );
2827    });
2828
2829    view.update(cx, |view, cx| {
2830        view.add_selection_below(&AddSelectionBelow, cx);
2831        assert_eq!(
2832            view.selections.display_ranges(cx),
2833            vec![
2834                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
2835                DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
2836                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
2837            ]
2838        );
2839    });
2840}
2841
2842#[gpui::test]
2843async fn test_select_next(cx: &mut gpui::TestAppContext) {
2844    let mut cx = EditorTestContext::new(cx);
2845    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
2846
2847    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
2848    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
2849
2850    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
2851    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
2852
2853    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
2854    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
2855
2856    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
2857    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
2858
2859    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
2860    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
2861
2862    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
2863    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
2864}
2865
2866#[gpui::test]
2867async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
2868    cx.update(|cx| cx.set_global(Settings::test(cx)));
2869    let language = Arc::new(Language::new(
2870        LanguageConfig::default(),
2871        Some(tree_sitter_rust::language()),
2872    ));
2873
2874    let text = r#"
2875        use mod1::mod2::{mod3, mod4};
2876
2877        fn fn_1(param1: bool, param2: &str) {
2878            let var1 = "text";
2879        }
2880    "#
2881    .unindent();
2882
2883    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
2884    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
2885    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
2886    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
2887        .await;
2888
2889    view.update(cx, |view, cx| {
2890        view.change_selections(None, cx, |s| {
2891            s.select_display_ranges([
2892                DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
2893                DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
2894                DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
2895            ]);
2896        });
2897        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
2898    });
2899    assert_eq!(
2900        view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
2901        &[
2902            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
2903            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
2904            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
2905        ]
2906    );
2907
2908    view.update(cx, |view, cx| {
2909        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
2910    });
2911    assert_eq!(
2912        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2913        &[
2914            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
2915            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
2916        ]
2917    );
2918
2919    view.update(cx, |view, cx| {
2920        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
2921    });
2922    assert_eq!(
2923        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2924        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
2925    );
2926
2927    // Trying to expand the selected syntax node one more time has no effect.
2928    view.update(cx, |view, cx| {
2929        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
2930    });
2931    assert_eq!(
2932        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2933        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
2934    );
2935
2936    view.update(cx, |view, cx| {
2937        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
2938    });
2939    assert_eq!(
2940        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2941        &[
2942            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
2943            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
2944        ]
2945    );
2946
2947    view.update(cx, |view, cx| {
2948        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
2949    });
2950    assert_eq!(
2951        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2952        &[
2953            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
2954            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
2955            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
2956        ]
2957    );
2958
2959    view.update(cx, |view, cx| {
2960        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
2961    });
2962    assert_eq!(
2963        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2964        &[
2965            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
2966            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
2967            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
2968        ]
2969    );
2970
2971    // Trying to shrink the selected syntax node one more time has no effect.
2972    view.update(cx, |view, cx| {
2973        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
2974    });
2975    assert_eq!(
2976        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2977        &[
2978            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
2979            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
2980            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
2981        ]
2982    );
2983
2984    // Ensure that we keep expanding the selection if the larger selection starts or ends within
2985    // a fold.
2986    view.update(cx, |view, cx| {
2987        view.fold_ranges(
2988            vec![
2989                Point::new(0, 21)..Point::new(0, 24),
2990                Point::new(3, 20)..Point::new(3, 22),
2991            ],
2992            true,
2993            cx,
2994        );
2995        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
2996    });
2997    assert_eq!(
2998        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2999        &[
3000            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3001            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3002            DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
3003        ]
3004    );
3005}
3006
3007#[gpui::test]
3008async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
3009    cx.update(|cx| cx.set_global(Settings::test(cx)));
3010    let language = Arc::new(
3011        Language::new(
3012            LanguageConfig {
3013                brackets: BracketPairConfig {
3014                    pairs: vec![
3015                        BracketPair {
3016                            start: "{".to_string(),
3017                            end: "}".to_string(),
3018                            close: false,
3019                            newline: true,
3020                        },
3021                        BracketPair {
3022                            start: "(".to_string(),
3023                            end: ")".to_string(),
3024                            close: false,
3025                            newline: true,
3026                        },
3027                    ],
3028                    ..Default::default()
3029                },
3030                ..Default::default()
3031            },
3032            Some(tree_sitter_rust::language()),
3033        )
3034        .with_indents_query(
3035            r#"
3036                (_ "(" ")" @end) @indent
3037                (_ "{" "}" @end) @indent
3038            "#,
3039        )
3040        .unwrap(),
3041    );
3042
3043    let text = "fn a() {}";
3044
3045    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3046    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3047    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3048    editor
3049        .condition(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
3050        .await;
3051
3052    editor.update(cx, |editor, cx| {
3053        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
3054        editor.newline(&Newline, cx);
3055        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
3056        assert_eq!(
3057            editor.selections.ranges(cx),
3058            &[
3059                Point::new(1, 4)..Point::new(1, 4),
3060                Point::new(3, 4)..Point::new(3, 4),
3061                Point::new(5, 0)..Point::new(5, 0)
3062            ]
3063        );
3064    });
3065}
3066
3067#[gpui::test]
3068async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
3069    let mut cx = EditorTestContext::new(cx);
3070
3071    let language = Arc::new(Language::new(
3072        LanguageConfig {
3073            brackets: BracketPairConfig {
3074                pairs: vec![
3075                    BracketPair {
3076                        start: "{".to_string(),
3077                        end: "}".to_string(),
3078                        close: true,
3079                        newline: true,
3080                    },
3081                    BracketPair {
3082                        start: "(".to_string(),
3083                        end: ")".to_string(),
3084                        close: true,
3085                        newline: true,
3086                    },
3087                    BracketPair {
3088                        start: "/*".to_string(),
3089                        end: " */".to_string(),
3090                        close: true,
3091                        newline: true,
3092                    },
3093                    BracketPair {
3094                        start: "[".to_string(),
3095                        end: "]".to_string(),
3096                        close: false,
3097                        newline: true,
3098                    },
3099                    BracketPair {
3100                        start: "\"".to_string(),
3101                        end: "\"".to_string(),
3102                        close: true,
3103                        newline: false,
3104                    },
3105                ],
3106                ..Default::default()
3107            },
3108            autoclose_before: "})]".to_string(),
3109            ..Default::default()
3110        },
3111        Some(tree_sitter_rust::language()),
3112    ));
3113
3114    let registry = Arc::new(LanguageRegistry::test());
3115    registry.add(language.clone());
3116    cx.update_buffer(|buffer, cx| {
3117        buffer.set_language_registry(registry);
3118        buffer.set_language(Some(language), cx);
3119    });
3120
3121    cx.set_state(
3122        &r#"
3123            🏀ˇ
3124            εˇ
3125            ❤️ˇ
3126        "#
3127        .unindent(),
3128    );
3129
3130    // autoclose multiple nested brackets at multiple cursors
3131    cx.update_editor(|view, cx| {
3132        view.handle_input("{", cx);
3133        view.handle_input("{", cx);
3134        view.handle_input("{", cx);
3135    });
3136    cx.assert_editor_state(
3137        &"
3138            🏀{{{ˇ}}}
3139            ε{{{ˇ}}}
3140            ❤️{{{ˇ}}}
3141        "
3142        .unindent(),
3143    );
3144
3145    // insert a different closing bracket
3146    cx.update_editor(|view, cx| {
3147        view.handle_input(")", cx);
3148    });
3149    cx.assert_editor_state(
3150        &"
3151            🏀{{{)ˇ}}}
3152            ε{{{)ˇ}}}
3153            ❤️{{{)ˇ}}}
3154        "
3155        .unindent(),
3156    );
3157
3158    // skip over the auto-closed brackets when typing a closing bracket
3159    cx.update_editor(|view, cx| {
3160        view.move_right(&MoveRight, cx);
3161        view.handle_input("}", cx);
3162        view.handle_input("}", cx);
3163        view.handle_input("}", cx);
3164    });
3165    cx.assert_editor_state(
3166        &"
3167            🏀{{{)}}}}ˇ
3168            ε{{{)}}}}ˇ
3169            ❤️{{{)}}}}ˇ
3170        "
3171        .unindent(),
3172    );
3173
3174    // autoclose multi-character pairs
3175    cx.set_state(
3176        &"
3177            ˇ
3178            ˇ
3179        "
3180        .unindent(),
3181    );
3182    cx.update_editor(|view, cx| {
3183        view.handle_input("/", cx);
3184        view.handle_input("*", cx);
3185    });
3186    cx.assert_editor_state(
3187        &"
3188            /*ˇ */
3189            /*ˇ */
3190        "
3191        .unindent(),
3192    );
3193
3194    // one cursor autocloses a multi-character pair, one cursor
3195    // does not autoclose.
3196    cx.set_state(
3197        &"
31983199            ˇ
3200        "
3201        .unindent(),
3202    );
3203    cx.update_editor(|view, cx| view.handle_input("*", cx));
3204    cx.assert_editor_state(
3205        &"
3206            /*ˇ */
32073208        "
3209        .unindent(),
3210    );
3211
3212    // Don't autoclose if the next character isn't whitespace and isn't
3213    // listed in the language's "autoclose_before" section.
3214    cx.set_state("ˇa b");
3215    cx.update_editor(|view, cx| view.handle_input("{", cx));
3216    cx.assert_editor_state("{ˇa b");
3217
3218    // Don't autoclose if `close` is false for the bracket pair
3219    cx.set_state("ˇ");
3220    cx.update_editor(|view, cx| view.handle_input("[", cx));
3221    cx.assert_editor_state("");
3222
3223    // Surround with brackets if text is selected
3224    cx.set_state("«aˇ» b");
3225    cx.update_editor(|view, cx| view.handle_input("{", cx));
3226    cx.assert_editor_state("{«aˇ»} b");
3227
3228    // Autclose pair where the start and end characters are the same
3229    cx.set_state("");
3230    cx.update_editor(|view, cx| view.handle_input("\"", cx));
3231    cx.assert_editor_state("a\"ˇ\"");
3232    cx.update_editor(|view, cx| view.handle_input("\"", cx));
3233    cx.assert_editor_state("a\"\"ˇ");
3234}
3235
3236#[gpui::test]
3237async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
3238    let mut cx = EditorTestContext::new(cx);
3239
3240    let html_language = Arc::new(
3241        Language::new(
3242            LanguageConfig {
3243                name: "HTML".into(),
3244                brackets: BracketPairConfig {
3245                    pairs: vec![
3246                        BracketPair {
3247                            start: "<".into(),
3248                            end: ">".into(),
3249                            close: true,
3250                            ..Default::default()
3251                        },
3252                        BracketPair {
3253                            start: "{".into(),
3254                            end: "}".into(),
3255                            close: true,
3256                            ..Default::default()
3257                        },
3258                        BracketPair {
3259                            start: "(".into(),
3260                            end: ")".into(),
3261                            close: true,
3262                            ..Default::default()
3263                        },
3264                    ],
3265                    ..Default::default()
3266                },
3267                autoclose_before: "})]>".into(),
3268                ..Default::default()
3269            },
3270            Some(tree_sitter_html::language()),
3271        )
3272        .with_injection_query(
3273            r#"
3274            (script_element
3275                (raw_text) @content
3276                (#set! "language" "javascript"))
3277            "#,
3278        )
3279        .unwrap(),
3280    );
3281
3282    let javascript_language = Arc::new(Language::new(
3283        LanguageConfig {
3284            name: "JavaScript".into(),
3285            brackets: BracketPairConfig {
3286                pairs: vec![
3287                    BracketPair {
3288                        start: "/*".into(),
3289                        end: " */".into(),
3290                        close: true,
3291                        ..Default::default()
3292                    },
3293                    BracketPair {
3294                        start: "{".into(),
3295                        end: "}".into(),
3296                        close: true,
3297                        ..Default::default()
3298                    },
3299                    BracketPair {
3300                        start: "(".into(),
3301                        end: ")".into(),
3302                        close: true,
3303                        ..Default::default()
3304                    },
3305                ],
3306                ..Default::default()
3307            },
3308            autoclose_before: "})]>".into(),
3309            ..Default::default()
3310        },
3311        Some(tree_sitter_javascript::language()),
3312    ));
3313
3314    let registry = Arc::new(LanguageRegistry::test());
3315    registry.add(html_language.clone());
3316    registry.add(javascript_language.clone());
3317
3318    cx.update_buffer(|buffer, cx| {
3319        buffer.set_language_registry(registry);
3320        buffer.set_language(Some(html_language), cx);
3321    });
3322
3323    cx.set_state(
3324        &r#"
3325            <body>ˇ
3326                <script>
3327                    var x = 1;ˇ
3328                </script>
3329            </body>ˇ
3330        "#
3331        .unindent(),
3332    );
3333
3334    // Precondition: different languages are active at different locations.
3335    cx.update_editor(|editor, cx| {
3336        let snapshot = editor.snapshot(cx);
3337        let cursors = editor.selections.ranges::<usize>(cx);
3338        let languages = cursors
3339            .iter()
3340            .map(|c| snapshot.language_at(c.start).unwrap().name())
3341            .collect::<Vec<_>>();
3342        assert_eq!(
3343            languages,
3344            &["HTML".into(), "JavaScript".into(), "HTML".into()]
3345        );
3346    });
3347
3348    // Angle brackets autoclose in HTML, but not JavaScript.
3349    cx.update_editor(|editor, cx| {
3350        editor.handle_input("<", cx);
3351        editor.handle_input("a", cx);
3352    });
3353    cx.assert_editor_state(
3354        &r#"
3355            <body><aˇ>
3356                <script>
3357                    var x = 1;<aˇ
3358                </script>
3359            </body><aˇ>
3360        "#
3361        .unindent(),
3362    );
3363
3364    // Curly braces and parens autoclose in both HTML and JavaScript.
3365    cx.update_editor(|editor, cx| {
3366        editor.handle_input(" b=", cx);
3367        editor.handle_input("{", cx);
3368        editor.handle_input("c", cx);
3369        editor.handle_input("(", cx);
3370    });
3371    cx.assert_editor_state(
3372        &r#"
3373            <body><a b={c(ˇ)}>
3374                <script>
3375                    var x = 1;<a b={c(ˇ)}
3376                </script>
3377            </body><a b={c(ˇ)}>
3378        "#
3379        .unindent(),
3380    );
3381
3382    // Brackets that were already autoclosed are skipped.
3383    cx.update_editor(|editor, cx| {
3384        editor.handle_input(")", cx);
3385        editor.handle_input("d", cx);
3386        editor.handle_input("}", cx);
3387    });
3388    cx.assert_editor_state(
3389        &r#"
3390            <body><a b={c()d}ˇ>
3391                <script>
3392                    var x = 1;<a b={c()d}ˇ
3393                </script>
3394            </body><a b={c()d}ˇ>
3395        "#
3396        .unindent(),
3397    );
3398    cx.update_editor(|editor, cx| {
3399        editor.handle_input(">", cx);
3400    });
3401    cx.assert_editor_state(
3402        &r#"
3403            <body><a b={c()d}>ˇ
3404                <script>
3405                    var x = 1;<a b={c()d}>ˇ
3406                </script>
3407            </body><a b={c()d}>ˇ
3408        "#
3409        .unindent(),
3410    );
3411
3412    // Reset
3413    cx.set_state(
3414        &r#"
3415            <body>ˇ
3416                <script>
3417                    var x = 1;ˇ
3418                </script>
3419            </body>ˇ
3420        "#
3421        .unindent(),
3422    );
3423
3424    cx.update_editor(|editor, cx| {
3425        editor.handle_input("<", cx);
3426    });
3427    cx.assert_editor_state(
3428        &r#"
3429            <body><ˇ>
3430                <script>
3431                    var x = 1;<ˇ
3432                </script>
3433            </body><ˇ>
3434        "#
3435        .unindent(),
3436    );
3437
3438    // When backspacing, the closing angle brackets are removed.
3439    cx.update_editor(|editor, cx| {
3440        editor.backspace(&Backspace, cx);
3441    });
3442    cx.assert_editor_state(
3443        &r#"
3444            <body>ˇ
3445                <script>
3446                    var x = 1;ˇ
3447                </script>
3448            </body>ˇ
3449        "#
3450        .unindent(),
3451    );
3452
3453    // Block comments autoclose in JavaScript, but not HTML.
3454    cx.update_editor(|editor, cx| {
3455        editor.handle_input("/", cx);
3456        editor.handle_input("*", cx);
3457    });
3458    cx.assert_editor_state(
3459        &r#"
3460            <body>/*ˇ
3461                <script>
3462                    var x = 1;/*ˇ */
3463                </script>
3464            </body>/*ˇ
3465        "#
3466        .unindent(),
3467    );
3468}
3469
3470#[gpui::test]
3471async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
3472    let mut cx = EditorTestContext::new(cx);
3473
3474    let rust_language = Arc::new(
3475        Language::new(
3476            LanguageConfig {
3477                name: "Rust".into(),
3478                brackets: serde_json::from_value(json!([
3479                    { "start": "{", "end": "}", "close": true, "newline": true },
3480                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
3481                ]))
3482                .unwrap(),
3483                autoclose_before: "})]>".into(),
3484                ..Default::default()
3485            },
3486            Some(tree_sitter_rust::language()),
3487        )
3488        .with_override_query("(string_literal) @string")
3489        .unwrap(),
3490    );
3491
3492    let registry = Arc::new(LanguageRegistry::test());
3493    registry.add(rust_language.clone());
3494
3495    cx.update_buffer(|buffer, cx| {
3496        buffer.set_language_registry(registry);
3497        buffer.set_language(Some(rust_language), cx);
3498    });
3499
3500    cx.set_state(
3501        &r#"
3502            let x = ˇ
3503        "#
3504        .unindent(),
3505    );
3506
3507    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
3508    cx.update_editor(|editor, cx| {
3509        editor.handle_input("\"", cx);
3510    });
3511    cx.assert_editor_state(
3512        &r#"
3513            let x = "ˇ"
3514        "#
3515        .unindent(),
3516    );
3517
3518    // Inserting another quotation mark. The cursor moves across the existing
3519    // automatically-inserted quotation mark.
3520    cx.update_editor(|editor, cx| {
3521        editor.handle_input("\"", cx);
3522    });
3523    cx.assert_editor_state(
3524        &r#"
3525            let x = ""ˇ
3526        "#
3527        .unindent(),
3528    );
3529
3530    // Reset
3531    cx.set_state(
3532        &r#"
3533            let x = ˇ
3534        "#
3535        .unindent(),
3536    );
3537
3538    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
3539    cx.update_editor(|editor, cx| {
3540        editor.handle_input("\"", cx);
3541        editor.handle_input(" ", cx);
3542        editor.move_left(&Default::default(), cx);
3543        editor.handle_input("\\", cx);
3544        editor.handle_input("\"", cx);
3545    });
3546    cx.assert_editor_state(
3547        &r#"
3548            let x = "\"ˇ "
3549        "#
3550        .unindent(),
3551    );
3552
3553    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
3554    // mark. Nothing is inserted.
3555    cx.update_editor(|editor, cx| {
3556        editor.move_right(&Default::default(), cx);
3557        editor.handle_input("\"", cx);
3558    });
3559    cx.assert_editor_state(
3560        &r#"
3561            let x = "\" "ˇ
3562        "#
3563        .unindent(),
3564    );
3565}
3566
3567#[gpui::test]
3568async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
3569    cx.update(|cx| cx.set_global(Settings::test(cx)));
3570    let language = Arc::new(Language::new(
3571        LanguageConfig {
3572            brackets: BracketPairConfig {
3573                pairs: vec![
3574                    BracketPair {
3575                        start: "{".to_string(),
3576                        end: "}".to_string(),
3577                        close: true,
3578                        newline: true,
3579                    },
3580                    BracketPair {
3581                        start: "/* ".to_string(),
3582                        end: "*/".to_string(),
3583                        close: true,
3584                        ..Default::default()
3585                    },
3586                ],
3587                ..Default::default()
3588            },
3589            ..Default::default()
3590        },
3591        Some(tree_sitter_rust::language()),
3592    ));
3593
3594    let text = r#"
3595        a
3596        b
3597        c
3598    "#
3599    .unindent();
3600
3601    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3602    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3603    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
3604    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
3605        .await;
3606
3607    view.update(cx, |view, cx| {
3608        view.change_selections(None, cx, |s| {
3609            s.select_display_ranges([
3610                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3611                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
3612                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
3613            ])
3614        });
3615
3616        view.handle_input("{", cx);
3617        view.handle_input("{", cx);
3618        view.handle_input("{", cx);
3619        assert_eq!(
3620            view.text(cx),
3621            "
3622                {{{a}}}
3623                {{{b}}}
3624                {{{c}}}
3625            "
3626            .unindent()
3627        );
3628        assert_eq!(
3629            view.selections.display_ranges(cx),
3630            [
3631                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
3632                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
3633                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
3634            ]
3635        );
3636
3637        view.undo(&Undo, cx);
3638        view.undo(&Undo, cx);
3639        view.undo(&Undo, cx);
3640        assert_eq!(
3641            view.text(cx),
3642            "
3643                a
3644                b
3645                c
3646            "
3647            .unindent()
3648        );
3649        assert_eq!(
3650            view.selections.display_ranges(cx),
3651            [
3652                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3653                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
3654                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
3655            ]
3656        );
3657
3658        // Ensure inserting the first character of a multi-byte bracket pair
3659        // doesn't surround the selections with the bracket.
3660        view.handle_input("/", cx);
3661        assert_eq!(
3662            view.text(cx),
3663            "
3664                /
3665                /
3666                /
3667            "
3668            .unindent()
3669        );
3670        assert_eq!(
3671            view.selections.display_ranges(cx),
3672            [
3673                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
3674                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
3675                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
3676            ]
3677        );
3678
3679        view.undo(&Undo, cx);
3680        assert_eq!(
3681            view.text(cx),
3682            "
3683                a
3684                b
3685                c
3686            "
3687            .unindent()
3688        );
3689        assert_eq!(
3690            view.selections.display_ranges(cx),
3691            [
3692                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3693                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
3694                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
3695            ]
3696        );
3697
3698        // Ensure inserting the last character of a multi-byte bracket pair
3699        // doesn't surround the selections with the bracket.
3700        view.handle_input("*", cx);
3701        assert_eq!(
3702            view.text(cx),
3703            "
3704                *
3705                *
3706                *
3707            "
3708            .unindent()
3709        );
3710        assert_eq!(
3711            view.selections.display_ranges(cx),
3712            [
3713                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
3714                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
3715                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
3716            ]
3717        );
3718    });
3719}
3720
3721#[gpui::test]
3722async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
3723    cx.update(|cx| cx.set_global(Settings::test(cx)));
3724    let language = Arc::new(Language::new(
3725        LanguageConfig {
3726            brackets: BracketPairConfig {
3727                pairs: vec![BracketPair {
3728                    start: "{".to_string(),
3729                    end: "}".to_string(),
3730                    close: true,
3731                    newline: true,
3732                }],
3733                ..Default::default()
3734            },
3735            autoclose_before: "}".to_string(),
3736            ..Default::default()
3737        },
3738        Some(tree_sitter_rust::language()),
3739    ));
3740
3741    let text = r#"
3742        a
3743        b
3744        c
3745    "#
3746    .unindent();
3747
3748    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3749    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3750    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3751    editor
3752        .condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
3753        .await;
3754
3755    editor.update(cx, |editor, cx| {
3756        editor.change_selections(None, cx, |s| {
3757            s.select_ranges([
3758                Point::new(0, 1)..Point::new(0, 1),
3759                Point::new(1, 1)..Point::new(1, 1),
3760                Point::new(2, 1)..Point::new(2, 1),
3761            ])
3762        });
3763
3764        editor.handle_input("{", cx);
3765        editor.handle_input("{", cx);
3766        editor.handle_input("_", cx);
3767        assert_eq!(
3768            editor.text(cx),
3769            "
3770                a{{_}}
3771                b{{_}}
3772                c{{_}}
3773            "
3774            .unindent()
3775        );
3776        assert_eq!(
3777            editor.selections.ranges::<Point>(cx),
3778            [
3779                Point::new(0, 4)..Point::new(0, 4),
3780                Point::new(1, 4)..Point::new(1, 4),
3781                Point::new(2, 4)..Point::new(2, 4)
3782            ]
3783        );
3784
3785        editor.backspace(&Default::default(), cx);
3786        editor.backspace(&Default::default(), cx);
3787        assert_eq!(
3788            editor.text(cx),
3789            "
3790                a{}
3791                b{}
3792                c{}
3793            "
3794            .unindent()
3795        );
3796        assert_eq!(
3797            editor.selections.ranges::<Point>(cx),
3798            [
3799                Point::new(0, 2)..Point::new(0, 2),
3800                Point::new(1, 2)..Point::new(1, 2),
3801                Point::new(2, 2)..Point::new(2, 2)
3802            ]
3803        );
3804
3805        editor.delete_to_previous_word_start(&Default::default(), cx);
3806        assert_eq!(
3807            editor.text(cx),
3808            "
3809                a
3810                b
3811                c
3812            "
3813            .unindent()
3814        );
3815        assert_eq!(
3816            editor.selections.ranges::<Point>(cx),
3817            [
3818                Point::new(0, 1)..Point::new(0, 1),
3819                Point::new(1, 1)..Point::new(1, 1),
3820                Point::new(2, 1)..Point::new(2, 1)
3821            ]
3822        );
3823    });
3824}
3825
3826#[gpui::test]
3827async fn test_snippets(cx: &mut gpui::TestAppContext) {
3828    cx.update(|cx| cx.set_global(Settings::test(cx)));
3829
3830    let (text, insertion_ranges) = marked_text_ranges(
3831        indoc! {"
3832            a.ˇ b
3833            a.ˇ b
3834            a.ˇ b
3835        "},
3836        false,
3837    );
3838
3839    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
3840    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3841
3842    editor.update(cx, |editor, cx| {
3843        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
3844
3845        editor
3846            .insert_snippet(&insertion_ranges, snippet, cx)
3847            .unwrap();
3848
3849        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
3850            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
3851            assert_eq!(editor.text(cx), expected_text);
3852            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
3853        }
3854
3855        assert(
3856            editor,
3857            cx,
3858            indoc! {"
3859                a.f(«one», two, «three») b
3860                a.f(«one», two, «three») b
3861                a.f(«one», two, «three») b
3862            "},
3863        );
3864
3865        // Can't move earlier than the first tab stop
3866        assert!(!editor.move_to_prev_snippet_tabstop(cx));
3867        assert(
3868            editor,
3869            cx,
3870            indoc! {"
3871                a.f(«one», two, «three») b
3872                a.f(«one», two, «three») b
3873                a.f(«one», two, «three») b
3874            "},
3875        );
3876
3877        assert!(editor.move_to_next_snippet_tabstop(cx));
3878        assert(
3879            editor,
3880            cx,
3881            indoc! {"
3882                a.f(one, «two», three) b
3883                a.f(one, «two», three) b
3884                a.f(one, «two», three) b
3885            "},
3886        );
3887
3888        editor.move_to_prev_snippet_tabstop(cx);
3889        assert(
3890            editor,
3891            cx,
3892            indoc! {"
3893                a.f(«one», two, «three») b
3894                a.f(«one», two, «three») b
3895                a.f(«one», two, «three») b
3896            "},
3897        );
3898
3899        assert!(editor.move_to_next_snippet_tabstop(cx));
3900        assert(
3901            editor,
3902            cx,
3903            indoc! {"
3904                a.f(one, «two», three) b
3905                a.f(one, «two», three) b
3906                a.f(one, «two», three) b
3907            "},
3908        );
3909        assert!(editor.move_to_next_snippet_tabstop(cx));
3910        assert(
3911            editor,
3912            cx,
3913            indoc! {"
3914                a.f(one, two, three)ˇ b
3915                a.f(one, two, three)ˇ b
3916                a.f(one, two, three)ˇ b
3917            "},
3918        );
3919
3920        // As soon as the last tab stop is reached, snippet state is gone
3921        editor.move_to_prev_snippet_tabstop(cx);
3922        assert(
3923            editor,
3924            cx,
3925            indoc! {"
3926                a.f(one, two, three)ˇ b
3927                a.f(one, two, three)ˇ b
3928                a.f(one, two, three)ˇ b
3929            "},
3930        );
3931    });
3932}
3933
3934#[gpui::test]
3935async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
3936    cx.foreground().forbid_parking();
3937
3938    let mut language = Language::new(
3939        LanguageConfig {
3940            name: "Rust".into(),
3941            path_suffixes: vec!["rs".to_string()],
3942            ..Default::default()
3943        },
3944        Some(tree_sitter_rust::language()),
3945    );
3946    let mut fake_servers = language
3947        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3948            capabilities: lsp::ServerCapabilities {
3949                document_formatting_provider: Some(lsp::OneOf::Left(true)),
3950                ..Default::default()
3951            },
3952            ..Default::default()
3953        }))
3954        .await;
3955
3956    let fs = FakeFs::new(cx.background());
3957    fs.insert_file("/file.rs", Default::default()).await;
3958
3959    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
3960    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
3961    let buffer = project
3962        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
3963        .await
3964        .unwrap();
3965
3966    cx.foreground().start_waiting();
3967    let fake_server = fake_servers.next().await.unwrap();
3968
3969    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3970    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3971    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
3972    assert!(cx.read(|cx| editor.is_dirty(cx)));
3973
3974    let save = cx.update(|cx| editor.save(project.clone(), cx));
3975    fake_server
3976        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
3977            assert_eq!(
3978                params.text_document.uri,
3979                lsp::Url::from_file_path("/file.rs").unwrap()
3980            );
3981            assert_eq!(params.options.tab_size, 4);
3982            Ok(Some(vec![lsp::TextEdit::new(
3983                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
3984                ", ".to_string(),
3985            )]))
3986        })
3987        .next()
3988        .await;
3989    cx.foreground().start_waiting();
3990    save.await.unwrap();
3991    assert_eq!(
3992        editor.read_with(cx, |editor, cx| editor.text(cx)),
3993        "one, two\nthree\n"
3994    );
3995    assert!(!cx.read(|cx| editor.is_dirty(cx)));
3996
3997    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
3998    assert!(cx.read(|cx| editor.is_dirty(cx)));
3999
4000    // Ensure we can still save even if formatting hangs.
4001    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4002        assert_eq!(
4003            params.text_document.uri,
4004            lsp::Url::from_file_path("/file.rs").unwrap()
4005        );
4006        futures::future::pending::<()>().await;
4007        unreachable!()
4008    });
4009    let save = cx.update(|cx| editor.save(project.clone(), cx));
4010    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4011    cx.foreground().start_waiting();
4012    save.await.unwrap();
4013    assert_eq!(
4014        editor.read_with(cx, |editor, cx| editor.text(cx)),
4015        "one\ntwo\nthree\n"
4016    );
4017    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4018
4019    // Set rust language override and assert overriden tabsize is sent to language server
4020    cx.update(|cx| {
4021        cx.update_global::<Settings, _, _>(|settings, _| {
4022            settings.language_overrides.insert(
4023                "Rust".into(),
4024                EditorSettings {
4025                    tab_size: Some(8.try_into().unwrap()),
4026                    ..Default::default()
4027                },
4028            );
4029        })
4030    });
4031
4032    let save = cx.update(|cx| editor.save(project.clone(), cx));
4033    fake_server
4034        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4035            assert_eq!(
4036                params.text_document.uri,
4037                lsp::Url::from_file_path("/file.rs").unwrap()
4038            );
4039            assert_eq!(params.options.tab_size, 8);
4040            Ok(Some(vec![]))
4041        })
4042        .next()
4043        .await;
4044    cx.foreground().start_waiting();
4045    save.await.unwrap();
4046}
4047
4048#[gpui::test]
4049async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
4050    cx.foreground().forbid_parking();
4051
4052    let mut language = Language::new(
4053        LanguageConfig {
4054            name: "Rust".into(),
4055            path_suffixes: vec!["rs".to_string()],
4056            ..Default::default()
4057        },
4058        Some(tree_sitter_rust::language()),
4059    );
4060    let mut fake_servers = language
4061        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4062            capabilities: lsp::ServerCapabilities {
4063                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
4064                ..Default::default()
4065            },
4066            ..Default::default()
4067        }))
4068        .await;
4069
4070    let fs = FakeFs::new(cx.background());
4071    fs.insert_file("/file.rs", Default::default()).await;
4072
4073    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4074    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4075    let buffer = project
4076        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4077        .await
4078        .unwrap();
4079
4080    cx.foreground().start_waiting();
4081    let fake_server = fake_servers.next().await.unwrap();
4082
4083    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4084    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4085    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4086    assert!(cx.read(|cx| editor.is_dirty(cx)));
4087
4088    let save = cx.update(|cx| editor.save(project.clone(), cx));
4089    fake_server
4090        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
4091            assert_eq!(
4092                params.text_document.uri,
4093                lsp::Url::from_file_path("/file.rs").unwrap()
4094            );
4095            assert_eq!(params.options.tab_size, 4);
4096            Ok(Some(vec![lsp::TextEdit::new(
4097                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4098                ", ".to_string(),
4099            )]))
4100        })
4101        .next()
4102        .await;
4103    cx.foreground().start_waiting();
4104    save.await.unwrap();
4105    assert_eq!(
4106        editor.read_with(cx, |editor, cx| editor.text(cx)),
4107        "one, two\nthree\n"
4108    );
4109    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4110
4111    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4112    assert!(cx.read(|cx| editor.is_dirty(cx)));
4113
4114    // Ensure we can still save even if formatting hangs.
4115    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
4116        move |params, _| async move {
4117            assert_eq!(
4118                params.text_document.uri,
4119                lsp::Url::from_file_path("/file.rs").unwrap()
4120            );
4121            futures::future::pending::<()>().await;
4122            unreachable!()
4123        },
4124    );
4125    let save = cx.update(|cx| editor.save(project.clone(), cx));
4126    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4127    cx.foreground().start_waiting();
4128    save.await.unwrap();
4129    assert_eq!(
4130        editor.read_with(cx, |editor, cx| editor.text(cx)),
4131        "one\ntwo\nthree\n"
4132    );
4133    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4134
4135    // Set rust language override and assert overriden tabsize is sent to language server
4136    cx.update(|cx| {
4137        cx.update_global::<Settings, _, _>(|settings, _| {
4138            settings.language_overrides.insert(
4139                "Rust".into(),
4140                EditorSettings {
4141                    tab_size: Some(8.try_into().unwrap()),
4142                    ..Default::default()
4143                },
4144            );
4145        })
4146    });
4147
4148    let save = cx.update(|cx| editor.save(project.clone(), cx));
4149    fake_server
4150        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
4151            assert_eq!(
4152                params.text_document.uri,
4153                lsp::Url::from_file_path("/file.rs").unwrap()
4154            );
4155            assert_eq!(params.options.tab_size, 8);
4156            Ok(Some(vec![]))
4157        })
4158        .next()
4159        .await;
4160    cx.foreground().start_waiting();
4161    save.await.unwrap();
4162}
4163
4164#[gpui::test]
4165async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
4166    cx.foreground().forbid_parking();
4167
4168    let mut language = Language::new(
4169        LanguageConfig {
4170            name: "Rust".into(),
4171            path_suffixes: vec!["rs".to_string()],
4172            ..Default::default()
4173        },
4174        Some(tree_sitter_rust::language()),
4175    );
4176    let mut fake_servers = language
4177        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4178            capabilities: lsp::ServerCapabilities {
4179                document_formatting_provider: Some(lsp::OneOf::Left(true)),
4180                ..Default::default()
4181            },
4182            ..Default::default()
4183        }))
4184        .await;
4185
4186    let fs = FakeFs::new(cx.background());
4187    fs.insert_file("/file.rs", Default::default()).await;
4188
4189    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4190    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4191    let buffer = project
4192        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4193        .await
4194        .unwrap();
4195
4196    cx.foreground().start_waiting();
4197    let fake_server = fake_servers.next().await.unwrap();
4198
4199    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4200    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4201    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4202
4203    let format = editor.update(cx, |editor, cx| {
4204        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
4205    });
4206    fake_server
4207        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4208            assert_eq!(
4209                params.text_document.uri,
4210                lsp::Url::from_file_path("/file.rs").unwrap()
4211            );
4212            assert_eq!(params.options.tab_size, 4);
4213            Ok(Some(vec![lsp::TextEdit::new(
4214                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4215                ", ".to_string(),
4216            )]))
4217        })
4218        .next()
4219        .await;
4220    cx.foreground().start_waiting();
4221    format.await.unwrap();
4222    assert_eq!(
4223        editor.read_with(cx, |editor, cx| editor.text(cx)),
4224        "one, two\nthree\n"
4225    );
4226
4227    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4228    // Ensure we don't lock if formatting hangs.
4229    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4230        assert_eq!(
4231            params.text_document.uri,
4232            lsp::Url::from_file_path("/file.rs").unwrap()
4233        );
4234        futures::future::pending::<()>().await;
4235        unreachable!()
4236    });
4237    let format = editor.update(cx, |editor, cx| {
4238        editor.perform_format(project, FormatTrigger::Manual, cx)
4239    });
4240    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4241    cx.foreground().start_waiting();
4242    format.await.unwrap();
4243    assert_eq!(
4244        editor.read_with(cx, |editor, cx| editor.text(cx)),
4245        "one\ntwo\nthree\n"
4246    );
4247}
4248
4249#[gpui::test]
4250async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
4251    cx.foreground().forbid_parking();
4252
4253    let mut cx = EditorLspTestContext::new_rust(
4254        lsp::ServerCapabilities {
4255            document_formatting_provider: Some(lsp::OneOf::Left(true)),
4256            ..Default::default()
4257        },
4258        cx,
4259    )
4260    .await;
4261
4262    cx.set_state(indoc! {"
4263        one.twoˇ
4264    "});
4265
4266    // The format request takes a long time. When it completes, it inserts
4267    // a newline and an indent before the `.`
4268    cx.lsp
4269        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
4270            let executor = cx.background();
4271            async move {
4272                executor.timer(Duration::from_millis(100)).await;
4273                Ok(Some(vec![lsp::TextEdit {
4274                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
4275                    new_text: "\n    ".into(),
4276                }]))
4277            }
4278        });
4279
4280    // Submit a format request.
4281    let format_1 = cx
4282        .update_editor(|editor, cx| editor.format(&Format, cx))
4283        .unwrap();
4284    cx.foreground().run_until_parked();
4285
4286    // Submit a second format request.
4287    let format_2 = cx
4288        .update_editor(|editor, cx| editor.format(&Format, cx))
4289        .unwrap();
4290    cx.foreground().run_until_parked();
4291
4292    // Wait for both format requests to complete
4293    cx.foreground().advance_clock(Duration::from_millis(200));
4294    cx.foreground().start_waiting();
4295    format_1.await.unwrap();
4296    cx.foreground().start_waiting();
4297    format_2.await.unwrap();
4298
4299    // The formatting edits only happens once.
4300    cx.assert_editor_state(indoc! {"
4301        one
4302            .twoˇ
4303    "});
4304}
4305
4306#[gpui::test]
4307async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
4308    cx.foreground().forbid_parking();
4309
4310    let mut cx = EditorLspTestContext::new_rust(
4311        lsp::ServerCapabilities {
4312            document_formatting_provider: Some(lsp::OneOf::Left(true)),
4313            ..Default::default()
4314        },
4315        cx,
4316    )
4317    .await;
4318
4319    // Set up a buffer white some trailing whitespace and no trailing newline.
4320    cx.set_state(
4321        &[
4322            "one ",   //
4323            "twoˇ",  //
4324            "three ", //
4325            "four",   //
4326        ]
4327        .join("\n"),
4328    );
4329
4330    // Submit a format request.
4331    let format = cx
4332        .update_editor(|editor, cx| editor.format(&Format, cx))
4333        .unwrap();
4334
4335    // Record which buffer changes have been sent to the language server
4336    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
4337    cx.lsp
4338        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
4339            let buffer_changes = buffer_changes.clone();
4340            move |params, _| {
4341                buffer_changes.lock().extend(
4342                    params
4343                        .content_changes
4344                        .into_iter()
4345                        .map(|e| (e.range.unwrap(), e.text)),
4346                );
4347            }
4348        });
4349
4350    // Handle formatting requests to the language server.
4351    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
4352        let buffer_changes = buffer_changes.clone();
4353        move |_, _| {
4354            // When formatting is requested, trailing whitespace has already been stripped,
4355            // and the trailing newline has already been added.
4356            assert_eq!(
4357                &buffer_changes.lock()[1..],
4358                &[
4359                    (
4360                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
4361                        "".into()
4362                    ),
4363                    (
4364                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
4365                        "".into()
4366                    ),
4367                    (
4368                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
4369                        "\n".into()
4370                    ),
4371                ]
4372            );
4373
4374            // Insert blank lines between each line of the buffer.
4375            async move {
4376                Ok(Some(vec![
4377                    lsp::TextEdit {
4378                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
4379                        new_text: "\n".into(),
4380                    },
4381                    lsp::TextEdit {
4382                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
4383                        new_text: "\n".into(),
4384                    },
4385                ]))
4386            }
4387        }
4388    });
4389
4390    // After formatting the buffer, the trailing whitespace is stripped,
4391    // a newline is appended, and the edits provided by the language server
4392    // have been applied.
4393    format.await.unwrap();
4394    cx.assert_editor_state(
4395        &[
4396            "one",   //
4397            "",      //
4398            "twoˇ", //
4399            "",      //
4400            "three", //
4401            "four",  //
4402            "",      //
4403        ]
4404        .join("\n"),
4405    );
4406
4407    // Undoing the formatting undoes the trailing whitespace removal, the
4408    // trailing newline, and the LSP edits.
4409    cx.update_buffer(|buffer, cx| buffer.undo(cx));
4410    cx.assert_editor_state(
4411        &[
4412            "one ",   //
4413            "twoˇ",  //
4414            "three ", //
4415            "four",   //
4416        ]
4417        .join("\n"),
4418    );
4419}
4420
4421#[gpui::test]
4422async fn test_completion(cx: &mut gpui::TestAppContext) {
4423    let mut cx = EditorLspTestContext::new_rust(
4424        lsp::ServerCapabilities {
4425            completion_provider: Some(lsp::CompletionOptions {
4426                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
4427                ..Default::default()
4428            }),
4429            ..Default::default()
4430        },
4431        cx,
4432    )
4433    .await;
4434
4435    cx.set_state(indoc! {"
4436        oneˇ
4437        two
4438        three
4439    "});
4440    cx.simulate_keystroke(".");
4441    handle_completion_request(
4442        &mut cx,
4443        indoc! {"
4444            one.|<>
4445            two
4446            three
4447        "},
4448        vec!["first_completion", "second_completion"],
4449    )
4450    .await;
4451    cx.condition(|editor, _| editor.context_menu_visible())
4452        .await;
4453    let apply_additional_edits = cx.update_editor(|editor, cx| {
4454        editor.move_down(&MoveDown, cx);
4455        editor
4456            .confirm_completion(&ConfirmCompletion::default(), cx)
4457            .unwrap()
4458    });
4459    cx.assert_editor_state(indoc! {"
4460        one.second_completionˇ
4461        two
4462        three
4463    "});
4464
4465    handle_resolve_completion_request(
4466        &mut cx,
4467        Some(vec![
4468            (
4469                //This overlaps with the primary completion edit which is
4470                //misbehavior from the LSP spec, test that we filter it out
4471                indoc! {"
4472                    one.second_ˇcompletion
4473                    two
4474                    threeˇ
4475                "},
4476                "overlapping aditional edit",
4477            ),
4478            (
4479                indoc! {"
4480                    one.second_completion
4481                    two
4482                    threeˇ
4483                "},
4484                "\nadditional edit",
4485            ),
4486        ]),
4487    )
4488    .await;
4489    apply_additional_edits.await.unwrap();
4490    cx.assert_editor_state(indoc! {"
4491        one.second_completionˇ
4492        two
4493        three
4494        additional edit
4495    "});
4496
4497    cx.set_state(indoc! {"
4498        one.second_completion
4499        twoˇ
4500        threeˇ
4501        additional edit
4502    "});
4503    cx.simulate_keystroke(" ");
4504    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4505    cx.simulate_keystroke("s");
4506    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4507
4508    cx.assert_editor_state(indoc! {"
4509        one.second_completion
4510        two sˇ
4511        three sˇ
4512        additional edit
4513    "});
4514    handle_completion_request(
4515        &mut cx,
4516        indoc! {"
4517            one.second_completion
4518            two s
4519            three <s|>
4520            additional edit
4521        "},
4522        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
4523    )
4524    .await;
4525    cx.condition(|editor, _| editor.context_menu_visible())
4526        .await;
4527
4528    cx.simulate_keystroke("i");
4529
4530    handle_completion_request(
4531        &mut cx,
4532        indoc! {"
4533            one.second_completion
4534            two si
4535            three <si|>
4536            additional edit
4537        "},
4538        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
4539    )
4540    .await;
4541    cx.condition(|editor, _| editor.context_menu_visible())
4542        .await;
4543
4544    let apply_additional_edits = cx.update_editor(|editor, cx| {
4545        editor
4546            .confirm_completion(&ConfirmCompletion::default(), cx)
4547            .unwrap()
4548    });
4549    cx.assert_editor_state(indoc! {"
4550        one.second_completion
4551        two sixth_completionˇ
4552        three sixth_completionˇ
4553        additional edit
4554    "});
4555
4556    handle_resolve_completion_request(&mut cx, None).await;
4557    apply_additional_edits.await.unwrap();
4558
4559    cx.update(|cx| {
4560        cx.update_global::<Settings, _, _>(|settings, _| {
4561            settings.show_completions_on_input = false;
4562        })
4563    });
4564    cx.set_state("editorˇ");
4565    cx.simulate_keystroke(".");
4566    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4567    cx.simulate_keystroke("c");
4568    cx.simulate_keystroke("l");
4569    cx.simulate_keystroke("o");
4570    cx.assert_editor_state("editor.cloˇ");
4571    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4572    cx.update_editor(|editor, cx| {
4573        editor.show_completions(&ShowCompletions, cx);
4574    });
4575    handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
4576    cx.condition(|editor, _| editor.context_menu_visible())
4577        .await;
4578    let apply_additional_edits = cx.update_editor(|editor, cx| {
4579        editor
4580            .confirm_completion(&ConfirmCompletion::default(), cx)
4581            .unwrap()
4582    });
4583    cx.assert_editor_state("editor.closeˇ");
4584    handle_resolve_completion_request(&mut cx, None).await;
4585    apply_additional_edits.await.unwrap();
4586
4587    // Handle completion request passing a marked string specifying where the completion
4588    // should be triggered from using '|' character, what range should be replaced, and what completions
4589    // should be returned using '<' and '>' to delimit the range
4590    async fn handle_completion_request<'a>(
4591        cx: &mut EditorLspTestContext<'a>,
4592        marked_string: &str,
4593        completions: Vec<&'static str>,
4594    ) {
4595        let complete_from_marker: TextRangeMarker = '|'.into();
4596        let replace_range_marker: TextRangeMarker = ('<', '>').into();
4597        let (_, mut marked_ranges) = marked_text_ranges_by(
4598            marked_string,
4599            vec![complete_from_marker.clone(), replace_range_marker.clone()],
4600        );
4601
4602        let complete_from_position =
4603            cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
4604        let replace_range =
4605            cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
4606
4607        cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
4608            let completions = completions.clone();
4609            async move {
4610                assert_eq!(params.text_document_position.text_document.uri, url.clone());
4611                assert_eq!(
4612                    params.text_document_position.position,
4613                    complete_from_position
4614                );
4615                Ok(Some(lsp::CompletionResponse::Array(
4616                    completions
4617                        .iter()
4618                        .map(|completion_text| lsp::CompletionItem {
4619                            label: completion_text.to_string(),
4620                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
4621                                range: replace_range,
4622                                new_text: completion_text.to_string(),
4623                            })),
4624                            ..Default::default()
4625                        })
4626                        .collect(),
4627                )))
4628            }
4629        })
4630        .next()
4631        .await;
4632    }
4633
4634    async fn handle_resolve_completion_request<'a>(
4635        cx: &mut EditorLspTestContext<'a>,
4636        edits: Option<Vec<(&'static str, &'static str)>>,
4637    ) {
4638        let edits = edits.map(|edits| {
4639            edits
4640                .iter()
4641                .map(|(marked_string, new_text)| {
4642                    let (_, marked_ranges) = marked_text_ranges(marked_string, false);
4643                    let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
4644                    lsp::TextEdit::new(replace_range, new_text.to_string())
4645                })
4646                .collect::<Vec<_>>()
4647        });
4648
4649        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
4650            let edits = edits.clone();
4651            async move {
4652                Ok(lsp::CompletionItem {
4653                    additional_text_edits: edits,
4654                    ..Default::default()
4655                })
4656            }
4657        })
4658        .next()
4659        .await;
4660    }
4661}
4662
4663#[gpui::test]
4664async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
4665    cx.update(|cx| cx.set_global(Settings::test(cx)));
4666    let language = Arc::new(Language::new(
4667        LanguageConfig {
4668            line_comment: Some("// ".into()),
4669            ..Default::default()
4670        },
4671        Some(tree_sitter_rust::language()),
4672    ));
4673
4674    let text = "
4675        fn a() {
4676            //b();
4677            // c();
4678            //  d();
4679        }
4680    "
4681    .unindent();
4682
4683    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
4684    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4685    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
4686
4687    view.update(cx, |editor, cx| {
4688        // If multiple selections intersect a line, the line is only
4689        // toggled once.
4690        editor.change_selections(None, cx, |s| {
4691            s.select_display_ranges([
4692                DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
4693                DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
4694            ])
4695        });
4696        editor.toggle_comments(&ToggleComments::default(), cx);
4697        assert_eq!(
4698            editor.text(cx),
4699            "
4700                fn a() {
4701                    b();
4702                    c();
4703                     d();
4704                }
4705            "
4706            .unindent()
4707        );
4708
4709        // The comment prefix is inserted at the same column for every line
4710        // in a selection.
4711        editor.change_selections(None, cx, |s| {
4712            s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)])
4713        });
4714        editor.toggle_comments(&ToggleComments::default(), cx);
4715        assert_eq!(
4716            editor.text(cx),
4717            "
4718                fn a() {
4719                    // b();
4720                    // c();
4721                    //  d();
4722                }
4723            "
4724            .unindent()
4725        );
4726
4727        // If a selection ends at the beginning of a line, that line is not toggled.
4728        editor.change_selections(None, cx, |s| {
4729            s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)])
4730        });
4731        editor.toggle_comments(&ToggleComments::default(), cx);
4732        assert_eq!(
4733            editor.text(cx),
4734            "
4735                fn a() {
4736                    // b();
4737                    c();
4738                    //  d();
4739                }
4740            "
4741            .unindent()
4742        );
4743    });
4744}
4745
4746#[gpui::test]
4747async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
4748    let mut cx = EditorTestContext::new(cx);
4749    cx.update(|cx| cx.set_global(Settings::test(cx)));
4750
4751    let language = Arc::new(Language::new(
4752        LanguageConfig {
4753            line_comment: Some("// ".into()),
4754            ..Default::default()
4755        },
4756        Some(tree_sitter_rust::language()),
4757    ));
4758
4759    let registry = Arc::new(LanguageRegistry::test());
4760    registry.add(language.clone());
4761
4762    cx.update_buffer(|buffer, cx| {
4763        buffer.set_language_registry(registry);
4764        buffer.set_language(Some(language), cx);
4765    });
4766
4767    let toggle_comments = &ToggleComments {
4768        advance_downwards: true,
4769    };
4770
4771    // Single cursor on one line -> advance
4772    // Cursor moves horizontally 3 characters as well on non-blank line
4773    cx.set_state(indoc!(
4774        "fn a() {
4775             ˇdog();
4776             cat();
4777        }"
4778    ));
4779    cx.update_editor(|editor, cx| {
4780        editor.toggle_comments(toggle_comments, cx);
4781    });
4782    cx.assert_editor_state(indoc!(
4783        "fn a() {
4784             // dog();
4785             catˇ();
4786        }"
4787    ));
4788
4789    // Single selection on one line -> don't advance
4790    cx.set_state(indoc!(
4791        "fn a() {
4792             «dog()ˇ»;
4793             cat();
4794        }"
4795    ));
4796    cx.update_editor(|editor, cx| {
4797        editor.toggle_comments(toggle_comments, cx);
4798    });
4799    cx.assert_editor_state(indoc!(
4800        "fn a() {
4801             // «dog()ˇ»;
4802             cat();
4803        }"
4804    ));
4805
4806    // Multiple cursors on one line -> advance
4807    cx.set_state(indoc!(
4808        "fn a() {
4809             ˇdˇog();
4810             cat();
4811        }"
4812    ));
4813    cx.update_editor(|editor, cx| {
4814        editor.toggle_comments(toggle_comments, cx);
4815    });
4816    cx.assert_editor_state(indoc!(
4817        "fn a() {
4818             // dog();
4819             catˇ(ˇ);
4820        }"
4821    ));
4822
4823    // Multiple cursors on one line, with selection -> don't advance
4824    cx.set_state(indoc!(
4825        "fn a() {
4826             ˇdˇog«()ˇ»;
4827             cat();
4828        }"
4829    ));
4830    cx.update_editor(|editor, cx| {
4831        editor.toggle_comments(toggle_comments, cx);
4832    });
4833    cx.assert_editor_state(indoc!(
4834        "fn a() {
4835             // ˇdˇog«()ˇ»;
4836             cat();
4837        }"
4838    ));
4839
4840    // Single cursor on one line -> advance
4841    // Cursor moves to column 0 on blank line
4842    cx.set_state(indoc!(
4843        "fn a() {
4844             ˇdog();
4845
4846             cat();
4847        }"
4848    ));
4849    cx.update_editor(|editor, cx| {
4850        editor.toggle_comments(toggle_comments, cx);
4851    });
4852    cx.assert_editor_state(indoc!(
4853        "fn a() {
4854             // dog();
4855        ˇ
4856             cat();
4857        }"
4858    ));
4859
4860    // Single cursor on one line -> advance
4861    // Cursor starts and ends at column 0
4862    cx.set_state(indoc!(
4863        "fn a() {
4864         ˇ    dog();
4865             cat();
4866        }"
4867    ));
4868    cx.update_editor(|editor, cx| {
4869        editor.toggle_comments(toggle_comments, cx);
4870    });
4871    cx.assert_editor_state(indoc!(
4872        "fn a() {
4873             // dog();
4874         ˇ    cat();
4875        }"
4876    ));
4877}
4878
4879#[gpui::test]
4880async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
4881    let mut cx = EditorTestContext::new(cx);
4882
4883    let html_language = Arc::new(
4884        Language::new(
4885            LanguageConfig {
4886                name: "HTML".into(),
4887                block_comment: Some(("<!-- ".into(), " -->".into())),
4888                ..Default::default()
4889            },
4890            Some(tree_sitter_html::language()),
4891        )
4892        .with_injection_query(
4893            r#"
4894            (script_element
4895                (raw_text) @content
4896                (#set! "language" "javascript"))
4897            "#,
4898        )
4899        .unwrap(),
4900    );
4901
4902    let javascript_language = Arc::new(Language::new(
4903        LanguageConfig {
4904            name: "JavaScript".into(),
4905            line_comment: Some("// ".into()),
4906            ..Default::default()
4907        },
4908        Some(tree_sitter_javascript::language()),
4909    ));
4910
4911    let registry = Arc::new(LanguageRegistry::test());
4912    registry.add(html_language.clone());
4913    registry.add(javascript_language.clone());
4914
4915    cx.update_buffer(|buffer, cx| {
4916        buffer.set_language_registry(registry);
4917        buffer.set_language(Some(html_language), cx);
4918    });
4919
4920    // Toggle comments for empty selections
4921    cx.set_state(
4922        &r#"
4923            <p>A</p>ˇ
4924            <p>B</p>ˇ
4925            <p>C</p>ˇ
4926        "#
4927        .unindent(),
4928    );
4929    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
4930    cx.assert_editor_state(
4931        &r#"
4932            <!-- <p>A</p>ˇ -->
4933            <!-- <p>B</p>ˇ -->
4934            <!-- <p>C</p>ˇ -->
4935        "#
4936        .unindent(),
4937    );
4938    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
4939    cx.assert_editor_state(
4940        &r#"
4941            <p>A</p>ˇ
4942            <p>B</p>ˇ
4943            <p>C</p>ˇ
4944        "#
4945        .unindent(),
4946    );
4947
4948    // Toggle comments for mixture of empty and non-empty selections, where
4949    // multiple selections occupy a given line.
4950    cx.set_state(
4951        &r#"
4952            <p>A«</p>
4953            <p>ˇ»B</p>ˇ
4954            <p>C«</p>
4955            <p>ˇ»D</p>ˇ
4956        "#
4957        .unindent(),
4958    );
4959
4960    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
4961    cx.assert_editor_state(
4962        &r#"
4963            <!-- <p>A«</p>
4964            <p>ˇ»B</p>ˇ -->
4965            <!-- <p>C«</p>
4966            <p>ˇ»D</p>ˇ -->
4967        "#
4968        .unindent(),
4969    );
4970    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
4971    cx.assert_editor_state(
4972        &r#"
4973            <p>A«</p>
4974            <p>ˇ»B</p>ˇ
4975            <p>C«</p>
4976            <p>ˇ»D</p>ˇ
4977        "#
4978        .unindent(),
4979    );
4980
4981    // Toggle comments when different languages are active for different
4982    // selections.
4983    cx.set_state(
4984        &r#"
4985            ˇ<script>
4986                ˇvar x = new Y();
4987            ˇ</script>
4988        "#
4989        .unindent(),
4990    );
4991    cx.foreground().run_until_parked();
4992    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
4993    cx.assert_editor_state(
4994        &r#"
4995            <!-- ˇ<script> -->
4996                // ˇvar x = new Y();
4997            <!-- ˇ</script> -->
4998        "#
4999        .unindent(),
5000    );
5001}
5002
5003#[gpui::test]
5004fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
5005    cx.set_global(Settings::test(cx));
5006    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5007    let multibuffer = cx.add_model(|cx| {
5008        let mut multibuffer = MultiBuffer::new(0);
5009        multibuffer.push_excerpts(
5010            buffer.clone(),
5011            [
5012                ExcerptRange {
5013                    context: Point::new(0, 0)..Point::new(0, 4),
5014                    primary: None,
5015                },
5016                ExcerptRange {
5017                    context: Point::new(1, 0)..Point::new(1, 4),
5018                    primary: None,
5019                },
5020            ],
5021            cx,
5022        );
5023        multibuffer
5024    });
5025
5026    assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
5027
5028    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
5029    view.update(cx, |view, cx| {
5030        assert_eq!(view.text(cx), "aaaa\nbbbb");
5031        view.change_selections(None, cx, |s| {
5032            s.select_ranges([
5033                Point::new(0, 0)..Point::new(0, 0),
5034                Point::new(1, 0)..Point::new(1, 0),
5035            ])
5036        });
5037
5038        view.handle_input("X", cx);
5039        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
5040        assert_eq!(
5041            view.selections.ranges(cx),
5042            [
5043                Point::new(0, 1)..Point::new(0, 1),
5044                Point::new(1, 1)..Point::new(1, 1),
5045            ]
5046        )
5047    });
5048}
5049
5050#[gpui::test]
5051fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
5052    cx.set_global(Settings::test(cx));
5053    let markers = vec![('[', ']').into(), ('(', ')').into()];
5054    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
5055        indoc! {"
5056            [aaaa
5057            (bbbb]
5058            cccc)",
5059        },
5060        markers.clone(),
5061    );
5062    let excerpt_ranges = markers.into_iter().map(|marker| {
5063        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
5064        ExcerptRange {
5065            context,
5066            primary: None,
5067        }
5068    });
5069    let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
5070    let multibuffer = cx.add_model(|cx| {
5071        let mut multibuffer = MultiBuffer::new(0);
5072        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
5073        multibuffer
5074    });
5075
5076    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
5077    view.update(cx, |view, cx| {
5078        let (expected_text, selection_ranges) = marked_text_ranges(
5079            indoc! {"
5080                aaaa
5081                bˇbbb
5082                bˇbbˇb
5083                cccc"
5084            },
5085            true,
5086        );
5087        assert_eq!(view.text(cx), expected_text);
5088        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
5089
5090        view.handle_input("X", cx);
5091
5092        let (expected_text, expected_selections) = marked_text_ranges(
5093            indoc! {"
5094                aaaa
5095                bXˇbbXb
5096                bXˇbbXˇb
5097                cccc"
5098            },
5099            false,
5100        );
5101        assert_eq!(view.text(cx), expected_text);
5102        assert_eq!(view.selections.ranges(cx), expected_selections);
5103
5104        view.newline(&Newline, cx);
5105        let (expected_text, expected_selections) = marked_text_ranges(
5106            indoc! {"
5107                aaaa
5108                bX
5109                ˇbbX
5110                b
5111                bX
5112                ˇbbX
5113                ˇb
5114                cccc"
5115            },
5116            false,
5117        );
5118        assert_eq!(view.text(cx), expected_text);
5119        assert_eq!(view.selections.ranges(cx), expected_selections);
5120    });
5121}
5122
5123#[gpui::test]
5124fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
5125    cx.set_global(Settings::test(cx));
5126    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5127    let mut excerpt1_id = None;
5128    let multibuffer = cx.add_model(|cx| {
5129        let mut multibuffer = MultiBuffer::new(0);
5130        excerpt1_id = multibuffer
5131            .push_excerpts(
5132                buffer.clone(),
5133                [
5134                    ExcerptRange {
5135                        context: Point::new(0, 0)..Point::new(1, 4),
5136                        primary: None,
5137                    },
5138                    ExcerptRange {
5139                        context: Point::new(1, 0)..Point::new(2, 4),
5140                        primary: None,
5141                    },
5142                ],
5143                cx,
5144            )
5145            .into_iter()
5146            .next();
5147        multibuffer
5148    });
5149    assert_eq!(
5150        multibuffer.read(cx).read(cx).text(),
5151        "aaaa\nbbbb\nbbbb\ncccc"
5152    );
5153    let (_, editor) = cx.add_window(Default::default(), |cx| {
5154        let mut editor = build_editor(multibuffer.clone(), cx);
5155        let snapshot = editor.snapshot(cx);
5156        editor.change_selections(None, cx, |s| {
5157            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
5158        });
5159        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
5160        assert_eq!(
5161            editor.selections.ranges(cx),
5162            [
5163                Point::new(1, 3)..Point::new(1, 3),
5164                Point::new(2, 1)..Point::new(2, 1),
5165            ]
5166        );
5167        editor
5168    });
5169
5170    // Refreshing selections is a no-op when excerpts haven't changed.
5171    editor.update(cx, |editor, cx| {
5172        editor.change_selections(None, cx, |s| s.refresh());
5173        assert_eq!(
5174            editor.selections.ranges(cx),
5175            [
5176                Point::new(1, 3)..Point::new(1, 3),
5177                Point::new(2, 1)..Point::new(2, 1),
5178            ]
5179        );
5180    });
5181
5182    multibuffer.update(cx, |multibuffer, cx| {
5183        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
5184    });
5185    editor.update(cx, |editor, cx| {
5186        // Removing an excerpt causes the first selection to become degenerate.
5187        assert_eq!(
5188            editor.selections.ranges(cx),
5189            [
5190                Point::new(0, 0)..Point::new(0, 0),
5191                Point::new(0, 1)..Point::new(0, 1)
5192            ]
5193        );
5194
5195        // Refreshing selections will relocate the first selection to the original buffer
5196        // location.
5197        editor.change_selections(None, cx, |s| s.refresh());
5198        assert_eq!(
5199            editor.selections.ranges(cx),
5200            [
5201                Point::new(0, 1)..Point::new(0, 1),
5202                Point::new(0, 3)..Point::new(0, 3)
5203            ]
5204        );
5205        assert!(editor.selections.pending_anchor().is_some());
5206    });
5207}
5208
5209#[gpui::test]
5210fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) {
5211    cx.set_global(Settings::test(cx));
5212    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5213    let mut excerpt1_id = None;
5214    let multibuffer = cx.add_model(|cx| {
5215        let mut multibuffer = MultiBuffer::new(0);
5216        excerpt1_id = multibuffer
5217            .push_excerpts(
5218                buffer.clone(),
5219                [
5220                    ExcerptRange {
5221                        context: Point::new(0, 0)..Point::new(1, 4),
5222                        primary: None,
5223                    },
5224                    ExcerptRange {
5225                        context: Point::new(1, 0)..Point::new(2, 4),
5226                        primary: None,
5227                    },
5228                ],
5229                cx,
5230            )
5231            .into_iter()
5232            .next();
5233        multibuffer
5234    });
5235    assert_eq!(
5236        multibuffer.read(cx).read(cx).text(),
5237        "aaaa\nbbbb\nbbbb\ncccc"
5238    );
5239    let (_, editor) = cx.add_window(Default::default(), |cx| {
5240        let mut editor = build_editor(multibuffer.clone(), cx);
5241        let snapshot = editor.snapshot(cx);
5242        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
5243        assert_eq!(
5244            editor.selections.ranges(cx),
5245            [Point::new(1, 3)..Point::new(1, 3)]
5246        );
5247        editor
5248    });
5249
5250    multibuffer.update(cx, |multibuffer, cx| {
5251        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
5252    });
5253    editor.update(cx, |editor, cx| {
5254        assert_eq!(
5255            editor.selections.ranges(cx),
5256            [Point::new(0, 0)..Point::new(0, 0)]
5257        );
5258
5259        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
5260        editor.change_selections(None, cx, |s| s.refresh());
5261        assert_eq!(
5262            editor.selections.ranges(cx),
5263            [Point::new(0, 3)..Point::new(0, 3)]
5264        );
5265        assert!(editor.selections.pending_anchor().is_some());
5266    });
5267}
5268
5269#[gpui::test]
5270async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
5271    cx.update(|cx| cx.set_global(Settings::test(cx)));
5272    let language = Arc::new(
5273        Language::new(
5274            LanguageConfig {
5275                brackets: BracketPairConfig {
5276                    pairs: vec![
5277                        BracketPair {
5278                            start: "{".to_string(),
5279                            end: "}".to_string(),
5280                            close: true,
5281                            newline: true,
5282                        },
5283                        BracketPair {
5284                            start: "/* ".to_string(),
5285                            end: " */".to_string(),
5286                            close: true,
5287                            newline: true,
5288                        },
5289                    ],
5290                    ..Default::default()
5291                },
5292                ..Default::default()
5293            },
5294            Some(tree_sitter_rust::language()),
5295        )
5296        .with_indents_query("")
5297        .unwrap(),
5298    );
5299
5300    let text = concat!(
5301        "{   }\n",     //
5302        "  x\n",       //
5303        "  /*   */\n", //
5304        "x\n",         //
5305        "{{} }\n",     //
5306    );
5307
5308    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
5309    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
5310    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
5311    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
5312        .await;
5313
5314    view.update(cx, |view, cx| {
5315        view.change_selections(None, cx, |s| {
5316            s.select_display_ranges([
5317                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
5318                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
5319                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
5320            ])
5321        });
5322        view.newline(&Newline, cx);
5323
5324        assert_eq!(
5325            view.buffer().read(cx).read(cx).text(),
5326            concat!(
5327                "{ \n",    // Suppress rustfmt
5328                "\n",      //
5329                "}\n",     //
5330                "  x\n",   //
5331                "  /* \n", //
5332                "  \n",    //
5333                "  */\n",  //
5334                "x\n",     //
5335                "{{} \n",  //
5336                "}\n",     //
5337            )
5338        );
5339    });
5340}
5341
5342#[gpui::test]
5343fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
5344    let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
5345
5346    cx.set_global(Settings::test(cx));
5347    let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
5348
5349    editor.update(cx, |editor, cx| {
5350        struct Type1;
5351        struct Type2;
5352
5353        let buffer = buffer.read(cx).snapshot(cx);
5354
5355        let anchor_range =
5356            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
5357
5358        editor.highlight_background::<Type1>(
5359            vec![
5360                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
5361                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
5362                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
5363                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
5364            ],
5365            |_| Color::red(),
5366            cx,
5367        );
5368        editor.highlight_background::<Type2>(
5369            vec![
5370                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
5371                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
5372                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
5373                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
5374            ],
5375            |_| Color::green(),
5376            cx,
5377        );
5378
5379        let snapshot = editor.snapshot(cx);
5380        let mut highlighted_ranges = editor.background_highlights_in_range(
5381            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
5382            &snapshot,
5383            cx.global::<Settings>().theme.as_ref(),
5384        );
5385        // Enforce a consistent ordering based on color without relying on the ordering of the
5386        // highlight's `TypeId` which is non-deterministic.
5387        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
5388        assert_eq!(
5389            highlighted_ranges,
5390            &[
5391                (
5392                    DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
5393                    Color::green(),
5394                ),
5395                (
5396                    DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
5397                    Color::green(),
5398                ),
5399                (
5400                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
5401                    Color::red(),
5402                ),
5403                (
5404                    DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
5405                    Color::red(),
5406                ),
5407            ]
5408        );
5409        assert_eq!(
5410            editor.background_highlights_in_range(
5411                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
5412                &snapshot,
5413                cx.global::<Settings>().theme.as_ref(),
5414            ),
5415            &[(
5416                DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
5417                Color::red(),
5418            )]
5419        );
5420    });
5421}
5422
5423#[gpui::test]
5424async fn test_following(cx: &mut gpui::TestAppContext) {
5425    Settings::test_async(cx);
5426    let fs = FakeFs::new(cx.background());
5427    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5428
5429    let buffer = project.update(cx, |project, cx| {
5430        let buffer = project
5431            .create_buffer(&sample_text(16, 8, 'a'), None, cx)
5432            .unwrap();
5433        cx.add_model(|cx| MultiBuffer::singleton(buffer, cx))
5434    });
5435    let (_, leader) = cx.add_window(|cx| build_editor(buffer.clone(), cx));
5436    let (_, follower) = cx.update(|cx| {
5437        cx.add_window(
5438            WindowOptions {
5439                bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
5440                ..Default::default()
5441            },
5442            |cx| build_editor(buffer.clone(), cx),
5443        )
5444    });
5445
5446    let is_still_following = Rc::new(RefCell::new(true));
5447    let pending_update = Rc::new(RefCell::new(None));
5448    follower.update(cx, {
5449        let update = pending_update.clone();
5450        let is_still_following = is_still_following.clone();
5451        |_, cx| {
5452            cx.subscribe(&leader, move |_, leader, event, cx| {
5453                leader
5454                    .read(cx)
5455                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
5456            })
5457            .detach();
5458
5459            cx.subscribe(&follower, move |_, _, event, cx| {
5460                if Editor::should_unfollow_on_event(event, cx) {
5461                    *is_still_following.borrow_mut() = false;
5462                }
5463            })
5464            .detach();
5465        }
5466    });
5467
5468    // Update the selections only
5469    leader.update(cx, |leader, cx| {
5470        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
5471    });
5472    follower
5473        .update(cx, |follower, cx| {
5474            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5475        })
5476        .await
5477        .unwrap();
5478    follower.read_with(cx, |follower, cx| {
5479        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
5480    });
5481    assert_eq!(*is_still_following.borrow(), true);
5482
5483    // Update the scroll position only
5484    leader.update(cx, |leader, cx| {
5485        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
5486    });
5487    follower
5488        .update(cx, |follower, cx| {
5489            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5490        })
5491        .await
5492        .unwrap();
5493    assert_eq!(
5494        follower.update(cx, |follower, cx| follower.scroll_position(cx)),
5495        vec2f(1.5, 3.5)
5496    );
5497    assert_eq!(*is_still_following.borrow(), true);
5498
5499    // Update the selections and scroll position. The follower's scroll position is updated
5500    // via autoscroll, not via the leader's exact scroll position.
5501    leader.update(cx, |leader, cx| {
5502        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
5503        leader.request_autoscroll(Autoscroll::newest(), cx);
5504        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
5505    });
5506    follower
5507        .update(cx, |follower, cx| {
5508            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5509        })
5510        .await
5511        .unwrap();
5512    follower.update(cx, |follower, cx| {
5513        assert_eq!(follower.scroll_position(cx), vec2f(1.5, 0.0));
5514        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
5515    });
5516    assert_eq!(*is_still_following.borrow(), true);
5517
5518    // Creating a pending selection that precedes another selection
5519    leader.update(cx, |leader, cx| {
5520        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
5521        leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
5522    });
5523    follower
5524        .update(cx, |follower, cx| {
5525            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5526        })
5527        .await
5528        .unwrap();
5529    follower.read_with(cx, |follower, cx| {
5530        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
5531    });
5532    assert_eq!(*is_still_following.borrow(), true);
5533
5534    // Extend the pending selection so that it surrounds another selection
5535    leader.update(cx, |leader, cx| {
5536        leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
5537    });
5538    follower
5539        .update(cx, |follower, cx| {
5540            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5541        })
5542        .await
5543        .unwrap();
5544    follower.read_with(cx, |follower, cx| {
5545        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
5546    });
5547
5548    // Scrolling locally breaks the follow
5549    follower.update(cx, |follower, cx| {
5550        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
5551        follower.set_scroll_anchor(
5552            ScrollAnchor {
5553                top_anchor,
5554                offset: vec2f(0.0, 0.5),
5555            },
5556            cx,
5557        );
5558    });
5559    assert_eq!(*is_still_following.borrow(), false);
5560}
5561
5562#[gpui::test]
5563async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
5564    Settings::test_async(cx);
5565    let fs = FakeFs::new(cx.background());
5566    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5567    let (_, pane) = cx.add_window(|cx| Pane::new(0, None, || unimplemented!(), cx));
5568
5569    let leader = pane.update(cx, |_, cx| {
5570        let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
5571        cx.add_view(|cx| build_editor(multibuffer.clone(), cx))
5572    });
5573
5574    // Start following the editor when it has no excerpts.
5575    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
5576    let follower_1 = cx
5577        .update(|cx| {
5578            Editor::from_state_proto(
5579                pane.clone(),
5580                project.clone(),
5581                ViewId {
5582                    creator: Default::default(),
5583                    id: 0,
5584                },
5585                &mut state_message,
5586                cx,
5587            )
5588        })
5589        .unwrap()
5590        .await
5591        .unwrap();
5592
5593    let update_message = Rc::new(RefCell::new(None));
5594    follower_1.update(cx, {
5595        let update = update_message.clone();
5596        |_, cx| {
5597            cx.subscribe(&leader, move |_, leader, event, cx| {
5598                leader
5599                    .read(cx)
5600                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
5601            })
5602            .detach();
5603        }
5604    });
5605
5606    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
5607        (
5608            project
5609                .create_buffer("abc\ndef\nghi\njkl\n", None, cx)
5610                .unwrap(),
5611            project
5612                .create_buffer("mno\npqr\nstu\nvwx\n", None, cx)
5613                .unwrap(),
5614        )
5615    });
5616
5617    // Insert some excerpts.
5618    leader.update(cx, |leader, cx| {
5619        leader.buffer.update(cx, |multibuffer, cx| {
5620            let excerpt_ids = multibuffer.push_excerpts(
5621                buffer_1.clone(),
5622                [
5623                    ExcerptRange {
5624                        context: 1..6,
5625                        primary: None,
5626                    },
5627                    ExcerptRange {
5628                        context: 12..15,
5629                        primary: None,
5630                    },
5631                    ExcerptRange {
5632                        context: 0..3,
5633                        primary: None,
5634                    },
5635                ],
5636                cx,
5637            );
5638            multibuffer.insert_excerpts_after(
5639                excerpt_ids[0],
5640                buffer_2.clone(),
5641                [
5642                    ExcerptRange {
5643                        context: 8..12,
5644                        primary: None,
5645                    },
5646                    ExcerptRange {
5647                        context: 0..6,
5648                        primary: None,
5649                    },
5650                ],
5651                cx,
5652            );
5653        });
5654    });
5655
5656    // Apply the update of adding the excerpts.
5657    follower_1
5658        .update(cx, |follower, cx| {
5659            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
5660        })
5661        .await
5662        .unwrap();
5663    assert_eq!(
5664        follower_1.read_with(cx, Editor::text),
5665        leader.read_with(cx, Editor::text)
5666    );
5667    update_message.borrow_mut().take();
5668
5669    // Start following separately after it already has excerpts.
5670    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
5671    let follower_2 = cx
5672        .update(|cx| {
5673            Editor::from_state_proto(
5674                pane.clone(),
5675                project.clone(),
5676                ViewId {
5677                    creator: Default::default(),
5678                    id: 0,
5679                },
5680                &mut state_message,
5681                cx,
5682            )
5683        })
5684        .unwrap()
5685        .await
5686        .unwrap();
5687    assert_eq!(
5688        follower_2.read_with(cx, Editor::text),
5689        leader.read_with(cx, Editor::text)
5690    );
5691
5692    // Remove some excerpts.
5693    leader.update(cx, |leader, cx| {
5694        leader.buffer.update(cx, |multibuffer, cx| {
5695            let excerpt_ids = multibuffer.excerpt_ids();
5696            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
5697            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
5698        });
5699    });
5700
5701    // Apply the update of removing the excerpts.
5702    follower_1
5703        .update(cx, |follower, cx| {
5704            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
5705        })
5706        .await
5707        .unwrap();
5708    follower_2
5709        .update(cx, |follower, cx| {
5710            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
5711        })
5712        .await
5713        .unwrap();
5714    update_message.borrow_mut().take();
5715    assert_eq!(
5716        follower_1.read_with(cx, Editor::text),
5717        leader.read_with(cx, Editor::text)
5718    );
5719}
5720
5721#[test]
5722fn test_combine_syntax_and_fuzzy_match_highlights() {
5723    let string = "abcdefghijklmnop";
5724    let syntax_ranges = [
5725        (
5726            0..3,
5727            HighlightStyle {
5728                color: Some(Color::red()),
5729                ..Default::default()
5730            },
5731        ),
5732        (
5733            4..8,
5734            HighlightStyle {
5735                color: Some(Color::green()),
5736                ..Default::default()
5737            },
5738        ),
5739    ];
5740    let match_indices = [4, 6, 7, 8];
5741    assert_eq!(
5742        combine_syntax_and_fuzzy_match_highlights(
5743            string,
5744            Default::default(),
5745            syntax_ranges.into_iter(),
5746            &match_indices,
5747        ),
5748        &[
5749            (
5750                0..3,
5751                HighlightStyle {
5752                    color: Some(Color::red()),
5753                    ..Default::default()
5754                },
5755            ),
5756            (
5757                4..5,
5758                HighlightStyle {
5759                    color: Some(Color::green()),
5760                    weight: Some(fonts::Weight::BOLD),
5761                    ..Default::default()
5762                },
5763            ),
5764            (
5765                5..6,
5766                HighlightStyle {
5767                    color: Some(Color::green()),
5768                    ..Default::default()
5769                },
5770            ),
5771            (
5772                6..8,
5773                HighlightStyle {
5774                    color: Some(Color::green()),
5775                    weight: Some(fonts::Weight::BOLD),
5776                    ..Default::default()
5777                },
5778            ),
5779            (
5780                8..9,
5781                HighlightStyle {
5782                    weight: Some(fonts::Weight::BOLD),
5783                    ..Default::default()
5784                },
5785            ),
5786        ]
5787    );
5788}
5789
5790#[gpui::test]
5791async fn go_to_hunk(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
5792    let mut cx = EditorTestContext::new(cx);
5793
5794    let diff_base = r#"
5795        use some::mod;
5796
5797        const A: u32 = 42;
5798
5799        fn main() {
5800            println!("hello");
5801
5802            println!("world");
5803        }
5804        "#
5805    .unindent();
5806
5807    // Edits are modified, removed, modified, added
5808    cx.set_state(
5809        &r#"
5810        use some::modified;
5811
5812        ˇ
5813        fn main() {
5814            println!("hello there");
5815
5816            println!("around the");
5817            println!("world");
5818        }
5819        "#
5820        .unindent(),
5821    );
5822
5823    cx.set_diff_base(Some(&diff_base));
5824    deterministic.run_until_parked();
5825
5826    cx.update_editor(|editor, cx| {
5827        //Wrap around the bottom of the buffer
5828        for _ in 0..3 {
5829            editor.go_to_hunk(&GoToHunk, cx);
5830        }
5831    });
5832
5833    cx.assert_editor_state(
5834        &r#"
5835        ˇuse some::modified;
5836
5837
5838        fn main() {
5839            println!("hello there");
5840
5841            println!("around the");
5842            println!("world");
5843        }
5844        "#
5845        .unindent(),
5846    );
5847
5848    cx.update_editor(|editor, cx| {
5849        //Wrap around the top of the buffer
5850        for _ in 0..2 {
5851            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
5852        }
5853    });
5854
5855    cx.assert_editor_state(
5856        &r#"
5857        use some::modified;
5858
5859
5860        fn main() {
5861        ˇ    println!("hello there");
5862
5863            println!("around the");
5864            println!("world");
5865        }
5866        "#
5867        .unindent(),
5868    );
5869
5870    cx.update_editor(|editor, cx| {
5871        editor.fold(&Fold, cx);
5872
5873        //Make sure that the fold only gets one hunk
5874        for _ in 0..4 {
5875            editor.go_to_hunk(&GoToHunk, cx);
5876        }
5877    });
5878
5879    cx.assert_editor_state(
5880        &r#"
5881        ˇuse some::modified;
5882
5883
5884        fn main() {
5885            println!("hello there");
5886
5887            println!("around the");
5888            println!("world");
5889        }
5890        "#
5891        .unindent(),
5892    );
5893}
5894
5895#[test]
5896fn test_split_words() {
5897    fn split<'a>(text: &'a str) -> Vec<&'a str> {
5898        split_words(text).collect()
5899    }
5900
5901    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
5902    assert_eq!(split("hello_world"), &["hello_", "world"]);
5903    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
5904    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
5905    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
5906    assert_eq!(split("helloworld"), &["helloworld"]);
5907}
5908
5909#[gpui::test]
5910async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
5911    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
5912    let mut assert = |before, after| {
5913        let _state_context = cx.set_state(before);
5914        cx.update_editor(|editor, cx| {
5915            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
5916        });
5917        cx.assert_editor_state(after);
5918    };
5919
5920    // Outside bracket jumps to outside of matching bracket
5921    assert("console.logˇ(var);", "console.log(var)ˇ;");
5922    assert("console.log(var)ˇ;", "console.logˇ(var);");
5923
5924    // Inside bracket jumps to inside of matching bracket
5925    assert("console.log(ˇvar);", "console.log(varˇ);");
5926    assert("console.log(varˇ);", "console.log(ˇvar);");
5927
5928    // When outside a bracket and inside, favor jumping to the inside bracket
5929    assert(
5930        "console.log('foo', [1, 2, 3]ˇ);",
5931        "console.log(ˇ'foo', [1, 2, 3]);",
5932    );
5933    assert(
5934        "console.log(ˇ'foo', [1, 2, 3]);",
5935        "console.log('foo', [1, 2, 3]ˇ);",
5936    );
5937
5938    // Bias forward if two options are equally likely
5939    assert(
5940        "let result = curried_fun()ˇ();",
5941        "let result = curried_fun()()ˇ;",
5942    );
5943
5944    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
5945    assert(
5946        indoc! {"
5947            function test() {
5948                console.log('test')ˇ
5949            }"},
5950        indoc! {"
5951            function test() {
5952                console.logˇ('test')
5953            }"},
5954    );
5955}
5956
5957fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
5958    let point = DisplayPoint::new(row as u32, column as u32);
5959    point..point
5960}
5961
5962fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
5963    let (text, ranges) = marked_text_ranges(marked_text, true);
5964    assert_eq!(view.text(cx), text);
5965    assert_eq!(
5966        view.selections.ranges(cx),
5967        ranges,
5968        "Assert selections are {}",
5969        marked_text
5970    );
5971}