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(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        editor.join_lines(&JoinLines, cx);
2346        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
2347        assert_eq!(
2348            editor.selections.ranges::<Point>(cx),
2349            &[Point::new(0, 3)..Point::new(0, 3)]
2350        );
2351
2352        editor.change_selections(None, cx, |s| {
2353            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
2354        });
2355        editor.join_lines(&JoinLines, cx);
2356        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
2357        assert_eq!(
2358            editor.selections.ranges::<Point>(cx),
2359            &[Point::new(0, 11)..Point::new(0, 11)]
2360        );
2361
2362        editor.undo(&Undo, cx);
2363        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
2364        assert_eq!(
2365            editor.selections.ranges::<Point>(cx),
2366            &[Point::new(0, 5)..Point::new(2, 2)]
2367        );
2368
2369        editor.change_selections(None, cx, |s| {
2370            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
2371        });
2372        editor.join_lines(&JoinLines, cx);
2373        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
2374        assert_eq!(
2375            editor.selections.ranges::<Point>(cx),
2376            [Point::new(2, 3)..Point::new(2, 3)]
2377        );
2378
2379        editor.join_lines(&JoinLines, cx);
2380        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
2381        assert_eq!(
2382            editor.selections.ranges::<Point>(cx),
2383            [Point::new(2, 3)..Point::new(2, 3)]
2384        );
2385
2386        editor.join_lines(&JoinLines, cx);
2387        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
2388        assert_eq!(
2389            editor.selections.ranges::<Point>(cx),
2390            [Point::new(2, 3)..Point::new(2, 3)]
2391        );
2392
2393        editor
2394    });
2395}
2396
2397#[gpui::test]
2398fn test_duplicate_line(cx: &mut TestAppContext) {
2399    init_test(cx, |_| {});
2400
2401    let (_, view) = cx.add_window(|cx| {
2402        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2403        build_editor(buffer, cx)
2404    });
2405    view.update(cx, |view, cx| {
2406        view.change_selections(None, cx, |s| {
2407            s.select_display_ranges([
2408                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2409                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2410                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2411                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2412            ])
2413        });
2414        view.duplicate_line(&DuplicateLine, cx);
2415        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
2416        assert_eq!(
2417            view.selections.display_ranges(cx),
2418            vec![
2419                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
2420                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
2421                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2422                DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
2423            ]
2424        );
2425    });
2426
2427    let (_, view) = cx.add_window(|cx| {
2428        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2429        build_editor(buffer, cx)
2430    });
2431    view.update(cx, |view, cx| {
2432        view.change_selections(None, cx, |s| {
2433            s.select_display_ranges([
2434                DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
2435                DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
2436            ])
2437        });
2438        view.duplicate_line(&DuplicateLine, cx);
2439        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
2440        assert_eq!(
2441            view.selections.display_ranges(cx),
2442            vec![
2443                DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
2444                DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
2445            ]
2446        );
2447    });
2448}
2449
2450#[gpui::test]
2451fn test_move_line_up_down(cx: &mut TestAppContext) {
2452    init_test(cx, |_| {});
2453
2454    let (_, view) = cx.add_window(|cx| {
2455        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
2456        build_editor(buffer, cx)
2457    });
2458    view.update(cx, |view, cx| {
2459        view.fold_ranges(
2460            vec![
2461                Point::new(0, 2)..Point::new(1, 2),
2462                Point::new(2, 3)..Point::new(4, 1),
2463                Point::new(7, 0)..Point::new(8, 4),
2464            ],
2465            true,
2466            cx,
2467        );
2468        view.change_selections(None, cx, |s| {
2469            s.select_display_ranges([
2470                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2471                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2472                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2473                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
2474            ])
2475        });
2476        assert_eq!(
2477            view.display_text(cx),
2478            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
2479        );
2480
2481        view.move_line_up(&MoveLineUp, cx);
2482        assert_eq!(
2483            view.display_text(cx),
2484            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
2485        );
2486        assert_eq!(
2487            view.selections.display_ranges(cx),
2488            vec![
2489                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2490                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2491                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
2492                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
2493            ]
2494        );
2495    });
2496
2497    view.update(cx, |view, cx| {
2498        view.move_line_down(&MoveLineDown, cx);
2499        assert_eq!(
2500            view.display_text(cx),
2501            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
2502        );
2503        assert_eq!(
2504            view.selections.display_ranges(cx),
2505            vec![
2506                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
2507                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2508                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2509                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
2510            ]
2511        );
2512    });
2513
2514    view.update(cx, |view, cx| {
2515        view.move_line_down(&MoveLineDown, cx);
2516        assert_eq!(
2517            view.display_text(cx),
2518            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
2519        );
2520        assert_eq!(
2521            view.selections.display_ranges(cx),
2522            vec![
2523                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2524                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
2525                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
2526                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
2527            ]
2528        );
2529    });
2530
2531    view.update(cx, |view, cx| {
2532        view.move_line_up(&MoveLineUp, cx);
2533        assert_eq!(
2534            view.display_text(cx),
2535            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
2536        );
2537        assert_eq!(
2538            view.selections.display_ranges(cx),
2539            vec![
2540                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
2541                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
2542                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
2543                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
2544            ]
2545        );
2546    });
2547}
2548
2549#[gpui::test]
2550fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
2551    init_test(cx, |_| {});
2552
2553    let (_, editor) = cx.add_window(|cx| {
2554        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
2555        build_editor(buffer, cx)
2556    });
2557    editor.update(cx, |editor, cx| {
2558        let snapshot = editor.buffer.read(cx).snapshot(cx);
2559        editor.insert_blocks(
2560            [BlockProperties {
2561                style: BlockStyle::Fixed,
2562                position: snapshot.anchor_after(Point::new(2, 0)),
2563                disposition: BlockDisposition::Below,
2564                height: 1,
2565                render: Arc::new(|_| Empty::new().into_any()),
2566            }],
2567            Some(Autoscroll::fit()),
2568            cx,
2569        );
2570        editor.change_selections(None, cx, |s| {
2571            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
2572        });
2573        editor.move_line_down(&MoveLineDown, cx);
2574    });
2575}
2576
2577#[gpui::test]
2578fn test_transpose(cx: &mut TestAppContext) {
2579    init_test(cx, |_| {});
2580
2581    _ = cx
2582        .add_window(|cx| {
2583            let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
2584
2585            editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
2586            editor.transpose(&Default::default(), cx);
2587            assert_eq!(editor.text(cx), "bac");
2588            assert_eq!(editor.selections.ranges(cx), [2..2]);
2589
2590            editor.transpose(&Default::default(), cx);
2591            assert_eq!(editor.text(cx), "bca");
2592            assert_eq!(editor.selections.ranges(cx), [3..3]);
2593
2594            editor.transpose(&Default::default(), cx);
2595            assert_eq!(editor.text(cx), "bac");
2596            assert_eq!(editor.selections.ranges(cx), [3..3]);
2597
2598            editor
2599        })
2600        .1;
2601
2602    _ = cx
2603        .add_window(|cx| {
2604            let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
2605
2606            editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
2607            editor.transpose(&Default::default(), cx);
2608            assert_eq!(editor.text(cx), "acb\nde");
2609            assert_eq!(editor.selections.ranges(cx), [3..3]);
2610
2611            editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
2612            editor.transpose(&Default::default(), cx);
2613            assert_eq!(editor.text(cx), "acbd\ne");
2614            assert_eq!(editor.selections.ranges(cx), [5..5]);
2615
2616            editor.transpose(&Default::default(), cx);
2617            assert_eq!(editor.text(cx), "acbde\n");
2618            assert_eq!(editor.selections.ranges(cx), [6..6]);
2619
2620            editor.transpose(&Default::default(), cx);
2621            assert_eq!(editor.text(cx), "acbd\ne");
2622            assert_eq!(editor.selections.ranges(cx), [6..6]);
2623
2624            editor
2625        })
2626        .1;
2627
2628    _ = cx
2629        .add_window(|cx| {
2630            let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
2631
2632            editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
2633            editor.transpose(&Default::default(), cx);
2634            assert_eq!(editor.text(cx), "bacd\ne");
2635            assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
2636
2637            editor.transpose(&Default::default(), cx);
2638            assert_eq!(editor.text(cx), "bcade\n");
2639            assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
2640
2641            editor.transpose(&Default::default(), cx);
2642            assert_eq!(editor.text(cx), "bcda\ne");
2643            assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
2644
2645            editor.transpose(&Default::default(), cx);
2646            assert_eq!(editor.text(cx), "bcade\n");
2647            assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
2648
2649            editor.transpose(&Default::default(), cx);
2650            assert_eq!(editor.text(cx), "bcaed\n");
2651            assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
2652
2653            editor
2654        })
2655        .1;
2656
2657    _ = cx
2658        .add_window(|cx| {
2659            let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
2660
2661            editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
2662            editor.transpose(&Default::default(), cx);
2663            assert_eq!(editor.text(cx), "🏀🍐✋");
2664            assert_eq!(editor.selections.ranges(cx), [8..8]);
2665
2666            editor.transpose(&Default::default(), cx);
2667            assert_eq!(editor.text(cx), "🏀✋🍐");
2668            assert_eq!(editor.selections.ranges(cx), [11..11]);
2669
2670            editor.transpose(&Default::default(), cx);
2671            assert_eq!(editor.text(cx), "🏀🍐✋");
2672            assert_eq!(editor.selections.ranges(cx), [11..11]);
2673
2674            editor
2675        })
2676        .1;
2677}
2678
2679#[gpui::test]
2680async fn test_clipboard(cx: &mut gpui::TestAppContext) {
2681    init_test(cx, |_| {});
2682
2683    let mut cx = EditorTestContext::new(cx).await;
2684
2685    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
2686    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2687    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
2688
2689    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
2690    cx.set_state("two ˇfour ˇsix ˇ");
2691    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2692    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
2693
2694    // Paste again but with only two cursors. Since the number of cursors doesn't
2695    // match the number of slices in the clipboard, the entire clipboard text
2696    // is pasted at each cursor.
2697    cx.set_state("ˇtwo one✅ four three six five ˇ");
2698    cx.update_editor(|e, cx| {
2699        e.handle_input("( ", cx);
2700        e.paste(&Paste, cx);
2701        e.handle_input(") ", cx);
2702    });
2703    cx.assert_editor_state(
2704        &([
2705            "( one✅ ",
2706            "three ",
2707            "five ) ˇtwo one✅ four three six five ( one✅ ",
2708            "three ",
2709            "five ) ˇ",
2710        ]
2711        .join("\n")),
2712    );
2713
2714    // Cut with three selections, one of which is full-line.
2715    cx.set_state(indoc! {"
2716        1«2ˇ»3
2717        4ˇ567
2718        «8ˇ»9"});
2719    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2720    cx.assert_editor_state(indoc! {"
2721        1ˇ3
2722        ˇ9"});
2723
2724    // Paste with three selections, noticing how the copied selection that was full-line
2725    // gets inserted before the second cursor.
2726    cx.set_state(indoc! {"
2727        1ˇ3
27282729        «oˇ»ne"});
2730    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2731    cx.assert_editor_state(indoc! {"
2732        12ˇ3
2733        4567
27342735        8ˇne"});
2736
2737    // Copy with a single cursor only, which writes the whole line into the clipboard.
2738    cx.set_state(indoc! {"
2739        The quick brown
2740        fox juˇmps over
2741        the lazy dog"});
2742    cx.update_editor(|e, cx| e.copy(&Copy, cx));
2743    cx.cx.assert_clipboard_content(Some("fox jumps over\n"));
2744
2745    // Paste with three selections, noticing how the copied full-line selection is inserted
2746    // before the empty selections but replaces the selection that is non-empty.
2747    cx.set_state(indoc! {"
2748        Tˇhe quick brown
2749        «foˇ»x jumps over
2750        tˇhe lazy dog"});
2751    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2752    cx.assert_editor_state(indoc! {"
2753        fox jumps over
2754        Tˇhe quick brown
2755        fox jumps over
2756        ˇx jumps over
2757        fox jumps over
2758        tˇhe lazy dog"});
2759}
2760
2761#[gpui::test]
2762async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
2763    init_test(cx, |_| {});
2764
2765    let mut cx = EditorTestContext::new(cx).await;
2766    let language = Arc::new(Language::new(
2767        LanguageConfig::default(),
2768        Some(tree_sitter_rust::language()),
2769    ));
2770    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
2771
2772    // Cut an indented block, without the leading whitespace.
2773    cx.set_state(indoc! {"
2774        const a: B = (
2775            c(),
2776            «d(
2777                e,
2778                f
2779            )ˇ»
2780        );
2781    "});
2782    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2783    cx.assert_editor_state(indoc! {"
2784        const a: B = (
2785            c(),
2786            ˇ
2787        );
2788    "});
2789
2790    // Paste it at the same position.
2791    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2792    cx.assert_editor_state(indoc! {"
2793        const a: B = (
2794            c(),
2795            d(
2796                e,
2797                f
27982799        );
2800    "});
2801
2802    // Paste it at a line with a lower indent level.
2803    cx.set_state(indoc! {"
2804        ˇ
2805        const a: B = (
2806            c(),
2807        );
2808    "});
2809    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2810    cx.assert_editor_state(indoc! {"
2811        d(
2812            e,
2813            f
28142815        const a: B = (
2816            c(),
2817        );
2818    "});
2819
2820    // Cut an indented block, with the leading whitespace.
2821    cx.set_state(indoc! {"
2822        const a: B = (
2823            c(),
2824        «    d(
2825                e,
2826                f
2827            )
2828        ˇ»);
2829    "});
2830    cx.update_editor(|e, cx| e.cut(&Cut, cx));
2831    cx.assert_editor_state(indoc! {"
2832        const a: B = (
2833            c(),
2834        ˇ);
2835    "});
2836
2837    // Paste it at the same position.
2838    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2839    cx.assert_editor_state(indoc! {"
2840        const a: B = (
2841            c(),
2842            d(
2843                e,
2844                f
2845            )
2846        ˇ);
2847    "});
2848
2849    // Paste it at a line with a higher indent level.
2850    cx.set_state(indoc! {"
2851        const a: B = (
2852            c(),
2853            d(
2854                e,
28552856            )
2857        );
2858    "});
2859    cx.update_editor(|e, cx| e.paste(&Paste, cx));
2860    cx.assert_editor_state(indoc! {"
2861        const a: B = (
2862            c(),
2863            d(
2864                e,
2865                f    d(
2866                    e,
2867                    f
2868                )
2869        ˇ
2870            )
2871        );
2872    "});
2873}
2874
2875#[gpui::test]
2876fn test_select_all(cx: &mut TestAppContext) {
2877    init_test(cx, |_| {});
2878
2879    let (_, view) = cx.add_window(|cx| {
2880        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
2881        build_editor(buffer, cx)
2882    });
2883    view.update(cx, |view, cx| {
2884        view.select_all(&SelectAll, cx);
2885        assert_eq!(
2886            view.selections.display_ranges(cx),
2887            &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
2888        );
2889    });
2890}
2891
2892#[gpui::test]
2893fn test_select_line(cx: &mut TestAppContext) {
2894    init_test(cx, |_| {});
2895
2896    let (_, view) = cx.add_window(|cx| {
2897        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
2898        build_editor(buffer, cx)
2899    });
2900    view.update(cx, |view, cx| {
2901        view.change_selections(None, cx, |s| {
2902            s.select_display_ranges([
2903                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2904                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2905                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2906                DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
2907            ])
2908        });
2909        view.select_line(&SelectLine, cx);
2910        assert_eq!(
2911            view.selections.display_ranges(cx),
2912            vec![
2913                DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
2914                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
2915            ]
2916        );
2917    });
2918
2919    view.update(cx, |view, cx| {
2920        view.select_line(&SelectLine, cx);
2921        assert_eq!(
2922            view.selections.display_ranges(cx),
2923            vec![
2924                DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
2925                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
2926            ]
2927        );
2928    });
2929
2930    view.update(cx, |view, cx| {
2931        view.select_line(&SelectLine, cx);
2932        assert_eq!(
2933            view.selections.display_ranges(cx),
2934            vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
2935        );
2936    });
2937}
2938
2939#[gpui::test]
2940fn test_split_selection_into_lines(cx: &mut TestAppContext) {
2941    init_test(cx, |_| {});
2942
2943    let (_, view) = cx.add_window(|cx| {
2944        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
2945        build_editor(buffer, cx)
2946    });
2947    view.update(cx, |view, cx| {
2948        view.fold_ranges(
2949            vec![
2950                Point::new(0, 2)..Point::new(1, 2),
2951                Point::new(2, 3)..Point::new(4, 1),
2952                Point::new(7, 0)..Point::new(8, 4),
2953            ],
2954            true,
2955            cx,
2956        );
2957        view.change_selections(None, cx, |s| {
2958            s.select_display_ranges([
2959                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
2960                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2961                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
2962                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
2963            ])
2964        });
2965        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
2966    });
2967
2968    view.update(cx, |view, cx| {
2969        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
2970        assert_eq!(
2971            view.display_text(cx),
2972            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
2973        );
2974        assert_eq!(
2975            view.selections.display_ranges(cx),
2976            [
2977                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2978                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
2979                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
2980                DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
2981            ]
2982        );
2983    });
2984
2985    view.update(cx, |view, cx| {
2986        view.change_selections(None, cx, |s| {
2987            s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
2988        });
2989        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
2990        assert_eq!(
2991            view.display_text(cx),
2992            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
2993        );
2994        assert_eq!(
2995            view.selections.display_ranges(cx),
2996            [
2997                DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
2998                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
2999                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
3000                DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
3001                DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
3002                DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
3003                DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
3004                DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
3005            ]
3006        );
3007    });
3008}
3009
3010#[gpui::test]
3011fn test_add_selection_above_below(cx: &mut TestAppContext) {
3012    init_test(cx, |_| {});
3013
3014    let (_, view) = cx.add_window(|cx| {
3015        let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
3016        build_editor(buffer, cx)
3017    });
3018
3019    view.update(cx, |view, cx| {
3020        view.change_selections(None, cx, |s| {
3021            s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
3022        });
3023    });
3024    view.update(cx, |view, cx| {
3025        view.add_selection_above(&AddSelectionAbove, cx);
3026        assert_eq!(
3027            view.selections.display_ranges(cx),
3028            vec![
3029                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
3030                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
3031            ]
3032        );
3033    });
3034
3035    view.update(cx, |view, cx| {
3036        view.add_selection_above(&AddSelectionAbove, cx);
3037        assert_eq!(
3038            view.selections.display_ranges(cx),
3039            vec![
3040                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
3041                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
3042            ]
3043        );
3044    });
3045
3046    view.update(cx, |view, cx| {
3047        view.add_selection_below(&AddSelectionBelow, cx);
3048        assert_eq!(
3049            view.selections.display_ranges(cx),
3050            vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
3051        );
3052
3053        view.undo_selection(&UndoSelection, cx);
3054        assert_eq!(
3055            view.selections.display_ranges(cx),
3056            vec![
3057                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
3058                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
3059            ]
3060        );
3061
3062        view.redo_selection(&RedoSelection, cx);
3063        assert_eq!(
3064            view.selections.display_ranges(cx),
3065            vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
3066        );
3067    });
3068
3069    view.update(cx, |view, cx| {
3070        view.add_selection_below(&AddSelectionBelow, cx);
3071        assert_eq!(
3072            view.selections.display_ranges(cx),
3073            vec![
3074                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
3075                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
3076            ]
3077        );
3078    });
3079
3080    view.update(cx, |view, cx| {
3081        view.add_selection_below(&AddSelectionBelow, cx);
3082        assert_eq!(
3083            view.selections.display_ranges(cx),
3084            vec![
3085                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
3086                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
3087            ]
3088        );
3089    });
3090
3091    view.update(cx, |view, cx| {
3092        view.change_selections(None, cx, |s| {
3093            s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
3094        });
3095    });
3096    view.update(cx, |view, cx| {
3097        view.add_selection_below(&AddSelectionBelow, cx);
3098        assert_eq!(
3099            view.selections.display_ranges(cx),
3100            vec![
3101                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
3102                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
3103            ]
3104        );
3105    });
3106
3107    view.update(cx, |view, cx| {
3108        view.add_selection_below(&AddSelectionBelow, cx);
3109        assert_eq!(
3110            view.selections.display_ranges(cx),
3111            vec![
3112                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
3113                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
3114            ]
3115        );
3116    });
3117
3118    view.update(cx, |view, cx| {
3119        view.add_selection_above(&AddSelectionAbove, cx);
3120        assert_eq!(
3121            view.selections.display_ranges(cx),
3122            vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
3123        );
3124    });
3125
3126    view.update(cx, |view, cx| {
3127        view.add_selection_above(&AddSelectionAbove, cx);
3128        assert_eq!(
3129            view.selections.display_ranges(cx),
3130            vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
3131        );
3132    });
3133
3134    view.update(cx, |view, cx| {
3135        view.change_selections(None, cx, |s| {
3136            s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
3137        });
3138        view.add_selection_below(&AddSelectionBelow, cx);
3139        assert_eq!(
3140            view.selections.display_ranges(cx),
3141            vec![
3142                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
3143                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
3144                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
3145            ]
3146        );
3147    });
3148
3149    view.update(cx, |view, cx| {
3150        view.add_selection_below(&AddSelectionBelow, cx);
3151        assert_eq!(
3152            view.selections.display_ranges(cx),
3153            vec![
3154                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
3155                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
3156                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
3157                DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
3158            ]
3159        );
3160    });
3161
3162    view.update(cx, |view, cx| {
3163        view.add_selection_above(&AddSelectionAbove, cx);
3164        assert_eq!(
3165            view.selections.display_ranges(cx),
3166            vec![
3167                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
3168                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
3169                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
3170            ]
3171        );
3172    });
3173
3174    view.update(cx, |view, cx| {
3175        view.change_selections(None, cx, |s| {
3176            s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
3177        });
3178    });
3179    view.update(cx, |view, cx| {
3180        view.add_selection_above(&AddSelectionAbove, cx);
3181        assert_eq!(
3182            view.selections.display_ranges(cx),
3183            vec![
3184                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
3185                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
3186                DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
3187                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
3188            ]
3189        );
3190    });
3191
3192    view.update(cx, |view, cx| {
3193        view.add_selection_below(&AddSelectionBelow, cx);
3194        assert_eq!(
3195            view.selections.display_ranges(cx),
3196            vec![
3197                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
3198                DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
3199                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
3200            ]
3201        );
3202    });
3203}
3204
3205#[gpui::test]
3206async fn test_select_next(cx: &mut gpui::TestAppContext) {
3207    init_test(cx, |_| {});
3208
3209    let mut cx = EditorTestContext::new(cx).await;
3210    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
3211
3212    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
3213    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3214
3215    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
3216    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
3217
3218    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
3219    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3220
3221    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
3222    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
3223
3224    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
3225    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
3226
3227    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));
3228    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
3229}
3230
3231#[gpui::test]
3232async fn test_select_previous(cx: &mut gpui::TestAppContext) {
3233    init_test(cx, |_| {});
3234    {
3235        // `Select previous` without a selection (selects wordwise)
3236        let mut cx = EditorTestContext::new(cx).await;
3237        cx.set_state("abc\nˇabc abc\ndefabc\nabc");
3238
3239        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3240        cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3241
3242        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3243        cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
3244
3245        cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
3246        cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
3247
3248        cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
3249        cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
3250
3251        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3252        cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
3253
3254        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3255        cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
3256    }
3257    {
3258        // `Select previous` with a selection
3259        let mut cx = EditorTestContext::new(cx).await;
3260        cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
3261
3262        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3263        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
3264
3265        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3266        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
3267
3268        cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
3269        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
3270
3271        cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
3272        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
3273
3274        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3275        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
3276
3277        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx));
3278        cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
3279    }
3280}
3281
3282#[gpui::test]
3283async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
3284    init_test(cx, |_| {});
3285
3286    let language = Arc::new(Language::new(
3287        LanguageConfig::default(),
3288        Some(tree_sitter_rust::language()),
3289    ));
3290
3291    let text = r#"
3292        use mod1::mod2::{mod3, mod4};
3293
3294        fn fn_1(param1: bool, param2: &str) {
3295            let var1 = "text";
3296        }
3297    "#
3298    .unindent();
3299
3300    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3301    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3302    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
3303    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
3304        .await;
3305
3306    view.update(cx, |view, cx| {
3307        view.change_selections(None, cx, |s| {
3308            s.select_display_ranges([
3309                DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3310                DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3311                DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3312            ]);
3313        });
3314        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3315    });
3316    assert_eq!(
3317        view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
3318        &[
3319            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
3320            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3321            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
3322        ]
3323    );
3324
3325    view.update(cx, |view, cx| {
3326        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3327    });
3328    assert_eq!(
3329        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3330        &[
3331            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3332            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
3333        ]
3334    );
3335
3336    view.update(cx, |view, cx| {
3337        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3338    });
3339    assert_eq!(
3340        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3341        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
3342    );
3343
3344    // Trying to expand the selected syntax node one more time has no effect.
3345    view.update(cx, |view, cx| {
3346        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3347    });
3348    assert_eq!(
3349        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3350        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
3351    );
3352
3353    view.update(cx, |view, cx| {
3354        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3355    });
3356    assert_eq!(
3357        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3358        &[
3359            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3360            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
3361        ]
3362    );
3363
3364    view.update(cx, |view, cx| {
3365        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3366    });
3367    assert_eq!(
3368        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3369        &[
3370            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
3371            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3372            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
3373        ]
3374    );
3375
3376    view.update(cx, |view, cx| {
3377        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3378    });
3379    assert_eq!(
3380        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3381        &[
3382            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3383            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3384            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3385        ]
3386    );
3387
3388    // Trying to shrink the selected syntax node one more time has no effect.
3389    view.update(cx, |view, cx| {
3390        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
3391    });
3392    assert_eq!(
3393        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3394        &[
3395            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
3396            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
3397            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
3398        ]
3399    );
3400
3401    // Ensure that we keep expanding the selection if the larger selection starts or ends within
3402    // a fold.
3403    view.update(cx, |view, cx| {
3404        view.fold_ranges(
3405            vec![
3406                Point::new(0, 21)..Point::new(0, 24),
3407                Point::new(3, 20)..Point::new(3, 22),
3408            ],
3409            true,
3410            cx,
3411        );
3412        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
3413    });
3414    assert_eq!(
3415        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
3416        &[
3417            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
3418            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
3419            DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
3420        ]
3421    );
3422}
3423
3424#[gpui::test]
3425async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
3426    init_test(cx, |_| {});
3427
3428    let language = Arc::new(
3429        Language::new(
3430            LanguageConfig {
3431                brackets: BracketPairConfig {
3432                    pairs: vec![
3433                        BracketPair {
3434                            start: "{".to_string(),
3435                            end: "}".to_string(),
3436                            close: false,
3437                            newline: true,
3438                        },
3439                        BracketPair {
3440                            start: "(".to_string(),
3441                            end: ")".to_string(),
3442                            close: false,
3443                            newline: true,
3444                        },
3445                    ],
3446                    ..Default::default()
3447                },
3448                ..Default::default()
3449            },
3450            Some(tree_sitter_rust::language()),
3451        )
3452        .with_indents_query(
3453            r#"
3454                (_ "(" ")" @end) @indent
3455                (_ "{" "}" @end) @indent
3456            "#,
3457        )
3458        .unwrap(),
3459    );
3460
3461    let text = "fn a() {}";
3462
3463    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
3464    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3465    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
3466    editor
3467        .condition(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
3468        .await;
3469
3470    editor.update(cx, |editor, cx| {
3471        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
3472        editor.newline(&Newline, cx);
3473        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
3474        assert_eq!(
3475            editor.selections.ranges(cx),
3476            &[
3477                Point::new(1, 4)..Point::new(1, 4),
3478                Point::new(3, 4)..Point::new(3, 4),
3479                Point::new(5, 0)..Point::new(5, 0)
3480            ]
3481        );
3482    });
3483}
3484
3485#[gpui::test]
3486async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
3487    init_test(cx, |_| {});
3488
3489    let mut cx = EditorTestContext::new(cx).await;
3490
3491    let language = Arc::new(Language::new(
3492        LanguageConfig {
3493            brackets: BracketPairConfig {
3494                pairs: vec![
3495                    BracketPair {
3496                        start: "{".to_string(),
3497                        end: "}".to_string(),
3498                        close: true,
3499                        newline: true,
3500                    },
3501                    BracketPair {
3502                        start: "(".to_string(),
3503                        end: ")".to_string(),
3504                        close: true,
3505                        newline: true,
3506                    },
3507                    BracketPair {
3508                        start: "/*".to_string(),
3509                        end: " */".to_string(),
3510                        close: true,
3511                        newline: true,
3512                    },
3513                    BracketPair {
3514                        start: "[".to_string(),
3515                        end: "]".to_string(),
3516                        close: false,
3517                        newline: true,
3518                    },
3519                    BracketPair {
3520                        start: "\"".to_string(),
3521                        end: "\"".to_string(),
3522                        close: true,
3523                        newline: false,
3524                    },
3525                ],
3526                ..Default::default()
3527            },
3528            autoclose_before: "})]".to_string(),
3529            ..Default::default()
3530        },
3531        Some(tree_sitter_rust::language()),
3532    ));
3533
3534    let registry = Arc::new(LanguageRegistry::test());
3535    registry.add(language.clone());
3536    cx.update_buffer(|buffer, cx| {
3537        buffer.set_language_registry(registry);
3538        buffer.set_language(Some(language), cx);
3539    });
3540
3541    cx.set_state(
3542        &r#"
3543            🏀ˇ
3544            εˇ
3545            ❤️ˇ
3546        "#
3547        .unindent(),
3548    );
3549
3550    // autoclose multiple nested brackets at multiple cursors
3551    cx.update_editor(|view, cx| {
3552        view.handle_input("{", cx);
3553        view.handle_input("{", cx);
3554        view.handle_input("{", cx);
3555    });
3556    cx.assert_editor_state(
3557        &"
3558            🏀{{{ˇ}}}
3559            ε{{{ˇ}}}
3560            ❤️{{{ˇ}}}
3561        "
3562        .unindent(),
3563    );
3564
3565    // insert a different closing bracket
3566    cx.update_editor(|view, cx| {
3567        view.handle_input(")", cx);
3568    });
3569    cx.assert_editor_state(
3570        &"
3571            🏀{{{)ˇ}}}
3572            ε{{{)ˇ}}}
3573            ❤️{{{)ˇ}}}
3574        "
3575        .unindent(),
3576    );
3577
3578    // skip over the auto-closed brackets when typing a closing bracket
3579    cx.update_editor(|view, cx| {
3580        view.move_right(&MoveRight, cx);
3581        view.handle_input("}", cx);
3582        view.handle_input("}", cx);
3583        view.handle_input("}", cx);
3584    });
3585    cx.assert_editor_state(
3586        &"
3587            🏀{{{)}}}}ˇ
3588            ε{{{)}}}}ˇ
3589            ❤️{{{)}}}}ˇ
3590        "
3591        .unindent(),
3592    );
3593
3594    // autoclose multi-character pairs
3595    cx.set_state(
3596        &"
3597            ˇ
3598            ˇ
3599        "
3600        .unindent(),
3601    );
3602    cx.update_editor(|view, cx| {
3603        view.handle_input("/", cx);
3604        view.handle_input("*", cx);
3605    });
3606    cx.assert_editor_state(
3607        &"
3608            /*ˇ */
3609            /*ˇ */
3610        "
3611        .unindent(),
3612    );
3613
3614    // one cursor autocloses a multi-character pair, one cursor
3615    // does not autoclose.
3616    cx.set_state(
3617        &"
36183619            ˇ
3620        "
3621        .unindent(),
3622    );
3623    cx.update_editor(|view, cx| view.handle_input("*", cx));
3624    cx.assert_editor_state(
3625        &"
3626            /*ˇ */
36273628        "
3629        .unindent(),
3630    );
3631
3632    // Don't autoclose if the next character isn't whitespace and isn't
3633    // listed in the language's "autoclose_before" section.
3634    cx.set_state("ˇa b");
3635    cx.update_editor(|view, cx| view.handle_input("{", cx));
3636    cx.assert_editor_state("{ˇa b");
3637
3638    // Don't autoclose if `close` is false for the bracket pair
3639    cx.set_state("ˇ");
3640    cx.update_editor(|view, cx| view.handle_input("[", cx));
3641    cx.assert_editor_state("");
3642
3643    // Surround with brackets if text is selected
3644    cx.set_state("«aˇ» b");
3645    cx.update_editor(|view, cx| view.handle_input("{", cx));
3646    cx.assert_editor_state("{«aˇ»} b");
3647
3648    // Autclose pair where the start and end characters are the same
3649    cx.set_state("");
3650    cx.update_editor(|view, cx| view.handle_input("\"", cx));
3651    cx.assert_editor_state("a\"ˇ\"");
3652    cx.update_editor(|view, cx| view.handle_input("\"", cx));
3653    cx.assert_editor_state("a\"\"ˇ");
3654}
3655
3656#[gpui::test]
3657async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
3658    init_test(cx, |_| {});
3659
3660    let mut cx = EditorTestContext::new(cx).await;
3661
3662    let html_language = Arc::new(
3663        Language::new(
3664            LanguageConfig {
3665                name: "HTML".into(),
3666                brackets: BracketPairConfig {
3667                    pairs: vec![
3668                        BracketPair {
3669                            start: "<".into(),
3670                            end: ">".into(),
3671                            close: true,
3672                            ..Default::default()
3673                        },
3674                        BracketPair {
3675                            start: "{".into(),
3676                            end: "}".into(),
3677                            close: true,
3678                            ..Default::default()
3679                        },
3680                        BracketPair {
3681                            start: "(".into(),
3682                            end: ")".into(),
3683                            close: true,
3684                            ..Default::default()
3685                        },
3686                    ],
3687                    ..Default::default()
3688                },
3689                autoclose_before: "})]>".into(),
3690                ..Default::default()
3691            },
3692            Some(tree_sitter_html::language()),
3693        )
3694        .with_injection_query(
3695            r#"
3696            (script_element
3697                (raw_text) @content
3698                (#set! "language" "javascript"))
3699            "#,
3700        )
3701        .unwrap(),
3702    );
3703
3704    let javascript_language = Arc::new(Language::new(
3705        LanguageConfig {
3706            name: "JavaScript".into(),
3707            brackets: BracketPairConfig {
3708                pairs: vec![
3709                    BracketPair {
3710                        start: "/*".into(),
3711                        end: " */".into(),
3712                        close: true,
3713                        ..Default::default()
3714                    },
3715                    BracketPair {
3716                        start: "{".into(),
3717                        end: "}".into(),
3718                        close: true,
3719                        ..Default::default()
3720                    },
3721                    BracketPair {
3722                        start: "(".into(),
3723                        end: ")".into(),
3724                        close: true,
3725                        ..Default::default()
3726                    },
3727                ],
3728                ..Default::default()
3729            },
3730            autoclose_before: "})]>".into(),
3731            ..Default::default()
3732        },
3733        Some(tree_sitter_javascript::language()),
3734    ));
3735
3736    let registry = Arc::new(LanguageRegistry::test());
3737    registry.add(html_language.clone());
3738    registry.add(javascript_language.clone());
3739
3740    cx.update_buffer(|buffer, cx| {
3741        buffer.set_language_registry(registry);
3742        buffer.set_language(Some(html_language), cx);
3743    });
3744
3745    cx.set_state(
3746        &r#"
3747            <body>ˇ
3748                <script>
3749                    var x = 1;ˇ
3750                </script>
3751            </body>ˇ
3752        "#
3753        .unindent(),
3754    );
3755
3756    // Precondition: different languages are active at different locations.
3757    cx.update_editor(|editor, cx| {
3758        let snapshot = editor.snapshot(cx);
3759        let cursors = editor.selections.ranges::<usize>(cx);
3760        let languages = cursors
3761            .iter()
3762            .map(|c| snapshot.language_at(c.start).unwrap().name())
3763            .collect::<Vec<_>>();
3764        assert_eq!(
3765            languages,
3766            &["HTML".into(), "JavaScript".into(), "HTML".into()]
3767        );
3768    });
3769
3770    // Angle brackets autoclose in HTML, but not JavaScript.
3771    cx.update_editor(|editor, cx| {
3772        editor.handle_input("<", cx);
3773        editor.handle_input("a", cx);
3774    });
3775    cx.assert_editor_state(
3776        &r#"
3777            <body><aˇ>
3778                <script>
3779                    var x = 1;<aˇ
3780                </script>
3781            </body><aˇ>
3782        "#
3783        .unindent(),
3784    );
3785
3786    // Curly braces and parens autoclose in both HTML and JavaScript.
3787    cx.update_editor(|editor, cx| {
3788        editor.handle_input(" b=", cx);
3789        editor.handle_input("{", cx);
3790        editor.handle_input("c", cx);
3791        editor.handle_input("(", cx);
3792    });
3793    cx.assert_editor_state(
3794        &r#"
3795            <body><a b={c(ˇ)}>
3796                <script>
3797                    var x = 1;<a b={c(ˇ)}
3798                </script>
3799            </body><a b={c(ˇ)}>
3800        "#
3801        .unindent(),
3802    );
3803
3804    // Brackets that were already autoclosed are skipped.
3805    cx.update_editor(|editor, cx| {
3806        editor.handle_input(")", cx);
3807        editor.handle_input("d", cx);
3808        editor.handle_input("}", cx);
3809    });
3810    cx.assert_editor_state(
3811        &r#"
3812            <body><a b={c()d}ˇ>
3813                <script>
3814                    var x = 1;<a b={c()d}ˇ
3815                </script>
3816            </body><a b={c()d}ˇ>
3817        "#
3818        .unindent(),
3819    );
3820    cx.update_editor(|editor, cx| {
3821        editor.handle_input(">", cx);
3822    });
3823    cx.assert_editor_state(
3824        &r#"
3825            <body><a b={c()d}>ˇ
3826                <script>
3827                    var x = 1;<a b={c()d}>ˇ
3828                </script>
3829            </body><a b={c()d}>ˇ
3830        "#
3831        .unindent(),
3832    );
3833
3834    // Reset
3835    cx.set_state(
3836        &r#"
3837            <body>ˇ
3838                <script>
3839                    var x = 1;ˇ
3840                </script>
3841            </body>ˇ
3842        "#
3843        .unindent(),
3844    );
3845
3846    cx.update_editor(|editor, cx| {
3847        editor.handle_input("<", cx);
3848    });
3849    cx.assert_editor_state(
3850        &r#"
3851            <body><ˇ>
3852                <script>
3853                    var x = 1;<ˇ
3854                </script>
3855            </body><ˇ>
3856        "#
3857        .unindent(),
3858    );
3859
3860    // When backspacing, the closing angle brackets are removed.
3861    cx.update_editor(|editor, cx| {
3862        editor.backspace(&Backspace, cx);
3863    });
3864    cx.assert_editor_state(
3865        &r#"
3866            <body>ˇ
3867                <script>
3868                    var x = 1;ˇ
3869                </script>
3870            </body>ˇ
3871        "#
3872        .unindent(),
3873    );
3874
3875    // Block comments autoclose in JavaScript, but not HTML.
3876    cx.update_editor(|editor, cx| {
3877        editor.handle_input("/", cx);
3878        editor.handle_input("*", cx);
3879    });
3880    cx.assert_editor_state(
3881        &r#"
3882            <body>/*ˇ
3883                <script>
3884                    var x = 1;/*ˇ */
3885                </script>
3886            </body>/*ˇ
3887        "#
3888        .unindent(),
3889    );
3890}
3891
3892#[gpui::test]
3893async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
3894    init_test(cx, |_| {});
3895
3896    let mut cx = EditorTestContext::new(cx).await;
3897
3898    let rust_language = Arc::new(
3899        Language::new(
3900            LanguageConfig {
3901                name: "Rust".into(),
3902                brackets: serde_json::from_value(json!([
3903                    { "start": "{", "end": "}", "close": true, "newline": true },
3904                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
3905                ]))
3906                .unwrap(),
3907                autoclose_before: "})]>".into(),
3908                ..Default::default()
3909            },
3910            Some(tree_sitter_rust::language()),
3911        )
3912        .with_override_query("(string_literal) @string")
3913        .unwrap(),
3914    );
3915
3916    let registry = Arc::new(LanguageRegistry::test());
3917    registry.add(rust_language.clone());
3918
3919    cx.update_buffer(|buffer, cx| {
3920        buffer.set_language_registry(registry);
3921        buffer.set_language(Some(rust_language), cx);
3922    });
3923
3924    cx.set_state(
3925        &r#"
3926            let x = ˇ
3927        "#
3928        .unindent(),
3929    );
3930
3931    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
3932    cx.update_editor(|editor, cx| {
3933        editor.handle_input("\"", cx);
3934    });
3935    cx.assert_editor_state(
3936        &r#"
3937            let x = "ˇ"
3938        "#
3939        .unindent(),
3940    );
3941
3942    // Inserting another quotation mark. The cursor moves across the existing
3943    // automatically-inserted quotation mark.
3944    cx.update_editor(|editor, cx| {
3945        editor.handle_input("\"", cx);
3946    });
3947    cx.assert_editor_state(
3948        &r#"
3949            let x = ""ˇ
3950        "#
3951        .unindent(),
3952    );
3953
3954    // Reset
3955    cx.set_state(
3956        &r#"
3957            let x = ˇ
3958        "#
3959        .unindent(),
3960    );
3961
3962    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
3963    cx.update_editor(|editor, cx| {
3964        editor.handle_input("\"", cx);
3965        editor.handle_input(" ", cx);
3966        editor.move_left(&Default::default(), cx);
3967        editor.handle_input("\\", cx);
3968        editor.handle_input("\"", cx);
3969    });
3970    cx.assert_editor_state(
3971        &r#"
3972            let x = "\"ˇ "
3973        "#
3974        .unindent(),
3975    );
3976
3977    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
3978    // mark. Nothing is inserted.
3979    cx.update_editor(|editor, cx| {
3980        editor.move_right(&Default::default(), cx);
3981        editor.handle_input("\"", cx);
3982    });
3983    cx.assert_editor_state(
3984        &r#"
3985            let x = "\" "ˇ
3986        "#
3987        .unindent(),
3988    );
3989}
3990
3991#[gpui::test]
3992async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
3993    init_test(cx, |_| {});
3994
3995    let language = Arc::new(Language::new(
3996        LanguageConfig {
3997            brackets: BracketPairConfig {
3998                pairs: vec![
3999                    BracketPair {
4000                        start: "{".to_string(),
4001                        end: "}".to_string(),
4002                        close: true,
4003                        newline: true,
4004                    },
4005                    BracketPair {
4006                        start: "/* ".to_string(),
4007                        end: "*/".to_string(),
4008                        close: true,
4009                        ..Default::default()
4010                    },
4011                ],
4012                ..Default::default()
4013            },
4014            ..Default::default()
4015        },
4016        Some(tree_sitter_rust::language()),
4017    ));
4018
4019    let text = r#"
4020        a
4021        b
4022        c
4023    "#
4024    .unindent();
4025
4026    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
4027    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4028    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
4029    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
4030        .await;
4031
4032    view.update(cx, |view, cx| {
4033        view.change_selections(None, cx, |s| {
4034            s.select_display_ranges([
4035                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
4036                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
4037                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
4038            ])
4039        });
4040
4041        view.handle_input("{", cx);
4042        view.handle_input("{", cx);
4043        view.handle_input("{", cx);
4044        assert_eq!(
4045            view.text(cx),
4046            "
4047                {{{a}}}
4048                {{{b}}}
4049                {{{c}}}
4050            "
4051            .unindent()
4052        );
4053        assert_eq!(
4054            view.selections.display_ranges(cx),
4055            [
4056                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
4057                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
4058                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
4059            ]
4060        );
4061
4062        view.undo(&Undo, cx);
4063        view.undo(&Undo, cx);
4064        view.undo(&Undo, cx);
4065        assert_eq!(
4066            view.text(cx),
4067            "
4068                a
4069                b
4070                c
4071            "
4072            .unindent()
4073        );
4074        assert_eq!(
4075            view.selections.display_ranges(cx),
4076            [
4077                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
4078                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
4079                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
4080            ]
4081        );
4082
4083        // Ensure inserting the first character of a multi-byte bracket pair
4084        // doesn't surround the selections with the bracket.
4085        view.handle_input("/", cx);
4086        assert_eq!(
4087            view.text(cx),
4088            "
4089                /
4090                /
4091                /
4092            "
4093            .unindent()
4094        );
4095        assert_eq!(
4096            view.selections.display_ranges(cx),
4097            [
4098                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
4099                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
4100                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
4101            ]
4102        );
4103
4104        view.undo(&Undo, cx);
4105        assert_eq!(
4106            view.text(cx),
4107            "
4108                a
4109                b
4110                c
4111            "
4112            .unindent()
4113        );
4114        assert_eq!(
4115            view.selections.display_ranges(cx),
4116            [
4117                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
4118                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
4119                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
4120            ]
4121        );
4122
4123        // Ensure inserting the last character of a multi-byte bracket pair
4124        // doesn't surround the selections with the bracket.
4125        view.handle_input("*", cx);
4126        assert_eq!(
4127            view.text(cx),
4128            "
4129                *
4130                *
4131                *
4132            "
4133            .unindent()
4134        );
4135        assert_eq!(
4136            view.selections.display_ranges(cx),
4137            [
4138                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
4139                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
4140                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
4141            ]
4142        );
4143    });
4144}
4145
4146#[gpui::test]
4147async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
4148    init_test(cx, |_| {});
4149
4150    let language = Arc::new(Language::new(
4151        LanguageConfig {
4152            brackets: BracketPairConfig {
4153                pairs: vec![BracketPair {
4154                    start: "{".to_string(),
4155                    end: "}".to_string(),
4156                    close: true,
4157                    newline: true,
4158                }],
4159                ..Default::default()
4160            },
4161            autoclose_before: "}".to_string(),
4162            ..Default::default()
4163        },
4164        Some(tree_sitter_rust::language()),
4165    ));
4166
4167    let text = r#"
4168        a
4169        b
4170        c
4171    "#
4172    .unindent();
4173
4174    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
4175    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4176    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4177    editor
4178        .condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
4179        .await;
4180
4181    editor.update(cx, |editor, cx| {
4182        editor.change_selections(None, cx, |s| {
4183            s.select_ranges([
4184                Point::new(0, 1)..Point::new(0, 1),
4185                Point::new(1, 1)..Point::new(1, 1),
4186                Point::new(2, 1)..Point::new(2, 1),
4187            ])
4188        });
4189
4190        editor.handle_input("{", cx);
4191        editor.handle_input("{", cx);
4192        editor.handle_input("_", cx);
4193        assert_eq!(
4194            editor.text(cx),
4195            "
4196                a{{_}}
4197                b{{_}}
4198                c{{_}}
4199            "
4200            .unindent()
4201        );
4202        assert_eq!(
4203            editor.selections.ranges::<Point>(cx),
4204            [
4205                Point::new(0, 4)..Point::new(0, 4),
4206                Point::new(1, 4)..Point::new(1, 4),
4207                Point::new(2, 4)..Point::new(2, 4)
4208            ]
4209        );
4210
4211        editor.backspace(&Default::default(), cx);
4212        editor.backspace(&Default::default(), cx);
4213        assert_eq!(
4214            editor.text(cx),
4215            "
4216                a{}
4217                b{}
4218                c{}
4219            "
4220            .unindent()
4221        );
4222        assert_eq!(
4223            editor.selections.ranges::<Point>(cx),
4224            [
4225                Point::new(0, 2)..Point::new(0, 2),
4226                Point::new(1, 2)..Point::new(1, 2),
4227                Point::new(2, 2)..Point::new(2, 2)
4228            ]
4229        );
4230
4231        editor.delete_to_previous_word_start(&Default::default(), cx);
4232        assert_eq!(
4233            editor.text(cx),
4234            "
4235                a
4236                b
4237                c
4238            "
4239            .unindent()
4240        );
4241        assert_eq!(
4242            editor.selections.ranges::<Point>(cx),
4243            [
4244                Point::new(0, 1)..Point::new(0, 1),
4245                Point::new(1, 1)..Point::new(1, 1),
4246                Point::new(2, 1)..Point::new(2, 1)
4247            ]
4248        );
4249    });
4250}
4251
4252#[gpui::test]
4253async fn test_snippets(cx: &mut gpui::TestAppContext) {
4254    init_test(cx, |_| {});
4255
4256    let (text, insertion_ranges) = marked_text_ranges(
4257        indoc! {"
4258            a.ˇ b
4259            a.ˇ b
4260            a.ˇ b
4261        "},
4262        false,
4263    );
4264
4265    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
4266    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4267
4268    editor.update(cx, |editor, cx| {
4269        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
4270
4271        editor
4272            .insert_snippet(&insertion_ranges, snippet, cx)
4273            .unwrap();
4274
4275        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
4276            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
4277            assert_eq!(editor.text(cx), expected_text);
4278            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
4279        }
4280
4281        assert(
4282            editor,
4283            cx,
4284            indoc! {"
4285                a.f(«one», two, «three») b
4286                a.f(«one», two, «three») b
4287                a.f(«one», two, «three») b
4288            "},
4289        );
4290
4291        // Can't move earlier than the first tab stop
4292        assert!(!editor.move_to_prev_snippet_tabstop(cx));
4293        assert(
4294            editor,
4295            cx,
4296            indoc! {"
4297                a.f(«one», two, «three») b
4298                a.f(«one», two, «three») b
4299                a.f(«one», two, «three») b
4300            "},
4301        );
4302
4303        assert!(editor.move_to_next_snippet_tabstop(cx));
4304        assert(
4305            editor,
4306            cx,
4307            indoc! {"
4308                a.f(one, «two», three) b
4309                a.f(one, «two», three) b
4310                a.f(one, «two», three) b
4311            "},
4312        );
4313
4314        editor.move_to_prev_snippet_tabstop(cx);
4315        assert(
4316            editor,
4317            cx,
4318            indoc! {"
4319                a.f(«one», two, «three») b
4320                a.f(«one», two, «three») b
4321                a.f(«one», two, «three») b
4322            "},
4323        );
4324
4325        assert!(editor.move_to_next_snippet_tabstop(cx));
4326        assert(
4327            editor,
4328            cx,
4329            indoc! {"
4330                a.f(one, «two», three) b
4331                a.f(one, «two», three) b
4332                a.f(one, «two», three) b
4333            "},
4334        );
4335        assert!(editor.move_to_next_snippet_tabstop(cx));
4336        assert(
4337            editor,
4338            cx,
4339            indoc! {"
4340                a.f(one, two, three)ˇ b
4341                a.f(one, two, three)ˇ b
4342                a.f(one, two, three)ˇ b
4343            "},
4344        );
4345
4346        // As soon as the last tab stop is reached, snippet state is gone
4347        editor.move_to_prev_snippet_tabstop(cx);
4348        assert(
4349            editor,
4350            cx,
4351            indoc! {"
4352                a.f(one, two, three)ˇ b
4353                a.f(one, two, three)ˇ b
4354                a.f(one, two, three)ˇ b
4355            "},
4356        );
4357    });
4358}
4359
4360#[gpui::test]
4361async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
4362    init_test(cx, |_| {});
4363
4364    let mut language = Language::new(
4365        LanguageConfig {
4366            name: "Rust".into(),
4367            path_suffixes: vec!["rs".to_string()],
4368            ..Default::default()
4369        },
4370        Some(tree_sitter_rust::language()),
4371    );
4372    let mut fake_servers = language
4373        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4374            capabilities: lsp::ServerCapabilities {
4375                document_formatting_provider: Some(lsp::OneOf::Left(true)),
4376                ..Default::default()
4377            },
4378            ..Default::default()
4379        }))
4380        .await;
4381
4382    let fs = FakeFs::new(cx.background());
4383    fs.insert_file("/file.rs", Default::default()).await;
4384
4385    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4386    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4387    let buffer = project
4388        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4389        .await
4390        .unwrap();
4391
4392    cx.foreground().start_waiting();
4393    let fake_server = fake_servers.next().await.unwrap();
4394
4395    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4396    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4397    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4398    assert!(cx.read(|cx| editor.is_dirty(cx)));
4399
4400    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4401    fake_server
4402        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4403            assert_eq!(
4404                params.text_document.uri,
4405                lsp::Url::from_file_path("/file.rs").unwrap()
4406            );
4407            assert_eq!(params.options.tab_size, 4);
4408            Ok(Some(vec![lsp::TextEdit::new(
4409                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4410                ", ".to_string(),
4411            )]))
4412        })
4413        .next()
4414        .await;
4415    cx.foreground().start_waiting();
4416    save.await.unwrap();
4417    assert_eq!(
4418        editor.read_with(cx, |editor, cx| editor.text(cx)),
4419        "one, two\nthree\n"
4420    );
4421    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4422
4423    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4424    assert!(cx.read(|cx| editor.is_dirty(cx)));
4425
4426    // Ensure we can still save even if formatting hangs.
4427    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4428        assert_eq!(
4429            params.text_document.uri,
4430            lsp::Url::from_file_path("/file.rs").unwrap()
4431        );
4432        futures::future::pending::<()>().await;
4433        unreachable!()
4434    });
4435    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4436    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4437    cx.foreground().start_waiting();
4438    save.await.unwrap();
4439    assert_eq!(
4440        editor.read_with(cx, |editor, cx| editor.text(cx)),
4441        "one\ntwo\nthree\n"
4442    );
4443    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4444
4445    // Set rust language override and assert overridden tabsize is sent to language server
4446    update_test_settings(cx, |settings| {
4447        settings.languages.insert(
4448            "Rust".into(),
4449            LanguageSettingsContent {
4450                tab_size: NonZeroU32::new(8),
4451                ..Default::default()
4452            },
4453        );
4454    });
4455
4456    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4457    fake_server
4458        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4459            assert_eq!(
4460                params.text_document.uri,
4461                lsp::Url::from_file_path("/file.rs").unwrap()
4462            );
4463            assert_eq!(params.options.tab_size, 8);
4464            Ok(Some(vec![]))
4465        })
4466        .next()
4467        .await;
4468    cx.foreground().start_waiting();
4469    save.await.unwrap();
4470}
4471
4472#[gpui::test]
4473async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
4474    init_test(cx, |_| {});
4475
4476    let mut language = Language::new(
4477        LanguageConfig {
4478            name: "Rust".into(),
4479            path_suffixes: vec!["rs".to_string()],
4480            ..Default::default()
4481        },
4482        Some(tree_sitter_rust::language()),
4483    );
4484    let mut fake_servers = language
4485        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4486            capabilities: lsp::ServerCapabilities {
4487                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
4488                ..Default::default()
4489            },
4490            ..Default::default()
4491        }))
4492        .await;
4493
4494    let fs = FakeFs::new(cx.background());
4495    fs.insert_file("/file.rs", Default::default()).await;
4496
4497    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4498    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4499    let buffer = project
4500        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4501        .await
4502        .unwrap();
4503
4504    cx.foreground().start_waiting();
4505    let fake_server = fake_servers.next().await.unwrap();
4506
4507    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4508    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4509    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4510    assert!(cx.read(|cx| editor.is_dirty(cx)));
4511
4512    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4513    fake_server
4514        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
4515            assert_eq!(
4516                params.text_document.uri,
4517                lsp::Url::from_file_path("/file.rs").unwrap()
4518            );
4519            assert_eq!(params.options.tab_size, 4);
4520            Ok(Some(vec![lsp::TextEdit::new(
4521                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4522                ", ".to_string(),
4523            )]))
4524        })
4525        .next()
4526        .await;
4527    cx.foreground().start_waiting();
4528    save.await.unwrap();
4529    assert_eq!(
4530        editor.read_with(cx, |editor, cx| editor.text(cx)),
4531        "one, two\nthree\n"
4532    );
4533    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4534
4535    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4536    assert!(cx.read(|cx| editor.is_dirty(cx)));
4537
4538    // Ensure we can still save even if formatting hangs.
4539    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
4540        move |params, _| async move {
4541            assert_eq!(
4542                params.text_document.uri,
4543                lsp::Url::from_file_path("/file.rs").unwrap()
4544            );
4545            futures::future::pending::<()>().await;
4546            unreachable!()
4547        },
4548    );
4549    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4550    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4551    cx.foreground().start_waiting();
4552    save.await.unwrap();
4553    assert_eq!(
4554        editor.read_with(cx, |editor, cx| editor.text(cx)),
4555        "one\ntwo\nthree\n"
4556    );
4557    assert!(!cx.read(|cx| editor.is_dirty(cx)));
4558
4559    // Set rust language override and assert overridden tabsize is sent to language server
4560    update_test_settings(cx, |settings| {
4561        settings.languages.insert(
4562            "Rust".into(),
4563            LanguageSettingsContent {
4564                tab_size: NonZeroU32::new(8),
4565                ..Default::default()
4566            },
4567        );
4568    });
4569
4570    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
4571    fake_server
4572        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
4573            assert_eq!(
4574                params.text_document.uri,
4575                lsp::Url::from_file_path("/file.rs").unwrap()
4576            );
4577            assert_eq!(params.options.tab_size, 8);
4578            Ok(Some(vec![]))
4579        })
4580        .next()
4581        .await;
4582    cx.foreground().start_waiting();
4583    save.await.unwrap();
4584}
4585
4586#[gpui::test]
4587async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
4588    init_test(cx, |_| {});
4589
4590    let mut language = Language::new(
4591        LanguageConfig {
4592            name: "Rust".into(),
4593            path_suffixes: vec!["rs".to_string()],
4594            ..Default::default()
4595        },
4596        Some(tree_sitter_rust::language()),
4597    );
4598    let mut fake_servers = language
4599        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
4600            capabilities: lsp::ServerCapabilities {
4601                document_formatting_provider: Some(lsp::OneOf::Left(true)),
4602                ..Default::default()
4603            },
4604            ..Default::default()
4605        }))
4606        .await;
4607
4608    let fs = FakeFs::new(cx.background());
4609    fs.insert_file("/file.rs", Default::default()).await;
4610
4611    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
4612    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
4613    let buffer = project
4614        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
4615        .await
4616        .unwrap();
4617
4618    cx.foreground().start_waiting();
4619    let fake_server = fake_servers.next().await.unwrap();
4620
4621    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
4622    let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
4623    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4624
4625    let format = editor.update(cx, |editor, cx| {
4626        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
4627    });
4628    fake_server
4629        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4630            assert_eq!(
4631                params.text_document.uri,
4632                lsp::Url::from_file_path("/file.rs").unwrap()
4633            );
4634            assert_eq!(params.options.tab_size, 4);
4635            Ok(Some(vec![lsp::TextEdit::new(
4636                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
4637                ", ".to_string(),
4638            )]))
4639        })
4640        .next()
4641        .await;
4642    cx.foreground().start_waiting();
4643    format.await.unwrap();
4644    assert_eq!(
4645        editor.read_with(cx, |editor, cx| editor.text(cx)),
4646        "one, two\nthree\n"
4647    );
4648
4649    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
4650    // Ensure we don't lock if formatting hangs.
4651    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
4652        assert_eq!(
4653            params.text_document.uri,
4654            lsp::Url::from_file_path("/file.rs").unwrap()
4655        );
4656        futures::future::pending::<()>().await;
4657        unreachable!()
4658    });
4659    let format = editor.update(cx, |editor, cx| {
4660        editor.perform_format(project, FormatTrigger::Manual, cx)
4661    });
4662    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
4663    cx.foreground().start_waiting();
4664    format.await.unwrap();
4665    assert_eq!(
4666        editor.read_with(cx, |editor, cx| editor.text(cx)),
4667        "one\ntwo\nthree\n"
4668    );
4669}
4670
4671#[gpui::test]
4672async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
4673    init_test(cx, |_| {});
4674
4675    let mut cx = EditorLspTestContext::new_rust(
4676        lsp::ServerCapabilities {
4677            document_formatting_provider: Some(lsp::OneOf::Left(true)),
4678            ..Default::default()
4679        },
4680        cx,
4681    )
4682    .await;
4683
4684    cx.set_state(indoc! {"
4685        one.twoˇ
4686    "});
4687
4688    // The format request takes a long time. When it completes, it inserts
4689    // a newline and an indent before the `.`
4690    cx.lsp
4691        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
4692            let executor = cx.background();
4693            async move {
4694                executor.timer(Duration::from_millis(100)).await;
4695                Ok(Some(vec![lsp::TextEdit {
4696                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
4697                    new_text: "\n    ".into(),
4698                }]))
4699            }
4700        });
4701
4702    // Submit a format request.
4703    let format_1 = cx
4704        .update_editor(|editor, cx| editor.format(&Format, cx))
4705        .unwrap();
4706    cx.foreground().run_until_parked();
4707
4708    // Submit a second format request.
4709    let format_2 = cx
4710        .update_editor(|editor, cx| editor.format(&Format, cx))
4711        .unwrap();
4712    cx.foreground().run_until_parked();
4713
4714    // Wait for both format requests to complete
4715    cx.foreground().advance_clock(Duration::from_millis(200));
4716    cx.foreground().start_waiting();
4717    format_1.await.unwrap();
4718    cx.foreground().start_waiting();
4719    format_2.await.unwrap();
4720
4721    // The formatting edits only happens once.
4722    cx.assert_editor_state(indoc! {"
4723        one
4724            .twoˇ
4725    "});
4726}
4727
4728#[gpui::test]
4729async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
4730    init_test(cx, |_| {});
4731
4732    let mut cx = EditorLspTestContext::new_rust(
4733        lsp::ServerCapabilities {
4734            document_formatting_provider: Some(lsp::OneOf::Left(true)),
4735            ..Default::default()
4736        },
4737        cx,
4738    )
4739    .await;
4740
4741    // Set up a buffer white some trailing whitespace and no trailing newline.
4742    cx.set_state(
4743        &[
4744            "one ",   //
4745            "twoˇ",   //
4746            "three ", //
4747            "four",   //
4748        ]
4749        .join("\n"),
4750    );
4751
4752    // Submit a format request.
4753    let format = cx
4754        .update_editor(|editor, cx| editor.format(&Format, cx))
4755        .unwrap();
4756
4757    // Record which buffer changes have been sent to the language server
4758    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
4759    cx.lsp
4760        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
4761            let buffer_changes = buffer_changes.clone();
4762            move |params, _| {
4763                buffer_changes.lock().extend(
4764                    params
4765                        .content_changes
4766                        .into_iter()
4767                        .map(|e| (e.range.unwrap(), e.text)),
4768                );
4769            }
4770        });
4771
4772    // Handle formatting requests to the language server.
4773    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
4774        let buffer_changes = buffer_changes.clone();
4775        move |_, _| {
4776            // When formatting is requested, trailing whitespace has already been stripped,
4777            // and the trailing newline has already been added.
4778            assert_eq!(
4779                &buffer_changes.lock()[1..],
4780                &[
4781                    (
4782                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
4783                        "".into()
4784                    ),
4785                    (
4786                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
4787                        "".into()
4788                    ),
4789                    (
4790                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
4791                        "\n".into()
4792                    ),
4793                ]
4794            );
4795
4796            // Insert blank lines between each line of the buffer.
4797            async move {
4798                Ok(Some(vec![
4799                    lsp::TextEdit {
4800                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
4801                        new_text: "\n".into(),
4802                    },
4803                    lsp::TextEdit {
4804                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
4805                        new_text: "\n".into(),
4806                    },
4807                ]))
4808            }
4809        }
4810    });
4811
4812    // After formatting the buffer, the trailing whitespace is stripped,
4813    // a newline is appended, and the edits provided by the language server
4814    // have been applied.
4815    format.await.unwrap();
4816    cx.assert_editor_state(
4817        &[
4818            "one",   //
4819            "",      //
4820            "twoˇ",  //
4821            "",      //
4822            "three", //
4823            "four",  //
4824            "",      //
4825        ]
4826        .join("\n"),
4827    );
4828
4829    // Undoing the formatting undoes the trailing whitespace removal, the
4830    // trailing newline, and the LSP edits.
4831    cx.update_buffer(|buffer, cx| buffer.undo(cx));
4832    cx.assert_editor_state(
4833        &[
4834            "one ",   //
4835            "twoˇ",   //
4836            "three ", //
4837            "four",   //
4838        ]
4839        .join("\n"),
4840    );
4841}
4842
4843#[gpui::test]
4844async fn test_completion(cx: &mut gpui::TestAppContext) {
4845    init_test(cx, |_| {});
4846
4847    let mut cx = EditorLspTestContext::new_rust(
4848        lsp::ServerCapabilities {
4849            completion_provider: Some(lsp::CompletionOptions {
4850                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
4851                ..Default::default()
4852            }),
4853            ..Default::default()
4854        },
4855        cx,
4856    )
4857    .await;
4858
4859    cx.set_state(indoc! {"
4860        oneˇ
4861        two
4862        three
4863    "});
4864    cx.simulate_keystroke(".");
4865    handle_completion_request(
4866        &mut cx,
4867        indoc! {"
4868            one.|<>
4869            two
4870            three
4871        "},
4872        vec!["first_completion", "second_completion"],
4873    )
4874    .await;
4875    cx.condition(|editor, _| editor.context_menu_visible())
4876        .await;
4877    let apply_additional_edits = cx.update_editor(|editor, cx| {
4878        editor.move_down(&MoveDown, cx);
4879        editor
4880            .confirm_completion(&ConfirmCompletion::default(), cx)
4881            .unwrap()
4882    });
4883    cx.assert_editor_state(indoc! {"
4884        one.second_completionˇ
4885        two
4886        three
4887    "});
4888
4889    handle_resolve_completion_request(
4890        &mut cx,
4891        Some(vec![
4892            (
4893                //This overlaps with the primary completion edit which is
4894                //misbehavior from the LSP spec, test that we filter it out
4895                indoc! {"
4896                    one.second_ˇcompletion
4897                    two
4898                    threeˇ
4899                "},
4900                "overlapping additional edit",
4901            ),
4902            (
4903                indoc! {"
4904                    one.second_completion
4905                    two
4906                    threeˇ
4907                "},
4908                "\nadditional edit",
4909            ),
4910        ]),
4911    )
4912    .await;
4913    apply_additional_edits.await.unwrap();
4914    cx.assert_editor_state(indoc! {"
4915        one.second_completionˇ
4916        two
4917        three
4918        additional edit
4919    "});
4920
4921    cx.set_state(indoc! {"
4922        one.second_completion
4923        twoˇ
4924        threeˇ
4925        additional edit
4926    "});
4927    cx.simulate_keystroke(" ");
4928    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4929    cx.simulate_keystroke("s");
4930    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4931
4932    cx.assert_editor_state(indoc! {"
4933        one.second_completion
4934        two sˇ
4935        three sˇ
4936        additional edit
4937    "});
4938    handle_completion_request(
4939        &mut cx,
4940        indoc! {"
4941            one.second_completion
4942            two s
4943            three <s|>
4944            additional edit
4945        "},
4946        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
4947    )
4948    .await;
4949    cx.condition(|editor, _| editor.context_menu_visible())
4950        .await;
4951
4952    cx.simulate_keystroke("i");
4953
4954    handle_completion_request(
4955        &mut cx,
4956        indoc! {"
4957            one.second_completion
4958            two si
4959            three <si|>
4960            additional edit
4961        "},
4962        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
4963    )
4964    .await;
4965    cx.condition(|editor, _| editor.context_menu_visible())
4966        .await;
4967
4968    let apply_additional_edits = cx.update_editor(|editor, cx| {
4969        editor
4970            .confirm_completion(&ConfirmCompletion::default(), cx)
4971            .unwrap()
4972    });
4973    cx.assert_editor_state(indoc! {"
4974        one.second_completion
4975        two sixth_completionˇ
4976        three sixth_completionˇ
4977        additional edit
4978    "});
4979
4980    handle_resolve_completion_request(&mut cx, None).await;
4981    apply_additional_edits.await.unwrap();
4982
4983    cx.update(|cx| {
4984        cx.update_global::<SettingsStore, _, _>(|settings, cx| {
4985            settings.update_user_settings::<EditorSettings>(cx, |settings| {
4986                settings.show_completions_on_input = Some(false);
4987            });
4988        })
4989    });
4990    cx.set_state("editorˇ");
4991    cx.simulate_keystroke(".");
4992    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4993    cx.simulate_keystroke("c");
4994    cx.simulate_keystroke("l");
4995    cx.simulate_keystroke("o");
4996    cx.assert_editor_state("editor.cloˇ");
4997    assert!(cx.editor(|e, _| e.context_menu.is_none()));
4998    cx.update_editor(|editor, cx| {
4999        editor.show_completions(&ShowCompletions, cx);
5000    });
5001    handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
5002    cx.condition(|editor, _| editor.context_menu_visible())
5003        .await;
5004    let apply_additional_edits = cx.update_editor(|editor, cx| {
5005        editor
5006            .confirm_completion(&ConfirmCompletion::default(), cx)
5007            .unwrap()
5008    });
5009    cx.assert_editor_state("editor.closeˇ");
5010    handle_resolve_completion_request(&mut cx, None).await;
5011    apply_additional_edits.await.unwrap();
5012}
5013
5014#[gpui::test]
5015async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
5016    init_test(cx, |_| {});
5017    let mut cx = EditorTestContext::new(cx).await;
5018    let language = Arc::new(Language::new(
5019        LanguageConfig {
5020            line_comment: Some("// ".into()),
5021            ..Default::default()
5022        },
5023        Some(tree_sitter_rust::language()),
5024    ));
5025    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
5026
5027    // If multiple selections intersect a line, the line is only toggled once.
5028    cx.set_state(indoc! {"
5029        fn a() {
5030            «//b();
5031            ˇ»// «c();
5032            //ˇ»  d();
5033        }
5034    "});
5035
5036    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5037
5038    cx.assert_editor_state(indoc! {"
5039        fn a() {
5040            «b();
5041            c();
5042            ˇ» d();
5043        }
5044    "});
5045
5046    // The comment prefix is inserted at the same column for every line in a
5047    // selection.
5048    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5049
5050    cx.assert_editor_state(indoc! {"
5051        fn a() {
5052            // «b();
5053            // c();
5054            ˇ»//  d();
5055        }
5056    "});
5057
5058    // If a selection ends at the beginning of a line, that line is not toggled.
5059    cx.set_selections_state(indoc! {"
5060        fn a() {
5061            // b();
5062            «// c();
5063        ˇ»    //  d();
5064        }
5065    "});
5066
5067    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5068
5069    cx.assert_editor_state(indoc! {"
5070        fn a() {
5071            // b();
5072            «c();
5073        ˇ»    //  d();
5074        }
5075    "});
5076
5077    // If a selection span a single line and is empty, the line is toggled.
5078    cx.set_state(indoc! {"
5079        fn a() {
5080            a();
5081            b();
5082        ˇ
5083        }
5084    "});
5085
5086    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5087
5088    cx.assert_editor_state(indoc! {"
5089        fn a() {
5090            a();
5091            b();
5092        //•ˇ
5093        }
5094    "});
5095
5096    // If a selection span multiple lines, empty lines are not toggled.
5097    cx.set_state(indoc! {"
5098        fn a() {
5099            «a();
5100
5101            c();ˇ»
5102        }
5103    "});
5104
5105    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5106
5107    cx.assert_editor_state(indoc! {"
5108        fn a() {
5109            // «a();
5110
5111            // c();ˇ»
5112        }
5113    "});
5114}
5115
5116#[gpui::test]
5117async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
5118    init_test(cx, |_| {});
5119
5120    let language = Arc::new(Language::new(
5121        LanguageConfig {
5122            line_comment: Some("// ".into()),
5123            ..Default::default()
5124        },
5125        Some(tree_sitter_rust::language()),
5126    ));
5127
5128    let registry = Arc::new(LanguageRegistry::test());
5129    registry.add(language.clone());
5130
5131    let mut cx = EditorTestContext::new(cx).await;
5132    cx.update_buffer(|buffer, cx| {
5133        buffer.set_language_registry(registry);
5134        buffer.set_language(Some(language), cx);
5135    });
5136
5137    let toggle_comments = &ToggleComments {
5138        advance_downwards: true,
5139    };
5140
5141    // Single cursor on one line -> advance
5142    // Cursor moves horizontally 3 characters as well on non-blank line
5143    cx.set_state(indoc!(
5144        "fn a() {
5145             ˇdog();
5146             cat();
5147        }"
5148    ));
5149    cx.update_editor(|editor, cx| {
5150        editor.toggle_comments(toggle_comments, cx);
5151    });
5152    cx.assert_editor_state(indoc!(
5153        "fn a() {
5154             // dog();
5155             catˇ();
5156        }"
5157    ));
5158
5159    // Single selection on one line -> don't advance
5160    cx.set_state(indoc!(
5161        "fn a() {
5162             «dog()ˇ»;
5163             cat();
5164        }"
5165    ));
5166    cx.update_editor(|editor, cx| {
5167        editor.toggle_comments(toggle_comments, cx);
5168    });
5169    cx.assert_editor_state(indoc!(
5170        "fn a() {
5171             // «dog()ˇ»;
5172             cat();
5173        }"
5174    ));
5175
5176    // Multiple cursors on one line -> advance
5177    cx.set_state(indoc!(
5178        "fn a() {
5179             ˇdˇog();
5180             cat();
5181        }"
5182    ));
5183    cx.update_editor(|editor, cx| {
5184        editor.toggle_comments(toggle_comments, cx);
5185    });
5186    cx.assert_editor_state(indoc!(
5187        "fn a() {
5188             // dog();
5189             catˇ(ˇ);
5190        }"
5191    ));
5192
5193    // Multiple cursors on one line, with selection -> don't advance
5194    cx.set_state(indoc!(
5195        "fn a() {
5196             ˇdˇog«()ˇ»;
5197             cat();
5198        }"
5199    ));
5200    cx.update_editor(|editor, cx| {
5201        editor.toggle_comments(toggle_comments, cx);
5202    });
5203    cx.assert_editor_state(indoc!(
5204        "fn a() {
5205             // ˇdˇog«()ˇ»;
5206             cat();
5207        }"
5208    ));
5209
5210    // Single cursor on one line -> advance
5211    // Cursor moves to column 0 on blank line
5212    cx.set_state(indoc!(
5213        "fn a() {
5214             ˇdog();
5215
5216             cat();
5217        }"
5218    ));
5219    cx.update_editor(|editor, cx| {
5220        editor.toggle_comments(toggle_comments, cx);
5221    });
5222    cx.assert_editor_state(indoc!(
5223        "fn a() {
5224             // dog();
5225        ˇ
5226             cat();
5227        }"
5228    ));
5229
5230    // Single cursor on one line -> advance
5231    // Cursor starts and ends at column 0
5232    cx.set_state(indoc!(
5233        "fn a() {
5234         ˇ    dog();
5235             cat();
5236        }"
5237    ));
5238    cx.update_editor(|editor, cx| {
5239        editor.toggle_comments(toggle_comments, cx);
5240    });
5241    cx.assert_editor_state(indoc!(
5242        "fn a() {
5243             // dog();
5244         ˇ    cat();
5245        }"
5246    ));
5247}
5248
5249#[gpui::test]
5250async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
5251    init_test(cx, |_| {});
5252
5253    let mut cx = EditorTestContext::new(cx).await;
5254
5255    let html_language = Arc::new(
5256        Language::new(
5257            LanguageConfig {
5258                name: "HTML".into(),
5259                block_comment: Some(("<!-- ".into(), " -->".into())),
5260                ..Default::default()
5261            },
5262            Some(tree_sitter_html::language()),
5263        )
5264        .with_injection_query(
5265            r#"
5266            (script_element
5267                (raw_text) @content
5268                (#set! "language" "javascript"))
5269            "#,
5270        )
5271        .unwrap(),
5272    );
5273
5274    let javascript_language = Arc::new(Language::new(
5275        LanguageConfig {
5276            name: "JavaScript".into(),
5277            line_comment: Some("// ".into()),
5278            ..Default::default()
5279        },
5280        Some(tree_sitter_javascript::language()),
5281    ));
5282
5283    let registry = Arc::new(LanguageRegistry::test());
5284    registry.add(html_language.clone());
5285    registry.add(javascript_language.clone());
5286
5287    cx.update_buffer(|buffer, cx| {
5288        buffer.set_language_registry(registry);
5289        buffer.set_language(Some(html_language), cx);
5290    });
5291
5292    // Toggle comments for empty selections
5293    cx.set_state(
5294        &r#"
5295            <p>A</p>ˇ
5296            <p>B</p>ˇ
5297            <p>C</p>ˇ
5298        "#
5299        .unindent(),
5300    );
5301    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5302    cx.assert_editor_state(
5303        &r#"
5304            <!-- <p>A</p>ˇ -->
5305            <!-- <p>B</p>ˇ -->
5306            <!-- <p>C</p>ˇ -->
5307        "#
5308        .unindent(),
5309    );
5310    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5311    cx.assert_editor_state(
5312        &r#"
5313            <p>A</p>ˇ
5314            <p>B</p>ˇ
5315            <p>C</p>ˇ
5316        "#
5317        .unindent(),
5318    );
5319
5320    // Toggle comments for mixture of empty and non-empty selections, where
5321    // multiple selections occupy a given line.
5322    cx.set_state(
5323        &r#"
5324            <p>A«</p>
5325            <p>ˇ»B</p>ˇ
5326            <p>C«</p>
5327            <p>ˇ»D</p>ˇ
5328        "#
5329        .unindent(),
5330    );
5331
5332    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5333    cx.assert_editor_state(
5334        &r#"
5335            <!-- <p>A«</p>
5336            <p>ˇ»B</p>ˇ -->
5337            <!-- <p>C«</p>
5338            <p>ˇ»D</p>ˇ -->
5339        "#
5340        .unindent(),
5341    );
5342    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5343    cx.assert_editor_state(
5344        &r#"
5345            <p>A«</p>
5346            <p>ˇ»B</p>ˇ
5347            <p>C«</p>
5348            <p>ˇ»D</p>ˇ
5349        "#
5350        .unindent(),
5351    );
5352
5353    // Toggle comments when different languages are active for different
5354    // selections.
5355    cx.set_state(
5356        &r#"
5357            ˇ<script>
5358                ˇvar x = new Y();
5359            ˇ</script>
5360        "#
5361        .unindent(),
5362    );
5363    cx.foreground().run_until_parked();
5364    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
5365    cx.assert_editor_state(
5366        &r#"
5367            <!-- ˇ<script> -->
5368                // ˇvar x = new Y();
5369            <!-- ˇ</script> -->
5370        "#
5371        .unindent(),
5372    );
5373}
5374
5375#[gpui::test]
5376fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
5377    init_test(cx, |_| {});
5378
5379    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5380    let multibuffer = cx.add_model(|cx| {
5381        let mut multibuffer = MultiBuffer::new(0);
5382        multibuffer.push_excerpts(
5383            buffer.clone(),
5384            [
5385                ExcerptRange {
5386                    context: Point::new(0, 0)..Point::new(0, 4),
5387                    primary: None,
5388                },
5389                ExcerptRange {
5390                    context: Point::new(1, 0)..Point::new(1, 4),
5391                    primary: None,
5392                },
5393            ],
5394            cx,
5395        );
5396        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
5397        multibuffer
5398    });
5399
5400    let (_, view) = cx.add_window(|cx| build_editor(multibuffer, cx));
5401    view.update(cx, |view, cx| {
5402        assert_eq!(view.text(cx), "aaaa\nbbbb");
5403        view.change_selections(None, cx, |s| {
5404            s.select_ranges([
5405                Point::new(0, 0)..Point::new(0, 0),
5406                Point::new(1, 0)..Point::new(1, 0),
5407            ])
5408        });
5409
5410        view.handle_input("X", cx);
5411        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
5412        assert_eq!(
5413            view.selections.ranges(cx),
5414            [
5415                Point::new(0, 1)..Point::new(0, 1),
5416                Point::new(1, 1)..Point::new(1, 1),
5417            ]
5418        );
5419
5420        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
5421        view.change_selections(None, cx, |s| {
5422            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
5423        });
5424        view.backspace(&Default::default(), cx);
5425        assert_eq!(view.text(cx), "Xa\nbbb");
5426        assert_eq!(
5427            view.selections.ranges(cx),
5428            [Point::new(1, 0)..Point::new(1, 0)]
5429        );
5430
5431        view.change_selections(None, cx, |s| {
5432            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
5433        });
5434        view.backspace(&Default::default(), cx);
5435        assert_eq!(view.text(cx), "X\nbb");
5436        assert_eq!(
5437            view.selections.ranges(cx),
5438            [Point::new(0, 1)..Point::new(0, 1)]
5439        );
5440    });
5441}
5442
5443#[gpui::test]
5444fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
5445    init_test(cx, |_| {});
5446
5447    let markers = vec![('[', ']').into(), ('(', ')').into()];
5448    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
5449        indoc! {"
5450            [aaaa
5451            (bbbb]
5452            cccc)",
5453        },
5454        markers.clone(),
5455    );
5456    let excerpt_ranges = markers.into_iter().map(|marker| {
5457        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
5458        ExcerptRange {
5459            context,
5460            primary: None,
5461        }
5462    });
5463    let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
5464    let multibuffer = cx.add_model(|cx| {
5465        let mut multibuffer = MultiBuffer::new(0);
5466        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
5467        multibuffer
5468    });
5469
5470    let (_, view) = cx.add_window(|cx| build_editor(multibuffer, cx));
5471    view.update(cx, |view, cx| {
5472        let (expected_text, selection_ranges) = marked_text_ranges(
5473            indoc! {"
5474                aaaa
5475                bˇbbb
5476                bˇbbˇb
5477                cccc"
5478            },
5479            true,
5480        );
5481        assert_eq!(view.text(cx), expected_text);
5482        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
5483
5484        view.handle_input("X", cx);
5485
5486        let (expected_text, expected_selections) = marked_text_ranges(
5487            indoc! {"
5488                aaaa
5489                bXˇbbXb
5490                bXˇbbXˇb
5491                cccc"
5492            },
5493            false,
5494        );
5495        assert_eq!(view.text(cx), expected_text);
5496        assert_eq!(view.selections.ranges(cx), expected_selections);
5497
5498        view.newline(&Newline, cx);
5499        let (expected_text, expected_selections) = marked_text_ranges(
5500            indoc! {"
5501                aaaa
5502                bX
5503                ˇbbX
5504                b
5505                bX
5506                ˇbbX
5507                ˇb
5508                cccc"
5509            },
5510            false,
5511        );
5512        assert_eq!(view.text(cx), expected_text);
5513        assert_eq!(view.selections.ranges(cx), expected_selections);
5514    });
5515}
5516
5517#[gpui::test]
5518fn test_refresh_selections(cx: &mut TestAppContext) {
5519    init_test(cx, |_| {});
5520
5521    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5522    let mut excerpt1_id = None;
5523    let multibuffer = cx.add_model(|cx| {
5524        let mut multibuffer = MultiBuffer::new(0);
5525        excerpt1_id = multibuffer
5526            .push_excerpts(
5527                buffer.clone(),
5528                [
5529                    ExcerptRange {
5530                        context: Point::new(0, 0)..Point::new(1, 4),
5531                        primary: None,
5532                    },
5533                    ExcerptRange {
5534                        context: Point::new(1, 0)..Point::new(2, 4),
5535                        primary: None,
5536                    },
5537                ],
5538                cx,
5539            )
5540            .into_iter()
5541            .next();
5542        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
5543        multibuffer
5544    });
5545
5546    let (_, editor) = cx.add_window(|cx| {
5547        let mut editor = build_editor(multibuffer.clone(), cx);
5548        let snapshot = editor.snapshot(cx);
5549        editor.change_selections(None, cx, |s| {
5550            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
5551        });
5552        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
5553        assert_eq!(
5554            editor.selections.ranges(cx),
5555            [
5556                Point::new(1, 3)..Point::new(1, 3),
5557                Point::new(2, 1)..Point::new(2, 1),
5558            ]
5559        );
5560        editor
5561    });
5562
5563    // Refreshing selections is a no-op when excerpts haven't changed.
5564    editor.update(cx, |editor, cx| {
5565        editor.change_selections(None, cx, |s| s.refresh());
5566        assert_eq!(
5567            editor.selections.ranges(cx),
5568            [
5569                Point::new(1, 3)..Point::new(1, 3),
5570                Point::new(2, 1)..Point::new(2, 1),
5571            ]
5572        );
5573    });
5574
5575    multibuffer.update(cx, |multibuffer, cx| {
5576        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
5577    });
5578    editor.update(cx, |editor, cx| {
5579        // Removing an excerpt causes the first selection to become degenerate.
5580        assert_eq!(
5581            editor.selections.ranges(cx),
5582            [
5583                Point::new(0, 0)..Point::new(0, 0),
5584                Point::new(0, 1)..Point::new(0, 1)
5585            ]
5586        );
5587
5588        // Refreshing selections will relocate the first selection to the original buffer
5589        // location.
5590        editor.change_selections(None, cx, |s| s.refresh());
5591        assert_eq!(
5592            editor.selections.ranges(cx),
5593            [
5594                Point::new(0, 1)..Point::new(0, 1),
5595                Point::new(0, 3)..Point::new(0, 3)
5596            ]
5597        );
5598        assert!(editor.selections.pending_anchor().is_some());
5599    });
5600}
5601
5602#[gpui::test]
5603fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
5604    init_test(cx, |_| {});
5605
5606    let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
5607    let mut excerpt1_id = None;
5608    let multibuffer = cx.add_model(|cx| {
5609        let mut multibuffer = MultiBuffer::new(0);
5610        excerpt1_id = multibuffer
5611            .push_excerpts(
5612                buffer.clone(),
5613                [
5614                    ExcerptRange {
5615                        context: Point::new(0, 0)..Point::new(1, 4),
5616                        primary: None,
5617                    },
5618                    ExcerptRange {
5619                        context: Point::new(1, 0)..Point::new(2, 4),
5620                        primary: None,
5621                    },
5622                ],
5623                cx,
5624            )
5625            .into_iter()
5626            .next();
5627        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
5628        multibuffer
5629    });
5630
5631    let (_, editor) = cx.add_window(|cx| {
5632        let mut editor = build_editor(multibuffer.clone(), cx);
5633        let snapshot = editor.snapshot(cx);
5634        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
5635        assert_eq!(
5636            editor.selections.ranges(cx),
5637            [Point::new(1, 3)..Point::new(1, 3)]
5638        );
5639        editor
5640    });
5641
5642    multibuffer.update(cx, |multibuffer, cx| {
5643        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
5644    });
5645    editor.update(cx, |editor, cx| {
5646        assert_eq!(
5647            editor.selections.ranges(cx),
5648            [Point::new(0, 0)..Point::new(0, 0)]
5649        );
5650
5651        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
5652        editor.change_selections(None, cx, |s| s.refresh());
5653        assert_eq!(
5654            editor.selections.ranges(cx),
5655            [Point::new(0, 3)..Point::new(0, 3)]
5656        );
5657        assert!(editor.selections.pending_anchor().is_some());
5658    });
5659}
5660
5661#[gpui::test]
5662async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
5663    init_test(cx, |_| {});
5664
5665    let language = Arc::new(
5666        Language::new(
5667            LanguageConfig {
5668                brackets: BracketPairConfig {
5669                    pairs: vec![
5670                        BracketPair {
5671                            start: "{".to_string(),
5672                            end: "}".to_string(),
5673                            close: true,
5674                            newline: true,
5675                        },
5676                        BracketPair {
5677                            start: "/* ".to_string(),
5678                            end: " */".to_string(),
5679                            close: true,
5680                            newline: true,
5681                        },
5682                    ],
5683                    ..Default::default()
5684                },
5685                ..Default::default()
5686            },
5687            Some(tree_sitter_rust::language()),
5688        )
5689        .with_indents_query("")
5690        .unwrap(),
5691    );
5692
5693    let text = concat!(
5694        "{   }\n",     //
5695        "  x\n",       //
5696        "  /*   */\n", //
5697        "x\n",         //
5698        "{{} }\n",     //
5699    );
5700
5701    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
5702    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
5703    let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
5704    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
5705        .await;
5706
5707    view.update(cx, |view, cx| {
5708        view.change_selections(None, cx, |s| {
5709            s.select_display_ranges([
5710                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
5711                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
5712                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
5713            ])
5714        });
5715        view.newline(&Newline, cx);
5716
5717        assert_eq!(
5718            view.buffer().read(cx).read(cx).text(),
5719            concat!(
5720                "{ \n",    // Suppress rustfmt
5721                "\n",      //
5722                "}\n",     //
5723                "  x\n",   //
5724                "  /* \n", //
5725                "  \n",    //
5726                "  */\n",  //
5727                "x\n",     //
5728                "{{} \n",  //
5729                "}\n",     //
5730            )
5731        );
5732    });
5733}
5734
5735#[gpui::test]
5736fn test_highlighted_ranges(cx: &mut TestAppContext) {
5737    init_test(cx, |_| {});
5738
5739    let (_, editor) = cx.add_window(|cx| {
5740        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
5741        build_editor(buffer.clone(), cx)
5742    });
5743
5744    editor.update(cx, |editor, cx| {
5745        struct Type1;
5746        struct Type2;
5747
5748        let buffer = editor.buffer.read(cx).snapshot(cx);
5749
5750        let anchor_range =
5751            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
5752
5753        editor.highlight_background::<Type1>(
5754            vec![
5755                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
5756                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
5757                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
5758                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
5759            ],
5760            |_| Color::red(),
5761            cx,
5762        );
5763        editor.highlight_background::<Type2>(
5764            vec![
5765                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
5766                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
5767                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
5768                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
5769            ],
5770            |_| Color::green(),
5771            cx,
5772        );
5773
5774        let snapshot = editor.snapshot(cx);
5775        let mut highlighted_ranges = editor.background_highlights_in_range(
5776            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
5777            &snapshot,
5778            theme::current(cx).as_ref(),
5779        );
5780        // Enforce a consistent ordering based on color without relying on the ordering of the
5781        // highlight's `TypeId` which is non-deterministic.
5782        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
5783        assert_eq!(
5784            highlighted_ranges,
5785            &[
5786                (
5787                    DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
5788                    Color::green(),
5789                ),
5790                (
5791                    DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
5792                    Color::green(),
5793                ),
5794                (
5795                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
5796                    Color::red(),
5797                ),
5798                (
5799                    DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
5800                    Color::red(),
5801                ),
5802            ]
5803        );
5804        assert_eq!(
5805            editor.background_highlights_in_range(
5806                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
5807                &snapshot,
5808                theme::current(cx).as_ref(),
5809            ),
5810            &[(
5811                DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
5812                Color::red(),
5813            )]
5814        );
5815    });
5816}
5817
5818#[gpui::test]
5819async fn test_following(cx: &mut gpui::TestAppContext) {
5820    init_test(cx, |_| {});
5821
5822    let fs = FakeFs::new(cx.background());
5823    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5824
5825    let buffer = project.update(cx, |project, cx| {
5826        let buffer = project
5827            .create_buffer(&sample_text(16, 8, 'a'), None, cx)
5828            .unwrap();
5829        cx.add_model(|cx| MultiBuffer::singleton(buffer, cx))
5830    });
5831    let (_, leader) = cx.add_window(|cx| build_editor(buffer.clone(), cx));
5832    let (_, follower) = cx.update(|cx| {
5833        cx.add_window(
5834            WindowOptions {
5835                bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
5836                ..Default::default()
5837            },
5838            |cx| build_editor(buffer.clone(), cx),
5839        )
5840    });
5841
5842    let is_still_following = Rc::new(RefCell::new(true));
5843    let follower_edit_event_count = Rc::new(RefCell::new(0));
5844    let pending_update = Rc::new(RefCell::new(None));
5845    follower.update(cx, {
5846        let update = pending_update.clone();
5847        let is_still_following = is_still_following.clone();
5848        let follower_edit_event_count = follower_edit_event_count.clone();
5849        |_, cx| {
5850            cx.subscribe(&leader, move |_, leader, event, cx| {
5851                leader
5852                    .read(cx)
5853                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
5854            })
5855            .detach();
5856
5857            cx.subscribe(&follower, move |_, _, event, cx| {
5858                if Editor::should_unfollow_on_event(event, cx) {
5859                    *is_still_following.borrow_mut() = false;
5860                }
5861                if let Event::BufferEdited = event {
5862                    *follower_edit_event_count.borrow_mut() += 1;
5863                }
5864            })
5865            .detach();
5866        }
5867    });
5868
5869    // Update the selections only
5870    leader.update(cx, |leader, cx| {
5871        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
5872    });
5873    follower
5874        .update(cx, |follower, cx| {
5875            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5876        })
5877        .await
5878        .unwrap();
5879    follower.read_with(cx, |follower, cx| {
5880        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
5881    });
5882    assert_eq!(*is_still_following.borrow(), true);
5883    assert_eq!(*follower_edit_event_count.borrow(), 0);
5884
5885    // Update the scroll position only
5886    leader.update(cx, |leader, cx| {
5887        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
5888    });
5889    follower
5890        .update(cx, |follower, cx| {
5891            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5892        })
5893        .await
5894        .unwrap();
5895    assert_eq!(
5896        follower.update(cx, |follower, cx| follower.scroll_position(cx)),
5897        vec2f(1.5, 3.5)
5898    );
5899    assert_eq!(*is_still_following.borrow(), true);
5900    assert_eq!(*follower_edit_event_count.borrow(), 0);
5901
5902    // Update the selections and scroll position. The follower's scroll position is updated
5903    // via autoscroll, not via the leader's exact scroll position.
5904    leader.update(cx, |leader, cx| {
5905        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
5906        leader.request_autoscroll(Autoscroll::newest(), cx);
5907        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
5908    });
5909    follower
5910        .update(cx, |follower, cx| {
5911            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5912        })
5913        .await
5914        .unwrap();
5915    follower.update(cx, |follower, cx| {
5916        assert_eq!(follower.scroll_position(cx), vec2f(1.5, 0.0));
5917        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
5918    });
5919    assert_eq!(*is_still_following.borrow(), true);
5920
5921    // Creating a pending selection that precedes another selection
5922    leader.update(cx, |leader, cx| {
5923        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
5924        leader.begin_selection(DisplayPoint::new(0, 0), true, 1, 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    follower.read_with(cx, |follower, cx| {
5933        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
5934    });
5935    assert_eq!(*is_still_following.borrow(), true);
5936
5937    // Extend the pending selection so that it surrounds another selection
5938    leader.update(cx, |leader, cx| {
5939        leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
5940    });
5941    follower
5942        .update(cx, |follower, cx| {
5943            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
5944        })
5945        .await
5946        .unwrap();
5947    follower.read_with(cx, |follower, cx| {
5948        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
5949    });
5950
5951    // Scrolling locally breaks the follow
5952    follower.update(cx, |follower, cx| {
5953        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
5954        follower.set_scroll_anchor(
5955            ScrollAnchor {
5956                anchor: top_anchor,
5957                offset: vec2f(0.0, 0.5),
5958            },
5959            cx,
5960        );
5961    });
5962    assert_eq!(*is_still_following.borrow(), false);
5963}
5964
5965#[gpui::test]
5966async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
5967    init_test(cx, |_| {});
5968
5969    let fs = FakeFs::new(cx.background());
5970    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5971    let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
5972    let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
5973
5974    let leader = pane.update(cx, |_, cx| {
5975        let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
5976        cx.add_view(|cx| build_editor(multibuffer.clone(), cx))
5977    });
5978
5979    // Start following the editor when it has no excerpts.
5980    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
5981    let follower_1 = cx
5982        .update(|cx| {
5983            Editor::from_state_proto(
5984                pane.clone(),
5985                project.clone(),
5986                ViewId {
5987                    creator: Default::default(),
5988                    id: 0,
5989                },
5990                &mut state_message,
5991                cx,
5992            )
5993        })
5994        .unwrap()
5995        .await
5996        .unwrap();
5997
5998    let update_message = Rc::new(RefCell::new(None));
5999    follower_1.update(cx, {
6000        let update = update_message.clone();
6001        |_, cx| {
6002            cx.subscribe(&leader, move |_, leader, event, cx| {
6003                leader
6004                    .read(cx)
6005                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
6006            })
6007            .detach();
6008        }
6009    });
6010
6011    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
6012        (
6013            project
6014                .create_buffer("abc\ndef\nghi\njkl\n", None, cx)
6015                .unwrap(),
6016            project
6017                .create_buffer("mno\npqr\nstu\nvwx\n", None, cx)
6018                .unwrap(),
6019        )
6020    });
6021
6022    // Insert some excerpts.
6023    leader.update(cx, |leader, cx| {
6024        leader.buffer.update(cx, |multibuffer, cx| {
6025            let excerpt_ids = multibuffer.push_excerpts(
6026                buffer_1.clone(),
6027                [
6028                    ExcerptRange {
6029                        context: 1..6,
6030                        primary: None,
6031                    },
6032                    ExcerptRange {
6033                        context: 12..15,
6034                        primary: None,
6035                    },
6036                    ExcerptRange {
6037                        context: 0..3,
6038                        primary: None,
6039                    },
6040                ],
6041                cx,
6042            );
6043            multibuffer.insert_excerpts_after(
6044                excerpt_ids[0],
6045                buffer_2.clone(),
6046                [
6047                    ExcerptRange {
6048                        context: 8..12,
6049                        primary: None,
6050                    },
6051                    ExcerptRange {
6052                        context: 0..6,
6053                        primary: None,
6054                    },
6055                ],
6056                cx,
6057            );
6058        });
6059    });
6060
6061    // Apply the update of adding the excerpts.
6062    follower_1
6063        .update(cx, |follower, cx| {
6064            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6065        })
6066        .await
6067        .unwrap();
6068    assert_eq!(
6069        follower_1.read_with(cx, |editor, cx| editor.text(cx)),
6070        leader.read_with(cx, |editor, cx| editor.text(cx))
6071    );
6072    update_message.borrow_mut().take();
6073
6074    // Start following separately after it already has excerpts.
6075    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
6076    let follower_2 = cx
6077        .update(|cx| {
6078            Editor::from_state_proto(
6079                pane.clone(),
6080                project.clone(),
6081                ViewId {
6082                    creator: Default::default(),
6083                    id: 0,
6084                },
6085                &mut state_message,
6086                cx,
6087            )
6088        })
6089        .unwrap()
6090        .await
6091        .unwrap();
6092    assert_eq!(
6093        follower_2.read_with(cx, |editor, cx| editor.text(cx)),
6094        leader.read_with(cx, |editor, cx| editor.text(cx))
6095    );
6096
6097    // Remove some excerpts.
6098    leader.update(cx, |leader, cx| {
6099        leader.buffer.update(cx, |multibuffer, cx| {
6100            let excerpt_ids = multibuffer.excerpt_ids();
6101            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
6102            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
6103        });
6104    });
6105
6106    // Apply the update of removing the excerpts.
6107    follower_1
6108        .update(cx, |follower, cx| {
6109            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6110        })
6111        .await
6112        .unwrap();
6113    follower_2
6114        .update(cx, |follower, cx| {
6115            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
6116        })
6117        .await
6118        .unwrap();
6119    update_message.borrow_mut().take();
6120    assert_eq!(
6121        follower_1.read_with(cx, |editor, cx| editor.text(cx)),
6122        leader.read_with(cx, |editor, cx| editor.text(cx))
6123    );
6124}
6125
6126#[test]
6127fn test_combine_syntax_and_fuzzy_match_highlights() {
6128    let string = "abcdefghijklmnop";
6129    let syntax_ranges = [
6130        (
6131            0..3,
6132            HighlightStyle {
6133                color: Some(Color::red()),
6134                ..Default::default()
6135            },
6136        ),
6137        (
6138            4..8,
6139            HighlightStyle {
6140                color: Some(Color::green()),
6141                ..Default::default()
6142            },
6143        ),
6144    ];
6145    let match_indices = [4, 6, 7, 8];
6146    assert_eq!(
6147        combine_syntax_and_fuzzy_match_highlights(
6148            string,
6149            Default::default(),
6150            syntax_ranges.into_iter(),
6151            &match_indices,
6152        ),
6153        &[
6154            (
6155                0..3,
6156                HighlightStyle {
6157                    color: Some(Color::red()),
6158                    ..Default::default()
6159                },
6160            ),
6161            (
6162                4..5,
6163                HighlightStyle {
6164                    color: Some(Color::green()),
6165                    weight: Some(fonts::Weight::BOLD),
6166                    ..Default::default()
6167                },
6168            ),
6169            (
6170                5..6,
6171                HighlightStyle {
6172                    color: Some(Color::green()),
6173                    ..Default::default()
6174                },
6175            ),
6176            (
6177                6..8,
6178                HighlightStyle {
6179                    color: Some(Color::green()),
6180                    weight: Some(fonts::Weight::BOLD),
6181                    ..Default::default()
6182                },
6183            ),
6184            (
6185                8..9,
6186                HighlightStyle {
6187                    weight: Some(fonts::Weight::BOLD),
6188                    ..Default::default()
6189                },
6190            ),
6191        ]
6192    );
6193}
6194
6195#[gpui::test]
6196async fn go_to_hunk(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
6197    init_test(cx, |_| {});
6198
6199    let mut cx = EditorTestContext::new(cx).await;
6200
6201    let diff_base = r#"
6202        use some::mod;
6203
6204        const A: u32 = 42;
6205
6206        fn main() {
6207            println!("hello");
6208
6209            println!("world");
6210        }
6211        "#
6212    .unindent();
6213
6214    // Edits are modified, removed, modified, added
6215    cx.set_state(
6216        &r#"
6217        use some::modified;
6218
6219        ˇ
6220        fn main() {
6221            println!("hello there");
6222
6223            println!("around the");
6224            println!("world");
6225        }
6226        "#
6227        .unindent(),
6228    );
6229
6230    cx.set_diff_base(Some(&diff_base));
6231    deterministic.run_until_parked();
6232
6233    cx.update_editor(|editor, cx| {
6234        //Wrap around the bottom of the buffer
6235        for _ in 0..3 {
6236            editor.go_to_hunk(&GoToHunk, cx);
6237        }
6238    });
6239
6240    cx.assert_editor_state(
6241        &r#"
6242        ˇuse some::modified;
6243
6244
6245        fn main() {
6246            println!("hello there");
6247
6248            println!("around the");
6249            println!("world");
6250        }
6251        "#
6252        .unindent(),
6253    );
6254
6255    cx.update_editor(|editor, cx| {
6256        //Wrap around the top of the buffer
6257        for _ in 0..2 {
6258            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
6259        }
6260    });
6261
6262    cx.assert_editor_state(
6263        &r#"
6264        use some::modified;
6265
6266
6267        fn main() {
6268        ˇ    println!("hello there");
6269
6270            println!("around the");
6271            println!("world");
6272        }
6273        "#
6274        .unindent(),
6275    );
6276
6277    cx.update_editor(|editor, cx| {
6278        editor.fold(&Fold, cx);
6279
6280        //Make sure that the fold only gets one hunk
6281        for _ in 0..4 {
6282            editor.go_to_hunk(&GoToHunk, cx);
6283        }
6284    });
6285
6286    cx.assert_editor_state(
6287        &r#"
6288        ˇuse some::modified;
6289
6290
6291        fn main() {
6292            println!("hello there");
6293
6294            println!("around the");
6295            println!("world");
6296        }
6297        "#
6298        .unindent(),
6299    );
6300}
6301
6302#[test]
6303fn test_split_words() {
6304    fn split<'a>(text: &'a str) -> Vec<&'a str> {
6305        split_words(text).collect()
6306    }
6307
6308    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
6309    assert_eq!(split("hello_world"), &["hello_", "world"]);
6310    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
6311    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
6312    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
6313    assert_eq!(split("helloworld"), &["helloworld"]);
6314}
6315
6316#[gpui::test]
6317async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
6318    init_test(cx, |_| {});
6319
6320    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
6321    let mut assert = |before, after| {
6322        let _state_context = cx.set_state(before);
6323        cx.update_editor(|editor, cx| {
6324            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
6325        });
6326        cx.assert_editor_state(after);
6327    };
6328
6329    // Outside bracket jumps to outside of matching bracket
6330    assert("console.logˇ(var);", "console.log(var)ˇ;");
6331    assert("console.log(var)ˇ;", "console.logˇ(var);");
6332
6333    // Inside bracket jumps to inside of matching bracket
6334    assert("console.log(ˇvar);", "console.log(varˇ);");
6335    assert("console.log(varˇ);", "console.log(ˇvar);");
6336
6337    // When outside a bracket and inside, favor jumping to the inside bracket
6338    assert(
6339        "console.log('foo', [1, 2, 3]ˇ);",
6340        "console.log(ˇ'foo', [1, 2, 3]);",
6341    );
6342    assert(
6343        "console.log(ˇ'foo', [1, 2, 3]);",
6344        "console.log('foo', [1, 2, 3]ˇ);",
6345    );
6346
6347    // Bias forward if two options are equally likely
6348    assert(
6349        "let result = curried_fun()ˇ();",
6350        "let result = curried_fun()()ˇ;",
6351    );
6352
6353    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
6354    assert(
6355        indoc! {"
6356            function test() {
6357                console.log('test')ˇ
6358            }"},
6359        indoc! {"
6360            function test() {
6361                console.logˇ('test')
6362            }"},
6363    );
6364}
6365
6366#[gpui::test(iterations = 10)]
6367async fn test_copilot(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
6368    init_test(cx, |_| {});
6369
6370    let (copilot, copilot_lsp) = Copilot::fake(cx);
6371    cx.update(|cx| cx.set_global(copilot));
6372    let mut cx = EditorLspTestContext::new_rust(
6373        lsp::ServerCapabilities {
6374            completion_provider: Some(lsp::CompletionOptions {
6375                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
6376                ..Default::default()
6377            }),
6378            ..Default::default()
6379        },
6380        cx,
6381    )
6382    .await;
6383
6384    // When inserting, ensure autocompletion is favored over Copilot suggestions.
6385    cx.set_state(indoc! {"
6386        oneˇ
6387        two
6388        three
6389    "});
6390    cx.simulate_keystroke(".");
6391    let _ = handle_completion_request(
6392        &mut cx,
6393        indoc! {"
6394            one.|<>
6395            two
6396            three
6397        "},
6398        vec!["completion_a", "completion_b"],
6399    );
6400    handle_copilot_completion_request(
6401        &copilot_lsp,
6402        vec![copilot::request::Completion {
6403            text: "one.copilot1".into(),
6404            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6405            ..Default::default()
6406        }],
6407        vec![],
6408    );
6409    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6410    cx.update_editor(|editor, cx| {
6411        assert!(editor.context_menu_visible());
6412        assert!(!editor.has_active_copilot_suggestion(cx));
6413
6414        // Confirming a completion inserts it and hides the context menu, without showing
6415        // the copilot suggestion afterwards.
6416        editor
6417            .confirm_completion(&Default::default(), cx)
6418            .unwrap()
6419            .detach();
6420        assert!(!editor.context_menu_visible());
6421        assert!(!editor.has_active_copilot_suggestion(cx));
6422        assert_eq!(editor.text(cx), "one.completion_a\ntwo\nthree\n");
6423        assert_eq!(editor.display_text(cx), "one.completion_a\ntwo\nthree\n");
6424    });
6425
6426    // Ensure Copilot suggestions are shown right away if no autocompletion is available.
6427    cx.set_state(indoc! {"
6428        oneˇ
6429        two
6430        three
6431    "});
6432    cx.simulate_keystroke(".");
6433    let _ = handle_completion_request(
6434        &mut cx,
6435        indoc! {"
6436            one.|<>
6437            two
6438            three
6439        "},
6440        vec![],
6441    );
6442    handle_copilot_completion_request(
6443        &copilot_lsp,
6444        vec![copilot::request::Completion {
6445            text: "one.copilot1".into(),
6446            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6447            ..Default::default()
6448        }],
6449        vec![],
6450    );
6451    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6452    cx.update_editor(|editor, cx| {
6453        assert!(!editor.context_menu_visible());
6454        assert!(editor.has_active_copilot_suggestion(cx));
6455        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
6456        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
6457    });
6458
6459    // Reset editor, and ensure autocompletion is still favored over Copilot suggestions.
6460    cx.set_state(indoc! {"
6461        oneˇ
6462        two
6463        three
6464    "});
6465    cx.simulate_keystroke(".");
6466    let _ = handle_completion_request(
6467        &mut cx,
6468        indoc! {"
6469            one.|<>
6470            two
6471            three
6472        "},
6473        vec!["completion_a", "completion_b"],
6474    );
6475    handle_copilot_completion_request(
6476        &copilot_lsp,
6477        vec![copilot::request::Completion {
6478            text: "one.copilot1".into(),
6479            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
6480            ..Default::default()
6481        }],
6482        vec![],
6483    );
6484    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6485    cx.update_editor(|editor, cx| {
6486        assert!(editor.context_menu_visible());
6487        assert!(!editor.has_active_copilot_suggestion(cx));
6488
6489        // When hiding the context menu, the Copilot suggestion becomes visible.
6490        editor.hide_context_menu(cx);
6491        assert!(!editor.context_menu_visible());
6492        assert!(editor.has_active_copilot_suggestion(cx));
6493        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
6494        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
6495    });
6496
6497    // Ensure existing completion is interpolated when inserting again.
6498    cx.simulate_keystroke("c");
6499    deterministic.run_until_parked();
6500    cx.update_editor(|editor, cx| {
6501        assert!(!editor.context_menu_visible());
6502        assert!(editor.has_active_copilot_suggestion(cx));
6503        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
6504        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6505    });
6506
6507    // After debouncing, new Copilot completions should be requested.
6508    handle_copilot_completion_request(
6509        &copilot_lsp,
6510        vec![copilot::request::Completion {
6511            text: "one.copilot2".into(),
6512            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 5)),
6513            ..Default::default()
6514        }],
6515        vec![],
6516    );
6517    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6518    cx.update_editor(|editor, cx| {
6519        assert!(!editor.context_menu_visible());
6520        assert!(editor.has_active_copilot_suggestion(cx));
6521        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6522        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6523
6524        // Canceling should remove the active Copilot suggestion.
6525        editor.cancel(&Default::default(), cx);
6526        assert!(!editor.has_active_copilot_suggestion(cx));
6527        assert_eq!(editor.display_text(cx), "one.c\ntwo\nthree\n");
6528        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6529
6530        // After canceling, tabbing shouldn't insert the previously shown suggestion.
6531        editor.tab(&Default::default(), cx);
6532        assert!(!editor.has_active_copilot_suggestion(cx));
6533        assert_eq!(editor.display_text(cx), "one.c   \ntwo\nthree\n");
6534        assert_eq!(editor.text(cx), "one.c   \ntwo\nthree\n");
6535
6536        // When undoing the previously active suggestion is shown again.
6537        editor.undo(&Default::default(), cx);
6538        assert!(editor.has_active_copilot_suggestion(cx));
6539        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6540        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
6541    });
6542
6543    // If an edit occurs outside of this editor, the suggestion is still correctly interpolated.
6544    cx.update_buffer(|buffer, cx| buffer.edit([(5..5, "o")], None, cx));
6545    cx.update_editor(|editor, cx| {
6546        assert!(editor.has_active_copilot_suggestion(cx));
6547        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6548        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6549
6550        // Tabbing when there is an active suggestion inserts it.
6551        editor.tab(&Default::default(), cx);
6552        assert!(!editor.has_active_copilot_suggestion(cx));
6553        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6554        assert_eq!(editor.text(cx), "one.copilot2\ntwo\nthree\n");
6555
6556        // When undoing the previously active suggestion is shown again.
6557        editor.undo(&Default::default(), cx);
6558        assert!(editor.has_active_copilot_suggestion(cx));
6559        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
6560        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6561
6562        // Hide suggestion.
6563        editor.cancel(&Default::default(), cx);
6564        assert!(!editor.has_active_copilot_suggestion(cx));
6565        assert_eq!(editor.display_text(cx), "one.co\ntwo\nthree\n");
6566        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
6567    });
6568
6569    // If an edit occurs outside of this editor but no suggestion is being shown,
6570    // we won't make it visible.
6571    cx.update_buffer(|buffer, cx| buffer.edit([(6..6, "p")], None, cx));
6572    cx.update_editor(|editor, cx| {
6573        assert!(!editor.has_active_copilot_suggestion(cx));
6574        assert_eq!(editor.display_text(cx), "one.cop\ntwo\nthree\n");
6575        assert_eq!(editor.text(cx), "one.cop\ntwo\nthree\n");
6576    });
6577
6578    // Reset the editor to verify how suggestions behave when tabbing on leading indentation.
6579    cx.update_editor(|editor, cx| {
6580        editor.set_text("fn foo() {\n  \n}", cx);
6581        editor.change_selections(None, cx, |s| {
6582            s.select_ranges([Point::new(1, 2)..Point::new(1, 2)])
6583        });
6584    });
6585    handle_copilot_completion_request(
6586        &copilot_lsp,
6587        vec![copilot::request::Completion {
6588            text: "    let x = 4;".into(),
6589            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
6590            ..Default::default()
6591        }],
6592        vec![],
6593    );
6594
6595    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
6596    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6597    cx.update_editor(|editor, cx| {
6598        assert!(editor.has_active_copilot_suggestion(cx));
6599        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6600        assert_eq!(editor.text(cx), "fn foo() {\n  \n}");
6601
6602        // Tabbing inside of leading whitespace inserts indentation without accepting the suggestion.
6603        editor.tab(&Default::default(), cx);
6604        assert!(editor.has_active_copilot_suggestion(cx));
6605        assert_eq!(editor.text(cx), "fn foo() {\n    \n}");
6606        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6607
6608        // Tabbing again accepts the suggestion.
6609        editor.tab(&Default::default(), cx);
6610        assert!(!editor.has_active_copilot_suggestion(cx));
6611        assert_eq!(editor.text(cx), "fn foo() {\n    let x = 4;\n}");
6612        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
6613    });
6614}
6615
6616#[gpui::test]
6617async fn test_copilot_completion_invalidation(
6618    deterministic: Arc<Deterministic>,
6619    cx: &mut gpui::TestAppContext,
6620) {
6621    init_test(cx, |_| {});
6622
6623    let (copilot, copilot_lsp) = Copilot::fake(cx);
6624    cx.update(|cx| cx.set_global(copilot));
6625    let mut cx = EditorLspTestContext::new_rust(
6626        lsp::ServerCapabilities {
6627            completion_provider: Some(lsp::CompletionOptions {
6628                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
6629                ..Default::default()
6630            }),
6631            ..Default::default()
6632        },
6633        cx,
6634    )
6635    .await;
6636
6637    cx.set_state(indoc! {"
6638        one
6639        twˇ
6640        three
6641    "});
6642
6643    handle_copilot_completion_request(
6644        &copilot_lsp,
6645        vec![copilot::request::Completion {
6646            text: "two.foo()".into(),
6647            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
6648            ..Default::default()
6649        }],
6650        vec![],
6651    );
6652    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
6653    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6654    cx.update_editor(|editor, cx| {
6655        assert!(editor.has_active_copilot_suggestion(cx));
6656        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6657        assert_eq!(editor.text(cx), "one\ntw\nthree\n");
6658
6659        editor.backspace(&Default::default(), cx);
6660        assert!(editor.has_active_copilot_suggestion(cx));
6661        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6662        assert_eq!(editor.text(cx), "one\nt\nthree\n");
6663
6664        editor.backspace(&Default::default(), cx);
6665        assert!(editor.has_active_copilot_suggestion(cx));
6666        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6667        assert_eq!(editor.text(cx), "one\n\nthree\n");
6668
6669        // Deleting across the original suggestion range invalidates it.
6670        editor.backspace(&Default::default(), cx);
6671        assert!(!editor.has_active_copilot_suggestion(cx));
6672        assert_eq!(editor.display_text(cx), "one\nthree\n");
6673        assert_eq!(editor.text(cx), "one\nthree\n");
6674
6675        // Undoing the deletion restores the suggestion.
6676        editor.undo(&Default::default(), cx);
6677        assert!(editor.has_active_copilot_suggestion(cx));
6678        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
6679        assert_eq!(editor.text(cx), "one\n\nthree\n");
6680    });
6681}
6682
6683#[gpui::test]
6684async fn test_copilot_multibuffer(
6685    deterministic: Arc<Deterministic>,
6686    cx: &mut gpui::TestAppContext,
6687) {
6688    init_test(cx, |_| {});
6689
6690    let (copilot, copilot_lsp) = Copilot::fake(cx);
6691    cx.update(|cx| cx.set_global(copilot));
6692
6693    let buffer_1 = cx.add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx));
6694    let buffer_2 = cx.add_model(|cx| Buffer::new(0, "c = 3\nd = 4\n", cx));
6695    let multibuffer = cx.add_model(|cx| {
6696        let mut multibuffer = MultiBuffer::new(0);
6697        multibuffer.push_excerpts(
6698            buffer_1.clone(),
6699            [ExcerptRange {
6700                context: Point::new(0, 0)..Point::new(2, 0),
6701                primary: None,
6702            }],
6703            cx,
6704        );
6705        multibuffer.push_excerpts(
6706            buffer_2.clone(),
6707            [ExcerptRange {
6708                context: Point::new(0, 0)..Point::new(2, 0),
6709                primary: None,
6710            }],
6711            cx,
6712        );
6713        multibuffer
6714    });
6715    let (_, editor) = cx.add_window(|cx| build_editor(multibuffer, cx));
6716
6717    handle_copilot_completion_request(
6718        &copilot_lsp,
6719        vec![copilot::request::Completion {
6720            text: "b = 2 + a".into(),
6721            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 5)),
6722            ..Default::default()
6723        }],
6724        vec![],
6725    );
6726    editor.update(cx, |editor, cx| {
6727        // Ensure copilot suggestions are shown for the first excerpt.
6728        editor.change_selections(None, cx, |s| {
6729            s.select_ranges([Point::new(1, 5)..Point::new(1, 5)])
6730        });
6731        editor.next_copilot_suggestion(&Default::default(), cx);
6732    });
6733    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6734    editor.update(cx, |editor, cx| {
6735        assert!(editor.has_active_copilot_suggestion(cx));
6736        assert_eq!(
6737            editor.display_text(cx),
6738            "\n\na = 1\nb = 2 + a\n\n\n\nc = 3\nd = 4\n"
6739        );
6740        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
6741    });
6742
6743    handle_copilot_completion_request(
6744        &copilot_lsp,
6745        vec![copilot::request::Completion {
6746            text: "d = 4 + c".into(),
6747            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 6)),
6748            ..Default::default()
6749        }],
6750        vec![],
6751    );
6752    editor.update(cx, |editor, cx| {
6753        // Move to another excerpt, ensuring the suggestion gets cleared.
6754        editor.change_selections(None, cx, |s| {
6755            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
6756        });
6757        assert!(!editor.has_active_copilot_suggestion(cx));
6758        assert_eq!(
6759            editor.display_text(cx),
6760            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4\n"
6761        );
6762        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
6763
6764        // Type a character, ensuring we don't even try to interpolate the previous suggestion.
6765        editor.handle_input(" ", cx);
6766        assert!(!editor.has_active_copilot_suggestion(cx));
6767        assert_eq!(
6768            editor.display_text(cx),
6769            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 \n"
6770        );
6771        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
6772    });
6773
6774    // Ensure the new suggestion is displayed when the debounce timeout expires.
6775    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6776    editor.update(cx, |editor, cx| {
6777        assert!(editor.has_active_copilot_suggestion(cx));
6778        assert_eq!(
6779            editor.display_text(cx),
6780            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 + c\n"
6781        );
6782        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
6783    });
6784}
6785
6786#[gpui::test]
6787async fn test_copilot_disabled_globs(
6788    deterministic: Arc<Deterministic>,
6789    cx: &mut gpui::TestAppContext,
6790) {
6791    init_test(cx, |settings| {
6792        settings
6793            .copilot
6794            .get_or_insert(Default::default())
6795            .disabled_globs = Some(vec![".env*".to_string()]);
6796    });
6797
6798    let (copilot, copilot_lsp) = Copilot::fake(cx);
6799    cx.update(|cx| cx.set_global(copilot));
6800
6801    let fs = FakeFs::new(cx.background());
6802    fs.insert_tree(
6803        "/test",
6804        json!({
6805            ".env": "SECRET=something\n",
6806            "README.md": "hello\n"
6807        }),
6808    )
6809    .await;
6810    let project = Project::test(fs, ["/test".as_ref()], cx).await;
6811
6812    let private_buffer = project
6813        .update(cx, |project, cx| {
6814            project.open_local_buffer("/test/.env", cx)
6815        })
6816        .await
6817        .unwrap();
6818    let public_buffer = project
6819        .update(cx, |project, cx| {
6820            project.open_local_buffer("/test/README.md", cx)
6821        })
6822        .await
6823        .unwrap();
6824
6825    let multibuffer = cx.add_model(|cx| {
6826        let mut multibuffer = MultiBuffer::new(0);
6827        multibuffer.push_excerpts(
6828            private_buffer.clone(),
6829            [ExcerptRange {
6830                context: Point::new(0, 0)..Point::new(1, 0),
6831                primary: None,
6832            }],
6833            cx,
6834        );
6835        multibuffer.push_excerpts(
6836            public_buffer.clone(),
6837            [ExcerptRange {
6838                context: Point::new(0, 0)..Point::new(1, 0),
6839                primary: None,
6840            }],
6841            cx,
6842        );
6843        multibuffer
6844    });
6845    let (_, editor) = cx.add_window(|cx| build_editor(multibuffer, cx));
6846
6847    let mut copilot_requests = copilot_lsp
6848        .handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| async move {
6849            Ok(copilot::request::GetCompletionsResult {
6850                completions: vec![copilot::request::Completion {
6851                    text: "next line".into(),
6852                    range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
6853                    ..Default::default()
6854                }],
6855            })
6856        });
6857
6858    editor.update(cx, |editor, cx| {
6859        editor.change_selections(None, cx, |selections| {
6860            selections.select_ranges([Point::new(0, 0)..Point::new(0, 0)])
6861        });
6862        editor.next_copilot_suggestion(&Default::default(), cx);
6863    });
6864
6865    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6866    assert!(copilot_requests.try_next().is_err());
6867
6868    editor.update(cx, |editor, cx| {
6869        editor.change_selections(None, cx, |s| {
6870            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
6871        });
6872        editor.next_copilot_suggestion(&Default::default(), cx);
6873    });
6874
6875    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
6876    assert!(copilot_requests.try_next().is_ok());
6877}
6878
6879fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
6880    let point = DisplayPoint::new(row as u32, column as u32);
6881    point..point
6882}
6883
6884fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
6885    let (text, ranges) = marked_text_ranges(marked_text, true);
6886    assert_eq!(view.text(cx), text);
6887    assert_eq!(
6888        view.selections.ranges(cx),
6889        ranges,
6890        "Assert selections are {}",
6891        marked_text
6892    );
6893}
6894
6895/// Handle completion request passing a marked string specifying where the completion
6896/// should be triggered from using '|' character, what range should be replaced, and what completions
6897/// should be returned using '<' and '>' to delimit the range
6898fn handle_completion_request<'a>(
6899    cx: &mut EditorLspTestContext<'a>,
6900    marked_string: &str,
6901    completions: Vec<&'static str>,
6902) -> impl Future<Output = ()> {
6903    let complete_from_marker: TextRangeMarker = '|'.into();
6904    let replace_range_marker: TextRangeMarker = ('<', '>').into();
6905    let (_, mut marked_ranges) = marked_text_ranges_by(
6906        marked_string,
6907        vec![complete_from_marker.clone(), replace_range_marker.clone()],
6908    );
6909
6910    let complete_from_position =
6911        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
6912    let replace_range =
6913        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
6914
6915    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
6916        let completions = completions.clone();
6917        async move {
6918            assert_eq!(params.text_document_position.text_document.uri, url.clone());
6919            assert_eq!(
6920                params.text_document_position.position,
6921                complete_from_position
6922            );
6923            Ok(Some(lsp::CompletionResponse::Array(
6924                completions
6925                    .iter()
6926                    .map(|completion_text| lsp::CompletionItem {
6927                        label: completion_text.to_string(),
6928                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
6929                            range: replace_range,
6930                            new_text: completion_text.to_string(),
6931                        })),
6932                        ..Default::default()
6933                    })
6934                    .collect(),
6935            )))
6936        }
6937    });
6938
6939    async move {
6940        request.next().await;
6941    }
6942}
6943
6944fn handle_resolve_completion_request<'a>(
6945    cx: &mut EditorLspTestContext<'a>,
6946    edits: Option<Vec<(&'static str, &'static str)>>,
6947) -> impl Future<Output = ()> {
6948    let edits = edits.map(|edits| {
6949        edits
6950            .iter()
6951            .map(|(marked_string, new_text)| {
6952                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
6953                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
6954                lsp::TextEdit::new(replace_range, new_text.to_string())
6955            })
6956            .collect::<Vec<_>>()
6957    });
6958
6959    let mut request =
6960        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
6961            let edits = edits.clone();
6962            async move {
6963                Ok(lsp::CompletionItem {
6964                    additional_text_edits: edits,
6965                    ..Default::default()
6966                })
6967            }
6968        });
6969
6970    async move {
6971        request.next().await;
6972    }
6973}
6974
6975fn handle_copilot_completion_request(
6976    lsp: &lsp::FakeLanguageServer,
6977    completions: Vec<copilot::request::Completion>,
6978    completions_cycling: Vec<copilot::request::Completion>,
6979) {
6980    lsp.handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| {
6981        let completions = completions.clone();
6982        async move {
6983            Ok(copilot::request::GetCompletionsResult {
6984                completions: completions.clone(),
6985            })
6986        }
6987    });
6988    lsp.handle_request::<copilot::request::GetCompletionsCycling, _, _>(move |_params, _cx| {
6989        let completions_cycling = completions_cycling.clone();
6990        async move {
6991            Ok(copilot::request::GetCompletionsResult {
6992                completions: completions_cycling.clone(),
6993            })
6994        }
6995    });
6996}
6997
6998pub(crate) fn update_test_settings(
6999    cx: &mut TestAppContext,
7000    f: impl Fn(&mut AllLanguageSettingsContent),
7001) {
7002    cx.update(|cx| {
7003        cx.update_global::<SettingsStore, _, _>(|store, cx| {
7004            store.update_user_settings::<AllLanguageSettings>(cx, f);
7005        });
7006    });
7007}
7008
7009pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
7010    cx.foreground().forbid_parking();
7011
7012    cx.update(|cx| {
7013        cx.set_global(SettingsStore::test(cx));
7014        theme::init((), cx);
7015        client::init_settings(cx);
7016        language::init(cx);
7017        Project::init_settings(cx);
7018        workspace::init_settings(cx);
7019        crate::init(cx);
7020    });
7021
7022    update_test_settings(cx, f);
7023}