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