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::{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: vec![
3006                    BracketPair {
3007                        start: "{".to_string(),
3008                        end: "}".to_string(),
3009                        close: false,
3010                        newline: true,
3011                    },
3012                    BracketPair {
3013                        start: "(".to_string(),
3014                        end: ")".to_string(),
3015                        close: false,
3016                        newline: true,
3017                    },
3018                ],
3019                ..Default::default()
3020            },
3021            Some(tree_sitter_rust::language()),
3022        )
3023        .with_indents_query(
3024            r#"
3025                (_ "(" ")" @end) @indent
3026                (_ "{" "}" @end) @indent
3027            "#,
3028        )
3029        .unwrap(),
3030    );
3031
3032    let text = "fn a() {}";
3033
3034    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3035    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3036    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3037    editor
3038        .condition(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
3039        .await;
3040
3041    editor.update(cx, |editor, cx| {
3042        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
3043        editor.newline(&Newline, cx);
3044        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
3045        assert_eq!(
3046            editor.selections.ranges(cx),
3047            &[
3048                Point::new(1, 4)..Point::new(1, 4),
3049                Point::new(3, 4)..Point::new(3, 4),
3050                Point::new(5, 0)..Point::new(5, 0)
3051            ]
3052        );
3053    });
3054}
3055
3056#[gpui::test]
3057async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
3058    let mut cx = EditorTestContext::new(cx);
3059
3060    let language = Arc::new(Language::new(
3061        LanguageConfig {
3062            brackets: vec![
3063                BracketPair {
3064                    start: "{".to_string(),
3065                    end: "}".to_string(),
3066                    close: true,
3067                    newline: true,
3068                },
3069                BracketPair {
3070                    start: "(".to_string(),
3071                    end: ")".to_string(),
3072                    close: true,
3073                    newline: true,
3074                },
3075                BracketPair {
3076                    start: "/*".to_string(),
3077                    end: " */".to_string(),
3078                    close: true,
3079                    newline: true,
3080                },
3081                BracketPair {
3082                    start: "[".to_string(),
3083                    end: "]".to_string(),
3084                    close: false,
3085                    newline: true,
3086                },
3087                BracketPair {
3088                    start: "\"".to_string(),
3089                    end: "\"".to_string(),
3090                    close: true,
3091                    newline: false,
3092                },
3093            ],
3094            autoclose_before: "})]".to_string(),
3095            ..Default::default()
3096        },
3097        Some(tree_sitter_rust::language()),
3098    ));
3099
3100    let registry = Arc::new(LanguageRegistry::test());
3101    registry.add(language.clone());
3102    cx.update_buffer(|buffer, cx| {
3103        buffer.set_language_registry(registry);
3104        buffer.set_language(Some(language), cx);
3105    });
3106
3107    cx.set_state(
3108        &r#"
3109            🏀ˇ
3110            εˇ
3111            ❤️ˇ
3112        "#
3113        .unindent(),
3114    );
3115
3116    // autoclose multiple nested brackets at multiple cursors
3117    cx.update_editor(|view, cx| {
3118        view.handle_input("{", cx);
3119        view.handle_input("{", cx);
3120        view.handle_input("{", cx);
3121    });
3122    cx.assert_editor_state(
3123        &"
3124            🏀{{{ˇ}}}
3125            ε{{{ˇ}}}
3126            ❤️{{{ˇ}}}
3127        "
3128        .unindent(),
3129    );
3130
3131    // insert a different closing bracket
3132    cx.update_editor(|view, cx| {
3133        view.handle_input(")", cx);
3134    });
3135    cx.assert_editor_state(
3136        &"
3137            🏀{{{)ˇ}}}
3138            ε{{{)ˇ}}}
3139            ❤️{{{)ˇ}}}
3140        "
3141        .unindent(),
3142    );
3143
3144    // skip over the auto-closed brackets when typing a closing bracket
3145    cx.update_editor(|view, cx| {
3146        view.move_right(&MoveRight, cx);
3147        view.handle_input("}", cx);
3148        view.handle_input("}", cx);
3149        view.handle_input("}", cx);
3150    });
3151    cx.assert_editor_state(
3152        &"
3153            🏀{{{)}}}}ˇ
3154            ε{{{)}}}}ˇ
3155            ❤️{{{)}}}}ˇ
3156        "
3157        .unindent(),
3158    );
3159
3160    // autoclose multi-character pairs
3161    cx.set_state(
3162        &"
3163            ˇ
3164            ˇ
3165        "
3166        .unindent(),
3167    );
3168    cx.update_editor(|view, cx| {
3169        view.handle_input("/", cx);
3170        view.handle_input("*", cx);
3171    });
3172    cx.assert_editor_state(
3173        &"
3174            /*ˇ */
3175            /*ˇ */
3176        "
3177        .unindent(),
3178    );
3179
3180    // one cursor autocloses a multi-character pair, one cursor
3181    // does not autoclose.
3182    cx.set_state(
3183        &"
31843185            ˇ
3186        "
3187        .unindent(),
3188    );
3189    cx.update_editor(|view, cx| view.handle_input("*", cx));
3190    cx.assert_editor_state(
3191        &"
3192            /*ˇ */
31933194        "
3195        .unindent(),
3196    );
3197
3198    // Don't autoclose if the next character isn't whitespace and isn't
3199    // listed in the language's "autoclose_before" section.
3200    cx.set_state("ˇa b");
3201    cx.update_editor(|view, cx| view.handle_input("{", cx));
3202    cx.assert_editor_state("{ˇa b");
3203
3204    // Don't autoclose if `close` is false for the bracket pair
3205    cx.set_state("ˇ");
3206    cx.update_editor(|view, cx| view.handle_input("[", cx));
3207    cx.assert_editor_state("");
3208
3209    // Surround with brackets if text is selected
3210    cx.set_state("«aˇ» b");
3211    cx.update_editor(|view, cx| view.handle_input("{", cx));
3212    cx.assert_editor_state("{«aˇ»} b");
3213
3214    // Autclose pair where the start and end characters are the same
3215    cx.set_state("");
3216    cx.update_editor(|view, cx| view.handle_input("\"", cx));
3217    cx.assert_editor_state("a\"ˇ\"");
3218    cx.update_editor(|view, cx| view.handle_input("\"", cx));
3219    cx.assert_editor_state("a\"\"ˇ");
3220}
3221
3222#[gpui::test]
3223async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
3224    let mut cx = EditorTestContext::new(cx);
3225
3226    let html_language = Arc::new(
3227        Language::new(
3228            LanguageConfig {
3229                name: "HTML".into(),
3230                brackets: vec![
3231                    BracketPair {
3232                        start: "<".into(),
3233                        end: ">".into(),
3234                        close: true,
3235                        ..Default::default()
3236                    },
3237                    BracketPair {
3238                        start: "{".into(),
3239                        end: "}".into(),
3240                        close: true,
3241                        ..Default::default()
3242                    },
3243                    BracketPair {
3244                        start: "(".into(),
3245                        end: ")".into(),
3246                        close: true,
3247                        ..Default::default()
3248                    },
3249                ],
3250                autoclose_before: "})]>".into(),
3251                ..Default::default()
3252            },
3253            Some(tree_sitter_html::language()),
3254        )
3255        .with_injection_query(
3256            r#"
3257            (script_element
3258                (raw_text) @content
3259                (#set! "language" "javascript"))
3260            "#,
3261        )
3262        .unwrap(),
3263    );
3264
3265    let javascript_language = Arc::new(Language::new(
3266        LanguageConfig {
3267            name: "JavaScript".into(),
3268            brackets: vec![
3269                BracketPair {
3270                    start: "/*".into(),
3271                    end: " */".into(),
3272                    close: true,
3273                    ..Default::default()
3274                },
3275                BracketPair {
3276                    start: "{".into(),
3277                    end: "}".into(),
3278                    close: true,
3279                    ..Default::default()
3280                },
3281                BracketPair {
3282                    start: "(".into(),
3283                    end: ")".into(),
3284                    close: true,
3285                    ..Default::default()
3286                },
3287            ],
3288            autoclose_before: "})]>".into(),
3289            ..Default::default()
3290        },
3291        Some(tree_sitter_javascript::language()),
3292    ));
3293
3294    let registry = Arc::new(LanguageRegistry::test());
3295    registry.add(html_language.clone());
3296    registry.add(javascript_language.clone());
3297
3298    cx.update_buffer(|buffer, cx| {
3299        buffer.set_language_registry(registry);
3300        buffer.set_language(Some(html_language), cx);
3301    });
3302
3303    cx.set_state(
3304        &r#"
3305            <body>ˇ
3306                <script>
3307                    var x = 1;ˇ
3308                </script>
3309            </body>ˇ
3310        "#
3311        .unindent(),
3312    );
3313
3314    // Precondition: different languages are active at different locations.
3315    cx.update_editor(|editor, cx| {
3316        let snapshot = editor.snapshot(cx);
3317        let cursors = editor.selections.ranges::<usize>(cx);
3318        let languages = cursors
3319            .iter()
3320            .map(|c| snapshot.language_at(c.start).unwrap().name())
3321            .collect::<Vec<_>>();
3322        assert_eq!(
3323            languages,
3324            &["HTML".into(), "JavaScript".into(), "HTML".into()]
3325        );
3326    });
3327
3328    // Angle brackets autoclose in HTML, but not JavaScript.
3329    cx.update_editor(|editor, cx| {
3330        editor.handle_input("<", cx);
3331        editor.handle_input("a", cx);
3332    });
3333    cx.assert_editor_state(
3334        &r#"
3335            <body><aˇ>
3336                <script>
3337                    var x = 1;<aˇ
3338                </script>
3339            </body><aˇ>
3340        "#
3341        .unindent(),
3342    );
3343
3344    // Curly braces and parens autoclose in both HTML and JavaScript.
3345    cx.update_editor(|editor, cx| {
3346        editor.handle_input(" b=", cx);
3347        editor.handle_input("{", cx);
3348        editor.handle_input("c", cx);
3349        editor.handle_input("(", cx);
3350    });
3351    cx.assert_editor_state(
3352        &r#"
3353            <body><a b={c(ˇ)}>
3354                <script>
3355                    var x = 1;<a b={c(ˇ)}
3356                </script>
3357            </body><a b={c(ˇ)}>
3358        "#
3359        .unindent(),
3360    );
3361
3362    // Brackets that were already autoclosed are skipped.
3363    cx.update_editor(|editor, cx| {
3364        editor.handle_input(")", cx);
3365        editor.handle_input("d", cx);
3366        editor.handle_input("}", cx);
3367    });
3368    cx.assert_editor_state(
3369        &r#"
3370            <body><a b={c()d}ˇ>
3371                <script>
3372                    var x = 1;<a b={c()d}ˇ
3373                </script>
3374            </body><a b={c()d}ˇ>
3375        "#
3376        .unindent(),
3377    );
3378    cx.update_editor(|editor, cx| {
3379        editor.handle_input(">", cx);
3380    });
3381    cx.assert_editor_state(
3382        &r#"
3383            <body><a b={c()d}>ˇ
3384                <script>
3385                    var x = 1;<a b={c()d}>ˇ
3386                </script>
3387            </body><a b={c()d}>ˇ
3388        "#
3389        .unindent(),
3390    );
3391
3392    // Reset
3393    cx.set_state(
3394        &r#"
3395            <body>ˇ
3396                <script>
3397                    var x = 1;ˇ
3398                </script>
3399            </body>ˇ
3400        "#
3401        .unindent(),
3402    );
3403
3404    cx.update_editor(|editor, cx| {
3405        editor.handle_input("<", cx);
3406    });
3407    cx.assert_editor_state(
3408        &r#"
3409            <body><ˇ>
3410                <script>
3411                    var x = 1;<ˇ
3412                </script>
3413            </body><ˇ>
3414        "#
3415        .unindent(),
3416    );
3417
3418    // When backspacing, the closing angle brackets are removed.
3419    cx.update_editor(|editor, cx| {
3420        editor.backspace(&Backspace, cx);
3421    });
3422    cx.assert_editor_state(
3423        &r#"
3424            <body>ˇ
3425                <script>
3426                    var x = 1;ˇ
3427                </script>
3428            </body>ˇ
3429        "#
3430        .unindent(),
3431    );
3432
3433    // Block comments autoclose in JavaScript, but not HTML.
3434    cx.update_editor(|editor, cx| {
3435        editor.handle_input("/", cx);
3436        editor.handle_input("*", cx);
3437    });
3438    cx.assert_editor_state(
3439        &r#"
3440            <body>/*ˇ
3441                <script>
3442                    var x = 1;/*ˇ */
3443                </script>
3444            </body>/*ˇ
3445        "#
3446        .unindent(),
3447    );
3448}
3449
3450#[gpui::test]
3451async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
3452    cx.update(|cx| cx.set_global(Settings::test(cx)));
3453    let language = Arc::new(Language::new(
3454        LanguageConfig {
3455            brackets: vec![BracketPair {
3456                start: "{".to_string(),
3457                end: "}".to_string(),
3458                close: true,
3459                newline: true,
3460            }],
3461            ..Default::default()
3462        },
3463        Some(tree_sitter_rust::language()),
3464    ));
3465
3466    let text = r#"
3467        a
3468        b
3469        c
3470    "#
3471    .unindent();
3472
3473    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3474    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3475    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
3476    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
3477        .await;
3478
3479    view.update(cx, |view, cx| {
3480        view.change_selections(None, cx, |s| {
3481            s.select_display_ranges([
3482                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3483                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
3484                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
3485            ])
3486        });
3487
3488        view.handle_input("{", cx);
3489        view.handle_input("{", cx);
3490        view.handle_input("{", cx);
3491        assert_eq!(
3492            view.text(cx),
3493            "
3494                {{{a}}}
3495                {{{b}}}
3496                {{{c}}}
3497            "
3498            .unindent()
3499        );
3500        assert_eq!(
3501            view.selections.display_ranges(cx),
3502            [
3503                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
3504                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
3505                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
3506            ]
3507        );
3508
3509        view.undo(&Undo, cx);
3510        view.undo(&Undo, cx);
3511        view.undo(&Undo, cx);
3512        assert_eq!(
3513            view.text(cx),
3514            "
3515                a
3516                b
3517                c
3518            "
3519            .unindent()
3520        );
3521        assert_eq!(
3522            view.selections.display_ranges(cx),
3523            [
3524                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3525                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
3526                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
3527            ]
3528        );
3529    });
3530}
3531
3532#[gpui::test]
3533async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
3534    cx.update(|cx| cx.set_global(Settings::test(cx)));
3535    let language = Arc::new(Language::new(
3536        LanguageConfig {
3537            brackets: vec![BracketPair {
3538                start: "{".to_string(),
3539                end: "}".to_string(),
3540                close: true,
3541                newline: true,
3542            }],
3543            autoclose_before: "}".to_string(),
3544            ..Default::default()
3545        },
3546        Some(tree_sitter_rust::language()),
3547    ));
3548
3549    let text = r#"
3550        a
3551        b
3552        c
3553    "#
3554    .unindent();
3555
3556    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3557    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3558    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3559    editor
3560        .condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
3561        .await;
3562
3563    editor.update(cx, |editor, cx| {
3564        editor.change_selections(None, cx, |s| {
3565            s.select_ranges([
3566                Point::new(0, 1)..Point::new(0, 1),
3567                Point::new(1, 1)..Point::new(1, 1),
3568                Point::new(2, 1)..Point::new(2, 1),
3569            ])
3570        });
3571
3572        editor.handle_input("{", cx);
3573        editor.handle_input("{", cx);
3574        editor.handle_input("_", cx);
3575        assert_eq!(
3576            editor.text(cx),
3577            "
3578                a{{_}}
3579                b{{_}}
3580                c{{_}}
3581            "
3582            .unindent()
3583        );
3584        assert_eq!(
3585            editor.selections.ranges::<Point>(cx),
3586            [
3587                Point::new(0, 4)..Point::new(0, 4),
3588                Point::new(1, 4)..Point::new(1, 4),
3589                Point::new(2, 4)..Point::new(2, 4)
3590            ]
3591        );
3592
3593        editor.backspace(&Default::default(), cx);
3594        editor.backspace(&Default::default(), cx);
3595        assert_eq!(
3596            editor.text(cx),
3597            "
3598                a{}
3599                b{}
3600                c{}
3601            "
3602            .unindent()
3603        );
3604        assert_eq!(
3605            editor.selections.ranges::<Point>(cx),
3606            [
3607                Point::new(0, 2)..Point::new(0, 2),
3608                Point::new(1, 2)..Point::new(1, 2),
3609                Point::new(2, 2)..Point::new(2, 2)
3610            ]
3611        );
3612
3613        editor.delete_to_previous_word_start(&Default::default(), cx);
3614        assert_eq!(
3615            editor.text(cx),
3616            "
3617                a
3618                b
3619                c
3620            "
3621            .unindent()
3622        );
3623        assert_eq!(
3624            editor.selections.ranges::<Point>(cx),
3625            [
3626                Point::new(0, 1)..Point::new(0, 1),
3627                Point::new(1, 1)..Point::new(1, 1),
3628                Point::new(2, 1)..Point::new(2, 1)
3629            ]
3630        );
3631    });
3632}
3633
3634#[gpui::test]
3635async fn test_snippets(cx: &mut gpui::TestAppContext) {
3636    cx.update(|cx| cx.set_global(Settings::test(cx)));
3637
3638    let (text, insertion_ranges) = marked_text_ranges(
3639        indoc! {"
3640            a.ˇ b
3641            a.ˇ b
3642            a.ˇ b
3643        "},
3644        false,
3645    );
3646
3647    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
3648    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3649
3650    editor.update(cx, |editor, cx| {
3651        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
3652
3653        editor
3654            .insert_snippet(&insertion_ranges, snippet, cx)
3655            .unwrap();
3656
3657        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
3658            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
3659            assert_eq!(editor.text(cx), expected_text);
3660            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
3661        }
3662
3663        assert(
3664            editor,
3665            cx,
3666            indoc! {"
3667                a.f(«one», two, «three») b
3668                a.f(«one», two, «three») b
3669                a.f(«one», two, «three») b
3670            "},
3671        );
3672
3673        // Can't move earlier than the first tab stop
3674        assert!(!editor.move_to_prev_snippet_tabstop(cx));
3675        assert(
3676            editor,
3677            cx,
3678            indoc! {"
3679                a.f(«one», two, «three») b
3680                a.f(«one», two, «three») b
3681                a.f(«one», two, «three») b
3682            "},
3683        );
3684
3685        assert!(editor.move_to_next_snippet_tabstop(cx));
3686        assert(
3687            editor,
3688            cx,
3689            indoc! {"
3690                a.f(one, «two», three) b
3691                a.f(one, «two», three) b
3692                a.f(one, «two», three) b
3693            "},
3694        );
3695
3696        editor.move_to_prev_snippet_tabstop(cx);
3697        assert(
3698            editor,
3699            cx,
3700            indoc! {"
3701                a.f(«one», two, «three») b
3702                a.f(«one», two, «three») b
3703                a.f(«one», two, «three») b
3704            "},
3705        );
3706
3707        assert!(editor.move_to_next_snippet_tabstop(cx));
3708        assert(
3709            editor,
3710            cx,
3711            indoc! {"
3712                a.f(one, «two», three) b
3713                a.f(one, «two», three) b
3714                a.f(one, «two», three) b
3715            "},
3716        );
3717        assert!(editor.move_to_next_snippet_tabstop(cx));
3718        assert(
3719            editor,
3720            cx,
3721            indoc! {"
3722                a.f(one, two, three)ˇ b
3723                a.f(one, two, three)ˇ b
3724                a.f(one, two, three)ˇ b
3725            "},
3726        );
3727
3728        // As soon as the last tab stop is reached, snippet state is gone
3729        editor.move_to_prev_snippet_tabstop(cx);
3730        assert(
3731            editor,
3732            cx,
3733            indoc! {"
3734                a.f(one, two, three)ˇ b
3735                a.f(one, two, three)ˇ b
3736                a.f(one, two, three)ˇ b
3737            "},
3738        );
3739    });
3740}
3741
3742#[gpui::test]
3743async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
3744    cx.foreground().forbid_parking();
3745
3746    let mut language = Language::new(
3747        LanguageConfig {
3748            name: "Rust".into(),
3749            path_suffixes: vec!["rs".to_string()],
3750            ..Default::default()
3751        },
3752        Some(tree_sitter_rust::language()),
3753    );
3754    let mut fake_servers = language
3755        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3756            capabilities: lsp::ServerCapabilities {
3757                document_formatting_provider: Some(lsp::OneOf::Left(true)),
3758                ..Default::default()
3759            },
3760            ..Default::default()
3761        }))
3762        .await;
3763
3764    let fs = FakeFs::new(cx.background());
3765    fs.insert_file("/file.rs", Default::default()).await;
3766
3767    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
3768    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
3769    let buffer = project
3770        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
3771        .await
3772        .unwrap();
3773
3774    cx.foreground().start_waiting();
3775    let fake_server = fake_servers.next().await.unwrap();
3776
3777    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3778    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3779    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
3780    assert!(cx.read(|cx| editor.is_dirty(cx)));
3781
3782    let save = cx.update(|cx| editor.save(project.clone(), cx));
3783    fake_server
3784        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
3785            assert_eq!(
3786                params.text_document.uri,
3787                lsp::Url::from_file_path("/file.rs").unwrap()
3788            );
3789            assert_eq!(params.options.tab_size, 4);
3790            Ok(Some(vec![lsp::TextEdit::new(
3791                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
3792                ", ".to_string(),
3793            )]))
3794        })
3795        .next()
3796        .await;
3797    cx.foreground().start_waiting();
3798    save.await.unwrap();
3799    assert_eq!(
3800        editor.read_with(cx, |editor, cx| editor.text(cx)),
3801        "one, two\nthree\n"
3802    );
3803    assert!(!cx.read(|cx| editor.is_dirty(cx)));
3804
3805    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
3806    assert!(cx.read(|cx| editor.is_dirty(cx)));
3807
3808    // Ensure we can still save even if formatting hangs.
3809    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
3810        assert_eq!(
3811            params.text_document.uri,
3812            lsp::Url::from_file_path("/file.rs").unwrap()
3813        );
3814        futures::future::pending::<()>().await;
3815        unreachable!()
3816    });
3817    let save = cx.update(|cx| editor.save(project.clone(), cx));
3818    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
3819    cx.foreground().start_waiting();
3820    save.await.unwrap();
3821    assert_eq!(
3822        editor.read_with(cx, |editor, cx| editor.text(cx)),
3823        "one\ntwo\nthree\n"
3824    );
3825    assert!(!cx.read(|cx| editor.is_dirty(cx)));
3826
3827    // Set rust language override and assert overriden tabsize is sent to language server
3828    cx.update(|cx| {
3829        cx.update_global::<Settings, _, _>(|settings, _| {
3830            settings.language_overrides.insert(
3831                "Rust".into(),
3832                EditorSettings {
3833                    tab_size: Some(8.try_into().unwrap()),
3834                    ..Default::default()
3835                },
3836            );
3837        })
3838    });
3839
3840    let save = cx.update(|cx| editor.save(project.clone(), cx));
3841    fake_server
3842        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
3843            assert_eq!(
3844                params.text_document.uri,
3845                lsp::Url::from_file_path("/file.rs").unwrap()
3846            );
3847            assert_eq!(params.options.tab_size, 8);
3848            Ok(Some(vec![]))
3849        })
3850        .next()
3851        .await;
3852    cx.foreground().start_waiting();
3853    save.await.unwrap();
3854}
3855
3856#[gpui::test]
3857async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
3858    cx.foreground().forbid_parking();
3859
3860    let mut language = Language::new(
3861        LanguageConfig {
3862            name: "Rust".into(),
3863            path_suffixes: vec!["rs".to_string()],
3864            ..Default::default()
3865        },
3866        Some(tree_sitter_rust::language()),
3867    );
3868    let mut fake_servers = language
3869        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3870            capabilities: lsp::ServerCapabilities {
3871                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
3872                ..Default::default()
3873            },
3874            ..Default::default()
3875        }))
3876        .await;
3877
3878    let fs = FakeFs::new(cx.background());
3879    fs.insert_file("/file.rs", Default::default()).await;
3880
3881    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
3882    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
3883    let buffer = project
3884        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
3885        .await
3886        .unwrap();
3887
3888    cx.foreground().start_waiting();
3889    let fake_server = fake_servers.next().await.unwrap();
3890
3891    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3892    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3893    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
3894    assert!(cx.read(|cx| editor.is_dirty(cx)));
3895
3896    let save = cx.update(|cx| editor.save(project.clone(), cx));
3897    fake_server
3898        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
3899            assert_eq!(
3900                params.text_document.uri,
3901                lsp::Url::from_file_path("/file.rs").unwrap()
3902            );
3903            assert_eq!(params.options.tab_size, 4);
3904            Ok(Some(vec![lsp::TextEdit::new(
3905                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
3906                ", ".to_string(),
3907            )]))
3908        })
3909        .next()
3910        .await;
3911    cx.foreground().start_waiting();
3912    save.await.unwrap();
3913    assert_eq!(
3914        editor.read_with(cx, |editor, cx| editor.text(cx)),
3915        "one, two\nthree\n"
3916    );
3917    assert!(!cx.read(|cx| editor.is_dirty(cx)));
3918
3919    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
3920    assert!(cx.read(|cx| editor.is_dirty(cx)));
3921
3922    // Ensure we can still save even if formatting hangs.
3923    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
3924        move |params, _| async move {
3925            assert_eq!(
3926                params.text_document.uri,
3927                lsp::Url::from_file_path("/file.rs").unwrap()
3928            );
3929            futures::future::pending::<()>().await;
3930            unreachable!()
3931        },
3932    );
3933    let save = cx.update(|cx| editor.save(project.clone(), cx));
3934    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
3935    cx.foreground().start_waiting();
3936    save.await.unwrap();
3937    assert_eq!(
3938        editor.read_with(cx, |editor, cx| editor.text(cx)),
3939        "one\ntwo\nthree\n"
3940    );
3941    assert!(!cx.read(|cx| editor.is_dirty(cx)));
3942
3943    // Set rust language override and assert overriden tabsize is sent to language server
3944    cx.update(|cx| {
3945        cx.update_global::<Settings, _, _>(|settings, _| {
3946            settings.language_overrides.insert(
3947                "Rust".into(),
3948                EditorSettings {
3949                    tab_size: Some(8.try_into().unwrap()),
3950                    ..Default::default()
3951                },
3952            );
3953        })
3954    });
3955
3956    let save = cx.update(|cx| editor.save(project.clone(), cx));
3957    fake_server
3958        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
3959            assert_eq!(
3960                params.text_document.uri,
3961                lsp::Url::from_file_path("/file.rs").unwrap()
3962            );
3963            assert_eq!(params.options.tab_size, 8);
3964            Ok(Some(vec![]))
3965        })
3966        .next()
3967        .await;
3968    cx.foreground().start_waiting();
3969    save.await.unwrap();
3970}
3971
3972#[gpui::test]
3973async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
3974    cx.foreground().forbid_parking();
3975
3976    let mut language = Language::new(
3977        LanguageConfig {
3978            name: "Rust".into(),
3979            path_suffixes: vec!["rs".to_string()],
3980            ..Default::default()
3981        },
3982        Some(tree_sitter_rust::language()),
3983    );
3984    let mut fake_servers = language
3985        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3986            capabilities: lsp::ServerCapabilities {
3987                document_formatting_provider: Some(lsp::OneOf::Left(true)),
3988                ..Default::default()
3989            },
3990            ..Default::default()
3991        }))
3992        .await;
3993
3994    let fs = FakeFs::new(cx.background());
3995    fs.insert_file("/file.rs", Default::default()).await;
3996
3997    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
3998    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
3999    let buffer = project
4000        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4001        .await
4002        .unwrap();
4003
4004    cx.foreground().start_waiting();
4005    let fake_server = fake_servers.next().await.unwrap();
4006
4007    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4008    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4009    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4010
4011    let format = editor.update(cx, |editor, cx| editor.perform_format(project.clone(), cx));
4012    fake_server
4013        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4014            assert_eq!(
4015                params.text_document.uri,
4016                lsp::Url::from_file_path("/file.rs").unwrap()
4017            );
4018            assert_eq!(params.options.tab_size, 4);
4019            Ok(Some(vec![lsp::TextEdit::new(
4020                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4021                ", ".to_string(),
4022            )]))
4023        })
4024        .next()
4025        .await;
4026    cx.foreground().start_waiting();
4027    format.await.unwrap();
4028    assert_eq!(
4029        editor.read_with(cx, |editor, cx| editor.text(cx)),
4030        "one, two\nthree\n"
4031    );
4032
4033    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4034    // Ensure we don't lock if formatting hangs.
4035    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4036        assert_eq!(
4037            params.text_document.uri,
4038            lsp::Url::from_file_path("/file.rs").unwrap()
4039        );
4040        futures::future::pending::<()>().await;
4041        unreachable!()
4042    });
4043    let format = editor.update(cx, |editor, cx| editor.perform_format(project, cx));
4044    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4045    cx.foreground().start_waiting();
4046    format.await.unwrap();
4047    assert_eq!(
4048        editor.read_with(cx, |editor, cx| editor.text(cx)),
4049        "one\ntwo\nthree\n"
4050    );
4051}
4052
4053#[gpui::test]
4054async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
4055    cx.foreground().forbid_parking();
4056
4057    let mut cx = EditorLspTestContext::new_rust(
4058        lsp::ServerCapabilities {
4059            document_formatting_provider: Some(lsp::OneOf::Left(true)),
4060            ..Default::default()
4061        },
4062        cx,
4063    )
4064    .await;
4065
4066    cx.set_state(indoc! {"
4067        one.twoˇ
4068    "});
4069
4070    // The format request takes a long time. When it completes, it inserts
4071    // a newline and an indent before the `.`
4072    cx.lsp
4073        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
4074            let executor = cx.background();
4075            async move {
4076                executor.timer(Duration::from_millis(100)).await;
4077                Ok(Some(vec![lsp::TextEdit {
4078                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
4079                    new_text: "\n    ".into(),
4080                }]))
4081            }
4082        });
4083
4084    // Submit a format request.
4085    let format_1 = cx
4086        .update_editor(|editor, cx| editor.format(&Format, cx))
4087        .unwrap();
4088    cx.foreground().run_until_parked();
4089
4090    // Submit a second format request.
4091    let format_2 = cx
4092        .update_editor(|editor, cx| editor.format(&Format, cx))
4093        .unwrap();
4094    cx.foreground().run_until_parked();
4095
4096    // Wait for both format requests to complete
4097    cx.foreground().advance_clock(Duration::from_millis(200));
4098    cx.foreground().start_waiting();
4099    format_1.await.unwrap();
4100    cx.foreground().start_waiting();
4101    format_2.await.unwrap();
4102
4103    // The formatting edits only happens once.
4104    cx.assert_editor_state(indoc! {"
4105        one
4106            .twoˇ
4107    "});
4108}
4109
4110#[gpui::test]
4111async fn test_completion(cx: &mut gpui::TestAppContext) {
4112    let mut cx = EditorLspTestContext::new_rust(
4113        lsp::ServerCapabilities {
4114            completion_provider: Some(lsp::CompletionOptions {
4115                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
4116                ..Default::default()
4117            }),
4118            ..Default::default()
4119        },
4120        cx,
4121    )
4122    .await;
4123
4124    cx.set_state(indoc! {"
4125        oneˇ
4126        two
4127        three
4128    "});
4129    cx.simulate_keystroke(".");
4130    handle_completion_request(
4131        &mut cx,
4132        indoc! {"
4133            one.|<>
4134            two
4135            three
4136        "},
4137        vec!["first_completion", "second_completion"],
4138    )
4139    .await;
4140    cx.condition(|editor, _| editor.context_menu_visible())
4141        .await;
4142    let apply_additional_edits = cx.update_editor(|editor, cx| {
4143        editor.move_down(&MoveDown, cx);
4144        editor
4145            .confirm_completion(&ConfirmCompletion::default(), cx)
4146            .unwrap()
4147    });
4148    cx.assert_editor_state(indoc! {"
4149        one.second_completionˇ
4150        two
4151        three
4152    "});
4153
4154    handle_resolve_completion_request(
4155        &mut cx,
4156        Some(vec![
4157            (
4158                //This overlaps with the primary completion edit which is
4159                //misbehavior from the LSP spec, test that we filter it out
4160                indoc! {"
4161                    one.second_ˇcompletion
4162                    two
4163                    threeˇ
4164                "},
4165                "overlapping aditional edit",
4166            ),
4167            (
4168                indoc! {"
4169                    one.second_completion
4170                    two
4171                    threeˇ
4172                "},
4173                "\nadditional edit",
4174            ),
4175        ]),
4176    )
4177    .await;
4178    apply_additional_edits.await.unwrap();
4179    cx.assert_editor_state(indoc! {"
4180        one.second_completionˇ
4181        two
4182        three
4183        additional edit
4184    "});
4185
4186    cx.set_state(indoc! {"
4187        one.second_completion
4188        twoˇ
4189        threeˇ
4190        additional edit
4191    "});
4192    cx.simulate_keystroke(" ");
4193    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4194    cx.simulate_keystroke("s");
4195    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4196
4197    cx.assert_editor_state(indoc! {"
4198        one.second_completion
4199        two sˇ
4200        three sˇ
4201        additional edit
4202    "});
4203    handle_completion_request(
4204        &mut cx,
4205        indoc! {"
4206            one.second_completion
4207            two s
4208            three <s|>
4209            additional edit
4210        "},
4211        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
4212    )
4213    .await;
4214    cx.condition(|editor, _| editor.context_menu_visible())
4215        .await;
4216
4217    cx.simulate_keystroke("i");
4218
4219    handle_completion_request(
4220        &mut cx,
4221        indoc! {"
4222            one.second_completion
4223            two si
4224            three <si|>
4225            additional edit
4226        "},
4227        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
4228    )
4229    .await;
4230    cx.condition(|editor, _| editor.context_menu_visible())
4231        .await;
4232
4233    let apply_additional_edits = cx.update_editor(|editor, cx| {
4234        editor
4235            .confirm_completion(&ConfirmCompletion::default(), cx)
4236            .unwrap()
4237    });
4238    cx.assert_editor_state(indoc! {"
4239        one.second_completion
4240        two sixth_completionˇ
4241        three sixth_completionˇ
4242        additional edit
4243    "});
4244
4245    handle_resolve_completion_request(&mut cx, None).await;
4246    apply_additional_edits.await.unwrap();
4247
4248    cx.update(|cx| {
4249        cx.update_global::<Settings, _, _>(|settings, _| {
4250            settings.show_completions_on_input = false;
4251        })
4252    });
4253    cx.set_state("editorˇ");
4254    cx.simulate_keystroke(".");
4255    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4256    cx.simulate_keystroke("c");
4257    cx.simulate_keystroke("l");
4258    cx.simulate_keystroke("o");
4259    cx.assert_editor_state("editor.cloˇ");
4260    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4261    cx.update_editor(|editor, cx| {
4262        editor.show_completions(&ShowCompletions, cx);
4263    });
4264    handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
4265    cx.condition(|editor, _| editor.context_menu_visible())
4266        .await;
4267    let apply_additional_edits = cx.update_editor(|editor, cx| {
4268        editor
4269            .confirm_completion(&ConfirmCompletion::default(), cx)
4270            .unwrap()
4271    });
4272    cx.assert_editor_state("editor.closeˇ");
4273    handle_resolve_completion_request(&mut cx, None).await;
4274    apply_additional_edits.await.unwrap();
4275
4276    // Handle completion request passing a marked string specifying where the completion
4277    // should be triggered from using '|' character, what range should be replaced, and what completions
4278    // should be returned using '<' and '>' to delimit the range
4279    async fn handle_completion_request<'a>(
4280        cx: &mut EditorLspTestContext<'a>,
4281        marked_string: &str,
4282        completions: Vec<&'static str>,
4283    ) {
4284        let complete_from_marker: TextRangeMarker = '|'.into();
4285        let replace_range_marker: TextRangeMarker = ('<', '>').into();
4286        let (_, mut marked_ranges) = marked_text_ranges_by(
4287            marked_string,
4288            vec![complete_from_marker.clone(), replace_range_marker.clone()],
4289        );
4290
4291        let complete_from_position =
4292            cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
4293        let replace_range =
4294            cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
4295
4296        cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
4297            let completions = completions.clone();
4298            async move {
4299                assert_eq!(params.text_document_position.text_document.uri, url.clone());
4300                assert_eq!(
4301                    params.text_document_position.position,
4302                    complete_from_position
4303                );
4304                Ok(Some(lsp::CompletionResponse::Array(
4305                    completions
4306                        .iter()
4307                        .map(|completion_text| lsp::CompletionItem {
4308                            label: completion_text.to_string(),
4309                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
4310                                range: replace_range,
4311                                new_text: completion_text.to_string(),
4312                            })),
4313                            ..Default::default()
4314                        })
4315                        .collect(),
4316                )))
4317            }
4318        })
4319        .next()
4320        .await;
4321    }
4322
4323    async fn handle_resolve_completion_request<'a>(
4324        cx: &mut EditorLspTestContext<'a>,
4325        edits: Option<Vec<(&'static str, &'static str)>>,
4326    ) {
4327        let edits = edits.map(|edits| {
4328            edits
4329                .iter()
4330                .map(|(marked_string, new_text)| {
4331                    let (_, marked_ranges) = marked_text_ranges(marked_string, false);
4332                    let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
4333                    lsp::TextEdit::new(replace_range, new_text.to_string())
4334                })
4335                .collect::<Vec<_>>()
4336        });
4337
4338        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
4339            let edits = edits.clone();
4340            async move {
4341                Ok(lsp::CompletionItem {
4342                    additional_text_edits: edits,
4343                    ..Default::default()
4344                })
4345            }
4346        })
4347        .next()
4348        .await;
4349    }
4350}
4351
4352#[gpui::test]
4353async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
4354    cx.update(|cx| cx.set_global(Settings::test(cx)));
4355    let language = Arc::new(Language::new(
4356        LanguageConfig {
4357            line_comment: Some("// ".into()),
4358            ..Default::default()
4359        },
4360        Some(tree_sitter_rust::language()),
4361    ));
4362
4363    let text = "
4364        fn a() {
4365            //b();
4366            // c();
4367            //  d();
4368        }
4369    "
4370    .unindent();
4371
4372    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
4373    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4374    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
4375
4376    view.update(cx, |editor, cx| {
4377        // If multiple selections intersect a line, the line is only
4378        // toggled once.
4379        editor.change_selections(None, cx, |s| {
4380            s.select_display_ranges([
4381                DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
4382                DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
4383            ])
4384        });
4385        editor.toggle_comments(&ToggleComments, cx);
4386        assert_eq!(
4387            editor.text(cx),
4388            "
4389                fn a() {
4390                    b();
4391                    c();
4392                     d();
4393                }
4394            "
4395            .unindent()
4396        );
4397
4398        // The comment prefix is inserted at the same column for every line
4399        // in a selection.
4400        editor.change_selections(None, cx, |s| {
4401            s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)])
4402        });
4403        editor.toggle_comments(&ToggleComments, cx);
4404        assert_eq!(
4405            editor.text(cx),
4406            "
4407                fn a() {
4408                    // b();
4409                    // c();
4410                    //  d();
4411                }
4412            "
4413            .unindent()
4414        );
4415
4416        // If a selection ends at the beginning of a line, that line is not toggled.
4417        editor.change_selections(None, cx, |s| {
4418            s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)])
4419        });
4420        editor.toggle_comments(&ToggleComments, cx);
4421        assert_eq!(
4422            editor.text(cx),
4423            "
4424                fn a() {
4425                    // b();
4426                    c();
4427                    //  d();
4428                }
4429            "
4430            .unindent()
4431        );
4432    });
4433}
4434
4435#[gpui::test]
4436async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
4437    let mut cx = EditorTestContext::new(cx);
4438
4439    let html_language = Arc::new(
4440        Language::new(
4441            LanguageConfig {
4442                name: "HTML".into(),
4443                block_comment: Some(("<!-- ".into(), " -->".into())),
4444                ..Default::default()
4445            },
4446            Some(tree_sitter_html::language()),
4447        )
4448        .with_injection_query(
4449            r#"
4450            (script_element
4451                (raw_text) @content
4452                (#set! "language" "javascript"))
4453            "#,
4454        )
4455        .unwrap(),
4456    );
4457
4458    let javascript_language = Arc::new(Language::new(
4459        LanguageConfig {
4460            name: "JavaScript".into(),
4461            line_comment: Some("// ".into()),
4462            ..Default::default()
4463        },
4464        Some(tree_sitter_javascript::language()),
4465    ));
4466
4467    let registry = Arc::new(LanguageRegistry::test());
4468    registry.add(html_language.clone());
4469    registry.add(javascript_language.clone());
4470
4471    cx.update_buffer(|buffer, cx| {
4472        buffer.set_language_registry(registry);
4473        buffer.set_language(Some(html_language), cx);
4474    });
4475
4476    // Toggle comments for empty selections
4477    cx.set_state(
4478        &r#"
4479            <p>A</p>ˇ
4480            <p>B</p>ˇ
4481            <p>C</p>ˇ
4482        "#
4483        .unindent(),
4484    );
4485    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
4486    cx.assert_editor_state(
4487        &r#"
4488            <!-- <p>A</p>ˇ -->
4489            <!-- <p>B</p>ˇ -->
4490            <!-- <p>C</p>ˇ -->
4491        "#
4492        .unindent(),
4493    );
4494    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
4495    cx.assert_editor_state(
4496        &r#"
4497            <p>A</p>ˇ
4498            <p>B</p>ˇ
4499            <p>C</p>ˇ
4500        "#
4501        .unindent(),
4502    );
4503
4504    // Toggle comments for mixture of empty and non-empty selections, where
4505    // multiple selections occupy a given line.
4506    cx.set_state(
4507        &r#"
4508            <p>A«</p>
4509            <p>ˇ»B</p>ˇ
4510            <p>C«</p>
4511            <p>ˇ»D</p>ˇ
4512        "#
4513        .unindent(),
4514    );
4515
4516    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
4517    cx.assert_editor_state(
4518        &r#"
4519            <!-- <p>A«</p>
4520            <p>ˇ»B</p>ˇ -->
4521            <!-- <p>C«</p>
4522            <p>ˇ»D</p>ˇ -->
4523        "#
4524        .unindent(),
4525    );
4526    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
4527    cx.assert_editor_state(
4528        &r#"
4529            <p>A«</p>
4530            <p>ˇ»B</p>ˇ
4531            <p>C«</p>
4532            <p>ˇ»D</p>ˇ
4533        "#
4534        .unindent(),
4535    );
4536
4537    // Toggle comments when different languages are active for different
4538    // selections.
4539    cx.set_state(
4540        &r#"
4541            ˇ<script>
4542                ˇvar x = new Y();
4543            ˇ</script>
4544        "#
4545        .unindent(),
4546    );
4547    cx.foreground().run_until_parked();
4548    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
4549    cx.assert_editor_state(
4550        &r#"
4551            <!-- ˇ<script> -->
4552                // ˇvar x = new Y();
4553            <!-- ˇ</script> -->
4554        "#
4555        .unindent(),
4556    );
4557}
4558
4559#[gpui::test]
4560fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
4561    cx.set_global(Settings::test(cx));
4562    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
4563    let multibuffer = cx.add_model(|cx| {
4564        let mut multibuffer = MultiBuffer::new(0);
4565        multibuffer.push_excerpts(
4566            buffer.clone(),
4567            [
4568                ExcerptRange {
4569                    context: Point::new(0, 0)..Point::new(0, 4),
4570                    primary: None,
4571                },
4572                ExcerptRange {
4573                    context: Point::new(1, 0)..Point::new(1, 4),
4574                    primary: None,
4575                },
4576            ],
4577            cx,
4578        );
4579        multibuffer
4580    });
4581
4582    assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
4583
4584    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
4585    view.update(cx, |view, cx| {
4586        assert_eq!(view.text(cx), "aaaa\nbbbb");
4587        view.change_selections(None, cx, |s| {
4588            s.select_ranges([
4589                Point::new(0, 0)..Point::new(0, 0),
4590                Point::new(1, 0)..Point::new(1, 0),
4591            ])
4592        });
4593
4594        view.handle_input("X", cx);
4595        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
4596        assert_eq!(
4597            view.selections.ranges(cx),
4598            [
4599                Point::new(0, 1)..Point::new(0, 1),
4600                Point::new(1, 1)..Point::new(1, 1),
4601            ]
4602        )
4603    });
4604}
4605
4606#[gpui::test]
4607fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
4608    cx.set_global(Settings::test(cx));
4609    let markers = vec![('[', ']').into(), ('(', ')').into()];
4610    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
4611        indoc! {"
4612            [aaaa
4613            (bbbb]
4614            cccc)",
4615        },
4616        markers.clone(),
4617    );
4618    let excerpt_ranges = markers.into_iter().map(|marker| {
4619        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
4620        ExcerptRange {
4621            context,
4622            primary: None,
4623        }
4624    });
4625    let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
4626    let multibuffer = cx.add_model(|cx| {
4627        let mut multibuffer = MultiBuffer::new(0);
4628        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
4629        multibuffer
4630    });
4631
4632    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
4633    view.update(cx, |view, cx| {
4634        let (expected_text, selection_ranges) = marked_text_ranges(
4635            indoc! {"
4636                aaaa
4637                bˇbbb
4638                bˇbbˇb
4639                cccc"
4640            },
4641            true,
4642        );
4643        assert_eq!(view.text(cx), expected_text);
4644        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
4645
4646        view.handle_input("X", cx);
4647
4648        let (expected_text, expected_selections) = marked_text_ranges(
4649            indoc! {"
4650                aaaa
4651                bXˇbbXb
4652                bXˇbbXˇb
4653                cccc"
4654            },
4655            false,
4656        );
4657        assert_eq!(view.text(cx), expected_text);
4658        assert_eq!(view.selections.ranges(cx), expected_selections);
4659
4660        view.newline(&Newline, cx);
4661        let (expected_text, expected_selections) = marked_text_ranges(
4662            indoc! {"
4663                aaaa
4664                bX
4665                ˇbbX
4666                b
4667                bX
4668                ˇbbX
4669                ˇb
4670                cccc"
4671            },
4672            false,
4673        );
4674        assert_eq!(view.text(cx), expected_text);
4675        assert_eq!(view.selections.ranges(cx), expected_selections);
4676    });
4677}
4678
4679#[gpui::test]
4680fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
4681    cx.set_global(Settings::test(cx));
4682    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
4683    let mut excerpt1_id = None;
4684    let multibuffer = cx.add_model(|cx| {
4685        let mut multibuffer = MultiBuffer::new(0);
4686        excerpt1_id = multibuffer
4687            .push_excerpts(
4688                buffer.clone(),
4689                [
4690                    ExcerptRange {
4691                        context: Point::new(0, 0)..Point::new(1, 4),
4692                        primary: None,
4693                    },
4694                    ExcerptRange {
4695                        context: Point::new(1, 0)..Point::new(2, 4),
4696                        primary: None,
4697                    },
4698                ],
4699                cx,
4700            )
4701            .into_iter()
4702            .next();
4703        multibuffer
4704    });
4705    assert_eq!(
4706        multibuffer.read(cx).read(cx).text(),
4707        "aaaa\nbbbb\nbbbb\ncccc"
4708    );
4709    let (_, editor) = cx.add_window(Default::default(), |cx| {
4710        let mut editor = build_editor(multibuffer.clone(), cx);
4711        let snapshot = editor.snapshot(cx);
4712        editor.change_selections(None, cx, |s| {
4713            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
4714        });
4715        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
4716        assert_eq!(
4717            editor.selections.ranges(cx),
4718            [
4719                Point::new(1, 3)..Point::new(1, 3),
4720                Point::new(2, 1)..Point::new(2, 1),
4721            ]
4722        );
4723        editor
4724    });
4725
4726    // Refreshing selections is a no-op when excerpts haven't changed.
4727    editor.update(cx, |editor, cx| {
4728        editor.change_selections(None, cx, |s| s.refresh());
4729        assert_eq!(
4730            editor.selections.ranges(cx),
4731            [
4732                Point::new(1, 3)..Point::new(1, 3),
4733                Point::new(2, 1)..Point::new(2, 1),
4734            ]
4735        );
4736    });
4737
4738    multibuffer.update(cx, |multibuffer, cx| {
4739        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
4740    });
4741    editor.update(cx, |editor, cx| {
4742        // Removing an excerpt causes the first selection to become degenerate.
4743        assert_eq!(
4744            editor.selections.ranges(cx),
4745            [
4746                Point::new(0, 0)..Point::new(0, 0),
4747                Point::new(0, 1)..Point::new(0, 1)
4748            ]
4749        );
4750
4751        // Refreshing selections will relocate the first selection to the original buffer
4752        // location.
4753        editor.change_selections(None, cx, |s| s.refresh());
4754        assert_eq!(
4755            editor.selections.ranges(cx),
4756            [
4757                Point::new(0, 1)..Point::new(0, 1),
4758                Point::new(0, 3)..Point::new(0, 3)
4759            ]
4760        );
4761        assert!(editor.selections.pending_anchor().is_some());
4762    });
4763}
4764
4765#[gpui::test]
4766fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) {
4767    cx.set_global(Settings::test(cx));
4768    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
4769    let mut excerpt1_id = None;
4770    let multibuffer = cx.add_model(|cx| {
4771        let mut multibuffer = MultiBuffer::new(0);
4772        excerpt1_id = multibuffer
4773            .push_excerpts(
4774                buffer.clone(),
4775                [
4776                    ExcerptRange {
4777                        context: Point::new(0, 0)..Point::new(1, 4),
4778                        primary: None,
4779                    },
4780                    ExcerptRange {
4781                        context: Point::new(1, 0)..Point::new(2, 4),
4782                        primary: None,
4783                    },
4784                ],
4785                cx,
4786            )
4787            .into_iter()
4788            .next();
4789        multibuffer
4790    });
4791    assert_eq!(
4792        multibuffer.read(cx).read(cx).text(),
4793        "aaaa\nbbbb\nbbbb\ncccc"
4794    );
4795    let (_, editor) = cx.add_window(Default::default(), |cx| {
4796        let mut editor = build_editor(multibuffer.clone(), cx);
4797        let snapshot = editor.snapshot(cx);
4798        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
4799        assert_eq!(
4800            editor.selections.ranges(cx),
4801            [Point::new(1, 3)..Point::new(1, 3)]
4802        );
4803        editor
4804    });
4805
4806    multibuffer.update(cx, |multibuffer, cx| {
4807        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
4808    });
4809    editor.update(cx, |editor, cx| {
4810        assert_eq!(
4811            editor.selections.ranges(cx),
4812            [Point::new(0, 0)..Point::new(0, 0)]
4813        );
4814
4815        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
4816        editor.change_selections(None, cx, |s| s.refresh());
4817        assert_eq!(
4818            editor.selections.ranges(cx),
4819            [Point::new(0, 3)..Point::new(0, 3)]
4820        );
4821        assert!(editor.selections.pending_anchor().is_some());
4822    });
4823}
4824
4825#[gpui::test]
4826async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
4827    cx.update(|cx| cx.set_global(Settings::test(cx)));
4828    let language = Arc::new(
4829        Language::new(
4830            LanguageConfig {
4831                brackets: vec![
4832                    BracketPair {
4833                        start: "{".to_string(),
4834                        end: "}".to_string(),
4835                        close: true,
4836                        newline: true,
4837                    },
4838                    BracketPair {
4839                        start: "/* ".to_string(),
4840                        end: " */".to_string(),
4841                        close: true,
4842                        newline: true,
4843                    },
4844                ],
4845                ..Default::default()
4846            },
4847            Some(tree_sitter_rust::language()),
4848        )
4849        .with_indents_query("")
4850        .unwrap(),
4851    );
4852
4853    let text = concat!(
4854        "{   }\n",     //
4855        "  x\n",       //
4856        "  /*   */\n", //
4857        "x\n",         //
4858        "{{} }\n",     //
4859    );
4860
4861    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
4862    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4863    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
4864    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
4865        .await;
4866
4867    view.update(cx, |view, cx| {
4868        view.change_selections(None, cx, |s| {
4869            s.select_display_ranges([
4870                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
4871                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
4872                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
4873            ])
4874        });
4875        view.newline(&Newline, cx);
4876
4877        assert_eq!(
4878            view.buffer().read(cx).read(cx).text(),
4879            concat!(
4880                "{ \n",    // Suppress rustfmt
4881                "\n",      //
4882                "}\n",     //
4883                "  x\n",   //
4884                "  /* \n", //
4885                "  \n",    //
4886                "  */\n",  //
4887                "x\n",     //
4888                "{{} \n",  //
4889                "}\n",     //
4890            )
4891        );
4892    });
4893}
4894
4895#[gpui::test]
4896fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
4897    let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
4898
4899    cx.set_global(Settings::test(cx));
4900    let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
4901
4902    editor.update(cx, |editor, cx| {
4903        struct Type1;
4904        struct Type2;
4905
4906        let buffer = buffer.read(cx).snapshot(cx);
4907
4908        let anchor_range =
4909            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
4910
4911        editor.highlight_background::<Type1>(
4912            vec![
4913                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
4914                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
4915                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
4916                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
4917            ],
4918            |_| Color::red(),
4919            cx,
4920        );
4921        editor.highlight_background::<Type2>(
4922            vec![
4923                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
4924                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
4925                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
4926                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
4927            ],
4928            |_| Color::green(),
4929            cx,
4930        );
4931
4932        let snapshot = editor.snapshot(cx);
4933        let mut highlighted_ranges = editor.background_highlights_in_range(
4934            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
4935            &snapshot,
4936            cx.global::<Settings>().theme.as_ref(),
4937        );
4938        // Enforce a consistent ordering based on color without relying on the ordering of the
4939        // highlight's `TypeId` which is non-deterministic.
4940        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
4941        assert_eq!(
4942            highlighted_ranges,
4943            &[
4944                (
4945                    DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
4946                    Color::green(),
4947                ),
4948                (
4949                    DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
4950                    Color::green(),
4951                ),
4952                (
4953                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
4954                    Color::red(),
4955                ),
4956                (
4957                    DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
4958                    Color::red(),
4959                ),
4960            ]
4961        );
4962        assert_eq!(
4963            editor.background_highlights_in_range(
4964                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
4965                &snapshot,
4966                cx.global::<Settings>().theme.as_ref(),
4967            ),
4968            &[(
4969                DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
4970                Color::red(),
4971            )]
4972        );
4973    });
4974}
4975
4976#[gpui::test]
4977async fn test_following(cx: &mut gpui::TestAppContext) {
4978    Settings::test_async(cx);
4979    let fs = FakeFs::new(cx.background());
4980    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4981
4982    let buffer = project.update(cx, |project, cx| {
4983        let buffer = project
4984            .create_buffer(&sample_text(16, 8, 'a'), None, cx)
4985            .unwrap();
4986        cx.add_model(|cx| MultiBuffer::singleton(buffer, cx))
4987    });
4988    let (_, leader) = cx.add_window(|cx| build_editor(buffer.clone(), cx));
4989    let (_, follower) = cx.update(|cx| {
4990        cx.add_window(
4991            WindowOptions {
4992                bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
4993                ..Default::default()
4994            },
4995            |cx| build_editor(buffer.clone(), cx),
4996        )
4997    });
4998
4999    let is_still_following = Rc::new(RefCell::new(true));
5000    let pending_update = Rc::new(RefCell::new(None));
5001    follower.update(cx, {
5002        let update = pending_update.clone();
5003        let is_still_following = is_still_following.clone();
5004        |_, cx| {
5005            cx.subscribe(&leader, move |_, leader, event, cx| {
5006                leader
5007                    .read(cx)
5008                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
5009            })
5010            .detach();
5011
5012            cx.subscribe(&follower, move |_, _, event, cx| {
5013                if Editor::should_unfollow_on_event(event, cx) {
5014                    *is_still_following.borrow_mut() = false;
5015                }
5016            })
5017            .detach();
5018        }
5019    });
5020
5021    // Update the selections only
5022    leader.update(cx, |leader, cx| {
5023        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
5024    });
5025    follower
5026        .update(cx, |follower, cx| {
5027            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5028        })
5029        .await
5030        .unwrap();
5031    follower.read_with(cx, |follower, cx| {
5032        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
5033    });
5034    assert_eq!(*is_still_following.borrow(), true);
5035
5036    // Update the scroll position only
5037    leader.update(cx, |leader, cx| {
5038        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
5039    });
5040    follower
5041        .update(cx, |follower, cx| {
5042            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5043        })
5044        .await
5045        .unwrap();
5046    assert_eq!(
5047        follower.update(cx, |follower, cx| follower.scroll_position(cx)),
5048        vec2f(1.5, 3.5)
5049    );
5050    assert_eq!(*is_still_following.borrow(), true);
5051
5052    // Update the selections and scroll position. The follower's scroll position is updated
5053    // via autoscroll, not via the leader's exact scroll position.
5054    leader.update(cx, |leader, cx| {
5055        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
5056        leader.request_autoscroll(Autoscroll::newest(), cx);
5057        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
5058    });
5059    follower
5060        .update(cx, |follower, cx| {
5061            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5062        })
5063        .await
5064        .unwrap();
5065    follower.update(cx, |follower, cx| {
5066        assert_eq!(follower.scroll_position(cx), vec2f(1.5, 0.0));
5067        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
5068    });
5069    assert_eq!(*is_still_following.borrow(), true);
5070
5071    // Creating a pending selection that precedes another selection
5072    leader.update(cx, |leader, cx| {
5073        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
5074        leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
5075    });
5076    follower
5077        .update(cx, |follower, cx| {
5078            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5079        })
5080        .await
5081        .unwrap();
5082    follower.read_with(cx, |follower, cx| {
5083        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
5084    });
5085    assert_eq!(*is_still_following.borrow(), true);
5086
5087    // Extend the pending selection so that it surrounds another selection
5088    leader.update(cx, |leader, cx| {
5089        leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
5090    });
5091    follower
5092        .update(cx, |follower, cx| {
5093            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5094        })
5095        .await
5096        .unwrap();
5097    follower.read_with(cx, |follower, cx| {
5098        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
5099    });
5100
5101    // Scrolling locally breaks the follow
5102    follower.update(cx, |follower, cx| {
5103        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
5104        follower.set_scroll_anchor(
5105            ScrollAnchor {
5106                top_anchor,
5107                offset: vec2f(0.0, 0.5),
5108            },
5109            cx,
5110        );
5111    });
5112    assert_eq!(*is_still_following.borrow(), false);
5113}
5114
5115#[gpui::test]
5116async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
5117    Settings::test_async(cx);
5118    let fs = FakeFs::new(cx.background());
5119    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5120    let (_, pane) = cx.add_window(|cx| Pane::new(None, cx));
5121
5122    let leader = pane.update(cx, |_, cx| {
5123        let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
5124        cx.add_view(|cx| build_editor(multibuffer.clone(), cx))
5125    });
5126
5127    // Start following the editor when it has no excerpts.
5128    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
5129    let follower_1 = cx
5130        .update(|cx| {
5131            Editor::from_state_proto(
5132                pane.clone(),
5133                project.clone(),
5134                ViewId {
5135                    creator: Default::default(),
5136                    id: 0,
5137                },
5138                &mut state_message,
5139                cx,
5140            )
5141        })
5142        .unwrap()
5143        .await
5144        .unwrap();
5145
5146    let update_message = Rc::new(RefCell::new(None));
5147    follower_1.update(cx, {
5148        let update = update_message.clone();
5149        |_, cx| {
5150            cx.subscribe(&leader, move |_, leader, event, cx| {
5151                leader
5152                    .read(cx)
5153                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
5154            })
5155            .detach();
5156        }
5157    });
5158
5159    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
5160        (
5161            project
5162                .create_buffer("abc\ndef\nghi\njkl\n", None, cx)
5163                .unwrap(),
5164            project
5165                .create_buffer("mno\npqr\nstu\nvwx\n", None, cx)
5166                .unwrap(),
5167        )
5168    });
5169
5170    // Insert some excerpts.
5171    leader.update(cx, |leader, cx| {
5172        leader.buffer.update(cx, |multibuffer, cx| {
5173            let excerpt_ids = multibuffer.push_excerpts(
5174                buffer_1.clone(),
5175                [
5176                    ExcerptRange {
5177                        context: 1..6,
5178                        primary: None,
5179                    },
5180                    ExcerptRange {
5181                        context: 12..15,
5182                        primary: None,
5183                    },
5184                    ExcerptRange {
5185                        context: 0..3,
5186                        primary: None,
5187                    },
5188                ],
5189                cx,
5190            );
5191            multibuffer.insert_excerpts_after(
5192                excerpt_ids[0],
5193                buffer_2.clone(),
5194                [
5195                    ExcerptRange {
5196                        context: 8..12,
5197                        primary: None,
5198                    },
5199                    ExcerptRange {
5200                        context: 0..6,
5201                        primary: None,
5202                    },
5203                ],
5204                cx,
5205            );
5206        });
5207    });
5208
5209    // Apply the update of adding the excerpts.
5210    follower_1
5211        .update(cx, |follower, cx| {
5212            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
5213        })
5214        .await
5215        .unwrap();
5216    assert_eq!(
5217        follower_1.read_with(cx, Editor::text),
5218        leader.read_with(cx, Editor::text)
5219    );
5220    update_message.borrow_mut().take();
5221
5222    // Start following separately after it already has excerpts.
5223    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
5224    let follower_2 = cx
5225        .update(|cx| {
5226            Editor::from_state_proto(
5227                pane.clone(),
5228                project.clone(),
5229                ViewId {
5230                    creator: Default::default(),
5231                    id: 0,
5232                },
5233                &mut state_message,
5234                cx,
5235            )
5236        })
5237        .unwrap()
5238        .await
5239        .unwrap();
5240    assert_eq!(
5241        follower_2.read_with(cx, Editor::text),
5242        leader.read_with(cx, Editor::text)
5243    );
5244
5245    // Remove some excerpts.
5246    leader.update(cx, |leader, cx| {
5247        leader.buffer.update(cx, |multibuffer, cx| {
5248            let excerpt_ids = multibuffer.excerpt_ids();
5249            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
5250            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
5251        });
5252    });
5253
5254    // Apply the update of removing the excerpts.
5255    follower_1
5256        .update(cx, |follower, cx| {
5257            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
5258        })
5259        .await
5260        .unwrap();
5261    follower_2
5262        .update(cx, |follower, cx| {
5263            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
5264        })
5265        .await
5266        .unwrap();
5267    update_message.borrow_mut().take();
5268    assert_eq!(
5269        follower_1.read_with(cx, Editor::text),
5270        leader.read_with(cx, Editor::text)
5271    );
5272}
5273
5274#[test]
5275fn test_combine_syntax_and_fuzzy_match_highlights() {
5276    let string = "abcdefghijklmnop";
5277    let syntax_ranges = [
5278        (
5279            0..3,
5280            HighlightStyle {
5281                color: Some(Color::red()),
5282                ..Default::default()
5283            },
5284        ),
5285        (
5286            4..8,
5287            HighlightStyle {
5288                color: Some(Color::green()),
5289                ..Default::default()
5290            },
5291        ),
5292    ];
5293    let match_indices = [4, 6, 7, 8];
5294    assert_eq!(
5295        combine_syntax_and_fuzzy_match_highlights(
5296            string,
5297            Default::default(),
5298            syntax_ranges.into_iter(),
5299            &match_indices,
5300        ),
5301        &[
5302            (
5303                0..3,
5304                HighlightStyle {
5305                    color: Some(Color::red()),
5306                    ..Default::default()
5307                },
5308            ),
5309            (
5310                4..5,
5311                HighlightStyle {
5312                    color: Some(Color::green()),
5313                    weight: Some(fonts::Weight::BOLD),
5314                    ..Default::default()
5315                },
5316            ),
5317            (
5318                5..6,
5319                HighlightStyle {
5320                    color: Some(Color::green()),
5321                    ..Default::default()
5322                },
5323            ),
5324            (
5325                6..8,
5326                HighlightStyle {
5327                    color: Some(Color::green()),
5328                    weight: Some(fonts::Weight::BOLD),
5329                    ..Default::default()
5330                },
5331            ),
5332            (
5333                8..9,
5334                HighlightStyle {
5335                    weight: Some(fonts::Weight::BOLD),
5336                    ..Default::default()
5337                },
5338            ),
5339        ]
5340    );
5341}
5342
5343#[gpui::test]
5344async fn go_to_hunk(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
5345    let mut cx = EditorTestContext::new(cx);
5346
5347    let diff_base = r#"
5348        use some::mod;
5349
5350        const A: u32 = 42;
5351
5352        fn main() {
5353            println!("hello");
5354
5355            println!("world");
5356        }
5357        "#
5358    .unindent();
5359
5360    // Edits are modified, removed, modified, added
5361    cx.set_state(
5362        &r#"
5363        use some::modified;
5364
5365        ˇ
5366        fn main() {
5367            println!("hello there");
5368
5369            println!("around the");
5370            println!("world");
5371        }
5372        "#
5373        .unindent(),
5374    );
5375
5376    cx.set_diff_base(Some(&diff_base));
5377    deterministic.run_until_parked();
5378
5379    cx.update_editor(|editor, cx| {
5380        //Wrap around the bottom of the buffer
5381        for _ in 0..3 {
5382            editor.go_to_hunk(&GoToHunk, cx);
5383        }
5384    });
5385
5386    cx.assert_editor_state(
5387        &r#"
5388        ˇuse some::modified;
5389    
5390    
5391        fn main() {
5392            println!("hello there");
5393    
5394            println!("around the");
5395            println!("world");
5396        }
5397        "#
5398        .unindent(),
5399    );
5400
5401    cx.update_editor(|editor, cx| {
5402        //Wrap around the top of the buffer
5403        for _ in 0..2 {
5404            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
5405        }
5406    });
5407
5408    cx.assert_editor_state(
5409        &r#"
5410        use some::modified;
5411
5412
5413        fn main() {
5414        ˇ    println!("hello there");
5415
5416            println!("around the");
5417            println!("world");
5418        }
5419        "#
5420        .unindent(),
5421    );
5422
5423    cx.update_editor(|editor, cx| {
5424        editor.fold(&Fold, cx);
5425
5426        //Make sure that the fold only gets one hunk
5427        for _ in 0..4 {
5428            editor.go_to_hunk(&GoToHunk, cx);
5429        }
5430    });
5431
5432    cx.assert_editor_state(
5433        &r#"
5434        ˇuse some::modified;
5435
5436
5437        fn main() {
5438            println!("hello there");
5439
5440            println!("around the");
5441            println!("world");
5442        }
5443        "#
5444        .unindent(),
5445    );
5446}
5447
5448#[test]
5449fn test_split_words() {
5450    fn split<'a>(text: &'a str) -> Vec<&'a str> {
5451        split_words(text).collect()
5452    }
5453
5454    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
5455    assert_eq!(split("hello_world"), &["hello_", "world"]);
5456    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
5457    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
5458    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
5459    assert_eq!(split("helloworld"), &["helloworld"]);
5460}
5461
5462fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
5463    let point = DisplayPoint::new(row as u32, column as u32);
5464    point..point
5465}
5466
5467fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
5468    let (text, ranges) = marked_text_ranges(marked_text, true);
5469    assert_eq!(view.text(cx), text);
5470    assert_eq!(
5471        view.selections.ranges(cx),
5472        ranges,
5473        "Assert selections are {}",
5474        marked_text
5475    );
5476}