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