editor_tests.rs

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