editor_tests.rs

   1use super::*;
   2use crate::{
   3    test::{
   4        assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
   5        editor_test_context::EditorTestContext, select_ranges,
   6    },
   7    JoinLines,
   8};
   9use drag_and_drop::DragAndDrop;
  10use futures::StreamExt;
  11use gpui::{
  12    executor::Deterministic,
  13    geometry::{rect::RectF, vector::vec2f},
  14    platform::{WindowBounds, WindowOptions},
  15    serde_json::{self, json},
  16    TestAppContext,
  17};
  18use indoc::indoc;
  19use language::{
  20    language_settings::{AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent},
  21    BracketPairConfig, FakeLspAdapter, LanguageConfig, LanguageRegistry, Point,
  22};
  23use parking_lot::Mutex;
  24use project::FakeFs;
  25use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
  26use unindent::Unindent;
  27use util::{
  28    assert_set_eq,
  29    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
  30};
  31use workspace::{
  32    item::{FollowableItem, Item, ItemHandle},
  33    NavigationEntry, ViewId,
  34};
  35
  36#[gpui::test]
  37fn test_edit_events(cx: &mut TestAppContext) {
  38    init_test(cx, |_| {});
  39
  40    let buffer = cx.add_model(|cx| {
  41        let mut buffer = language::Buffer::new(0, "123456", cx);
  42        buffer.set_group_interval(Duration::from_secs(1));
  43        buffer
  44    });
  45
  46    let events = Rc::new(RefCell::new(Vec::new()));
  47    let (_, editor1) = cx.add_window({
  48        let events = events.clone();
  49        |cx| {
  50            cx.subscribe(&cx.handle(), move |_, _, event, _| {
  51                if matches!(
  52                    event,
  53                    Event::Edited | Event::BufferEdited | Event::DirtyChanged
  54                ) {
  55                    events.borrow_mut().push(("editor1", event.clone()));
  56                }
  57            })
  58            .detach();
  59            Editor::for_buffer(buffer.clone(), None, cx)
  60        }
  61    });
  62    let (_, editor2) = cx.add_window({
  63        let events = events.clone();
  64        |cx| {
  65            cx.subscribe(&cx.handle(), move |_, _, event, _| {
  66                if matches!(
  67                    event,
  68                    Event::Edited | Event::BufferEdited | Event::DirtyChanged
  69                ) {
  70                    events.borrow_mut().push(("editor2", event.clone()));
  71                }
  72            })
  73            .detach();
  74            Editor::for_buffer(buffer.clone(), None, cx)
  75        }
  76    });
  77    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  78
  79    // Mutating editor 1 will emit an `Edited` event only for that editor.
  80    editor1.update(cx, |editor, cx| editor.insert("X", cx));
  81    assert_eq!(
  82        mem::take(&mut *events.borrow_mut()),
  83        [
  84            ("editor1", Event::Edited),
  85            ("editor1", Event::BufferEdited),
  86            ("editor2", Event::BufferEdited),
  87            ("editor1", Event::DirtyChanged),
  88            ("editor2", Event::DirtyChanged)
  89        ]
  90    );
  91
  92    // Mutating editor 2 will emit an `Edited` event only for that editor.
  93    editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
  94    assert_eq!(
  95        mem::take(&mut *events.borrow_mut()),
  96        [
  97            ("editor2", Event::Edited),
  98            ("editor1", Event::BufferEdited),
  99            ("editor2", Event::BufferEdited),
 100        ]
 101    );
 102
 103    // Undoing on editor 1 will emit an `Edited` event only for that editor.
 104    editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
 105    assert_eq!(
 106        mem::take(&mut *events.borrow_mut()),
 107        [
 108            ("editor1", Event::Edited),
 109            ("editor1", Event::BufferEdited),
 110            ("editor2", Event::BufferEdited),
 111            ("editor1", Event::DirtyChanged),
 112            ("editor2", Event::DirtyChanged),
 113        ]
 114    );
 115
 116    // Redoing on editor 1 will emit an `Edited` event only for that editor.
 117    editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
 118    assert_eq!(
 119        mem::take(&mut *events.borrow_mut()),
 120        [
 121            ("editor1", Event::Edited),
 122            ("editor1", Event::BufferEdited),
 123            ("editor2", Event::BufferEdited),
 124            ("editor1", Event::DirtyChanged),
 125            ("editor2", Event::DirtyChanged),
 126        ]
 127    );
 128
 129    // Undoing on editor 2 will emit an `Edited` event only for that editor.
 130    editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
 131    assert_eq!(
 132        mem::take(&mut *events.borrow_mut()),
 133        [
 134            ("editor2", Event::Edited),
 135            ("editor1", Event::BufferEdited),
 136            ("editor2", Event::BufferEdited),
 137            ("editor1", Event::DirtyChanged),
 138            ("editor2", Event::DirtyChanged),
 139        ]
 140    );
 141
 142    // Redoing on editor 2 will emit an `Edited` event only for that editor.
 143    editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
 144    assert_eq!(
 145        mem::take(&mut *events.borrow_mut()),
 146        [
 147            ("editor2", Event::Edited),
 148            ("editor1", Event::BufferEdited),
 149            ("editor2", Event::BufferEdited),
 150            ("editor1", Event::DirtyChanged),
 151            ("editor2", Event::DirtyChanged),
 152        ]
 153    );
 154
 155    // No event is emitted when the mutation is a no-op.
 156    editor2.update(cx, |editor, cx| {
 157        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 158
 159        editor.backspace(&Backspace, cx);
 160    });
 161    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
 162}
 163
 164#[gpui::test]
 165fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
 166    init_test(cx, |_| {});
 167
 168    let mut now = Instant::now();
 169    let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
 170    let group_interval = buffer.read_with(cx, |buffer, _| buffer.transaction_group_interval());
 171    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
 172    let (_, editor) = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 173
 174    editor.update(cx, |editor, cx| {
 175        editor.start_transaction_at(now, cx);
 176        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
 177
 178        editor.insert("cd", cx);
 179        editor.end_transaction_at(now, cx);
 180        assert_eq!(editor.text(cx), "12cd56");
 181        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
 182
 183        editor.start_transaction_at(now, cx);
 184        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
 185        editor.insert("e", cx);
 186        editor.end_transaction_at(now, cx);
 187        assert_eq!(editor.text(cx), "12cde6");
 188        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
 189
 190        now += group_interval + Duration::from_millis(1);
 191        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
 192
 193        // Simulate an edit in another editor
 194        buffer.update(cx, |buffer, cx| {
 195            buffer.start_transaction_at(now, cx);
 196            buffer.edit([(0..1, "a")], None, cx);
 197            buffer.edit([(1..1, "b")], None, cx);
 198            buffer.end_transaction_at(now, cx);
 199        });
 200
 201        assert_eq!(editor.text(cx), "ab2cde6");
 202        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
 203
 204        // Last transaction happened past the group interval in a different editor.
 205        // Undo it individually and don't restore selections.
 206        editor.undo(&Undo, cx);
 207        assert_eq!(editor.text(cx), "12cde6");
 208        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
 209
 210        // First two transactions happened within the group interval in this editor.
 211        // Undo them together and restore selections.
 212        editor.undo(&Undo, cx);
 213        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
 214        assert_eq!(editor.text(cx), "123456");
 215        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
 216
 217        // Redo the first two transactions together.
 218        editor.redo(&Redo, cx);
 219        assert_eq!(editor.text(cx), "12cde6");
 220        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
 221
 222        // Redo the last transaction on its own.
 223        editor.redo(&Redo, cx);
 224        assert_eq!(editor.text(cx), "ab2cde6");
 225        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
 226
 227        // Test empty transactions.
 228        editor.start_transaction_at(now, cx);
 229        editor.end_transaction_at(now, cx);
 230        editor.undo(&Undo, cx);
 231        assert_eq!(editor.text(cx), "12cde6");
 232    });
 233}
 234
 235#[gpui::test]
 236fn test_ime_composition(cx: &mut TestAppContext) {
 237    init_test(cx, |_| {});
 238
 239    let buffer = cx.add_model(|cx| {
 240        let mut buffer = language::Buffer::new(0, "abcde", cx);
 241        // Ensure automatic grouping doesn't occur.
 242        buffer.set_group_interval(Duration::ZERO);
 243        buffer
 244    });
 245
 246    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
 247    cx.add_window(|cx| {
 248        let mut editor = build_editor(buffer.clone(), cx);
 249
 250        // Start a new IME composition.
 251        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
 252        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
 253        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
 254        assert_eq!(editor.text(cx), "äbcde");
 255        assert_eq!(
 256            editor.marked_text_ranges(cx),
 257            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
 258        );
 259
 260        // Finalize IME composition.
 261        editor.replace_text_in_range(None, "ā", cx);
 262        assert_eq!(editor.text(cx), "ābcde");
 263        assert_eq!(editor.marked_text_ranges(cx), None);
 264
 265        // IME composition edits are grouped and are undone/redone at once.
 266        editor.undo(&Default::default(), cx);
 267        assert_eq!(editor.text(cx), "abcde");
 268        assert_eq!(editor.marked_text_ranges(cx), None);
 269        editor.redo(&Default::default(), cx);
 270        assert_eq!(editor.text(cx), "ābcde");
 271        assert_eq!(editor.marked_text_ranges(cx), None);
 272
 273        // Start a new IME composition.
 274        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
 275        assert_eq!(
 276            editor.marked_text_ranges(cx),
 277            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
 278        );
 279
 280        // Undoing during an IME composition cancels it.
 281        editor.undo(&Default::default(), cx);
 282        assert_eq!(editor.text(cx), "ābcde");
 283        assert_eq!(editor.marked_text_ranges(cx), None);
 284
 285        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
 286        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
 287        assert_eq!(editor.text(cx), "ābcdè");
 288        assert_eq!(
 289            editor.marked_text_ranges(cx),
 290            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
 291        );
 292
 293        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
 294        editor.replace_text_in_range(Some(4..999), "ę", cx);
 295        assert_eq!(editor.text(cx), "ābcdę");
 296        assert_eq!(editor.marked_text_ranges(cx), None);
 297
 298        // Start a new IME composition with multiple cursors.
 299        editor.change_selections(None, cx, |s| {
 300            s.select_ranges([
 301                OffsetUtf16(1)..OffsetUtf16(1),
 302                OffsetUtf16(3)..OffsetUtf16(3),
 303                OffsetUtf16(5)..OffsetUtf16(5),
 304            ])
 305        });
 306        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
 307        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
 308        assert_eq!(
 309            editor.marked_text_ranges(cx),
 310            Some(vec![
 311                OffsetUtf16(0)..OffsetUtf16(3),
 312                OffsetUtf16(4)..OffsetUtf16(7),
 313                OffsetUtf16(8)..OffsetUtf16(11)
 314            ])
 315        );
 316
 317        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
 318        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
 319        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
 320        assert_eq!(
 321            editor.marked_text_ranges(cx),
 322            Some(vec![
 323                OffsetUtf16(1)..OffsetUtf16(2),
 324                OffsetUtf16(5)..OffsetUtf16(6),
 325                OffsetUtf16(9)..OffsetUtf16(10)
 326            ])
 327        );
 328
 329        // Finalize IME composition with multiple cursors.
 330        editor.replace_text_in_range(Some(9..10), "2", cx);
 331        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
 332        assert_eq!(editor.marked_text_ranges(cx), None);
 333
 334        editor
 335    });
 336}
 337
 338#[gpui::test]
 339fn test_selection_with_mouse(cx: &mut TestAppContext) {
 340    init_test(cx, |_| {});
 341
 342    let (_, editor) = cx.add_window(|cx| {
 343        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
 344        build_editor(buffer, cx)
 345    });
 346    editor.update(cx, |view, cx| {
 347        view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
 348    });
 349    assert_eq!(
 350        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 351        [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
 352    );
 353
 354    editor.update(cx, |view, cx| {
 355        view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
 356    });
 357
 358    assert_eq!(
 359        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 360        [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
 361    );
 362
 363    editor.update(cx, |view, cx| {
 364        view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
 365    });
 366
 367    assert_eq!(
 368        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 369        [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
 370    );
 371
 372    editor.update(cx, |view, cx| {
 373        view.end_selection(cx);
 374        view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
 375    });
 376
 377    assert_eq!(
 378        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 379        [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
 380    );
 381
 382    editor.update(cx, |view, cx| {
 383        view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
 384        view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx);
 385    });
 386
 387    assert_eq!(
 388        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 389        [
 390            DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
 391            DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
 392        ]
 393    );
 394
 395    editor.update(cx, |view, cx| {
 396        view.end_selection(cx);
 397    });
 398
 399    assert_eq!(
 400        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
 401        [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
 402    );
 403}
 404
 405#[gpui::test]
 406fn test_canceling_pending_selection(cx: &mut TestAppContext) {
 407    init_test(cx, |_| {});
 408
 409    let (_, view) = cx.add_window(|cx| {
 410        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
 411        build_editor(buffer, cx)
 412    });
 413
 414    view.update(cx, |view, cx| {
 415        view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
 416        assert_eq!(
 417            view.selections.display_ranges(cx),
 418            [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
 419        );
 420    });
 421
 422    view.update(cx, |view, cx| {
 423        view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
 424        assert_eq!(
 425            view.selections.display_ranges(cx),
 426            [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
 427        );
 428    });
 429
 430    view.update(cx, |view, cx| {
 431        view.cancel(&Cancel, cx);
 432        view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
 433        assert_eq!(
 434            view.selections.display_ranges(cx),
 435            [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
 436        );
 437    });
 438}
 439
 440#[gpui::test]
 441fn test_clone(cx: &mut TestAppContext) {
 442    init_test(cx, |_| {});
 443
 444    let (text, selection_ranges) = marked_text_ranges(
 445        indoc! {"
 446            one
 447            two
 448            threeˇ
 449            four
 450            fiveˇ
 451        "},
 452        true,
 453    );
 454
 455    let (_, editor) = cx.add_window(|cx| {
 456        let buffer = MultiBuffer::build_simple(&text, cx);
 457        build_editor(buffer, cx)
 458    });
 459
 460    editor.update(cx, |editor, cx| {
 461        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
 462        editor.fold_ranges(
 463            [
 464                Point::new(1, 0)..Point::new(2, 0),
 465                Point::new(3, 0)..Point::new(4, 0),
 466            ],
 467            true,
 468            cx,
 469        );
 470    });
 471
 472    let (_, cloned_editor) = editor.update(cx, |editor, cx| {
 473        cx.add_window(Default::default(), |cx| editor.clone(cx))
 474    });
 475
 476    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx));
 477    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx));
 478
 479    assert_eq!(
 480        cloned_editor.update(cx, |e, cx| e.display_text(cx)),
 481        editor.update(cx, |e, cx| e.display_text(cx))
 482    );
 483    assert_eq!(
 484        cloned_snapshot
 485            .folds_in_range(0..text.len())
 486            .collect::<Vec<_>>(),
 487        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
 488    );
 489    assert_set_eq!(
 490        cloned_editor.read_with(cx, |editor, cx| editor.selections.ranges::<Point>(cx)),
 491        editor.read_with(cx, |editor, cx| editor.selections.ranges(cx))
 492    );
 493    assert_set_eq!(
 494        cloned_editor.update(cx, |e, cx| e.selections.display_ranges(cx)),
 495        editor.update(cx, |e, cx| e.selections.display_ranges(cx))
 496    );
 497}
 498
 499#[gpui::test]
 500async fn test_navigation_history(cx: &mut TestAppContext) {
 501    init_test(cx, |_| {});
 502
 503    cx.set_global(DragAndDrop::<Workspace>::default());
 504    use workspace::item::Item;
 505
 506    let fs = FakeFs::new(cx.background());
 507    let project = Project::test(fs, [], cx).await;
 508    let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
 509    let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
 510    cx.add_view(window_id, |cx| {
 511        let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
 512        let mut editor = build_editor(buffer.clone(), cx);
 513        let handle = cx.handle();
 514        editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
 515
 516        fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
 517            editor.nav_history.as_mut().unwrap().pop_backward(cx)
 518        }
 519
 520        // Move the cursor a small distance.
 521        // Nothing is added to the navigation history.
 522        editor.change_selections(None, cx, |s| {
 523            s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
 524        });
 525        editor.change_selections(None, cx, |s| {
 526            s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
 527        });
 528        assert!(pop_history(&mut editor, cx).is_none());
 529
 530        // Move the cursor a large distance.
 531        // The history can jump back to the previous position.
 532        editor.change_selections(None, cx, |s| {
 533            s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
 534        });
 535        let nav_entry = pop_history(&mut editor, cx).unwrap();
 536        editor.navigate(nav_entry.data.unwrap(), cx);
 537        assert_eq!(nav_entry.item.id(), cx.view_id());
 538        assert_eq!(
 539            editor.selections.display_ranges(cx),
 540            &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
 541        );
 542        assert!(pop_history(&mut editor, cx).is_none());
 543
 544        // Move the cursor a small distance via the mouse.
 545        // Nothing is added to the navigation history.
 546        editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
 547        editor.end_selection(cx);
 548        assert_eq!(
 549            editor.selections.display_ranges(cx),
 550            &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
 551        );
 552        assert!(pop_history(&mut editor, cx).is_none());
 553
 554        // Move the cursor a large distance via the mouse.
 555        // The history can jump back to the previous position.
 556        editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
 557        editor.end_selection(cx);
 558        assert_eq!(
 559            editor.selections.display_ranges(cx),
 560            &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
 561        );
 562        let nav_entry = pop_history(&mut editor, cx).unwrap();
 563        editor.navigate(nav_entry.data.unwrap(), cx);
 564        assert_eq!(nav_entry.item.id(), cx.view_id());
 565        assert_eq!(
 566            editor.selections.display_ranges(cx),
 567            &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
 568        );
 569        assert!(pop_history(&mut editor, cx).is_none());
 570
 571        // Set scroll position to check later
 572        editor.set_scroll_position(Vector2F::new(5.5, 5.5), cx);
 573        let original_scroll_position = editor.scroll_manager.anchor();
 574
 575        // Jump to the end of the document and adjust scroll
 576        editor.move_to_end(&MoveToEnd, cx);
 577        editor.set_scroll_position(Vector2F::new(-2.5, -0.5), cx);
 578        assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
 579
 580        let nav_entry = pop_history(&mut editor, cx).unwrap();
 581        editor.navigate(nav_entry.data.unwrap(), cx);
 582        assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
 583
 584        // Ensure we don't panic when navigation data contains invalid anchors *and* points.
 585        let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
 586        invalid_anchor.text_anchor.buffer_id = Some(999);
 587        let invalid_point = Point::new(9999, 0);
 588        editor.navigate(
 589            Box::new(NavigationData {
 590                cursor_anchor: invalid_anchor,
 591                cursor_position: invalid_point,
 592                scroll_anchor: ScrollAnchor {
 593                    anchor: invalid_anchor,
 594                    offset: Default::default(),
 595                },
 596                scroll_top_row: invalid_point.row,
 597            }),
 598            cx,
 599        );
 600        assert_eq!(
 601            editor.selections.display_ranges(cx),
 602            &[editor.max_point(cx)..editor.max_point(cx)]
 603        );
 604        assert_eq!(
 605            editor.scroll_position(cx),
 606            vec2f(0., editor.max_point(cx).row() as f32)
 607        );
 608
 609        editor
 610    });
 611}
 612
 613#[gpui::test]
 614fn test_cancel(cx: &mut TestAppContext) {
 615    init_test(cx, |_| {});
 616
 617    let (_, view) = cx.add_window(|cx| {
 618        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
 619        build_editor(buffer, cx)
 620    });
 621
 622    view.update(cx, |view, cx| {
 623        view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
 624        view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
 625        view.end_selection(cx);
 626
 627        view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
 628        view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx);
 629        view.end_selection(cx);
 630        assert_eq!(
 631            view.selections.display_ranges(cx),
 632            [
 633                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
 634                DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
 635            ]
 636        );
 637    });
 638
 639    view.update(cx, |view, cx| {
 640        view.cancel(&Cancel, cx);
 641        assert_eq!(
 642            view.selections.display_ranges(cx),
 643            [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
 644        );
 645    });
 646
 647    view.update(cx, |view, cx| {
 648        view.cancel(&Cancel, cx);
 649        assert_eq!(
 650            view.selections.display_ranges(cx),
 651            [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
 652        );
 653    });
 654}
 655
 656#[gpui::test]
 657fn test_fold_action(cx: &mut TestAppContext) {
 658    init_test(cx, |_| {});
 659
 660    let (_, view) = cx.add_window(|cx| {
 661        let buffer = MultiBuffer::build_simple(
 662            &"
 663                impl Foo {
 664                    // Hello!
 665
 666                    fn a() {
 667                        1
 668                    }
 669
 670                    fn b() {
 671                        2
 672                    }
 673
 674                    fn c() {
 675                        3
 676                    }
 677                }
 678            "
 679            .unindent(),
 680            cx,
 681        );
 682        build_editor(buffer.clone(), cx)
 683    });
 684
 685    view.update(cx, |view, cx| {
 686        view.change_selections(None, cx, |s| {
 687            s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]);
 688        });
 689        view.fold(&Fold, cx);
 690        assert_eq!(
 691            view.display_text(cx),
 692            "
 693                impl Foo {
 694                    // Hello!
 695
 696                    fn a() {
 697                        1
 698                    }
 699
 700                    fn b() {⋯
 701                    }
 702
 703                    fn c() {⋯
 704                    }
 705                }
 706            "
 707            .unindent(),
 708        );
 709
 710        view.fold(&Fold, cx);
 711        assert_eq!(
 712            view.display_text(cx),
 713            "
 714                impl Foo {⋯
 715                }
 716            "
 717            .unindent(),
 718        );
 719
 720        view.unfold_lines(&UnfoldLines, cx);
 721        assert_eq!(
 722            view.display_text(cx),
 723            "
 724                impl Foo {
 725                    // Hello!
 726
 727                    fn a() {
 728                        1
 729                    }
 730
 731                    fn b() {⋯
 732                    }
 733
 734                    fn c() {⋯
 735                    }
 736                }
 737            "
 738            .unindent(),
 739        );
 740
 741        view.unfold_lines(&UnfoldLines, cx);
 742        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 743    });
 744}
 745
 746#[gpui::test]
 747fn test_move_cursor(cx: &mut TestAppContext) {
 748    init_test(cx, |_| {});
 749
 750    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 751    let (_, view) = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 752
 753    buffer.update(cx, |buffer, cx| {
 754        buffer.edit(
 755            vec![
 756                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 757                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 758            ],
 759            None,
 760            cx,
 761        );
 762    });
 763    view.update(cx, |view, cx| {
 764        assert_eq!(
 765            view.selections.display_ranges(cx),
 766            &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
 767        );
 768
 769        view.move_down(&MoveDown, cx);
 770        assert_eq!(
 771            view.selections.display_ranges(cx),
 772            &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
 773        );
 774
 775        view.move_right(&MoveRight, cx);
 776        assert_eq!(
 777            view.selections.display_ranges(cx),
 778            &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
 779        );
 780
 781        view.move_left(&MoveLeft, cx);
 782        assert_eq!(
 783            view.selections.display_ranges(cx),
 784            &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
 785        );
 786
 787        view.move_up(&MoveUp, cx);
 788        assert_eq!(
 789            view.selections.display_ranges(cx),
 790            &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
 791        );
 792
 793        view.move_to_end(&MoveToEnd, cx);
 794        assert_eq!(
 795            view.selections.display_ranges(cx),
 796            &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
 797        );
 798
 799        view.move_to_beginning(&MoveToBeginning, cx);
 800        assert_eq!(
 801            view.selections.display_ranges(cx),
 802            &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
 803        );
 804
 805        view.change_selections(None, cx, |s| {
 806            s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]);
 807        });
 808        view.select_to_beginning(&SelectToBeginning, cx);
 809        assert_eq!(
 810            view.selections.display_ranges(cx),
 811            &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
 812        );
 813
 814        view.select_to_end(&SelectToEnd, cx);
 815        assert_eq!(
 816            view.selections.display_ranges(cx),
 817            &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
 818        );
 819    });
 820}
 821
 822#[gpui::test]
 823fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 824    init_test(cx, |_| {});
 825
 826    let (_, view) = cx.add_window(|cx| {
 827        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
 828        build_editor(buffer.clone(), cx)
 829    });
 830
 831    assert_eq!('ⓐ'.len_utf8(), 3);
 832    assert_eq!('α'.len_utf8(), 2);
 833
 834    view.update(cx, |view, cx| {
 835        view.fold_ranges(
 836            vec![
 837                Point::new(0, 6)..Point::new(0, 12),
 838                Point::new(1, 2)..Point::new(1, 4),
 839                Point::new(2, 4)..Point::new(2, 8),
 840            ],
 841            true,
 842            cx,
 843        );
 844        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε\n");
 845
 846        view.move_right(&MoveRight, cx);
 847        assert_eq!(
 848            view.selections.display_ranges(cx),
 849            &[empty_range(0, "".len())]
 850        );
 851        view.move_right(&MoveRight, cx);
 852        assert_eq!(
 853            view.selections.display_ranges(cx),
 854            &[empty_range(0, "ⓐⓑ".len())]
 855        );
 856        view.move_right(&MoveRight, cx);
 857        assert_eq!(
 858            view.selections.display_ranges(cx),
 859            &[empty_range(0, "ⓐⓑ⋯".len())]
 860        );
 861
 862        view.move_down(&MoveDown, cx);
 863        assert_eq!(
 864            view.selections.display_ranges(cx),
 865            &[empty_range(1, "ab⋯".len())]
 866        );
 867        view.move_left(&MoveLeft, cx);
 868        assert_eq!(
 869            view.selections.display_ranges(cx),
 870            &[empty_range(1, "ab".len())]
 871        );
 872        view.move_left(&MoveLeft, cx);
 873        assert_eq!(
 874            view.selections.display_ranges(cx),
 875            &[empty_range(1, "a".len())]
 876        );
 877
 878        view.move_down(&MoveDown, cx);
 879        assert_eq!(
 880            view.selections.display_ranges(cx),
 881            &[empty_range(2, "α".len())]
 882        );
 883        view.move_right(&MoveRight, cx);
 884        assert_eq!(
 885            view.selections.display_ranges(cx),
 886            &[empty_range(2, "αβ".len())]
 887        );
 888        view.move_right(&MoveRight, cx);
 889        assert_eq!(
 890            view.selections.display_ranges(cx),
 891            &[empty_range(2, "αβ⋯".len())]
 892        );
 893        view.move_right(&MoveRight, cx);
 894        assert_eq!(
 895            view.selections.display_ranges(cx),
 896            &[empty_range(2, "αβ⋯ε".len())]
 897        );
 898
 899        view.move_up(&MoveUp, cx);
 900        assert_eq!(
 901            view.selections.display_ranges(cx),
 902            &[empty_range(1, "ab⋯e".len())]
 903        );
 904        view.move_up(&MoveUp, cx);
 905        assert_eq!(
 906            view.selections.display_ranges(cx),
 907            &[empty_range(0, "ⓐⓑ⋯ⓔ".len())]
 908        );
 909        view.move_left(&MoveLeft, cx);
 910        assert_eq!(
 911            view.selections.display_ranges(cx),
 912            &[empty_range(0, "ⓐⓑ⋯".len())]
 913        );
 914        view.move_left(&MoveLeft, cx);
 915        assert_eq!(
 916            view.selections.display_ranges(cx),
 917            &[empty_range(0, "ⓐⓑ".len())]
 918        );
 919        view.move_left(&MoveLeft, cx);
 920        assert_eq!(
 921            view.selections.display_ranges(cx),
 922            &[empty_range(0, "".len())]
 923        );
 924    });
 925}
 926
 927#[gpui::test]
 928fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 929    init_test(cx, |_| {});
 930
 931    let (_, view) = cx.add_window(|cx| {
 932        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 933        build_editor(buffer.clone(), cx)
 934    });
 935    view.update(cx, |view, cx| {
 936        view.change_selections(None, cx, |s| {
 937            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 938        });
 939        view.move_down(&MoveDown, cx);
 940        assert_eq!(
 941            view.selections.display_ranges(cx),
 942            &[empty_range(1, "abcd".len())]
 943        );
 944
 945        view.move_down(&MoveDown, cx);
 946        assert_eq!(
 947            view.selections.display_ranges(cx),
 948            &[empty_range(2, "αβγ".len())]
 949        );
 950
 951        view.move_down(&MoveDown, cx);
 952        assert_eq!(
 953            view.selections.display_ranges(cx),
 954            &[empty_range(3, "abcd".len())]
 955        );
 956
 957        view.move_down(&MoveDown, cx);
 958        assert_eq!(
 959            view.selections.display_ranges(cx),
 960            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 961        );
 962
 963        view.move_up(&MoveUp, cx);
 964        assert_eq!(
 965            view.selections.display_ranges(cx),
 966            &[empty_range(3, "abcd".len())]
 967        );
 968
 969        view.move_up(&MoveUp, cx);
 970        assert_eq!(
 971            view.selections.display_ranges(cx),
 972            &[empty_range(2, "αβγ".len())]
 973        );
 974    });
 975}
 976
 977#[gpui::test]
 978fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 979    init_test(cx, |_| {});
 980
 981    let (_, view) = cx.add_window(|cx| {
 982        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 983        build_editor(buffer, cx)
 984    });
 985    view.update(cx, |view, cx| {
 986        view.change_selections(None, cx, |s| {
 987            s.select_display_ranges([
 988                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
 989                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
 990            ]);
 991        });
 992    });
 993
 994    view.update(cx, |view, cx| {
 995        view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
 996        assert_eq!(
 997            view.selections.display_ranges(cx),
 998            &[
 999                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
1000                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
1001            ]
1002        );
1003    });
1004
1005    view.update(cx, |view, cx| {
1006        view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
1007        assert_eq!(
1008            view.selections.display_ranges(cx),
1009            &[
1010                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
1011                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
1012            ]
1013        );
1014    });
1015
1016    view.update(cx, |view, cx| {
1017        view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
1018        assert_eq!(
1019            view.selections.display_ranges(cx),
1020            &[
1021                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
1022                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
1023            ]
1024        );
1025    });
1026
1027    view.update(cx, |view, cx| {
1028        view.move_to_end_of_line(&MoveToEndOfLine, cx);
1029        assert_eq!(
1030            view.selections.display_ranges(cx),
1031            &[
1032                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
1033                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
1034            ]
1035        );
1036    });
1037
1038    // Moving to the end of line again is a no-op.
1039    view.update(cx, |view, cx| {
1040        view.move_to_end_of_line(&MoveToEndOfLine, cx);
1041        assert_eq!(
1042            view.selections.display_ranges(cx),
1043            &[
1044                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
1045                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
1046            ]
1047        );
1048    });
1049
1050    view.update(cx, |view, cx| {
1051        view.move_left(&MoveLeft, cx);
1052        view.select_to_beginning_of_line(
1053            &SelectToBeginningOfLine {
1054                stop_at_soft_wraps: true,
1055            },
1056            cx,
1057        );
1058        assert_eq!(
1059            view.selections.display_ranges(cx),
1060            &[
1061                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
1062                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
1063            ]
1064        );
1065    });
1066
1067    view.update(cx, |view, cx| {
1068        view.select_to_beginning_of_line(
1069            &SelectToBeginningOfLine {
1070                stop_at_soft_wraps: true,
1071            },
1072            cx,
1073        );
1074        assert_eq!(
1075            view.selections.display_ranges(cx),
1076            &[
1077                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
1078                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
1079            ]
1080        );
1081    });
1082
1083    view.update(cx, |view, cx| {
1084        view.select_to_beginning_of_line(
1085            &SelectToBeginningOfLine {
1086                stop_at_soft_wraps: true,
1087            },
1088            cx,
1089        );
1090        assert_eq!(
1091            view.selections.display_ranges(cx),
1092            &[
1093                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
1094                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
1095            ]
1096        );
1097    });
1098
1099    view.update(cx, |view, cx| {
1100        view.select_to_end_of_line(
1101            &SelectToEndOfLine {
1102                stop_at_soft_wraps: true,
1103            },
1104            cx,
1105        );
1106        assert_eq!(
1107            view.selections.display_ranges(cx),
1108            &[
1109                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
1110                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
1111            ]
1112        );
1113    });
1114
1115    view.update(cx, |view, cx| {
1116        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
1117        assert_eq!(view.display_text(cx), "ab\n  de");
1118        assert_eq!(
1119            view.selections.display_ranges(cx),
1120            &[
1121                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
1122                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
1123            ]
1124        );
1125    });
1126
1127    view.update(cx, |view, cx| {
1128        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
1129        assert_eq!(view.display_text(cx), "\n");
1130        assert_eq!(
1131            view.selections.display_ranges(cx),
1132            &[
1133                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
1134                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
1135            ]
1136        );
1137    });
1138}
1139
1140#[gpui::test]
1141fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
1142    init_test(cx, |_| {});
1143
1144    let (_, view) = cx.add_window(|cx| {
1145        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
1146        build_editor(buffer, cx)
1147    });
1148    view.update(cx, |view, cx| {
1149        view.change_selections(None, cx, |s| {
1150            s.select_display_ranges([
1151                DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
1152                DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
1153            ])
1154        });
1155
1156        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1157        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
1158
1159        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1160        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
1161
1162        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1163        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
1164
1165        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1166        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
1167
1168        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1169        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
1170
1171        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1172        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
1173
1174        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1175        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
1176
1177        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1178        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
1179
1180        view.move_right(&MoveRight, cx);
1181        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
1182        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
1183
1184        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
1185        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
1186
1187        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
1188        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
1189    });
1190}
1191
1192#[gpui::test]
1193fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
1194    init_test(cx, |_| {});
1195
1196    let (_, view) = cx.add_window(|cx| {
1197        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
1198        build_editor(buffer, cx)
1199    });
1200
1201    view.update(cx, |view, cx| {
1202        view.set_wrap_width(Some(140.), cx);
1203        assert_eq!(
1204            view.display_text(cx),
1205            "use one::{\n    two::three::\n    four::five\n};"
1206        );
1207
1208        view.change_selections(None, cx, |s| {
1209            s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]);
1210        });
1211
1212        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1213        assert_eq!(
1214            view.selections.display_ranges(cx),
1215            &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
1216        );
1217
1218        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1219        assert_eq!(
1220            view.selections.display_ranges(cx),
1221            &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
1222        );
1223
1224        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1225        assert_eq!(
1226            view.selections.display_ranges(cx),
1227            &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
1228        );
1229
1230        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1231        assert_eq!(
1232            view.selections.display_ranges(cx),
1233            &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
1234        );
1235
1236        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1237        assert_eq!(
1238            view.selections.display_ranges(cx),
1239            &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
1240        );
1241
1242        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1243        assert_eq!(
1244            view.selections.display_ranges(cx),
1245            &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
1246        );
1247    });
1248}
1249
1250#[gpui::test]
1251async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
1252    init_test(cx, |_| {});
1253    let mut cx = EditorTestContext::new(cx).await;
1254
1255    let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
1256    cx.simulate_window_resize(cx.window_id, vec2f(100., 4. * line_height));
1257
1258    cx.set_state(
1259        &r#"ˇone
1260        two
1261
1262        three
1263        fourˇ
1264        five
1265
1266        six"#
1267            .unindent(),
1268    );
1269
1270    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
1271    cx.assert_editor_state(
1272        &r#"one
1273        two
1274        ˇ
1275        three
1276        four
1277        five
1278        ˇ
1279        six"#
1280            .unindent(),
1281    );
1282
1283    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
1284    cx.assert_editor_state(
1285        &r#"one
1286        two
1287
1288        three
1289        four
1290        five
1291        ˇ
1292        sixˇ"#
1293            .unindent(),
1294    );
1295
1296    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
1297    cx.assert_editor_state(
1298        &r#"ˇone
1299        two
1300
1301        three
1302        four
1303        five
1304
1305        sixˇ"#
1306            .unindent(),
1307    );
1308
1309    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
1310    cx.assert_editor_state(
1311        &r#"ˇone
1312        two
1313        ˇ
1314        three
1315        four
1316        five
1317
1318        six"#
1319            .unindent(),
1320    );
1321
1322    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
1323    cx.assert_editor_state(
1324        &r#"ˇone
1325        two
1326
1327        three
1328        four
1329        five
1330
1331        sixˇ"#
1332            .unindent(),
1333    );
1334
1335    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
1336    cx.assert_editor_state(
1337        &r#"one
1338        two
1339
1340        three
1341        four
1342        five
1343        ˇ
1344        sixˇ"#
1345            .unindent(),
1346    );
1347
1348    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
1349    cx.assert_editor_state(
1350        &r#"one
1351        two
1352        ˇ
1353        three
1354        four
1355        five
1356        ˇ
1357        six"#
1358            .unindent(),
1359    );
1360}
1361
1362#[gpui::test]
1363async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
1364    init_test(cx, |_| {});
1365    let mut cx = EditorTestContext::new(cx).await;
1366
1367    let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
1368    cx.simulate_window_resize(cx.window_id, vec2f(100., 4. * line_height));
1369
1370    cx.set_state(
1371        &r#"
1372        ˇone
1373        two
1374        threeˇ
1375        four
1376        five
1377        six
1378        seven
1379        eight
1380        nine
1381        ten
1382        "#
1383        .unindent(),
1384    );
1385
1386    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
1387    cx.assert_editor_state(
1388        &r#"
1389        one
1390        two
1391        three
1392        ˇfour
1393        five
1394        sixˇ
1395        seven
1396        eight
1397        nine
1398        ten
1399        "#
1400        .unindent(),
1401    );
1402
1403    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
1404    cx.assert_editor_state(
1405        &r#"
1406        one
1407        two
1408        three
1409        four
1410        five
1411        six
1412        ˇseven
1413        eight
1414        nineˇ
1415        ten
1416        "#
1417        .unindent(),
1418    );
1419
1420    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
1421    cx.assert_editor_state(
1422        &r#"
1423        one
1424        two
1425        three
1426        ˇfour
1427        five
1428        sixˇ
1429        seven
1430        eight
1431        nine
1432        ten
1433        "#
1434        .unindent(),
1435    );
1436
1437    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
1438    cx.assert_editor_state(
1439        &r#"
1440        ˇone
1441        two
1442        threeˇ
1443        four
1444        five
1445        six
1446        seven
1447        eight
1448        nine
1449        ten
1450        "#
1451        .unindent(),
1452    );
1453
1454    // Test select collapsing
1455    cx.update_editor(|editor, cx| {
1456        editor.move_page_down(&MovePageDown::default(), cx);
1457        editor.move_page_down(&MovePageDown::default(), cx);
1458        editor.move_page_down(&MovePageDown::default(), cx);
1459    });
1460    cx.assert_editor_state(
1461        &r#"
1462        one
1463        two
1464        three
1465        four
1466        five
1467        six
1468        seven
1469        eight
1470        nine
1471        ˇten
1472        ˇ"#
1473        .unindent(),
1474    );
1475}
1476
1477#[gpui::test]
1478async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
1479    init_test(cx, |_| {});
1480    let mut cx = EditorTestContext::new(cx).await;
1481    cx.set_state("one «two threeˇ» four");
1482    cx.update_editor(|editor, cx| {
1483        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
1484        assert_eq!(editor.text(cx), " four");
1485    });
1486}
1487
1488#[gpui::test]
1489fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
1490    init_test(cx, |_| {});
1491
1492    let (_, view) = cx.add_window(|cx| {
1493        let buffer = MultiBuffer::build_simple("one two three four", cx);
1494        build_editor(buffer.clone(), cx)
1495    });
1496
1497    view.update(cx, |view, cx| {
1498        view.change_selections(None, cx, |s| {
1499            s.select_display_ranges([
1500                // an empty selection - the preceding word fragment is deleted
1501                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
1502                // characters selected - they are deleted
1503                DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
1504            ])
1505        });
1506        view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
1507        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
1508    });
1509
1510    view.update(cx, |view, cx| {
1511        view.change_selections(None, cx, |s| {
1512            s.select_display_ranges([
1513                // an empty selection - the following word fragment is deleted
1514                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
1515                // characters selected - they are deleted
1516                DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
1517            ])
1518        });
1519        view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
1520        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
1521    });
1522}
1523
1524#[gpui::test]
1525fn test_newline(cx: &mut TestAppContext) {
1526    init_test(cx, |_| {});
1527
1528    let (_, view) = cx.add_window(|cx| {
1529        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
1530        build_editor(buffer.clone(), cx)
1531    });
1532
1533    view.update(cx, |view, cx| {
1534        view.change_selections(None, cx, |s| {
1535            s.select_display_ranges([
1536                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
1537                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
1538                DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
1539            ])
1540        });
1541
1542        view.newline(&Newline, cx);
1543        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
1544    });
1545}
1546
1547#[gpui::test]
1548fn test_newline_with_old_selections(cx: &mut TestAppContext) {
1549    init_test(cx, |_| {});
1550
1551    let (_, editor) = cx.add_window(|cx| {
1552        let buffer = MultiBuffer::build_simple(
1553            "
1554                a
1555                b(
1556                    X
1557                )
1558                c(
1559                    X
1560                )
1561            "
1562            .unindent()
1563            .as_str(),
1564            cx,
1565        );
1566        let mut editor = build_editor(buffer.clone(), cx);
1567        editor.change_selections(None, cx, |s| {
1568            s.select_ranges([
1569                Point::new(2, 4)..Point::new(2, 5),
1570                Point::new(5, 4)..Point::new(5, 5),
1571            ])
1572        });
1573        editor
1574    });
1575
1576    editor.update(cx, |editor, cx| {
1577        // Edit the buffer directly, deleting ranges surrounding the editor's selections
1578        editor.buffer.update(cx, |buffer, cx| {
1579            buffer.edit(
1580                [
1581                    (Point::new(1, 2)..Point::new(3, 0), ""),
1582                    (Point::new(4, 2)..Point::new(6, 0), ""),
1583                ],
1584                None,
1585                cx,
1586            );
1587            assert_eq!(
1588                buffer.read(cx).text(),
1589                "
1590                    a
1591                    b()
1592                    c()
1593                "
1594                .unindent()
1595            );
1596        });
1597        assert_eq!(
1598            editor.selections.ranges(cx),
1599            &[
1600                Point::new(1, 2)..Point::new(1, 2),
1601                Point::new(2, 2)..Point::new(2, 2),
1602            ],
1603        );
1604
1605        editor.newline(&Newline, cx);
1606        assert_eq!(
1607            editor.text(cx),
1608            "
1609                a
1610                b(
1611                )
1612                c(
1613                )
1614            "
1615            .unindent()
1616        );
1617
1618        // The selections are moved after the inserted newlines
1619        assert_eq!(
1620            editor.selections.ranges(cx),
1621            &[
1622                Point::new(2, 0)..Point::new(2, 0),
1623                Point::new(4, 0)..Point::new(4, 0),
1624            ],
1625        );
1626    });
1627}
1628
1629#[gpui::test]
1630async fn test_newline_above(cx: &mut gpui::TestAppContext) {
1631    init_test(cx, |settings| {
1632        settings.defaults.tab_size = NonZeroU32::new(4)
1633    });
1634
1635    let language = Arc::new(
1636        Language::new(
1637            LanguageConfig::default(),
1638            Some(tree_sitter_rust::language()),
1639        )
1640        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
1641        .unwrap(),
1642    );
1643
1644    let mut cx = EditorTestContext::new(cx).await;
1645    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1646    cx.set_state(indoc! {"
1647        const a: ˇA = (
16481649                «const_functionˇ»(ˇ),
1650                so«mˇ»et«hˇ»ing_ˇelse,ˇ
16511652        ˇ);ˇ
1653    "});
1654
1655    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
1656    cx.assert_editor_state(indoc! {"
1657        ˇ
1658        const a: A = (
1659            ˇ
1660            (
1661                ˇ
1662                ˇ
1663                const_function(),
1664                ˇ
1665                ˇ
1666                ˇ
1667                ˇ
1668                something_else,
1669                ˇ
1670            )
1671            ˇ
1672            ˇ
1673        );
1674    "});
1675}
1676
1677#[gpui::test]
1678async fn test_newline_below(cx: &mut gpui::TestAppContext) {
1679    init_test(cx, |settings| {
1680        settings.defaults.tab_size = NonZeroU32::new(4)
1681    });
1682
1683    let language = Arc::new(
1684        Language::new(
1685            LanguageConfig::default(),
1686            Some(tree_sitter_rust::language()),
1687        )
1688        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
1689        .unwrap(),
1690    );
1691
1692    let mut cx = EditorTestContext::new(cx).await;
1693    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1694    cx.set_state(indoc! {"
1695        const a: ˇA = (
16961697                «const_functionˇ»(ˇ),
1698                so«mˇ»et«hˇ»ing_ˇelse,ˇ
16991700        ˇ);ˇ
1701    "});
1702
1703    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
1704    cx.assert_editor_state(indoc! {"
1705        const a: A = (
1706            ˇ
1707            (
1708                ˇ
1709                const_function(),
1710                ˇ
1711                ˇ
1712                something_else,
1713                ˇ
1714                ˇ
1715                ˇ
1716                ˇ
1717            )
1718            ˇ
1719        );
1720        ˇ
1721        ˇ
1722    "});
1723}
1724
1725#[gpui::test]
1726async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
1727    init_test(cx, |settings| {
1728        settings.defaults.tab_size = NonZeroU32::new(4)
1729    });
1730
1731    let language = Arc::new(Language::new(
1732        LanguageConfig {
1733            line_comment: Some("//".into()),
1734            ..LanguageConfig::default()
1735        },
1736        None,
1737    ));
1738    {
1739        let mut cx = EditorTestContext::new(cx).await;
1740        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1741        cx.set_state(indoc! {"
1742        // Fooˇ
1743    "});
1744
1745        cx.update_editor(|e, cx| e.newline(&Newline, cx));
1746        cx.assert_editor_state(indoc! {"
1747        // Foo
1748        //ˇ
1749    "});
1750        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
1751        cx.set_state(indoc! {"
1752        ˇ// Foo
1753    "});
1754        cx.update_editor(|e, cx| e.newline(&Newline, cx));
1755        cx.assert_editor_state(indoc! {"
1756
1757        ˇ// Foo
1758    "});
1759    }
1760    // Ensure that comment continuations can be disabled.
1761    update_test_settings(cx, |settings| {
1762        settings.defaults.extend_comment_on_newline = Some(false);
1763    });
1764    let mut cx = EditorTestContext::new(cx).await;
1765    cx.set_state(indoc! {"
1766        // Fooˇ
1767    "});
1768    cx.update_editor(|e, cx| e.newline(&Newline, cx));
1769    cx.assert_editor_state(indoc! {"
1770        // Foo
1771        ˇ
1772    "});
1773}
1774
1775#[gpui::test]
1776fn test_insert_with_old_selections(cx: &mut TestAppContext) {
1777    init_test(cx, |_| {});
1778
1779    let (_, editor) = cx.add_window(|cx| {
1780        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
1781        let mut editor = build_editor(buffer.clone(), cx);
1782        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
1783        editor
1784    });
1785
1786    editor.update(cx, |editor, cx| {
1787        // Edit the buffer directly, deleting ranges surrounding the editor's selections
1788        editor.buffer.update(cx, |buffer, cx| {
1789            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
1790            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
1791        });
1792        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
1793
1794        editor.insert("Z", cx);
1795        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
1796
1797        // The selections are moved after the inserted characters
1798        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
1799    });
1800}
1801
1802#[gpui::test]
1803async fn test_tab(cx: &mut gpui::TestAppContext) {
1804    init_test(cx, |settings| {
1805        settings.defaults.tab_size = NonZeroU32::new(3)
1806    });
1807
1808    let mut cx = EditorTestContext::new(cx).await;
1809    cx.set_state(indoc! {"
1810        ˇabˇc
1811        ˇ🏀ˇ🏀ˇefg
18121813    "});
1814    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1815    cx.assert_editor_state(indoc! {"
1816           ˇab ˇc
1817           ˇ🏀  ˇ🏀  ˇefg
1818        d  ˇ
1819    "});
1820
1821    cx.set_state(indoc! {"
1822        a
1823        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
1824    "});
1825    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1826    cx.assert_editor_state(indoc! {"
1827        a
1828           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
1829    "});
1830}
1831
1832#[gpui::test]
1833async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
1834    init_test(cx, |_| {});
1835
1836    let mut cx = EditorTestContext::new(cx).await;
1837    let language = Arc::new(
1838        Language::new(
1839            LanguageConfig::default(),
1840            Some(tree_sitter_rust::language()),
1841        )
1842        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
1843        .unwrap(),
1844    );
1845    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1846
1847    // cursors that are already at the suggested indent level insert
1848    // a soft tab. cursors that are to the left of the suggested indent
1849    // auto-indent their line.
1850    cx.set_state(indoc! {"
1851        ˇ
1852        const a: B = (
1853            c(
1854                d(
1855        ˇ
1856                )
1857        ˇ
1858        ˇ    )
1859        );
1860    "});
1861    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1862    cx.assert_editor_state(indoc! {"
1863            ˇ
1864        const a: B = (
1865            c(
1866                d(
1867                    ˇ
1868                )
1869                ˇ
1870            ˇ)
1871        );
1872    "});
1873
1874    // handle auto-indent when there are multiple cursors on the same line
1875    cx.set_state(indoc! {"
1876        const a: B = (
1877            c(
1878        ˇ    ˇ
1879        ˇ    )
1880        );
1881    "});
1882    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1883    cx.assert_editor_state(indoc! {"
1884        const a: B = (
1885            c(
1886                ˇ
1887            ˇ)
1888        );
1889    "});
1890}
1891
1892#[gpui::test]
1893async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
1894    init_test(cx, |settings| {
1895        settings.defaults.tab_size = NonZeroU32::new(4)
1896    });
1897
1898    let language = Arc::new(
1899        Language::new(
1900            LanguageConfig::default(),
1901            Some(tree_sitter_rust::language()),
1902        )
1903        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
1904        .unwrap(),
1905    );
1906
1907    let mut cx = EditorTestContext::new(cx).await;
1908    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1909    cx.set_state(indoc! {"
1910        fn a() {
1911            if b {
1912        \t ˇc
1913            }
1914        }
1915    "});
1916
1917    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1918    cx.assert_editor_state(indoc! {"
1919        fn a() {
1920            if b {
1921                ˇc
1922            }
1923        }
1924    "});
1925}
1926
1927#[gpui::test]
1928async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
1929    init_test(cx, |settings| {
1930        settings.defaults.tab_size = NonZeroU32::new(4);
1931    });
1932
1933    let mut cx = EditorTestContext::new(cx).await;
1934
1935    cx.set_state(indoc! {"
1936          «oneˇ» «twoˇ»
1937        three
1938         four
1939    "});
1940    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1941    cx.assert_editor_state(indoc! {"
1942            «oneˇ» «twoˇ»
1943        three
1944         four
1945    "});
1946
1947    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1948    cx.assert_editor_state(indoc! {"
1949        «oneˇ» «twoˇ»
1950        three
1951         four
1952    "});
1953
1954    // select across line ending
1955    cx.set_state(indoc! {"
1956        one two
1957        t«hree
1958        ˇ» four
1959    "});
1960    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1961    cx.assert_editor_state(indoc! {"
1962        one two
1963            t«hree
1964        ˇ» four
1965    "});
1966
1967    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1968    cx.assert_editor_state(indoc! {"
1969        one two
1970        t«hree
1971        ˇ» four
1972    "});
1973
1974    // Ensure that indenting/outdenting works when the cursor is at column 0.
1975    cx.set_state(indoc! {"
1976        one two
1977        ˇthree
1978            four
1979    "});
1980    cx.update_editor(|e, cx| e.tab(&Tab, cx));
1981    cx.assert_editor_state(indoc! {"
1982        one two
1983            ˇthree
1984            four
1985    "});
1986
1987    cx.set_state(indoc! {"
1988        one two
1989        ˇ    three
1990            four
1991    "});
1992    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
1993    cx.assert_editor_state(indoc! {"
1994        one two
1995        ˇthree
1996            four
1997    "});
1998}
1999
2000#[gpui::test]
2001async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
2002    init_test(cx, |settings| {
2003        settings.defaults.hard_tabs = Some(true);
2004    });
2005
2006    let mut cx = EditorTestContext::new(cx).await;
2007
2008    // select two ranges on one line
2009    cx.set_state(indoc! {"
2010        «oneˇ» «twoˇ»
2011        three
2012        four
2013    "});
2014    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2015    cx.assert_editor_state(indoc! {"
2016        \t«oneˇ» «twoˇ»
2017        three
2018        four
2019    "});
2020    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2021    cx.assert_editor_state(indoc! {"
2022        \t\t«oneˇ» «twoˇ»
2023        three
2024        four
2025    "});
2026    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2027    cx.assert_editor_state(indoc! {"
2028        \t«oneˇ» «twoˇ»
2029        three
2030        four
2031    "});
2032    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2033    cx.assert_editor_state(indoc! {"
2034        «oneˇ» «twoˇ»
2035        three
2036        four
2037    "});
2038
2039    // select across a line ending
2040    cx.set_state(indoc! {"
2041        one two
2042        t«hree
2043        ˇ»four
2044    "});
2045    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2046    cx.assert_editor_state(indoc! {"
2047        one two
2048        \tt«hree
2049        ˇ»four
2050    "});
2051    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2052    cx.assert_editor_state(indoc! {"
2053        one two
2054        \t\tt«hree
2055        ˇ»four
2056    "});
2057    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2058    cx.assert_editor_state(indoc! {"
2059        one two
2060        \tt«hree
2061        ˇ»four
2062    "});
2063    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2064    cx.assert_editor_state(indoc! {"
2065        one two
2066        t«hree
2067        ˇ»four
2068    "});
2069
2070    // Ensure that indenting/outdenting works when the cursor is at column 0.
2071    cx.set_state(indoc! {"
2072        one two
2073        ˇthree
2074        four
2075    "});
2076    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2077    cx.assert_editor_state(indoc! {"
2078        one two
2079        ˇthree
2080        four
2081    "});
2082    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2083    cx.assert_editor_state(indoc! {"
2084        one two
2085        \tˇthree
2086        four
2087    "});
2088    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2089    cx.assert_editor_state(indoc! {"
2090        one two
2091        ˇthree
2092        four
2093    "});
2094}
2095
2096#[gpui::test]
2097fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
2098    init_test(cx, |settings| {
2099        settings.languages.extend([
2100            (
2101                "TOML".into(),
2102                LanguageSettingsContent {
2103                    tab_size: NonZeroU32::new(2),
2104                    ..Default::default()
2105                },
2106            ),
2107            (
2108                "Rust".into(),
2109                LanguageSettingsContent {
2110                    tab_size: NonZeroU32::new(4),
2111                    ..Default::default()
2112                },
2113            ),
2114        ]);
2115    });
2116
2117    let toml_language = Arc::new(Language::new(
2118        LanguageConfig {
2119            name: "TOML".into(),
2120            ..Default::default()
2121        },
2122        None,
2123    ));
2124    let rust_language = Arc::new(Language::new(
2125        LanguageConfig {
2126            name: "Rust".into(),
2127            ..Default::default()
2128        },
2129        None,
2130    ));
2131
2132    let toml_buffer =
2133        cx.add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx));
2134    let rust_buffer = cx.add_model(|cx| {
2135        Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx)
2136    });
2137    let multibuffer = cx.add_model(|cx| {
2138        let mut multibuffer = MultiBuffer::new(0);
2139        multibuffer.push_excerpts(
2140            toml_buffer.clone(),
2141            [ExcerptRange {
2142                context: Point::new(0, 0)..Point::new(2, 0),
2143                primary: None,
2144            }],
2145            cx,
2146        );
2147        multibuffer.push_excerpts(
2148            rust_buffer.clone(),
2149            [ExcerptRange {
2150                context: Point::new(0, 0)..Point::new(1, 0),
2151                primary: None,
2152            }],
2153            cx,
2154        );
2155        multibuffer
2156    });
2157
2158    cx.add_window(|cx| {
2159        let mut editor = build_editor(multibuffer, cx);
2160
2161        assert_eq!(
2162            editor.text(cx),
2163            indoc! {"
2164                a = 1
2165                b = 2
2166
2167                const c: usize = 3;
2168            "}
2169        );
2170
2171        select_ranges(
2172            &mut editor,
2173            indoc! {"
2174                «aˇ» = 1
2175                b = 2
2176
2177                «const c:ˇ» usize = 3;
2178            "},
2179            cx,
2180        );
2181
2182        editor.tab(&Tab, cx);
2183        assert_text_with_selections(
2184            &mut editor,
2185            indoc! {"
2186                  «aˇ» = 1
2187                b = 2
2188
2189                    «const c:ˇ» usize = 3;
2190            "},
2191            cx,
2192        );
2193        editor.tab_prev(&TabPrev, cx);
2194        assert_text_with_selections(
2195            &mut editor,
2196            indoc! {"
2197                «aˇ» = 1
2198                b = 2
2199
2200                «const c:ˇ» usize = 3;
2201            "},
2202            cx,
2203        );
2204
2205        editor
2206    });
2207}
2208
2209#[gpui::test]
2210async fn test_backspace(cx: &mut gpui::TestAppContext) {
2211    init_test(cx, |_| {});
2212
2213    let mut cx = EditorTestContext::new(cx).await;
2214
2215    // Basic backspace
2216    cx.set_state(indoc! {"
2217        onˇe two three
2218        fou«rˇ» five six
2219        seven «ˇeight nine
2220        »ten
2221    "});
2222    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2223    cx.assert_editor_state(indoc! {"
2224        oˇe two three
2225        fouˇ five six
2226        seven ˇten
2227    "});
2228
2229    // Test backspace inside and around indents
2230    cx.set_state(indoc! {"
2231        zero
2232            ˇone
2233                ˇtwo
2234            ˇ ˇ ˇ  three
2235        ˇ  ˇ  four
2236    "});
2237    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2238    cx.assert_editor_state(indoc! {"
2239        zero
2240        ˇone
2241            ˇtwo
2242        ˇ  threeˇ  four
2243    "});
2244
2245    // Test backspace with line_mode set to true
2246    cx.update_editor(|e, _| e.selections.line_mode = true);
2247    cx.set_state(indoc! {"
2248        The ˇquick ˇbrown
2249        fox jumps over
2250        the lazy dog
2251        ˇThe qu«ick bˇ»rown"});
2252    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2253    cx.assert_editor_state(indoc! {"
2254        ˇfox jumps over
2255        the lazy dogˇ"});
2256}
2257
2258#[gpui::test]
2259async fn test_delete(cx: &mut gpui::TestAppContext) {
2260    init_test(cx, |_| {});
2261
2262    let mut cx = EditorTestContext::new(cx).await;
2263    cx.set_state(indoc! {"
2264        onˇe two three
2265        fou«rˇ» five six
2266        seven «ˇeight nine
2267        »ten
2268    "});
2269    cx.update_editor(|e, cx| e.delete(&Delete, cx));
2270    cx.assert_editor_state(indoc! {"
2271        onˇ two three
2272        fouˇ five six
2273        seven ˇten
2274    "});
2275
2276    // Test backspace with line_mode set to true
2277    cx.update_editor(|e, _| e.selections.line_mode = true);
2278    cx.set_state(indoc! {"
2279        The ˇquick ˇbrown
2280        fox «ˇjum»ps over
2281        the lazy dog
2282        ˇThe qu«ick bˇ»rown"});
2283    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2284    cx.assert_editor_state("ˇthe lazy dogˇ");
2285}
2286
2287#[gpui::test]
2288fn test_delete_line(cx: &mut TestAppContext) {
2289    init_test(cx, |_| {});
2290
2291    let (_, view) = cx.add_window(|cx| {
2292        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2293        build_editor(buffer, cx)
2294    });
2295    view.update(cx, |view, cx| {
2296        view.change_selections(None, cx, |s| {
2297            s.select_display_ranges([
2298                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2299                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
2300                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2301            ])
2302        });
2303        view.delete_line(&DeleteLine, cx);
2304        assert_eq!(view.display_text(cx), "ghi");
2305        assert_eq!(
2306            view.selections.display_ranges(cx),
2307            vec![
2308                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
2309                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
2310            ]
2311        );
2312    });
2313
2314    let (_, view) = cx.add_window(|cx| {
2315        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2316        build_editor(buffer, cx)
2317    });
2318    view.update(cx, |view, cx| {
2319        view.change_selections(None, cx, |s| {
2320            s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
2321        });
2322        view.delete_line(&DeleteLine, cx);
2323        assert_eq!(view.display_text(cx), "ghi\n");
2324        assert_eq!(
2325            view.selections.display_ranges(cx),
2326            vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
2327        );
2328    });
2329}
2330
2331#[gpui::test]
2332fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
2333    init_test(cx, |_| {});
2334
2335    cx.add_window(|cx| {
2336        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
2337        let mut editor = build_editor(buffer.clone(), cx);
2338        let buffer = buffer.read(cx).as_singleton().unwrap();
2339
2340        assert_eq!(
2341            editor.selections.ranges::<Point>(cx),
2342            &[Point::new(0, 0)..Point::new(0, 0)]
2343        );
2344
2345        // When on single line, replace newline at end by space
2346        editor.join_lines(&JoinLines, cx);
2347        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
2348        assert_eq!(
2349            editor.selections.ranges::<Point>(cx),
2350            &[Point::new(0, 3)..Point::new(0, 3)]
2351        );
2352
2353        // When multiple lines are selected, remove newlines that are spanned by the selection
2354        editor.change_selections(None, cx, |s| {
2355            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
2356        });
2357        editor.join_lines(&JoinLines, cx);
2358        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
2359        assert_eq!(
2360            editor.selections.ranges::<Point>(cx),
2361            &[Point::new(0, 11)..Point::new(0, 11)]
2362        );
2363
2364        // Undo should be transactional
2365        editor.undo(&Undo, cx);
2366        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
2367        assert_eq!(
2368            editor.selections.ranges::<Point>(cx),
2369            &[Point::new(0, 5)..Point::new(2, 2)]
2370        );
2371
2372        // When joining an empty line don't insert a space
2373        editor.change_selections(None, cx, |s| {
2374            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
2375        });
2376        editor.join_lines(&JoinLines, cx);
2377        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
2378        assert_eq!(
2379            editor.selections.ranges::<Point>(cx),
2380            [Point::new(2, 3)..Point::new(2, 3)]
2381        );
2382
2383        // We can remove trailing newlines
2384        editor.join_lines(&JoinLines, cx);
2385        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
2386        assert_eq!(
2387            editor.selections.ranges::<Point>(cx),
2388            [Point::new(2, 3)..Point::new(2, 3)]
2389        );
2390
2391        // We don't blow up on the last line
2392        editor.join_lines(&JoinLines, cx);
2393        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
2394        assert_eq!(
2395            editor.selections.ranges::<Point>(cx),
2396            [Point::new(2, 3)..Point::new(2, 3)]
2397        );
2398
2399        editor
2400    });
2401}
2402
2403#[gpui::test]
2404fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
2405    init_test(cx, |_| {});
2406
2407    cx.add_window(|cx| {
2408        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
2409        let mut editor = build_editor(buffer.clone(), cx);
2410        let buffer = buffer.read(cx).as_singleton().unwrap();
2411
2412        editor.change_selections(None, cx, |s| {
2413            s.select_ranges([
2414                Point::new(0, 2)..Point::new(1, 1),
2415                Point::new(1, 2)..Point::new(1, 2),
2416                Point::new(3, 1)..Point::new(3, 2),
2417            ])
2418        });
2419
2420        editor.join_lines(&JoinLines, cx);
2421        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
2422
2423        assert_eq!(
2424            editor.selections.ranges::<Point>(cx),
2425            [
2426                Point::new(0, 7)..Point::new(0, 7),
2427                Point::new(1, 3)..Point::new(1, 3)
2428            ]
2429        );
2430        editor
2431    });
2432}
2433
2434#[gpui::test]
2435fn test_duplicate_line(cx: &mut TestAppContext) {
2436    init_test(cx, |_| {});
2437
2438    let (_, view) = cx.add_window(|cx| {
2439        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2440        build_editor(buffer, cx)
2441    });
2442    view.update(cx, |view, cx| {
2443        view.change_selections(None, cx, |s| {
2444            s.select_display_ranges([
2445                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2446                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2447                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2448                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2449            ])
2450        });
2451        view.duplicate_line(&DuplicateLine, cx);
2452        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
2453        assert_eq!(
2454            view.selections.display_ranges(cx),
2455            vec![
2456                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
2457                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
2458                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2459                DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
2460            ]
2461        );
2462    });
2463
2464    let (_, view) = cx.add_window(|cx| {
2465        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2466        build_editor(buffer, cx)
2467    });
2468    view.update(cx, |view, cx| {
2469        view.change_selections(None, cx, |s| {
2470            s.select_display_ranges([
2471                DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
2472                DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
2473            ])
2474        });
2475        view.duplicate_line(&DuplicateLine, cx);
2476        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
2477        assert_eq!(
2478            view.selections.display_ranges(cx),
2479            vec![
2480                DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
2481                DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
2482            ]
2483        );
2484    });
2485}
2486
2487#[gpui::test]
2488fn test_move_line_up_down(cx: &mut TestAppContext) {
2489    init_test(cx, |_| {});
2490
2491    let (_, view) = cx.add_window(|cx| {
2492        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
2493        build_editor(buffer, cx)
2494    });
2495    view.update(cx, |view, cx| {
2496        view.fold_ranges(
2497            vec![
2498                Point::new(0, 2)..Point::new(1, 2),
2499                Point::new(2, 3)..Point::new(4, 1),
2500                Point::new(7, 0)..Point::new(8, 4),
2501            ],
2502            true,
2503            cx,
2504        );
2505        view.change_selections(None, cx, |s| {
2506            s.select_display_ranges([
2507                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2508                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2509                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2510                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
2511            ])
2512        });
2513        assert_eq!(
2514            view.display_text(cx),
2515            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
2516        );
2517
2518        view.move_line_up(&MoveLineUp, cx);
2519        assert_eq!(
2520            view.display_text(cx),
2521            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
2522        );
2523        assert_eq!(
2524            view.selections.display_ranges(cx),
2525            vec![
2526                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2527                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2528                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
2529                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
2530            ]
2531        );
2532    });
2533
2534    view.update(cx, |view, cx| {
2535        view.move_line_down(&MoveLineDown, cx);
2536        assert_eq!(
2537            view.display_text(cx),
2538            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
2539        );
2540        assert_eq!(
2541            view.selections.display_ranges(cx),
2542            vec![
2543                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
2544                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2545                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2546                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
2547            ]
2548        );
2549    });
2550
2551    view.update(cx, |view, cx| {
2552        view.move_line_down(&MoveLineDown, cx);
2553        assert_eq!(
2554            view.display_text(cx),
2555            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
2556        );
2557        assert_eq!(
2558            view.selections.display_ranges(cx),
2559            vec![
2560                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2561                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2562                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2563                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
2564            ]
2565        );
2566    });
2567
2568    view.update(cx, |view, cx| {
2569        view.move_line_up(&MoveLineUp, cx);
2570        assert_eq!(
2571            view.display_text(cx),
2572            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
2573        );
2574        assert_eq!(
2575            view.selections.display_ranges(cx),
2576            vec![
2577                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
2578                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2579                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
2580                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
2581            ]
2582        );
2583    });
2584}
2585
2586#[gpui::test]
2587fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
2588    init_test(cx, |_| {});
2589
2590    let (_, editor) = cx.add_window(|cx| {
2591        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
2592        build_editor(buffer, cx)
2593    });
2594    editor.update(cx, |editor, cx| {
2595        let snapshot = editor.buffer.read(cx).snapshot(cx);
2596        editor.insert_blocks(
2597            [BlockProperties {
2598                style: BlockStyle::Fixed,
2599                position: snapshot.anchor_after(Point::new(2, 0)),
2600                disposition: BlockDisposition::Below,
2601                height: 1,
2602                render: Arc::new(|_| Empty::new().into_any()),
2603            }],
2604            Some(Autoscroll::fit()),
2605            cx,
2606        );
2607        editor.change_selections(None, cx, |s| {
2608            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
2609        });
2610        editor.move_line_down(&MoveLineDown, cx);
2611    });
2612}
2613
2614#[gpui::test]
2615fn test_transpose(cx: &mut TestAppContext) {
2616    init_test(cx, |_| {});
2617
2618    _ = cx
2619        .add_window(|cx| {
2620            let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
2621
2622            editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
2623            editor.transpose(&Default::default(), cx);
2624            assert_eq!(editor.text(cx), "bac");
2625            assert_eq!(editor.selections.ranges(cx), [2..2]);
2626
2627            editor.transpose(&Default::default(), cx);
2628            assert_eq!(editor.text(cx), "bca");
2629            assert_eq!(editor.selections.ranges(cx), [3..3]);
2630
2631            editor.transpose(&Default::default(), cx);
2632            assert_eq!(editor.text(cx), "bac");
2633            assert_eq!(editor.selections.ranges(cx), [3..3]);
2634
2635            editor
2636        })
2637        .1;
2638
2639    _ = cx
2640        .add_window(|cx| {
2641            let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
2642
2643            editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
2644            editor.transpose(&Default::default(), cx);
2645            assert_eq!(editor.text(cx), "acb\nde");
2646            assert_eq!(editor.selections.ranges(cx), [3..3]);
2647
2648            editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
2649            editor.transpose(&Default::default(), cx);
2650            assert_eq!(editor.text(cx), "acbd\ne");
2651            assert_eq!(editor.selections.ranges(cx), [5..5]);
2652
2653            editor.transpose(&Default::default(), cx);
2654            assert_eq!(editor.text(cx), "acbde\n");
2655            assert_eq!(editor.selections.ranges(cx), [6..6]);
2656
2657            editor.transpose(&Default::default(), cx);
2658            assert_eq!(editor.text(cx), "acbd\ne");
2659            assert_eq!(editor.selections.ranges(cx), [6..6]);
2660
2661            editor
2662        })
2663        .1;
2664
2665    _ = cx
2666        .add_window(|cx| {
2667            let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
2668
2669            editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
2670            editor.transpose(&Default::default(), cx);
2671            assert_eq!(editor.text(cx), "bacd\ne");
2672            assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
2673
2674            editor.transpose(&Default::default(), cx);
2675            assert_eq!(editor.text(cx), "bcade\n");
2676            assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
2677
2678            editor.transpose(&Default::default(), cx);
2679            assert_eq!(editor.text(cx), "bcda\ne");
2680            assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
2681
2682            editor.transpose(&Default::default(), cx);
2683            assert_eq!(editor.text(cx), "bcade\n");
2684            assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
2685
2686            editor.transpose(&Default::default(), cx);
2687            assert_eq!(editor.text(cx), "bcaed\n");
2688            assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
2689
2690            editor
2691        })
2692        .1;
2693
2694    _ = cx
2695        .add_window(|cx| {
2696            let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
2697
2698            editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
2699            editor.transpose(&Default::default(), cx);
2700            assert_eq!(editor.text(cx), "🏀🍐✋");
2701            assert_eq!(editor.selections.ranges(cx), [8..8]);
2702
2703            editor.transpose(&Default::default(), cx);
2704            assert_eq!(editor.text(cx), "🏀✋🍐");
2705            assert_eq!(editor.selections.ranges(cx), [11..11]);
2706
2707            editor.transpose(&Default::default(), cx);
2708            assert_eq!(editor.text(cx), "🏀🍐✋");
2709            assert_eq!(editor.selections.ranges(cx), [11..11]);
2710
2711            editor
2712        })
2713        .1;
2714}
2715
2716#[gpui::test]
2717async fn test_clipboard(cx: &mut gpui::TestAppContext) {
2718    init_test(cx, |_| {});
2719
2720    let mut cx = EditorTestContext::new(cx).await;
2721
2722    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
2723    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2724    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
2725
2726    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
2727    cx.set_state("two ˇfour ˇsix ˇ");
2728    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2729    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
2730
2731    // Paste again but with only two cursors. Since the number of cursors doesn't
2732    // match the number of slices in the clipboard, the entire clipboard text
2733    // is pasted at each cursor.
2734    cx.set_state("ˇtwo one✅ four three six five ˇ");
2735    cx.update_editor(|e, cx| {
2736        e.handle_input("( ", cx);
2737        e.paste(&Paste, cx);
2738        e.handle_input(") ", cx);
2739    });
2740    cx.assert_editor_state(
2741        &([
2742            "( one✅ ",
2743            "three ",
2744            "five ) ˇtwo one✅ four three six five ( one✅ ",
2745            "three ",
2746            "five ) ˇ",
2747        ]
2748        .join("\n")),
2749    );
2750
2751    // Cut with three selections, one of which is full-line.
2752    cx.set_state(indoc! {"
2753        1«2ˇ»3
2754        4ˇ567
2755        «8ˇ»9"});
2756    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2757    cx.assert_editor_state(indoc! {"
2758        1ˇ3
2759        ˇ9"});
2760
2761    // Paste with three selections, noticing how the copied selection that was full-line
2762    // gets inserted before the second cursor.
2763    cx.set_state(indoc! {"
2764        1ˇ3
27652766        «oˇ»ne"});
2767    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2768    cx.assert_editor_state(indoc! {"
2769        12ˇ3
2770        4567
27712772        8ˇne"});
2773
2774    // Copy with a single cursor only, which writes the whole line into the clipboard.
2775    cx.set_state(indoc! {"
2776        The quick brown
2777        fox juˇmps over
2778        the lazy dog"});
2779    cx.update_editor(|e, cx| e.copy(&Copy, cx));
2780    cx.cx.assert_clipboard_content(Some("fox jumps over\n"));
2781
2782    // Paste with three selections, noticing how the copied full-line selection is inserted
2783    // before the empty selections but replaces the selection that is non-empty.
2784    cx.set_state(indoc! {"
2785        Tˇhe quick brown
2786        «foˇ»x jumps over
2787        tˇhe lazy dog"});
2788    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2789    cx.assert_editor_state(indoc! {"
2790        fox jumps over
2791        Tˇhe quick brown
2792        fox jumps over
2793        ˇx jumps over
2794        fox jumps over
2795        tˇhe lazy dog"});
2796}
2797
2798#[gpui::test]
2799async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
2800    init_test(cx, |_| {});
2801
2802    let mut cx = EditorTestContext::new(cx).await;
2803    let language = Arc::new(Language::new(
2804        LanguageConfig::default(),
2805        Some(tree_sitter_rust::language()),
2806    ));
2807    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
2808
2809    // Cut an indented block, without the leading whitespace.
2810    cx.set_state(indoc! {"
2811        const a: B = (
2812            c(),
2813            «d(
2814                e,
2815                f
2816            )ˇ»
2817        );
2818    "});
2819    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2820    cx.assert_editor_state(indoc! {"
2821        const a: B = (
2822            c(),
2823            ˇ
2824        );
2825    "});
2826
2827    // Paste it at the same position.
2828    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2829    cx.assert_editor_state(indoc! {"
2830        const a: B = (
2831            c(),
2832            d(
2833                e,
2834                f
28352836        );
2837    "});
2838
2839    // Paste it at a line with a lower indent level.
2840    cx.set_state(indoc! {"
2841        ˇ
2842        const a: B = (
2843            c(),
2844        );
2845    "});
2846    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2847    cx.assert_editor_state(indoc! {"
2848        d(
2849            e,
2850            f
28512852        const a: B = (
2853            c(),
2854        );
2855    "});
2856
2857    // Cut an indented block, with the leading whitespace.
2858    cx.set_state(indoc! {"
2859        const a: B = (
2860            c(),
2861        «    d(
2862                e,
2863                f
2864            )
2865        ˇ»);
2866    "});
2867    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2868    cx.assert_editor_state(indoc! {"
2869        const a: B = (
2870            c(),
2871        ˇ);
2872    "});
2873
2874    // Paste it at the same position.
2875    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2876    cx.assert_editor_state(indoc! {"
2877        const a: B = (
2878            c(),
2879            d(
2880                e,
2881                f
2882            )
2883        ˇ);
2884    "});
2885
2886    // Paste it at a line with a higher indent level.
2887    cx.set_state(indoc! {"
2888        const a: B = (
2889            c(),
2890            d(
2891                e,
28922893            )
2894        );
2895    "});
2896    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2897    cx.assert_editor_state(indoc! {"
2898        const a: B = (
2899            c(),
2900            d(
2901                e,
2902                f    d(
2903                    e,
2904                    f
2905                )
2906        ˇ
2907            )
2908        );
2909    "});
2910}
2911
2912#[gpui::test]
2913fn test_select_all(cx: &mut TestAppContext) {
2914    init_test(cx, |_| {});
2915
2916    let (_, view) = cx.add_window(|cx| {
2917        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
2918        build_editor(buffer, cx)
2919    });
2920    view.update(cx, |view, cx| {
2921        view.select_all(&SelectAll, cx);
2922        assert_eq!(
2923            view.selections.display_ranges(cx),
2924            &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
2925        );
2926    });
2927}
2928
2929#[gpui::test]
2930fn test_select_line(cx: &mut TestAppContext) {
2931    init_test(cx, |_| {});
2932
2933    let (_, view) = cx.add_window(|cx| {
2934        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
2935        build_editor(buffer, cx)
2936    });
2937    view.update(cx, |view, cx| {
2938        view.change_selections(None, cx, |s| {
2939            s.select_display_ranges([
2940                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2941                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2942                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2943                DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
2944            ])
2945        });
2946        view.select_line(&SelectLine, cx);
2947        assert_eq!(
2948            view.selections.display_ranges(cx),
2949            vec![
2950                DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
2951                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
2952            ]
2953        );
2954    });
2955
2956    view.update(cx, |view, cx| {
2957        view.select_line(&SelectLine, cx);
2958        assert_eq!(
2959            view.selections.display_ranges(cx),
2960            vec![
2961                DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
2962                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
2963            ]
2964        );
2965    });
2966
2967    view.update(cx, |view, cx| {
2968        view.select_line(&SelectLine, cx);
2969        assert_eq!(
2970            view.selections.display_ranges(cx),
2971            vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
2972        );
2973    });
2974}
2975
2976#[gpui::test]
2977fn test_split_selection_into_lines(cx: &mut TestAppContext) {
2978    init_test(cx, |_| {});
2979
2980    let (_, view) = cx.add_window(|cx| {
2981        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
2982        build_editor(buffer, cx)
2983    });
2984    view.update(cx, |view, cx| {
2985        view.fold_ranges(
2986            vec![
2987                Point::new(0, 2)..Point::new(1, 2),
2988                Point::new(2, 3)..Point::new(4, 1),
2989                Point::new(7, 0)..Point::new(8, 4),
2990            ],
2991            true,
2992            cx,
2993        );
2994        view.change_selections(None, cx, |s| {
2995            s.select_display_ranges([
2996                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2997                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2998                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2999                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
3000            ])
3001        });
3002        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
3003    });
3004
3005    view.update(cx, |view, cx| {
3006        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
3007        assert_eq!(
3008            view.display_text(cx),
3009            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
3010        );
3011        assert_eq!(
3012            view.selections.display_ranges(cx),
3013            [
3014                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
3015                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3016                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
3017                DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
3018            ]
3019        );
3020    });
3021
3022    view.update(cx, |view, cx| {
3023        view.change_selections(None, cx, |s| {
3024            s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
3025        });
3026        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
3027        assert_eq!(
3028            view.display_text(cx),
3029            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
3030        );
3031        assert_eq!(
3032            view.selections.display_ranges(cx),
3033            [
3034                DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
3035                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
3036                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
3037                DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
3038                DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
3039                DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
3040                DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
3041                DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
3042            ]
3043        );
3044    });
3045}
3046
3047#[gpui::test]
3048fn test_add_selection_above_below(cx: &mut TestAppContext) {
3049    init_test(cx, |_| {});
3050
3051    let (_, view) = cx.add_window(|cx| {
3052        let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
3053        build_editor(buffer, cx)
3054    });
3055
3056    view.update(cx, |view, cx| {
3057        view.change_selections(None, cx, |s| {
3058            s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
3059        });
3060    });
3061    view.update(cx, |view, cx| {
3062        view.add_selection_above(&AddSelectionAbove, cx);
3063        assert_eq!(
3064            view.selections.display_ranges(cx),
3065            vec![
3066                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
3067                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
3068            ]
3069        );
3070    });
3071
3072    view.update(cx, |view, cx| {
3073        view.add_selection_above(&AddSelectionAbove, cx);
3074        assert_eq!(
3075            view.selections.display_ranges(cx),
3076            vec![
3077                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
3078                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
3079            ]
3080        );
3081    });
3082
3083    view.update(cx, |view, cx| {
3084        view.add_selection_below(&AddSelectionBelow, cx);
3085        assert_eq!(
3086            view.selections.display_ranges(cx),
3087            vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
3088        );
3089
3090        view.undo_selection(&UndoSelection, cx);
3091        assert_eq!(
3092            view.selections.display_ranges(cx),
3093            vec![
3094                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
3095                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
3096            ]
3097        );
3098
3099        view.redo_selection(&RedoSelection, cx);
3100        assert_eq!(
3101            view.selections.display_ranges(cx),
3102            vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
3103        );
3104    });
3105
3106    view.update(cx, |view, cx| {
3107        view.add_selection_below(&AddSelectionBelow, cx);
3108        assert_eq!(
3109            view.selections.display_ranges(cx),
3110            vec![
3111                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
3112                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
3113            ]
3114        );
3115    });
3116
3117    view.update(cx, |view, cx| {
3118        view.add_selection_below(&AddSelectionBelow, cx);
3119        assert_eq!(
3120            view.selections.display_ranges(cx),
3121            vec![
3122                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
3123                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
3124            ]
3125        );
3126    });
3127
3128    view.update(cx, |view, cx| {
3129        view.change_selections(None, cx, |s| {
3130            s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
3131        });
3132    });
3133    view.update(cx, |view, cx| {
3134        view.add_selection_below(&AddSelectionBelow, cx);
3135        assert_eq!(
3136            view.selections.display_ranges(cx),
3137            vec![
3138                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
3139                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
3140            ]
3141        );
3142    });
3143
3144    view.update(cx, |view, cx| {
3145        view.add_selection_below(&AddSelectionBelow, cx);
3146        assert_eq!(
3147            view.selections.display_ranges(cx),
3148            vec![
3149                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
3150                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
3151            ]
3152        );
3153    });
3154
3155    view.update(cx, |view, cx| {
3156        view.add_selection_above(&AddSelectionAbove, cx);
3157        assert_eq!(
3158            view.selections.display_ranges(cx),
3159            vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
3160        );
3161    });
3162
3163    view.update(cx, |view, cx| {
3164        view.add_selection_above(&AddSelectionAbove, cx);
3165        assert_eq!(
3166            view.selections.display_ranges(cx),
3167            vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
3168        );
3169    });
3170
3171    view.update(cx, |view, cx| {
3172        view.change_selections(None, cx, |s| {
3173            s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
3174        });
3175        view.add_selection_below(&AddSelectionBelow, cx);
3176        assert_eq!(
3177            view.selections.display_ranges(cx),
3178            vec![
3179                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
3180                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
3181                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
3182            ]
3183        );
3184    });
3185
3186    view.update(cx, |view, cx| {
3187        view.add_selection_below(&AddSelectionBelow, cx);
3188        assert_eq!(
3189            view.selections.display_ranges(cx),
3190            vec![
3191                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
3192                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
3193                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
3194                DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
3195            ]
3196        );
3197    });
3198
3199    view.update(cx, |view, cx| {
3200        view.add_selection_above(&AddSelectionAbove, cx);
3201        assert_eq!(
3202            view.selections.display_ranges(cx),
3203            vec![
3204                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
3205                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
3206                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
3207            ]
3208        );
3209    });
3210
3211    view.update(cx, |view, cx| {
3212        view.change_selections(None, cx, |s| {
3213            s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
3214        });
3215    });
3216    view.update(cx, |view, cx| {
3217        view.add_selection_above(&AddSelectionAbove, cx);
3218        assert_eq!(
3219            view.selections.display_ranges(cx),
3220            vec![
3221                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
3222                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
3223                DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
3224                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
3225            ]
3226        );
3227    });
3228
3229    view.update(cx, |view, cx| {
3230        view.add_selection_below(&AddSelectionBelow, cx);
3231        assert_eq!(
3232            view.selections.display_ranges(cx),
3233            vec![
3234                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
3235                DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
3236                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
3237            ]
3238        );
3239    });
3240}
3241
3242#[gpui::test]
3243async fn test_select_next(cx: &mut gpui::TestAppContext) {
3244    init_test(cx, |_| {});
3245
3246    let mut cx = EditorTestContext::new(cx).await;
3247    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
3248
3249    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
3250    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3251
3252    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
3253    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
3254
3255    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
3256    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3257
3258    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
3259    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
3260
3261    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
3262    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
3263
3264    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
3265    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
3266}
3267
3268#[gpui::test]
3269async fn test_select_previous(cx: &mut gpui::TestAppContext) {
3270    init_test(cx, |_| {});
3271    {
3272        // `Select previous` without a selection (selects wordwise)
3273        let mut cx = EditorTestContext::new(cx).await;
3274        cx.set_state("abc\nˇabc abc\ndefabc\nabc");
3275
3276        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3277        cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3278
3279        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3280        cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
3281
3282        cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
3283        cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3284
3285        cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
3286        cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
3287
3288        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3289        cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
3290
3291        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3292        cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
3293    }
3294    {
3295        // `Select previous` with a selection
3296        let mut cx = EditorTestContext::new(cx).await;
3297        cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
3298
3299        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3300        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
3301
3302        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3303        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
3304
3305        cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
3306        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
3307
3308        cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
3309        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
3310
3311        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3312        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
3313
3314        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3315        cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
3316    }
3317}
3318
3319#[gpui::test]
3320async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
3321    init_test(cx, |_| {});
3322
3323    let language = Arc::new(Language::new(
3324        LanguageConfig::default(),
3325        Some(tree_sitter_rust::language()),
3326    ));
3327
3328    let text = r#"
3329        use mod1::mod2::{mod3, mod4};
3330
3331        fn fn_1(param1: bool, param2: &str) {
3332            let var1 = "text";
3333        }
3334    "#
3335    .unindent();
3336
3337    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3338    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3339    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
3340    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
3341        .await;
3342
3343    view.update(cx, |view, cx| {
3344        view.change_selections(None, cx, |s| {
3345            s.select_display_ranges([
3346                DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3347                DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3348                DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3349            ]);
3350        });
3351        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3352    });
3353    assert_eq!(
3354        view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
3355        &[
3356            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
3357            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3358            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
3359        ]
3360    );
3361
3362    view.update(cx, |view, cx| {
3363        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3364    });
3365    assert_eq!(
3366        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3367        &[
3368            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3369            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
3370        ]
3371    );
3372
3373    view.update(cx, |view, cx| {
3374        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3375    });
3376    assert_eq!(
3377        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3378        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
3379    );
3380
3381    // Trying to expand the selected syntax node one more time has no effect.
3382    view.update(cx, |view, cx| {
3383        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3384    });
3385    assert_eq!(
3386        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3387        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
3388    );
3389
3390    view.update(cx, |view, cx| {
3391        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3392    });
3393    assert_eq!(
3394        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3395        &[
3396            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3397            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
3398        ]
3399    );
3400
3401    view.update(cx, |view, cx| {
3402        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3403    });
3404    assert_eq!(
3405        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3406        &[
3407            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
3408            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3409            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
3410        ]
3411    );
3412
3413    view.update(cx, |view, cx| {
3414        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3415    });
3416    assert_eq!(
3417        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3418        &[
3419            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3420            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3421            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3422        ]
3423    );
3424
3425    // Trying to shrink the selected syntax node one more time has no effect.
3426    view.update(cx, |view, cx| {
3427        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3428    });
3429    assert_eq!(
3430        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3431        &[
3432            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3433            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3434            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3435        ]
3436    );
3437
3438    // Ensure that we keep expanding the selection if the larger selection starts or ends within
3439    // a fold.
3440    view.update(cx, |view, cx| {
3441        view.fold_ranges(
3442            vec![
3443                Point::new(0, 21)..Point::new(0, 24),
3444                Point::new(3, 20)..Point::new(3, 22),
3445            ],
3446            true,
3447            cx,
3448        );
3449        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3450    });
3451    assert_eq!(
3452        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3453        &[
3454            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3455            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3456            DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
3457        ]
3458    );
3459}
3460
3461#[gpui::test]
3462async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
3463    init_test(cx, |_| {});
3464
3465    let language = Arc::new(
3466        Language::new(
3467            LanguageConfig {
3468                brackets: BracketPairConfig {
3469                    pairs: vec![
3470                        BracketPair {
3471                            start: "{".to_string(),
3472                            end: "}".to_string(),
3473                            close: false,
3474                            newline: true,
3475                        },
3476                        BracketPair {
3477                            start: "(".to_string(),
3478                            end: ")".to_string(),
3479                            close: false,
3480                            newline: true,
3481                        },
3482                    ],
3483                    ..Default::default()
3484                },
3485                ..Default::default()
3486            },
3487            Some(tree_sitter_rust::language()),
3488        )
3489        .with_indents_query(
3490            r#"
3491                (_ "(" ")" @end) @indent
3492                (_ "{" "}" @end) @indent
3493            "#,
3494        )
3495        .unwrap(),
3496    );
3497
3498    let text = "fn a() {}";
3499
3500    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3501    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3502    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3503    editor
3504        .condition(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
3505        .await;
3506
3507    editor.update(cx, |editor, cx| {
3508        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
3509        editor.newline(&Newline, cx);
3510        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
3511        assert_eq!(
3512            editor.selections.ranges(cx),
3513            &[
3514                Point::new(1, 4)..Point::new(1, 4),
3515                Point::new(3, 4)..Point::new(3, 4),
3516                Point::new(5, 0)..Point::new(5, 0)
3517            ]
3518        );
3519    });
3520}
3521
3522#[gpui::test]
3523async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
3524    init_test(cx, |_| {});
3525
3526    let mut cx = EditorTestContext::new(cx).await;
3527
3528    let language = Arc::new(Language::new(
3529        LanguageConfig {
3530            brackets: BracketPairConfig {
3531                pairs: vec![
3532                    BracketPair {
3533                        start: "{".to_string(),
3534                        end: "}".to_string(),
3535                        close: true,
3536                        newline: true,
3537                    },
3538                    BracketPair {
3539                        start: "(".to_string(),
3540                        end: ")".to_string(),
3541                        close: true,
3542                        newline: true,
3543                    },
3544                    BracketPair {
3545                        start: "/*".to_string(),
3546                        end: " */".to_string(),
3547                        close: true,
3548                        newline: true,
3549                    },
3550                    BracketPair {
3551                        start: "[".to_string(),
3552                        end: "]".to_string(),
3553                        close: false,
3554                        newline: true,
3555                    },
3556                    BracketPair {
3557                        start: "\"".to_string(),
3558                        end: "\"".to_string(),
3559                        close: true,
3560                        newline: false,
3561                    },
3562                ],
3563                ..Default::default()
3564            },
3565            autoclose_before: "})]".to_string(),
3566            ..Default::default()
3567        },
3568        Some(tree_sitter_rust::language()),
3569    ));
3570
3571    let registry = Arc::new(LanguageRegistry::test());
3572    registry.add(language.clone());
3573    cx.update_buffer(|buffer, cx| {
3574        buffer.set_language_registry(registry);
3575        buffer.set_language(Some(language), cx);
3576    });
3577
3578    cx.set_state(
3579        &r#"
3580            🏀ˇ
3581            εˇ
3582            ❤️ˇ
3583        "#
3584        .unindent(),
3585    );
3586
3587    // autoclose multiple nested brackets at multiple cursors
3588    cx.update_editor(|view, cx| {
3589        view.handle_input("{", cx);
3590        view.handle_input("{", cx);
3591        view.handle_input("{", cx);
3592    });
3593    cx.assert_editor_state(
3594        &"
3595            🏀{{{ˇ}}}
3596            ε{{{ˇ}}}
3597            ❤️{{{ˇ}}}
3598        "
3599        .unindent(),
3600    );
3601
3602    // insert a different closing bracket
3603    cx.update_editor(|view, cx| {
3604        view.handle_input(")", cx);
3605    });
3606    cx.assert_editor_state(
3607        &"
3608            🏀{{{)ˇ}}}
3609            ε{{{)ˇ}}}
3610            ❤️{{{)ˇ}}}
3611        "
3612        .unindent(),
3613    );
3614
3615    // skip over the auto-closed brackets when typing a closing bracket
3616    cx.update_editor(|view, cx| {
3617        view.move_right(&MoveRight, cx);
3618        view.handle_input("}", cx);
3619        view.handle_input("}", cx);
3620        view.handle_input("}", cx);
3621    });
3622    cx.assert_editor_state(
3623        &"
3624            🏀{{{)}}}}ˇ
3625            ε{{{)}}}}ˇ
3626            ❤️{{{)}}}}ˇ
3627        "
3628        .unindent(),
3629    );
3630
3631    // autoclose multi-character pairs
3632    cx.set_state(
3633        &"
3634            ˇ
3635            ˇ
3636        "
3637        .unindent(),
3638    );
3639    cx.update_editor(|view, cx| {
3640        view.handle_input("/", cx);
3641        view.handle_input("*", cx);
3642    });
3643    cx.assert_editor_state(
3644        &"
3645            /*ˇ */
3646            /*ˇ */
3647        "
3648        .unindent(),
3649    );
3650
3651    // one cursor autocloses a multi-character pair, one cursor
3652    // does not autoclose.
3653    cx.set_state(
3654        &"
36553656            ˇ
3657        "
3658        .unindent(),
3659    );
3660    cx.update_editor(|view, cx| view.handle_input("*", cx));
3661    cx.assert_editor_state(
3662        &"
3663            /*ˇ */
36643665        "
3666        .unindent(),
3667    );
3668
3669    // Don't autoclose if the next character isn't whitespace and isn't
3670    // listed in the language's "autoclose_before" section.
3671    cx.set_state("ˇa b");
3672    cx.update_editor(|view, cx| view.handle_input("{", cx));
3673    cx.assert_editor_state("{ˇa b");
3674
3675    // Don't autoclose if `close` is false for the bracket pair
3676    cx.set_state("ˇ");
3677    cx.update_editor(|view, cx| view.handle_input("[", cx));
3678    cx.assert_editor_state("");
3679
3680    // Surround with brackets if text is selected
3681    cx.set_state("«aˇ» b");
3682    cx.update_editor(|view, cx| view.handle_input("{", cx));
3683    cx.assert_editor_state("{«aˇ»} b");
3684
3685    // Autclose pair where the start and end characters are the same
3686    cx.set_state("");
3687    cx.update_editor(|view, cx| view.handle_input("\"", cx));
3688    cx.assert_editor_state("a\"ˇ\"");
3689    cx.update_editor(|view, cx| view.handle_input("\"", cx));
3690    cx.assert_editor_state("a\"\"ˇ");
3691}
3692
3693#[gpui::test]
3694async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
3695    init_test(cx, |_| {});
3696
3697    let mut cx = EditorTestContext::new(cx).await;
3698
3699    let html_language = Arc::new(
3700        Language::new(
3701            LanguageConfig {
3702                name: "HTML".into(),
3703                brackets: BracketPairConfig {
3704                    pairs: vec![
3705                        BracketPair {
3706                            start: "<".into(),
3707                            end: ">".into(),
3708                            close: true,
3709                            ..Default::default()
3710                        },
3711                        BracketPair {
3712                            start: "{".into(),
3713                            end: "}".into(),
3714                            close: true,
3715                            ..Default::default()
3716                        },
3717                        BracketPair {
3718                            start: "(".into(),
3719                            end: ")".into(),
3720                            close: true,
3721                            ..Default::default()
3722                        },
3723                    ],
3724                    ..Default::default()
3725                },
3726                autoclose_before: "})]>".into(),
3727                ..Default::default()
3728            },
3729            Some(tree_sitter_html::language()),
3730        )
3731        .with_injection_query(
3732            r#"
3733            (script_element
3734                (raw_text) @content
3735                (#set! "language" "javascript"))
3736            "#,
3737        )
3738        .unwrap(),
3739    );
3740
3741    let javascript_language = Arc::new(Language::new(
3742        LanguageConfig {
3743            name: "JavaScript".into(),
3744            brackets: BracketPairConfig {
3745                pairs: vec![
3746                    BracketPair {
3747                        start: "/*".into(),
3748                        end: " */".into(),
3749                        close: true,
3750                        ..Default::default()
3751                    },
3752                    BracketPair {
3753                        start: "{".into(),
3754                        end: "}".into(),
3755                        close: true,
3756                        ..Default::default()
3757                    },
3758                    BracketPair {
3759                        start: "(".into(),
3760                        end: ")".into(),
3761                        close: true,
3762                        ..Default::default()
3763                    },
3764                ],
3765                ..Default::default()
3766            },
3767            autoclose_before: "})]>".into(),
3768            ..Default::default()
3769        },
3770        Some(tree_sitter_javascript::language()),
3771    ));
3772
3773    let registry = Arc::new(LanguageRegistry::test());
3774    registry.add(html_language.clone());
3775    registry.add(javascript_language.clone());
3776
3777    cx.update_buffer(|buffer, cx| {
3778        buffer.set_language_registry(registry);
3779        buffer.set_language(Some(html_language), cx);
3780    });
3781
3782    cx.set_state(
3783        &r#"
3784            <body>ˇ
3785                <script>
3786                    var x = 1;ˇ
3787                </script>
3788            </body>ˇ
3789        "#
3790        .unindent(),
3791    );
3792
3793    // Precondition: different languages are active at different locations.
3794    cx.update_editor(|editor, cx| {
3795        let snapshot = editor.snapshot(cx);
3796        let cursors = editor.selections.ranges::<usize>(cx);
3797        let languages = cursors
3798            .iter()
3799            .map(|c| snapshot.language_at(c.start).unwrap().name())
3800            .collect::<Vec<_>>();
3801        assert_eq!(
3802            languages,
3803            &["HTML".into(), "JavaScript".into(), "HTML".into()]
3804        );
3805    });
3806
3807    // Angle brackets autoclose in HTML, but not JavaScript.
3808    cx.update_editor(|editor, cx| {
3809        editor.handle_input("<", cx);
3810        editor.handle_input("a", cx);
3811    });
3812    cx.assert_editor_state(
3813        &r#"
3814            <body><aˇ>
3815                <script>
3816                    var x = 1;<aˇ
3817                </script>
3818            </body><aˇ>
3819        "#
3820        .unindent(),
3821    );
3822
3823    // Curly braces and parens autoclose in both HTML and JavaScript.
3824    cx.update_editor(|editor, cx| {
3825        editor.handle_input(" b=", cx);
3826        editor.handle_input("{", cx);
3827        editor.handle_input("c", cx);
3828        editor.handle_input("(", cx);
3829    });
3830    cx.assert_editor_state(
3831        &r#"
3832            <body><a b={c(ˇ)}>
3833                <script>
3834                    var x = 1;<a b={c(ˇ)}
3835                </script>
3836            </body><a b={c(ˇ)}>
3837        "#
3838        .unindent(),
3839    );
3840
3841    // Brackets that were already autoclosed are skipped.
3842    cx.update_editor(|editor, cx| {
3843        editor.handle_input(")", cx);
3844        editor.handle_input("d", cx);
3845        editor.handle_input("}", cx);
3846    });
3847    cx.assert_editor_state(
3848        &r#"
3849            <body><a b={c()d}ˇ>
3850                <script>
3851                    var x = 1;<a b={c()d}ˇ
3852                </script>
3853            </body><a b={c()d}ˇ>
3854        "#
3855        .unindent(),
3856    );
3857    cx.update_editor(|editor, cx| {
3858        editor.handle_input(">", cx);
3859    });
3860    cx.assert_editor_state(
3861        &r#"
3862            <body><a b={c()d}>ˇ
3863                <script>
3864                    var x = 1;<a b={c()d}>ˇ
3865                </script>
3866            </body><a b={c()d}>ˇ
3867        "#
3868        .unindent(),
3869    );
3870
3871    // Reset
3872    cx.set_state(
3873        &r#"
3874            <body>ˇ
3875                <script>
3876                    var x = 1;ˇ
3877                </script>
3878            </body>ˇ
3879        "#
3880        .unindent(),
3881    );
3882
3883    cx.update_editor(|editor, cx| {
3884        editor.handle_input("<", cx);
3885    });
3886    cx.assert_editor_state(
3887        &r#"
3888            <body><ˇ>
3889                <script>
3890                    var x = 1;<ˇ
3891                </script>
3892            </body><ˇ>
3893        "#
3894        .unindent(),
3895    );
3896
3897    // When backspacing, the closing angle brackets are removed.
3898    cx.update_editor(|editor, cx| {
3899        editor.backspace(&Backspace, cx);
3900    });
3901    cx.assert_editor_state(
3902        &r#"
3903            <body>ˇ
3904                <script>
3905                    var x = 1;ˇ
3906                </script>
3907            </body>ˇ
3908        "#
3909        .unindent(),
3910    );
3911
3912    // Block comments autoclose in JavaScript, but not HTML.
3913    cx.update_editor(|editor, cx| {
3914        editor.handle_input("/", cx);
3915        editor.handle_input("*", cx);
3916    });
3917    cx.assert_editor_state(
3918        &r#"
3919            <body>/*ˇ
3920                <script>
3921                    var x = 1;/*ˇ */
3922                </script>
3923            </body>/*ˇ
3924        "#
3925        .unindent(),
3926    );
3927}
3928
3929#[gpui::test]
3930async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
3931    init_test(cx, |_| {});
3932
3933    let mut cx = EditorTestContext::new(cx).await;
3934
3935    let rust_language = Arc::new(
3936        Language::new(
3937            LanguageConfig {
3938                name: "Rust".into(),
3939                brackets: serde_json::from_value(json!([
3940                    { "start": "{", "end": "}", "close": true, "newline": true },
3941                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
3942                ]))
3943                .unwrap(),
3944                autoclose_before: "})]>".into(),
3945                ..Default::default()
3946            },
3947            Some(tree_sitter_rust::language()),
3948        )
3949        .with_override_query("(string_literal) @string")
3950        .unwrap(),
3951    );
3952
3953    let registry = Arc::new(LanguageRegistry::test());
3954    registry.add(rust_language.clone());
3955
3956    cx.update_buffer(|buffer, cx| {
3957        buffer.set_language_registry(registry);
3958        buffer.set_language(Some(rust_language), cx);
3959    });
3960
3961    cx.set_state(
3962        &r#"
3963            let x = ˇ
3964        "#
3965        .unindent(),
3966    );
3967
3968    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
3969    cx.update_editor(|editor, cx| {
3970        editor.handle_input("\"", cx);
3971    });
3972    cx.assert_editor_state(
3973        &r#"
3974            let x = "ˇ"
3975        "#
3976        .unindent(),
3977    );
3978
3979    // Inserting another quotation mark. The cursor moves across the existing
3980    // automatically-inserted quotation mark.
3981    cx.update_editor(|editor, cx| {
3982        editor.handle_input("\"", cx);
3983    });
3984    cx.assert_editor_state(
3985        &r#"
3986            let x = ""ˇ
3987        "#
3988        .unindent(),
3989    );
3990
3991    // Reset
3992    cx.set_state(
3993        &r#"
3994            let x = ˇ
3995        "#
3996        .unindent(),
3997    );
3998
3999    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
4000    cx.update_editor(|editor, cx| {
4001        editor.handle_input("\"", cx);
4002        editor.handle_input(" ", cx);
4003        editor.move_left(&Default::default(), cx);
4004        editor.handle_input("\\", cx);
4005        editor.handle_input("\"", cx);
4006    });
4007    cx.assert_editor_state(
4008        &r#"
4009            let x = "\"ˇ "
4010        "#
4011        .unindent(),
4012    );
4013
4014    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
4015    // mark. Nothing is inserted.
4016    cx.update_editor(|editor, cx| {
4017        editor.move_right(&Default::default(), cx);
4018        editor.handle_input("\"", cx);
4019    });
4020    cx.assert_editor_state(
4021        &r#"
4022            let x = "\" "ˇ
4023        "#
4024        .unindent(),
4025    );
4026}
4027
4028#[gpui::test]
4029async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
4030    init_test(cx, |_| {});
4031
4032    let language = Arc::new(Language::new(
4033        LanguageConfig {
4034            brackets: BracketPairConfig {
4035                pairs: vec![
4036                    BracketPair {
4037                        start: "{".to_string(),
4038                        end: "}".to_string(),
4039                        close: true,
4040                        newline: true,
4041                    },
4042                    BracketPair {
4043                        start: "/* ".to_string(),
4044                        end: "*/".to_string(),
4045                        close: true,
4046                        ..Default::default()
4047                    },
4048                ],
4049                ..Default::default()
4050            },
4051            ..Default::default()
4052        },
4053        Some(tree_sitter_rust::language()),
4054    ));
4055
4056    let text = r#"
4057        a
4058        b
4059        c
4060    "#
4061    .unindent();
4062
4063    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
4064    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4065    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
4066    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
4067        .await;
4068
4069    view.update(cx, |view, cx| {
4070        view.change_selections(None, cx, |s| {
4071            s.select_display_ranges([
4072                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
4073                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
4074                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
4075            ])
4076        });
4077
4078        view.handle_input("{", cx);
4079        view.handle_input("{", cx);
4080        view.handle_input("{", cx);
4081        assert_eq!(
4082            view.text(cx),
4083            "
4084                {{{a}}}
4085                {{{b}}}
4086                {{{c}}}
4087            "
4088            .unindent()
4089        );
4090        assert_eq!(
4091            view.selections.display_ranges(cx),
4092            [
4093                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
4094                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
4095                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
4096            ]
4097        );
4098
4099        view.undo(&Undo, cx);
4100        view.undo(&Undo, cx);
4101        view.undo(&Undo, cx);
4102        assert_eq!(
4103            view.text(cx),
4104            "
4105                a
4106                b
4107                c
4108            "
4109            .unindent()
4110        );
4111        assert_eq!(
4112            view.selections.display_ranges(cx),
4113            [
4114                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
4115                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
4116                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
4117            ]
4118        );
4119
4120        // Ensure inserting the first character of a multi-byte bracket pair
4121        // doesn't surround the selections with the bracket.
4122        view.handle_input("/", cx);
4123        assert_eq!(
4124            view.text(cx),
4125            "
4126                /
4127                /
4128                /
4129            "
4130            .unindent()
4131        );
4132        assert_eq!(
4133            view.selections.display_ranges(cx),
4134            [
4135                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
4136                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
4137                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
4138            ]
4139        );
4140
4141        view.undo(&Undo, cx);
4142        assert_eq!(
4143            view.text(cx),
4144            "
4145                a
4146                b
4147                c
4148            "
4149            .unindent()
4150        );
4151        assert_eq!(
4152            view.selections.display_ranges(cx),
4153            [
4154                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
4155                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
4156                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
4157            ]
4158        );
4159
4160        // Ensure inserting the last character of a multi-byte bracket pair
4161        // doesn't surround the selections with the bracket.
4162        view.handle_input("*", cx);
4163        assert_eq!(
4164            view.text(cx),
4165            "
4166                *
4167                *
4168                *
4169            "
4170            .unindent()
4171        );
4172        assert_eq!(
4173            view.selections.display_ranges(cx),
4174            [
4175                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
4176                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
4177                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
4178            ]
4179        );
4180    });
4181}
4182
4183#[gpui::test]
4184async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
4185    init_test(cx, |_| {});
4186
4187    let language = Arc::new(Language::new(
4188        LanguageConfig {
4189            brackets: BracketPairConfig {
4190                pairs: vec![BracketPair {
4191                    start: "{".to_string(),
4192                    end: "}".to_string(),
4193                    close: true,
4194                    newline: true,
4195                }],
4196                ..Default::default()
4197            },
4198            autoclose_before: "}".to_string(),
4199            ..Default::default()
4200        },
4201        Some(tree_sitter_rust::language()),
4202    ));
4203
4204    let text = r#"
4205        a
4206        b
4207        c
4208    "#
4209    .unindent();
4210
4211    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
4212    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4213    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4214    editor
4215        .condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
4216        .await;
4217
4218    editor.update(cx, |editor, cx| {
4219        editor.change_selections(None, cx, |s| {
4220            s.select_ranges([
4221                Point::new(0, 1)..Point::new(0, 1),
4222                Point::new(1, 1)..Point::new(1, 1),
4223                Point::new(2, 1)..Point::new(2, 1),
4224            ])
4225        });
4226
4227        editor.handle_input("{", cx);
4228        editor.handle_input("{", cx);
4229        editor.handle_input("_", cx);
4230        assert_eq!(
4231            editor.text(cx),
4232            "
4233                a{{_}}
4234                b{{_}}
4235                c{{_}}
4236            "
4237            .unindent()
4238        );
4239        assert_eq!(
4240            editor.selections.ranges::<Point>(cx),
4241            [
4242                Point::new(0, 4)..Point::new(0, 4),
4243                Point::new(1, 4)..Point::new(1, 4),
4244                Point::new(2, 4)..Point::new(2, 4)
4245            ]
4246        );
4247
4248        editor.backspace(&Default::default(), cx);
4249        editor.backspace(&Default::default(), cx);
4250        assert_eq!(
4251            editor.text(cx),
4252            "
4253                a{}
4254                b{}
4255                c{}
4256            "
4257            .unindent()
4258        );
4259        assert_eq!(
4260            editor.selections.ranges::<Point>(cx),
4261            [
4262                Point::new(0, 2)..Point::new(0, 2),
4263                Point::new(1, 2)..Point::new(1, 2),
4264                Point::new(2, 2)..Point::new(2, 2)
4265            ]
4266        );
4267
4268        editor.delete_to_previous_word_start(&Default::default(), cx);
4269        assert_eq!(
4270            editor.text(cx),
4271            "
4272                a
4273                b
4274                c
4275            "
4276            .unindent()
4277        );
4278        assert_eq!(
4279            editor.selections.ranges::<Point>(cx),
4280            [
4281                Point::new(0, 1)..Point::new(0, 1),
4282                Point::new(1, 1)..Point::new(1, 1),
4283                Point::new(2, 1)..Point::new(2, 1)
4284            ]
4285        );
4286    });
4287}
4288
4289#[gpui::test]
4290async fn test_snippets(cx: &mut gpui::TestAppContext) {
4291    init_test(cx, |_| {});
4292
4293    let (text, insertion_ranges) = marked_text_ranges(
4294        indoc! {"
4295            a.ˇ b
4296            a.ˇ b
4297            a.ˇ b
4298        "},
4299        false,
4300    );
4301
4302    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
4303    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4304
4305    editor.update(cx, |editor, cx| {
4306        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
4307
4308        editor
4309            .insert_snippet(&insertion_ranges, snippet, cx)
4310            .unwrap();
4311
4312        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
4313            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
4314            assert_eq!(editor.text(cx), expected_text);
4315            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
4316        }
4317
4318        assert(
4319            editor,
4320            cx,
4321            indoc! {"
4322                a.f(«one», two, «three») b
4323                a.f(«one», two, «three») b
4324                a.f(«one», two, «three») b
4325            "},
4326        );
4327
4328        // Can't move earlier than the first tab stop
4329        assert!(!editor.move_to_prev_snippet_tabstop(cx));
4330        assert(
4331            editor,
4332            cx,
4333            indoc! {"
4334                a.f(«one», two, «three») b
4335                a.f(«one», two, «three») b
4336                a.f(«one», two, «three») b
4337            "},
4338        );
4339
4340        assert!(editor.move_to_next_snippet_tabstop(cx));
4341        assert(
4342            editor,
4343            cx,
4344            indoc! {"
4345                a.f(one, «two», three) b
4346                a.f(one, «two», three) b
4347                a.f(one, «two», three) b
4348            "},
4349        );
4350
4351        editor.move_to_prev_snippet_tabstop(cx);
4352        assert(
4353            editor,
4354            cx,
4355            indoc! {"
4356                a.f(«one», two, «three») b
4357                a.f(«one», two, «three») b
4358                a.f(«one», two, «three») b
4359            "},
4360        );
4361
4362        assert!(editor.move_to_next_snippet_tabstop(cx));
4363        assert(
4364            editor,
4365            cx,
4366            indoc! {"
4367                a.f(one, «two», three) b
4368                a.f(one, «two», three) b
4369                a.f(one, «two», three) b
4370            "},
4371        );
4372        assert!(editor.move_to_next_snippet_tabstop(cx));
4373        assert(
4374            editor,
4375            cx,
4376            indoc! {"
4377                a.f(one, two, three)ˇ b
4378                a.f(one, two, three)ˇ b
4379                a.f(one, two, three)ˇ b
4380            "},
4381        );
4382
4383        // As soon as the last tab stop is reached, snippet state is gone
4384        editor.move_to_prev_snippet_tabstop(cx);
4385        assert(
4386            editor,
4387            cx,
4388            indoc! {"
4389                a.f(one, two, three)ˇ b
4390                a.f(one, two, three)ˇ b
4391                a.f(one, two, three)ˇ b
4392            "},
4393        );
4394    });
4395}
4396
4397#[gpui::test]
4398async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
4399    init_test(cx, |_| {});
4400
4401    let mut language = Language::new(
4402        LanguageConfig {
4403            name: "Rust".into(),
4404            path_suffixes: vec!["rs".to_string()],
4405            ..Default::default()
4406        },
4407        Some(tree_sitter_rust::language()),
4408    );
4409    let mut fake_servers = language
4410        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4411            capabilities: lsp::ServerCapabilities {
4412                document_formatting_provider: Some(lsp::OneOf::Left(true)),
4413                ..Default::default()
4414            },
4415            ..Default::default()
4416        }))
4417        .await;
4418
4419    let fs = FakeFs::new(cx.background());
4420    fs.insert_file("/file.rs", Default::default()).await;
4421
4422    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4423    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4424    let buffer = project
4425        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4426        .await
4427        .unwrap();
4428
4429    cx.foreground().start_waiting();
4430    let fake_server = fake_servers.next().await.unwrap();
4431
4432    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4433    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4434    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4435    assert!(cx.read(|cx| editor.is_dirty(cx)));
4436
4437    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4438    fake_server
4439        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4440            assert_eq!(
4441                params.text_document.uri,
4442                lsp::Url::from_file_path("/file.rs").unwrap()
4443            );
4444            assert_eq!(params.options.tab_size, 4);
4445            Ok(Some(vec![lsp::TextEdit::new(
4446                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4447                ", ".to_string(),
4448            )]))
4449        })
4450        .next()
4451        .await;
4452    cx.foreground().start_waiting();
4453    save.await.unwrap();
4454    assert_eq!(
4455        editor.read_with(cx, |editor, cx| editor.text(cx)),
4456        "one, two\nthree\n"
4457    );
4458    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4459
4460    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4461    assert!(cx.read(|cx| editor.is_dirty(cx)));
4462
4463    // Ensure we can still save even if formatting hangs.
4464    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4465        assert_eq!(
4466            params.text_document.uri,
4467            lsp::Url::from_file_path("/file.rs").unwrap()
4468        );
4469        futures::future::pending::<()>().await;
4470        unreachable!()
4471    });
4472    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4473    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4474    cx.foreground().start_waiting();
4475    save.await.unwrap();
4476    assert_eq!(
4477        editor.read_with(cx, |editor, cx| editor.text(cx)),
4478        "one\ntwo\nthree\n"
4479    );
4480    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4481
4482    // Set rust language override and assert overridden tabsize is sent to language server
4483    update_test_settings(cx, |settings| {
4484        settings.languages.insert(
4485            "Rust".into(),
4486            LanguageSettingsContent {
4487                tab_size: NonZeroU32::new(8),
4488                ..Default::default()
4489            },
4490        );
4491    });
4492
4493    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4494    fake_server
4495        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4496            assert_eq!(
4497                params.text_document.uri,
4498                lsp::Url::from_file_path("/file.rs").unwrap()
4499            );
4500            assert_eq!(params.options.tab_size, 8);
4501            Ok(Some(vec![]))
4502        })
4503        .next()
4504        .await;
4505    cx.foreground().start_waiting();
4506    save.await.unwrap();
4507}
4508
4509#[gpui::test]
4510async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
4511    init_test(cx, |_| {});
4512
4513    let mut language = Language::new(
4514        LanguageConfig {
4515            name: "Rust".into(),
4516            path_suffixes: vec!["rs".to_string()],
4517            ..Default::default()
4518        },
4519        Some(tree_sitter_rust::language()),
4520    );
4521    let mut fake_servers = language
4522        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4523            capabilities: lsp::ServerCapabilities {
4524                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
4525                ..Default::default()
4526            },
4527            ..Default::default()
4528        }))
4529        .await;
4530
4531    let fs = FakeFs::new(cx.background());
4532    fs.insert_file("/file.rs", Default::default()).await;
4533
4534    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4535    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4536    let buffer = project
4537        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4538        .await
4539        .unwrap();
4540
4541    cx.foreground().start_waiting();
4542    let fake_server = fake_servers.next().await.unwrap();
4543
4544    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4545    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4546    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4547    assert!(cx.read(|cx| editor.is_dirty(cx)));
4548
4549    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4550    fake_server
4551        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
4552            assert_eq!(
4553                params.text_document.uri,
4554                lsp::Url::from_file_path("/file.rs").unwrap()
4555            );
4556            assert_eq!(params.options.tab_size, 4);
4557            Ok(Some(vec![lsp::TextEdit::new(
4558                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4559                ", ".to_string(),
4560            )]))
4561        })
4562        .next()
4563        .await;
4564    cx.foreground().start_waiting();
4565    save.await.unwrap();
4566    assert_eq!(
4567        editor.read_with(cx, |editor, cx| editor.text(cx)),
4568        "one, two\nthree\n"
4569    );
4570    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4571
4572    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4573    assert!(cx.read(|cx| editor.is_dirty(cx)));
4574
4575    // Ensure we can still save even if formatting hangs.
4576    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
4577        move |params, _| async move {
4578            assert_eq!(
4579                params.text_document.uri,
4580                lsp::Url::from_file_path("/file.rs").unwrap()
4581            );
4582            futures::future::pending::<()>().await;
4583            unreachable!()
4584        },
4585    );
4586    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4587    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4588    cx.foreground().start_waiting();
4589    save.await.unwrap();
4590    assert_eq!(
4591        editor.read_with(cx, |editor, cx| editor.text(cx)),
4592        "one\ntwo\nthree\n"
4593    );
4594    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4595
4596    // Set rust language override and assert overridden tabsize is sent to language server
4597    update_test_settings(cx, |settings| {
4598        settings.languages.insert(
4599            "Rust".into(),
4600            LanguageSettingsContent {
4601                tab_size: NonZeroU32::new(8),
4602                ..Default::default()
4603            },
4604        );
4605    });
4606
4607    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4608    fake_server
4609        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
4610            assert_eq!(
4611                params.text_document.uri,
4612                lsp::Url::from_file_path("/file.rs").unwrap()
4613            );
4614            assert_eq!(params.options.tab_size, 8);
4615            Ok(Some(vec![]))
4616        })
4617        .next()
4618        .await;
4619    cx.foreground().start_waiting();
4620    save.await.unwrap();
4621}
4622
4623#[gpui::test]
4624async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
4625    init_test(cx, |_| {});
4626
4627    let mut language = Language::new(
4628        LanguageConfig {
4629            name: "Rust".into(),
4630            path_suffixes: vec!["rs".to_string()],
4631            ..Default::default()
4632        },
4633        Some(tree_sitter_rust::language()),
4634    );
4635    let mut fake_servers = language
4636        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4637            capabilities: lsp::ServerCapabilities {
4638                document_formatting_provider: Some(lsp::OneOf::Left(true)),
4639                ..Default::default()
4640            },
4641            ..Default::default()
4642        }))
4643        .await;
4644
4645    let fs = FakeFs::new(cx.background());
4646    fs.insert_file("/file.rs", Default::default()).await;
4647
4648    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4649    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4650    let buffer = project
4651        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4652        .await
4653        .unwrap();
4654
4655    cx.foreground().start_waiting();
4656    let fake_server = fake_servers.next().await.unwrap();
4657
4658    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4659    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4660    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4661
4662    let format = editor.update(cx, |editor, cx| {
4663        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
4664    });
4665    fake_server
4666        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4667            assert_eq!(
4668                params.text_document.uri,
4669                lsp::Url::from_file_path("/file.rs").unwrap()
4670            );
4671            assert_eq!(params.options.tab_size, 4);
4672            Ok(Some(vec![lsp::TextEdit::new(
4673                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4674                ", ".to_string(),
4675            )]))
4676        })
4677        .next()
4678        .await;
4679    cx.foreground().start_waiting();
4680    format.await.unwrap();
4681    assert_eq!(
4682        editor.read_with(cx, |editor, cx| editor.text(cx)),
4683        "one, two\nthree\n"
4684    );
4685
4686    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4687    // Ensure we don't lock if formatting hangs.
4688    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4689        assert_eq!(
4690            params.text_document.uri,
4691            lsp::Url::from_file_path("/file.rs").unwrap()
4692        );
4693        futures::future::pending::<()>().await;
4694        unreachable!()
4695    });
4696    let format = editor.update(cx, |editor, cx| {
4697        editor.perform_format(project, FormatTrigger::Manual, cx)
4698    });
4699    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4700    cx.foreground().start_waiting();
4701    format.await.unwrap();
4702    assert_eq!(
4703        editor.read_with(cx, |editor, cx| editor.text(cx)),
4704        "one\ntwo\nthree\n"
4705    );
4706}
4707
4708#[gpui::test]
4709async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
4710    init_test(cx, |_| {});
4711
4712    let mut cx = EditorLspTestContext::new_rust(
4713        lsp::ServerCapabilities {
4714            document_formatting_provider: Some(lsp::OneOf::Left(true)),
4715            ..Default::default()
4716        },
4717        cx,
4718    )
4719    .await;
4720
4721    cx.set_state(indoc! {"
4722        one.twoˇ
4723    "});
4724
4725    // The format request takes a long time. When it completes, it inserts
4726    // a newline and an indent before the `.`
4727    cx.lsp
4728        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
4729            let executor = cx.background();
4730            async move {
4731                executor.timer(Duration::from_millis(100)).await;
4732                Ok(Some(vec![lsp::TextEdit {
4733                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
4734                    new_text: "\n    ".into(),
4735                }]))
4736            }
4737        });
4738
4739    // Submit a format request.
4740    let format_1 = cx
4741        .update_editor(|editor, cx| editor.format(&Format, cx))
4742        .unwrap();
4743    cx.foreground().run_until_parked();
4744
4745    // Submit a second format request.
4746    let format_2 = cx
4747        .update_editor(|editor, cx| editor.format(&Format, cx))
4748        .unwrap();
4749    cx.foreground().run_until_parked();
4750
4751    // Wait for both format requests to complete
4752    cx.foreground().advance_clock(Duration::from_millis(200));
4753    cx.foreground().start_waiting();
4754    format_1.await.unwrap();
4755    cx.foreground().start_waiting();
4756    format_2.await.unwrap();
4757
4758    // The formatting edits only happens once.
4759    cx.assert_editor_state(indoc! {"
4760        one
4761            .twoˇ
4762    "});
4763}
4764
4765#[gpui::test]
4766async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
4767    init_test(cx, |_| {});
4768
4769    let mut cx = EditorLspTestContext::new_rust(
4770        lsp::ServerCapabilities {
4771            document_formatting_provider: Some(lsp::OneOf::Left(true)),
4772            ..Default::default()
4773        },
4774        cx,
4775    )
4776    .await;
4777
4778    // Set up a buffer white some trailing whitespace and no trailing newline.
4779    cx.set_state(
4780        &[
4781            "one ",   //
4782            "twoˇ",   //
4783            "three ", //
4784            "four",   //
4785        ]
4786        .join("\n"),
4787    );
4788
4789    // Submit a format request.
4790    let format = cx
4791        .update_editor(|editor, cx| editor.format(&Format, cx))
4792        .unwrap();
4793
4794    // Record which buffer changes have been sent to the language server
4795    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
4796    cx.lsp
4797        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
4798            let buffer_changes = buffer_changes.clone();
4799            move |params, _| {
4800                buffer_changes.lock().extend(
4801                    params
4802                        .content_changes
4803                        .into_iter()
4804                        .map(|e| (e.range.unwrap(), e.text)),
4805                );
4806            }
4807        });
4808
4809    // Handle formatting requests to the language server.
4810    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
4811        let buffer_changes = buffer_changes.clone();
4812        move |_, _| {
4813            // When formatting is requested, trailing whitespace has already been stripped,
4814            // and the trailing newline has already been added.
4815            assert_eq!(
4816                &buffer_changes.lock()[1..],
4817                &[
4818                    (
4819                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
4820                        "".into()
4821                    ),
4822                    (
4823                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
4824                        "".into()
4825                    ),
4826                    (
4827                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
4828                        "\n".into()
4829                    ),
4830                ]
4831            );
4832
4833            // Insert blank lines between each line of the buffer.
4834            async move {
4835                Ok(Some(vec![
4836                    lsp::TextEdit {
4837                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
4838                        new_text: "\n".into(),
4839                    },
4840                    lsp::TextEdit {
4841                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
4842                        new_text: "\n".into(),
4843                    },
4844                ]))
4845            }
4846        }
4847    });
4848
4849    // After formatting the buffer, the trailing whitespace is stripped,
4850    // a newline is appended, and the edits provided by the language server
4851    // have been applied.
4852    format.await.unwrap();
4853    cx.assert_editor_state(
4854        &[
4855            "one",   //
4856            "",      //
4857            "twoˇ",  //
4858            "",      //
4859            "three", //
4860            "four",  //
4861            "",      //
4862        ]
4863        .join("\n"),
4864    );
4865
4866    // Undoing the formatting undoes the trailing whitespace removal, the
4867    // trailing newline, and the LSP edits.
4868    cx.update_buffer(|buffer, cx| buffer.undo(cx));
4869    cx.assert_editor_state(
4870        &[
4871            "one ",   //
4872            "twoˇ",   //
4873            "three ", //
4874            "four",   //
4875        ]
4876        .join("\n"),
4877    );
4878}
4879
4880#[gpui::test]
4881async fn test_completion(cx: &mut gpui::TestAppContext) {
4882    init_test(cx, |_| {});
4883
4884    let mut cx = EditorLspTestContext::new_rust(
4885        lsp::ServerCapabilities {
4886            completion_provider: Some(lsp::CompletionOptions {
4887                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
4888                ..Default::default()
4889            }),
4890            ..Default::default()
4891        },
4892        cx,
4893    )
4894    .await;
4895
4896    cx.set_state(indoc! {"
4897        oneˇ
4898        two
4899        three
4900    "});
4901    cx.simulate_keystroke(".");
4902    handle_completion_request(
4903        &mut cx,
4904        indoc! {"
4905            one.|<>
4906            two
4907            three
4908        "},
4909        vec!["first_completion", "second_completion"],
4910    )
4911    .await;
4912    cx.condition(|editor, _| editor.context_menu_visible())
4913        .await;
4914    let apply_additional_edits = cx.update_editor(|editor, cx| {
4915        editor.move_down(&MoveDown, cx);
4916        editor
4917            .confirm_completion(&ConfirmCompletion::default(), cx)
4918            .unwrap()
4919    });
4920    cx.assert_editor_state(indoc! {"
4921        one.second_completionˇ
4922        two
4923        three
4924    "});
4925
4926    handle_resolve_completion_request(
4927        &mut cx,
4928        Some(vec![
4929            (
4930                //This overlaps with the primary completion edit which is
4931                //misbehavior from the LSP spec, test that we filter it out
4932                indoc! {"
4933                    one.second_ˇcompletion
4934                    two
4935                    threeˇ
4936                "},
4937                "overlapping additional edit",
4938            ),
4939            (
4940                indoc! {"
4941                    one.second_completion
4942                    two
4943                    threeˇ
4944                "},
4945                "\nadditional edit",
4946            ),
4947        ]),
4948    )
4949    .await;
4950    apply_additional_edits.await.unwrap();
4951    cx.assert_editor_state(indoc! {"
4952        one.second_completionˇ
4953        two
4954        three
4955        additional edit
4956    "});
4957
4958    cx.set_state(indoc! {"
4959        one.second_completion
4960        twoˇ
4961        threeˇ
4962        additional edit
4963    "});
4964    cx.simulate_keystroke(" ");
4965    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4966    cx.simulate_keystroke("s");
4967    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4968
4969    cx.assert_editor_state(indoc! {"
4970        one.second_completion
4971        two sˇ
4972        three sˇ
4973        additional edit
4974    "});
4975    handle_completion_request(
4976        &mut cx,
4977        indoc! {"
4978            one.second_completion
4979            two s
4980            three <s|>
4981            additional edit
4982        "},
4983        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
4984    )
4985    .await;
4986    cx.condition(|editor, _| editor.context_menu_visible())
4987        .await;
4988
4989    cx.simulate_keystroke("i");
4990
4991    handle_completion_request(
4992        &mut cx,
4993        indoc! {"
4994            one.second_completion
4995            two si
4996            three <si|>
4997            additional edit
4998        "},
4999        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
5000    )
5001    .await;
5002    cx.condition(|editor, _| editor.context_menu_visible())
5003        .await;
5004
5005    let apply_additional_edits = cx.update_editor(|editor, cx| {
5006        editor
5007            .confirm_completion(&ConfirmCompletion::default(), cx)
5008            .unwrap()
5009    });
5010    cx.assert_editor_state(indoc! {"
5011        one.second_completion
5012        two sixth_completionˇ
5013        three sixth_completionˇ
5014        additional edit
5015    "});
5016
5017    handle_resolve_completion_request(&mut cx, None).await;
5018    apply_additional_edits.await.unwrap();
5019
5020    cx.update(|cx| {
5021        cx.update_global::<SettingsStore, _, _>(|settings, cx| {
5022            settings.update_user_settings::<EditorSettings>(cx, |settings| {
5023                settings.show_completions_on_input = Some(false);
5024            });
5025        })
5026    });
5027    cx.set_state("editorˇ");
5028    cx.simulate_keystroke(".");
5029    assert!(cx.editor(|e, _| e.context_menu.is_none()));
5030    cx.simulate_keystroke("c");
5031    cx.simulate_keystroke("l");
5032    cx.simulate_keystroke("o");
5033    cx.assert_editor_state("editor.cloˇ");
5034    assert!(cx.editor(|e, _| e.context_menu.is_none()));
5035    cx.update_editor(|editor, cx| {
5036        editor.show_completions(&ShowCompletions, cx);
5037    });
5038    handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
5039    cx.condition(|editor, _| editor.context_menu_visible())
5040        .await;
5041    let apply_additional_edits = cx.update_editor(|editor, cx| {
5042        editor
5043            .confirm_completion(&ConfirmCompletion::default(), cx)
5044            .unwrap()
5045    });
5046    cx.assert_editor_state("editor.closeˇ");
5047    handle_resolve_completion_request(&mut cx, None).await;
5048    apply_additional_edits.await.unwrap();
5049}
5050
5051#[gpui::test]
5052async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
5053    init_test(cx, |_| {});
5054    let mut cx = EditorTestContext::new(cx).await;
5055    let language = Arc::new(Language::new(
5056        LanguageConfig {
5057            line_comment: Some("// ".into()),
5058            ..Default::default()
5059        },
5060        Some(tree_sitter_rust::language()),
5061    ));
5062    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
5063
5064    // If multiple selections intersect a line, the line is only toggled once.
5065    cx.set_state(indoc! {"
5066        fn a() {
5067            «//b();
5068            ˇ»// «c();
5069            //ˇ»  d();
5070        }
5071    "});
5072
5073    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5074
5075    cx.assert_editor_state(indoc! {"
5076        fn a() {
5077            «b();
5078            c();
5079            ˇ» d();
5080        }
5081    "});
5082
5083    // The comment prefix is inserted at the same column for every line in a
5084    // selection.
5085    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5086
5087    cx.assert_editor_state(indoc! {"
5088        fn a() {
5089            // «b();
5090            // c();
5091            ˇ»//  d();
5092        }
5093    "});
5094
5095    // If a selection ends at the beginning of a line, that line is not toggled.
5096    cx.set_selections_state(indoc! {"
5097        fn a() {
5098            // b();
5099            «// c();
5100        ˇ»    //  d();
5101        }
5102    "});
5103
5104    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5105
5106    cx.assert_editor_state(indoc! {"
5107        fn a() {
5108            // b();
5109            «c();
5110        ˇ»    //  d();
5111        }
5112    "});
5113
5114    // If a selection span a single line and is empty, the line is toggled.
5115    cx.set_state(indoc! {"
5116        fn a() {
5117            a();
5118            b();
5119        ˇ
5120        }
5121    "});
5122
5123    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5124
5125    cx.assert_editor_state(indoc! {"
5126        fn a() {
5127            a();
5128            b();
5129        //•ˇ
5130        }
5131    "});
5132
5133    // If a selection span multiple lines, empty lines are not toggled.
5134    cx.set_state(indoc! {"
5135        fn a() {
5136            «a();
5137
5138            c();ˇ»
5139        }
5140    "});
5141
5142    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5143
5144    cx.assert_editor_state(indoc! {"
5145        fn a() {
5146            // «a();
5147
5148            // c();ˇ»
5149        }
5150    "});
5151}
5152
5153#[gpui::test]
5154async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
5155    init_test(cx, |_| {});
5156
5157    let language = Arc::new(Language::new(
5158        LanguageConfig {
5159            line_comment: Some("// ".into()),
5160            ..Default::default()
5161        },
5162        Some(tree_sitter_rust::language()),
5163    ));
5164
5165    let registry = Arc::new(LanguageRegistry::test());
5166    registry.add(language.clone());
5167
5168    let mut cx = EditorTestContext::new(cx).await;
5169    cx.update_buffer(|buffer, cx| {
5170        buffer.set_language_registry(registry);
5171        buffer.set_language(Some(language), cx);
5172    });
5173
5174    let toggle_comments = &ToggleComments {
5175        advance_downwards: true,
5176    };
5177
5178    // Single cursor on one line -> advance
5179    // Cursor moves horizontally 3 characters as well on non-blank line
5180    cx.set_state(indoc!(
5181        "fn a() {
5182             ˇdog();
5183             cat();
5184        }"
5185    ));
5186    cx.update_editor(|editor, cx| {
5187        editor.toggle_comments(toggle_comments, cx);
5188    });
5189    cx.assert_editor_state(indoc!(
5190        "fn a() {
5191             // dog();
5192             catˇ();
5193        }"
5194    ));
5195
5196    // Single selection on one line -> don't advance
5197    cx.set_state(indoc!(
5198        "fn a() {
5199             «dog()ˇ»;
5200             cat();
5201        }"
5202    ));
5203    cx.update_editor(|editor, cx| {
5204        editor.toggle_comments(toggle_comments, cx);
5205    });
5206    cx.assert_editor_state(indoc!(
5207        "fn a() {
5208             // «dog()ˇ»;
5209             cat();
5210        }"
5211    ));
5212
5213    // Multiple cursors on one line -> advance
5214    cx.set_state(indoc!(
5215        "fn a() {
5216             ˇdˇog();
5217             cat();
5218        }"
5219    ));
5220    cx.update_editor(|editor, cx| {
5221        editor.toggle_comments(toggle_comments, cx);
5222    });
5223    cx.assert_editor_state(indoc!(
5224        "fn a() {
5225             // dog();
5226             catˇ(ˇ);
5227        }"
5228    ));
5229
5230    // Multiple cursors on one line, with selection -> don't advance
5231    cx.set_state(indoc!(
5232        "fn a() {
5233             ˇdˇog«()ˇ»;
5234             cat();
5235        }"
5236    ));
5237    cx.update_editor(|editor, cx| {
5238        editor.toggle_comments(toggle_comments, cx);
5239    });
5240    cx.assert_editor_state(indoc!(
5241        "fn a() {
5242             // ˇdˇog«()ˇ»;
5243             cat();
5244        }"
5245    ));
5246
5247    // Single cursor on one line -> advance
5248    // Cursor moves to column 0 on blank line
5249    cx.set_state(indoc!(
5250        "fn a() {
5251             ˇdog();
5252
5253             cat();
5254        }"
5255    ));
5256    cx.update_editor(|editor, cx| {
5257        editor.toggle_comments(toggle_comments, cx);
5258    });
5259    cx.assert_editor_state(indoc!(
5260        "fn a() {
5261             // dog();
5262        ˇ
5263             cat();
5264        }"
5265    ));
5266
5267    // Single cursor on one line -> advance
5268    // Cursor starts and ends at column 0
5269    cx.set_state(indoc!(
5270        "fn a() {
5271         ˇ    dog();
5272             cat();
5273        }"
5274    ));
5275    cx.update_editor(|editor, cx| {
5276        editor.toggle_comments(toggle_comments, cx);
5277    });
5278    cx.assert_editor_state(indoc!(
5279        "fn a() {
5280             // dog();
5281         ˇ    cat();
5282        }"
5283    ));
5284}
5285
5286#[gpui::test]
5287async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
5288    init_test(cx, |_| {});
5289
5290    let mut cx = EditorTestContext::new(cx).await;
5291
5292    let html_language = Arc::new(
5293        Language::new(
5294            LanguageConfig {
5295                name: "HTML".into(),
5296                block_comment: Some(("<!-- ".into(), " -->".into())),
5297                ..Default::default()
5298            },
5299            Some(tree_sitter_html::language()),
5300        )
5301        .with_injection_query(
5302            r#"
5303            (script_element
5304                (raw_text) @content
5305                (#set! "language" "javascript"))
5306            "#,
5307        )
5308        .unwrap(),
5309    );
5310
5311    let javascript_language = Arc::new(Language::new(
5312        LanguageConfig {
5313            name: "JavaScript".into(),
5314            line_comment: Some("// ".into()),
5315            ..Default::default()
5316        },
5317        Some(tree_sitter_javascript::language()),
5318    ));
5319
5320    let registry = Arc::new(LanguageRegistry::test());
5321    registry.add(html_language.clone());
5322    registry.add(javascript_language.clone());
5323
5324    cx.update_buffer(|buffer, cx| {
5325        buffer.set_language_registry(registry);
5326        buffer.set_language(Some(html_language), cx);
5327    });
5328
5329    // Toggle comments for empty selections
5330    cx.set_state(
5331        &r#"
5332            <p>A</p>ˇ
5333            <p>B</p>ˇ
5334            <p>C</p>ˇ
5335        "#
5336        .unindent(),
5337    );
5338    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5339    cx.assert_editor_state(
5340        &r#"
5341            <!-- <p>A</p>ˇ -->
5342            <!-- <p>B</p>ˇ -->
5343            <!-- <p>C</p>ˇ -->
5344        "#
5345        .unindent(),
5346    );
5347    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5348    cx.assert_editor_state(
5349        &r#"
5350            <p>A</p>ˇ
5351            <p>B</p>ˇ
5352            <p>C</p>ˇ
5353        "#
5354        .unindent(),
5355    );
5356
5357    // Toggle comments for mixture of empty and non-empty selections, where
5358    // multiple selections occupy a given line.
5359    cx.set_state(
5360        &r#"
5361            <p>A«</p>
5362            <p>ˇ»B</p>ˇ
5363            <p>C«</p>
5364            <p>ˇ»D</p>ˇ
5365        "#
5366        .unindent(),
5367    );
5368
5369    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5370    cx.assert_editor_state(
5371        &r#"
5372            <!-- <p>A«</p>
5373            <p>ˇ»B</p>ˇ -->
5374            <!-- <p>C«</p>
5375            <p>ˇ»D</p>ˇ -->
5376        "#
5377        .unindent(),
5378    );
5379    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5380    cx.assert_editor_state(
5381        &r#"
5382            <p>A«</p>
5383            <p>ˇ»B</p>ˇ
5384            <p>C«</p>
5385            <p>ˇ»D</p>ˇ
5386        "#
5387        .unindent(),
5388    );
5389
5390    // Toggle comments when different languages are active for different
5391    // selections.
5392    cx.set_state(
5393        &r#"
5394            ˇ<script>
5395                ˇvar x = new Y();
5396            ˇ</script>
5397        "#
5398        .unindent(),
5399    );
5400    cx.foreground().run_until_parked();
5401    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5402    cx.assert_editor_state(
5403        &r#"
5404            <!-- ˇ<script> -->
5405                // ˇvar x = new Y();
5406            <!-- ˇ</script> -->
5407        "#
5408        .unindent(),
5409    );
5410}
5411
5412#[gpui::test]
5413fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
5414    init_test(cx, |_| {});
5415
5416    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5417    let multibuffer = cx.add_model(|cx| {
5418        let mut multibuffer = MultiBuffer::new(0);
5419        multibuffer.push_excerpts(
5420            buffer.clone(),
5421            [
5422                ExcerptRange {
5423                    context: Point::new(0, 0)..Point::new(0, 4),
5424                    primary: None,
5425                },
5426                ExcerptRange {
5427                    context: Point::new(1, 0)..Point::new(1, 4),
5428                    primary: None,
5429                },
5430            ],
5431            cx,
5432        );
5433        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
5434        multibuffer
5435    });
5436
5437    let (_, view) = cx.add_window(|cx| build_editor(multibuffer, cx));
5438    view.update(cx, |view, cx| {
5439        assert_eq!(view.text(cx), "aaaa\nbbbb");
5440        view.change_selections(None, cx, |s| {
5441            s.select_ranges([
5442                Point::new(0, 0)..Point::new(0, 0),
5443                Point::new(1, 0)..Point::new(1, 0),
5444            ])
5445        });
5446
5447        view.handle_input("X", cx);
5448        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
5449        assert_eq!(
5450            view.selections.ranges(cx),
5451            [
5452                Point::new(0, 1)..Point::new(0, 1),
5453                Point::new(1, 1)..Point::new(1, 1),
5454            ]
5455        );
5456
5457        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
5458        view.change_selections(None, cx, |s| {
5459            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
5460        });
5461        view.backspace(&Default::default(), cx);
5462        assert_eq!(view.text(cx), "Xa\nbbb");
5463        assert_eq!(
5464            view.selections.ranges(cx),
5465            [Point::new(1, 0)..Point::new(1, 0)]
5466        );
5467
5468        view.change_selections(None, cx, |s| {
5469            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
5470        });
5471        view.backspace(&Default::default(), cx);
5472        assert_eq!(view.text(cx), "X\nbb");
5473        assert_eq!(
5474            view.selections.ranges(cx),
5475            [Point::new(0, 1)..Point::new(0, 1)]
5476        );
5477    });
5478}
5479
5480#[gpui::test]
5481fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
5482    init_test(cx, |_| {});
5483
5484    let markers = vec![('[', ']').into(), ('(', ')').into()];
5485    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
5486        indoc! {"
5487            [aaaa
5488            (bbbb]
5489            cccc)",
5490        },
5491        markers.clone(),
5492    );
5493    let excerpt_ranges = markers.into_iter().map(|marker| {
5494        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
5495        ExcerptRange {
5496            context,
5497            primary: None,
5498        }
5499    });
5500    let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
5501    let multibuffer = cx.add_model(|cx| {
5502        let mut multibuffer = MultiBuffer::new(0);
5503        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
5504        multibuffer
5505    });
5506
5507    let (_, view) = cx.add_window(|cx| build_editor(multibuffer, cx));
5508    view.update(cx, |view, cx| {
5509        let (expected_text, selection_ranges) = marked_text_ranges(
5510            indoc! {"
5511                aaaa
5512                bˇbbb
5513                bˇbbˇb
5514                cccc"
5515            },
5516            true,
5517        );
5518        assert_eq!(view.text(cx), expected_text);
5519        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
5520
5521        view.handle_input("X", cx);
5522
5523        let (expected_text, expected_selections) = marked_text_ranges(
5524            indoc! {"
5525                aaaa
5526                bXˇbbXb
5527                bXˇbbXˇb
5528                cccc"
5529            },
5530            false,
5531        );
5532        assert_eq!(view.text(cx), expected_text);
5533        assert_eq!(view.selections.ranges(cx), expected_selections);
5534
5535        view.newline(&Newline, cx);
5536        let (expected_text, expected_selections) = marked_text_ranges(
5537            indoc! {"
5538                aaaa
5539                bX
5540                ˇbbX
5541                b
5542                bX
5543                ˇbbX
5544                ˇb
5545                cccc"
5546            },
5547            false,
5548        );
5549        assert_eq!(view.text(cx), expected_text);
5550        assert_eq!(view.selections.ranges(cx), expected_selections);
5551    });
5552}
5553
5554#[gpui::test]
5555fn test_refresh_selections(cx: &mut TestAppContext) {
5556    init_test(cx, |_| {});
5557
5558    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5559    let mut excerpt1_id = None;
5560    let multibuffer = cx.add_model(|cx| {
5561        let mut multibuffer = MultiBuffer::new(0);
5562        excerpt1_id = multibuffer
5563            .push_excerpts(
5564                buffer.clone(),
5565                [
5566                    ExcerptRange {
5567                        context: Point::new(0, 0)..Point::new(1, 4),
5568                        primary: None,
5569                    },
5570                    ExcerptRange {
5571                        context: Point::new(1, 0)..Point::new(2, 4),
5572                        primary: None,
5573                    },
5574                ],
5575                cx,
5576            )
5577            .into_iter()
5578            .next();
5579        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
5580        multibuffer
5581    });
5582
5583    let (_, editor) = cx.add_window(|cx| {
5584        let mut editor = build_editor(multibuffer.clone(), cx);
5585        let snapshot = editor.snapshot(cx);
5586        editor.change_selections(None, cx, |s| {
5587            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
5588        });
5589        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
5590        assert_eq!(
5591            editor.selections.ranges(cx),
5592            [
5593                Point::new(1, 3)..Point::new(1, 3),
5594                Point::new(2, 1)..Point::new(2, 1),
5595            ]
5596        );
5597        editor
5598    });
5599
5600    // Refreshing selections is a no-op when excerpts haven't changed.
5601    editor.update(cx, |editor, cx| {
5602        editor.change_selections(None, cx, |s| s.refresh());
5603        assert_eq!(
5604            editor.selections.ranges(cx),
5605            [
5606                Point::new(1, 3)..Point::new(1, 3),
5607                Point::new(2, 1)..Point::new(2, 1),
5608            ]
5609        );
5610    });
5611
5612    multibuffer.update(cx, |multibuffer, cx| {
5613        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
5614    });
5615    editor.update(cx, |editor, cx| {
5616        // Removing an excerpt causes the first selection to become degenerate.
5617        assert_eq!(
5618            editor.selections.ranges(cx),
5619            [
5620                Point::new(0, 0)..Point::new(0, 0),
5621                Point::new(0, 1)..Point::new(0, 1)
5622            ]
5623        );
5624
5625        // Refreshing selections will relocate the first selection to the original buffer
5626        // location.
5627        editor.change_selections(None, cx, |s| s.refresh());
5628        assert_eq!(
5629            editor.selections.ranges(cx),
5630            [
5631                Point::new(0, 1)..Point::new(0, 1),
5632                Point::new(0, 3)..Point::new(0, 3)
5633            ]
5634        );
5635        assert!(editor.selections.pending_anchor().is_some());
5636    });
5637}
5638
5639#[gpui::test]
5640fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
5641    init_test(cx, |_| {});
5642
5643    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5644    let mut excerpt1_id = None;
5645    let multibuffer = cx.add_model(|cx| {
5646        let mut multibuffer = MultiBuffer::new(0);
5647        excerpt1_id = multibuffer
5648            .push_excerpts(
5649                buffer.clone(),
5650                [
5651                    ExcerptRange {
5652                        context: Point::new(0, 0)..Point::new(1, 4),
5653                        primary: None,
5654                    },
5655                    ExcerptRange {
5656                        context: Point::new(1, 0)..Point::new(2, 4),
5657                        primary: None,
5658                    },
5659                ],
5660                cx,
5661            )
5662            .into_iter()
5663            .next();
5664        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
5665        multibuffer
5666    });
5667
5668    let (_, editor) = cx.add_window(|cx| {
5669        let mut editor = build_editor(multibuffer.clone(), cx);
5670        let snapshot = editor.snapshot(cx);
5671        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
5672        assert_eq!(
5673            editor.selections.ranges(cx),
5674            [Point::new(1, 3)..Point::new(1, 3)]
5675        );
5676        editor
5677    });
5678
5679    multibuffer.update(cx, |multibuffer, cx| {
5680        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
5681    });
5682    editor.update(cx, |editor, cx| {
5683        assert_eq!(
5684            editor.selections.ranges(cx),
5685            [Point::new(0, 0)..Point::new(0, 0)]
5686        );
5687
5688        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
5689        editor.change_selections(None, cx, |s| s.refresh());
5690        assert_eq!(
5691            editor.selections.ranges(cx),
5692            [Point::new(0, 3)..Point::new(0, 3)]
5693        );
5694        assert!(editor.selections.pending_anchor().is_some());
5695    });
5696}
5697
5698#[gpui::test]
5699async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
5700    init_test(cx, |_| {});
5701
5702    let language = Arc::new(
5703        Language::new(
5704            LanguageConfig {
5705                brackets: BracketPairConfig {
5706                    pairs: vec![
5707                        BracketPair {
5708                            start: "{".to_string(),
5709                            end: "}".to_string(),
5710                            close: true,
5711                            newline: true,
5712                        },
5713                        BracketPair {
5714                            start: "/* ".to_string(),
5715                            end: " */".to_string(),
5716                            close: true,
5717                            newline: true,
5718                        },
5719                    ],
5720                    ..Default::default()
5721                },
5722                ..Default::default()
5723            },
5724            Some(tree_sitter_rust::language()),
5725        )
5726        .with_indents_query("")
5727        .unwrap(),
5728    );
5729
5730    let text = concat!(
5731        "{   }\n",     //
5732        "  x\n",       //
5733        "  /*   */\n", //
5734        "x\n",         //
5735        "{{} }\n",     //
5736    );
5737
5738    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
5739    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
5740    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
5741    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
5742        .await;
5743
5744    view.update(cx, |view, cx| {
5745        view.change_selections(None, cx, |s| {
5746            s.select_display_ranges([
5747                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
5748                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
5749                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
5750            ])
5751        });
5752        view.newline(&Newline, cx);
5753
5754        assert_eq!(
5755            view.buffer().read(cx).read(cx).text(),
5756            concat!(
5757                "{ \n",    // Suppress rustfmt
5758                "\n",      //
5759                "}\n",     //
5760                "  x\n",   //
5761                "  /* \n", //
5762                "  \n",    //
5763                "  */\n",  //
5764                "x\n",     //
5765                "{{} \n",  //
5766                "}\n",     //
5767            )
5768        );
5769    });
5770}
5771
5772#[gpui::test]
5773fn test_highlighted_ranges(cx: &mut TestAppContext) {
5774    init_test(cx, |_| {});
5775
5776    let (_, editor) = cx.add_window(|cx| {
5777        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
5778        build_editor(buffer.clone(), cx)
5779    });
5780
5781    editor.update(cx, |editor, cx| {
5782        struct Type1;
5783        struct Type2;
5784
5785        let buffer = editor.buffer.read(cx).snapshot(cx);
5786
5787        let anchor_range =
5788            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
5789
5790        editor.highlight_background::<Type1>(
5791            vec![
5792                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
5793                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
5794                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
5795                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
5796            ],
5797            |_| Color::red(),
5798            cx,
5799        );
5800        editor.highlight_background::<Type2>(
5801            vec![
5802                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
5803                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
5804                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
5805                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
5806            ],
5807            |_| Color::green(),
5808            cx,
5809        );
5810
5811        let snapshot = editor.snapshot(cx);
5812        let mut highlighted_ranges = editor.background_highlights_in_range(
5813            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
5814            &snapshot,
5815            theme::current(cx).as_ref(),
5816        );
5817        // Enforce a consistent ordering based on color without relying on the ordering of the
5818        // highlight's `TypeId` which is non-deterministic.
5819        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
5820        assert_eq!(
5821            highlighted_ranges,
5822            &[
5823                (
5824                    DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
5825                    Color::green(),
5826                ),
5827                (
5828                    DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
5829                    Color::green(),
5830                ),
5831                (
5832                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
5833                    Color::red(),
5834                ),
5835                (
5836                    DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
5837                    Color::red(),
5838                ),
5839            ]
5840        );
5841        assert_eq!(
5842            editor.background_highlights_in_range(
5843                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
5844                &snapshot,
5845                theme::current(cx).as_ref(),
5846            ),
5847            &[(
5848                DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
5849                Color::red(),
5850            )]
5851        );
5852    });
5853}
5854
5855#[gpui::test]
5856async fn test_following(cx: &mut gpui::TestAppContext) {
5857    init_test(cx, |_| {});
5858
5859    let fs = FakeFs::new(cx.background());
5860    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5861
5862    let buffer = project.update(cx, |project, cx| {
5863        let buffer = project
5864            .create_buffer(&sample_text(16, 8, 'a'), None, cx)
5865            .unwrap();
5866        cx.add_model(|cx| MultiBuffer::singleton(buffer, cx))
5867    });
5868    let (_, leader) = cx.add_window(|cx| build_editor(buffer.clone(), cx));
5869    let (_, follower) = cx.update(|cx| {
5870        cx.add_window(
5871            WindowOptions {
5872                bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
5873                ..Default::default()
5874            },
5875            |cx| build_editor(buffer.clone(), cx),
5876        )
5877    });
5878
5879    let is_still_following = Rc::new(RefCell::new(true));
5880    let follower_edit_event_count = Rc::new(RefCell::new(0));
5881    let pending_update = Rc::new(RefCell::new(None));
5882    follower.update(cx, {
5883        let update = pending_update.clone();
5884        let is_still_following = is_still_following.clone();
5885        let follower_edit_event_count = follower_edit_event_count.clone();
5886        |_, cx| {
5887            cx.subscribe(&leader, move |_, leader, event, cx| {
5888                leader
5889                    .read(cx)
5890                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
5891            })
5892            .detach();
5893
5894            cx.subscribe(&follower, move |_, _, event, cx| {
5895                if Editor::should_unfollow_on_event(event, cx) {
5896                    *is_still_following.borrow_mut() = false;
5897                }
5898                if let Event::BufferEdited = event {
5899                    *follower_edit_event_count.borrow_mut() += 1;
5900                }
5901            })
5902            .detach();
5903        }
5904    });
5905
5906    // Update the selections only
5907    leader.update(cx, |leader, cx| {
5908        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
5909    });
5910    follower
5911        .update(cx, |follower, cx| {
5912            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5913        })
5914        .await
5915        .unwrap();
5916    follower.read_with(cx, |follower, cx| {
5917        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
5918    });
5919    assert_eq!(*is_still_following.borrow(), true);
5920    assert_eq!(*follower_edit_event_count.borrow(), 0);
5921
5922    // Update the scroll position only
5923    leader.update(cx, |leader, cx| {
5924        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
5925    });
5926    follower
5927        .update(cx, |follower, cx| {
5928            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5929        })
5930        .await
5931        .unwrap();
5932    assert_eq!(
5933        follower.update(cx, |follower, cx| follower.scroll_position(cx)),
5934        vec2f(1.5, 3.5)
5935    );
5936    assert_eq!(*is_still_following.borrow(), true);
5937    assert_eq!(*follower_edit_event_count.borrow(), 0);
5938
5939    // Update the selections and scroll position. The follower's scroll position is updated
5940    // via autoscroll, not via the leader's exact scroll position.
5941    leader.update(cx, |leader, cx| {
5942        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
5943        leader.request_autoscroll(Autoscroll::newest(), cx);
5944        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
5945    });
5946    follower
5947        .update(cx, |follower, cx| {
5948            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5949        })
5950        .await
5951        .unwrap();
5952    follower.update(cx, |follower, cx| {
5953        assert_eq!(follower.scroll_position(cx), vec2f(1.5, 0.0));
5954        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
5955    });
5956    assert_eq!(*is_still_following.borrow(), true);
5957
5958    // Creating a pending selection that precedes another selection
5959    leader.update(cx, |leader, cx| {
5960        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
5961        leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
5962    });
5963    follower
5964        .update(cx, |follower, cx| {
5965            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5966        })
5967        .await
5968        .unwrap();
5969    follower.read_with(cx, |follower, cx| {
5970        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
5971    });
5972    assert_eq!(*is_still_following.borrow(), true);
5973
5974    // Extend the pending selection so that it surrounds another selection
5975    leader.update(cx, |leader, cx| {
5976        leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
5977    });
5978    follower
5979        .update(cx, |follower, cx| {
5980            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5981        })
5982        .await
5983        .unwrap();
5984    follower.read_with(cx, |follower, cx| {
5985        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
5986    });
5987
5988    // Scrolling locally breaks the follow
5989    follower.update(cx, |follower, cx| {
5990        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
5991        follower.set_scroll_anchor(
5992            ScrollAnchor {
5993                anchor: top_anchor,
5994                offset: vec2f(0.0, 0.5),
5995            },
5996            cx,
5997        );
5998    });
5999    assert_eq!(*is_still_following.borrow(), false);
6000}
6001
6002#[gpui::test]
6003async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
6004    init_test(cx, |_| {});
6005
6006    let fs = FakeFs::new(cx.background());
6007    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
6008    let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
6009    let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
6010
6011    let leader = pane.update(cx, |_, cx| {
6012        let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
6013        cx.add_view(|cx| build_editor(multibuffer.clone(), cx))
6014    });
6015
6016    // Start following the editor when it has no excerpts.
6017    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
6018    let follower_1 = cx
6019        .update(|cx| {
6020            Editor::from_state_proto(
6021                pane.clone(),
6022                project.clone(),
6023                ViewId {
6024                    creator: Default::default(),
6025                    id: 0,
6026                },
6027                &mut state_message,
6028                cx,
6029            )
6030        })
6031        .unwrap()
6032        .await
6033        .unwrap();
6034
6035    let update_message = Rc::new(RefCell::new(None));
6036    follower_1.update(cx, {
6037        let update = update_message.clone();
6038        |_, cx| {
6039            cx.subscribe(&leader, move |_, leader, event, cx| {
6040                leader
6041                    .read(cx)
6042                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
6043            })
6044            .detach();
6045        }
6046    });
6047
6048    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
6049        (
6050            project
6051                .create_buffer("abc\ndef\nghi\njkl\n", None, cx)
6052                .unwrap(),
6053            project
6054                .create_buffer("mno\npqr\nstu\nvwx\n", None, cx)
6055                .unwrap(),
6056        )
6057    });
6058
6059    // Insert some excerpts.
6060    leader.update(cx, |leader, cx| {
6061        leader.buffer.update(cx, |multibuffer, cx| {
6062            let excerpt_ids = multibuffer.push_excerpts(
6063                buffer_1.clone(),
6064                [
6065                    ExcerptRange {
6066                        context: 1..6,
6067                        primary: None,
6068                    },
6069                    ExcerptRange {
6070                        context: 12..15,
6071                        primary: None,
6072                    },
6073                    ExcerptRange {
6074                        context: 0..3,
6075                        primary: None,
6076                    },
6077                ],
6078                cx,
6079            );
6080            multibuffer.insert_excerpts_after(
6081                excerpt_ids[0],
6082                buffer_2.clone(),
6083                [
6084                    ExcerptRange {
6085                        context: 8..12,
6086                        primary: None,
6087                    },
6088                    ExcerptRange {
6089                        context: 0..6,
6090                        primary: None,
6091                    },
6092                ],
6093                cx,
6094            );
6095        });
6096    });
6097
6098    // Apply the update of adding the excerpts.
6099    follower_1
6100        .update(cx, |follower, cx| {
6101            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6102        })
6103        .await
6104        .unwrap();
6105    assert_eq!(
6106        follower_1.read_with(cx, |editor, cx| editor.text(cx)),
6107        leader.read_with(cx, |editor, cx| editor.text(cx))
6108    );
6109    update_message.borrow_mut().take();
6110
6111    // Start following separately after it already has excerpts.
6112    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
6113    let follower_2 = cx
6114        .update(|cx| {
6115            Editor::from_state_proto(
6116                pane.clone(),
6117                project.clone(),
6118                ViewId {
6119                    creator: Default::default(),
6120                    id: 0,
6121                },
6122                &mut state_message,
6123                cx,
6124            )
6125        })
6126        .unwrap()
6127        .await
6128        .unwrap();
6129    assert_eq!(
6130        follower_2.read_with(cx, |editor, cx| editor.text(cx)),
6131        leader.read_with(cx, |editor, cx| editor.text(cx))
6132    );
6133
6134    // Remove some excerpts.
6135    leader.update(cx, |leader, cx| {
6136        leader.buffer.update(cx, |multibuffer, cx| {
6137            let excerpt_ids = multibuffer.excerpt_ids();
6138            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
6139            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
6140        });
6141    });
6142
6143    // Apply the update of removing the excerpts.
6144    follower_1
6145        .update(cx, |follower, cx| {
6146            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6147        })
6148        .await
6149        .unwrap();
6150    follower_2
6151        .update(cx, |follower, cx| {
6152            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6153        })
6154        .await
6155        .unwrap();
6156    update_message.borrow_mut().take();
6157    assert_eq!(
6158        follower_1.read_with(cx, |editor, cx| editor.text(cx)),
6159        leader.read_with(cx, |editor, cx| editor.text(cx))
6160    );
6161}
6162
6163#[test]
6164fn test_combine_syntax_and_fuzzy_match_highlights() {
6165    let string = "abcdefghijklmnop";
6166    let syntax_ranges = [
6167        (
6168            0..3,
6169            HighlightStyle {
6170                color: Some(Color::red()),
6171                ..Default::default()
6172            },
6173        ),
6174        (
6175            4..8,
6176            HighlightStyle {
6177                color: Some(Color::green()),
6178                ..Default::default()
6179            },
6180        ),
6181    ];
6182    let match_indices = [4, 6, 7, 8];
6183    assert_eq!(
6184        combine_syntax_and_fuzzy_match_highlights(
6185            string,
6186            Default::default(),
6187            syntax_ranges.into_iter(),
6188            &match_indices,
6189        ),
6190        &[
6191            (
6192                0..3,
6193                HighlightStyle {
6194                    color: Some(Color::red()),
6195                    ..Default::default()
6196                },
6197            ),
6198            (
6199                4..5,
6200                HighlightStyle {
6201                    color: Some(Color::green()),
6202                    weight: Some(fonts::Weight::BOLD),
6203                    ..Default::default()
6204                },
6205            ),
6206            (
6207                5..6,
6208                HighlightStyle {
6209                    color: Some(Color::green()),
6210                    ..Default::default()
6211                },
6212            ),
6213            (
6214                6..8,
6215                HighlightStyle {
6216                    color: Some(Color::green()),
6217                    weight: Some(fonts::Weight::BOLD),
6218                    ..Default::default()
6219                },
6220            ),
6221            (
6222                8..9,
6223                HighlightStyle {
6224                    weight: Some(fonts::Weight::BOLD),
6225                    ..Default::default()
6226                },
6227            ),
6228        ]
6229    );
6230}
6231
6232#[gpui::test]
6233async fn go_to_hunk(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
6234    init_test(cx, |_| {});
6235
6236    let mut cx = EditorTestContext::new(cx).await;
6237
6238    let diff_base = r#"
6239        use some::mod;
6240
6241        const A: u32 = 42;
6242
6243        fn main() {
6244            println!("hello");
6245
6246            println!("world");
6247        }
6248        "#
6249    .unindent();
6250
6251    // Edits are modified, removed, modified, added
6252    cx.set_state(
6253        &r#"
6254        use some::modified;
6255
6256        ˇ
6257        fn main() {
6258            println!("hello there");
6259
6260            println!("around the");
6261            println!("world");
6262        }
6263        "#
6264        .unindent(),
6265    );
6266
6267    cx.set_diff_base(Some(&diff_base));
6268    deterministic.run_until_parked();
6269
6270    cx.update_editor(|editor, cx| {
6271        //Wrap around the bottom of the buffer
6272        for _ in 0..3 {
6273            editor.go_to_hunk(&GoToHunk, cx);
6274        }
6275    });
6276
6277    cx.assert_editor_state(
6278        &r#"
6279        ˇuse some::modified;
6280
6281
6282        fn main() {
6283            println!("hello there");
6284
6285            println!("around the");
6286            println!("world");
6287        }
6288        "#
6289        .unindent(),
6290    );
6291
6292    cx.update_editor(|editor, cx| {
6293        //Wrap around the top of the buffer
6294        for _ in 0..2 {
6295            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
6296        }
6297    });
6298
6299    cx.assert_editor_state(
6300        &r#"
6301        use some::modified;
6302
6303
6304        fn main() {
6305        ˇ    println!("hello there");
6306
6307            println!("around the");
6308            println!("world");
6309        }
6310        "#
6311        .unindent(),
6312    );
6313
6314    cx.update_editor(|editor, cx| {
6315        editor.fold(&Fold, cx);
6316
6317        //Make sure that the fold only gets one hunk
6318        for _ in 0..4 {
6319            editor.go_to_hunk(&GoToHunk, cx);
6320        }
6321    });
6322
6323    cx.assert_editor_state(
6324        &r#"
6325        ˇuse some::modified;
6326
6327
6328        fn main() {
6329            println!("hello there");
6330
6331            println!("around the");
6332            println!("world");
6333        }
6334        "#
6335        .unindent(),
6336    );
6337}
6338
6339#[test]
6340fn test_split_words() {
6341    fn split<'a>(text: &'a str) -> Vec<&'a str> {
6342        split_words(text).collect()
6343    }
6344
6345    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
6346    assert_eq!(split("hello_world"), &["hello_", "world"]);
6347    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
6348    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
6349    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
6350    assert_eq!(split("helloworld"), &["helloworld"]);
6351}
6352
6353#[gpui::test]
6354async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
6355    init_test(cx, |_| {});
6356
6357    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
6358    let mut assert = |before, after| {
6359        let _state_context = cx.set_state(before);
6360        cx.update_editor(|editor, cx| {
6361            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
6362        });
6363        cx.assert_editor_state(after);
6364    };
6365
6366    // Outside bracket jumps to outside of matching bracket
6367    assert("console.logˇ(var);", "console.log(var)ˇ;");
6368    assert("console.log(var)ˇ;", "console.logˇ(var);");
6369
6370    // Inside bracket jumps to inside of matching bracket
6371    assert("console.log(ˇvar);", "console.log(varˇ);");
6372    assert("console.log(varˇ);", "console.log(ˇvar);");
6373
6374    // When outside a bracket and inside, favor jumping to the inside bracket
6375    assert(
6376        "console.log('foo', [1, 2, 3]ˇ);",
6377        "console.log(ˇ'foo', [1, 2, 3]);",
6378    );
6379    assert(
6380        "console.log(ˇ'foo', [1, 2, 3]);",
6381        "console.log('foo', [1, 2, 3]ˇ);",
6382    );
6383
6384    // Bias forward if two options are equally likely
6385    assert(
6386        "let result = curried_fun()ˇ();",
6387        "let result = curried_fun()()ˇ;",
6388    );
6389
6390    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
6391    assert(
6392        indoc! {"
6393            function test() {
6394                console.log('test')ˇ
6395            }"},
6396        indoc! {"
6397            function test() {
6398                console.logˇ('test')
6399            }"},
6400    );
6401}
6402
6403#[gpui::test(iterations = 10)]
6404async fn test_copilot(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
6405    init_test(cx, |_| {});
6406
6407    let (copilot, copilot_lsp) = Copilot::fake(cx);
6408    cx.update(|cx| cx.set_global(copilot));
6409    let mut cx = EditorLspTestContext::new_rust(
6410        lsp::ServerCapabilities {
6411            completion_provider: Some(lsp::CompletionOptions {
6412                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
6413                ..Default::default()
6414            }),
6415            ..Default::default()
6416        },
6417        cx,
6418    )
6419    .await;
6420
6421    // When inserting, ensure autocompletion is favored over Copilot suggestions.
6422    cx.set_state(indoc! {"
6423        oneˇ
6424        two
6425        three
6426    "});
6427    cx.simulate_keystroke(".");
6428    let _ = handle_completion_request(
6429        &mut cx,
6430        indoc! {"
6431            one.|<>
6432            two
6433            three
6434        "},
6435        vec!["completion_a", "completion_b"],
6436    );
6437    handle_copilot_completion_request(
6438        &copilot_lsp,
6439        vec![copilot::request::Completion {
6440            text: "one.copilot1".into(),
6441            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6442            ..Default::default()
6443        }],
6444        vec![],
6445    );
6446    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6447    cx.update_editor(|editor, cx| {
6448        assert!(editor.context_menu_visible());
6449        assert!(!editor.has_active_copilot_suggestion(cx));
6450
6451        // Confirming a completion inserts it and hides the context menu, without showing
6452        // the copilot suggestion afterwards.
6453        editor
6454            .confirm_completion(&Default::default(), cx)
6455            .unwrap()
6456            .detach();
6457        assert!(!editor.context_menu_visible());
6458        assert!(!editor.has_active_copilot_suggestion(cx));
6459        assert_eq!(editor.text(cx), "one.completion_a\ntwo\nthree\n");
6460        assert_eq!(editor.display_text(cx), "one.completion_a\ntwo\nthree\n");
6461    });
6462
6463    // Ensure Copilot suggestions are shown right away if no autocompletion is available.
6464    cx.set_state(indoc! {"
6465        oneˇ
6466        two
6467        three
6468    "});
6469    cx.simulate_keystroke(".");
6470    let _ = handle_completion_request(
6471        &mut cx,
6472        indoc! {"
6473            one.|<>
6474            two
6475            three
6476        "},
6477        vec![],
6478    );
6479    handle_copilot_completion_request(
6480        &copilot_lsp,
6481        vec![copilot::request::Completion {
6482            text: "one.copilot1".into(),
6483            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6484            ..Default::default()
6485        }],
6486        vec![],
6487    );
6488    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6489    cx.update_editor(|editor, cx| {
6490        assert!(!editor.context_menu_visible());
6491        assert!(editor.has_active_copilot_suggestion(cx));
6492        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
6493        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
6494    });
6495
6496    // Reset editor, and ensure autocompletion is still favored over Copilot suggestions.
6497    cx.set_state(indoc! {"
6498        oneˇ
6499        two
6500        three
6501    "});
6502    cx.simulate_keystroke(".");
6503    let _ = handle_completion_request(
6504        &mut cx,
6505        indoc! {"
6506            one.|<>
6507            two
6508            three
6509        "},
6510        vec!["completion_a", "completion_b"],
6511    );
6512    handle_copilot_completion_request(
6513        &copilot_lsp,
6514        vec![copilot::request::Completion {
6515            text: "one.copilot1".into(),
6516            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6517            ..Default::default()
6518        }],
6519        vec![],
6520    );
6521    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6522    cx.update_editor(|editor, cx| {
6523        assert!(editor.context_menu_visible());
6524        assert!(!editor.has_active_copilot_suggestion(cx));
6525
6526        // When hiding the context menu, the Copilot suggestion becomes visible.
6527        editor.hide_context_menu(cx);
6528        assert!(!editor.context_menu_visible());
6529        assert!(editor.has_active_copilot_suggestion(cx));
6530        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
6531        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
6532    });
6533
6534    // Ensure existing completion is interpolated when inserting again.
6535    cx.simulate_keystroke("c");
6536    deterministic.run_until_parked();
6537    cx.update_editor(|editor, cx| {
6538        assert!(!editor.context_menu_visible());
6539        assert!(editor.has_active_copilot_suggestion(cx));
6540        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
6541        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6542    });
6543
6544    // After debouncing, new Copilot completions should be requested.
6545    handle_copilot_completion_request(
6546        &copilot_lsp,
6547        vec![copilot::request::Completion {
6548            text: "one.copilot2".into(),
6549            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 5)),
6550            ..Default::default()
6551        }],
6552        vec![],
6553    );
6554    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6555    cx.update_editor(|editor, cx| {
6556        assert!(!editor.context_menu_visible());
6557        assert!(editor.has_active_copilot_suggestion(cx));
6558        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6559        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6560
6561        // Canceling should remove the active Copilot suggestion.
6562        editor.cancel(&Default::default(), cx);
6563        assert!(!editor.has_active_copilot_suggestion(cx));
6564        assert_eq!(editor.display_text(cx), "one.c\ntwo\nthree\n");
6565        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6566
6567        // After canceling, tabbing shouldn't insert the previously shown suggestion.
6568        editor.tab(&Default::default(), cx);
6569        assert!(!editor.has_active_copilot_suggestion(cx));
6570        assert_eq!(editor.display_text(cx), "one.c   \ntwo\nthree\n");
6571        assert_eq!(editor.text(cx), "one.c   \ntwo\nthree\n");
6572
6573        // When undoing the previously active suggestion is shown again.
6574        editor.undo(&Default::default(), cx);
6575        assert!(editor.has_active_copilot_suggestion(cx));
6576        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6577        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6578    });
6579
6580    // If an edit occurs outside of this editor, the suggestion is still correctly interpolated.
6581    cx.update_buffer(|buffer, cx| buffer.edit([(5..5, "o")], None, cx));
6582    cx.update_editor(|editor, cx| {
6583        assert!(editor.has_active_copilot_suggestion(cx));
6584        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6585        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6586
6587        // Tabbing when there is an active suggestion inserts it.
6588        editor.tab(&Default::default(), cx);
6589        assert!(!editor.has_active_copilot_suggestion(cx));
6590        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6591        assert_eq!(editor.text(cx), "one.copilot2\ntwo\nthree\n");
6592
6593        // When undoing the previously active suggestion is shown again.
6594        editor.undo(&Default::default(), cx);
6595        assert!(editor.has_active_copilot_suggestion(cx));
6596        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6597        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6598
6599        // Hide suggestion.
6600        editor.cancel(&Default::default(), cx);
6601        assert!(!editor.has_active_copilot_suggestion(cx));
6602        assert_eq!(editor.display_text(cx), "one.co\ntwo\nthree\n");
6603        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6604    });
6605
6606    // If an edit occurs outside of this editor but no suggestion is being shown,
6607    // we won't make it visible.
6608    cx.update_buffer(|buffer, cx| buffer.edit([(6..6, "p")], None, cx));
6609    cx.update_editor(|editor, cx| {
6610        assert!(!editor.has_active_copilot_suggestion(cx));
6611        assert_eq!(editor.display_text(cx), "one.cop\ntwo\nthree\n");
6612        assert_eq!(editor.text(cx), "one.cop\ntwo\nthree\n");
6613    });
6614
6615    // Reset the editor to verify how suggestions behave when tabbing on leading indentation.
6616    cx.update_editor(|editor, cx| {
6617        editor.set_text("fn foo() {\n  \n}", cx);
6618        editor.change_selections(None, cx, |s| {
6619            s.select_ranges([Point::new(1, 2)..Point::new(1, 2)])
6620        });
6621    });
6622    handle_copilot_completion_request(
6623        &copilot_lsp,
6624        vec![copilot::request::Completion {
6625            text: "    let x = 4;".into(),
6626            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
6627            ..Default::default()
6628        }],
6629        vec![],
6630    );
6631
6632    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
6633    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6634    cx.update_editor(|editor, cx| {
6635        assert!(editor.has_active_copilot_suggestion(cx));
6636        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6637        assert_eq!(editor.text(cx), "fn foo() {\n  \n}");
6638
6639        // Tabbing inside of leading whitespace inserts indentation without accepting the suggestion.
6640        editor.tab(&Default::default(), cx);
6641        assert!(editor.has_active_copilot_suggestion(cx));
6642        assert_eq!(editor.text(cx), "fn foo() {\n    \n}");
6643        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6644
6645        // Tabbing again accepts the suggestion.
6646        editor.tab(&Default::default(), cx);
6647        assert!(!editor.has_active_copilot_suggestion(cx));
6648        assert_eq!(editor.text(cx), "fn foo() {\n    let x = 4;\n}");
6649        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6650    });
6651}
6652
6653#[gpui::test]
6654async fn test_copilot_completion_invalidation(
6655    deterministic: Arc<Deterministic>,
6656    cx: &mut gpui::TestAppContext,
6657) {
6658    init_test(cx, |_| {});
6659
6660    let (copilot, copilot_lsp) = Copilot::fake(cx);
6661    cx.update(|cx| cx.set_global(copilot));
6662    let mut cx = EditorLspTestContext::new_rust(
6663        lsp::ServerCapabilities {
6664            completion_provider: Some(lsp::CompletionOptions {
6665                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
6666                ..Default::default()
6667            }),
6668            ..Default::default()
6669        },
6670        cx,
6671    )
6672    .await;
6673
6674    cx.set_state(indoc! {"
6675        one
6676        twˇ
6677        three
6678    "});
6679
6680    handle_copilot_completion_request(
6681        &copilot_lsp,
6682        vec![copilot::request::Completion {
6683            text: "two.foo()".into(),
6684            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
6685            ..Default::default()
6686        }],
6687        vec![],
6688    );
6689    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
6690    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6691    cx.update_editor(|editor, cx| {
6692        assert!(editor.has_active_copilot_suggestion(cx));
6693        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6694        assert_eq!(editor.text(cx), "one\ntw\nthree\n");
6695
6696        editor.backspace(&Default::default(), cx);
6697        assert!(editor.has_active_copilot_suggestion(cx));
6698        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6699        assert_eq!(editor.text(cx), "one\nt\nthree\n");
6700
6701        editor.backspace(&Default::default(), cx);
6702        assert!(editor.has_active_copilot_suggestion(cx));
6703        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6704        assert_eq!(editor.text(cx), "one\n\nthree\n");
6705
6706        // Deleting across the original suggestion range invalidates it.
6707        editor.backspace(&Default::default(), cx);
6708        assert!(!editor.has_active_copilot_suggestion(cx));
6709        assert_eq!(editor.display_text(cx), "one\nthree\n");
6710        assert_eq!(editor.text(cx), "one\nthree\n");
6711
6712        // Undoing the deletion restores the suggestion.
6713        editor.undo(&Default::default(), cx);
6714        assert!(editor.has_active_copilot_suggestion(cx));
6715        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6716        assert_eq!(editor.text(cx), "one\n\nthree\n");
6717    });
6718}
6719
6720#[gpui::test]
6721async fn test_copilot_multibuffer(
6722    deterministic: Arc<Deterministic>,
6723    cx: &mut gpui::TestAppContext,
6724) {
6725    init_test(cx, |_| {});
6726
6727    let (copilot, copilot_lsp) = Copilot::fake(cx);
6728    cx.update(|cx| cx.set_global(copilot));
6729
6730    let buffer_1 = cx.add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx));
6731    let buffer_2 = cx.add_model(|cx| Buffer::new(0, "c = 3\nd = 4\n", cx));
6732    let multibuffer = cx.add_model(|cx| {
6733        let mut multibuffer = MultiBuffer::new(0);
6734        multibuffer.push_excerpts(
6735            buffer_1.clone(),
6736            [ExcerptRange {
6737                context: Point::new(0, 0)..Point::new(2, 0),
6738                primary: None,
6739            }],
6740            cx,
6741        );
6742        multibuffer.push_excerpts(
6743            buffer_2.clone(),
6744            [ExcerptRange {
6745                context: Point::new(0, 0)..Point::new(2, 0),
6746                primary: None,
6747            }],
6748            cx,
6749        );
6750        multibuffer
6751    });
6752    let (_, editor) = cx.add_window(|cx| build_editor(multibuffer, cx));
6753
6754    handle_copilot_completion_request(
6755        &copilot_lsp,
6756        vec![copilot::request::Completion {
6757            text: "b = 2 + a".into(),
6758            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 5)),
6759            ..Default::default()
6760        }],
6761        vec![],
6762    );
6763    editor.update(cx, |editor, cx| {
6764        // Ensure copilot suggestions are shown for the first excerpt.
6765        editor.change_selections(None, cx, |s| {
6766            s.select_ranges([Point::new(1, 5)..Point::new(1, 5)])
6767        });
6768        editor.next_copilot_suggestion(&Default::default(), cx);
6769    });
6770    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6771    editor.update(cx, |editor, cx| {
6772        assert!(editor.has_active_copilot_suggestion(cx));
6773        assert_eq!(
6774            editor.display_text(cx),
6775            "\n\na = 1\nb = 2 + a\n\n\n\nc = 3\nd = 4\n"
6776        );
6777        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
6778    });
6779
6780    handle_copilot_completion_request(
6781        &copilot_lsp,
6782        vec![copilot::request::Completion {
6783            text: "d = 4 + c".into(),
6784            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 6)),
6785            ..Default::default()
6786        }],
6787        vec![],
6788    );
6789    editor.update(cx, |editor, cx| {
6790        // Move to another excerpt, ensuring the suggestion gets cleared.
6791        editor.change_selections(None, cx, |s| {
6792            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
6793        });
6794        assert!(!editor.has_active_copilot_suggestion(cx));
6795        assert_eq!(
6796            editor.display_text(cx),
6797            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4\n"
6798        );
6799        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
6800
6801        // Type a character, ensuring we don't even try to interpolate the previous suggestion.
6802        editor.handle_input(" ", cx);
6803        assert!(!editor.has_active_copilot_suggestion(cx));
6804        assert_eq!(
6805            editor.display_text(cx),
6806            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 \n"
6807        );
6808        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
6809    });
6810
6811    // Ensure the new suggestion is displayed when the debounce timeout expires.
6812    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6813    editor.update(cx, |editor, cx| {
6814        assert!(editor.has_active_copilot_suggestion(cx));
6815        assert_eq!(
6816            editor.display_text(cx),
6817            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 + c\n"
6818        );
6819        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
6820    });
6821}
6822
6823#[gpui::test]
6824async fn test_copilot_disabled_globs(
6825    deterministic: Arc<Deterministic>,
6826    cx: &mut gpui::TestAppContext,
6827) {
6828    init_test(cx, |settings| {
6829        settings
6830            .copilot
6831            .get_or_insert(Default::default())
6832            .disabled_globs = Some(vec![".env*".to_string()]);
6833    });
6834
6835    let (copilot, copilot_lsp) = Copilot::fake(cx);
6836    cx.update(|cx| cx.set_global(copilot));
6837
6838    let fs = FakeFs::new(cx.background());
6839    fs.insert_tree(
6840        "/test",
6841        json!({
6842            ".env": "SECRET=something\n",
6843            "README.md": "hello\n"
6844        }),
6845    )
6846    .await;
6847    let project = Project::test(fs, ["/test".as_ref()], cx).await;
6848
6849    let private_buffer = project
6850        .update(cx, |project, cx| {
6851            project.open_local_buffer("/test/.env", cx)
6852        })
6853        .await
6854        .unwrap();
6855    let public_buffer = project
6856        .update(cx, |project, cx| {
6857            project.open_local_buffer("/test/README.md", cx)
6858        })
6859        .await
6860        .unwrap();
6861
6862    let multibuffer = cx.add_model(|cx| {
6863        let mut multibuffer = MultiBuffer::new(0);
6864        multibuffer.push_excerpts(
6865            private_buffer.clone(),
6866            [ExcerptRange {
6867                context: Point::new(0, 0)..Point::new(1, 0),
6868                primary: None,
6869            }],
6870            cx,
6871        );
6872        multibuffer.push_excerpts(
6873            public_buffer.clone(),
6874            [ExcerptRange {
6875                context: Point::new(0, 0)..Point::new(1, 0),
6876                primary: None,
6877            }],
6878            cx,
6879        );
6880        multibuffer
6881    });
6882    let (_, editor) = cx.add_window(|cx| build_editor(multibuffer, cx));
6883
6884    let mut copilot_requests = copilot_lsp
6885        .handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| async move {
6886            Ok(copilot::request::GetCompletionsResult {
6887                completions: vec![copilot::request::Completion {
6888                    text: "next line".into(),
6889                    range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
6890                    ..Default::default()
6891                }],
6892            })
6893        });
6894
6895    editor.update(cx, |editor, cx| {
6896        editor.change_selections(None, cx, |selections| {
6897            selections.select_ranges([Point::new(0, 0)..Point::new(0, 0)])
6898        });
6899        editor.next_copilot_suggestion(&Default::default(), cx);
6900    });
6901
6902    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6903    assert!(copilot_requests.try_next().is_err());
6904
6905    editor.update(cx, |editor, cx| {
6906        editor.change_selections(None, cx, |s| {
6907            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
6908        });
6909        editor.next_copilot_suggestion(&Default::default(), cx);
6910    });
6911
6912    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6913    assert!(copilot_requests.try_next().is_ok());
6914}
6915
6916fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
6917    let point = DisplayPoint::new(row as u32, column as u32);
6918    point..point
6919}
6920
6921fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
6922    let (text, ranges) = marked_text_ranges(marked_text, true);
6923    assert_eq!(view.text(cx), text);
6924    assert_eq!(
6925        view.selections.ranges(cx),
6926        ranges,
6927        "Assert selections are {}",
6928        marked_text
6929    );
6930}
6931
6932/// Handle completion request passing a marked string specifying where the completion
6933/// should be triggered from using '|' character, what range should be replaced, and what completions
6934/// should be returned using '<' and '>' to delimit the range
6935fn handle_completion_request<'a>(
6936    cx: &mut EditorLspTestContext<'a>,
6937    marked_string: &str,
6938    completions: Vec<&'static str>,
6939) -> impl Future<Output = ()> {
6940    let complete_from_marker: TextRangeMarker = '|'.into();
6941    let replace_range_marker: TextRangeMarker = ('<', '>').into();
6942    let (_, mut marked_ranges) = marked_text_ranges_by(
6943        marked_string,
6944        vec![complete_from_marker.clone(), replace_range_marker.clone()],
6945    );
6946
6947    let complete_from_position =
6948        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
6949    let replace_range =
6950        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
6951
6952    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
6953        let completions = completions.clone();
6954        async move {
6955            assert_eq!(params.text_document_position.text_document.uri, url.clone());
6956            assert_eq!(
6957                params.text_document_position.position,
6958                complete_from_position
6959            );
6960            Ok(Some(lsp::CompletionResponse::Array(
6961                completions
6962                    .iter()
6963                    .map(|completion_text| lsp::CompletionItem {
6964                        label: completion_text.to_string(),
6965                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
6966                            range: replace_range,
6967                            new_text: completion_text.to_string(),
6968                        })),
6969                        ..Default::default()
6970                    })
6971                    .collect(),
6972            )))
6973        }
6974    });
6975
6976    async move {
6977        request.next().await;
6978    }
6979}
6980
6981fn handle_resolve_completion_request<'a>(
6982    cx: &mut EditorLspTestContext<'a>,
6983    edits: Option<Vec<(&'static str, &'static str)>>,
6984) -> impl Future<Output = ()> {
6985    let edits = edits.map(|edits| {
6986        edits
6987            .iter()
6988            .map(|(marked_string, new_text)| {
6989                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
6990                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
6991                lsp::TextEdit::new(replace_range, new_text.to_string())
6992            })
6993            .collect::<Vec<_>>()
6994    });
6995
6996    let mut request =
6997        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
6998            let edits = edits.clone();
6999            async move {
7000                Ok(lsp::CompletionItem {
7001                    additional_text_edits: edits,
7002                    ..Default::default()
7003                })
7004            }
7005        });
7006
7007    async move {
7008        request.next().await;
7009    }
7010}
7011
7012fn handle_copilot_completion_request(
7013    lsp: &lsp::FakeLanguageServer,
7014    completions: Vec<copilot::request::Completion>,
7015    completions_cycling: Vec<copilot::request::Completion>,
7016) {
7017    lsp.handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| {
7018        let completions = completions.clone();
7019        async move {
7020            Ok(copilot::request::GetCompletionsResult {
7021                completions: completions.clone(),
7022            })
7023        }
7024    });
7025    lsp.handle_request::<copilot::request::GetCompletionsCycling, _, _>(move |_params, _cx| {
7026        let completions_cycling = completions_cycling.clone();
7027        async move {
7028            Ok(copilot::request::GetCompletionsResult {
7029                completions: completions_cycling.clone(),
7030            })
7031        }
7032    });
7033}
7034
7035pub(crate) fn update_test_settings(
7036    cx: &mut TestAppContext,
7037    f: impl Fn(&mut AllLanguageSettingsContent),
7038) {
7039    cx.update(|cx| {
7040        cx.update_global::<SettingsStore, _, _>(|store, cx| {
7041            store.update_user_settings::<AllLanguageSettings>(cx, f);
7042        });
7043    });
7044}
7045
7046pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
7047    cx.foreground().forbid_parking();
7048
7049    cx.update(|cx| {
7050        cx.set_global(SettingsStore::test(cx));
7051        theme::init((), cx);
7052        client::init_settings(cx);
7053        language::init(cx);
7054        Project::init_settings(cx);
7055        workspace::init_settings(cx);
7056        crate::init(cx);
7057    });
7058
7059    update_test_settings(cx, f);
7060}