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_on_blank_line_auto_indents(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_indent_outdent(cx: &mut gpui::TestAppContext) {
1629    let mut cx = EditorTestContext::new(cx);
1630
1631    cx.set_state(indoc! {"
1632          «oneˇ» «twoˇ»
1633        three
1634         four
1635    "});
1636    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1637    cx.assert_editor_state(indoc! {"
1638            «oneˇ» «twoˇ»
1639        three
1640         four
1641    "});
1642
1643    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1644    cx.assert_editor_state(indoc! {"
1645        «oneˇ» «twoˇ»
1646        three
1647         four
1648    "});
1649
1650    // select across line ending
1651    cx.set_state(indoc! {"
1652        one two
1653        t«hree
1654        ˇ» four
1655    "});
1656    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1657    cx.assert_editor_state(indoc! {"
1658        one two
1659            t«hree
1660        ˇ» four
1661    "});
1662
1663    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1664    cx.assert_editor_state(indoc! {"
1665        one two
1666        t«hree
1667        ˇ» four
1668    "});
1669
1670    // Ensure that indenting/outdenting works when the cursor is at column 0.
1671    cx.set_state(indoc! {"
1672        one two
1673        ˇthree
1674            four
1675    "});
1676    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1677    cx.assert_editor_state(indoc! {"
1678        one two
1679            ˇthree
1680            four
1681    "});
1682
1683    cx.set_state(indoc! {"
1684        one two
1685        ˇ    three
1686            four
1687    "});
1688    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1689    cx.assert_editor_state(indoc! {"
1690        one two
1691        ˇthree
1692            four
1693    "});
1694}
1695
1696#[gpui::test]
1697async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
1698    let mut cx = EditorTestContext::new(cx);
1699    cx.update(|cx| {
1700        cx.update_global::<Settings, _, _>(|settings, _| {
1701            settings.editor_overrides.hard_tabs = Some(true);
1702        });
1703    });
1704
1705    // select two ranges on one line
1706    cx.set_state(indoc! {"
1707        «oneˇ» «twoˇ»
1708        three
1709        four
1710    "});
1711    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1712    cx.assert_editor_state(indoc! {"
1713        \t«oneˇ» «twoˇ»
1714        three
1715        four
1716    "});
1717    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1718    cx.assert_editor_state(indoc! {"
1719        \t\t«oneˇ» «twoˇ»
1720        three
1721        four
1722    "});
1723    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1724    cx.assert_editor_state(indoc! {"
1725        \t«oneˇ» «twoˇ»
1726        three
1727        four
1728    "});
1729    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1730    cx.assert_editor_state(indoc! {"
1731        «oneˇ» «twoˇ»
1732        three
1733        four
1734    "});
1735
1736    // select across a line ending
1737    cx.set_state(indoc! {"
1738        one two
1739        t«hree
1740        ˇ»four
1741    "});
1742    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1743    cx.assert_editor_state(indoc! {"
1744        one two
1745        \tt«hree
1746        ˇ»four
1747    "});
1748    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1749    cx.assert_editor_state(indoc! {"
1750        one two
1751        \t\tt«hree
1752        ˇ»four
1753    "});
1754    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1755    cx.assert_editor_state(indoc! {"
1756        one two
1757        \tt«hree
1758        ˇ»four
1759    "});
1760    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1761    cx.assert_editor_state(indoc! {"
1762        one two
1763        t«hree
1764        ˇ»four
1765    "});
1766
1767    // Ensure that indenting/outdenting works when the cursor is at column 0.
1768    cx.set_state(indoc! {"
1769        one two
1770        ˇthree
1771        four
1772    "});
1773    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1774    cx.assert_editor_state(indoc! {"
1775        one two
1776        ˇthree
1777        four
1778    "});
1779    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1780    cx.assert_editor_state(indoc! {"
1781        one two
1782        \tˇthree
1783        four
1784    "});
1785    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1786    cx.assert_editor_state(indoc! {"
1787        one two
1788        ˇthree
1789        four
1790    "});
1791}
1792
1793#[gpui::test]
1794fn test_indent_outdent_with_excerpts(cx: &mut gpui::MutableAppContext) {
1795    cx.set_global(
1796        Settings::test(cx)
1797            .with_language_defaults(
1798                "TOML",
1799                EditorSettings {
1800                    tab_size: Some(2.try_into().unwrap()),
1801                    ..Default::default()
1802                },
1803            )
1804            .with_language_defaults(
1805                "Rust",
1806                EditorSettings {
1807                    tab_size: Some(4.try_into().unwrap()),
1808                    ..Default::default()
1809                },
1810            ),
1811    );
1812    let toml_language = Arc::new(Language::new(
1813        LanguageConfig {
1814            name: "TOML".into(),
1815            ..Default::default()
1816        },
1817        None,
1818    ));
1819    let rust_language = Arc::new(Language::new(
1820        LanguageConfig {
1821            name: "Rust".into(),
1822            ..Default::default()
1823        },
1824        None,
1825    ));
1826
1827    let toml_buffer =
1828        cx.add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx));
1829    let rust_buffer = cx.add_model(|cx| {
1830        Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx)
1831    });
1832    let multibuffer = cx.add_model(|cx| {
1833        let mut multibuffer = MultiBuffer::new(0);
1834        multibuffer.push_excerpts(
1835            toml_buffer.clone(),
1836            [ExcerptRange {
1837                context: Point::new(0, 0)..Point::new(2, 0),
1838                primary: None,
1839            }],
1840            cx,
1841        );
1842        multibuffer.push_excerpts(
1843            rust_buffer.clone(),
1844            [ExcerptRange {
1845                context: Point::new(0, 0)..Point::new(1, 0),
1846                primary: None,
1847            }],
1848            cx,
1849        );
1850        multibuffer
1851    });
1852
1853    cx.add_window(Default::default(), |cx| {
1854        let mut editor = build_editor(multibuffer, cx);
1855
1856        assert_eq!(
1857            editor.text(cx),
1858            indoc! {"
1859                a = 1
1860                b = 2
1861
1862                const c: usize = 3;
1863            "}
1864        );
1865
1866        select_ranges(
1867            &mut editor,
1868            indoc! {"
1869                «aˇ» = 1
1870                b = 2
1871
1872                «const c:ˇ» usize = 3;
1873            "},
1874            cx,
1875        );
1876
1877        editor.tab(&Tab, cx);
1878        assert_text_with_selections(
1879            &mut editor,
1880            indoc! {"
1881                  «aˇ» = 1
1882                b = 2
1883
1884                    «const c:ˇ» usize = 3;
1885            "},
1886            cx,
1887        );
1888        editor.tab_prev(&TabPrev, cx);
1889        assert_text_with_selections(
1890            &mut editor,
1891            indoc! {"
1892                «aˇ» = 1
1893                b = 2
1894
1895                «const c:ˇ» usize = 3;
1896            "},
1897            cx,
1898        );
1899
1900        editor
1901    });
1902}
1903
1904#[gpui::test]
1905async fn test_backspace(cx: &mut gpui::TestAppContext) {
1906    let mut cx = EditorTestContext::new(cx);
1907
1908    // Basic backspace
1909    cx.set_state(indoc! {"
1910        onˇe two three
1911        fou«rˇ» five six
1912        seven «ˇeight nine
1913        »ten
1914    "});
1915    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
1916    cx.assert_editor_state(indoc! {"
1917        oˇe two three
1918        fouˇ five six
1919        seven ˇten
1920    "});
1921
1922    // Test backspace inside and around indents
1923    cx.set_state(indoc! {"
1924        zero
1925            ˇone
1926                ˇtwo
1927            ˇ ˇ ˇ  three
1928        ˇ  ˇ  four
1929    "});
1930    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
1931    cx.assert_editor_state(indoc! {"
1932        zero
1933        ˇone
1934            ˇtwo
1935        ˇ  threeˇ  four
1936    "});
1937
1938    // Test backspace with line_mode set to true
1939    cx.update_editor(|e, _| e.selections.line_mode = true);
1940    cx.set_state(indoc! {"
1941        The ˇquick ˇbrown
1942        fox jumps over
1943        the lazy dog
1944        ˇThe qu«ick bˇ»rown"});
1945    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
1946    cx.assert_editor_state(indoc! {"
1947        ˇfox jumps over
1948        the lazy dogˇ"});
1949}
1950
1951#[gpui::test]
1952async fn test_delete(cx: &mut gpui::TestAppContext) {
1953    let mut cx = EditorTestContext::new(cx);
1954
1955    cx.set_state(indoc! {"
1956        onˇe two three
1957        fou«rˇ» five six
1958        seven «ˇeight nine
1959        »ten
1960    "});
1961    cx.update_editor(|e, cx| e.delete(&Delete, cx));
1962    cx.assert_editor_state(indoc! {"
1963        onˇ two three
1964        fouˇ five six
1965        seven ˇten
1966    "});
1967
1968    // Test backspace with line_mode set to true
1969    cx.update_editor(|e, _| e.selections.line_mode = true);
1970    cx.set_state(indoc! {"
1971        The ˇquick ˇbrown
1972        fox «ˇjum»ps over
1973        the lazy dog
1974        ˇThe qu«ick bˇ»rown"});
1975    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
1976    cx.assert_editor_state("ˇthe lazy dogˇ");
1977}
1978
1979#[gpui::test]
1980fn test_delete_line(cx: &mut gpui::MutableAppContext) {
1981    cx.set_global(Settings::test(cx));
1982    let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
1983    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
1984    view.update(cx, |view, cx| {
1985        view.change_selections(None, cx, |s| {
1986            s.select_display_ranges([
1987                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
1988                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
1989                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
1990            ])
1991        });
1992        view.delete_line(&DeleteLine, cx);
1993        assert_eq!(view.display_text(cx), "ghi");
1994        assert_eq!(
1995            view.selections.display_ranges(cx),
1996            vec![
1997                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
1998                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
1999            ]
2000        );
2001    });
2002
2003    cx.set_global(Settings::test(cx));
2004    let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2005    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2006    view.update(cx, |view, cx| {
2007        view.change_selections(None, cx, |s| {
2008            s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
2009        });
2010        view.delete_line(&DeleteLine, cx);
2011        assert_eq!(view.display_text(cx), "ghi\n");
2012        assert_eq!(
2013            view.selections.display_ranges(cx),
2014            vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
2015        );
2016    });
2017}
2018
2019#[gpui::test]
2020fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
2021    cx.set_global(Settings::test(cx));
2022    let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2023    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2024    view.update(cx, |view, cx| {
2025        view.change_selections(None, cx, |s| {
2026            s.select_display_ranges([
2027                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2028                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2029                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2030                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2031            ])
2032        });
2033        view.duplicate_line(&DuplicateLine, cx);
2034        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
2035        assert_eq!(
2036            view.selections.display_ranges(cx),
2037            vec![
2038                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
2039                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
2040                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2041                DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
2042            ]
2043        );
2044    });
2045
2046    let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2047    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2048    view.update(cx, |view, cx| {
2049        view.change_selections(None, cx, |s| {
2050            s.select_display_ranges([
2051                DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
2052                DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
2053            ])
2054        });
2055        view.duplicate_line(&DuplicateLine, cx);
2056        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
2057        assert_eq!(
2058            view.selections.display_ranges(cx),
2059            vec![
2060                DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
2061                DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
2062            ]
2063        );
2064    });
2065}
2066
2067#[gpui::test]
2068fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
2069    cx.set_global(Settings::test(cx));
2070    let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
2071    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2072    view.update(cx, |view, cx| {
2073        view.fold_ranges(
2074            vec![
2075                Point::new(0, 2)..Point::new(1, 2),
2076                Point::new(2, 3)..Point::new(4, 1),
2077                Point::new(7, 0)..Point::new(8, 4),
2078            ],
2079            cx,
2080        );
2081        view.change_selections(None, cx, |s| {
2082            s.select_display_ranges([
2083                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2084                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2085                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2086                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
2087            ])
2088        });
2089        assert_eq!(
2090            view.display_text(cx),
2091            "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj"
2092        );
2093
2094        view.move_line_up(&MoveLineUp, cx);
2095        assert_eq!(
2096            view.display_text(cx),
2097            "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff"
2098        );
2099        assert_eq!(
2100            view.selections.display_ranges(cx),
2101            vec![
2102                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2103                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2104                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
2105                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
2106            ]
2107        );
2108    });
2109
2110    view.update(cx, |view, cx| {
2111        view.move_line_down(&MoveLineDown, cx);
2112        assert_eq!(
2113            view.display_text(cx),
2114            "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj"
2115        );
2116        assert_eq!(
2117            view.selections.display_ranges(cx),
2118            vec![
2119                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
2120                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2121                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2122                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
2123            ]
2124        );
2125    });
2126
2127    view.update(cx, |view, cx| {
2128        view.move_line_down(&MoveLineDown, cx);
2129        assert_eq!(
2130            view.display_text(cx),
2131            "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj"
2132        );
2133        assert_eq!(
2134            view.selections.display_ranges(cx),
2135            vec![
2136                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2137                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2138                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2139                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
2140            ]
2141        );
2142    });
2143
2144    view.update(cx, |view, cx| {
2145        view.move_line_up(&MoveLineUp, cx);
2146        assert_eq!(
2147            view.display_text(cx),
2148            "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff"
2149        );
2150        assert_eq!(
2151            view.selections.display_ranges(cx),
2152            vec![
2153                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
2154                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2155                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
2156                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
2157            ]
2158        );
2159    });
2160}
2161
2162#[gpui::test]
2163fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) {
2164    cx.set_global(Settings::test(cx));
2165    let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
2166    let snapshot = buffer.read(cx).snapshot(cx);
2167    let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2168    editor.update(cx, |editor, cx| {
2169        editor.insert_blocks(
2170            [BlockProperties {
2171                style: BlockStyle::Fixed,
2172                position: snapshot.anchor_after(Point::new(2, 0)),
2173                disposition: BlockDisposition::Below,
2174                height: 1,
2175                render: Arc::new(|_| Empty::new().boxed()),
2176            }],
2177            cx,
2178        );
2179        editor.change_selections(None, cx, |s| {
2180            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
2181        });
2182        editor.move_line_down(&MoveLineDown, cx);
2183    });
2184}
2185
2186#[gpui::test]
2187fn test_transpose(cx: &mut gpui::MutableAppContext) {
2188    cx.set_global(Settings::test(cx));
2189
2190    _ = cx
2191        .add_window(Default::default(), |cx| {
2192            let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
2193
2194            editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
2195            editor.transpose(&Default::default(), cx);
2196            assert_eq!(editor.text(cx), "bac");
2197            assert_eq!(editor.selections.ranges(cx), [2..2]);
2198
2199            editor.transpose(&Default::default(), cx);
2200            assert_eq!(editor.text(cx), "bca");
2201            assert_eq!(editor.selections.ranges(cx), [3..3]);
2202
2203            editor.transpose(&Default::default(), cx);
2204            assert_eq!(editor.text(cx), "bac");
2205            assert_eq!(editor.selections.ranges(cx), [3..3]);
2206
2207            editor
2208        })
2209        .1;
2210
2211    _ = cx
2212        .add_window(Default::default(), |cx| {
2213            let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
2214
2215            editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
2216            editor.transpose(&Default::default(), cx);
2217            assert_eq!(editor.text(cx), "acb\nde");
2218            assert_eq!(editor.selections.ranges(cx), [3..3]);
2219
2220            editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
2221            editor.transpose(&Default::default(), cx);
2222            assert_eq!(editor.text(cx), "acbd\ne");
2223            assert_eq!(editor.selections.ranges(cx), [5..5]);
2224
2225            editor.transpose(&Default::default(), cx);
2226            assert_eq!(editor.text(cx), "acbde\n");
2227            assert_eq!(editor.selections.ranges(cx), [6..6]);
2228
2229            editor.transpose(&Default::default(), cx);
2230            assert_eq!(editor.text(cx), "acbd\ne");
2231            assert_eq!(editor.selections.ranges(cx), [6..6]);
2232
2233            editor
2234        })
2235        .1;
2236
2237    _ = cx
2238        .add_window(Default::default(), |cx| {
2239            let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
2240
2241            editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
2242            editor.transpose(&Default::default(), cx);
2243            assert_eq!(editor.text(cx), "bacd\ne");
2244            assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
2245
2246            editor.transpose(&Default::default(), cx);
2247            assert_eq!(editor.text(cx), "bcade\n");
2248            assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
2249
2250            editor.transpose(&Default::default(), cx);
2251            assert_eq!(editor.text(cx), "bcda\ne");
2252            assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
2253
2254            editor.transpose(&Default::default(), cx);
2255            assert_eq!(editor.text(cx), "bcade\n");
2256            assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
2257
2258            editor.transpose(&Default::default(), cx);
2259            assert_eq!(editor.text(cx), "bcaed\n");
2260            assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
2261
2262            editor
2263        })
2264        .1;
2265
2266    _ = cx
2267        .add_window(Default::default(), |cx| {
2268            let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
2269
2270            editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
2271            editor.transpose(&Default::default(), cx);
2272            assert_eq!(editor.text(cx), "🏀🍐✋");
2273            assert_eq!(editor.selections.ranges(cx), [8..8]);
2274
2275            editor.transpose(&Default::default(), cx);
2276            assert_eq!(editor.text(cx), "🏀✋🍐");
2277            assert_eq!(editor.selections.ranges(cx), [11..11]);
2278
2279            editor.transpose(&Default::default(), cx);
2280            assert_eq!(editor.text(cx), "🏀🍐✋");
2281            assert_eq!(editor.selections.ranges(cx), [11..11]);
2282
2283            editor
2284        })
2285        .1;
2286}
2287
2288#[gpui::test]
2289async fn test_clipboard(cx: &mut gpui::TestAppContext) {
2290    let mut cx = EditorTestContext::new(cx);
2291
2292    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
2293    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2294    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
2295
2296    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
2297    cx.set_state("two ˇfour ˇsix ˇ");
2298    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2299    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
2300
2301    // Paste again but with only two cursors. Since the number of cursors doesn't
2302    // match the number of slices in the clipboard, the entire clipboard text
2303    // is pasted at each cursor.
2304    cx.set_state("ˇtwo one✅ four three six five ˇ");
2305    cx.update_editor(|e, cx| {
2306        e.handle_input("( ", cx);
2307        e.paste(&Paste, cx);
2308        e.handle_input(") ", cx);
2309    });
2310    cx.assert_editor_state(indoc! {"
2311        ( one✅ 
2312        three 
2313        five ) ˇtwo one✅ four three six five ( one✅ 
2314        three 
2315        five ) ˇ"});
2316
2317    // Cut with three selections, one of which is full-line.
2318    cx.set_state(indoc! {"
2319        1«2ˇ»3
2320        4ˇ567
2321        «8ˇ»9"});
2322    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2323    cx.assert_editor_state(indoc! {"
2324        1ˇ3
2325        ˇ9"});
2326
2327    // Paste with three selections, noticing how the copied selection that was full-line
2328    // gets inserted before the second cursor.
2329    cx.set_state(indoc! {"
2330        1ˇ3
23312332        «oˇ»ne"});
2333    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2334    cx.assert_editor_state(indoc! {"
2335        12ˇ3
2336        4567
23372338        8ˇne"});
2339
2340    // Copy with a single cursor only, which writes the whole line into the clipboard.
2341    cx.set_state(indoc! {"
2342        The quick brown
2343        fox juˇmps over
2344        the lazy dog"});
2345    cx.update_editor(|e, cx| e.copy(&Copy, cx));
2346    cx.cx.assert_clipboard_content(Some("fox jumps over\n"));
2347
2348    // Paste with three selections, noticing how the copied full-line selection is inserted
2349    // before the empty selections but replaces the selection that is non-empty.
2350    cx.set_state(indoc! {"
2351        Tˇhe quick brown
2352        «foˇ»x jumps over
2353        tˇhe lazy dog"});
2354    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2355    cx.assert_editor_state(indoc! {"
2356        fox jumps over
2357        Tˇhe quick brown
2358        fox jumps over
2359        ˇx jumps over
2360        fox jumps over
2361        tˇhe lazy dog"});
2362}
2363
2364#[gpui::test]
2365async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
2366    let mut cx = EditorTestContext::new(cx);
2367    let language = Arc::new(Language::new(
2368        LanguageConfig::default(),
2369        Some(tree_sitter_rust::language()),
2370    ));
2371    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
2372
2373    // Cut an indented block, without the leading whitespace.
2374    cx.set_state(indoc! {"
2375        const a: B = (
2376            c(),
2377            «d(
2378                e,
2379                f
2380            )ˇ»
2381        );
2382    "});
2383    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2384    cx.assert_editor_state(indoc! {"
2385        const a: B = (
2386            c(),
2387            ˇ
2388        );
2389    "});
2390
2391    // Paste it at the same position.
2392    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2393    cx.assert_editor_state(indoc! {"
2394        const a: B = (
2395            c(),
2396            d(
2397                e,
2398                f
23992400        );
2401    "});
2402
2403    // Paste it at a line with a lower indent level.
2404    cx.set_state(indoc! {"
2405        ˇ
2406        const a: B = (
2407            c(),
2408        );
2409    "});
2410    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2411    cx.assert_editor_state(indoc! {"
2412        d(
2413            e,
2414            f
24152416        const a: B = (
2417            c(),
2418        );
2419    "});
2420
2421    // Cut an indented block, with the leading whitespace.
2422    cx.set_state(indoc! {"
2423        const a: B = (
2424            c(),
2425        «    d(
2426                e,
2427                f
2428            )
2429        ˇ»);
2430    "});
2431    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2432    cx.assert_editor_state(indoc! {"
2433        const a: B = (
2434            c(),
2435        ˇ);
2436    "});
2437
2438    // Paste it at the same position.
2439    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2440    cx.assert_editor_state(indoc! {"
2441        const a: B = (
2442            c(),
2443            d(
2444                e,
2445                f
2446            )
2447        ˇ);
2448    "});
2449
2450    // Paste it at a line with a higher indent level.
2451    cx.set_state(indoc! {"
2452        const a: B = (
2453            c(),
2454            d(
2455                e,
24562457            )
2458        );
2459    "});
2460    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2461    cx.assert_editor_state(indoc! {"
2462        const a: B = (
2463            c(),
2464            d(
2465                e,
2466                f    d(
2467                    e,
2468                    f
2469                )
2470        ˇ
2471            )
2472        );
2473    "});
2474}
2475
2476#[gpui::test]
2477fn test_select_all(cx: &mut gpui::MutableAppContext) {
2478    cx.set_global(Settings::test(cx));
2479    let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
2480    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2481    view.update(cx, |view, cx| {
2482        view.select_all(&SelectAll, cx);
2483        assert_eq!(
2484            view.selections.display_ranges(cx),
2485            &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
2486        );
2487    });
2488}
2489
2490#[gpui::test]
2491fn test_select_line(cx: &mut gpui::MutableAppContext) {
2492    cx.set_global(Settings::test(cx));
2493    let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
2494    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2495    view.update(cx, |view, cx| {
2496        view.change_selections(None, cx, |s| {
2497            s.select_display_ranges([
2498                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2499                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2500                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2501                DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
2502            ])
2503        });
2504        view.select_line(&SelectLine, cx);
2505        assert_eq!(
2506            view.selections.display_ranges(cx),
2507            vec![
2508                DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
2509                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
2510            ]
2511        );
2512    });
2513
2514    view.update(cx, |view, cx| {
2515        view.select_line(&SelectLine, cx);
2516        assert_eq!(
2517            view.selections.display_ranges(cx),
2518            vec![
2519                DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
2520                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
2521            ]
2522        );
2523    });
2524
2525    view.update(cx, |view, cx| {
2526        view.select_line(&SelectLine, cx);
2527        assert_eq!(
2528            view.selections.display_ranges(cx),
2529            vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
2530        );
2531    });
2532}
2533
2534#[gpui::test]
2535fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
2536    cx.set_global(Settings::test(cx));
2537    let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
2538    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2539    view.update(cx, |view, cx| {
2540        view.fold_ranges(
2541            vec![
2542                Point::new(0, 2)..Point::new(1, 2),
2543                Point::new(2, 3)..Point::new(4, 1),
2544                Point::new(7, 0)..Point::new(8, 4),
2545            ],
2546            cx,
2547        );
2548        view.change_selections(None, cx, |s| {
2549            s.select_display_ranges([
2550                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2551                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2552                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2553                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
2554            ])
2555        });
2556        assert_eq!(view.display_text(cx), "aa…bbb\nccc…eeee\nfffff\nggggg\n…i");
2557    });
2558
2559    view.update(cx, |view, cx| {
2560        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
2561        assert_eq!(
2562            view.display_text(cx),
2563            "aaaaa\nbbbbb\nccc…eeee\nfffff\nggggg\n…i"
2564        );
2565        assert_eq!(
2566            view.selections.display_ranges(cx),
2567            [
2568                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2569                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2570                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
2571                DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
2572            ]
2573        );
2574    });
2575
2576    view.update(cx, |view, cx| {
2577        view.change_selections(None, cx, |s| {
2578            s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
2579        });
2580        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
2581        assert_eq!(
2582            view.display_text(cx),
2583            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
2584        );
2585        assert_eq!(
2586            view.selections.display_ranges(cx),
2587            [
2588                DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
2589                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
2590                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
2591                DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
2592                DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
2593                DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
2594                DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
2595                DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
2596            ]
2597        );
2598    });
2599}
2600
2601#[gpui::test]
2602fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
2603    cx.set_global(Settings::test(cx));
2604    let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
2605    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
2606
2607    view.update(cx, |view, cx| {
2608        view.change_selections(None, cx, |s| {
2609            s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
2610        });
2611    });
2612    view.update(cx, |view, cx| {
2613        view.add_selection_above(&AddSelectionAbove, cx);
2614        assert_eq!(
2615            view.selections.display_ranges(cx),
2616            vec![
2617                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
2618                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
2619            ]
2620        );
2621    });
2622
2623    view.update(cx, |view, cx| {
2624        view.add_selection_above(&AddSelectionAbove, cx);
2625        assert_eq!(
2626            view.selections.display_ranges(cx),
2627            vec![
2628                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
2629                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
2630            ]
2631        );
2632    });
2633
2634    view.update(cx, |view, cx| {
2635        view.add_selection_below(&AddSelectionBelow, cx);
2636        assert_eq!(
2637            view.selections.display_ranges(cx),
2638            vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
2639        );
2640
2641        view.undo_selection(&UndoSelection, cx);
2642        assert_eq!(
2643            view.selections.display_ranges(cx),
2644            vec![
2645                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
2646                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
2647            ]
2648        );
2649
2650        view.redo_selection(&RedoSelection, cx);
2651        assert_eq!(
2652            view.selections.display_ranges(cx),
2653            vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
2654        );
2655    });
2656
2657    view.update(cx, |view, cx| {
2658        view.add_selection_below(&AddSelectionBelow, cx);
2659        assert_eq!(
2660            view.selections.display_ranges(cx),
2661            vec![
2662                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
2663                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
2664            ]
2665        );
2666    });
2667
2668    view.update(cx, |view, cx| {
2669        view.add_selection_below(&AddSelectionBelow, cx);
2670        assert_eq!(
2671            view.selections.display_ranges(cx),
2672            vec![
2673                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
2674                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
2675            ]
2676        );
2677    });
2678
2679    view.update(cx, |view, cx| {
2680        view.change_selections(None, cx, |s| {
2681            s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
2682        });
2683    });
2684    view.update(cx, |view, cx| {
2685        view.add_selection_below(&AddSelectionBelow, cx);
2686        assert_eq!(
2687            view.selections.display_ranges(cx),
2688            vec![
2689                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
2690                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
2691            ]
2692        );
2693    });
2694
2695    view.update(cx, |view, cx| {
2696        view.add_selection_below(&AddSelectionBelow, cx);
2697        assert_eq!(
2698            view.selections.display_ranges(cx),
2699            vec![
2700                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
2701                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
2702            ]
2703        );
2704    });
2705
2706    view.update(cx, |view, cx| {
2707        view.add_selection_above(&AddSelectionAbove, cx);
2708        assert_eq!(
2709            view.selections.display_ranges(cx),
2710            vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
2711        );
2712    });
2713
2714    view.update(cx, |view, cx| {
2715        view.add_selection_above(&AddSelectionAbove, cx);
2716        assert_eq!(
2717            view.selections.display_ranges(cx),
2718            vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
2719        );
2720    });
2721
2722    view.update(cx, |view, cx| {
2723        view.change_selections(None, cx, |s| {
2724            s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
2725        });
2726        view.add_selection_below(&AddSelectionBelow, cx);
2727        assert_eq!(
2728            view.selections.display_ranges(cx),
2729            vec![
2730                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
2731                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
2732                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
2733            ]
2734        );
2735    });
2736
2737    view.update(cx, |view, cx| {
2738        view.add_selection_below(&AddSelectionBelow, cx);
2739        assert_eq!(
2740            view.selections.display_ranges(cx),
2741            vec![
2742                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
2743                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
2744                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
2745                DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
2746            ]
2747        );
2748    });
2749
2750    view.update(cx, |view, cx| {
2751        view.add_selection_above(&AddSelectionAbove, cx);
2752        assert_eq!(
2753            view.selections.display_ranges(cx),
2754            vec![
2755                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
2756                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
2757                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
2758            ]
2759        );
2760    });
2761
2762    view.update(cx, |view, cx| {
2763        view.change_selections(None, cx, |s| {
2764            s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
2765        });
2766    });
2767    view.update(cx, |view, cx| {
2768        view.add_selection_above(&AddSelectionAbove, cx);
2769        assert_eq!(
2770            view.selections.display_ranges(cx),
2771            vec![
2772                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
2773                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
2774                DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
2775                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
2776            ]
2777        );
2778    });
2779
2780    view.update(cx, |view, cx| {
2781        view.add_selection_below(&AddSelectionBelow, cx);
2782        assert_eq!(
2783            view.selections.display_ranges(cx),
2784            vec![
2785                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
2786                DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
2787                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
2788            ]
2789        );
2790    });
2791}
2792
2793#[gpui::test]
2794async fn test_select_next(cx: &mut gpui::TestAppContext) {
2795    let mut cx = EditorTestContext::new(cx);
2796    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
2797
2798    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
2799    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
2800
2801    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
2802    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
2803
2804    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
2805    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
2806
2807    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
2808    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
2809
2810    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
2811    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
2812
2813    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
2814    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
2815}
2816
2817#[gpui::test]
2818async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
2819    cx.update(|cx| cx.set_global(Settings::test(cx)));
2820    let language = Arc::new(Language::new(
2821        LanguageConfig::default(),
2822        Some(tree_sitter_rust::language()),
2823    ));
2824
2825    let text = r#"
2826        use mod1::mod2::{mod3, mod4};
2827
2828        fn fn_1(param1: bool, param2: &str) {
2829            let var1 = "text";
2830        }
2831    "#
2832    .unindent();
2833
2834    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
2835    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
2836    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
2837    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
2838        .await;
2839
2840    view.update(cx, |view, cx| {
2841        view.change_selections(None, cx, |s| {
2842            s.select_display_ranges([
2843                DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
2844                DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
2845                DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
2846            ]);
2847        });
2848        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
2849    });
2850    assert_eq!(
2851        view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
2852        &[
2853            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
2854            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
2855            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
2856        ]
2857    );
2858
2859    view.update(cx, |view, cx| {
2860        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
2861    });
2862    assert_eq!(
2863        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2864        &[
2865            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
2866            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
2867        ]
2868    );
2869
2870    view.update(cx, |view, cx| {
2871        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
2872    });
2873    assert_eq!(
2874        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2875        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
2876    );
2877
2878    // Trying to expand the selected syntax node one more time has no effect.
2879    view.update(cx, |view, cx| {
2880        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
2881    });
2882    assert_eq!(
2883        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2884        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
2885    );
2886
2887    view.update(cx, |view, cx| {
2888        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
2889    });
2890    assert_eq!(
2891        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2892        &[
2893            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
2894            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
2895        ]
2896    );
2897
2898    view.update(cx, |view, cx| {
2899        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
2900    });
2901    assert_eq!(
2902        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2903        &[
2904            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
2905            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
2906            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
2907        ]
2908    );
2909
2910    view.update(cx, |view, cx| {
2911        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
2912    });
2913    assert_eq!(
2914        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2915        &[
2916            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
2917            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
2918            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
2919        ]
2920    );
2921
2922    // Trying to shrink the selected syntax node one more time has no effect.
2923    view.update(cx, |view, cx| {
2924        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
2925    });
2926    assert_eq!(
2927        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2928        &[
2929            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
2930            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
2931            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
2932        ]
2933    );
2934
2935    // Ensure that we keep expanding the selection if the larger selection starts or ends within
2936    // a fold.
2937    view.update(cx, |view, cx| {
2938        view.fold_ranges(
2939            vec![
2940                Point::new(0, 21)..Point::new(0, 24),
2941                Point::new(3, 20)..Point::new(3, 22),
2942            ],
2943            cx,
2944        );
2945        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
2946    });
2947    assert_eq!(
2948        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
2949        &[
2950            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
2951            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
2952            DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
2953        ]
2954    );
2955}
2956
2957#[gpui::test]
2958async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
2959    cx.update(|cx| cx.set_global(Settings::test(cx)));
2960    let language = Arc::new(
2961        Language::new(
2962            LanguageConfig {
2963                brackets: vec![
2964                    BracketPair {
2965                        start: "{".to_string(),
2966                        end: "}".to_string(),
2967                        close: false,
2968                        newline: true,
2969                    },
2970                    BracketPair {
2971                        start: "(".to_string(),
2972                        end: ")".to_string(),
2973                        close: false,
2974                        newline: true,
2975                    },
2976                ],
2977                ..Default::default()
2978            },
2979            Some(tree_sitter_rust::language()),
2980        )
2981        .with_indents_query(
2982            r#"
2983                (_ "(" ")" @end) @indent
2984                (_ "{" "}" @end) @indent
2985            "#,
2986        )
2987        .unwrap(),
2988    );
2989
2990    let text = "fn a() {}";
2991
2992    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
2993    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
2994    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
2995    editor
2996        .condition(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
2997        .await;
2998
2999    editor.update(cx, |editor, cx| {
3000        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
3001        editor.newline(&Newline, cx);
3002        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
3003        assert_eq!(
3004            editor.selections.ranges(cx),
3005            &[
3006                Point::new(1, 4)..Point::new(1, 4),
3007                Point::new(3, 4)..Point::new(3, 4),
3008                Point::new(5, 0)..Point::new(5, 0)
3009            ]
3010        );
3011    });
3012}
3013
3014#[gpui::test]
3015async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
3016    let mut cx = EditorTestContext::new(cx);
3017
3018    let language = Arc::new(Language::new(
3019        LanguageConfig {
3020            brackets: vec![
3021                BracketPair {
3022                    start: "{".to_string(),
3023                    end: "}".to_string(),
3024                    close: true,
3025                    newline: true,
3026                },
3027                BracketPair {
3028                    start: "(".to_string(),
3029                    end: ")".to_string(),
3030                    close: true,
3031                    newline: true,
3032                },
3033                BracketPair {
3034                    start: "/*".to_string(),
3035                    end: " */".to_string(),
3036                    close: true,
3037                    newline: true,
3038                },
3039                BracketPair {
3040                    start: "[".to_string(),
3041                    end: "]".to_string(),
3042                    close: false,
3043                    newline: true,
3044                },
3045                BracketPair {
3046                    start: "\"".to_string(),
3047                    end: "\"".to_string(),
3048                    close: true,
3049                    newline: false,
3050                },
3051            ],
3052            autoclose_before: "})]".to_string(),
3053            ..Default::default()
3054        },
3055        Some(tree_sitter_rust::language()),
3056    ));
3057
3058    let registry = Arc::new(LanguageRegistry::test());
3059    registry.add(language.clone());
3060    cx.update_buffer(|buffer, cx| {
3061        buffer.set_language_registry(registry);
3062        buffer.set_language(Some(language), cx);
3063    });
3064
3065    cx.set_state(
3066        &r#"
3067            🏀ˇ
3068            εˇ
3069            ❤️ˇ
3070        "#
3071        .unindent(),
3072    );
3073
3074    // autoclose multiple nested brackets at multiple cursors
3075    cx.update_editor(|view, cx| {
3076        view.handle_input("{", cx);
3077        view.handle_input("{", cx);
3078        view.handle_input("{", cx);
3079    });
3080    cx.assert_editor_state(
3081        &"
3082            🏀{{{ˇ}}}
3083            ε{{{ˇ}}}
3084            ❤️{{{ˇ}}}
3085        "
3086        .unindent(),
3087    );
3088
3089    // insert a different closing bracket
3090    cx.update_editor(|view, cx| {
3091        view.handle_input(")", cx);
3092    });
3093    cx.assert_editor_state(
3094        &"
3095            🏀{{{)ˇ}}}
3096            ε{{{)ˇ}}}
3097            ❤️{{{)ˇ}}}
3098        "
3099        .unindent(),
3100    );
3101
3102    // skip over the auto-closed brackets when typing a closing bracket
3103    cx.update_editor(|view, cx| {
3104        view.move_right(&MoveRight, cx);
3105        view.handle_input("}", cx);
3106        view.handle_input("}", cx);
3107        view.handle_input("}", cx);
3108    });
3109    cx.assert_editor_state(
3110        &"
3111            🏀{{{)}}}}ˇ
3112            ε{{{)}}}}ˇ
3113            ❤️{{{)}}}}ˇ
3114        "
3115        .unindent(),
3116    );
3117
3118    // autoclose multi-character pairs
3119    cx.set_state(
3120        &"
3121            ˇ
3122            ˇ
3123        "
3124        .unindent(),
3125    );
3126    cx.update_editor(|view, cx| {
3127        view.handle_input("/", cx);
3128        view.handle_input("*", cx);
3129    });
3130    cx.assert_editor_state(
3131        &"
3132            /*ˇ */
3133            /*ˇ */
3134        "
3135        .unindent(),
3136    );
3137
3138    // one cursor autocloses a multi-character pair, one cursor
3139    // does not autoclose.
3140    cx.set_state(
3141        &"
31423143            ˇ
3144        "
3145        .unindent(),
3146    );
3147    cx.update_editor(|view, cx| view.handle_input("*", cx));
3148    cx.assert_editor_state(
3149        &"
3150            /*ˇ */
31513152        "
3153        .unindent(),
3154    );
3155
3156    // Don't autoclose if the next character isn't whitespace and isn't
3157    // listed in the language's "autoclose_before" section.
3158    cx.set_state("ˇa b");
3159    cx.update_editor(|view, cx| view.handle_input("{", cx));
3160    cx.assert_editor_state("{ˇa b");
3161
3162    // Don't autoclose if `close` is false for the bracket pair
3163    cx.set_state("ˇ");
3164    cx.update_editor(|view, cx| view.handle_input("[", cx));
3165    cx.assert_editor_state("");
3166
3167    // Surround with brackets if text is selected
3168    cx.set_state("«aˇ» b");
3169    cx.update_editor(|view, cx| view.handle_input("{", cx));
3170    cx.assert_editor_state("{«aˇ»} b");
3171
3172    // Autclose pair where the start and end characters are the same
3173    cx.set_state("");
3174    cx.update_editor(|view, cx| view.handle_input("\"", cx));
3175    cx.assert_editor_state("a\"ˇ\"");
3176    cx.update_editor(|view, cx| view.handle_input("\"", cx));
3177    cx.assert_editor_state("a\"\"ˇ");
3178}
3179
3180#[gpui::test]
3181async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
3182    let mut cx = EditorTestContext::new(cx);
3183
3184    let html_language = Arc::new(
3185        Language::new(
3186            LanguageConfig {
3187                name: "HTML".into(),
3188                brackets: vec![
3189                    BracketPair {
3190                        start: "<".into(),
3191                        end: ">".into(),
3192                        close: true,
3193                        ..Default::default()
3194                    },
3195                    BracketPair {
3196                        start: "{".into(),
3197                        end: "}".into(),
3198                        close: true,
3199                        ..Default::default()
3200                    },
3201                    BracketPair {
3202                        start: "(".into(),
3203                        end: ")".into(),
3204                        close: true,
3205                        ..Default::default()
3206                    },
3207                ],
3208                autoclose_before: "})]>".into(),
3209                ..Default::default()
3210            },
3211            Some(tree_sitter_html::language()),
3212        )
3213        .with_injection_query(
3214            r#"
3215            (script_element
3216                (raw_text) @content
3217                (#set! "language" "javascript"))
3218            "#,
3219        )
3220        .unwrap(),
3221    );
3222
3223    let javascript_language = Arc::new(Language::new(
3224        LanguageConfig {
3225            name: "JavaScript".into(),
3226            brackets: vec![
3227                BracketPair {
3228                    start: "/*".into(),
3229                    end: " */".into(),
3230                    close: true,
3231                    ..Default::default()
3232                },
3233                BracketPair {
3234                    start: "{".into(),
3235                    end: "}".into(),
3236                    close: true,
3237                    ..Default::default()
3238                },
3239                BracketPair {
3240                    start: "(".into(),
3241                    end: ")".into(),
3242                    close: true,
3243                    ..Default::default()
3244                },
3245            ],
3246            autoclose_before: "})]>".into(),
3247            ..Default::default()
3248        },
3249        Some(tree_sitter_javascript::language()),
3250    ));
3251
3252    let registry = Arc::new(LanguageRegistry::test());
3253    registry.add(html_language.clone());
3254    registry.add(javascript_language.clone());
3255
3256    cx.update_buffer(|buffer, cx| {
3257        buffer.set_language_registry(registry);
3258        buffer.set_language(Some(html_language), cx);
3259    });
3260
3261    cx.set_state(
3262        &r#"
3263            <body>ˇ
3264                <script>
3265                    var x = 1;ˇ
3266                </script>
3267            </body>ˇ
3268        "#
3269        .unindent(),
3270    );
3271
3272    // Precondition: different languages are active at different locations.
3273    cx.update_editor(|editor, cx| {
3274        let snapshot = editor.snapshot(cx);
3275        let cursors = editor.selections.ranges::<usize>(cx);
3276        let languages = cursors
3277            .iter()
3278            .map(|c| snapshot.language_at(c.start).unwrap().name())
3279            .collect::<Vec<_>>();
3280        assert_eq!(
3281            languages,
3282            &["HTML".into(), "JavaScript".into(), "HTML".into()]
3283        );
3284    });
3285
3286    // Angle brackets autoclose in HTML, but not JavaScript.
3287    cx.update_editor(|editor, cx| {
3288        editor.handle_input("<", cx);
3289        editor.handle_input("a", cx);
3290    });
3291    cx.assert_editor_state(
3292        &r#"
3293            <body><aˇ>
3294                <script>
3295                    var x = 1;<aˇ
3296                </script>
3297            </body><aˇ>
3298        "#
3299        .unindent(),
3300    );
3301
3302    // Curly braces and parens autoclose in both HTML and JavaScript.
3303    cx.update_editor(|editor, cx| {
3304        editor.handle_input(" b=", cx);
3305        editor.handle_input("{", cx);
3306        editor.handle_input("c", cx);
3307        editor.handle_input("(", cx);
3308    });
3309    cx.assert_editor_state(
3310        &r#"
3311            <body><a b={c(ˇ)}>
3312                <script>
3313                    var x = 1;<a b={c(ˇ)}
3314                </script>
3315            </body><a b={c(ˇ)}>
3316        "#
3317        .unindent(),
3318    );
3319
3320    // Brackets that were already autoclosed are skipped.
3321    cx.update_editor(|editor, cx| {
3322        editor.handle_input(")", cx);
3323        editor.handle_input("d", cx);
3324        editor.handle_input("}", cx);
3325    });
3326    cx.assert_editor_state(
3327        &r#"
3328            <body><a b={c()d}ˇ>
3329                <script>
3330                    var x = 1;<a b={c()d}ˇ
3331                </script>
3332            </body><a b={c()d}ˇ>
3333        "#
3334        .unindent(),
3335    );
3336    cx.update_editor(|editor, cx| {
3337        editor.handle_input(">", cx);
3338    });
3339    cx.assert_editor_state(
3340        &r#"
3341            <body><a b={c()d}>ˇ
3342                <script>
3343                    var x = 1;<a b={c()d}>ˇ
3344                </script>
3345            </body><a b={c()d}>ˇ
3346        "#
3347        .unindent(),
3348    );
3349
3350    // Reset
3351    cx.set_state(
3352        &r#"
3353            <body>ˇ
3354                <script>
3355                    var x = 1;ˇ
3356                </script>
3357            </body>ˇ
3358        "#
3359        .unindent(),
3360    );
3361
3362    cx.update_editor(|editor, cx| {
3363        editor.handle_input("<", cx);
3364    });
3365    cx.assert_editor_state(
3366        &r#"
3367            <body><ˇ>
3368                <script>
3369                    var x = 1;<ˇ
3370                </script>
3371            </body><ˇ>
3372        "#
3373        .unindent(),
3374    );
3375
3376    // When backspacing, the closing angle brackets are removed.
3377    cx.update_editor(|editor, cx| {
3378        editor.backspace(&Backspace, cx);
3379    });
3380    cx.assert_editor_state(
3381        &r#"
3382            <body>ˇ
3383                <script>
3384                    var x = 1;ˇ
3385                </script>
3386            </body>ˇ
3387        "#
3388        .unindent(),
3389    );
3390
3391    // Block comments autoclose in JavaScript, but not HTML.
3392    cx.update_editor(|editor, cx| {
3393        editor.handle_input("/", cx);
3394        editor.handle_input("*", cx);
3395    });
3396    cx.assert_editor_state(
3397        &r#"
3398            <body>/*ˇ
3399                <script>
3400                    var x = 1;/*ˇ */
3401                </script>
3402            </body>/*ˇ
3403        "#
3404        .unindent(),
3405    );
3406}
3407
3408#[gpui::test]
3409async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
3410    cx.update(|cx| cx.set_global(Settings::test(cx)));
3411    let language = Arc::new(Language::new(
3412        LanguageConfig {
3413            brackets: vec![BracketPair {
3414                start: "{".to_string(),
3415                end: "}".to_string(),
3416                close: true,
3417                newline: true,
3418            }],
3419            ..Default::default()
3420        },
3421        Some(tree_sitter_rust::language()),
3422    ));
3423
3424    let text = r#"
3425        a
3426        b
3427        c
3428    "#
3429    .unindent();
3430
3431    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3432    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3433    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
3434    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
3435        .await;
3436
3437    view.update(cx, |view, cx| {
3438        view.change_selections(None, cx, |s| {
3439            s.select_display_ranges([
3440                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3441                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
3442                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
3443            ])
3444        });
3445
3446        view.handle_input("{", cx);
3447        view.handle_input("{", cx);
3448        view.handle_input("{", cx);
3449        assert_eq!(
3450            view.text(cx),
3451            "
3452                {{{a}}}
3453                {{{b}}}
3454                {{{c}}}
3455            "
3456            .unindent()
3457        );
3458        assert_eq!(
3459            view.selections.display_ranges(cx),
3460            [
3461                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
3462                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
3463                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
3464            ]
3465        );
3466
3467        view.undo(&Undo, cx);
3468        assert_eq!(
3469            view.text(cx),
3470            "
3471                a
3472                b
3473                c
3474            "
3475            .unindent()
3476        );
3477        assert_eq!(
3478            view.selections.display_ranges(cx),
3479            [
3480                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3481                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
3482                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
3483            ]
3484        );
3485    });
3486}
3487
3488#[gpui::test]
3489async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
3490    cx.update(|cx| cx.set_global(Settings::test(cx)));
3491    let language = Arc::new(Language::new(
3492        LanguageConfig {
3493            brackets: vec![BracketPair {
3494                start: "{".to_string(),
3495                end: "}".to_string(),
3496                close: true,
3497                newline: true,
3498            }],
3499            autoclose_before: "}".to_string(),
3500            ..Default::default()
3501        },
3502        Some(tree_sitter_rust::language()),
3503    ));
3504
3505    let text = r#"
3506        a
3507        b
3508        c
3509    "#
3510    .unindent();
3511
3512    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3513    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3514    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3515    editor
3516        .condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
3517        .await;
3518
3519    editor.update(cx, |editor, cx| {
3520        editor.change_selections(None, cx, |s| {
3521            s.select_ranges([
3522                Point::new(0, 1)..Point::new(0, 1),
3523                Point::new(1, 1)..Point::new(1, 1),
3524                Point::new(2, 1)..Point::new(2, 1),
3525            ])
3526        });
3527
3528        editor.handle_input("{", cx);
3529        editor.handle_input("{", cx);
3530        editor.handle_input("_", cx);
3531        assert_eq!(
3532            editor.text(cx),
3533            "
3534                a{{_}}
3535                b{{_}}
3536                c{{_}}
3537            "
3538            .unindent()
3539        );
3540        assert_eq!(
3541            editor.selections.ranges::<Point>(cx),
3542            [
3543                Point::new(0, 4)..Point::new(0, 4),
3544                Point::new(1, 4)..Point::new(1, 4),
3545                Point::new(2, 4)..Point::new(2, 4)
3546            ]
3547        );
3548
3549        editor.backspace(&Default::default(), cx);
3550        editor.backspace(&Default::default(), cx);
3551        assert_eq!(
3552            editor.text(cx),
3553            "
3554                a{}
3555                b{}
3556                c{}
3557            "
3558            .unindent()
3559        );
3560        assert_eq!(
3561            editor.selections.ranges::<Point>(cx),
3562            [
3563                Point::new(0, 2)..Point::new(0, 2),
3564                Point::new(1, 2)..Point::new(1, 2),
3565                Point::new(2, 2)..Point::new(2, 2)
3566            ]
3567        );
3568
3569        editor.delete_to_previous_word_start(&Default::default(), cx);
3570        assert_eq!(
3571            editor.text(cx),
3572            "
3573                a
3574                b
3575                c
3576            "
3577            .unindent()
3578        );
3579        assert_eq!(
3580            editor.selections.ranges::<Point>(cx),
3581            [
3582                Point::new(0, 1)..Point::new(0, 1),
3583                Point::new(1, 1)..Point::new(1, 1),
3584                Point::new(2, 1)..Point::new(2, 1)
3585            ]
3586        );
3587    });
3588}
3589
3590#[gpui::test]
3591async fn test_snippets(cx: &mut gpui::TestAppContext) {
3592    cx.update(|cx| cx.set_global(Settings::test(cx)));
3593
3594    let (text, insertion_ranges) = marked_text_ranges(
3595        indoc! {"
3596            a.ˇ b
3597            a.ˇ b
3598            a.ˇ b
3599        "},
3600        false,
3601    );
3602
3603    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
3604    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3605
3606    editor.update(cx, |editor, cx| {
3607        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
3608
3609        editor
3610            .insert_snippet(&insertion_ranges, snippet, cx)
3611            .unwrap();
3612
3613        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
3614            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
3615            assert_eq!(editor.text(cx), expected_text);
3616            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
3617        }
3618
3619        assert(
3620            editor,
3621            cx,
3622            indoc! {"
3623                a.f(«one», two, «three») b
3624                a.f(«one», two, «three») b
3625                a.f(«one», two, «three») b
3626            "},
3627        );
3628
3629        // Can't move earlier than the first tab stop
3630        assert!(!editor.move_to_prev_snippet_tabstop(cx));
3631        assert(
3632            editor,
3633            cx,
3634            indoc! {"
3635                a.f(«one», two, «three») b
3636                a.f(«one», two, «three») b
3637                a.f(«one», two, «three») b
3638            "},
3639        );
3640
3641        assert!(editor.move_to_next_snippet_tabstop(cx));
3642        assert(
3643            editor,
3644            cx,
3645            indoc! {"
3646                a.f(one, «two», three) b
3647                a.f(one, «two», three) b
3648                a.f(one, «two», three) b
3649            "},
3650        );
3651
3652        editor.move_to_prev_snippet_tabstop(cx);
3653        assert(
3654            editor,
3655            cx,
3656            indoc! {"
3657                a.f(«one», two, «three») b
3658                a.f(«one», two, «three») b
3659                a.f(«one», two, «three») b
3660            "},
3661        );
3662
3663        assert!(editor.move_to_next_snippet_tabstop(cx));
3664        assert(
3665            editor,
3666            cx,
3667            indoc! {"
3668                a.f(one, «two», three) b
3669                a.f(one, «two», three) b
3670                a.f(one, «two», three) b
3671            "},
3672        );
3673        assert!(editor.move_to_next_snippet_tabstop(cx));
3674        assert(
3675            editor,
3676            cx,
3677            indoc! {"
3678                a.f(one, two, three)ˇ b
3679                a.f(one, two, three)ˇ b
3680                a.f(one, two, three)ˇ b
3681            "},
3682        );
3683
3684        // As soon as the last tab stop is reached, snippet state is gone
3685        editor.move_to_prev_snippet_tabstop(cx);
3686        assert(
3687            editor,
3688            cx,
3689            indoc! {"
3690                a.f(one, two, three)ˇ b
3691                a.f(one, two, three)ˇ b
3692                a.f(one, two, three)ˇ b
3693            "},
3694        );
3695    });
3696}
3697
3698#[gpui::test]
3699async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
3700    cx.foreground().forbid_parking();
3701
3702    let mut language = Language::new(
3703        LanguageConfig {
3704            name: "Rust".into(),
3705            path_suffixes: vec!["rs".to_string()],
3706            ..Default::default()
3707        },
3708        Some(tree_sitter_rust::language()),
3709    );
3710    let mut fake_servers = language
3711        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3712            capabilities: lsp::ServerCapabilities {
3713                document_formatting_provider: Some(lsp::OneOf::Left(true)),
3714                ..Default::default()
3715            },
3716            ..Default::default()
3717        }))
3718        .await;
3719
3720    let fs = FakeFs::new(cx.background());
3721    fs.insert_file("/file.rs", Default::default()).await;
3722
3723    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
3724    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
3725    let buffer = project
3726        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
3727        .await
3728        .unwrap();
3729
3730    cx.foreground().start_waiting();
3731    let fake_server = fake_servers.next().await.unwrap();
3732
3733    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3734    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3735    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
3736    assert!(cx.read(|cx| editor.is_dirty(cx)));
3737
3738    let save = cx.update(|cx| editor.save(project.clone(), cx));
3739    fake_server
3740        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
3741            assert_eq!(
3742                params.text_document.uri,
3743                lsp::Url::from_file_path("/file.rs").unwrap()
3744            );
3745            assert_eq!(params.options.tab_size, 4);
3746            Ok(Some(vec![lsp::TextEdit::new(
3747                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
3748                ", ".to_string(),
3749            )]))
3750        })
3751        .next()
3752        .await;
3753    cx.foreground().start_waiting();
3754    save.await.unwrap();
3755    assert_eq!(
3756        editor.read_with(cx, |editor, cx| editor.text(cx)),
3757        "one, two\nthree\n"
3758    );
3759    assert!(!cx.read(|cx| editor.is_dirty(cx)));
3760
3761    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
3762    assert!(cx.read(|cx| editor.is_dirty(cx)));
3763
3764    // Ensure we can still save even if formatting hangs.
3765    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
3766        assert_eq!(
3767            params.text_document.uri,
3768            lsp::Url::from_file_path("/file.rs").unwrap()
3769        );
3770        futures::future::pending::<()>().await;
3771        unreachable!()
3772    });
3773    let save = cx.update(|cx| editor.save(project.clone(), cx));
3774    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
3775    cx.foreground().start_waiting();
3776    save.await.unwrap();
3777    assert_eq!(
3778        editor.read_with(cx, |editor, cx| editor.text(cx)),
3779        "one\ntwo\nthree\n"
3780    );
3781    assert!(!cx.read(|cx| editor.is_dirty(cx)));
3782
3783    // Set rust language override and assert overriden tabsize is sent to language server
3784    cx.update(|cx| {
3785        cx.update_global::<Settings, _, _>(|settings, _| {
3786            settings.language_overrides.insert(
3787                "Rust".into(),
3788                EditorSettings {
3789                    tab_size: Some(8.try_into().unwrap()),
3790                    ..Default::default()
3791                },
3792            );
3793        })
3794    });
3795
3796    let save = cx.update(|cx| editor.save(project.clone(), cx));
3797    fake_server
3798        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
3799            assert_eq!(
3800                params.text_document.uri,
3801                lsp::Url::from_file_path("/file.rs").unwrap()
3802            );
3803            assert_eq!(params.options.tab_size, 8);
3804            Ok(Some(vec![]))
3805        })
3806        .next()
3807        .await;
3808    cx.foreground().start_waiting();
3809    save.await.unwrap();
3810}
3811
3812#[gpui::test]
3813async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
3814    cx.foreground().forbid_parking();
3815
3816    let mut language = Language::new(
3817        LanguageConfig {
3818            name: "Rust".into(),
3819            path_suffixes: vec!["rs".to_string()],
3820            ..Default::default()
3821        },
3822        Some(tree_sitter_rust::language()),
3823    );
3824    let mut fake_servers = language
3825        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3826            capabilities: lsp::ServerCapabilities {
3827                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
3828                ..Default::default()
3829            },
3830            ..Default::default()
3831        }))
3832        .await;
3833
3834    let fs = FakeFs::new(cx.background());
3835    fs.insert_file("/file.rs", Default::default()).await;
3836
3837    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
3838    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
3839    let buffer = project
3840        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
3841        .await
3842        .unwrap();
3843
3844    cx.foreground().start_waiting();
3845    let fake_server = fake_servers.next().await.unwrap();
3846
3847    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3848    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3849    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
3850    assert!(cx.read(|cx| editor.is_dirty(cx)));
3851
3852    let save = cx.update(|cx| editor.save(project.clone(), cx));
3853    fake_server
3854        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
3855            assert_eq!(
3856                params.text_document.uri,
3857                lsp::Url::from_file_path("/file.rs").unwrap()
3858            );
3859            assert_eq!(params.options.tab_size, 4);
3860            Ok(Some(vec![lsp::TextEdit::new(
3861                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
3862                ", ".to_string(),
3863            )]))
3864        })
3865        .next()
3866        .await;
3867    cx.foreground().start_waiting();
3868    save.await.unwrap();
3869    assert_eq!(
3870        editor.read_with(cx, |editor, cx| editor.text(cx)),
3871        "one, two\nthree\n"
3872    );
3873    assert!(!cx.read(|cx| editor.is_dirty(cx)));
3874
3875    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
3876    assert!(cx.read(|cx| editor.is_dirty(cx)));
3877
3878    // Ensure we can still save even if formatting hangs.
3879    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
3880        move |params, _| async move {
3881            assert_eq!(
3882                params.text_document.uri,
3883                lsp::Url::from_file_path("/file.rs").unwrap()
3884            );
3885            futures::future::pending::<()>().await;
3886            unreachable!()
3887        },
3888    );
3889    let save = cx.update(|cx| editor.save(project.clone(), cx));
3890    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
3891    cx.foreground().start_waiting();
3892    save.await.unwrap();
3893    assert_eq!(
3894        editor.read_with(cx, |editor, cx| editor.text(cx)),
3895        "one\ntwo\nthree\n"
3896    );
3897    assert!(!cx.read(|cx| editor.is_dirty(cx)));
3898
3899    // Set rust language override and assert overriden tabsize is sent to language server
3900    cx.update(|cx| {
3901        cx.update_global::<Settings, _, _>(|settings, _| {
3902            settings.language_overrides.insert(
3903                "Rust".into(),
3904                EditorSettings {
3905                    tab_size: Some(8.try_into().unwrap()),
3906                    ..Default::default()
3907                },
3908            );
3909        })
3910    });
3911
3912    let save = cx.update(|cx| editor.save(project.clone(), cx));
3913    fake_server
3914        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
3915            assert_eq!(
3916                params.text_document.uri,
3917                lsp::Url::from_file_path("/file.rs").unwrap()
3918            );
3919            assert_eq!(params.options.tab_size, 8);
3920            Ok(Some(vec![]))
3921        })
3922        .next()
3923        .await;
3924    cx.foreground().start_waiting();
3925    save.await.unwrap();
3926}
3927
3928#[gpui::test]
3929async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
3930    cx.foreground().forbid_parking();
3931
3932    let mut language = Language::new(
3933        LanguageConfig {
3934            name: "Rust".into(),
3935            path_suffixes: vec!["rs".to_string()],
3936            ..Default::default()
3937        },
3938        Some(tree_sitter_rust::language()),
3939    );
3940    let mut fake_servers = language
3941        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3942            capabilities: lsp::ServerCapabilities {
3943                document_formatting_provider: Some(lsp::OneOf::Left(true)),
3944                ..Default::default()
3945            },
3946            ..Default::default()
3947        }))
3948        .await;
3949
3950    let fs = FakeFs::new(cx.background());
3951    fs.insert_file("/file.rs", Default::default()).await;
3952
3953    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
3954    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
3955    let buffer = project
3956        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
3957        .await
3958        .unwrap();
3959
3960    cx.foreground().start_waiting();
3961    let fake_server = fake_servers.next().await.unwrap();
3962
3963    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3964    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3965    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
3966
3967    let format = editor.update(cx, |editor, cx| editor.perform_format(project.clone(), cx));
3968    fake_server
3969        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
3970            assert_eq!(
3971                params.text_document.uri,
3972                lsp::Url::from_file_path("/file.rs").unwrap()
3973            );
3974            assert_eq!(params.options.tab_size, 4);
3975            Ok(Some(vec![lsp::TextEdit::new(
3976                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
3977                ", ".to_string(),
3978            )]))
3979        })
3980        .next()
3981        .await;
3982    cx.foreground().start_waiting();
3983    format.await.unwrap();
3984    assert_eq!(
3985        editor.read_with(cx, |editor, cx| editor.text(cx)),
3986        "one, two\nthree\n"
3987    );
3988
3989    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
3990    // Ensure we don't lock if formatting hangs.
3991    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
3992        assert_eq!(
3993            params.text_document.uri,
3994            lsp::Url::from_file_path("/file.rs").unwrap()
3995        );
3996        futures::future::pending::<()>().await;
3997        unreachable!()
3998    });
3999    let format = editor.update(cx, |editor, cx| editor.perform_format(project, cx));
4000    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4001    cx.foreground().start_waiting();
4002    format.await.unwrap();
4003    assert_eq!(
4004        editor.read_with(cx, |editor, cx| editor.text(cx)),
4005        "one\ntwo\nthree\n"
4006    );
4007}
4008
4009#[gpui::test]
4010async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
4011    cx.foreground().forbid_parking();
4012
4013    let mut cx = EditorLspTestContext::new_rust(
4014        lsp::ServerCapabilities {
4015            document_formatting_provider: Some(lsp::OneOf::Left(true)),
4016            ..Default::default()
4017        },
4018        cx,
4019    )
4020    .await;
4021
4022    cx.set_state(indoc! {"
4023        one.twoˇ
4024    "});
4025
4026    // The format request takes a long time. When it completes, it inserts
4027    // a newline and an indent before the `.`
4028    cx.lsp
4029        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
4030            let executor = cx.background();
4031            async move {
4032                executor.timer(Duration::from_millis(100)).await;
4033                Ok(Some(vec![lsp::TextEdit {
4034                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
4035                    new_text: "\n    ".into(),
4036                }]))
4037            }
4038        });
4039
4040    // Submit a format request.
4041    let format_1 = cx
4042        .update_editor(|editor, cx| editor.format(&Format, cx))
4043        .unwrap();
4044    cx.foreground().run_until_parked();
4045
4046    // Submit a second format request.
4047    let format_2 = cx
4048        .update_editor(|editor, cx| editor.format(&Format, cx))
4049        .unwrap();
4050    cx.foreground().run_until_parked();
4051
4052    // Wait for both format requests to complete
4053    cx.foreground().advance_clock(Duration::from_millis(200));
4054    cx.foreground().start_waiting();
4055    format_1.await.unwrap();
4056    cx.foreground().start_waiting();
4057    format_2.await.unwrap();
4058
4059    // The formatting edits only happens once.
4060    cx.assert_editor_state(indoc! {"
4061        one
4062            .twoˇ
4063    "});
4064}
4065
4066#[gpui::test]
4067async fn test_completion(cx: &mut gpui::TestAppContext) {
4068    let mut cx = EditorLspTestContext::new_rust(
4069        lsp::ServerCapabilities {
4070            completion_provider: Some(lsp::CompletionOptions {
4071                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
4072                ..Default::default()
4073            }),
4074            ..Default::default()
4075        },
4076        cx,
4077    )
4078    .await;
4079
4080    cx.set_state(indoc! {"
4081        oneˇ
4082        two
4083        three
4084    "});
4085    cx.simulate_keystroke(".");
4086    handle_completion_request(
4087        &mut cx,
4088        indoc! {"
4089            one.|<>
4090            two
4091            three
4092        "},
4093        vec!["first_completion", "second_completion"],
4094    )
4095    .await;
4096    cx.condition(|editor, _| editor.context_menu_visible())
4097        .await;
4098    let apply_additional_edits = cx.update_editor(|editor, cx| {
4099        editor.move_down(&MoveDown, cx);
4100        editor
4101            .confirm_completion(&ConfirmCompletion::default(), cx)
4102            .unwrap()
4103    });
4104    cx.assert_editor_state(indoc! {"
4105        one.second_completionˇ
4106        two
4107        three
4108    "});
4109
4110    handle_resolve_completion_request(
4111        &mut cx,
4112        Some((
4113            indoc! {"
4114                one.second_completion
4115                two
4116                threeˇ
4117            "},
4118            "\nadditional edit",
4119        )),
4120    )
4121    .await;
4122    apply_additional_edits.await.unwrap();
4123    cx.assert_editor_state(indoc! {"
4124        one.second_completionˇ
4125        two
4126        three
4127        additional edit
4128    "});
4129
4130    cx.set_state(indoc! {"
4131        one.second_completion
4132        twoˇ
4133        threeˇ
4134        additional edit
4135    "});
4136    cx.simulate_keystroke(" ");
4137    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4138    cx.simulate_keystroke("s");
4139    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4140
4141    cx.assert_editor_state(indoc! {"
4142        one.second_completion
4143        two sˇ
4144        three sˇ
4145        additional edit
4146    "});
4147    handle_completion_request(
4148        &mut cx,
4149        indoc! {"
4150            one.second_completion
4151            two s
4152            three <s|>
4153            additional edit
4154        "},
4155        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
4156    )
4157    .await;
4158    cx.condition(|editor, _| editor.context_menu_visible())
4159        .await;
4160
4161    cx.simulate_keystroke("i");
4162
4163    handle_completion_request(
4164        &mut cx,
4165        indoc! {"
4166            one.second_completion
4167            two si
4168            three <si|>
4169            additional edit
4170        "},
4171        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
4172    )
4173    .await;
4174    cx.condition(|editor, _| editor.context_menu_visible())
4175        .await;
4176
4177    let apply_additional_edits = cx.update_editor(|editor, cx| {
4178        editor
4179            .confirm_completion(&ConfirmCompletion::default(), cx)
4180            .unwrap()
4181    });
4182    cx.assert_editor_state(indoc! {"
4183        one.second_completion
4184        two sixth_completionˇ
4185        three sixth_completionˇ
4186        additional edit
4187    "});
4188
4189    handle_resolve_completion_request(&mut cx, None).await;
4190    apply_additional_edits.await.unwrap();
4191
4192    cx.update(|cx| {
4193        cx.update_global::<Settings, _, _>(|settings, _| {
4194            settings.show_completions_on_input = false;
4195        })
4196    });
4197    cx.set_state("editorˇ");
4198    cx.simulate_keystroke(".");
4199    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4200    cx.simulate_keystroke("c");
4201    cx.simulate_keystroke("l");
4202    cx.simulate_keystroke("o");
4203    cx.assert_editor_state("editor.cloˇ");
4204    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4205    cx.update_editor(|editor, cx| {
4206        editor.show_completions(&ShowCompletions, cx);
4207    });
4208    handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
4209    cx.condition(|editor, _| editor.context_menu_visible())
4210        .await;
4211    let apply_additional_edits = cx.update_editor(|editor, cx| {
4212        editor
4213            .confirm_completion(&ConfirmCompletion::default(), cx)
4214            .unwrap()
4215    });
4216    cx.assert_editor_state("editor.closeˇ");
4217    handle_resolve_completion_request(&mut cx, None).await;
4218    apply_additional_edits.await.unwrap();
4219
4220    // Handle completion request passing a marked string specifying where the completion
4221    // should be triggered from using '|' character, what range should be replaced, and what completions
4222    // should be returned using '<' and '>' to delimit the range
4223    async fn handle_completion_request<'a>(
4224        cx: &mut EditorLspTestContext<'a>,
4225        marked_string: &str,
4226        completions: Vec<&'static str>,
4227    ) {
4228        let complete_from_marker: TextRangeMarker = '|'.into();
4229        let replace_range_marker: TextRangeMarker = ('<', '>').into();
4230        let (_, mut marked_ranges) = marked_text_ranges_by(
4231            marked_string,
4232            vec![complete_from_marker.clone(), replace_range_marker.clone()],
4233        );
4234
4235        let complete_from_position =
4236            cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
4237        let replace_range =
4238            cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
4239
4240        cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
4241            let completions = completions.clone();
4242            async move {
4243                assert_eq!(params.text_document_position.text_document.uri, url.clone());
4244                assert_eq!(
4245                    params.text_document_position.position,
4246                    complete_from_position
4247                );
4248                Ok(Some(lsp::CompletionResponse::Array(
4249                    completions
4250                        .iter()
4251                        .map(|completion_text| lsp::CompletionItem {
4252                            label: completion_text.to_string(),
4253                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
4254                                range: replace_range,
4255                                new_text: completion_text.to_string(),
4256                            })),
4257                            ..Default::default()
4258                        })
4259                        .collect(),
4260                )))
4261            }
4262        })
4263        .next()
4264        .await;
4265    }
4266
4267    async fn handle_resolve_completion_request<'a>(
4268        cx: &mut EditorLspTestContext<'a>,
4269        edit: Option<(&'static str, &'static str)>,
4270    ) {
4271        let edit = edit.map(|(marked_string, new_text)| {
4272            let (_, marked_ranges) = marked_text_ranges(marked_string, false);
4273            let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
4274            vec![lsp::TextEdit::new(replace_range, new_text.to_string())]
4275        });
4276
4277        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
4278            let edit = edit.clone();
4279            async move {
4280                Ok(lsp::CompletionItem {
4281                    additional_text_edits: edit,
4282                    ..Default::default()
4283                })
4284            }
4285        })
4286        .next()
4287        .await;
4288    }
4289}
4290
4291#[gpui::test]
4292async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
4293    cx.update(|cx| cx.set_global(Settings::test(cx)));
4294    let language = Arc::new(Language::new(
4295        LanguageConfig {
4296            line_comment: Some("// ".into()),
4297            ..Default::default()
4298        },
4299        Some(tree_sitter_rust::language()),
4300    ));
4301
4302    let text = "
4303        fn a() {
4304            //b();
4305            // c();
4306            //  d();
4307        }
4308    "
4309    .unindent();
4310
4311    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
4312    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4313    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
4314
4315    view.update(cx, |editor, cx| {
4316        // If multiple selections intersect a line, the line is only
4317        // toggled once.
4318        editor.change_selections(None, cx, |s| {
4319            s.select_display_ranges([
4320                DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
4321                DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
4322            ])
4323        });
4324        editor.toggle_comments(&ToggleComments, cx);
4325        assert_eq!(
4326            editor.text(cx),
4327            "
4328                fn a() {
4329                    b();
4330                    c();
4331                     d();
4332                }
4333            "
4334            .unindent()
4335        );
4336
4337        // The comment prefix is inserted at the same column for every line
4338        // in a selection.
4339        editor.change_selections(None, cx, |s| {
4340            s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)])
4341        });
4342        editor.toggle_comments(&ToggleComments, cx);
4343        assert_eq!(
4344            editor.text(cx),
4345            "
4346                fn a() {
4347                    // b();
4348                    // c();
4349                    //  d();
4350                }
4351            "
4352            .unindent()
4353        );
4354
4355        // If a selection ends at the beginning of a line, that line is not toggled.
4356        editor.change_selections(None, cx, |s| {
4357            s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)])
4358        });
4359        editor.toggle_comments(&ToggleComments, cx);
4360        assert_eq!(
4361            editor.text(cx),
4362            "
4363                fn a() {
4364                    // b();
4365                    c();
4366                    //  d();
4367                }
4368            "
4369            .unindent()
4370        );
4371    });
4372}
4373
4374#[gpui::test]
4375async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
4376    let mut cx = EditorTestContext::new(cx);
4377
4378    let html_language = Arc::new(
4379        Language::new(
4380            LanguageConfig {
4381                name: "HTML".into(),
4382                block_comment: Some(("<!-- ".into(), " -->".into())),
4383                ..Default::default()
4384            },
4385            Some(tree_sitter_html::language()),
4386        )
4387        .with_injection_query(
4388            r#"
4389            (script_element
4390                (raw_text) @content
4391                (#set! "language" "javascript"))
4392            "#,
4393        )
4394        .unwrap(),
4395    );
4396
4397    let javascript_language = Arc::new(Language::new(
4398        LanguageConfig {
4399            name: "JavaScript".into(),
4400            line_comment: Some("// ".into()),
4401            ..Default::default()
4402        },
4403        Some(tree_sitter_javascript::language()),
4404    ));
4405
4406    let registry = Arc::new(LanguageRegistry::test());
4407    registry.add(html_language.clone());
4408    registry.add(javascript_language.clone());
4409
4410    cx.update_buffer(|buffer, cx| {
4411        buffer.set_language_registry(registry);
4412        buffer.set_language(Some(html_language), cx);
4413    });
4414
4415    // Toggle comments for empty selections
4416    cx.set_state(
4417        &r#"
4418            <p>A</p>ˇ
4419            <p>B</p>ˇ
4420            <p>C</p>ˇ
4421        "#
4422        .unindent(),
4423    );
4424    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
4425    cx.assert_editor_state(
4426        &r#"
4427            <!-- <p>A</p>ˇ -->
4428            <!-- <p>B</p>ˇ -->
4429            <!-- <p>C</p>ˇ -->
4430        "#
4431        .unindent(),
4432    );
4433    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
4434    cx.assert_editor_state(
4435        &r#"
4436            <p>A</p>ˇ
4437            <p>B</p>ˇ
4438            <p>C</p>ˇ
4439        "#
4440        .unindent(),
4441    );
4442
4443    // Toggle comments for mixture of empty and non-empty selections, where
4444    // multiple selections occupy a given line.
4445    cx.set_state(
4446        &r#"
4447            <p>A«</p>
4448            <p>ˇ»B</p>ˇ
4449            <p>C«</p>
4450            <p>ˇ»D</p>ˇ
4451        "#
4452        .unindent(),
4453    );
4454
4455    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
4456    cx.assert_editor_state(
4457        &r#"
4458            <!-- <p>A«</p>
4459            <p>ˇ»B</p>ˇ -->
4460            <!-- <p>C«</p>
4461            <p>ˇ»D</p>ˇ -->
4462        "#
4463        .unindent(),
4464    );
4465    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
4466    cx.assert_editor_state(
4467        &r#"
4468            <p>A«</p>
4469            <p>ˇ»B</p>ˇ
4470            <p>C«</p>
4471            <p>ˇ»D</p>ˇ
4472        "#
4473        .unindent(),
4474    );
4475
4476    // Toggle comments when different languages are active for different
4477    // selections.
4478    cx.set_state(
4479        &r#"
4480            ˇ<script>
4481                ˇvar x = new Y();
4482            ˇ</script>
4483        "#
4484        .unindent(),
4485    );
4486    cx.foreground().run_until_parked();
4487    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx));
4488    cx.assert_editor_state(
4489        &r#"
4490            <!-- ˇ<script> -->
4491                // ˇvar x = new Y();
4492            <!-- ˇ</script> -->
4493        "#
4494        .unindent(),
4495    );
4496}
4497
4498#[gpui::test]
4499fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
4500    cx.set_global(Settings::test(cx));
4501    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
4502    let multibuffer = cx.add_model(|cx| {
4503        let mut multibuffer = MultiBuffer::new(0);
4504        multibuffer.push_excerpts(
4505            buffer.clone(),
4506            [
4507                ExcerptRange {
4508                    context: Point::new(0, 0)..Point::new(0, 4),
4509                    primary: None,
4510                },
4511                ExcerptRange {
4512                    context: Point::new(1, 0)..Point::new(1, 4),
4513                    primary: None,
4514                },
4515            ],
4516            cx,
4517        );
4518        multibuffer
4519    });
4520
4521    assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
4522
4523    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
4524    view.update(cx, |view, cx| {
4525        assert_eq!(view.text(cx), "aaaa\nbbbb");
4526        view.change_selections(None, cx, |s| {
4527            s.select_ranges([
4528                Point::new(0, 0)..Point::new(0, 0),
4529                Point::new(1, 0)..Point::new(1, 0),
4530            ])
4531        });
4532
4533        view.handle_input("X", cx);
4534        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
4535        assert_eq!(
4536            view.selections.ranges(cx),
4537            [
4538                Point::new(0, 1)..Point::new(0, 1),
4539                Point::new(1, 1)..Point::new(1, 1),
4540            ]
4541        )
4542    });
4543}
4544
4545#[gpui::test]
4546fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
4547    cx.set_global(Settings::test(cx));
4548    let markers = vec![('[', ']').into(), ('(', ')').into()];
4549    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
4550        indoc! {"
4551            [aaaa
4552            (bbbb]
4553            cccc)",
4554        },
4555        markers.clone(),
4556    );
4557    let excerpt_ranges = markers.into_iter().map(|marker| {
4558        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
4559        ExcerptRange {
4560            context,
4561            primary: None,
4562        }
4563    });
4564    let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
4565    let multibuffer = cx.add_model(|cx| {
4566        let mut multibuffer = MultiBuffer::new(0);
4567        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
4568        multibuffer
4569    });
4570
4571    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
4572    view.update(cx, |view, cx| {
4573        let (expected_text, selection_ranges) = marked_text_ranges(
4574            indoc! {"
4575                aaaa
4576                bˇbbb
4577                bˇbbˇb
4578                cccc"
4579            },
4580            true,
4581        );
4582        assert_eq!(view.text(cx), expected_text);
4583        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
4584
4585        view.handle_input("X", cx);
4586
4587        let (expected_text, expected_selections) = marked_text_ranges(
4588            indoc! {"
4589                aaaa
4590                bXˇbbXb
4591                bXˇbbXˇb
4592                cccc"
4593            },
4594            false,
4595        );
4596        assert_eq!(view.text(cx), expected_text);
4597        assert_eq!(view.selections.ranges(cx), expected_selections);
4598
4599        view.newline(&Newline, cx);
4600        let (expected_text, expected_selections) = marked_text_ranges(
4601            indoc! {"
4602                aaaa
4603                bX
4604                ˇbbX
4605                b
4606                bX
4607                ˇbbX
4608                ˇb
4609                cccc"
4610            },
4611            false,
4612        );
4613        assert_eq!(view.text(cx), expected_text);
4614        assert_eq!(view.selections.ranges(cx), expected_selections);
4615    });
4616}
4617
4618#[gpui::test]
4619fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
4620    cx.set_global(Settings::test(cx));
4621    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
4622    let mut excerpt1_id = None;
4623    let multibuffer = cx.add_model(|cx| {
4624        let mut multibuffer = MultiBuffer::new(0);
4625        excerpt1_id = multibuffer
4626            .push_excerpts(
4627                buffer.clone(),
4628                [
4629                    ExcerptRange {
4630                        context: Point::new(0, 0)..Point::new(1, 4),
4631                        primary: None,
4632                    },
4633                    ExcerptRange {
4634                        context: Point::new(1, 0)..Point::new(2, 4),
4635                        primary: None,
4636                    },
4637                ],
4638                cx,
4639            )
4640            .into_iter()
4641            .next();
4642        multibuffer
4643    });
4644    assert_eq!(
4645        multibuffer.read(cx).read(cx).text(),
4646        "aaaa\nbbbb\nbbbb\ncccc"
4647    );
4648    let (_, editor) = cx.add_window(Default::default(), |cx| {
4649        let mut editor = build_editor(multibuffer.clone(), cx);
4650        let snapshot = editor.snapshot(cx);
4651        editor.change_selections(None, cx, |s| {
4652            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
4653        });
4654        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
4655        assert_eq!(
4656            editor.selections.ranges(cx),
4657            [
4658                Point::new(1, 3)..Point::new(1, 3),
4659                Point::new(2, 1)..Point::new(2, 1),
4660            ]
4661        );
4662        editor
4663    });
4664
4665    // Refreshing selections is a no-op when excerpts haven't changed.
4666    editor.update(cx, |editor, cx| {
4667        editor.change_selections(None, cx, |s| {
4668            s.refresh();
4669        });
4670        assert_eq!(
4671            editor.selections.ranges(cx),
4672            [
4673                Point::new(1, 3)..Point::new(1, 3),
4674                Point::new(2, 1)..Point::new(2, 1),
4675            ]
4676        );
4677    });
4678
4679    multibuffer.update(cx, |multibuffer, cx| {
4680        multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
4681    });
4682    editor.update(cx, |editor, cx| {
4683        // Removing an excerpt causes the first selection to become degenerate.
4684        assert_eq!(
4685            editor.selections.ranges(cx),
4686            [
4687                Point::new(0, 0)..Point::new(0, 0),
4688                Point::new(0, 1)..Point::new(0, 1)
4689            ]
4690        );
4691
4692        // Refreshing selections will relocate the first selection to the original buffer
4693        // location.
4694        editor.change_selections(None, cx, |s| {
4695            s.refresh();
4696        });
4697        assert_eq!(
4698            editor.selections.ranges(cx),
4699            [
4700                Point::new(0, 1)..Point::new(0, 1),
4701                Point::new(0, 3)..Point::new(0, 3)
4702            ]
4703        );
4704        assert!(editor.selections.pending_anchor().is_some());
4705    });
4706}
4707
4708#[gpui::test]
4709fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) {
4710    cx.set_global(Settings::test(cx));
4711    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
4712    let mut excerpt1_id = None;
4713    let multibuffer = cx.add_model(|cx| {
4714        let mut multibuffer = MultiBuffer::new(0);
4715        excerpt1_id = multibuffer
4716            .push_excerpts(
4717                buffer.clone(),
4718                [
4719                    ExcerptRange {
4720                        context: Point::new(0, 0)..Point::new(1, 4),
4721                        primary: None,
4722                    },
4723                    ExcerptRange {
4724                        context: Point::new(1, 0)..Point::new(2, 4),
4725                        primary: None,
4726                    },
4727                ],
4728                cx,
4729            )
4730            .into_iter()
4731            .next();
4732        multibuffer
4733    });
4734    assert_eq!(
4735        multibuffer.read(cx).read(cx).text(),
4736        "aaaa\nbbbb\nbbbb\ncccc"
4737    );
4738    let (_, editor) = cx.add_window(Default::default(), |cx| {
4739        let mut editor = build_editor(multibuffer.clone(), cx);
4740        let snapshot = editor.snapshot(cx);
4741        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
4742        assert_eq!(
4743            editor.selections.ranges(cx),
4744            [Point::new(1, 3)..Point::new(1, 3)]
4745        );
4746        editor
4747    });
4748
4749    multibuffer.update(cx, |multibuffer, cx| {
4750        multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
4751    });
4752    editor.update(cx, |editor, cx| {
4753        assert_eq!(
4754            editor.selections.ranges(cx),
4755            [Point::new(0, 0)..Point::new(0, 0)]
4756        );
4757
4758        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
4759        editor.change_selections(None, cx, |s| {
4760            s.refresh();
4761        });
4762        assert_eq!(
4763            editor.selections.ranges(cx),
4764            [Point::new(0, 3)..Point::new(0, 3)]
4765        );
4766        assert!(editor.selections.pending_anchor().is_some());
4767    });
4768}
4769
4770#[gpui::test]
4771async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
4772    cx.update(|cx| cx.set_global(Settings::test(cx)));
4773    let language = Arc::new(
4774        Language::new(
4775            LanguageConfig {
4776                brackets: vec![
4777                    BracketPair {
4778                        start: "{".to_string(),
4779                        end: "}".to_string(),
4780                        close: true,
4781                        newline: true,
4782                    },
4783                    BracketPair {
4784                        start: "/* ".to_string(),
4785                        end: " */".to_string(),
4786                        close: true,
4787                        newline: true,
4788                    },
4789                ],
4790                ..Default::default()
4791            },
4792            Some(tree_sitter_rust::language()),
4793        )
4794        .with_indents_query("")
4795        .unwrap(),
4796    );
4797
4798    let text = concat!(
4799        "{   }\n",     //
4800        "  x\n",       //
4801        "  /*   */\n", //
4802        "x\n",         //
4803        "{{} }\n",     //
4804    );
4805
4806    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
4807    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4808    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
4809    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
4810        .await;
4811
4812    view.update(cx, |view, cx| {
4813        view.change_selections(None, cx, |s| {
4814            s.select_display_ranges([
4815                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
4816                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
4817                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
4818            ])
4819        });
4820        view.newline(&Newline, cx);
4821
4822        assert_eq!(
4823            view.buffer().read(cx).read(cx).text(),
4824            concat!(
4825                "{ \n",    // Suppress rustfmt
4826                "\n",      //
4827                "}\n",     //
4828                "  x\n",   //
4829                "  /* \n", //
4830                "  \n",    //
4831                "  */\n",  //
4832                "x\n",     //
4833                "{{} \n",  //
4834                "}\n",     //
4835            )
4836        );
4837    });
4838}
4839
4840#[gpui::test]
4841fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
4842    let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
4843
4844    cx.set_global(Settings::test(cx));
4845    let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
4846
4847    editor.update(cx, |editor, cx| {
4848        struct Type1;
4849        struct Type2;
4850
4851        let buffer = buffer.read(cx).snapshot(cx);
4852
4853        let anchor_range =
4854            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
4855
4856        editor.highlight_background::<Type1>(
4857            vec![
4858                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
4859                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
4860                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
4861                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
4862            ],
4863            |_| Color::red(),
4864            cx,
4865        );
4866        editor.highlight_background::<Type2>(
4867            vec![
4868                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
4869                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
4870                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
4871                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
4872            ],
4873            |_| Color::green(),
4874            cx,
4875        );
4876
4877        let snapshot = editor.snapshot(cx);
4878        let mut highlighted_ranges = editor.background_highlights_in_range(
4879            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
4880            &snapshot,
4881            cx.global::<Settings>().theme.as_ref(),
4882        );
4883        // Enforce a consistent ordering based on color without relying on the ordering of the
4884        // highlight's `TypeId` which is non-deterministic.
4885        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
4886        assert_eq!(
4887            highlighted_ranges,
4888            &[
4889                (
4890                    DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
4891                    Color::green(),
4892                ),
4893                (
4894                    DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
4895                    Color::green(),
4896                ),
4897                (
4898                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
4899                    Color::red(),
4900                ),
4901                (
4902                    DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
4903                    Color::red(),
4904                ),
4905            ]
4906        );
4907        assert_eq!(
4908            editor.background_highlights_in_range(
4909                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
4910                &snapshot,
4911                cx.global::<Settings>().theme.as_ref(),
4912            ),
4913            &[(
4914                DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
4915                Color::red(),
4916            )]
4917        );
4918    });
4919}
4920
4921#[gpui::test]
4922fn test_following(cx: &mut gpui::MutableAppContext) {
4923    let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
4924
4925    cx.set_global(Settings::test(cx));
4926
4927    let (_, leader) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
4928    let (_, follower) = cx.add_window(
4929        WindowOptions {
4930            bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
4931            ..Default::default()
4932        },
4933        |cx| build_editor(buffer.clone(), cx),
4934    );
4935
4936    let pending_update = Rc::new(RefCell::new(None));
4937    follower.update(cx, {
4938        let update = pending_update.clone();
4939        |_, cx| {
4940            cx.subscribe(&leader, move |_, leader, event, cx| {
4941                leader
4942                    .read(cx)
4943                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
4944            })
4945            .detach();
4946        }
4947    });
4948
4949    // Update the selections only
4950    leader.update(cx, |leader, cx| {
4951        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
4952    });
4953    follower.update(cx, |follower, cx| {
4954        follower
4955            .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
4956            .unwrap();
4957    });
4958    assert_eq!(follower.read(cx).selections.ranges(cx), vec![1..1]);
4959
4960    // Update the scroll position only
4961    leader.update(cx, |leader, cx| {
4962        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
4963    });
4964    follower.update(cx, |follower, cx| {
4965        follower
4966            .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
4967            .unwrap();
4968    });
4969    assert_eq!(
4970        follower.update(cx, |follower, cx| follower.scroll_position(cx)),
4971        vec2f(1.5, 3.5)
4972    );
4973
4974    // Update the selections and scroll position
4975    leader.update(cx, |leader, cx| {
4976        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
4977        leader.request_autoscroll(Autoscroll::Newest, cx);
4978        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
4979    });
4980    follower.update(cx, |follower, cx| {
4981        let initial_scroll_position = follower.scroll_position(cx);
4982        follower
4983            .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
4984            .unwrap();
4985        assert_eq!(follower.scroll_position(cx), initial_scroll_position);
4986        assert!(follower.autoscroll_request.is_some());
4987    });
4988    assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0]);
4989
4990    // Creating a pending selection that precedes another selection
4991    leader.update(cx, |leader, cx| {
4992        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
4993        leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
4994    });
4995    follower.update(cx, |follower, cx| {
4996        follower
4997            .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
4998            .unwrap();
4999    });
5000    assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0, 1..1]);
5001
5002    // Extend the pending selection so that it surrounds another selection
5003    leader.update(cx, |leader, cx| {
5004        leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
5005    });
5006    follower.update(cx, |follower, cx| {
5007        follower
5008            .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
5009            .unwrap();
5010    });
5011    assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..2]);
5012}
5013
5014#[test]
5015fn test_combine_syntax_and_fuzzy_match_highlights() {
5016    let string = "abcdefghijklmnop";
5017    let syntax_ranges = [
5018        (
5019            0..3,
5020            HighlightStyle {
5021                color: Some(Color::red()),
5022                ..Default::default()
5023            },
5024        ),
5025        (
5026            4..8,
5027            HighlightStyle {
5028                color: Some(Color::green()),
5029                ..Default::default()
5030            },
5031        ),
5032    ];
5033    let match_indices = [4, 6, 7, 8];
5034    assert_eq!(
5035        combine_syntax_and_fuzzy_match_highlights(
5036            string,
5037            Default::default(),
5038            syntax_ranges.into_iter(),
5039            &match_indices,
5040        ),
5041        &[
5042            (
5043                0..3,
5044                HighlightStyle {
5045                    color: Some(Color::red()),
5046                    ..Default::default()
5047                },
5048            ),
5049            (
5050                4..5,
5051                HighlightStyle {
5052                    color: Some(Color::green()),
5053                    weight: Some(fonts::Weight::BOLD),
5054                    ..Default::default()
5055                },
5056            ),
5057            (
5058                5..6,
5059                HighlightStyle {
5060                    color: Some(Color::green()),
5061                    ..Default::default()
5062                },
5063            ),
5064            (
5065                6..8,
5066                HighlightStyle {
5067                    color: Some(Color::green()),
5068                    weight: Some(fonts::Weight::BOLD),
5069                    ..Default::default()
5070                },
5071            ),
5072            (
5073                8..9,
5074                HighlightStyle {
5075                    weight: Some(fonts::Weight::BOLD),
5076                    ..Default::default()
5077                },
5078            ),
5079        ]
5080    );
5081}
5082
5083#[gpui::test]
5084async fn go_to_hunk(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
5085    let mut cx = EditorTestContext::new(cx);
5086
5087    let diff_base = r#"
5088        use some::mod;
5089
5090        const A: u32 = 42;
5091
5092        fn main() {
5093            println!("hello");
5094
5095            println!("world");
5096        }
5097        "#
5098    .unindent();
5099
5100    // Edits are modified, removed, modified, added
5101    cx.set_state(
5102        &r#"
5103        use some::modified;
5104
5105        ˇ
5106        fn main() {
5107            println!("hello there");
5108
5109            println!("around the");
5110            println!("world");
5111        }
5112        "#
5113        .unindent(),
5114    );
5115
5116    cx.set_diff_base(Some(&diff_base));
5117    deterministic.run_until_parked();
5118
5119    cx.update_editor(|editor, cx| {
5120        //Wrap around the bottom of the buffer
5121        for _ in 0..3 {
5122            editor.go_to_hunk(&GoToHunk, cx);
5123        }
5124    });
5125
5126    cx.assert_editor_state(
5127        &r#"
5128        ˇuse some::modified;
5129    
5130    
5131        fn main() {
5132            println!("hello there");
5133    
5134            println!("around the");
5135            println!("world");
5136        }
5137        "#
5138        .unindent(),
5139    );
5140
5141    cx.update_editor(|editor, cx| {
5142        //Wrap around the top of the buffer
5143        for _ in 0..2 {
5144            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
5145        }
5146    });
5147
5148    cx.assert_editor_state(
5149        &r#"
5150        use some::modified;
5151
5152
5153        fn main() {
5154        ˇ    println!("hello there");
5155
5156            println!("around the");
5157            println!("world");
5158        }
5159        "#
5160        .unindent(),
5161    );
5162
5163    cx.update_editor(|editor, cx| {
5164        editor.fold(&Fold, cx);
5165
5166        //Make sure that the fold only gets one hunk
5167        for _ in 0..4 {
5168            editor.go_to_hunk(&GoToHunk, cx);
5169        }
5170    });
5171
5172    cx.assert_editor_state(
5173        &r#"
5174        ˇuse some::modified;
5175
5176
5177        fn main() {
5178            println!("hello there");
5179
5180            println!("around the");
5181            println!("world");
5182        }
5183        "#
5184        .unindent(),
5185    );
5186}
5187
5188fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
5189    let point = DisplayPoint::new(row as u32, column as u32);
5190    point..point
5191}
5192
5193fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
5194    let (text, ranges) = marked_text_ranges(marked_text, true);
5195    assert_eq!(view.text(cx), text);
5196    assert_eq!(
5197        view.selections.ranges(cx),
5198        ranges,
5199        "Assert selections are {}",
5200        marked_text
5201    );
5202}