editor_tests.rs

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