editor_tests.rs

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