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