editor_tests.rs

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