editor_tests.rs

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