editor_tests.rs

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