editor_tests.rs

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