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_in_leading_whitespace_auto_indents_lines(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_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
1628    let mut cx = EditorTestContext::new(cx);
1629    let language = Arc::new(
1630        Language::new(
1631            LanguageConfig::default(),
1632            Some(tree_sitter_rust::language()),
1633        )
1634        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
1635        .unwrap(),
1636    );
1637    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1638
1639    cx.update(|cx| {
1640        cx.update_global::<Settings, _, _>(|settings, _| {
1641            settings.editor_overrides.tab_size = Some(4.try_into().unwrap());
1642        });
1643    });
1644
1645    cx.set_state(indoc! {"
1646        fn a() {
1647            if b {
1648        \t ˇc
1649            }
1650        }
1651    "});
1652
1653    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1654    cx.assert_editor_state(indoc! {"
1655        fn a() {
1656            if b {
1657                ˇc
1658            }
1659        }
1660    "});
1661}
1662
1663#[gpui::test]
1664async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
1665    let mut cx = EditorTestContext::new(cx);
1666
1667    cx.set_state(indoc! {"
1668          «oneˇ» «twoˇ»
1669        three
1670         four
1671    "});
1672    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1673    cx.assert_editor_state(indoc! {"
1674            «oneˇ» «twoˇ»
1675        three
1676         four
1677    "});
1678
1679    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1680    cx.assert_editor_state(indoc! {"
1681        «oneˇ» «twoˇ»
1682        three
1683         four
1684    "});
1685
1686    // select across line ending
1687    cx.set_state(indoc! {"
1688        one two
1689        t«hree
1690        ˇ» four
1691    "});
1692    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1693    cx.assert_editor_state(indoc! {"
1694        one two
1695            t«hree
1696        ˇ» four
1697    "});
1698
1699    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1700    cx.assert_editor_state(indoc! {"
1701        one two
1702        t«hree
1703        ˇ» four
1704    "});
1705
1706    // Ensure that indenting/outdenting works when the cursor is at column 0.
1707    cx.set_state(indoc! {"
1708        one two
1709        ˇthree
1710            four
1711    "});
1712    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1713    cx.assert_editor_state(indoc! {"
1714        one two
1715            ˇthree
1716            four
1717    "});
1718
1719    cx.set_state(indoc! {"
1720        one two
1721        ˇ    three
1722            four
1723    "});
1724    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1725    cx.assert_editor_state(indoc! {"
1726        one two
1727        ˇthree
1728            four
1729    "});
1730}
1731
1732#[gpui::test]
1733async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
1734    let mut cx = EditorTestContext::new(cx);
1735    cx.update(|cx| {
1736        cx.update_global::<Settings, _, _>(|settings, _| {
1737            settings.editor_overrides.hard_tabs = Some(true);
1738        });
1739    });
1740
1741    // select two ranges on one line
1742    cx.set_state(indoc! {"
1743        «oneˇ» «twoˇ»
1744        three
1745        four
1746    "});
1747    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1748    cx.assert_editor_state(indoc! {"
1749        \t«oneˇ» «twoˇ»
1750        three
1751        four
1752    "});
1753    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1754    cx.assert_editor_state(indoc! {"
1755        \t\t«oneˇ» «twoˇ»
1756        three
1757        four
1758    "});
1759    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1760    cx.assert_editor_state(indoc! {"
1761        \t«oneˇ» «twoˇ»
1762        three
1763        four
1764    "});
1765    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1766    cx.assert_editor_state(indoc! {"
1767        «oneˇ» «twoˇ»
1768        three
1769        four
1770    "});
1771
1772    // select across a line ending
1773    cx.set_state(indoc! {"
1774        one two
1775        t«hree
1776        ˇ»four
1777    "});
1778    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1779    cx.assert_editor_state(indoc! {"
1780        one two
1781        \tt«hree
1782        ˇ»four
1783    "});
1784    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1785    cx.assert_editor_state(indoc! {"
1786        one two
1787        \t\tt«hree
1788        ˇ»four
1789    "});
1790    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1791    cx.assert_editor_state(indoc! {"
1792        one two
1793        \tt«hree
1794        ˇ»four
1795    "});
1796    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1797    cx.assert_editor_state(indoc! {"
1798        one two
1799        t«hree
1800        ˇ»four
1801    "});
1802
1803    // Ensure that indenting/outdenting works when the cursor is at column 0.
1804    cx.set_state(indoc! {"
1805        one two
1806        ˇthree
1807        four
1808    "});
1809    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1810    cx.assert_editor_state(indoc! {"
1811        one two
1812        ˇthree
1813        four
1814    "});
1815    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1816    cx.assert_editor_state(indoc! {"
1817        one two
1818        \tˇthree
1819        four
1820    "});
1821    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1822    cx.assert_editor_state(indoc! {"
1823        one two
1824        ˇthree
1825        four
1826    "});
1827}
1828
1829#[gpui::test]
1830fn test_indent_outdent_with_excerpts(cx: &mut gpui::MutableAppContext) {
1831    cx.set_global(
1832        Settings::test(cx)
1833            .with_language_defaults(
1834                "TOML",
1835                EditorSettings {
1836                    tab_size: Some(2.try_into().unwrap()),
1837                    ..Default::default()
1838                },
1839            )
1840            .with_language_defaults(
1841                "Rust",
1842                EditorSettings {
1843                    tab_size: Some(4.try_into().unwrap()),
1844                    ..Default::default()
1845                },
1846            ),
1847    );
1848    let toml_language = Arc::new(Language::new(
1849        LanguageConfig {
1850            name: "TOML".into(),
1851            ..Default::default()
1852        },
1853        None,
1854    ));
1855    let rust_language = Arc::new(Language::new(
1856        LanguageConfig {
1857            name: "Rust".into(),
1858            ..Default::default()
1859        },
1860        None,
1861    ));
1862
1863    let toml_buffer =
1864        cx.add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx));
1865    let rust_buffer = cx.add_model(|cx| {
1866        Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx)
1867    });
1868    let multibuffer = cx.add_model(|cx| {
1869        let mut multibuffer = MultiBuffer::new(0);
1870        multibuffer.push_excerpts(
1871            toml_buffer.clone(),
1872            [ExcerptRange {
1873                context: Point::new(0, 0)..Point::new(2, 0),
1874                primary: None,
1875            }],
1876            cx,
1877        );
1878        multibuffer.push_excerpts(
1879            rust_buffer.clone(),
1880            [ExcerptRange {
1881                context: Point::new(0, 0)..Point::new(1, 0),
1882                primary: None,
1883            }],
1884            cx,
1885        );
1886        multibuffer
1887    });
1888
1889    cx.add_window(Default::default(), |cx| {
1890        let mut editor = build_editor(multibuffer, cx);
1891
1892        assert_eq!(
1893            editor.text(cx),
1894            indoc! {"
1895                a = 1
1896                b = 2
1897
1898                const c: usize = 3;
1899            "}
1900        );
1901
1902        select_ranges(
1903            &mut editor,
1904            indoc! {"
1905                «aˇ» = 1
1906                b = 2
1907
1908                «const c:ˇ» usize = 3;
1909            "},
1910            cx,
1911        );
1912
1913        editor.tab(&Tab, cx);
1914        assert_text_with_selections(
1915            &mut editor,
1916            indoc! {"
1917                  «aˇ» = 1
1918                b = 2
1919
1920                    «const c:ˇ» usize = 3;
1921            "},
1922            cx,
1923        );
1924        editor.tab_prev(&TabPrev, cx);
1925        assert_text_with_selections(
1926            &mut editor,
1927            indoc! {"
1928                «aˇ» = 1
1929                b = 2
1930
1931                «const c:ˇ» usize = 3;
1932            "},
1933            cx,
1934        );
1935
1936        editor
1937    });
1938}
1939
1940#[gpui::test]
1941async fn test_backspace(cx: &mut gpui::TestAppContext) {
1942    let mut cx = EditorTestContext::new(cx);
1943
1944    // Basic backspace
1945    cx.set_state(indoc! {"
1946        onˇe two three
1947        fou«rˇ» five six
1948        seven «ˇeight nine
1949        »ten
1950    "});
1951    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
1952    cx.assert_editor_state(indoc! {"
1953        oˇe two three
1954        fouˇ five six
1955        seven ˇten
1956    "});
1957
1958    // Test backspace inside and around indents
1959    cx.set_state(indoc! {"
1960        zero
1961            ˇone
1962                ˇtwo
1963            ˇ ˇ ˇ  three
1964        ˇ  ˇ  four
1965    "});
1966    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
1967    cx.assert_editor_state(indoc! {"
1968        zero
1969        ˇone
1970            ˇtwo
1971        ˇ  threeˇ  four
1972    "});
1973
1974    // Test backspace with line_mode set to true
1975    cx.update_editor(|e, _| e.selections.line_mode = true);
1976    cx.set_state(indoc! {"
1977        The ˇquick ˇbrown
1978        fox jumps over
1979        the lazy dog
1980        ˇThe qu«ick bˇ»rown"});
1981    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
1982    cx.assert_editor_state(indoc! {"
1983        ˇfox jumps over
1984        the lazy dogˇ"});
1985}
1986
1987#[gpui::test]
1988async fn test_delete(cx: &mut gpui::TestAppContext) {
1989    let mut cx = EditorTestContext::new(cx);
1990
1991    cx.set_state(indoc! {"
1992        onˇe two three
1993        fou«rˇ» five six
1994        seven «ˇeight nine
1995        »ten
1996    "});
1997    cx.update_editor(|e, cx| e.delete(&Delete, cx));
1998    cx.assert_editor_state(indoc! {"
1999        onˇ two three
2000        fouˇ five six
2001        seven ˇten
2002    "});
2003
2004    // Test backspace with line_mode set to true
2005    cx.update_editor(|e, _| e.selections.line_mode = true);
2006    cx.set_state(indoc! {"
2007        The ˇquick ˇbrown
2008        fox «ˇjum»ps over
2009        the lazy dog
2010        ˇThe qu«ick bˇ»rown"});
2011    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2012    cx.assert_editor_state("ˇthe lazy dogˇ");
2013}
2014
2015#[gpui::test]
2016fn test_delete_line(cx: &mut gpui::MutableAppContext) {
2017    cx.set_global(Settings::test(cx));
2018    let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2019    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2020    view.update(cx, |view, cx| {
2021        view.change_selections(None, cx, |s| {
2022            s.select_display_ranges([
2023                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2024                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
2025                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2026            ])
2027        });
2028        view.delete_line(&DeleteLine, cx);
2029        assert_eq!(view.display_text(cx), "ghi");
2030        assert_eq!(
2031            view.selections.display_ranges(cx),
2032            vec![
2033                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
2034                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
2035            ]
2036        );
2037    });
2038
2039    cx.set_global(Settings::test(cx));
2040    let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2041    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2042    view.update(cx, |view, cx| {
2043        view.change_selections(None, cx, |s| {
2044            s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
2045        });
2046        view.delete_line(&DeleteLine, cx);
2047        assert_eq!(view.display_text(cx), "ghi\n");
2048        assert_eq!(
2049            view.selections.display_ranges(cx),
2050            vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
2051        );
2052    });
2053}
2054
2055#[gpui::test]
2056fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
2057    cx.set_global(Settings::test(cx));
2058    let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2059    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2060    view.update(cx, |view, cx| {
2061        view.change_selections(None, cx, |s| {
2062            s.select_display_ranges([
2063                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2064                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2065                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2066                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2067            ])
2068        });
2069        view.duplicate_line(&DuplicateLine, cx);
2070        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
2071        assert_eq!(
2072            view.selections.display_ranges(cx),
2073            vec![
2074                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
2075                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
2076                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2077                DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
2078            ]
2079        );
2080    });
2081
2082    let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2083    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2084    view.update(cx, |view, cx| {
2085        view.change_selections(None, cx, |s| {
2086            s.select_display_ranges([
2087                DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
2088                DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
2089            ])
2090        });
2091        view.duplicate_line(&DuplicateLine, cx);
2092        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
2093        assert_eq!(
2094            view.selections.display_ranges(cx),
2095            vec![
2096                DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
2097                DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
2098            ]
2099        );
2100    });
2101}
2102
2103#[gpui::test]
2104fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
2105    cx.set_global(Settings::test(cx));
2106    let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
2107    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2108    view.update(cx, |view, cx| {
2109        view.fold_ranges(
2110            vec![
2111                Point::new(0, 2)..Point::new(1, 2),
2112                Point::new(2, 3)..Point::new(4, 1),
2113                Point::new(7, 0)..Point::new(8, 4),
2114            ],
2115            cx,
2116        );
2117        view.change_selections(None, cx, |s| {
2118            s.select_display_ranges([
2119                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2120                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2121                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2122                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
2123            ])
2124        });
2125        assert_eq!(
2126            view.display_text(cx),
2127            "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj"
2128        );
2129
2130        view.move_line_up(&MoveLineUp, cx);
2131        assert_eq!(
2132            view.display_text(cx),
2133            "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff"
2134        );
2135        assert_eq!(
2136            view.selections.display_ranges(cx),
2137            vec![
2138                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2139                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2140                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
2141                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
2142            ]
2143        );
2144    });
2145
2146    view.update(cx, |view, cx| {
2147        view.move_line_down(&MoveLineDown, cx);
2148        assert_eq!(
2149            view.display_text(cx),
2150            "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj"
2151        );
2152        assert_eq!(
2153            view.selections.display_ranges(cx),
2154            vec![
2155                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
2156                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2157                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2158                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
2159            ]
2160        );
2161    });
2162
2163    view.update(cx, |view, cx| {
2164        view.move_line_down(&MoveLineDown, cx);
2165        assert_eq!(
2166            view.display_text(cx),
2167            "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj"
2168        );
2169        assert_eq!(
2170            view.selections.display_ranges(cx),
2171            vec![
2172                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2173                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2174                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2175                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
2176            ]
2177        );
2178    });
2179
2180    view.update(cx, |view, cx| {
2181        view.move_line_up(&MoveLineUp, cx);
2182        assert_eq!(
2183            view.display_text(cx),
2184            "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff"
2185        );
2186        assert_eq!(
2187            view.selections.display_ranges(cx),
2188            vec![
2189                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
2190                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2191                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
2192                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
2193            ]
2194        );
2195    });
2196}
2197
2198#[gpui::test]
2199fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) {
2200    cx.set_global(Settings::test(cx));
2201    let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
2202    let snapshot = buffer.read(cx).snapshot(cx);
2203    let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2204    editor.update(cx, |editor, cx| {
2205        editor.insert_blocks(
2206            [BlockProperties {
2207                style: BlockStyle::Fixed,
2208                position: snapshot.anchor_after(Point::new(2, 0)),
2209                disposition: BlockDisposition::Below,
2210                height: 1,
2211                render: Arc::new(|_| Empty::new().boxed()),
2212            }],
2213            cx,
2214        );
2215        editor.change_selections(None, cx, |s| {
2216            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
2217        });
2218        editor.move_line_down(&MoveLineDown, cx);
2219    });
2220}
2221
2222#[gpui::test]
2223fn test_transpose(cx: &mut gpui::MutableAppContext) {
2224    cx.set_global(Settings::test(cx));
2225
2226    _ = cx
2227        .add_window(Default::default(), |cx| {
2228            let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
2229
2230            editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
2231            editor.transpose(&Default::default(), cx);
2232            assert_eq!(editor.text(cx), "bac");
2233            assert_eq!(editor.selections.ranges(cx), [2..2]);
2234
2235            editor.transpose(&Default::default(), cx);
2236            assert_eq!(editor.text(cx), "bca");
2237            assert_eq!(editor.selections.ranges(cx), [3..3]);
2238
2239            editor.transpose(&Default::default(), cx);
2240            assert_eq!(editor.text(cx), "bac");
2241            assert_eq!(editor.selections.ranges(cx), [3..3]);
2242
2243            editor
2244        })
2245        .1;
2246
2247    _ = cx
2248        .add_window(Default::default(), |cx| {
2249            let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
2250
2251            editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
2252            editor.transpose(&Default::default(), cx);
2253            assert_eq!(editor.text(cx), "acb\nde");
2254            assert_eq!(editor.selections.ranges(cx), [3..3]);
2255
2256            editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
2257            editor.transpose(&Default::default(), cx);
2258            assert_eq!(editor.text(cx), "acbd\ne");
2259            assert_eq!(editor.selections.ranges(cx), [5..5]);
2260
2261            editor.transpose(&Default::default(), cx);
2262            assert_eq!(editor.text(cx), "acbde\n");
2263            assert_eq!(editor.selections.ranges(cx), [6..6]);
2264
2265            editor.transpose(&Default::default(), cx);
2266            assert_eq!(editor.text(cx), "acbd\ne");
2267            assert_eq!(editor.selections.ranges(cx), [6..6]);
2268
2269            editor
2270        })
2271        .1;
2272
2273    _ = cx
2274        .add_window(Default::default(), |cx| {
2275            let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
2276
2277            editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
2278            editor.transpose(&Default::default(), cx);
2279            assert_eq!(editor.text(cx), "bacd\ne");
2280            assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
2281
2282            editor.transpose(&Default::default(), cx);
2283            assert_eq!(editor.text(cx), "bcade\n");
2284            assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
2285
2286            editor.transpose(&Default::default(), cx);
2287            assert_eq!(editor.text(cx), "bcda\ne");
2288            assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
2289
2290            editor.transpose(&Default::default(), cx);
2291            assert_eq!(editor.text(cx), "bcade\n");
2292            assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
2293
2294            editor.transpose(&Default::default(), cx);
2295            assert_eq!(editor.text(cx), "bcaed\n");
2296            assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
2297
2298            editor
2299        })
2300        .1;
2301
2302    _ = cx
2303        .add_window(Default::default(), |cx| {
2304            let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
2305
2306            editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
2307            editor.transpose(&Default::default(), cx);
2308            assert_eq!(editor.text(cx), "🏀🍐✋");
2309            assert_eq!(editor.selections.ranges(cx), [8..8]);
2310
2311            editor.transpose(&Default::default(), cx);
2312            assert_eq!(editor.text(cx), "🏀✋🍐");
2313            assert_eq!(editor.selections.ranges(cx), [11..11]);
2314
2315            editor.transpose(&Default::default(), cx);
2316            assert_eq!(editor.text(cx), "🏀🍐✋");
2317            assert_eq!(editor.selections.ranges(cx), [11..11]);
2318
2319            editor
2320        })
2321        .1;
2322}
2323
2324#[gpui::test]
2325async fn test_clipboard(cx: &mut gpui::TestAppContext) {
2326    let mut cx = EditorTestContext::new(cx);
2327
2328    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
2329    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2330    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
2331
2332    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
2333    cx.set_state("two ˇfour ˇsix ˇ");
2334    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2335    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
2336
2337    // Paste again but with only two cursors. Since the number of cursors doesn't
2338    // match the number of slices in the clipboard, the entire clipboard text
2339    // is pasted at each cursor.
2340    cx.set_state("ˇtwo one✅ four three six five ˇ");
2341    cx.update_editor(|e, cx| {
2342        e.handle_input("( ", cx);
2343        e.paste(&Paste, cx);
2344        e.handle_input(") ", cx);
2345    });
2346    cx.assert_editor_state(indoc! {"
2347        ( one✅ 
2348        three 
2349        five ) ˇtwo one✅ four three six five ( one✅ 
2350        three 
2351        five ) ˇ"});
2352
2353    // Cut with three selections, one of which is full-line.
2354    cx.set_state(indoc! {"
2355        1«2ˇ»3
2356        4ˇ567
2357        «8ˇ»9"});
2358    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2359    cx.assert_editor_state(indoc! {"
2360        1ˇ3
2361        ˇ9"});
2362
2363    // Paste with three selections, noticing how the copied selection that was full-line
2364    // gets inserted before the second cursor.
2365    cx.set_state(indoc! {"
2366        1ˇ3
23672368        «oˇ»ne"});
2369    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2370    cx.assert_editor_state(indoc! {"
2371        12ˇ3
2372        4567
23732374        8ˇne"});
2375
2376    // Copy with a single cursor only, which writes the whole line into the clipboard.
2377    cx.set_state(indoc! {"
2378        The quick brown
2379        fox juˇmps over
2380        the lazy dog"});
2381    cx.update_editor(|e, cx| e.copy(&Copy, cx));
2382    cx.cx.assert_clipboard_content(Some("fox jumps over\n"));
2383
2384    // Paste with three selections, noticing how the copied full-line selection is inserted
2385    // before the empty selections but replaces the selection that is non-empty.
2386    cx.set_state(indoc! {"
2387        Tˇhe quick brown
2388        «foˇ»x jumps over
2389        tˇhe lazy dog"});
2390    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2391    cx.assert_editor_state(indoc! {"
2392        fox jumps over
2393        Tˇhe quick brown
2394        fox jumps over
2395        ˇx jumps over
2396        fox jumps over
2397        tˇhe lazy dog"});
2398}
2399
2400#[gpui::test]
2401async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
2402    let mut cx = EditorTestContext::new(cx);
2403    let language = Arc::new(Language::new(
2404        LanguageConfig::default(),
2405        Some(tree_sitter_rust::language()),
2406    ));
2407    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
2408
2409    // Cut an indented block, without the leading whitespace.
2410    cx.set_state(indoc! {"
2411        const a: B = (
2412            c(),
2413            «d(
2414                e,
2415                f
2416            )ˇ»
2417        );
2418    "});
2419    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2420    cx.assert_editor_state(indoc! {"
2421        const a: B = (
2422            c(),
2423            ˇ
2424        );
2425    "});
2426
2427    // Paste it at the same position.
2428    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2429    cx.assert_editor_state(indoc! {"
2430        const a: B = (
2431            c(),
2432            d(
2433                e,
2434                f
24352436        );
2437    "});
2438
2439    // Paste it at a line with a lower indent level.
2440    cx.set_state(indoc! {"
2441        ˇ
2442        const a: B = (
2443            c(),
2444        );
2445    "});
2446    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2447    cx.assert_editor_state(indoc! {"
2448        d(
2449            e,
2450            f
24512452        const a: B = (
2453            c(),
2454        );
2455    "});
2456
2457    // Cut an indented block, with the leading whitespace.
2458    cx.set_state(indoc! {"
2459        const a: B = (
2460            c(),
2461        «    d(
2462                e,
2463                f
2464            )
2465        ˇ»);
2466    "});
2467    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2468    cx.assert_editor_state(indoc! {"
2469        const a: B = (
2470            c(),
2471        ˇ);
2472    "});
2473
2474    // Paste it at the same position.
2475    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2476    cx.assert_editor_state(indoc! {"
2477        const a: B = (
2478            c(),
2479            d(
2480                e,
2481                f
2482            )
2483        ˇ);
2484    "});
2485
2486    // Paste it at a line with a higher indent level.
2487    cx.set_state(indoc! {"
2488        const a: B = (
2489            c(),
2490            d(
2491                e,
24922493            )
2494        );
2495    "});
2496    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2497    cx.assert_editor_state(indoc! {"
2498        const a: B = (
2499            c(),
2500            d(
2501                e,
2502                f    d(
2503                    e,
2504                    f
2505                )
2506        ˇ
2507            )
2508        );
2509    "});
2510}
2511
2512#[gpui::test]
2513fn test_select_all(cx: &mut gpui::MutableAppContext) {
2514    cx.set_global(Settings::test(cx));
2515    let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
2516    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2517    view.update(cx, |view, cx| {
2518        view.select_all(&SelectAll, cx);
2519        assert_eq!(
2520            view.selections.display_ranges(cx),
2521            &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
2522        );
2523    });
2524}
2525
2526#[gpui::test]
2527fn test_select_line(cx: &mut gpui::MutableAppContext) {
2528    cx.set_global(Settings::test(cx));
2529    let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
2530    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2531    view.update(cx, |view, cx| {
2532        view.change_selections(None, cx, |s| {
2533            s.select_display_ranges([
2534                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2535                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2536                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2537                DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
2538            ])
2539        });
2540        view.select_line(&SelectLine, cx);
2541        assert_eq!(
2542            view.selections.display_ranges(cx),
2543            vec![
2544                DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
2545                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
2546            ]
2547        );
2548    });
2549
2550    view.update(cx, |view, cx| {
2551        view.select_line(&SelectLine, cx);
2552        assert_eq!(
2553            view.selections.display_ranges(cx),
2554            vec![
2555                DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
2556                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
2557            ]
2558        );
2559    });
2560
2561    view.update(cx, |view, cx| {
2562        view.select_line(&SelectLine, cx);
2563        assert_eq!(
2564            view.selections.display_ranges(cx),
2565            vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
2566        );
2567    });
2568}
2569
2570#[gpui::test]
2571fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
2572    cx.set_global(Settings::test(cx));
2573    let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
2574    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2575    view.update(cx, |view, cx| {
2576        view.fold_ranges(
2577            vec![
2578                Point::new(0, 2)..Point::new(1, 2),
2579                Point::new(2, 3)..Point::new(4, 1),
2580                Point::new(7, 0)..Point::new(8, 4),
2581            ],
2582            cx,
2583        );
2584        view.change_selections(None, cx, |s| {
2585            s.select_display_ranges([
2586                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2587                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2588                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2589                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
2590            ])
2591        });
2592        assert_eq!(view.display_text(cx), "aa…bbb\nccc…eeee\nfffff\nggggg\n…i");
2593    });
2594
2595    view.update(cx, |view, cx| {
2596        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
2597        assert_eq!(
2598            view.display_text(cx),
2599            "aaaaa\nbbbbb\nccc…eeee\nfffff\nggggg\n…i"
2600        );
2601        assert_eq!(
2602            view.selections.display_ranges(cx),
2603            [
2604                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2605                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2606                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
2607                DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
2608            ]
2609        );
2610    });
2611
2612    view.update(cx, |view, cx| {
2613        view.change_selections(None, cx, |s| {
2614            s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
2615        });
2616        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
2617        assert_eq!(
2618            view.display_text(cx),
2619            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
2620        );
2621        assert_eq!(
2622            view.selections.display_ranges(cx),
2623            [
2624                DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
2625                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
2626                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
2627                DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
2628                DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
2629                DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
2630                DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
2631                DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
2632            ]
2633        );
2634    });
2635}
2636
2637#[gpui::test]
2638fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
2639    cx.set_global(Settings::test(cx));
2640    let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
2641    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2642
2643    view.update(cx, |view, cx| {
2644        view.change_selections(None, cx, |s| {
2645            s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
2646        });
2647    });
2648    view.update(cx, |view, cx| {
2649        view.add_selection_above(&AddSelectionAbove, cx);
2650        assert_eq!(
2651            view.selections.display_ranges(cx),
2652            vec![
2653                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
2654                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
2655            ]
2656        );
2657    });
2658
2659    view.update(cx, |view, cx| {
2660        view.add_selection_above(&AddSelectionAbove, cx);
2661        assert_eq!(
2662            view.selections.display_ranges(cx),
2663            vec![
2664                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
2665                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
2666            ]
2667        );
2668    });
2669
2670    view.update(cx, |view, cx| {
2671        view.add_selection_below(&AddSelectionBelow, cx);
2672        assert_eq!(
2673            view.selections.display_ranges(cx),
2674            vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
2675        );
2676
2677        view.undo_selection(&UndoSelection, cx);
2678        assert_eq!(
2679            view.selections.display_ranges(cx),
2680            vec![
2681                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
2682                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
2683            ]
2684        );
2685
2686        view.redo_selection(&RedoSelection, cx);
2687        assert_eq!(
2688            view.selections.display_ranges(cx),
2689            vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
2690        );
2691    });
2692
2693    view.update(cx, |view, cx| {
2694        view.add_selection_below(&AddSelectionBelow, cx);
2695        assert_eq!(
2696            view.selections.display_ranges(cx),
2697            vec![
2698                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
2699                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
2700            ]
2701        );
2702    });
2703
2704    view.update(cx, |view, cx| {
2705        view.add_selection_below(&AddSelectionBelow, cx);
2706        assert_eq!(
2707            view.selections.display_ranges(cx),
2708            vec![
2709                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
2710                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
2711            ]
2712        );
2713    });
2714
2715    view.update(cx, |view, cx| {
2716        view.change_selections(None, cx, |s| {
2717            s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
2718        });
2719    });
2720    view.update(cx, |view, cx| {
2721        view.add_selection_below(&AddSelectionBelow, cx);
2722        assert_eq!(
2723            view.selections.display_ranges(cx),
2724            vec![
2725                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
2726                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
2727            ]
2728        );
2729    });
2730
2731    view.update(cx, |view, cx| {
2732        view.add_selection_below(&AddSelectionBelow, cx);
2733        assert_eq!(
2734            view.selections.display_ranges(cx),
2735            vec![
2736                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
2737                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
2738            ]
2739        );
2740    });
2741
2742    view.update(cx, |view, cx| {
2743        view.add_selection_above(&AddSelectionAbove, cx);
2744        assert_eq!(
2745            view.selections.display_ranges(cx),
2746            vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
2747        );
2748    });
2749
2750    view.update(cx, |view, cx| {
2751        view.add_selection_above(&AddSelectionAbove, cx);
2752        assert_eq!(
2753            view.selections.display_ranges(cx),
2754            vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
2755        );
2756    });
2757
2758    view.update(cx, |view, cx| {
2759        view.change_selections(None, cx, |s| {
2760            s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
2761        });
2762        view.add_selection_below(&AddSelectionBelow, cx);
2763        assert_eq!(
2764            view.selections.display_ranges(cx),
2765            vec![
2766                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
2767                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
2768                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
2769            ]
2770        );
2771    });
2772
2773    view.update(cx, |view, cx| {
2774        view.add_selection_below(&AddSelectionBelow, cx);
2775        assert_eq!(
2776            view.selections.display_ranges(cx),
2777            vec![
2778                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
2779                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
2780                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
2781                DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
2782            ]
2783        );
2784    });
2785
2786    view.update(cx, |view, cx| {
2787        view.add_selection_above(&AddSelectionAbove, cx);
2788        assert_eq!(
2789            view.selections.display_ranges(cx),
2790            vec![
2791                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
2792                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
2793                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
2794            ]
2795        );
2796    });
2797
2798    view.update(cx, |view, cx| {
2799        view.change_selections(None, cx, |s| {
2800            s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
2801        });
2802    });
2803    view.update(cx, |view, cx| {
2804        view.add_selection_above(&AddSelectionAbove, cx);
2805        assert_eq!(
2806            view.selections.display_ranges(cx),
2807            vec![
2808                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
2809                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
2810                DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
2811                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
2812            ]
2813        );
2814    });
2815
2816    view.update(cx, |view, cx| {
2817        view.add_selection_below(&AddSelectionBelow, cx);
2818        assert_eq!(
2819            view.selections.display_ranges(cx),
2820            vec![
2821                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
2822                DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
2823                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
2824            ]
2825        );
2826    });
2827}
2828
2829#[gpui::test]
2830async fn test_select_next(cx: &mut gpui::TestAppContext) {
2831    let mut cx = EditorTestContext::new(cx);
2832    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
2833
2834    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
2835    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
2836
2837    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
2838    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
2839
2840    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
2841    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
2842
2843    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
2844    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
2845
2846    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
2847    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
2848
2849    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
2850    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
2851}
2852
2853#[gpui::test]
2854async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
2855    cx.update(|cx| cx.set_global(Settings::test(cx)));
2856    let language = Arc::new(Language::new(
2857        LanguageConfig::default(),
2858        Some(tree_sitter_rust::language()),
2859    ));
2860
2861    let text = r#"
2862        use mod1::mod2::{mod3, mod4};
2863
2864        fn fn_1(param1: bool, param2: &str) {
2865            let var1 = "text";
2866        }
2867    "#
2868    .unindent();
2869
2870    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
2871    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
2872    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
2873    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
2874        .await;
2875
2876    view.update(cx, |view, cx| {
2877        view.change_selections(None, cx, |s| {
2878            s.select_display_ranges([
2879                DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
2880                DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
2881                DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
2882            ]);
2883        });
2884        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
2885    });
2886    assert_eq!(
2887        view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
2888        &[
2889            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
2890            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
2891            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
2892        ]
2893    );
2894
2895    view.update(cx, |view, cx| {
2896        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
2897    });
2898    assert_eq!(
2899        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2900        &[
2901            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
2902            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
2903        ]
2904    );
2905
2906    view.update(cx, |view, cx| {
2907        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
2908    });
2909    assert_eq!(
2910        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2911        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
2912    );
2913
2914    // Trying to expand the selected syntax node one more time has no effect.
2915    view.update(cx, |view, cx| {
2916        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
2917    });
2918    assert_eq!(
2919        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2920        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
2921    );
2922
2923    view.update(cx, |view, cx| {
2924        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
2925    });
2926    assert_eq!(
2927        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2928        &[
2929            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
2930            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
2931        ]
2932    );
2933
2934    view.update(cx, |view, cx| {
2935        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
2936    });
2937    assert_eq!(
2938        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2939        &[
2940            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
2941            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
2942            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
2943        ]
2944    );
2945
2946    view.update(cx, |view, cx| {
2947        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
2948    });
2949    assert_eq!(
2950        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2951        &[
2952            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
2953            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
2954            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
2955        ]
2956    );
2957
2958    // Trying to shrink the selected syntax node one more time has no effect.
2959    view.update(cx, |view, cx| {
2960        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
2961    });
2962    assert_eq!(
2963        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2964        &[
2965            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
2966            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
2967            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
2968        ]
2969    );
2970
2971    // Ensure that we keep expanding the selection if the larger selection starts or ends within
2972    // a fold.
2973    view.update(cx, |view, cx| {
2974        view.fold_ranges(
2975            vec![
2976                Point::new(0, 21)..Point::new(0, 24),
2977                Point::new(3, 20)..Point::new(3, 22),
2978            ],
2979            cx,
2980        );
2981        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
2982    });
2983    assert_eq!(
2984        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2985        &[
2986            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
2987            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
2988            DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
2989        ]
2990    );
2991}
2992
2993#[gpui::test]
2994async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
2995    cx.update(|cx| cx.set_global(Settings::test(cx)));
2996    let language = Arc::new(
2997        Language::new(
2998            LanguageConfig {
2999                brackets: vec![
3000                    BracketPair {
3001                        start: "{".to_string(),
3002                        end: "}".to_string(),
3003                        close: false,
3004                        newline: true,
3005                    },
3006                    BracketPair {
3007                        start: "(".to_string(),
3008                        end: ")".to_string(),
3009                        close: false,
3010                        newline: true,
3011                    },
3012                ],
3013                ..Default::default()
3014            },
3015            Some(tree_sitter_rust::language()),
3016        )
3017        .with_indents_query(
3018            r#"
3019                (_ "(" ")" @end) @indent
3020                (_ "{" "}" @end) @indent
3021            "#,
3022        )
3023        .unwrap(),
3024    );
3025
3026    let text = "fn a() {}";
3027
3028    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3029    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3030    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3031    editor
3032        .condition(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
3033        .await;
3034
3035    editor.update(cx, |editor, cx| {
3036        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
3037        editor.newline(&Newline, cx);
3038        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
3039        assert_eq!(
3040            editor.selections.ranges(cx),
3041            &[
3042                Point::new(1, 4)..Point::new(1, 4),
3043                Point::new(3, 4)..Point::new(3, 4),
3044                Point::new(5, 0)..Point::new(5, 0)
3045            ]
3046        );
3047    });
3048}
3049
3050#[gpui::test]
3051async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
3052    let mut cx = EditorTestContext::new(cx);
3053
3054    let language = Arc::new(Language::new(
3055        LanguageConfig {
3056            brackets: vec![
3057                BracketPair {
3058                    start: "{".to_string(),
3059                    end: "}".to_string(),
3060                    close: true,
3061                    newline: true,
3062                },
3063                BracketPair {
3064                    start: "(".to_string(),
3065                    end: ")".to_string(),
3066                    close: true,
3067                    newline: true,
3068                },
3069                BracketPair {
3070                    start: "/*".to_string(),
3071                    end: " */".to_string(),
3072                    close: true,
3073                    newline: true,
3074                },
3075                BracketPair {
3076                    start: "[".to_string(),
3077                    end: "]".to_string(),
3078                    close: false,
3079                    newline: true,
3080                },
3081                BracketPair {
3082                    start: "\"".to_string(),
3083                    end: "\"".to_string(),
3084                    close: true,
3085                    newline: false,
3086                },
3087            ],
3088            autoclose_before: "})]".to_string(),
3089            ..Default::default()
3090        },
3091        Some(tree_sitter_rust::language()),
3092    ));
3093
3094    let registry = Arc::new(LanguageRegistry::test());
3095    registry.add(language.clone());
3096    cx.update_buffer(|buffer, cx| {
3097        buffer.set_language_registry(registry);
3098        buffer.set_language(Some(language), cx);
3099    });
3100
3101    cx.set_state(
3102        &r#"
3103            🏀ˇ
3104            εˇ
3105            ❤️ˇ
3106        "#
3107        .unindent(),
3108    );
3109
3110    // autoclose multiple nested brackets at multiple cursors
3111    cx.update_editor(|view, cx| {
3112        view.handle_input("{", cx);
3113        view.handle_input("{", cx);
3114        view.handle_input("{", cx);
3115    });
3116    cx.assert_editor_state(
3117        &"
3118            🏀{{{ˇ}}}
3119            ε{{{ˇ}}}
3120            ❤️{{{ˇ}}}
3121        "
3122        .unindent(),
3123    );
3124
3125    // insert a different closing bracket
3126    cx.update_editor(|view, cx| {
3127        view.handle_input(")", cx);
3128    });
3129    cx.assert_editor_state(
3130        &"
3131            🏀{{{)ˇ}}}
3132            ε{{{)ˇ}}}
3133            ❤️{{{)ˇ}}}
3134        "
3135        .unindent(),
3136    );
3137
3138    // skip over the auto-closed brackets when typing a closing bracket
3139    cx.update_editor(|view, cx| {
3140        view.move_right(&MoveRight, cx);
3141        view.handle_input("}", cx);
3142        view.handle_input("}", cx);
3143        view.handle_input("}", cx);
3144    });
3145    cx.assert_editor_state(
3146        &"
3147            🏀{{{)}}}}ˇ
3148            ε{{{)}}}}ˇ
3149            ❤️{{{)}}}}ˇ
3150        "
3151        .unindent(),
3152    );
3153
3154    // autoclose multi-character pairs
3155    cx.set_state(
3156        &"
3157            ˇ
3158            ˇ
3159        "
3160        .unindent(),
3161    );
3162    cx.update_editor(|view, cx| {
3163        view.handle_input("/", cx);
3164        view.handle_input("*", cx);
3165    });
3166    cx.assert_editor_state(
3167        &"
3168            /*ˇ */
3169            /*ˇ */
3170        "
3171        .unindent(),
3172    );
3173
3174    // one cursor autocloses a multi-character pair, one cursor
3175    // does not autoclose.
3176    cx.set_state(
3177        &"
31783179            ˇ
3180        "
3181        .unindent(),
3182    );
3183    cx.update_editor(|view, cx| view.handle_input("*", cx));
3184    cx.assert_editor_state(
3185        &"
3186            /*ˇ */
31873188        "
3189        .unindent(),
3190    );
3191
3192    // Don't autoclose if the next character isn't whitespace and isn't
3193    // listed in the language's "autoclose_before" section.
3194    cx.set_state("ˇa b");
3195    cx.update_editor(|view, cx| view.handle_input("{", cx));
3196    cx.assert_editor_state("{ˇa b");
3197
3198    // Don't autoclose if `close` is false for the bracket pair
3199    cx.set_state("ˇ");
3200    cx.update_editor(|view, cx| view.handle_input("[", cx));
3201    cx.assert_editor_state("");
3202
3203    // Surround with brackets if text is selected
3204    cx.set_state("«aˇ» b");
3205    cx.update_editor(|view, cx| view.handle_input("{", cx));
3206    cx.assert_editor_state("{«aˇ»} b");
3207
3208    // Autclose pair where the start and end characters are the same
3209    cx.set_state("");
3210    cx.update_editor(|view, cx| view.handle_input("\"", cx));
3211    cx.assert_editor_state("a\"ˇ\"");
3212    cx.update_editor(|view, cx| view.handle_input("\"", cx));
3213    cx.assert_editor_state("a\"\"ˇ");
3214}
3215
3216#[gpui::test]
3217async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
3218    let mut cx = EditorTestContext::new(cx);
3219
3220    let html_language = Arc::new(
3221        Language::new(
3222            LanguageConfig {
3223                name: "HTML".into(),
3224                brackets: vec![
3225                    BracketPair {
3226                        start: "<".into(),
3227                        end: ">".into(),
3228                        close: true,
3229                        ..Default::default()
3230                    },
3231                    BracketPair {
3232                        start: "{".into(),
3233                        end: "}".into(),
3234                        close: true,
3235                        ..Default::default()
3236                    },
3237                    BracketPair {
3238                        start: "(".into(),
3239                        end: ")".into(),
3240                        close: true,
3241                        ..Default::default()
3242                    },
3243                ],
3244                autoclose_before: "})]>".into(),
3245                ..Default::default()
3246            },
3247            Some(tree_sitter_html::language()),
3248        )
3249        .with_injection_query(
3250            r#"
3251            (script_element
3252                (raw_text) @content
3253                (#set! "language" "javascript"))
3254            "#,
3255        )
3256        .unwrap(),
3257    );
3258
3259    let javascript_language = Arc::new(Language::new(
3260        LanguageConfig {
3261            name: "JavaScript".into(),
3262            brackets: vec![
3263                BracketPair {
3264                    start: "/*".into(),
3265                    end: " */".into(),
3266                    close: true,
3267                    ..Default::default()
3268                },
3269                BracketPair {
3270                    start: "{".into(),
3271                    end: "}".into(),
3272                    close: true,
3273                    ..Default::default()
3274                },
3275                BracketPair {
3276                    start: "(".into(),
3277                    end: ")".into(),
3278                    close: true,
3279                    ..Default::default()
3280                },
3281            ],
3282            autoclose_before: "})]>".into(),
3283            ..Default::default()
3284        },
3285        Some(tree_sitter_javascript::language()),
3286    ));
3287
3288    let registry = Arc::new(LanguageRegistry::test());
3289    registry.add(html_language.clone());
3290    registry.add(javascript_language.clone());
3291
3292    cx.update_buffer(|buffer, cx| {
3293        buffer.set_language_registry(registry);
3294        buffer.set_language(Some(html_language), cx);
3295    });
3296
3297    cx.set_state(
3298        &r#"
3299            <body>ˇ
3300                <script>
3301                    var x = 1;ˇ
3302                </script>
3303            </body>ˇ
3304        "#
3305        .unindent(),
3306    );
3307
3308    // Precondition: different languages are active at different locations.
3309    cx.update_editor(|editor, cx| {
3310        let snapshot = editor.snapshot(cx);
3311        let cursors = editor.selections.ranges::<usize>(cx);
3312        let languages = cursors
3313            .iter()
3314            .map(|c| snapshot.language_at(c.start).unwrap().name())
3315            .collect::<Vec<_>>();
3316        assert_eq!(
3317            languages,
3318            &["HTML".into(), "JavaScript".into(), "HTML".into()]
3319        );
3320    });
3321
3322    // Angle brackets autoclose in HTML, but not JavaScript.
3323    cx.update_editor(|editor, cx| {
3324        editor.handle_input("<", cx);
3325        editor.handle_input("a", cx);
3326    });
3327    cx.assert_editor_state(
3328        &r#"
3329            <body><aˇ>
3330                <script>
3331                    var x = 1;<aˇ
3332                </script>
3333            </body><aˇ>
3334        "#
3335        .unindent(),
3336    );
3337
3338    // Curly braces and parens autoclose in both HTML and JavaScript.
3339    cx.update_editor(|editor, cx| {
3340        editor.handle_input(" b=", cx);
3341        editor.handle_input("{", cx);
3342        editor.handle_input("c", cx);
3343        editor.handle_input("(", cx);
3344    });
3345    cx.assert_editor_state(
3346        &r#"
3347            <body><a b={c(ˇ)}>
3348                <script>
3349                    var x = 1;<a b={c(ˇ)}
3350                </script>
3351            </body><a b={c(ˇ)}>
3352        "#
3353        .unindent(),
3354    );
3355
3356    // Brackets that were already autoclosed are skipped.
3357    cx.update_editor(|editor, cx| {
3358        editor.handle_input(")", cx);
3359        editor.handle_input("d", cx);
3360        editor.handle_input("}", cx);
3361    });
3362    cx.assert_editor_state(
3363        &r#"
3364            <body><a b={c()d}ˇ>
3365                <script>
3366                    var x = 1;<a b={c()d}ˇ
3367                </script>
3368            </body><a b={c()d}ˇ>
3369        "#
3370        .unindent(),
3371    );
3372    cx.update_editor(|editor, cx| {
3373        editor.handle_input(">", cx);
3374    });
3375    cx.assert_editor_state(
3376        &r#"
3377            <body><a b={c()d}>ˇ
3378                <script>
3379                    var x = 1;<a b={c()d}>ˇ
3380                </script>
3381            </body><a b={c()d}>ˇ
3382        "#
3383        .unindent(),
3384    );
3385
3386    // Reset
3387    cx.set_state(
3388        &r#"
3389            <body>ˇ
3390                <script>
3391                    var x = 1;ˇ
3392                </script>
3393            </body>ˇ
3394        "#
3395        .unindent(),
3396    );
3397
3398    cx.update_editor(|editor, cx| {
3399        editor.handle_input("<", cx);
3400    });
3401    cx.assert_editor_state(
3402        &r#"
3403            <body><ˇ>
3404                <script>
3405                    var x = 1;<ˇ
3406                </script>
3407            </body><ˇ>
3408        "#
3409        .unindent(),
3410    );
3411
3412    // When backspacing, the closing angle brackets are removed.
3413    cx.update_editor(|editor, cx| {
3414        editor.backspace(&Backspace, cx);
3415    });
3416    cx.assert_editor_state(
3417        &r#"
3418            <body>ˇ
3419                <script>
3420                    var x = 1;ˇ
3421                </script>
3422            </body>ˇ
3423        "#
3424        .unindent(),
3425    );
3426
3427    // Block comments autoclose in JavaScript, but not HTML.
3428    cx.update_editor(|editor, cx| {
3429        editor.handle_input("/", cx);
3430        editor.handle_input("*", cx);
3431    });
3432    cx.assert_editor_state(
3433        &r#"
3434            <body>/*ˇ
3435                <script>
3436                    var x = 1;/*ˇ */
3437                </script>
3438            </body>/*ˇ
3439        "#
3440        .unindent(),
3441    );
3442}
3443
3444#[gpui::test]
3445async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
3446    cx.update(|cx| cx.set_global(Settings::test(cx)));
3447    let language = Arc::new(Language::new(
3448        LanguageConfig {
3449            brackets: vec![BracketPair {
3450                start: "{".to_string(),
3451                end: "}".to_string(),
3452                close: true,
3453                newline: true,
3454            }],
3455            ..Default::default()
3456        },
3457        Some(tree_sitter_rust::language()),
3458    ));
3459
3460    let text = r#"
3461        a
3462        b
3463        c
3464    "#
3465    .unindent();
3466
3467    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3468    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3469    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
3470    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
3471        .await;
3472
3473    view.update(cx, |view, cx| {
3474        view.change_selections(None, cx, |s| {
3475            s.select_display_ranges([
3476                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3477                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
3478                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
3479            ])
3480        });
3481
3482        view.handle_input("{", cx);
3483        view.handle_input("{", cx);
3484        view.handle_input("{", cx);
3485        assert_eq!(
3486            view.text(cx),
3487            "
3488                {{{a}}}
3489                {{{b}}}
3490                {{{c}}}
3491            "
3492            .unindent()
3493        );
3494        assert_eq!(
3495            view.selections.display_ranges(cx),
3496            [
3497                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
3498                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
3499                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
3500            ]
3501        );
3502
3503        view.undo(&Undo, cx);
3504        assert_eq!(
3505            view.text(cx),
3506            "
3507                a
3508                b
3509                c
3510            "
3511            .unindent()
3512        );
3513        assert_eq!(
3514            view.selections.display_ranges(cx),
3515            [
3516                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3517                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
3518                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
3519            ]
3520        );
3521    });
3522}
3523
3524#[gpui::test]
3525async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
3526    cx.update(|cx| cx.set_global(Settings::test(cx)));
3527    let language = Arc::new(Language::new(
3528        LanguageConfig {
3529            brackets: vec![BracketPair {
3530                start: "{".to_string(),
3531                end: "}".to_string(),
3532                close: true,
3533                newline: true,
3534            }],
3535            autoclose_before: "}".to_string(),
3536            ..Default::default()
3537        },
3538        Some(tree_sitter_rust::language()),
3539    ));
3540
3541    let text = r#"
3542        a
3543        b
3544        c
3545    "#
3546    .unindent();
3547
3548    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3549    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3550    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3551    editor
3552        .condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
3553        .await;
3554
3555    editor.update(cx, |editor, cx| {
3556        editor.change_selections(None, cx, |s| {
3557            s.select_ranges([
3558                Point::new(0, 1)..Point::new(0, 1),
3559                Point::new(1, 1)..Point::new(1, 1),
3560                Point::new(2, 1)..Point::new(2, 1),
3561            ])
3562        });
3563
3564        editor.handle_input("{", cx);
3565        editor.handle_input("{", cx);
3566        editor.handle_input("_", cx);
3567        assert_eq!(
3568            editor.text(cx),
3569            "
3570                a{{_}}
3571                b{{_}}
3572                c{{_}}
3573            "
3574            .unindent()
3575        );
3576        assert_eq!(
3577            editor.selections.ranges::<Point>(cx),
3578            [
3579                Point::new(0, 4)..Point::new(0, 4),
3580                Point::new(1, 4)..Point::new(1, 4),
3581                Point::new(2, 4)..Point::new(2, 4)
3582            ]
3583        );
3584
3585        editor.backspace(&Default::default(), cx);
3586        editor.backspace(&Default::default(), cx);
3587        assert_eq!(
3588            editor.text(cx),
3589            "
3590                a{}
3591                b{}
3592                c{}
3593            "
3594            .unindent()
3595        );
3596        assert_eq!(
3597            editor.selections.ranges::<Point>(cx),
3598            [
3599                Point::new(0, 2)..Point::new(0, 2),
3600                Point::new(1, 2)..Point::new(1, 2),
3601                Point::new(2, 2)..Point::new(2, 2)
3602            ]
3603        );
3604
3605        editor.delete_to_previous_word_start(&Default::default(), cx);
3606        assert_eq!(
3607            editor.text(cx),
3608            "
3609                a
3610                b
3611                c
3612            "
3613            .unindent()
3614        );
3615        assert_eq!(
3616            editor.selections.ranges::<Point>(cx),
3617            [
3618                Point::new(0, 1)..Point::new(0, 1),
3619                Point::new(1, 1)..Point::new(1, 1),
3620                Point::new(2, 1)..Point::new(2, 1)
3621            ]
3622        );
3623    });
3624}
3625
3626#[gpui::test]
3627async fn test_snippets(cx: &mut gpui::TestAppContext) {
3628    cx.update(|cx| cx.set_global(Settings::test(cx)));
3629
3630    let (text, insertion_ranges) = marked_text_ranges(
3631        indoc! {"
3632            a.ˇ b
3633            a.ˇ b
3634            a.ˇ b
3635        "},
3636        false,
3637    );
3638
3639    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
3640    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3641
3642    editor.update(cx, |editor, cx| {
3643        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
3644
3645        editor
3646            .insert_snippet(&insertion_ranges, snippet, cx)
3647            .unwrap();
3648
3649        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
3650            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
3651            assert_eq!(editor.text(cx), expected_text);
3652            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
3653        }
3654
3655        assert(
3656            editor,
3657            cx,
3658            indoc! {"
3659                a.f(«one», two, «three») b
3660                a.f(«one», two, «three») b
3661                a.f(«one», two, «three») b
3662            "},
3663        );
3664
3665        // Can't move earlier than the first tab stop
3666        assert!(!editor.move_to_prev_snippet_tabstop(cx));
3667        assert(
3668            editor,
3669            cx,
3670            indoc! {"
3671                a.f(«one», two, «three») b
3672                a.f(«one», two, «three») b
3673                a.f(«one», two, «three») b
3674            "},
3675        );
3676
3677        assert!(editor.move_to_next_snippet_tabstop(cx));
3678        assert(
3679            editor,
3680            cx,
3681            indoc! {"
3682                a.f(one, «two», three) b
3683                a.f(one, «two», three) b
3684                a.f(one, «two», three) b
3685            "},
3686        );
3687
3688        editor.move_to_prev_snippet_tabstop(cx);
3689        assert(
3690            editor,
3691            cx,
3692            indoc! {"
3693                a.f(«one», two, «three») b
3694                a.f(«one», two, «three») b
3695                a.f(«one», two, «three») b
3696            "},
3697        );
3698
3699        assert!(editor.move_to_next_snippet_tabstop(cx));
3700        assert(
3701            editor,
3702            cx,
3703            indoc! {"
3704                a.f(one, «two», three) b
3705                a.f(one, «two», three) b
3706                a.f(one, «two», three) b
3707            "},
3708        );
3709        assert!(editor.move_to_next_snippet_tabstop(cx));
3710        assert(
3711            editor,
3712            cx,
3713            indoc! {"
3714                a.f(one, two, three)ˇ b
3715                a.f(one, two, three)ˇ b
3716                a.f(one, two, three)ˇ b
3717            "},
3718        );
3719
3720        // As soon as the last tab stop is reached, snippet state is gone
3721        editor.move_to_prev_snippet_tabstop(cx);
3722        assert(
3723            editor,
3724            cx,
3725            indoc! {"
3726                a.f(one, two, three)ˇ b
3727                a.f(one, two, three)ˇ b
3728                a.f(one, two, three)ˇ b
3729            "},
3730        );
3731    });
3732}
3733
3734#[gpui::test]
3735async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
3736    cx.foreground().forbid_parking();
3737
3738    let mut language = Language::new(
3739        LanguageConfig {
3740            name: "Rust".into(),
3741            path_suffixes: vec!["rs".to_string()],
3742            ..Default::default()
3743        },
3744        Some(tree_sitter_rust::language()),
3745    );
3746    let mut fake_servers = language
3747        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3748            capabilities: lsp::ServerCapabilities {
3749                document_formatting_provider: Some(lsp::OneOf::Left(true)),
3750                ..Default::default()
3751            },
3752            ..Default::default()
3753        }))
3754        .await;
3755
3756    let fs = FakeFs::new(cx.background());
3757    fs.insert_file("/file.rs", Default::default()).await;
3758
3759    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
3760    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
3761    let buffer = project
3762        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
3763        .await
3764        .unwrap();
3765
3766    cx.foreground().start_waiting();
3767    let fake_server = fake_servers.next().await.unwrap();
3768
3769    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3770    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3771    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
3772    assert!(cx.read(|cx| editor.is_dirty(cx)));
3773
3774    let save = cx.update(|cx| editor.save(project.clone(), cx));
3775    fake_server
3776        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
3777            assert_eq!(
3778                params.text_document.uri,
3779                lsp::Url::from_file_path("/file.rs").unwrap()
3780            );
3781            assert_eq!(params.options.tab_size, 4);
3782            Ok(Some(vec![lsp::TextEdit::new(
3783                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
3784                ", ".to_string(),
3785            )]))
3786        })
3787        .next()
3788        .await;
3789    cx.foreground().start_waiting();
3790    save.await.unwrap();
3791    assert_eq!(
3792        editor.read_with(cx, |editor, cx| editor.text(cx)),
3793        "one, two\nthree\n"
3794    );
3795    assert!(!cx.read(|cx| editor.is_dirty(cx)));
3796
3797    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
3798    assert!(cx.read(|cx| editor.is_dirty(cx)));
3799
3800    // Ensure we can still save even if formatting hangs.
3801    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
3802        assert_eq!(
3803            params.text_document.uri,
3804            lsp::Url::from_file_path("/file.rs").unwrap()
3805        );
3806        futures::future::pending::<()>().await;
3807        unreachable!()
3808    });
3809    let save = cx.update(|cx| editor.save(project.clone(), cx));
3810    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
3811    cx.foreground().start_waiting();
3812    save.await.unwrap();
3813    assert_eq!(
3814        editor.read_with(cx, |editor, cx| editor.text(cx)),
3815        "one\ntwo\nthree\n"
3816    );
3817    assert!(!cx.read(|cx| editor.is_dirty(cx)));
3818
3819    // Set rust language override and assert overriden tabsize is sent to language server
3820    cx.update(|cx| {
3821        cx.update_global::<Settings, _, _>(|settings, _| {
3822            settings.language_overrides.insert(
3823                "Rust".into(),
3824                EditorSettings {
3825                    tab_size: Some(8.try_into().unwrap()),
3826                    ..Default::default()
3827                },
3828            );
3829        })
3830    });
3831
3832    let save = cx.update(|cx| editor.save(project.clone(), cx));
3833    fake_server
3834        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
3835            assert_eq!(
3836                params.text_document.uri,
3837                lsp::Url::from_file_path("/file.rs").unwrap()
3838            );
3839            assert_eq!(params.options.tab_size, 8);
3840            Ok(Some(vec![]))
3841        })
3842        .next()
3843        .await;
3844    cx.foreground().start_waiting();
3845    save.await.unwrap();
3846}
3847
3848#[gpui::test]
3849async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
3850    cx.foreground().forbid_parking();
3851
3852    let mut language = Language::new(
3853        LanguageConfig {
3854            name: "Rust".into(),
3855            path_suffixes: vec!["rs".to_string()],
3856            ..Default::default()
3857        },
3858        Some(tree_sitter_rust::language()),
3859    );
3860    let mut fake_servers = language
3861        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3862            capabilities: lsp::ServerCapabilities {
3863                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
3864                ..Default::default()
3865            },
3866            ..Default::default()
3867        }))
3868        .await;
3869
3870    let fs = FakeFs::new(cx.background());
3871    fs.insert_file("/file.rs", Default::default()).await;
3872
3873    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
3874    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
3875    let buffer = project
3876        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
3877        .await
3878        .unwrap();
3879
3880    cx.foreground().start_waiting();
3881    let fake_server = fake_servers.next().await.unwrap();
3882
3883    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3884    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3885    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
3886    assert!(cx.read(|cx| editor.is_dirty(cx)));
3887
3888    let save = cx.update(|cx| editor.save(project.clone(), cx));
3889    fake_server
3890        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
3891            assert_eq!(
3892                params.text_document.uri,
3893                lsp::Url::from_file_path("/file.rs").unwrap()
3894            );
3895            assert_eq!(params.options.tab_size, 4);
3896            Ok(Some(vec![lsp::TextEdit::new(
3897                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
3898                ", ".to_string(),
3899            )]))
3900        })
3901        .next()
3902        .await;
3903    cx.foreground().start_waiting();
3904    save.await.unwrap();
3905    assert_eq!(
3906        editor.read_with(cx, |editor, cx| editor.text(cx)),
3907        "one, two\nthree\n"
3908    );
3909    assert!(!cx.read(|cx| editor.is_dirty(cx)));
3910
3911    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
3912    assert!(cx.read(|cx| editor.is_dirty(cx)));
3913
3914    // Ensure we can still save even if formatting hangs.
3915    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
3916        move |params, _| async move {
3917            assert_eq!(
3918                params.text_document.uri,
3919                lsp::Url::from_file_path("/file.rs").unwrap()
3920            );
3921            futures::future::pending::<()>().await;
3922            unreachable!()
3923        },
3924    );
3925    let save = cx.update(|cx| editor.save(project.clone(), cx));
3926    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
3927    cx.foreground().start_waiting();
3928    save.await.unwrap();
3929    assert_eq!(
3930        editor.read_with(cx, |editor, cx| editor.text(cx)),
3931        "one\ntwo\nthree\n"
3932    );
3933    assert!(!cx.read(|cx| editor.is_dirty(cx)));
3934
3935    // Set rust language override and assert overriden tabsize is sent to language server
3936    cx.update(|cx| {
3937        cx.update_global::<Settings, _, _>(|settings, _| {
3938            settings.language_overrides.insert(
3939                "Rust".into(),
3940                EditorSettings {
3941                    tab_size: Some(8.try_into().unwrap()),
3942                    ..Default::default()
3943                },
3944            );
3945        })
3946    });
3947
3948    let save = cx.update(|cx| editor.save(project.clone(), cx));
3949    fake_server
3950        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
3951            assert_eq!(
3952                params.text_document.uri,
3953                lsp::Url::from_file_path("/file.rs").unwrap()
3954            );
3955            assert_eq!(params.options.tab_size, 8);
3956            Ok(Some(vec![]))
3957        })
3958        .next()
3959        .await;
3960    cx.foreground().start_waiting();
3961    save.await.unwrap();
3962}
3963
3964#[gpui::test]
3965async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
3966    cx.foreground().forbid_parking();
3967
3968    let mut language = Language::new(
3969        LanguageConfig {
3970            name: "Rust".into(),
3971            path_suffixes: vec!["rs".to_string()],
3972            ..Default::default()
3973        },
3974        Some(tree_sitter_rust::language()),
3975    );
3976    let mut fake_servers = language
3977        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3978            capabilities: lsp::ServerCapabilities {
3979                document_formatting_provider: Some(lsp::OneOf::Left(true)),
3980                ..Default::default()
3981            },
3982            ..Default::default()
3983        }))
3984        .await;
3985
3986    let fs = FakeFs::new(cx.background());
3987    fs.insert_file("/file.rs", Default::default()).await;
3988
3989    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
3990    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
3991    let buffer = project
3992        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
3993        .await
3994        .unwrap();
3995
3996    cx.foreground().start_waiting();
3997    let fake_server = fake_servers.next().await.unwrap();
3998
3999    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4000    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4001    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4002
4003    let format = editor.update(cx, |editor, cx| editor.perform_format(project.clone(), cx));
4004    fake_server
4005        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4006            assert_eq!(
4007                params.text_document.uri,
4008                lsp::Url::from_file_path("/file.rs").unwrap()
4009            );
4010            assert_eq!(params.options.tab_size, 4);
4011            Ok(Some(vec![lsp::TextEdit::new(
4012                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4013                ", ".to_string(),
4014            )]))
4015        })
4016        .next()
4017        .await;
4018    cx.foreground().start_waiting();
4019    format.await.unwrap();
4020    assert_eq!(
4021        editor.read_with(cx, |editor, cx| editor.text(cx)),
4022        "one, two\nthree\n"
4023    );
4024
4025    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4026    // Ensure we don't lock if formatting hangs.
4027    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4028        assert_eq!(
4029            params.text_document.uri,
4030            lsp::Url::from_file_path("/file.rs").unwrap()
4031        );
4032        futures::future::pending::<()>().await;
4033        unreachable!()
4034    });
4035    let format = editor.update(cx, |editor, cx| editor.perform_format(project, cx));
4036    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4037    cx.foreground().start_waiting();
4038    format.await.unwrap();
4039    assert_eq!(
4040        editor.read_with(cx, |editor, cx| editor.text(cx)),
4041        "one\ntwo\nthree\n"
4042    );
4043}
4044
4045#[gpui::test]
4046async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
4047    cx.foreground().forbid_parking();
4048
4049    let mut cx = EditorLspTestContext::new_rust(
4050        lsp::ServerCapabilities {
4051            document_formatting_provider: Some(lsp::OneOf::Left(true)),
4052            ..Default::default()
4053        },
4054        cx,
4055    )
4056    .await;
4057
4058    cx.set_state(indoc! {"
4059        one.twoˇ
4060    "});
4061
4062    // The format request takes a long time. When it completes, it inserts
4063    // a newline and an indent before the `.`
4064    cx.lsp
4065        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
4066            let executor = cx.background();
4067            async move {
4068                executor.timer(Duration::from_millis(100)).await;
4069                Ok(Some(vec![lsp::TextEdit {
4070                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
4071                    new_text: "\n    ".into(),
4072                }]))
4073            }
4074        });
4075
4076    // Submit a format request.
4077    let format_1 = cx
4078        .update_editor(|editor, cx| editor.format(&Format, cx))
4079        .unwrap();
4080    cx.foreground().run_until_parked();
4081
4082    // Submit a second format request.
4083    let format_2 = cx
4084        .update_editor(|editor, cx| editor.format(&Format, cx))
4085        .unwrap();
4086    cx.foreground().run_until_parked();
4087
4088    // Wait for both format requests to complete
4089    cx.foreground().advance_clock(Duration::from_millis(200));
4090    cx.foreground().start_waiting();
4091    format_1.await.unwrap();
4092    cx.foreground().start_waiting();
4093    format_2.await.unwrap();
4094
4095    // The formatting edits only happens once.
4096    cx.assert_editor_state(indoc! {"
4097        one
4098            .twoˇ
4099    "});
4100}
4101
4102#[gpui::test]
4103async fn test_completion(cx: &mut gpui::TestAppContext) {
4104    let mut cx = EditorLspTestContext::new_rust(
4105        lsp::ServerCapabilities {
4106            completion_provider: Some(lsp::CompletionOptions {
4107                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
4108                ..Default::default()
4109            }),
4110            ..Default::default()
4111        },
4112        cx,
4113    )
4114    .await;
4115
4116    cx.set_state(indoc! {"
4117        oneˇ
4118        two
4119        three
4120    "});
4121    cx.simulate_keystroke(".");
4122    handle_completion_request(
4123        &mut cx,
4124        indoc! {"
4125            one.|<>
4126            two
4127            three
4128        "},
4129        vec!["first_completion", "second_completion"],
4130    )
4131    .await;
4132    cx.condition(|editor, _| editor.context_menu_visible())
4133        .await;
4134    let apply_additional_edits = cx.update_editor(|editor, cx| {
4135        editor.move_down(&MoveDown, cx);
4136        editor
4137            .confirm_completion(&ConfirmCompletion::default(), cx)
4138            .unwrap()
4139    });
4140    cx.assert_editor_state(indoc! {"
4141        one.second_completionˇ
4142        two
4143        three
4144    "});
4145
4146    handle_resolve_completion_request(
4147        &mut cx,
4148        Some((
4149            indoc! {"
4150                one.second_completion
4151                two
4152                threeˇ
4153            "},
4154            "\nadditional edit",
4155        )),
4156    )
4157    .await;
4158    apply_additional_edits.await.unwrap();
4159    cx.assert_editor_state(indoc! {"
4160        one.second_completionˇ
4161        two
4162        three
4163        additional edit
4164    "});
4165
4166    cx.set_state(indoc! {"
4167        one.second_completion
4168        twoˇ
4169        threeˇ
4170        additional edit
4171    "});
4172    cx.simulate_keystroke(" ");
4173    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4174    cx.simulate_keystroke("s");
4175    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4176
4177    cx.assert_editor_state(indoc! {"
4178        one.second_completion
4179        two sˇ
4180        three sˇ
4181        additional edit
4182    "});
4183    handle_completion_request(
4184        &mut cx,
4185        indoc! {"
4186            one.second_completion
4187            two s
4188            three <s|>
4189            additional edit
4190        "},
4191        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
4192    )
4193    .await;
4194    cx.condition(|editor, _| editor.context_menu_visible())
4195        .await;
4196
4197    cx.simulate_keystroke("i");
4198
4199    handle_completion_request(
4200        &mut cx,
4201        indoc! {"
4202            one.second_completion
4203            two si
4204            three <si|>
4205            additional edit
4206        "},
4207        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
4208    )
4209    .await;
4210    cx.condition(|editor, _| editor.context_menu_visible())
4211        .await;
4212
4213    let apply_additional_edits = cx.update_editor(|editor, cx| {
4214        editor
4215            .confirm_completion(&ConfirmCompletion::default(), cx)
4216            .unwrap()
4217    });
4218    cx.assert_editor_state(indoc! {"
4219        one.second_completion
4220        two sixth_completionˇ
4221        three sixth_completionˇ
4222        additional edit
4223    "});
4224
4225    handle_resolve_completion_request(&mut cx, None).await;
4226    apply_additional_edits.await.unwrap();
4227
4228    cx.update(|cx| {
4229        cx.update_global::<Settings, _, _>(|settings, _| {
4230            settings.show_completions_on_input = false;
4231        })
4232    });
4233    cx.set_state("editorˇ");
4234    cx.simulate_keystroke(".");
4235    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4236    cx.simulate_keystroke("c");
4237    cx.simulate_keystroke("l");
4238    cx.simulate_keystroke("o");
4239    cx.assert_editor_state("editor.cloˇ");
4240    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4241    cx.update_editor(|editor, cx| {
4242        editor.show_completions(&ShowCompletions, cx);
4243    });
4244    handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
4245    cx.condition(|editor, _| editor.context_menu_visible())
4246        .await;
4247    let apply_additional_edits = cx.update_editor(|editor, cx| {
4248        editor
4249            .confirm_completion(&ConfirmCompletion::default(), cx)
4250            .unwrap()
4251    });
4252    cx.assert_editor_state("editor.closeˇ");
4253    handle_resolve_completion_request(&mut cx, None).await;
4254    apply_additional_edits.await.unwrap();
4255
4256    // Handle completion request passing a marked string specifying where the completion
4257    // should be triggered from using '|' character, what range should be replaced, and what completions
4258    // should be returned using '<' and '>' to delimit the range
4259    async fn handle_completion_request<'a>(
4260        cx: &mut EditorLspTestContext<'a>,
4261        marked_string: &str,
4262        completions: Vec<&'static str>,
4263    ) {
4264        let complete_from_marker: TextRangeMarker = '|'.into();
4265        let replace_range_marker: TextRangeMarker = ('<', '>').into();
4266        let (_, mut marked_ranges) = marked_text_ranges_by(
4267            marked_string,
4268            vec![complete_from_marker.clone(), replace_range_marker.clone()],
4269        );
4270
4271        let complete_from_position =
4272            cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
4273        let replace_range =
4274            cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
4275
4276        cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
4277            let completions = completions.clone();
4278            async move {
4279                assert_eq!(params.text_document_position.text_document.uri, url.clone());
4280                assert_eq!(
4281                    params.text_document_position.position,
4282                    complete_from_position
4283                );
4284                Ok(Some(lsp::CompletionResponse::Array(
4285                    completions
4286                        .iter()
4287                        .map(|completion_text| lsp::CompletionItem {
4288                            label: completion_text.to_string(),
4289                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
4290                                range: replace_range,
4291                                new_text: completion_text.to_string(),
4292                            })),
4293                            ..Default::default()
4294                        })
4295                        .collect(),
4296                )))
4297            }
4298        })
4299        .next()
4300        .await;
4301    }
4302
4303    async fn handle_resolve_completion_request<'a>(
4304        cx: &mut EditorLspTestContext<'a>,
4305        edit: Option<(&'static str, &'static str)>,
4306    ) {
4307        let edit = edit.map(|(marked_string, new_text)| {
4308            let (_, marked_ranges) = marked_text_ranges(marked_string, false);
4309            let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
4310            vec![lsp::TextEdit::new(replace_range, new_text.to_string())]
4311        });
4312
4313        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
4314            let edit = edit.clone();
4315            async move {
4316                Ok(lsp::CompletionItem {
4317                    additional_text_edits: edit,
4318                    ..Default::default()
4319                })
4320            }
4321        })
4322        .next()
4323        .await;
4324    }
4325}
4326
4327#[gpui::test]
4328async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
4329    cx.update(|cx| cx.set_global(Settings::test(cx)));
4330    let language = Arc::new(Language::new(
4331        LanguageConfig {
4332            line_comment: Some("// ".into()),
4333            ..Default::default()
4334        },
4335        Some(tree_sitter_rust::language()),
4336    ));
4337
4338    let text = "
4339        fn a() {
4340            //b();
4341            // c();
4342            //  d();
4343        }
4344    "
4345    .unindent();
4346
4347    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
4348    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4349    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
4350
4351    view.update(cx, |editor, cx| {
4352        // If multiple selections intersect a line, the line is only
4353        // toggled once.
4354        editor.change_selections(None, cx, |s| {
4355            s.select_display_ranges([
4356                DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
4357                DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
4358            ])
4359        });
4360        editor.toggle_comments(&ToggleComments, cx);
4361        assert_eq!(
4362            editor.text(cx),
4363            "
4364                fn a() {
4365                    b();
4366                    c();
4367                     d();
4368                }
4369            "
4370            .unindent()
4371        );
4372
4373        // The comment prefix is inserted at the same column for every line
4374        // in a selection.
4375        editor.change_selections(None, cx, |s| {
4376            s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)])
4377        });
4378        editor.toggle_comments(&ToggleComments, cx);
4379        assert_eq!(
4380            editor.text(cx),
4381            "
4382                fn a() {
4383                    // b();
4384                    // c();
4385                    //  d();
4386                }
4387            "
4388            .unindent()
4389        );
4390
4391        // If a selection ends at the beginning of a line, that line is not toggled.
4392        editor.change_selections(None, cx, |s| {
4393            s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)])
4394        });
4395        editor.toggle_comments(&ToggleComments, cx);
4396        assert_eq!(
4397            editor.text(cx),
4398            "
4399                fn a() {
4400                    // b();
4401                    c();
4402                    //  d();
4403                }
4404            "
4405            .unindent()
4406        );
4407    });
4408}
4409
4410#[gpui::test]
4411async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
4412    let mut cx = EditorTestContext::new(cx);
4413
4414    let html_language = Arc::new(
4415        Language::new(
4416            LanguageConfig {
4417                name: "HTML".into(),
4418                block_comment: Some(("<!-- ".into(), " -->".into())),
4419                ..Default::default()
4420            },
4421            Some(tree_sitter_html::language()),
4422        )
4423        .with_injection_query(
4424            r#"
4425            (script_element
4426                (raw_text) @content
4427                (#set! "language" "javascript"))
4428            "#,
4429        )
4430        .unwrap(),
4431    );
4432
4433    let javascript_language = Arc::new(Language::new(
4434        LanguageConfig {
4435            name: "JavaScript".into(),
4436            line_comment: Some("// ".into()),
4437            ..Default::default()
4438        },
4439        Some(tree_sitter_javascript::language()),
4440    ));
4441
4442    let registry = Arc::new(LanguageRegistry::test());
4443    registry.add(html_language.clone());
4444    registry.add(javascript_language.clone());
4445
4446    cx.update_buffer(|buffer, cx| {
4447        buffer.set_language_registry(registry);
4448        buffer.set_language(Some(html_language), cx);
4449    });
4450
4451    // Toggle comments for empty selections
4452    cx.set_state(
4453        &r#"
4454            <p>A</p>ˇ
4455            <p>B</p>ˇ
4456            <p>C</p>ˇ
4457        "#
4458        .unindent(),
4459    );
4460    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
4461    cx.assert_editor_state(
4462        &r#"
4463            <!-- <p>A</p>ˇ -->
4464            <!-- <p>B</p>ˇ -->
4465            <!-- <p>C</p>ˇ -->
4466        "#
4467        .unindent(),
4468    );
4469    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
4470    cx.assert_editor_state(
4471        &r#"
4472            <p>A</p>ˇ
4473            <p>B</p>ˇ
4474            <p>C</p>ˇ
4475        "#
4476        .unindent(),
4477    );
4478
4479    // Toggle comments for mixture of empty and non-empty selections, where
4480    // multiple selections occupy a given line.
4481    cx.set_state(
4482        &r#"
4483            <p>A«</p>
4484            <p>ˇ»B</p>ˇ
4485            <p>C«</p>
4486            <p>ˇ»D</p>ˇ
4487        "#
4488        .unindent(),
4489    );
4490
4491    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
4492    cx.assert_editor_state(
4493        &r#"
4494            <!-- <p>A«</p>
4495            <p>ˇ»B</p>ˇ -->
4496            <!-- <p>C«</p>
4497            <p>ˇ»D</p>ˇ -->
4498        "#
4499        .unindent(),
4500    );
4501    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
4502    cx.assert_editor_state(
4503        &r#"
4504            <p>A«</p>
4505            <p>ˇ»B</p>ˇ
4506            <p>C«</p>
4507            <p>ˇ»D</p>ˇ
4508        "#
4509        .unindent(),
4510    );
4511
4512    // Toggle comments when different languages are active for different
4513    // selections.
4514    cx.set_state(
4515        &r#"
4516            ˇ<script>
4517                ˇvar x = new Y();
4518            ˇ</script>
4519        "#
4520        .unindent(),
4521    );
4522    cx.foreground().run_until_parked();
4523    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
4524    cx.assert_editor_state(
4525        &r#"
4526            <!-- ˇ<script> -->
4527                // ˇvar x = new Y();
4528            <!-- ˇ</script> -->
4529        "#
4530        .unindent(),
4531    );
4532}
4533
4534#[gpui::test]
4535fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
4536    cx.set_global(Settings::test(cx));
4537    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
4538    let multibuffer = cx.add_model(|cx| {
4539        let mut multibuffer = MultiBuffer::new(0);
4540        multibuffer.push_excerpts(
4541            buffer.clone(),
4542            [
4543                ExcerptRange {
4544                    context: Point::new(0, 0)..Point::new(0, 4),
4545                    primary: None,
4546                },
4547                ExcerptRange {
4548                    context: Point::new(1, 0)..Point::new(1, 4),
4549                    primary: None,
4550                },
4551            ],
4552            cx,
4553        );
4554        multibuffer
4555    });
4556
4557    assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
4558
4559    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
4560    view.update(cx, |view, cx| {
4561        assert_eq!(view.text(cx), "aaaa\nbbbb");
4562        view.change_selections(None, cx, |s| {
4563            s.select_ranges([
4564                Point::new(0, 0)..Point::new(0, 0),
4565                Point::new(1, 0)..Point::new(1, 0),
4566            ])
4567        });
4568
4569        view.handle_input("X", cx);
4570        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
4571        assert_eq!(
4572            view.selections.ranges(cx),
4573            [
4574                Point::new(0, 1)..Point::new(0, 1),
4575                Point::new(1, 1)..Point::new(1, 1),
4576            ]
4577        )
4578    });
4579}
4580
4581#[gpui::test]
4582fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
4583    cx.set_global(Settings::test(cx));
4584    let markers = vec![('[', ']').into(), ('(', ')').into()];
4585    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
4586        indoc! {"
4587            [aaaa
4588            (bbbb]
4589            cccc)",
4590        },
4591        markers.clone(),
4592    );
4593    let excerpt_ranges = markers.into_iter().map(|marker| {
4594        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
4595        ExcerptRange {
4596            context,
4597            primary: None,
4598        }
4599    });
4600    let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
4601    let multibuffer = cx.add_model(|cx| {
4602        let mut multibuffer = MultiBuffer::new(0);
4603        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
4604        multibuffer
4605    });
4606
4607    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
4608    view.update(cx, |view, cx| {
4609        let (expected_text, selection_ranges) = marked_text_ranges(
4610            indoc! {"
4611                aaaa
4612                bˇbbb
4613                bˇbbˇb
4614                cccc"
4615            },
4616            true,
4617        );
4618        assert_eq!(view.text(cx), expected_text);
4619        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
4620
4621        view.handle_input("X", cx);
4622
4623        let (expected_text, expected_selections) = marked_text_ranges(
4624            indoc! {"
4625                aaaa
4626                bXˇbbXb
4627                bXˇbbXˇb
4628                cccc"
4629            },
4630            false,
4631        );
4632        assert_eq!(view.text(cx), expected_text);
4633        assert_eq!(view.selections.ranges(cx), expected_selections);
4634
4635        view.newline(&Newline, cx);
4636        let (expected_text, expected_selections) = marked_text_ranges(
4637            indoc! {"
4638                aaaa
4639                bX
4640                ˇbbX
4641                b
4642                bX
4643                ˇbbX
4644                ˇb
4645                cccc"
4646            },
4647            false,
4648        );
4649        assert_eq!(view.text(cx), expected_text);
4650        assert_eq!(view.selections.ranges(cx), expected_selections);
4651    });
4652}
4653
4654#[gpui::test]
4655fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
4656    cx.set_global(Settings::test(cx));
4657    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
4658    let mut excerpt1_id = None;
4659    let multibuffer = cx.add_model(|cx| {
4660        let mut multibuffer = MultiBuffer::new(0);
4661        excerpt1_id = multibuffer
4662            .push_excerpts(
4663                buffer.clone(),
4664                [
4665                    ExcerptRange {
4666                        context: Point::new(0, 0)..Point::new(1, 4),
4667                        primary: None,
4668                    },
4669                    ExcerptRange {
4670                        context: Point::new(1, 0)..Point::new(2, 4),
4671                        primary: None,
4672                    },
4673                ],
4674                cx,
4675            )
4676            .into_iter()
4677            .next();
4678        multibuffer
4679    });
4680    assert_eq!(
4681        multibuffer.read(cx).read(cx).text(),
4682        "aaaa\nbbbb\nbbbb\ncccc"
4683    );
4684    let (_, editor) = cx.add_window(Default::default(), |cx| {
4685        let mut editor = build_editor(multibuffer.clone(), cx);
4686        let snapshot = editor.snapshot(cx);
4687        editor.change_selections(None, cx, |s| {
4688            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
4689        });
4690        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
4691        assert_eq!(
4692            editor.selections.ranges(cx),
4693            [
4694                Point::new(1, 3)..Point::new(1, 3),
4695                Point::new(2, 1)..Point::new(2, 1),
4696            ]
4697        );
4698        editor
4699    });
4700
4701    // Refreshing selections is a no-op when excerpts haven't changed.
4702    editor.update(cx, |editor, cx| {
4703        editor.change_selections(None, cx, |s| {
4704            s.refresh();
4705        });
4706        assert_eq!(
4707            editor.selections.ranges(cx),
4708            [
4709                Point::new(1, 3)..Point::new(1, 3),
4710                Point::new(2, 1)..Point::new(2, 1),
4711            ]
4712        );
4713    });
4714
4715    multibuffer.update(cx, |multibuffer, cx| {
4716        multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
4717    });
4718    editor.update(cx, |editor, cx| {
4719        // Removing an excerpt causes the first selection to become degenerate.
4720        assert_eq!(
4721            editor.selections.ranges(cx),
4722            [
4723                Point::new(0, 0)..Point::new(0, 0),
4724                Point::new(0, 1)..Point::new(0, 1)
4725            ]
4726        );
4727
4728        // Refreshing selections will relocate the first selection to the original buffer
4729        // location.
4730        editor.change_selections(None, cx, |s| {
4731            s.refresh();
4732        });
4733        assert_eq!(
4734            editor.selections.ranges(cx),
4735            [
4736                Point::new(0, 1)..Point::new(0, 1),
4737                Point::new(0, 3)..Point::new(0, 3)
4738            ]
4739        );
4740        assert!(editor.selections.pending_anchor().is_some());
4741    });
4742}
4743
4744#[gpui::test]
4745fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) {
4746    cx.set_global(Settings::test(cx));
4747    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
4748    let mut excerpt1_id = None;
4749    let multibuffer = cx.add_model(|cx| {
4750        let mut multibuffer = MultiBuffer::new(0);
4751        excerpt1_id = multibuffer
4752            .push_excerpts(
4753                buffer.clone(),
4754                [
4755                    ExcerptRange {
4756                        context: Point::new(0, 0)..Point::new(1, 4),
4757                        primary: None,
4758                    },
4759                    ExcerptRange {
4760                        context: Point::new(1, 0)..Point::new(2, 4),
4761                        primary: None,
4762                    },
4763                ],
4764                cx,
4765            )
4766            .into_iter()
4767            .next();
4768        multibuffer
4769    });
4770    assert_eq!(
4771        multibuffer.read(cx).read(cx).text(),
4772        "aaaa\nbbbb\nbbbb\ncccc"
4773    );
4774    let (_, editor) = cx.add_window(Default::default(), |cx| {
4775        let mut editor = build_editor(multibuffer.clone(), cx);
4776        let snapshot = editor.snapshot(cx);
4777        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
4778        assert_eq!(
4779            editor.selections.ranges(cx),
4780            [Point::new(1, 3)..Point::new(1, 3)]
4781        );
4782        editor
4783    });
4784
4785    multibuffer.update(cx, |multibuffer, cx| {
4786        multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
4787    });
4788    editor.update(cx, |editor, cx| {
4789        assert_eq!(
4790            editor.selections.ranges(cx),
4791            [Point::new(0, 0)..Point::new(0, 0)]
4792        );
4793
4794        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
4795        editor.change_selections(None, cx, |s| {
4796            s.refresh();
4797        });
4798        assert_eq!(
4799            editor.selections.ranges(cx),
4800            [Point::new(0, 3)..Point::new(0, 3)]
4801        );
4802        assert!(editor.selections.pending_anchor().is_some());
4803    });
4804}
4805
4806#[gpui::test]
4807async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
4808    cx.update(|cx| cx.set_global(Settings::test(cx)));
4809    let language = Arc::new(
4810        Language::new(
4811            LanguageConfig {
4812                brackets: vec![
4813                    BracketPair {
4814                        start: "{".to_string(),
4815                        end: "}".to_string(),
4816                        close: true,
4817                        newline: true,
4818                    },
4819                    BracketPair {
4820                        start: "/* ".to_string(),
4821                        end: " */".to_string(),
4822                        close: true,
4823                        newline: true,
4824                    },
4825                ],
4826                ..Default::default()
4827            },
4828            Some(tree_sitter_rust::language()),
4829        )
4830        .with_indents_query("")
4831        .unwrap(),
4832    );
4833
4834    let text = concat!(
4835        "{   }\n",     //
4836        "  x\n",       //
4837        "  /*   */\n", //
4838        "x\n",         //
4839        "{{} }\n",     //
4840    );
4841
4842    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
4843    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4844    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
4845    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
4846        .await;
4847
4848    view.update(cx, |view, cx| {
4849        view.change_selections(None, cx, |s| {
4850            s.select_display_ranges([
4851                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
4852                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
4853                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
4854            ])
4855        });
4856        view.newline(&Newline, cx);
4857
4858        assert_eq!(
4859            view.buffer().read(cx).read(cx).text(),
4860            concat!(
4861                "{ \n",    // Suppress rustfmt
4862                "\n",      //
4863                "}\n",     //
4864                "  x\n",   //
4865                "  /* \n", //
4866                "  \n",    //
4867                "  */\n",  //
4868                "x\n",     //
4869                "{{} \n",  //
4870                "}\n",     //
4871            )
4872        );
4873    });
4874}
4875
4876#[gpui::test]
4877fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
4878    let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
4879
4880    cx.set_global(Settings::test(cx));
4881    let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
4882
4883    editor.update(cx, |editor, cx| {
4884        struct Type1;
4885        struct Type2;
4886
4887        let buffer = buffer.read(cx).snapshot(cx);
4888
4889        let anchor_range =
4890            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
4891
4892        editor.highlight_background::<Type1>(
4893            vec![
4894                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
4895                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
4896                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
4897                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
4898            ],
4899            |_| Color::red(),
4900            cx,
4901        );
4902        editor.highlight_background::<Type2>(
4903            vec![
4904                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
4905                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
4906                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
4907                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
4908            ],
4909            |_| Color::green(),
4910            cx,
4911        );
4912
4913        let snapshot = editor.snapshot(cx);
4914        let mut highlighted_ranges = editor.background_highlights_in_range(
4915            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
4916            &snapshot,
4917            cx.global::<Settings>().theme.as_ref(),
4918        );
4919        // Enforce a consistent ordering based on color without relying on the ordering of the
4920        // highlight's `TypeId` which is non-deterministic.
4921        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
4922        assert_eq!(
4923            highlighted_ranges,
4924            &[
4925                (
4926                    DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
4927                    Color::green(),
4928                ),
4929                (
4930                    DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
4931                    Color::green(),
4932                ),
4933                (
4934                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
4935                    Color::red(),
4936                ),
4937                (
4938                    DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
4939                    Color::red(),
4940                ),
4941            ]
4942        );
4943        assert_eq!(
4944            editor.background_highlights_in_range(
4945                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
4946                &snapshot,
4947                cx.global::<Settings>().theme.as_ref(),
4948            ),
4949            &[(
4950                DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
4951                Color::red(),
4952            )]
4953        );
4954    });
4955}
4956
4957#[gpui::test]
4958fn test_following(cx: &mut gpui::MutableAppContext) {
4959    let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
4960
4961    cx.set_global(Settings::test(cx));
4962
4963    let (_, leader) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
4964    let (_, follower) = cx.add_window(
4965        WindowOptions {
4966            bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
4967            ..Default::default()
4968        },
4969        |cx| build_editor(buffer.clone(), cx),
4970    );
4971
4972    let pending_update = Rc::new(RefCell::new(None));
4973    follower.update(cx, {
4974        let update = pending_update.clone();
4975        |_, cx| {
4976            cx.subscribe(&leader, move |_, leader, event, cx| {
4977                leader
4978                    .read(cx)
4979                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
4980            })
4981            .detach();
4982        }
4983    });
4984
4985    // Update the selections only
4986    leader.update(cx, |leader, cx| {
4987        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
4988    });
4989    follower.update(cx, |follower, cx| {
4990        follower
4991            .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
4992            .unwrap();
4993    });
4994    assert_eq!(follower.read(cx).selections.ranges(cx), vec![1..1]);
4995
4996    // Update the scroll position only
4997    leader.update(cx, |leader, cx| {
4998        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
4999    });
5000    follower.update(cx, |follower, cx| {
5001        follower
5002            .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
5003            .unwrap();
5004    });
5005    assert_eq!(
5006        follower.update(cx, |follower, cx| follower.scroll_position(cx)),
5007        vec2f(1.5, 3.5)
5008    );
5009
5010    // Update the selections and scroll position
5011    leader.update(cx, |leader, cx| {
5012        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
5013        leader.request_autoscroll(Autoscroll::Newest, cx);
5014        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
5015    });
5016    follower.update(cx, |follower, cx| {
5017        let initial_scroll_position = follower.scroll_position(cx);
5018        follower
5019            .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
5020            .unwrap();
5021        assert_eq!(follower.scroll_position(cx), initial_scroll_position);
5022        assert!(follower.autoscroll_request.is_some());
5023    });
5024    assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0]);
5025
5026    // Creating a pending selection that precedes another selection
5027    leader.update(cx, |leader, cx| {
5028        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
5029        leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
5030    });
5031    follower.update(cx, |follower, cx| {
5032        follower
5033            .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
5034            .unwrap();
5035    });
5036    assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0, 1..1]);
5037
5038    // Extend the pending selection so that it surrounds another selection
5039    leader.update(cx, |leader, cx| {
5040        leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
5041    });
5042    follower.update(cx, |follower, cx| {
5043        follower
5044            .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
5045            .unwrap();
5046    });
5047    assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..2]);
5048}
5049
5050#[test]
5051fn test_combine_syntax_and_fuzzy_match_highlights() {
5052    let string = "abcdefghijklmnop";
5053    let syntax_ranges = [
5054        (
5055            0..3,
5056            HighlightStyle {
5057                color: Some(Color::red()),
5058                ..Default::default()
5059            },
5060        ),
5061        (
5062            4..8,
5063            HighlightStyle {
5064                color: Some(Color::green()),
5065                ..Default::default()
5066            },
5067        ),
5068    ];
5069    let match_indices = [4, 6, 7, 8];
5070    assert_eq!(
5071        combine_syntax_and_fuzzy_match_highlights(
5072            string,
5073            Default::default(),
5074            syntax_ranges.into_iter(),
5075            &match_indices,
5076        ),
5077        &[
5078            (
5079                0..3,
5080                HighlightStyle {
5081                    color: Some(Color::red()),
5082                    ..Default::default()
5083                },
5084            ),
5085            (
5086                4..5,
5087                HighlightStyle {
5088                    color: Some(Color::green()),
5089                    weight: Some(fonts::Weight::BOLD),
5090                    ..Default::default()
5091                },
5092            ),
5093            (
5094                5..6,
5095                HighlightStyle {
5096                    color: Some(Color::green()),
5097                    ..Default::default()
5098                },
5099            ),
5100            (
5101                6..8,
5102                HighlightStyle {
5103                    color: Some(Color::green()),
5104                    weight: Some(fonts::Weight::BOLD),
5105                    ..Default::default()
5106                },
5107            ),
5108            (
5109                8..9,
5110                HighlightStyle {
5111                    weight: Some(fonts::Weight::BOLD),
5112                    ..Default::default()
5113                },
5114            ),
5115        ]
5116    );
5117}
5118
5119fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
5120    let point = DisplayPoint::new(row as u32, column as u32);
5121    point..point
5122}
5123
5124fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
5125    let (text, ranges) = marked_text_ranges(marked_text, true);
5126    assert_eq!(view.text(cx), text);
5127    assert_eq!(
5128        view.selections.ranges(cx),
5129        ranges,
5130        "Assert selections are {}",
5131        marked_text
5132    );
5133}