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