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            ],
3045            autoclose_before: "})]".to_string(),
3046            ..Default::default()
3047        },
3048        Some(tree_sitter_rust::language()),
3049    ));
3050
3051    let registry = Arc::new(LanguageRegistry::test());
3052    registry.add(language.clone());
3053    cx.update_buffer(|buffer, cx| {
3054        buffer.set_language_registry(registry);
3055        buffer.set_language(Some(language), cx);
3056    });
3057
3058    cx.set_state(
3059        &r#"
3060            🏀ˇ
3061            εˇ
3062            ❤️ˇ
3063        "#
3064        .unindent(),
3065    );
3066
3067    // autoclose multiple nested brackets at multiple cursors
3068    cx.update_editor(|view, cx| {
3069        view.handle_input("{", cx);
3070        view.handle_input("{", cx);
3071        view.handle_input("{", cx);
3072    });
3073    cx.assert_editor_state(
3074        &"
3075            🏀{{{ˇ}}}
3076            ε{{{ˇ}}}
3077            ❤️{{{ˇ}}}
3078        "
3079        .unindent(),
3080    );
3081
3082    // insert a different closing bracket
3083    cx.update_editor(|view, cx| {
3084        view.handle_input(")", cx);
3085    });
3086    cx.assert_editor_state(
3087        &"
3088            🏀{{{)ˇ}}}
3089            ε{{{)ˇ}}}
3090            ❤️{{{)ˇ}}}
3091        "
3092        .unindent(),
3093    );
3094
3095    // skip over the auto-closed brackets when typing a closing bracket
3096    cx.update_editor(|view, cx| {
3097        view.move_right(&MoveRight, cx);
3098        view.handle_input("}", cx);
3099        view.handle_input("}", cx);
3100        view.handle_input("}", cx);
3101    });
3102    cx.assert_editor_state(
3103        &"
3104            🏀{{{)}}}}ˇ
3105            ε{{{)}}}}ˇ
3106            ❤️{{{)}}}}ˇ
3107        "
3108        .unindent(),
3109    );
3110
3111    // autoclose multi-character pairs
3112    cx.set_state(
3113        &"
3114            ˇ
3115            ˇ
3116        "
3117        .unindent(),
3118    );
3119    cx.update_editor(|view, cx| {
3120        view.handle_input("/", cx);
3121        view.handle_input("*", cx);
3122    });
3123    cx.assert_editor_state(
3124        &"
3125            /*ˇ */
3126            /*ˇ */
3127        "
3128        .unindent(),
3129    );
3130
3131    // one cursor autocloses a multi-character pair, one cursor
3132    // does not autoclose.
3133    cx.set_state(
3134        &"
31353136            ˇ
3137        "
3138        .unindent(),
3139    );
3140    cx.update_editor(|view, cx| view.handle_input("*", cx));
3141    cx.assert_editor_state(
3142        &"
3143            /*ˇ */
31443145        "
3146        .unindent(),
3147    );
3148
3149    // Don't autoclose if the next character isn't whitespace and isn't
3150    // listed in the language's "autoclose_before" section.
3151    cx.set_state("ˇa b");
3152    cx.update_editor(|view, cx| view.handle_input("{", cx));
3153    cx.assert_editor_state("{ˇa b");
3154
3155    // Don't autoclose if `close` is false for the bracket pair
3156    cx.set_state("ˇ");
3157    cx.update_editor(|view, cx| view.handle_input("[", cx));
3158    cx.assert_editor_state("");
3159
3160    // Surround with brackets if text is selected
3161    cx.set_state("«aˇ» b");
3162    cx.update_editor(|view, cx| view.handle_input("{", cx));
3163    cx.assert_editor_state("{«aˇ»} b");
3164}
3165
3166#[gpui::test]
3167async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
3168    let mut cx = EditorTestContext::new(cx);
3169
3170    let html_language = Arc::new(
3171        Language::new(
3172            LanguageConfig {
3173                name: "HTML".into(),
3174                brackets: vec![
3175                    BracketPair {
3176                        start: "<".into(),
3177                        end: ">".into(),
3178                        close: true,
3179                        ..Default::default()
3180                    },
3181                    BracketPair {
3182                        start: "{".into(),
3183                        end: "}".into(),
3184                        close: true,
3185                        ..Default::default()
3186                    },
3187                    BracketPair {
3188                        start: "(".into(),
3189                        end: ")".into(),
3190                        close: true,
3191                        ..Default::default()
3192                    },
3193                ],
3194                autoclose_before: "})]>".into(),
3195                ..Default::default()
3196            },
3197            Some(tree_sitter_html::language()),
3198        )
3199        .with_injection_query(
3200            r#"
3201            (script_element
3202                (raw_text) @content
3203                (#set! "language" "javascript"))
3204            "#,
3205        )
3206        .unwrap(),
3207    );
3208
3209    let javascript_language = Arc::new(Language::new(
3210        LanguageConfig {
3211            name: "JavaScript".into(),
3212            brackets: vec![
3213                BracketPair {
3214                    start: "/*".into(),
3215                    end: " */".into(),
3216                    close: true,
3217                    ..Default::default()
3218                },
3219                BracketPair {
3220                    start: "{".into(),
3221                    end: "}".into(),
3222                    close: true,
3223                    ..Default::default()
3224                },
3225                BracketPair {
3226                    start: "(".into(),
3227                    end: ")".into(),
3228                    close: true,
3229                    ..Default::default()
3230                },
3231            ],
3232            autoclose_before: "})]>".into(),
3233            ..Default::default()
3234        },
3235        Some(tree_sitter_javascript::language()),
3236    ));
3237
3238    let registry = Arc::new(LanguageRegistry::test());
3239    registry.add(html_language.clone());
3240    registry.add(javascript_language.clone());
3241
3242    cx.update_buffer(|buffer, cx| {
3243        buffer.set_language_registry(registry);
3244        buffer.set_language(Some(html_language), cx);
3245    });
3246
3247    cx.set_state(
3248        &r#"
3249            <body>ˇ
3250                <script>
3251                    var x = 1;ˇ
3252                </script>
3253            </body>ˇ
3254        "#
3255        .unindent(),
3256    );
3257
3258    // Precondition: different languages are active at different locations.
3259    cx.update_editor(|editor, cx| {
3260        let snapshot = editor.snapshot(cx);
3261        let cursors = editor.selections.ranges::<usize>(cx);
3262        let languages = cursors
3263            .iter()
3264            .map(|c| snapshot.language_at(c.start).unwrap().name())
3265            .collect::<Vec<_>>();
3266        assert_eq!(
3267            languages,
3268            &["HTML".into(), "JavaScript".into(), "HTML".into()]
3269        );
3270    });
3271
3272    // Angle brackets autoclose in HTML, but not JavaScript.
3273    cx.update_editor(|editor, cx| {
3274        editor.handle_input("<", cx);
3275        editor.handle_input("a", cx);
3276    });
3277    cx.assert_editor_state(
3278        &r#"
3279            <body><aˇ>
3280                <script>
3281                    var x = 1;<aˇ
3282                </script>
3283            </body><aˇ>
3284        "#
3285        .unindent(),
3286    );
3287
3288    // Curly braces and parens autoclose in both HTML and JavaScript.
3289    cx.update_editor(|editor, cx| {
3290        editor.handle_input(" b=", cx);
3291        editor.handle_input("{", cx);
3292        editor.handle_input("c", cx);
3293        editor.handle_input("(", cx);
3294    });
3295    cx.assert_editor_state(
3296        &r#"
3297            <body><a b={c(ˇ)}>
3298                <script>
3299                    var x = 1;<a b={c(ˇ)}
3300                </script>
3301            </body><a b={c(ˇ)}>
3302        "#
3303        .unindent(),
3304    );
3305
3306    // Brackets that were already autoclosed are skipped.
3307    cx.update_editor(|editor, cx| {
3308        editor.handle_input(")", cx);
3309        editor.handle_input("d", cx);
3310        editor.handle_input("}", cx);
3311    });
3312    cx.assert_editor_state(
3313        &r#"
3314            <body><a b={c()d}ˇ>
3315                <script>
3316                    var x = 1;<a b={c()d}ˇ
3317                </script>
3318            </body><a b={c()d}ˇ>
3319        "#
3320        .unindent(),
3321    );
3322    cx.update_editor(|editor, 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
3336    // Reset
3337    cx.set_state(
3338        &r#"
3339            <body>ˇ
3340                <script>
3341                    var x = 1;ˇ
3342                </script>
3343            </body>ˇ
3344        "#
3345        .unindent(),
3346    );
3347
3348    cx.update_editor(|editor, cx| {
3349        editor.handle_input("<", cx);
3350    });
3351    cx.assert_editor_state(
3352        &r#"
3353            <body><ˇ>
3354                <script>
3355                    var x = 1;<ˇ
3356                </script>
3357            </body><ˇ>
3358        "#
3359        .unindent(),
3360    );
3361
3362    // When backspacing, the closing angle brackets are removed.
3363    cx.update_editor(|editor, cx| {
3364        editor.backspace(&Backspace, cx);
3365    });
3366    cx.assert_editor_state(
3367        &r#"
3368            <body>ˇ
3369                <script>
3370                    var x = 1;ˇ
3371                </script>
3372            </body>ˇ
3373        "#
3374        .unindent(),
3375    );
3376
3377    // Block comments autoclose in JavaScript, but not HTML.
3378    cx.update_editor(|editor, cx| {
3379        editor.handle_input("/", cx);
3380        editor.handle_input("*", cx);
3381    });
3382    cx.assert_editor_state(
3383        &r#"
3384            <body>/*ˇ
3385                <script>
3386                    var x = 1;/*ˇ */
3387                </script>
3388            </body>/*ˇ
3389        "#
3390        .unindent(),
3391    );
3392}
3393
3394#[gpui::test]
3395async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
3396    cx.update(|cx| cx.set_global(Settings::test(cx)));
3397    let language = Arc::new(Language::new(
3398        LanguageConfig {
3399            brackets: vec![BracketPair {
3400                start: "{".to_string(),
3401                end: "}".to_string(),
3402                close: true,
3403                newline: true,
3404            }],
3405            ..Default::default()
3406        },
3407        Some(tree_sitter_rust::language()),
3408    ));
3409
3410    let text = r#"
3411        a
3412        b
3413        c
3414    "#
3415    .unindent();
3416
3417    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3418    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3419    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
3420    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
3421        .await;
3422
3423    view.update(cx, |view, cx| {
3424        view.change_selections(None, cx, |s| {
3425            s.select_display_ranges([
3426                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3427                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
3428                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
3429            ])
3430        });
3431
3432        view.handle_input("{", cx);
3433        view.handle_input("{", cx);
3434        view.handle_input("{", cx);
3435        assert_eq!(
3436            view.text(cx),
3437            "
3438                {{{a}}}
3439                {{{b}}}
3440                {{{c}}}
3441            "
3442            .unindent()
3443        );
3444        assert_eq!(
3445            view.selections.display_ranges(cx),
3446            [
3447                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
3448                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
3449                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
3450            ]
3451        );
3452
3453        view.undo(&Undo, cx);
3454        assert_eq!(
3455            view.text(cx),
3456            "
3457                a
3458                b
3459                c
3460            "
3461            .unindent()
3462        );
3463        assert_eq!(
3464            view.selections.display_ranges(cx),
3465            [
3466                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3467                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
3468                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
3469            ]
3470        );
3471    });
3472}
3473
3474#[gpui::test]
3475async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
3476    cx.update(|cx| cx.set_global(Settings::test(cx)));
3477    let language = Arc::new(Language::new(
3478        LanguageConfig {
3479            brackets: vec![BracketPair {
3480                start: "{".to_string(),
3481                end: "}".to_string(),
3482                close: true,
3483                newline: true,
3484            }],
3485            autoclose_before: "}".to_string(),
3486            ..Default::default()
3487        },
3488        Some(tree_sitter_rust::language()),
3489    ));
3490
3491    let text = r#"
3492        a
3493        b
3494        c
3495    "#
3496    .unindent();
3497
3498    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3499    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3500    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3501    editor
3502        .condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
3503        .await;
3504
3505    editor.update(cx, |editor, cx| {
3506        editor.change_selections(None, cx, |s| {
3507            s.select_ranges([
3508                Point::new(0, 1)..Point::new(0, 1),
3509                Point::new(1, 1)..Point::new(1, 1),
3510                Point::new(2, 1)..Point::new(2, 1),
3511            ])
3512        });
3513
3514        editor.handle_input("{", cx);
3515        editor.handle_input("{", cx);
3516        editor.handle_input("_", cx);
3517        assert_eq!(
3518            editor.text(cx),
3519            "
3520                a{{_}}
3521                b{{_}}
3522                c{{_}}
3523            "
3524            .unindent()
3525        );
3526        assert_eq!(
3527            editor.selections.ranges::<Point>(cx),
3528            [
3529                Point::new(0, 4)..Point::new(0, 4),
3530                Point::new(1, 4)..Point::new(1, 4),
3531                Point::new(2, 4)..Point::new(2, 4)
3532            ]
3533        );
3534
3535        editor.backspace(&Default::default(), cx);
3536        editor.backspace(&Default::default(), cx);
3537        assert_eq!(
3538            editor.text(cx),
3539            "
3540                a{}
3541                b{}
3542                c{}
3543            "
3544            .unindent()
3545        );
3546        assert_eq!(
3547            editor.selections.ranges::<Point>(cx),
3548            [
3549                Point::new(0, 2)..Point::new(0, 2),
3550                Point::new(1, 2)..Point::new(1, 2),
3551                Point::new(2, 2)..Point::new(2, 2)
3552            ]
3553        );
3554
3555        editor.delete_to_previous_word_start(&Default::default(), cx);
3556        assert_eq!(
3557            editor.text(cx),
3558            "
3559                a
3560                b
3561                c
3562            "
3563            .unindent()
3564        );
3565        assert_eq!(
3566            editor.selections.ranges::<Point>(cx),
3567            [
3568                Point::new(0, 1)..Point::new(0, 1),
3569                Point::new(1, 1)..Point::new(1, 1),
3570                Point::new(2, 1)..Point::new(2, 1)
3571            ]
3572        );
3573    });
3574}
3575
3576#[gpui::test]
3577async fn test_snippets(cx: &mut gpui::TestAppContext) {
3578    cx.update(|cx| cx.set_global(Settings::test(cx)));
3579
3580    let (text, insertion_ranges) = marked_text_ranges(
3581        indoc! {"
3582            a.ˇ b
3583            a.ˇ b
3584            a.ˇ b
3585        "},
3586        false,
3587    );
3588
3589    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
3590    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3591
3592    editor.update(cx, |editor, cx| {
3593        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
3594
3595        editor
3596            .insert_snippet(&insertion_ranges, snippet, cx)
3597            .unwrap();
3598
3599        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
3600            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
3601            assert_eq!(editor.text(cx), expected_text);
3602            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
3603        }
3604
3605        assert(
3606            editor,
3607            cx,
3608            indoc! {"
3609                a.f(«one», two, «three») b
3610                a.f(«one», two, «three») b
3611                a.f(«one», two, «three») b
3612            "},
3613        );
3614
3615        // Can't move earlier than the first tab stop
3616        assert!(!editor.move_to_prev_snippet_tabstop(cx));
3617        assert(
3618            editor,
3619            cx,
3620            indoc! {"
3621                a.f(«one», two, «three») b
3622                a.f(«one», two, «three») b
3623                a.f(«one», two, «three») b
3624            "},
3625        );
3626
3627        assert!(editor.move_to_next_snippet_tabstop(cx));
3628        assert(
3629            editor,
3630            cx,
3631            indoc! {"
3632                a.f(one, «two», three) b
3633                a.f(one, «two», three) b
3634                a.f(one, «two», three) b
3635            "},
3636        );
3637
3638        editor.move_to_prev_snippet_tabstop(cx);
3639        assert(
3640            editor,
3641            cx,
3642            indoc! {"
3643                a.f(«one», two, «three») b
3644                a.f(«one», two, «three») b
3645                a.f(«one», two, «three») b
3646            "},
3647        );
3648
3649        assert!(editor.move_to_next_snippet_tabstop(cx));
3650        assert(
3651            editor,
3652            cx,
3653            indoc! {"
3654                a.f(one, «two», three) b
3655                a.f(one, «two», three) b
3656                a.f(one, «two», three) b
3657            "},
3658        );
3659        assert!(editor.move_to_next_snippet_tabstop(cx));
3660        assert(
3661            editor,
3662            cx,
3663            indoc! {"
3664                a.f(one, two, three)ˇ b
3665                a.f(one, two, three)ˇ b
3666                a.f(one, two, three)ˇ b
3667            "},
3668        );
3669
3670        // As soon as the last tab stop is reached, snippet state is gone
3671        editor.move_to_prev_snippet_tabstop(cx);
3672        assert(
3673            editor,
3674            cx,
3675            indoc! {"
3676                a.f(one, two, three)ˇ b
3677                a.f(one, two, three)ˇ b
3678                a.f(one, two, three)ˇ b
3679            "},
3680        );
3681    });
3682}
3683
3684#[gpui::test]
3685async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
3686    cx.foreground().forbid_parking();
3687
3688    let mut language = Language::new(
3689        LanguageConfig {
3690            name: "Rust".into(),
3691            path_suffixes: vec!["rs".to_string()],
3692            ..Default::default()
3693        },
3694        Some(tree_sitter_rust::language()),
3695    );
3696    let mut fake_servers = language
3697        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3698            capabilities: lsp::ServerCapabilities {
3699                document_formatting_provider: Some(lsp::OneOf::Left(true)),
3700                ..Default::default()
3701            },
3702            ..Default::default()
3703        }))
3704        .await;
3705
3706    let fs = FakeFs::new(cx.background());
3707    fs.insert_file("/file.rs", Default::default()).await;
3708
3709    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
3710    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
3711    let buffer = project
3712        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
3713        .await
3714        .unwrap();
3715
3716    cx.foreground().start_waiting();
3717    let fake_server = fake_servers.next().await.unwrap();
3718
3719    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3720    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3721    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
3722    assert!(cx.read(|cx| editor.is_dirty(cx)));
3723
3724    let save = cx.update(|cx| editor.save(project.clone(), cx));
3725    fake_server
3726        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
3727            assert_eq!(
3728                params.text_document.uri,
3729                lsp::Url::from_file_path("/file.rs").unwrap()
3730            );
3731            assert_eq!(params.options.tab_size, 4);
3732            Ok(Some(vec![lsp::TextEdit::new(
3733                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
3734                ", ".to_string(),
3735            )]))
3736        })
3737        .next()
3738        .await;
3739    cx.foreground().start_waiting();
3740    save.await.unwrap();
3741    assert_eq!(
3742        editor.read_with(cx, |editor, cx| editor.text(cx)),
3743        "one, two\nthree\n"
3744    );
3745    assert!(!cx.read(|cx| editor.is_dirty(cx)));
3746
3747    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
3748    assert!(cx.read(|cx| editor.is_dirty(cx)));
3749
3750    // Ensure we can still save even if formatting hangs.
3751    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
3752        assert_eq!(
3753            params.text_document.uri,
3754            lsp::Url::from_file_path("/file.rs").unwrap()
3755        );
3756        futures::future::pending::<()>().await;
3757        unreachable!()
3758    });
3759    let save = cx.update(|cx| editor.save(project.clone(), cx));
3760    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
3761    cx.foreground().start_waiting();
3762    save.await.unwrap();
3763    assert_eq!(
3764        editor.read_with(cx, |editor, cx| editor.text(cx)),
3765        "one\ntwo\nthree\n"
3766    );
3767    assert!(!cx.read(|cx| editor.is_dirty(cx)));
3768
3769    // Set rust language override and assert overriden tabsize is sent to language server
3770    cx.update(|cx| {
3771        cx.update_global::<Settings, _, _>(|settings, _| {
3772            settings.language_overrides.insert(
3773                "Rust".into(),
3774                EditorSettings {
3775                    tab_size: Some(8.try_into().unwrap()),
3776                    ..Default::default()
3777                },
3778            );
3779        })
3780    });
3781
3782    let save = cx.update(|cx| editor.save(project.clone(), cx));
3783    fake_server
3784        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
3785            assert_eq!(
3786                params.text_document.uri,
3787                lsp::Url::from_file_path("/file.rs").unwrap()
3788            );
3789            assert_eq!(params.options.tab_size, 8);
3790            Ok(Some(vec![]))
3791        })
3792        .next()
3793        .await;
3794    cx.foreground().start_waiting();
3795    save.await.unwrap();
3796}
3797
3798#[gpui::test]
3799async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
3800    cx.foreground().forbid_parking();
3801
3802    let mut language = Language::new(
3803        LanguageConfig {
3804            name: "Rust".into(),
3805            path_suffixes: vec!["rs".to_string()],
3806            ..Default::default()
3807        },
3808        Some(tree_sitter_rust::language()),
3809    );
3810    let mut fake_servers = language
3811        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3812            capabilities: lsp::ServerCapabilities {
3813                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
3814                ..Default::default()
3815            },
3816            ..Default::default()
3817        }))
3818        .await;
3819
3820    let fs = FakeFs::new(cx.background());
3821    fs.insert_file("/file.rs", Default::default()).await;
3822
3823    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
3824    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
3825    let buffer = project
3826        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
3827        .await
3828        .unwrap();
3829
3830    cx.foreground().start_waiting();
3831    let fake_server = fake_servers.next().await.unwrap();
3832
3833    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3834    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3835    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
3836    assert!(cx.read(|cx| editor.is_dirty(cx)));
3837
3838    let save = cx.update(|cx| editor.save(project.clone(), cx));
3839    fake_server
3840        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
3841            assert_eq!(
3842                params.text_document.uri,
3843                lsp::Url::from_file_path("/file.rs").unwrap()
3844            );
3845            assert_eq!(params.options.tab_size, 4);
3846            Ok(Some(vec![lsp::TextEdit::new(
3847                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
3848                ", ".to_string(),
3849            )]))
3850        })
3851        .next()
3852        .await;
3853    cx.foreground().start_waiting();
3854    save.await.unwrap();
3855    assert_eq!(
3856        editor.read_with(cx, |editor, cx| editor.text(cx)),
3857        "one, two\nthree\n"
3858    );
3859    assert!(!cx.read(|cx| editor.is_dirty(cx)));
3860
3861    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
3862    assert!(cx.read(|cx| editor.is_dirty(cx)));
3863
3864    // Ensure we can still save even if formatting hangs.
3865    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
3866        move |params, _| async move {
3867            assert_eq!(
3868                params.text_document.uri,
3869                lsp::Url::from_file_path("/file.rs").unwrap()
3870            );
3871            futures::future::pending::<()>().await;
3872            unreachable!()
3873        },
3874    );
3875    let save = cx.update(|cx| editor.save(project.clone(), cx));
3876    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
3877    cx.foreground().start_waiting();
3878    save.await.unwrap();
3879    assert_eq!(
3880        editor.read_with(cx, |editor, cx| editor.text(cx)),
3881        "one\ntwo\nthree\n"
3882    );
3883    assert!(!cx.read(|cx| editor.is_dirty(cx)));
3884
3885    // Set rust language override and assert overriden tabsize is sent to language server
3886    cx.update(|cx| {
3887        cx.update_global::<Settings, _, _>(|settings, _| {
3888            settings.language_overrides.insert(
3889                "Rust".into(),
3890                EditorSettings {
3891                    tab_size: Some(8.try_into().unwrap()),
3892                    ..Default::default()
3893                },
3894            );
3895        })
3896    });
3897
3898    let save = cx.update(|cx| editor.save(project.clone(), cx));
3899    fake_server
3900        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
3901            assert_eq!(
3902                params.text_document.uri,
3903                lsp::Url::from_file_path("/file.rs").unwrap()
3904            );
3905            assert_eq!(params.options.tab_size, 8);
3906            Ok(Some(vec![]))
3907        })
3908        .next()
3909        .await;
3910    cx.foreground().start_waiting();
3911    save.await.unwrap();
3912}
3913
3914#[gpui::test]
3915async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
3916    cx.foreground().forbid_parking();
3917
3918    let mut language = Language::new(
3919        LanguageConfig {
3920            name: "Rust".into(),
3921            path_suffixes: vec!["rs".to_string()],
3922            ..Default::default()
3923        },
3924        Some(tree_sitter_rust::language()),
3925    );
3926    let mut fake_servers = language
3927        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3928            capabilities: lsp::ServerCapabilities {
3929                document_formatting_provider: Some(lsp::OneOf::Left(true)),
3930                ..Default::default()
3931            },
3932            ..Default::default()
3933        }))
3934        .await;
3935
3936    let fs = FakeFs::new(cx.background());
3937    fs.insert_file("/file.rs", Default::default()).await;
3938
3939    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
3940    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
3941    let buffer = project
3942        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
3943        .await
3944        .unwrap();
3945
3946    cx.foreground().start_waiting();
3947    let fake_server = fake_servers.next().await.unwrap();
3948
3949    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3950    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3951    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
3952
3953    let format = editor.update(cx, |editor, cx| editor.perform_format(project.clone(), cx));
3954    fake_server
3955        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
3956            assert_eq!(
3957                params.text_document.uri,
3958                lsp::Url::from_file_path("/file.rs").unwrap()
3959            );
3960            assert_eq!(params.options.tab_size, 4);
3961            Ok(Some(vec![lsp::TextEdit::new(
3962                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
3963                ", ".to_string(),
3964            )]))
3965        })
3966        .next()
3967        .await;
3968    cx.foreground().start_waiting();
3969    format.await.unwrap();
3970    assert_eq!(
3971        editor.read_with(cx, |editor, cx| editor.text(cx)),
3972        "one, two\nthree\n"
3973    );
3974
3975    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
3976    // Ensure we don't lock if formatting hangs.
3977    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
3978        assert_eq!(
3979            params.text_document.uri,
3980            lsp::Url::from_file_path("/file.rs").unwrap()
3981        );
3982        futures::future::pending::<()>().await;
3983        unreachable!()
3984    });
3985    let format = editor.update(cx, |editor, cx| editor.perform_format(project, cx));
3986    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
3987    cx.foreground().start_waiting();
3988    format.await.unwrap();
3989    assert_eq!(
3990        editor.read_with(cx, |editor, cx| editor.text(cx)),
3991        "one\ntwo\nthree\n"
3992    );
3993}
3994
3995#[gpui::test]
3996async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
3997    cx.foreground().forbid_parking();
3998
3999    let mut cx = EditorLspTestContext::new_rust(
4000        lsp::ServerCapabilities {
4001            document_formatting_provider: Some(lsp::OneOf::Left(true)),
4002            ..Default::default()
4003        },
4004        cx,
4005    )
4006    .await;
4007
4008    cx.set_state(indoc! {"
4009        one.twoˇ
4010    "});
4011
4012    // The format request takes a long time. When it completes, it inserts
4013    // a newline and an indent before the `.`
4014    cx.lsp
4015        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
4016            let executor = cx.background();
4017            async move {
4018                executor.timer(Duration::from_millis(100)).await;
4019                Ok(Some(vec![lsp::TextEdit {
4020                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
4021                    new_text: "\n    ".into(),
4022                }]))
4023            }
4024        });
4025
4026    // Submit a format request.
4027    let format_1 = cx
4028        .update_editor(|editor, cx| editor.format(&Format, cx))
4029        .unwrap();
4030    cx.foreground().run_until_parked();
4031
4032    // Submit a second format request.
4033    let format_2 = cx
4034        .update_editor(|editor, cx| editor.format(&Format, cx))
4035        .unwrap();
4036    cx.foreground().run_until_parked();
4037
4038    // Wait for both format requests to complete
4039    cx.foreground().advance_clock(Duration::from_millis(200));
4040    cx.foreground().start_waiting();
4041    format_1.await.unwrap();
4042    cx.foreground().start_waiting();
4043    format_2.await.unwrap();
4044
4045    // The formatting edits only happens once.
4046    cx.assert_editor_state(indoc! {"
4047        one
4048            .twoˇ
4049    "});
4050}
4051
4052#[gpui::test]
4053async fn test_completion(cx: &mut gpui::TestAppContext) {
4054    let mut cx = EditorLspTestContext::new_rust(
4055        lsp::ServerCapabilities {
4056            completion_provider: Some(lsp::CompletionOptions {
4057                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
4058                ..Default::default()
4059            }),
4060            ..Default::default()
4061        },
4062        cx,
4063    )
4064    .await;
4065
4066    cx.set_state(indoc! {"
4067        oneˇ
4068        two
4069        three
4070    "});
4071    cx.simulate_keystroke(".");
4072    handle_completion_request(
4073        &mut cx,
4074        indoc! {"
4075            one.|<>
4076            two
4077            three
4078        "},
4079        vec!["first_completion", "second_completion"],
4080    )
4081    .await;
4082    cx.condition(|editor, _| editor.context_menu_visible())
4083        .await;
4084    let apply_additional_edits = cx.update_editor(|editor, cx| {
4085        editor.move_down(&MoveDown, cx);
4086        editor
4087            .confirm_completion(&ConfirmCompletion::default(), cx)
4088            .unwrap()
4089    });
4090    cx.assert_editor_state(indoc! {"
4091        one.second_completionˇ
4092        two
4093        three
4094    "});
4095
4096    handle_resolve_completion_request(
4097        &mut cx,
4098        Some((
4099            indoc! {"
4100                one.second_completion
4101                two
4102                threeˇ
4103            "},
4104            "\nadditional edit",
4105        )),
4106    )
4107    .await;
4108    apply_additional_edits.await.unwrap();
4109    cx.assert_editor_state(indoc! {"
4110        one.second_completionˇ
4111        two
4112        three
4113        additional edit
4114    "});
4115
4116    cx.set_state(indoc! {"
4117        one.second_completion
4118        twoˇ
4119        threeˇ
4120        additional edit
4121    "});
4122    cx.simulate_keystroke(" ");
4123    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4124    cx.simulate_keystroke("s");
4125    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4126
4127    cx.assert_editor_state(indoc! {"
4128        one.second_completion
4129        two sˇ
4130        three sˇ
4131        additional edit
4132    "});
4133    handle_completion_request(
4134        &mut cx,
4135        indoc! {"
4136            one.second_completion
4137            two s
4138            three <s|>
4139            additional edit
4140        "},
4141        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
4142    )
4143    .await;
4144    cx.condition(|editor, _| editor.context_menu_visible())
4145        .await;
4146
4147    cx.simulate_keystroke("i");
4148
4149    handle_completion_request(
4150        &mut cx,
4151        indoc! {"
4152            one.second_completion
4153            two si
4154            three <si|>
4155            additional edit
4156        "},
4157        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
4158    )
4159    .await;
4160    cx.condition(|editor, _| editor.context_menu_visible())
4161        .await;
4162
4163    let apply_additional_edits = cx.update_editor(|editor, cx| {
4164        editor
4165            .confirm_completion(&ConfirmCompletion::default(), cx)
4166            .unwrap()
4167    });
4168    cx.assert_editor_state(indoc! {"
4169        one.second_completion
4170        two sixth_completionˇ
4171        three sixth_completionˇ
4172        additional edit
4173    "});
4174
4175    handle_resolve_completion_request(&mut cx, None).await;
4176    apply_additional_edits.await.unwrap();
4177
4178    cx.update(|cx| {
4179        cx.update_global::<Settings, _, _>(|settings, _| {
4180            settings.show_completions_on_input = false;
4181        })
4182    });
4183    cx.set_state("editorˇ");
4184    cx.simulate_keystroke(".");
4185    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4186    cx.simulate_keystroke("c");
4187    cx.simulate_keystroke("l");
4188    cx.simulate_keystroke("o");
4189    cx.assert_editor_state("editor.cloˇ");
4190    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4191    cx.update_editor(|editor, cx| {
4192        editor.show_completions(&ShowCompletions, cx);
4193    });
4194    handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
4195    cx.condition(|editor, _| editor.context_menu_visible())
4196        .await;
4197    let apply_additional_edits = cx.update_editor(|editor, cx| {
4198        editor
4199            .confirm_completion(&ConfirmCompletion::default(), cx)
4200            .unwrap()
4201    });
4202    cx.assert_editor_state("editor.closeˇ");
4203    handle_resolve_completion_request(&mut cx, None).await;
4204    apply_additional_edits.await.unwrap();
4205
4206    // Handle completion request passing a marked string specifying where the completion
4207    // should be triggered from using '|' character, what range should be replaced, and what completions
4208    // should be returned using '<' and '>' to delimit the range
4209    async fn handle_completion_request<'a>(
4210        cx: &mut EditorLspTestContext<'a>,
4211        marked_string: &str,
4212        completions: Vec<&'static str>,
4213    ) {
4214        let complete_from_marker: TextRangeMarker = '|'.into();
4215        let replace_range_marker: TextRangeMarker = ('<', '>').into();
4216        let (_, mut marked_ranges) = marked_text_ranges_by(
4217            marked_string,
4218            vec![complete_from_marker.clone(), replace_range_marker.clone()],
4219        );
4220
4221        let complete_from_position =
4222            cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
4223        let replace_range =
4224            cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
4225
4226        cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
4227            let completions = completions.clone();
4228            async move {
4229                assert_eq!(params.text_document_position.text_document.uri, url.clone());
4230                assert_eq!(
4231                    params.text_document_position.position,
4232                    complete_from_position
4233                );
4234                Ok(Some(lsp::CompletionResponse::Array(
4235                    completions
4236                        .iter()
4237                        .map(|completion_text| lsp::CompletionItem {
4238                            label: completion_text.to_string(),
4239                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
4240                                range: replace_range,
4241                                new_text: completion_text.to_string(),
4242                            })),
4243                            ..Default::default()
4244                        })
4245                        .collect(),
4246                )))
4247            }
4248        })
4249        .next()
4250        .await;
4251    }
4252
4253    async fn handle_resolve_completion_request<'a>(
4254        cx: &mut EditorLspTestContext<'a>,
4255        edit: Option<(&'static str, &'static str)>,
4256    ) {
4257        let edit = edit.map(|(marked_string, new_text)| {
4258            let (_, marked_ranges) = marked_text_ranges(marked_string, false);
4259            let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
4260            vec![lsp::TextEdit::new(replace_range, new_text.to_string())]
4261        });
4262
4263        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
4264            let edit = edit.clone();
4265            async move {
4266                Ok(lsp::CompletionItem {
4267                    additional_text_edits: edit,
4268                    ..Default::default()
4269                })
4270            }
4271        })
4272        .next()
4273        .await;
4274    }
4275}
4276
4277#[gpui::test]
4278async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
4279    cx.update(|cx| cx.set_global(Settings::test(cx)));
4280    let language = Arc::new(Language::new(
4281        LanguageConfig {
4282            line_comment: Some("// ".into()),
4283            ..Default::default()
4284        },
4285        Some(tree_sitter_rust::language()),
4286    ));
4287
4288    let text = "
4289        fn a() {
4290            //b();
4291            // c();
4292            //  d();
4293        }
4294    "
4295    .unindent();
4296
4297    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
4298    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4299    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
4300
4301    view.update(cx, |editor, cx| {
4302        // If multiple selections intersect a line, the line is only
4303        // toggled once.
4304        editor.change_selections(None, cx, |s| {
4305            s.select_display_ranges([
4306                DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
4307                DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
4308            ])
4309        });
4310        editor.toggle_comments(&ToggleComments, cx);
4311        assert_eq!(
4312            editor.text(cx),
4313            "
4314                fn a() {
4315                    b();
4316                    c();
4317                     d();
4318                }
4319            "
4320            .unindent()
4321        );
4322
4323        // The comment prefix is inserted at the same column for every line
4324        // in a selection.
4325        editor.change_selections(None, cx, |s| {
4326            s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)])
4327        });
4328        editor.toggle_comments(&ToggleComments, cx);
4329        assert_eq!(
4330            editor.text(cx),
4331            "
4332                fn a() {
4333                    // b();
4334                    // c();
4335                    //  d();
4336                }
4337            "
4338            .unindent()
4339        );
4340
4341        // If a selection ends at the beginning of a line, that line is not toggled.
4342        editor.change_selections(None, cx, |s| {
4343            s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)])
4344        });
4345        editor.toggle_comments(&ToggleComments, cx);
4346        assert_eq!(
4347            editor.text(cx),
4348            "
4349                fn a() {
4350                    // b();
4351                    c();
4352                    //  d();
4353                }
4354            "
4355            .unindent()
4356        );
4357    });
4358}
4359
4360#[gpui::test]
4361async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
4362    let mut cx = EditorTestContext::new(cx);
4363
4364    let html_language = Arc::new(
4365        Language::new(
4366            LanguageConfig {
4367                name: "HTML".into(),
4368                block_comment: Some(("<!-- ".into(), " -->".into())),
4369                ..Default::default()
4370            },
4371            Some(tree_sitter_html::language()),
4372        )
4373        .with_injection_query(
4374            r#"
4375            (script_element
4376                (raw_text) @content
4377                (#set! "language" "javascript"))
4378            "#,
4379        )
4380        .unwrap(),
4381    );
4382
4383    let javascript_language = Arc::new(Language::new(
4384        LanguageConfig {
4385            name: "JavaScript".into(),
4386            line_comment: Some("// ".into()),
4387            ..Default::default()
4388        },
4389        Some(tree_sitter_javascript::language()),
4390    ));
4391
4392    let registry = Arc::new(LanguageRegistry::test());
4393    registry.add(html_language.clone());
4394    registry.add(javascript_language.clone());
4395
4396    cx.update_buffer(|buffer, cx| {
4397        buffer.set_language_registry(registry);
4398        buffer.set_language(Some(html_language), cx);
4399    });
4400
4401    // Toggle comments for empty selections
4402    cx.set_state(
4403        &r#"
4404            <p>A</p>ˇ
4405            <p>B</p>ˇ
4406            <p>C</p>ˇ
4407        "#
4408        .unindent(),
4409    );
4410    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
4411    cx.assert_editor_state(
4412        &r#"
4413            <!-- <p>A</p>ˇ -->
4414            <!-- <p>B</p>ˇ -->
4415            <!-- <p>C</p>ˇ -->
4416        "#
4417        .unindent(),
4418    );
4419    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
4420    cx.assert_editor_state(
4421        &r#"
4422            <p>A</p>ˇ
4423            <p>B</p>ˇ
4424            <p>C</p>ˇ
4425        "#
4426        .unindent(),
4427    );
4428
4429    // Toggle comments for mixture of empty and non-empty selections, where
4430    // multiple selections occupy a given line.
4431    cx.set_state(
4432        &r#"
4433            <p>A«</p>
4434            <p>ˇ»B</p>ˇ
4435            <p>C«</p>
4436            <p>ˇ»D</p>ˇ
4437        "#
4438        .unindent(),
4439    );
4440
4441    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
4442    cx.assert_editor_state(
4443        &r#"
4444            <!-- <p>A«</p>
4445            <p>ˇ»B</p>ˇ -->
4446            <!-- <p>C«</p>
4447            <p>ˇ»D</p>ˇ -->
4448        "#
4449        .unindent(),
4450    );
4451    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
4452    cx.assert_editor_state(
4453        &r#"
4454            <p>A«</p>
4455            <p>ˇ»B</p>ˇ
4456            <p>C«</p>
4457            <p>ˇ»D</p>ˇ
4458        "#
4459        .unindent(),
4460    );
4461
4462    // Toggle comments when different languages are active for different
4463    // selections.
4464    cx.set_state(
4465        &r#"
4466            ˇ<script>
4467                ˇvar x = new Y();
4468            ˇ</script>
4469        "#
4470        .unindent(),
4471    );
4472    cx.foreground().run_until_parked();
4473    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
4474    cx.assert_editor_state(
4475        &r#"
4476            <!-- ˇ<script> -->
4477                // ˇvar x = new Y();
4478            <!-- ˇ</script> -->
4479        "#
4480        .unindent(),
4481    );
4482}
4483
4484#[gpui::test]
4485fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
4486    cx.set_global(Settings::test(cx));
4487    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
4488    let multibuffer = cx.add_model(|cx| {
4489        let mut multibuffer = MultiBuffer::new(0);
4490        multibuffer.push_excerpts(
4491            buffer.clone(),
4492            [
4493                ExcerptRange {
4494                    context: Point::new(0, 0)..Point::new(0, 4),
4495                    primary: None,
4496                },
4497                ExcerptRange {
4498                    context: Point::new(1, 0)..Point::new(1, 4),
4499                    primary: None,
4500                },
4501            ],
4502            cx,
4503        );
4504        multibuffer
4505    });
4506
4507    assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
4508
4509    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
4510    view.update(cx, |view, cx| {
4511        assert_eq!(view.text(cx), "aaaa\nbbbb");
4512        view.change_selections(None, cx, |s| {
4513            s.select_ranges([
4514                Point::new(0, 0)..Point::new(0, 0),
4515                Point::new(1, 0)..Point::new(1, 0),
4516            ])
4517        });
4518
4519        view.handle_input("X", cx);
4520        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
4521        assert_eq!(
4522            view.selections.ranges(cx),
4523            [
4524                Point::new(0, 1)..Point::new(0, 1),
4525                Point::new(1, 1)..Point::new(1, 1),
4526            ]
4527        )
4528    });
4529}
4530
4531#[gpui::test]
4532fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
4533    cx.set_global(Settings::test(cx));
4534    let markers = vec![('[', ']').into(), ('(', ')').into()];
4535    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
4536        indoc! {"
4537            [aaaa
4538            (bbbb]
4539            cccc)",
4540        },
4541        markers.clone(),
4542    );
4543    let excerpt_ranges = markers.into_iter().map(|marker| {
4544        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
4545        ExcerptRange {
4546            context,
4547            primary: None,
4548        }
4549    });
4550    let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
4551    let multibuffer = cx.add_model(|cx| {
4552        let mut multibuffer = MultiBuffer::new(0);
4553        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
4554        multibuffer
4555    });
4556
4557    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
4558    view.update(cx, |view, cx| {
4559        let (expected_text, selection_ranges) = marked_text_ranges(
4560            indoc! {"
4561                aaaa
4562                bˇbbb
4563                bˇbbˇb
4564                cccc"
4565            },
4566            true,
4567        );
4568        assert_eq!(view.text(cx), expected_text);
4569        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
4570
4571        view.handle_input("X", cx);
4572
4573        let (expected_text, expected_selections) = marked_text_ranges(
4574            indoc! {"
4575                aaaa
4576                bXˇbbXb
4577                bXˇbbXˇb
4578                cccc"
4579            },
4580            false,
4581        );
4582        assert_eq!(view.text(cx), expected_text);
4583        assert_eq!(view.selections.ranges(cx), expected_selections);
4584
4585        view.newline(&Newline, cx);
4586        let (expected_text, expected_selections) = marked_text_ranges(
4587            indoc! {"
4588                aaaa
4589                bX
4590                ˇbbX
4591                b
4592                bX
4593                ˇbbX
4594                ˇb
4595                cccc"
4596            },
4597            false,
4598        );
4599        assert_eq!(view.text(cx), expected_text);
4600        assert_eq!(view.selections.ranges(cx), expected_selections);
4601    });
4602}
4603
4604#[gpui::test]
4605fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
4606    cx.set_global(Settings::test(cx));
4607    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
4608    let mut excerpt1_id = None;
4609    let multibuffer = cx.add_model(|cx| {
4610        let mut multibuffer = MultiBuffer::new(0);
4611        excerpt1_id = multibuffer
4612            .push_excerpts(
4613                buffer.clone(),
4614                [
4615                    ExcerptRange {
4616                        context: Point::new(0, 0)..Point::new(1, 4),
4617                        primary: None,
4618                    },
4619                    ExcerptRange {
4620                        context: Point::new(1, 0)..Point::new(2, 4),
4621                        primary: None,
4622                    },
4623                ],
4624                cx,
4625            )
4626            .into_iter()
4627            .next();
4628        multibuffer
4629    });
4630    assert_eq!(
4631        multibuffer.read(cx).read(cx).text(),
4632        "aaaa\nbbbb\nbbbb\ncccc"
4633    );
4634    let (_, editor) = cx.add_window(Default::default(), |cx| {
4635        let mut editor = build_editor(multibuffer.clone(), cx);
4636        let snapshot = editor.snapshot(cx);
4637        editor.change_selections(None, cx, |s| {
4638            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
4639        });
4640        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
4641        assert_eq!(
4642            editor.selections.ranges(cx),
4643            [
4644                Point::new(1, 3)..Point::new(1, 3),
4645                Point::new(2, 1)..Point::new(2, 1),
4646            ]
4647        );
4648        editor
4649    });
4650
4651    // Refreshing selections is a no-op when excerpts haven't changed.
4652    editor.update(cx, |editor, cx| {
4653        editor.change_selections(None, cx, |s| {
4654            s.refresh();
4655        });
4656        assert_eq!(
4657            editor.selections.ranges(cx),
4658            [
4659                Point::new(1, 3)..Point::new(1, 3),
4660                Point::new(2, 1)..Point::new(2, 1),
4661            ]
4662        );
4663    });
4664
4665    multibuffer.update(cx, |multibuffer, cx| {
4666        multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
4667    });
4668    editor.update(cx, |editor, cx| {
4669        // Removing an excerpt causes the first selection to become degenerate.
4670        assert_eq!(
4671            editor.selections.ranges(cx),
4672            [
4673                Point::new(0, 0)..Point::new(0, 0),
4674                Point::new(0, 1)..Point::new(0, 1)
4675            ]
4676        );
4677
4678        // Refreshing selections will relocate the first selection to the original buffer
4679        // location.
4680        editor.change_selections(None, cx, |s| {
4681            s.refresh();
4682        });
4683        assert_eq!(
4684            editor.selections.ranges(cx),
4685            [
4686                Point::new(0, 1)..Point::new(0, 1),
4687                Point::new(0, 3)..Point::new(0, 3)
4688            ]
4689        );
4690        assert!(editor.selections.pending_anchor().is_some());
4691    });
4692}
4693
4694#[gpui::test]
4695fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) {
4696    cx.set_global(Settings::test(cx));
4697    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
4698    let mut excerpt1_id = None;
4699    let multibuffer = cx.add_model(|cx| {
4700        let mut multibuffer = MultiBuffer::new(0);
4701        excerpt1_id = multibuffer
4702            .push_excerpts(
4703                buffer.clone(),
4704                [
4705                    ExcerptRange {
4706                        context: Point::new(0, 0)..Point::new(1, 4),
4707                        primary: None,
4708                    },
4709                    ExcerptRange {
4710                        context: Point::new(1, 0)..Point::new(2, 4),
4711                        primary: None,
4712                    },
4713                ],
4714                cx,
4715            )
4716            .into_iter()
4717            .next();
4718        multibuffer
4719    });
4720    assert_eq!(
4721        multibuffer.read(cx).read(cx).text(),
4722        "aaaa\nbbbb\nbbbb\ncccc"
4723    );
4724    let (_, editor) = cx.add_window(Default::default(), |cx| {
4725        let mut editor = build_editor(multibuffer.clone(), cx);
4726        let snapshot = editor.snapshot(cx);
4727        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
4728        assert_eq!(
4729            editor.selections.ranges(cx),
4730            [Point::new(1, 3)..Point::new(1, 3)]
4731        );
4732        editor
4733    });
4734
4735    multibuffer.update(cx, |multibuffer, cx| {
4736        multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
4737    });
4738    editor.update(cx, |editor, cx| {
4739        assert_eq!(
4740            editor.selections.ranges(cx),
4741            [Point::new(0, 0)..Point::new(0, 0)]
4742        );
4743
4744        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
4745        editor.change_selections(None, cx, |s| {
4746            s.refresh();
4747        });
4748        assert_eq!(
4749            editor.selections.ranges(cx),
4750            [Point::new(0, 3)..Point::new(0, 3)]
4751        );
4752        assert!(editor.selections.pending_anchor().is_some());
4753    });
4754}
4755
4756#[gpui::test]
4757async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
4758    cx.update(|cx| cx.set_global(Settings::test(cx)));
4759    let language = Arc::new(
4760        Language::new(
4761            LanguageConfig {
4762                brackets: vec![
4763                    BracketPair {
4764                        start: "{".to_string(),
4765                        end: "}".to_string(),
4766                        close: true,
4767                        newline: true,
4768                    },
4769                    BracketPair {
4770                        start: "/* ".to_string(),
4771                        end: " */".to_string(),
4772                        close: true,
4773                        newline: true,
4774                    },
4775                ],
4776                ..Default::default()
4777            },
4778            Some(tree_sitter_rust::language()),
4779        )
4780        .with_indents_query("")
4781        .unwrap(),
4782    );
4783
4784    let text = concat!(
4785        "{   }\n",     //
4786        "  x\n",       //
4787        "  /*   */\n", //
4788        "x\n",         //
4789        "{{} }\n",     //
4790    );
4791
4792    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
4793    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4794    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
4795    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
4796        .await;
4797
4798    view.update(cx, |view, cx| {
4799        view.change_selections(None, cx, |s| {
4800            s.select_display_ranges([
4801                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
4802                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
4803                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
4804            ])
4805        });
4806        view.newline(&Newline, cx);
4807
4808        assert_eq!(
4809            view.buffer().read(cx).read(cx).text(),
4810            concat!(
4811                "{ \n",    // Suppress rustfmt
4812                "\n",      //
4813                "}\n",     //
4814                "  x\n",   //
4815                "  /* \n", //
4816                "  \n",    //
4817                "  */\n",  //
4818                "x\n",     //
4819                "{{} \n",  //
4820                "}\n",     //
4821            )
4822        );
4823    });
4824}
4825
4826#[gpui::test]
4827fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
4828    let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
4829
4830    cx.set_global(Settings::test(cx));
4831    let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
4832
4833    editor.update(cx, |editor, cx| {
4834        struct Type1;
4835        struct Type2;
4836
4837        let buffer = buffer.read(cx).snapshot(cx);
4838
4839        let anchor_range =
4840            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
4841
4842        editor.highlight_background::<Type1>(
4843            vec![
4844                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
4845                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
4846                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
4847                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
4848            ],
4849            |_| Color::red(),
4850            cx,
4851        );
4852        editor.highlight_background::<Type2>(
4853            vec![
4854                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
4855                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
4856                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
4857                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
4858            ],
4859            |_| Color::green(),
4860            cx,
4861        );
4862
4863        let snapshot = editor.snapshot(cx);
4864        let mut highlighted_ranges = editor.background_highlights_in_range(
4865            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
4866            &snapshot,
4867            cx.global::<Settings>().theme.as_ref(),
4868        );
4869        // Enforce a consistent ordering based on color without relying on the ordering of the
4870        // highlight's `TypeId` which is non-deterministic.
4871        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
4872        assert_eq!(
4873            highlighted_ranges,
4874            &[
4875                (
4876                    DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
4877                    Color::green(),
4878                ),
4879                (
4880                    DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
4881                    Color::green(),
4882                ),
4883                (
4884                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
4885                    Color::red(),
4886                ),
4887                (
4888                    DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
4889                    Color::red(),
4890                ),
4891            ]
4892        );
4893        assert_eq!(
4894            editor.background_highlights_in_range(
4895                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
4896                &snapshot,
4897                cx.global::<Settings>().theme.as_ref(),
4898            ),
4899            &[(
4900                DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
4901                Color::red(),
4902            )]
4903        );
4904    });
4905}
4906
4907#[gpui::test]
4908fn test_following(cx: &mut gpui::MutableAppContext) {
4909    let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
4910
4911    cx.set_global(Settings::test(cx));
4912
4913    let (_, leader) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
4914    let (_, follower) = cx.add_window(
4915        WindowOptions {
4916            bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
4917            ..Default::default()
4918        },
4919        |cx| build_editor(buffer.clone(), cx),
4920    );
4921
4922    let pending_update = Rc::new(RefCell::new(None));
4923    follower.update(cx, {
4924        let update = pending_update.clone();
4925        |_, cx| {
4926            cx.subscribe(&leader, move |_, leader, event, cx| {
4927                leader
4928                    .read(cx)
4929                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
4930            })
4931            .detach();
4932        }
4933    });
4934
4935    // Update the selections only
4936    leader.update(cx, |leader, cx| {
4937        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
4938    });
4939    follower.update(cx, |follower, cx| {
4940        follower
4941            .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
4942            .unwrap();
4943    });
4944    assert_eq!(follower.read(cx).selections.ranges(cx), vec![1..1]);
4945
4946    // Update the scroll position only
4947    leader.update(cx, |leader, cx| {
4948        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
4949    });
4950    follower.update(cx, |follower, cx| {
4951        follower
4952            .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
4953            .unwrap();
4954    });
4955    assert_eq!(
4956        follower.update(cx, |follower, cx| follower.scroll_position(cx)),
4957        vec2f(1.5, 3.5)
4958    );
4959
4960    // Update the selections and scroll position
4961    leader.update(cx, |leader, cx| {
4962        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
4963        leader.request_autoscroll(Autoscroll::Newest, cx);
4964        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
4965    });
4966    follower.update(cx, |follower, cx| {
4967        let initial_scroll_position = follower.scroll_position(cx);
4968        follower
4969            .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
4970            .unwrap();
4971        assert_eq!(follower.scroll_position(cx), initial_scroll_position);
4972        assert!(follower.autoscroll_request.is_some());
4973    });
4974    assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0]);
4975
4976    // Creating a pending selection that precedes another selection
4977    leader.update(cx, |leader, cx| {
4978        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
4979        leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
4980    });
4981    follower.update(cx, |follower, cx| {
4982        follower
4983            .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
4984            .unwrap();
4985    });
4986    assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0, 1..1]);
4987
4988    // Extend the pending selection so that it surrounds another selection
4989    leader.update(cx, |leader, cx| {
4990        leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
4991    });
4992    follower.update(cx, |follower, cx| {
4993        follower
4994            .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
4995            .unwrap();
4996    });
4997    assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..2]);
4998}
4999
5000#[test]
5001fn test_combine_syntax_and_fuzzy_match_highlights() {
5002    let string = "abcdefghijklmnop";
5003    let syntax_ranges = [
5004        (
5005            0..3,
5006            HighlightStyle {
5007                color: Some(Color::red()),
5008                ..Default::default()
5009            },
5010        ),
5011        (
5012            4..8,
5013            HighlightStyle {
5014                color: Some(Color::green()),
5015                ..Default::default()
5016            },
5017        ),
5018    ];
5019    let match_indices = [4, 6, 7, 8];
5020    assert_eq!(
5021        combine_syntax_and_fuzzy_match_highlights(
5022            string,
5023            Default::default(),
5024            syntax_ranges.into_iter(),
5025            &match_indices,
5026        ),
5027        &[
5028            (
5029                0..3,
5030                HighlightStyle {
5031                    color: Some(Color::red()),
5032                    ..Default::default()
5033                },
5034            ),
5035            (
5036                4..5,
5037                HighlightStyle {
5038                    color: Some(Color::green()),
5039                    weight: Some(fonts::Weight::BOLD),
5040                    ..Default::default()
5041                },
5042            ),
5043            (
5044                5..6,
5045                HighlightStyle {
5046                    color: Some(Color::green()),
5047                    ..Default::default()
5048                },
5049            ),
5050            (
5051                6..8,
5052                HighlightStyle {
5053                    color: Some(Color::green()),
5054                    weight: Some(fonts::Weight::BOLD),
5055                    ..Default::default()
5056                },
5057            ),
5058            (
5059                8..9,
5060                HighlightStyle {
5061                    weight: Some(fonts::Weight::BOLD),
5062                    ..Default::default()
5063                },
5064            ),
5065        ]
5066    );
5067}
5068
5069fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
5070    let point = DisplayPoint::new(row as u32, column as u32);
5071    point..point
5072}
5073
5074fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
5075    let (text, ranges) = marked_text_ranges(marked_text, true);
5076    assert_eq!(view.text(cx), text);
5077    assert_eq!(
5078        view.selections.ranges(cx),
5079        ranges,
5080        "Assert selections are {}",
5081        marked_text
5082    );
5083}