editor_tests.rs

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