editor_tests.rs

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