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