editor_tests.rs

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