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::default(), 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::default(), cx);
4473        assert_eq!(
4474            editor.text(cx),
4475            "
4476                fn a() {
4477                    // b();
4478                    // c();
4479                    //  d();
4480                }
4481            "
4482            .unindent()
4483        );
4484
4485        // 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::default(), 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_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
4506    let mut cx = EditorTestContext::new(cx);
4507    cx.update(|cx| cx.set_global(Settings::test(cx)));
4508
4509    let language = Arc::new(Language::new(
4510        LanguageConfig {
4511            line_comment: Some("// ".into()),
4512            ..Default::default()
4513        },
4514        Some(tree_sitter_rust::language()),
4515    ));
4516
4517    let registry = Arc::new(LanguageRegistry::test());
4518    registry.add(language.clone());
4519
4520    cx.update_buffer(|buffer, cx| {
4521        buffer.set_language_registry(registry);
4522        buffer.set_language(Some(language), cx);
4523    });
4524
4525    let toggle_comments = &ToggleComments {
4526        advance_downwards: true,
4527    };
4528
4529    // Single cursor on one line -> advance
4530    // Cursor moves horizontally 3 characters as well on non-blank line
4531    cx.set_state(indoc!(
4532        "fn a() {
4533             ˇdog();
4534             cat();
4535        }"
4536    ));
4537    cx.update_editor(|editor, cx| {
4538        editor.toggle_comments(toggle_comments, cx);
4539    });
4540    cx.assert_editor_state(indoc!(
4541        "fn a() {
4542             // dog();
4543             catˇ();
4544        }"
4545    ));
4546
4547    // Single selection on one line -> don't advance
4548    cx.set_state(indoc!(
4549        "fn a() {
4550             «dog()ˇ»;
4551             cat();
4552        }"
4553    ));
4554    cx.update_editor(|editor, cx| {
4555        editor.toggle_comments(toggle_comments, cx);
4556    });
4557    cx.assert_editor_state(indoc!(
4558        "fn a() {
4559             // «dog()ˇ»;
4560             cat();
4561        }"
4562    ));
4563
4564    // Multiple cursors on one line -> advance
4565    cx.set_state(indoc!(
4566        "fn a() {
4567             ˇdˇog();
4568             cat();
4569        }"
4570    ));
4571    cx.update_editor(|editor, cx| {
4572        editor.toggle_comments(toggle_comments, cx);
4573    });
4574    cx.assert_editor_state(indoc!(
4575        "fn a() {
4576             // dog();
4577             catˇ(ˇ);
4578        }"
4579    ));
4580
4581    // Multiple cursors on one line, with selection -> don't advance
4582    cx.set_state(indoc!(
4583        "fn a() {
4584             ˇdˇog«()ˇ»;
4585             cat();
4586        }"
4587    ));
4588    cx.update_editor(|editor, cx| {
4589        editor.toggle_comments(toggle_comments, cx);
4590    });
4591    cx.assert_editor_state(indoc!(
4592        "fn a() {
4593             // ˇdˇog«()ˇ»;
4594             cat();
4595        }"
4596    ));
4597
4598    // Single cursor on one line -> advance
4599    // Cursor moves to column 0 on blank line
4600    cx.set_state(indoc!(
4601        "fn a() {
4602             ˇdog();
4603
4604             cat();
4605        }"
4606    ));
4607    cx.update_editor(|editor, cx| {
4608        editor.toggle_comments(toggle_comments, cx);
4609    });
4610    cx.assert_editor_state(indoc!(
4611        "fn a() {
4612             // dog();
4613        ˇ
4614             cat();
4615        }"
4616    ));
4617
4618    // Single cursor on one line -> advance
4619    // Cursor starts and ends at column 0
4620    cx.set_state(indoc!(
4621        "fn a() {
4622         ˇ    dog();
4623             cat();
4624        }"
4625    ));
4626    cx.update_editor(|editor, cx| {
4627        editor.toggle_comments(toggle_comments, cx);
4628    });
4629    cx.assert_editor_state(indoc!(
4630        "fn a() {
4631             // dog();
4632         ˇ    cat();
4633        }"
4634    ));
4635}
4636
4637#[gpui::test]
4638async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
4639    let mut cx = EditorTestContext::new(cx);
4640
4641    let html_language = Arc::new(
4642        Language::new(
4643            LanguageConfig {
4644                name: "HTML".into(),
4645                block_comment: Some(("<!-- ".into(), " -->".into())),
4646                ..Default::default()
4647            },
4648            Some(tree_sitter_html::language()),
4649        )
4650        .with_injection_query(
4651            r#"
4652            (script_element
4653                (raw_text) @content
4654                (#set! "language" "javascript"))
4655            "#,
4656        )
4657        .unwrap(),
4658    );
4659
4660    let javascript_language = Arc::new(Language::new(
4661        LanguageConfig {
4662            name: "JavaScript".into(),
4663            line_comment: Some("// ".into()),
4664            ..Default::default()
4665        },
4666        Some(tree_sitter_javascript::language()),
4667    ));
4668
4669    let registry = Arc::new(LanguageRegistry::test());
4670    registry.add(html_language.clone());
4671    registry.add(javascript_language.clone());
4672
4673    cx.update_buffer(|buffer, cx| {
4674        buffer.set_language_registry(registry);
4675        buffer.set_language(Some(html_language), cx);
4676    });
4677
4678    // Toggle comments for empty selections
4679    cx.set_state(
4680        &r#"
4681            <p>A</p>ˇ
4682            <p>B</p>ˇ
4683            <p>C</p>ˇ
4684        "#
4685        .unindent(),
4686    );
4687    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
4688    cx.assert_editor_state(
4689        &r#"
4690            <!-- <p>A</p>ˇ -->
4691            <!-- <p>B</p>ˇ -->
4692            <!-- <p>C</p>ˇ -->
4693        "#
4694        .unindent(),
4695    );
4696    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
4697    cx.assert_editor_state(
4698        &r#"
4699            <p>A</p>ˇ
4700            <p>B</p>ˇ
4701            <p>C</p>ˇ
4702        "#
4703        .unindent(),
4704    );
4705
4706    // Toggle comments for mixture of empty and non-empty selections, where
4707    // multiple selections occupy a given line.
4708    cx.set_state(
4709        &r#"
4710            <p>A«</p>
4711            <p>ˇ»B</p>ˇ
4712            <p>C«</p>
4713            <p>ˇ»D</p>ˇ
4714        "#
4715        .unindent(),
4716    );
4717
4718    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
4719    cx.assert_editor_state(
4720        &r#"
4721            <!-- <p>A«</p>
4722            <p>ˇ»B</p>ˇ -->
4723            <!-- <p>C«</p>
4724            <p>ˇ»D</p>ˇ -->
4725        "#
4726        .unindent(),
4727    );
4728    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
4729    cx.assert_editor_state(
4730        &r#"
4731            <p>A«</p>
4732            <p>ˇ»B</p>ˇ
4733            <p>C«</p>
4734            <p>ˇ»D</p>ˇ
4735        "#
4736        .unindent(),
4737    );
4738
4739    // Toggle comments when different languages are active for different
4740    // selections.
4741    cx.set_state(
4742        &r#"
4743            ˇ<script>
4744                ˇvar x = new Y();
4745            ˇ</script>
4746        "#
4747        .unindent(),
4748    );
4749    cx.foreground().run_until_parked();
4750    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
4751    cx.assert_editor_state(
4752        &r#"
4753            <!-- ˇ<script> -->
4754                // ˇvar x = new Y();
4755            <!-- ˇ</script> -->
4756        "#
4757        .unindent(),
4758    );
4759}
4760
4761#[gpui::test]
4762fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
4763    cx.set_global(Settings::test(cx));
4764    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
4765    let multibuffer = cx.add_model(|cx| {
4766        let mut multibuffer = MultiBuffer::new(0);
4767        multibuffer.push_excerpts(
4768            buffer.clone(),
4769            [
4770                ExcerptRange {
4771                    context: Point::new(0, 0)..Point::new(0, 4),
4772                    primary: None,
4773                },
4774                ExcerptRange {
4775                    context: Point::new(1, 0)..Point::new(1, 4),
4776                    primary: None,
4777                },
4778            ],
4779            cx,
4780        );
4781        multibuffer
4782    });
4783
4784    assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
4785
4786    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
4787    view.update(cx, |view, cx| {
4788        assert_eq!(view.text(cx), "aaaa\nbbbb");
4789        view.change_selections(None, cx, |s| {
4790            s.select_ranges([
4791                Point::new(0, 0)..Point::new(0, 0),
4792                Point::new(1, 0)..Point::new(1, 0),
4793            ])
4794        });
4795
4796        view.handle_input("X", cx);
4797        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
4798        assert_eq!(
4799            view.selections.ranges(cx),
4800            [
4801                Point::new(0, 1)..Point::new(0, 1),
4802                Point::new(1, 1)..Point::new(1, 1),
4803            ]
4804        )
4805    });
4806}
4807
4808#[gpui::test]
4809fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
4810    cx.set_global(Settings::test(cx));
4811    let markers = vec![('[', ']').into(), ('(', ')').into()];
4812    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
4813        indoc! {"
4814            [aaaa
4815            (bbbb]
4816            cccc)",
4817        },
4818        markers.clone(),
4819    );
4820    let excerpt_ranges = markers.into_iter().map(|marker| {
4821        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
4822        ExcerptRange {
4823            context,
4824            primary: None,
4825        }
4826    });
4827    let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
4828    let multibuffer = cx.add_model(|cx| {
4829        let mut multibuffer = MultiBuffer::new(0);
4830        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
4831        multibuffer
4832    });
4833
4834    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
4835    view.update(cx, |view, cx| {
4836        let (expected_text, selection_ranges) = marked_text_ranges(
4837            indoc! {"
4838                aaaa
4839                bˇbbb
4840                bˇbbˇb
4841                cccc"
4842            },
4843            true,
4844        );
4845        assert_eq!(view.text(cx), expected_text);
4846        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
4847
4848        view.handle_input("X", cx);
4849
4850        let (expected_text, expected_selections) = marked_text_ranges(
4851            indoc! {"
4852                aaaa
4853                bXˇbbXb
4854                bXˇbbXˇb
4855                cccc"
4856            },
4857            false,
4858        );
4859        assert_eq!(view.text(cx), expected_text);
4860        assert_eq!(view.selections.ranges(cx), expected_selections);
4861
4862        view.newline(&Newline, cx);
4863        let (expected_text, expected_selections) = marked_text_ranges(
4864            indoc! {"
4865                aaaa
4866                bX
4867                ˇbbX
4868                b
4869                bX
4870                ˇbbX
4871                ˇb
4872                cccc"
4873            },
4874            false,
4875        );
4876        assert_eq!(view.text(cx), expected_text);
4877        assert_eq!(view.selections.ranges(cx), expected_selections);
4878    });
4879}
4880
4881#[gpui::test]
4882fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
4883    cx.set_global(Settings::test(cx));
4884    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
4885    let mut excerpt1_id = None;
4886    let multibuffer = cx.add_model(|cx| {
4887        let mut multibuffer = MultiBuffer::new(0);
4888        excerpt1_id = multibuffer
4889            .push_excerpts(
4890                buffer.clone(),
4891                [
4892                    ExcerptRange {
4893                        context: Point::new(0, 0)..Point::new(1, 4),
4894                        primary: None,
4895                    },
4896                    ExcerptRange {
4897                        context: Point::new(1, 0)..Point::new(2, 4),
4898                        primary: None,
4899                    },
4900                ],
4901                cx,
4902            )
4903            .into_iter()
4904            .next();
4905        multibuffer
4906    });
4907    assert_eq!(
4908        multibuffer.read(cx).read(cx).text(),
4909        "aaaa\nbbbb\nbbbb\ncccc"
4910    );
4911    let (_, editor) = cx.add_window(Default::default(), |cx| {
4912        let mut editor = build_editor(multibuffer.clone(), cx);
4913        let snapshot = editor.snapshot(cx);
4914        editor.change_selections(None, cx, |s| {
4915            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
4916        });
4917        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
4918        assert_eq!(
4919            editor.selections.ranges(cx),
4920            [
4921                Point::new(1, 3)..Point::new(1, 3),
4922                Point::new(2, 1)..Point::new(2, 1),
4923            ]
4924        );
4925        editor
4926    });
4927
4928    // Refreshing selections is a no-op when excerpts haven't changed.
4929    editor.update(cx, |editor, cx| {
4930        editor.change_selections(None, cx, |s| s.refresh());
4931        assert_eq!(
4932            editor.selections.ranges(cx),
4933            [
4934                Point::new(1, 3)..Point::new(1, 3),
4935                Point::new(2, 1)..Point::new(2, 1),
4936            ]
4937        );
4938    });
4939
4940    multibuffer.update(cx, |multibuffer, cx| {
4941        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
4942    });
4943    editor.update(cx, |editor, cx| {
4944        // Removing an excerpt causes the first selection to become degenerate.
4945        assert_eq!(
4946            editor.selections.ranges(cx),
4947            [
4948                Point::new(0, 0)..Point::new(0, 0),
4949                Point::new(0, 1)..Point::new(0, 1)
4950            ]
4951        );
4952
4953        // Refreshing selections will relocate the first selection to the original buffer
4954        // location.
4955        editor.change_selections(None, cx, |s| s.refresh());
4956        assert_eq!(
4957            editor.selections.ranges(cx),
4958            [
4959                Point::new(0, 1)..Point::new(0, 1),
4960                Point::new(0, 3)..Point::new(0, 3)
4961            ]
4962        );
4963        assert!(editor.selections.pending_anchor().is_some());
4964    });
4965}
4966
4967#[gpui::test]
4968fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) {
4969    cx.set_global(Settings::test(cx));
4970    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
4971    let mut excerpt1_id = None;
4972    let multibuffer = cx.add_model(|cx| {
4973        let mut multibuffer = MultiBuffer::new(0);
4974        excerpt1_id = multibuffer
4975            .push_excerpts(
4976                buffer.clone(),
4977                [
4978                    ExcerptRange {
4979                        context: Point::new(0, 0)..Point::new(1, 4),
4980                        primary: None,
4981                    },
4982                    ExcerptRange {
4983                        context: Point::new(1, 0)..Point::new(2, 4),
4984                        primary: None,
4985                    },
4986                ],
4987                cx,
4988            )
4989            .into_iter()
4990            .next();
4991        multibuffer
4992    });
4993    assert_eq!(
4994        multibuffer.read(cx).read(cx).text(),
4995        "aaaa\nbbbb\nbbbb\ncccc"
4996    );
4997    let (_, editor) = cx.add_window(Default::default(), |cx| {
4998        let mut editor = build_editor(multibuffer.clone(), cx);
4999        let snapshot = editor.snapshot(cx);
5000        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
5001        assert_eq!(
5002            editor.selections.ranges(cx),
5003            [Point::new(1, 3)..Point::new(1, 3)]
5004        );
5005        editor
5006    });
5007
5008    multibuffer.update(cx, |multibuffer, cx| {
5009        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
5010    });
5011    editor.update(cx, |editor, cx| {
5012        assert_eq!(
5013            editor.selections.ranges(cx),
5014            [Point::new(0, 0)..Point::new(0, 0)]
5015        );
5016
5017        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
5018        editor.change_selections(None, cx, |s| s.refresh());
5019        assert_eq!(
5020            editor.selections.ranges(cx),
5021            [Point::new(0, 3)..Point::new(0, 3)]
5022        );
5023        assert!(editor.selections.pending_anchor().is_some());
5024    });
5025}
5026
5027#[gpui::test]
5028async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
5029    cx.update(|cx| cx.set_global(Settings::test(cx)));
5030    let language = Arc::new(
5031        Language::new(
5032            LanguageConfig {
5033                brackets: vec![
5034                    BracketPair {
5035                        start: "{".to_string(),
5036                        end: "}".to_string(),
5037                        close: true,
5038                        newline: true,
5039                    },
5040                    BracketPair {
5041                        start: "/* ".to_string(),
5042                        end: " */".to_string(),
5043                        close: true,
5044                        newline: true,
5045                    },
5046                ],
5047                ..Default::default()
5048            },
5049            Some(tree_sitter_rust::language()),
5050        )
5051        .with_indents_query("")
5052        .unwrap(),
5053    );
5054
5055    let text = concat!(
5056        "{   }\n",     //
5057        "  x\n",       //
5058        "  /*   */\n", //
5059        "x\n",         //
5060        "{{} }\n",     //
5061    );
5062
5063    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
5064    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
5065    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
5066    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
5067        .await;
5068
5069    view.update(cx, |view, cx| {
5070        view.change_selections(None, cx, |s| {
5071            s.select_display_ranges([
5072                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
5073                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
5074                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
5075            ])
5076        });
5077        view.newline(&Newline, cx);
5078
5079        assert_eq!(
5080            view.buffer().read(cx).read(cx).text(),
5081            concat!(
5082                "{ \n",    // Suppress rustfmt
5083                "\n",      //
5084                "}\n",     //
5085                "  x\n",   //
5086                "  /* \n", //
5087                "  \n",    //
5088                "  */\n",  //
5089                "x\n",     //
5090                "{{} \n",  //
5091                "}\n",     //
5092            )
5093        );
5094    });
5095}
5096
5097#[gpui::test]
5098fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
5099    let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
5100
5101    cx.set_global(Settings::test(cx));
5102    let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
5103
5104    editor.update(cx, |editor, cx| {
5105        struct Type1;
5106        struct Type2;
5107
5108        let buffer = buffer.read(cx).snapshot(cx);
5109
5110        let anchor_range =
5111            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
5112
5113        editor.highlight_background::<Type1>(
5114            vec![
5115                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
5116                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
5117                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
5118                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
5119            ],
5120            |_| Color::red(),
5121            cx,
5122        );
5123        editor.highlight_background::<Type2>(
5124            vec![
5125                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
5126                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
5127                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
5128                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
5129            ],
5130            |_| Color::green(),
5131            cx,
5132        );
5133
5134        let snapshot = editor.snapshot(cx);
5135        let mut highlighted_ranges = editor.background_highlights_in_range(
5136            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
5137            &snapshot,
5138            cx.global::<Settings>().theme.as_ref(),
5139        );
5140        // Enforce a consistent ordering based on color without relying on the ordering of the
5141        // highlight's `TypeId` which is non-deterministic.
5142        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
5143        assert_eq!(
5144            highlighted_ranges,
5145            &[
5146                (
5147                    DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
5148                    Color::green(),
5149                ),
5150                (
5151                    DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
5152                    Color::green(),
5153                ),
5154                (
5155                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
5156                    Color::red(),
5157                ),
5158                (
5159                    DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
5160                    Color::red(),
5161                ),
5162            ]
5163        );
5164        assert_eq!(
5165            editor.background_highlights_in_range(
5166                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
5167                &snapshot,
5168                cx.global::<Settings>().theme.as_ref(),
5169            ),
5170            &[(
5171                DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
5172                Color::red(),
5173            )]
5174        );
5175    });
5176}
5177
5178#[gpui::test]
5179async fn test_following(cx: &mut gpui::TestAppContext) {
5180    Settings::test_async(cx);
5181    let fs = FakeFs::new(cx.background());
5182    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5183
5184    let buffer = project.update(cx, |project, cx| {
5185        let buffer = project
5186            .create_buffer(&sample_text(16, 8, 'a'), None, cx)
5187            .unwrap();
5188        cx.add_model(|cx| MultiBuffer::singleton(buffer, cx))
5189    });
5190    let (_, leader) = cx.add_window(|cx| build_editor(buffer.clone(), cx));
5191    let (_, follower) = cx.update(|cx| {
5192        cx.add_window(
5193            WindowOptions {
5194                bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
5195                ..Default::default()
5196            },
5197            |cx| build_editor(buffer.clone(), cx),
5198        )
5199    });
5200
5201    let is_still_following = Rc::new(RefCell::new(true));
5202    let pending_update = Rc::new(RefCell::new(None));
5203    follower.update(cx, {
5204        let update = pending_update.clone();
5205        let is_still_following = is_still_following.clone();
5206        |_, cx| {
5207            cx.subscribe(&leader, move |_, leader, event, cx| {
5208                leader
5209                    .read(cx)
5210                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
5211            })
5212            .detach();
5213
5214            cx.subscribe(&follower, move |_, _, event, cx| {
5215                if Editor::should_unfollow_on_event(event, cx) {
5216                    *is_still_following.borrow_mut() = false;
5217                }
5218            })
5219            .detach();
5220        }
5221    });
5222
5223    // Update the selections only
5224    leader.update(cx, |leader, cx| {
5225        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
5226    });
5227    follower
5228        .update(cx, |follower, cx| {
5229            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5230        })
5231        .await
5232        .unwrap();
5233    follower.read_with(cx, |follower, cx| {
5234        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
5235    });
5236    assert_eq!(*is_still_following.borrow(), true);
5237
5238    // Update the scroll position only
5239    leader.update(cx, |leader, cx| {
5240        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
5241    });
5242    follower
5243        .update(cx, |follower, cx| {
5244            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5245        })
5246        .await
5247        .unwrap();
5248    assert_eq!(
5249        follower.update(cx, |follower, cx| follower.scroll_position(cx)),
5250        vec2f(1.5, 3.5)
5251    );
5252    assert_eq!(*is_still_following.borrow(), true);
5253
5254    // Update the selections and scroll position. The follower's scroll position is updated
5255    // via autoscroll, not via the leader's exact scroll position.
5256    leader.update(cx, |leader, cx| {
5257        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
5258        leader.request_autoscroll(Autoscroll::newest(), cx);
5259        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
5260    });
5261    follower
5262        .update(cx, |follower, cx| {
5263            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5264        })
5265        .await
5266        .unwrap();
5267    follower.update(cx, |follower, cx| {
5268        assert_eq!(follower.scroll_position(cx), vec2f(1.5, 0.0));
5269        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
5270    });
5271    assert_eq!(*is_still_following.borrow(), true);
5272
5273    // Creating a pending selection that precedes another selection
5274    leader.update(cx, |leader, cx| {
5275        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
5276        leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
5277    });
5278    follower
5279        .update(cx, |follower, cx| {
5280            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5281        })
5282        .await
5283        .unwrap();
5284    follower.read_with(cx, |follower, cx| {
5285        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
5286    });
5287    assert_eq!(*is_still_following.borrow(), true);
5288
5289    // Extend the pending selection so that it surrounds another selection
5290    leader.update(cx, |leader, cx| {
5291        leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
5292    });
5293    follower
5294        .update(cx, |follower, cx| {
5295            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5296        })
5297        .await
5298        .unwrap();
5299    follower.read_with(cx, |follower, cx| {
5300        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
5301    });
5302
5303    // Scrolling locally breaks the follow
5304    follower.update(cx, |follower, cx| {
5305        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
5306        follower.set_scroll_anchor(
5307            ScrollAnchor {
5308                top_anchor,
5309                offset: vec2f(0.0, 0.5),
5310            },
5311            cx,
5312        );
5313    });
5314    assert_eq!(*is_still_following.borrow(), false);
5315}
5316
5317#[gpui::test]
5318async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
5319    Settings::test_async(cx);
5320    let fs = FakeFs::new(cx.background());
5321    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5322    let (_, pane) = cx.add_window(|cx| Pane::new(None, cx));
5323
5324    let leader = pane.update(cx, |_, cx| {
5325        let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
5326        cx.add_view(|cx| build_editor(multibuffer.clone(), cx))
5327    });
5328
5329    // Start following the editor when it has no excerpts.
5330    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
5331    let follower_1 = cx
5332        .update(|cx| {
5333            Editor::from_state_proto(
5334                pane.clone(),
5335                project.clone(),
5336                ViewId {
5337                    creator: Default::default(),
5338                    id: 0,
5339                },
5340                &mut state_message,
5341                cx,
5342            )
5343        })
5344        .unwrap()
5345        .await
5346        .unwrap();
5347
5348    let update_message = Rc::new(RefCell::new(None));
5349    follower_1.update(cx, {
5350        let update = update_message.clone();
5351        |_, cx| {
5352            cx.subscribe(&leader, move |_, leader, event, cx| {
5353                leader
5354                    .read(cx)
5355                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
5356            })
5357            .detach();
5358        }
5359    });
5360
5361    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
5362        (
5363            project
5364                .create_buffer("abc\ndef\nghi\njkl\n", None, cx)
5365                .unwrap(),
5366            project
5367                .create_buffer("mno\npqr\nstu\nvwx\n", None, cx)
5368                .unwrap(),
5369        )
5370    });
5371
5372    // Insert some excerpts.
5373    leader.update(cx, |leader, cx| {
5374        leader.buffer.update(cx, |multibuffer, cx| {
5375            let excerpt_ids = multibuffer.push_excerpts(
5376                buffer_1.clone(),
5377                [
5378                    ExcerptRange {
5379                        context: 1..6,
5380                        primary: None,
5381                    },
5382                    ExcerptRange {
5383                        context: 12..15,
5384                        primary: None,
5385                    },
5386                    ExcerptRange {
5387                        context: 0..3,
5388                        primary: None,
5389                    },
5390                ],
5391                cx,
5392            );
5393            multibuffer.insert_excerpts_after(
5394                excerpt_ids[0],
5395                buffer_2.clone(),
5396                [
5397                    ExcerptRange {
5398                        context: 8..12,
5399                        primary: None,
5400                    },
5401                    ExcerptRange {
5402                        context: 0..6,
5403                        primary: None,
5404                    },
5405                ],
5406                cx,
5407            );
5408        });
5409    });
5410
5411    // Apply the update of adding the excerpts.
5412    follower_1
5413        .update(cx, |follower, cx| {
5414            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
5415        })
5416        .await
5417        .unwrap();
5418    assert_eq!(
5419        follower_1.read_with(cx, Editor::text),
5420        leader.read_with(cx, Editor::text)
5421    );
5422    update_message.borrow_mut().take();
5423
5424    // Start following separately after it already has excerpts.
5425    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
5426    let follower_2 = cx
5427        .update(|cx| {
5428            Editor::from_state_proto(
5429                pane.clone(),
5430                project.clone(),
5431                ViewId {
5432                    creator: Default::default(),
5433                    id: 0,
5434                },
5435                &mut state_message,
5436                cx,
5437            )
5438        })
5439        .unwrap()
5440        .await
5441        .unwrap();
5442    assert_eq!(
5443        follower_2.read_with(cx, Editor::text),
5444        leader.read_with(cx, Editor::text)
5445    );
5446
5447    // Remove some excerpts.
5448    leader.update(cx, |leader, cx| {
5449        leader.buffer.update(cx, |multibuffer, cx| {
5450            let excerpt_ids = multibuffer.excerpt_ids();
5451            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
5452            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
5453        });
5454    });
5455
5456    // Apply the update of removing the excerpts.
5457    follower_1
5458        .update(cx, |follower, cx| {
5459            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
5460        })
5461        .await
5462        .unwrap();
5463    follower_2
5464        .update(cx, |follower, cx| {
5465            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
5466        })
5467        .await
5468        .unwrap();
5469    update_message.borrow_mut().take();
5470    assert_eq!(
5471        follower_1.read_with(cx, Editor::text),
5472        leader.read_with(cx, Editor::text)
5473    );
5474}
5475
5476#[test]
5477fn test_combine_syntax_and_fuzzy_match_highlights() {
5478    let string = "abcdefghijklmnop";
5479    let syntax_ranges = [
5480        (
5481            0..3,
5482            HighlightStyle {
5483                color: Some(Color::red()),
5484                ..Default::default()
5485            },
5486        ),
5487        (
5488            4..8,
5489            HighlightStyle {
5490                color: Some(Color::green()),
5491                ..Default::default()
5492            },
5493        ),
5494    ];
5495    let match_indices = [4, 6, 7, 8];
5496    assert_eq!(
5497        combine_syntax_and_fuzzy_match_highlights(
5498            string,
5499            Default::default(),
5500            syntax_ranges.into_iter(),
5501            &match_indices,
5502        ),
5503        &[
5504            (
5505                0..3,
5506                HighlightStyle {
5507                    color: Some(Color::red()),
5508                    ..Default::default()
5509                },
5510            ),
5511            (
5512                4..5,
5513                HighlightStyle {
5514                    color: Some(Color::green()),
5515                    weight: Some(fonts::Weight::BOLD),
5516                    ..Default::default()
5517                },
5518            ),
5519            (
5520                5..6,
5521                HighlightStyle {
5522                    color: Some(Color::green()),
5523                    ..Default::default()
5524                },
5525            ),
5526            (
5527                6..8,
5528                HighlightStyle {
5529                    color: Some(Color::green()),
5530                    weight: Some(fonts::Weight::BOLD),
5531                    ..Default::default()
5532                },
5533            ),
5534            (
5535                8..9,
5536                HighlightStyle {
5537                    weight: Some(fonts::Weight::BOLD),
5538                    ..Default::default()
5539                },
5540            ),
5541        ]
5542    );
5543}
5544
5545#[gpui::test]
5546async fn go_to_hunk(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
5547    let mut cx = EditorTestContext::new(cx);
5548
5549    let diff_base = r#"
5550        use some::mod;
5551
5552        const A: u32 = 42;
5553
5554        fn main() {
5555            println!("hello");
5556
5557            println!("world");
5558        }
5559        "#
5560    .unindent();
5561
5562    // Edits are modified, removed, modified, added
5563    cx.set_state(
5564        &r#"
5565        use some::modified;
5566
5567        ˇ
5568        fn main() {
5569            println!("hello there");
5570
5571            println!("around the");
5572            println!("world");
5573        }
5574        "#
5575        .unindent(),
5576    );
5577
5578    cx.set_diff_base(Some(&diff_base));
5579    deterministic.run_until_parked();
5580
5581    cx.update_editor(|editor, cx| {
5582        //Wrap around the bottom of the buffer
5583        for _ in 0..3 {
5584            editor.go_to_hunk(&GoToHunk, cx);
5585        }
5586    });
5587
5588    cx.assert_editor_state(
5589        &r#"
5590        ˇuse some::modified;
5591    
5592    
5593        fn main() {
5594            println!("hello there");
5595    
5596            println!("around the");
5597            println!("world");
5598        }
5599        "#
5600        .unindent(),
5601    );
5602
5603    cx.update_editor(|editor, cx| {
5604        //Wrap around the top of the buffer
5605        for _ in 0..2 {
5606            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
5607        }
5608    });
5609
5610    cx.assert_editor_state(
5611        &r#"
5612        use some::modified;
5613
5614
5615        fn main() {
5616        ˇ    println!("hello there");
5617
5618            println!("around the");
5619            println!("world");
5620        }
5621        "#
5622        .unindent(),
5623    );
5624
5625    cx.update_editor(|editor, cx| {
5626        editor.fold(&Fold, cx);
5627
5628        //Make sure that the fold only gets one hunk
5629        for _ in 0..4 {
5630            editor.go_to_hunk(&GoToHunk, cx);
5631        }
5632    });
5633
5634    cx.assert_editor_state(
5635        &r#"
5636        ˇuse some::modified;
5637
5638
5639        fn main() {
5640            println!("hello there");
5641
5642            println!("around the");
5643            println!("world");
5644        }
5645        "#
5646        .unindent(),
5647    );
5648}
5649
5650#[test]
5651fn test_split_words() {
5652    fn split<'a>(text: &'a str) -> Vec<&'a str> {
5653        split_words(text).collect()
5654    }
5655
5656    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
5657    assert_eq!(split("hello_world"), &["hello_", "world"]);
5658    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
5659    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
5660    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
5661    assert_eq!(split("helloworld"), &["helloworld"]);
5662}
5663
5664#[gpui::test]
5665async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
5666    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
5667    let mut assert = |before, after| {
5668        let _state_context = cx.set_state(before);
5669        cx.update_editor(|editor, cx| {
5670            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
5671        });
5672        cx.assert_editor_state(after);
5673    };
5674
5675    // Outside bracket jumps to outside of matching bracket
5676    assert("console.logˇ(var);", "console.log(var)ˇ;");
5677    assert("console.log(var)ˇ;", "console.logˇ(var);");
5678
5679    // Inside bracket jumps to inside of matching bracket
5680    assert("console.log(ˇvar);", "console.log(varˇ);");
5681    assert("console.log(varˇ);", "console.log(ˇvar);");
5682
5683    // When outside a bracket and inside, favor jumping to the inside bracket
5684    assert(
5685        "console.log('foo', [1, 2, 3]ˇ);",
5686        "console.log(ˇ'foo', [1, 2, 3]);",
5687    );
5688    assert(
5689        "console.log(ˇ'foo', [1, 2, 3]);",
5690        "console.log('foo', [1, 2, 3]ˇ);",
5691    );
5692
5693    // Bias forward if two options are equally likely
5694    assert(
5695        "let result = curried_fun()ˇ();",
5696        "let result = curried_fun()()ˇ;",
5697    );
5698
5699    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
5700    assert(
5701        indoc! {"
5702            function test() {
5703                console.log('test')ˇ
5704            }"},
5705        indoc! {"
5706            function test() {
5707                console.logˇ('test')
5708            }"},
5709    );
5710}
5711
5712fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
5713    let point = DisplayPoint::new(row as u32, column as u32);
5714    point..point
5715}
5716
5717fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
5718    let (text, ranges) = marked_text_ranges(marked_text, true);
5719    assert_eq!(view.text(cx), text);
5720    assert_eq!(
5721        view.selections.ranges(cx),
5722        ranges,
5723        "Assert selections are {}",
5724        marked_text
5725    );
5726}