editor_tests.rs

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