editor_tests.rs

   1use super::*;
   2use crate::{
   3    scroll::scroll_amount::ScrollAmount,
   4    test::{
   5        assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
   6        editor_test_context::EditorTestContext, select_ranges,
   7    },
   8    JoinLines,
   9};
  10
  11use futures::StreamExt;
  12use gpui::{div, TestAppContext, VisualTestContext, WindowBounds, WindowOptions};
  13use indoc::indoc;
  14use language::{
  15    language_settings::{AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent},
  16    BracketPairConfig,
  17    Capability::ReadWrite,
  18    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageRegistry,
  19    Override, Point,
  20};
  21use parking_lot::Mutex;
  22use project::project_settings::{LspSettings, ProjectSettings};
  23use project::FakeFs;
  24use serde_json::{self, json};
  25use std::sync::atomic;
  26use std::sync::atomic::AtomicUsize;
  27use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
  28use unindent::Unindent;
  29use util::{
  30    assert_set_eq,
  31    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
  32};
  33use workspace::{
  34    item::{FollowEvent, FollowableItem, Item, ItemHandle},
  35    NavigationEntry, ViewId,
  36};
  37
  38#[gpui::test]
  39fn test_edit_events(cx: &mut TestAppContext) {
  40    init_test(cx, |_| {});
  41
  42    let buffer = cx.new_model(|cx| {
  43        let mut buffer =
  44            language::Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "123456");
  45        buffer.set_group_interval(Duration::from_secs(1));
  46        buffer
  47    });
  48
  49    let events = Rc::new(RefCell::new(Vec::new()));
  50    let editor1 = cx.add_window({
  51        let events = events.clone();
  52        |cx| {
  53            let view = cx.view().clone();
  54            cx.subscribe(&view, move |_, _, event: &EditorEvent, _| {
  55                if matches!(event, EditorEvent::Edited | EditorEvent::BufferEdited) {
  56                    events.borrow_mut().push(("editor1", event.clone()));
  57                }
  58            })
  59            .detach();
  60            Editor::for_buffer(buffer.clone(), None, cx)
  61        }
  62    });
  63
  64    let editor2 = cx.add_window({
  65        let events = events.clone();
  66        |cx| {
  67            cx.subscribe(&cx.view().clone(), move |_, _, event: &EditorEvent, _| {
  68                if matches!(event, EditorEvent::Edited | EditorEvent::BufferEdited) {
  69                    events.borrow_mut().push(("editor2", event.clone()));
  70                }
  71            })
  72            .detach();
  73            Editor::for_buffer(buffer.clone(), None, cx)
  74        }
  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", EditorEvent::Edited),
  85            ("editor1", EditorEvent::BufferEdited),
  86            ("editor2", EditorEvent::BufferEdited),
  87        ]
  88    );
  89
  90    // Mutating editor 2 will emit an `Edited` event only for that editor.
  91    _ = editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
  92    assert_eq!(
  93        mem::take(&mut *events.borrow_mut()),
  94        [
  95            ("editor2", EditorEvent::Edited),
  96            ("editor1", EditorEvent::BufferEdited),
  97            ("editor2", EditorEvent::BufferEdited),
  98        ]
  99    );
 100
 101    // Undoing on editor 1 will emit an `Edited` event only for that editor.
 102    _ = editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
 103    assert_eq!(
 104        mem::take(&mut *events.borrow_mut()),
 105        [
 106            ("editor1", EditorEvent::Edited),
 107            ("editor1", EditorEvent::BufferEdited),
 108            ("editor2", EditorEvent::BufferEdited),
 109        ]
 110    );
 111
 112    // Redoing on editor 1 will emit an `Edited` event only for that editor.
 113    _ = editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
 114    assert_eq!(
 115        mem::take(&mut *events.borrow_mut()),
 116        [
 117            ("editor1", EditorEvent::Edited),
 118            ("editor1", EditorEvent::BufferEdited),
 119            ("editor2", EditorEvent::BufferEdited),
 120        ]
 121    );
 122
 123    // Undoing on editor 2 will emit an `Edited` event only for that editor.
 124    _ = editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
 125    assert_eq!(
 126        mem::take(&mut *events.borrow_mut()),
 127        [
 128            ("editor2", EditorEvent::Edited),
 129            ("editor1", EditorEvent::BufferEdited),
 130            ("editor2", EditorEvent::BufferEdited),
 131        ]
 132    );
 133
 134    // Redoing on editor 2 will emit an `Edited` event only for that editor.
 135    _ = editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
 136    assert_eq!(
 137        mem::take(&mut *events.borrow_mut()),
 138        [
 139            ("editor2", EditorEvent::Edited),
 140            ("editor1", EditorEvent::BufferEdited),
 141            ("editor2", EditorEvent::BufferEdited),
 142        ]
 143    );
 144
 145    // No event is emitted when the mutation is a no-op.
 146    _ = editor2.update(cx, |editor, cx| {
 147        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
 148
 149        editor.backspace(&Backspace, cx);
 150    });
 151    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
 152}
 153
 154#[gpui::test]
 155fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
 156    init_test(cx, |_| {});
 157
 158    let mut now = Instant::now();
 159    let buffer = cx.new_model(|cx| {
 160        language::Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "123456")
 161    });
 162    let group_interval = buffer.update(cx, |buffer, _| buffer.transaction_group_interval());
 163    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 164    let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 165
 166    _ = editor.update(cx, |editor, cx| {
 167        editor.start_transaction_at(now, cx);
 168        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
 169
 170        editor.insert("cd", cx);
 171        editor.end_transaction_at(now, cx);
 172        assert_eq!(editor.text(cx), "12cd56");
 173        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
 174
 175        editor.start_transaction_at(now, cx);
 176        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
 177        editor.insert("e", cx);
 178        editor.end_transaction_at(now, cx);
 179        assert_eq!(editor.text(cx), "12cde6");
 180        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
 181
 182        now += group_interval + Duration::from_millis(1);
 183        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
 184
 185        // Simulate an edit in another editor
 186        _ = buffer.update(cx, |buffer, cx| {
 187            buffer.start_transaction_at(now, cx);
 188            buffer.edit([(0..1, "a")], None, cx);
 189            buffer.edit([(1..1, "b")], None, cx);
 190            buffer.end_transaction_at(now, cx);
 191        });
 192
 193        assert_eq!(editor.text(cx), "ab2cde6");
 194        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
 195
 196        // Last transaction happened past the group interval in a different editor.
 197        // Undo it individually and don't restore selections.
 198        editor.undo(&Undo, cx);
 199        assert_eq!(editor.text(cx), "12cde6");
 200        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
 201
 202        // First two transactions happened within the group interval in this editor.
 203        // Undo them together and restore selections.
 204        editor.undo(&Undo, cx);
 205        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
 206        assert_eq!(editor.text(cx), "123456");
 207        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
 208
 209        // Redo the first two transactions together.
 210        editor.redo(&Redo, cx);
 211        assert_eq!(editor.text(cx), "12cde6");
 212        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
 213
 214        // Redo the last transaction on its own.
 215        editor.redo(&Redo, cx);
 216        assert_eq!(editor.text(cx), "ab2cde6");
 217        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
 218
 219        // Test empty transactions.
 220        editor.start_transaction_at(now, cx);
 221        editor.end_transaction_at(now, cx);
 222        editor.undo(&Undo, cx);
 223        assert_eq!(editor.text(cx), "12cde6");
 224    });
 225}
 226
 227#[gpui::test]
 228fn test_ime_composition(cx: &mut TestAppContext) {
 229    init_test(cx, |_| {});
 230
 231    let buffer = cx.new_model(|cx| {
 232        let mut buffer =
 233            language::Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "abcde");
 234        // Ensure automatic grouping doesn't occur.
 235        buffer.set_group_interval(Duration::ZERO);
 236        buffer
 237    });
 238
 239    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
 240    cx.add_window(|cx| {
 241        let mut editor = build_editor(buffer.clone(), cx);
 242
 243        // Start a new IME composition.
 244        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
 245        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
 246        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
 247        assert_eq!(editor.text(cx), "äbcde");
 248        assert_eq!(
 249            editor.marked_text_ranges(cx),
 250            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
 251        );
 252
 253        // Finalize IME composition.
 254        editor.replace_text_in_range(None, "ā", cx);
 255        assert_eq!(editor.text(cx), "ābcde");
 256        assert_eq!(editor.marked_text_ranges(cx), None);
 257
 258        // IME composition edits are grouped and are undone/redone at once.
 259        editor.undo(&Default::default(), cx);
 260        assert_eq!(editor.text(cx), "abcde");
 261        assert_eq!(editor.marked_text_ranges(cx), None);
 262        editor.redo(&Default::default(), cx);
 263        assert_eq!(editor.text(cx), "ābcde");
 264        assert_eq!(editor.marked_text_ranges(cx), None);
 265
 266        // Start a new IME composition.
 267        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
 268        assert_eq!(
 269            editor.marked_text_ranges(cx),
 270            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
 271        );
 272
 273        // Undoing during an IME composition cancels it.
 274        editor.undo(&Default::default(), cx);
 275        assert_eq!(editor.text(cx), "ābcde");
 276        assert_eq!(editor.marked_text_ranges(cx), None);
 277
 278        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
 279        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
 280        assert_eq!(editor.text(cx), "ābcdè");
 281        assert_eq!(
 282            editor.marked_text_ranges(cx),
 283            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
 284        );
 285
 286        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
 287        editor.replace_text_in_range(Some(4..999), "ę", cx);
 288        assert_eq!(editor.text(cx), "ābcdę");
 289        assert_eq!(editor.marked_text_ranges(cx), None);
 290
 291        // Start a new IME composition with multiple cursors.
 292        editor.change_selections(None, cx, |s| {
 293            s.select_ranges([
 294                OffsetUtf16(1)..OffsetUtf16(1),
 295                OffsetUtf16(3)..OffsetUtf16(3),
 296                OffsetUtf16(5)..OffsetUtf16(5),
 297            ])
 298        });
 299        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
 300        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
 301        assert_eq!(
 302            editor.marked_text_ranges(cx),
 303            Some(vec![
 304                OffsetUtf16(0)..OffsetUtf16(3),
 305                OffsetUtf16(4)..OffsetUtf16(7),
 306                OffsetUtf16(8)..OffsetUtf16(11)
 307            ])
 308        );
 309
 310        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
 311        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
 312        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
 313        assert_eq!(
 314            editor.marked_text_ranges(cx),
 315            Some(vec![
 316                OffsetUtf16(1)..OffsetUtf16(2),
 317                OffsetUtf16(5)..OffsetUtf16(6),
 318                OffsetUtf16(9)..OffsetUtf16(10)
 319            ])
 320        );
 321
 322        // Finalize IME composition with multiple cursors.
 323        editor.replace_text_in_range(Some(9..10), "2", cx);
 324        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
 325        assert_eq!(editor.marked_text_ranges(cx), None);
 326
 327        editor
 328    });
 329}
 330
 331#[gpui::test]
 332fn test_selection_with_mouse(cx: &mut TestAppContext) {
 333    init_test(cx, |_| {});
 334
 335    let editor = cx.add_window(|cx| {
 336        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
 337        build_editor(buffer, cx)
 338    });
 339
 340    _ = editor.update(cx, |view, cx| {
 341        view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
 342    });
 343    assert_eq!(
 344        editor
 345            .update(cx, |view, cx| view.selections.display_ranges(cx))
 346            .unwrap(),
 347        [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
 348    );
 349
 350    _ = editor.update(cx, |view, cx| {
 351        view.update_selection(
 352            DisplayPoint::new(3, 3),
 353            0,
 354            gpui::Point::<f32>::default(),
 355            cx,
 356        );
 357    });
 358
 359    assert_eq!(
 360        editor
 361            .update(cx, |view, cx| view.selections.display_ranges(cx))
 362            .unwrap(),
 363        [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
 364    );
 365
 366    _ = editor.update(cx, |view, cx| {
 367        view.update_selection(
 368            DisplayPoint::new(1, 1),
 369            0,
 370            gpui::Point::<f32>::default(),
 371            cx,
 372        );
 373    });
 374
 375    assert_eq!(
 376        editor
 377            .update(cx, |view, cx| view.selections.display_ranges(cx))
 378            .unwrap(),
 379        [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
 380    );
 381
 382    _ = editor.update(cx, |view, cx| {
 383        view.end_selection(cx);
 384        view.update_selection(
 385            DisplayPoint::new(3, 3),
 386            0,
 387            gpui::Point::<f32>::default(),
 388            cx,
 389        );
 390    });
 391
 392    assert_eq!(
 393        editor
 394            .update(cx, |view, cx| view.selections.display_ranges(cx))
 395            .unwrap(),
 396        [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
 397    );
 398
 399    _ = editor.update(cx, |view, cx| {
 400        view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
 401        view.update_selection(
 402            DisplayPoint::new(0, 0),
 403            0,
 404            gpui::Point::<f32>::default(),
 405            cx,
 406        );
 407    });
 408
 409    assert_eq!(
 410        editor
 411            .update(cx, |view, cx| view.selections.display_ranges(cx))
 412            .unwrap(),
 413        [
 414            DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
 415            DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
 416        ]
 417    );
 418
 419    _ = editor.update(cx, |view, cx| {
 420        view.end_selection(cx);
 421    });
 422
 423    assert_eq!(
 424        editor
 425            .update(cx, |view, cx| view.selections.display_ranges(cx))
 426            .unwrap(),
 427        [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
 428    );
 429}
 430
 431#[gpui::test]
 432fn test_canceling_pending_selection(cx: &mut TestAppContext) {
 433    init_test(cx, |_| {});
 434
 435    let view = cx.add_window(|cx| {
 436        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
 437        build_editor(buffer, cx)
 438    });
 439
 440    _ = view.update(cx, |view, cx| {
 441        view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
 442        assert_eq!(
 443            view.selections.display_ranges(cx),
 444            [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
 445        );
 446    });
 447
 448    _ = view.update(cx, |view, cx| {
 449        view.update_selection(
 450            DisplayPoint::new(3, 3),
 451            0,
 452            gpui::Point::<f32>::default(),
 453            cx,
 454        );
 455        assert_eq!(
 456            view.selections.display_ranges(cx),
 457            [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
 458        );
 459    });
 460
 461    _ = view.update(cx, |view, cx| {
 462        view.cancel(&Cancel, cx);
 463        view.update_selection(
 464            DisplayPoint::new(1, 1),
 465            0,
 466            gpui::Point::<f32>::default(),
 467            cx,
 468        );
 469        assert_eq!(
 470            view.selections.display_ranges(cx),
 471            [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
 472        );
 473    });
 474}
 475
 476#[gpui::test]
 477fn test_clone(cx: &mut TestAppContext) {
 478    init_test(cx, |_| {});
 479
 480    let (text, selection_ranges) = marked_text_ranges(
 481        indoc! {"
 482            one
 483            two
 484            threeˇ
 485            four
 486            fiveˇ
 487        "},
 488        true,
 489    );
 490
 491    let editor = cx.add_window(|cx| {
 492        let buffer = MultiBuffer::build_simple(&text, cx);
 493        build_editor(buffer, cx)
 494    });
 495
 496    _ = editor.update(cx, |editor, cx| {
 497        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
 498        editor.fold_ranges(
 499            [
 500                Point::new(1, 0)..Point::new(2, 0),
 501                Point::new(3, 0)..Point::new(4, 0),
 502            ],
 503            true,
 504            cx,
 505        );
 506    });
 507
 508    let cloned_editor = editor
 509        .update(cx, |editor, cx| {
 510            cx.open_window(Default::default(), |cx| cx.new_view(|cx| editor.clone(cx)))
 511        })
 512        .unwrap();
 513
 514    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
 515    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
 516
 517    assert_eq!(
 518        cloned_editor
 519            .update(cx, |e, cx| e.display_text(cx))
 520            .unwrap(),
 521        editor.update(cx, |e, cx| e.display_text(cx)).unwrap()
 522    );
 523    assert_eq!(
 524        cloned_snapshot
 525            .folds_in_range(0..text.len())
 526            .collect::<Vec<_>>(),
 527        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
 528    );
 529    assert_set_eq!(
 530        cloned_editor
 531            .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
 532            .unwrap(),
 533        editor
 534            .update(cx, |editor, cx| editor.selections.ranges(cx))
 535            .unwrap()
 536    );
 537    assert_set_eq!(
 538        cloned_editor
 539            .update(cx, |e, cx| e.selections.display_ranges(cx))
 540            .unwrap(),
 541        editor
 542            .update(cx, |e, cx| e.selections.display_ranges(cx))
 543            .unwrap()
 544    );
 545}
 546
 547#[gpui::test]
 548async fn test_navigation_history(cx: &mut TestAppContext) {
 549    init_test(cx, |_| {});
 550
 551    use workspace::item::Item;
 552
 553    let fs = FakeFs::new(cx.executor());
 554    let project = Project::test(fs, [], cx).await;
 555    let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
 556    let pane = workspace
 557        .update(cx, |workspace, _| workspace.active_pane().clone())
 558        .unwrap();
 559
 560    _ = workspace.update(cx, |_v, cx| {
 561        cx.new_view(|cx| {
 562            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
 563            let mut editor = build_editor(buffer.clone(), cx);
 564            let handle = cx.view();
 565            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
 566
 567            fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
 568                editor.nav_history.as_mut().unwrap().pop_backward(cx)
 569            }
 570
 571            // Move the cursor a small distance.
 572            // Nothing is added to the navigation history.
 573            editor.change_selections(None, cx, |s| {
 574                s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
 575            });
 576            editor.change_selections(None, cx, |s| {
 577                s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
 578            });
 579            assert!(pop_history(&mut editor, cx).is_none());
 580
 581            // Move the cursor a large distance.
 582            // The history can jump back to the previous position.
 583            editor.change_selections(None, cx, |s| {
 584                s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
 585            });
 586            let nav_entry = pop_history(&mut editor, cx).unwrap();
 587            editor.navigate(nav_entry.data.unwrap(), cx);
 588            assert_eq!(nav_entry.item.id(), cx.entity_id());
 589            assert_eq!(
 590                editor.selections.display_ranges(cx),
 591                &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
 592            );
 593            assert!(pop_history(&mut editor, cx).is_none());
 594
 595            // Move the cursor a small distance via the mouse.
 596            // Nothing is added to the navigation history.
 597            editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
 598            editor.end_selection(cx);
 599            assert_eq!(
 600                editor.selections.display_ranges(cx),
 601                &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
 602            );
 603            assert!(pop_history(&mut editor, cx).is_none());
 604
 605            // Move the cursor a large distance via the mouse.
 606            // The history can jump back to the previous position.
 607            editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
 608            editor.end_selection(cx);
 609            assert_eq!(
 610                editor.selections.display_ranges(cx),
 611                &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
 612            );
 613            let nav_entry = pop_history(&mut editor, cx).unwrap();
 614            editor.navigate(nav_entry.data.unwrap(), cx);
 615            assert_eq!(nav_entry.item.id(), cx.entity_id());
 616            assert_eq!(
 617                editor.selections.display_ranges(cx),
 618                &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
 619            );
 620            assert!(pop_history(&mut editor, cx).is_none());
 621
 622            // Set scroll position to check later
 623            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), cx);
 624            let original_scroll_position = editor.scroll_manager.anchor();
 625
 626            // Jump to the end of the document and adjust scroll
 627            editor.move_to_end(&MoveToEnd, cx);
 628            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), cx);
 629            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
 630
 631            let nav_entry = pop_history(&mut editor, cx).unwrap();
 632            editor.navigate(nav_entry.data.unwrap(), cx);
 633            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
 634
 635            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
 636            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
 637            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
 638            let invalid_point = Point::new(9999, 0);
 639            editor.navigate(
 640                Box::new(NavigationData {
 641                    cursor_anchor: invalid_anchor,
 642                    cursor_position: invalid_point,
 643                    scroll_anchor: ScrollAnchor {
 644                        anchor: invalid_anchor,
 645                        offset: Default::default(),
 646                    },
 647                    scroll_top_row: invalid_point.row,
 648                }),
 649                cx,
 650            );
 651            assert_eq!(
 652                editor.selections.display_ranges(cx),
 653                &[editor.max_point(cx)..editor.max_point(cx)]
 654            );
 655            assert_eq!(
 656                editor.scroll_position(cx),
 657                gpui::Point::new(0., editor.max_point(cx).row() as f32)
 658            );
 659
 660            editor
 661        })
 662    });
 663}
 664
 665#[gpui::test]
 666fn test_cancel(cx: &mut TestAppContext) {
 667    init_test(cx, |_| {});
 668
 669    let view = cx.add_window(|cx| {
 670        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
 671        build_editor(buffer, cx)
 672    });
 673
 674    _ = view.update(cx, |view, cx| {
 675        view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
 676        view.update_selection(
 677            DisplayPoint::new(1, 1),
 678            0,
 679            gpui::Point::<f32>::default(),
 680            cx,
 681        );
 682        view.end_selection(cx);
 683
 684        view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
 685        view.update_selection(
 686            DisplayPoint::new(0, 3),
 687            0,
 688            gpui::Point::<f32>::default(),
 689            cx,
 690        );
 691        view.end_selection(cx);
 692        assert_eq!(
 693            view.selections.display_ranges(cx),
 694            [
 695                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
 696                DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
 697            ]
 698        );
 699    });
 700
 701    _ = view.update(cx, |view, cx| {
 702        view.cancel(&Cancel, cx);
 703        assert_eq!(
 704            view.selections.display_ranges(cx),
 705            [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
 706        );
 707    });
 708
 709    _ = view.update(cx, |view, cx| {
 710        view.cancel(&Cancel, cx);
 711        assert_eq!(
 712            view.selections.display_ranges(cx),
 713            [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
 714        );
 715    });
 716}
 717
 718#[gpui::test]
 719fn test_fold_action(cx: &mut TestAppContext) {
 720    init_test(cx, |_| {});
 721
 722    let view = cx.add_window(|cx| {
 723        let buffer = MultiBuffer::build_simple(
 724            &"
 725                impl Foo {
 726                    // Hello!
 727
 728                    fn a() {
 729                        1
 730                    }
 731
 732                    fn b() {
 733                        2
 734                    }
 735
 736                    fn c() {
 737                        3
 738                    }
 739                }
 740            "
 741            .unindent(),
 742            cx,
 743        );
 744        build_editor(buffer.clone(), cx)
 745    });
 746
 747    _ = view.update(cx, |view, cx| {
 748        view.change_selections(None, cx, |s| {
 749            s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]);
 750        });
 751        view.fold(&Fold, cx);
 752        assert_eq!(
 753            view.display_text(cx),
 754            "
 755                impl Foo {
 756                    // Hello!
 757
 758                    fn a() {
 759                        1
 760                    }
 761
 762                    fn b() {⋯
 763                    }
 764
 765                    fn c() {⋯
 766                    }
 767                }
 768            "
 769            .unindent(),
 770        );
 771
 772        view.fold(&Fold, cx);
 773        assert_eq!(
 774            view.display_text(cx),
 775            "
 776                impl Foo {⋯
 777                }
 778            "
 779            .unindent(),
 780        );
 781
 782        view.unfold_lines(&UnfoldLines, cx);
 783        assert_eq!(
 784            view.display_text(cx),
 785            "
 786                impl Foo {
 787                    // Hello!
 788
 789                    fn a() {
 790                        1
 791                    }
 792
 793                    fn b() {⋯
 794                    }
 795
 796                    fn c() {⋯
 797                    }
 798                }
 799            "
 800            .unindent(),
 801        );
 802
 803        view.unfold_lines(&UnfoldLines, cx);
 804        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
 805    });
 806}
 807
 808#[gpui::test]
 809fn test_move_cursor(cx: &mut TestAppContext) {
 810    init_test(cx, |_| {});
 811
 812    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 813    let view = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 814
 815    _ = buffer.update(cx, |buffer, cx| {
 816        buffer.edit(
 817            vec![
 818                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 819                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 820            ],
 821            None,
 822            cx,
 823        );
 824    });
 825    _ = view.update(cx, |view, cx| {
 826        assert_eq!(
 827            view.selections.display_ranges(cx),
 828            &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
 829        );
 830
 831        view.move_down(&MoveDown, cx);
 832        assert_eq!(
 833            view.selections.display_ranges(cx),
 834            &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
 835        );
 836
 837        view.move_right(&MoveRight, cx);
 838        assert_eq!(
 839            view.selections.display_ranges(cx),
 840            &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
 841        );
 842
 843        view.move_left(&MoveLeft, cx);
 844        assert_eq!(
 845            view.selections.display_ranges(cx),
 846            &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
 847        );
 848
 849        view.move_up(&MoveUp, cx);
 850        assert_eq!(
 851            view.selections.display_ranges(cx),
 852            &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
 853        );
 854
 855        view.move_to_end(&MoveToEnd, cx);
 856        assert_eq!(
 857            view.selections.display_ranges(cx),
 858            &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
 859        );
 860
 861        view.move_to_beginning(&MoveToBeginning, cx);
 862        assert_eq!(
 863            view.selections.display_ranges(cx),
 864            &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
 865        );
 866
 867        view.change_selections(None, cx, |s| {
 868            s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]);
 869        });
 870        view.select_to_beginning(&SelectToBeginning, cx);
 871        assert_eq!(
 872            view.selections.display_ranges(cx),
 873            &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
 874        );
 875
 876        view.select_to_end(&SelectToEnd, cx);
 877        assert_eq!(
 878            view.selections.display_ranges(cx),
 879            &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
 880        );
 881    });
 882}
 883
 884#[gpui::test]
 885fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 886    init_test(cx, |_| {});
 887
 888    let view = cx.add_window(|cx| {
 889        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
 890        build_editor(buffer.clone(), cx)
 891    });
 892
 893    assert_eq!('ⓐ'.len_utf8(), 3);
 894    assert_eq!('α'.len_utf8(), 2);
 895
 896    _ = view.update(cx, |view, cx| {
 897        view.fold_ranges(
 898            vec![
 899                Point::new(0, 6)..Point::new(0, 12),
 900                Point::new(1, 2)..Point::new(1, 4),
 901                Point::new(2, 4)..Point::new(2, 8),
 902            ],
 903            true,
 904            cx,
 905        );
 906        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
 907
 908        view.move_right(&MoveRight, cx);
 909        assert_eq!(
 910            view.selections.display_ranges(cx),
 911            &[empty_range(0, "".len())]
 912        );
 913        view.move_right(&MoveRight, cx);
 914        assert_eq!(
 915            view.selections.display_ranges(cx),
 916            &[empty_range(0, "ⓐⓑ".len())]
 917        );
 918        view.move_right(&MoveRight, cx);
 919        assert_eq!(
 920            view.selections.display_ranges(cx),
 921            &[empty_range(0, "ⓐⓑ⋯".len())]
 922        );
 923
 924        view.move_down(&MoveDown, cx);
 925        assert_eq!(
 926            view.selections.display_ranges(cx),
 927            &[empty_range(1, "ab⋯e".len())]
 928        );
 929        view.move_left(&MoveLeft, cx);
 930        assert_eq!(
 931            view.selections.display_ranges(cx),
 932            &[empty_range(1, "ab⋯".len())]
 933        );
 934        view.move_left(&MoveLeft, cx);
 935        assert_eq!(
 936            view.selections.display_ranges(cx),
 937            &[empty_range(1, "ab".len())]
 938        );
 939        view.move_left(&MoveLeft, cx);
 940        assert_eq!(
 941            view.selections.display_ranges(cx),
 942            &[empty_range(1, "a".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        view.move_right(&MoveRight, cx);
 951        assert_eq!(
 952            view.selections.display_ranges(cx),
 953            &[empty_range(2, "αβ".len())]
 954        );
 955        view.move_right(&MoveRight, cx);
 956        assert_eq!(
 957            view.selections.display_ranges(cx),
 958            &[empty_range(2, "αβ⋯".len())]
 959        );
 960        view.move_right(&MoveRight, cx);
 961        assert_eq!(
 962            view.selections.display_ranges(cx),
 963            &[empty_range(2, "αβ⋯ε".len())]
 964        );
 965
 966        view.move_up(&MoveUp, cx);
 967        assert_eq!(
 968            view.selections.display_ranges(cx),
 969            &[empty_range(1, "ab⋯e".len())]
 970        );
 971        view.move_down(&MoveDown, cx);
 972        assert_eq!(
 973            view.selections.display_ranges(cx),
 974            &[empty_range(2, "αβ⋯ε".len())]
 975        );
 976        view.move_up(&MoveUp, cx);
 977        assert_eq!(
 978            view.selections.display_ranges(cx),
 979            &[empty_range(1, "ab⋯e".len())]
 980        );
 981
 982        view.move_up(&MoveUp, cx);
 983        assert_eq!(
 984            view.selections.display_ranges(cx),
 985            &[empty_range(0, "ⓐⓑ".len())]
 986        );
 987        view.move_left(&MoveLeft, cx);
 988        assert_eq!(
 989            view.selections.display_ranges(cx),
 990            &[empty_range(0, "".len())]
 991        );
 992        view.move_left(&MoveLeft, cx);
 993        assert_eq!(
 994            view.selections.display_ranges(cx),
 995            &[empty_range(0, "".len())]
 996        );
 997    });
 998}
 999
1000#[gpui::test]
1001fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
1002    init_test(cx, |_| {});
1003
1004    let view = cx.add_window(|cx| {
1005        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
1006        build_editor(buffer.clone(), cx)
1007    });
1008    _ = view.update(cx, |view, cx| {
1009        view.change_selections(None, cx, |s| {
1010            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
1011        });
1012        view.move_down(&MoveDown, cx);
1013        assert_eq!(
1014            view.selections.display_ranges(cx),
1015            &[empty_range(1, "abcd".len())]
1016        );
1017
1018        view.move_down(&MoveDown, cx);
1019        assert_eq!(
1020            view.selections.display_ranges(cx),
1021            &[empty_range(2, "αβγ".len())]
1022        );
1023
1024        view.move_down(&MoveDown, cx);
1025        assert_eq!(
1026            view.selections.display_ranges(cx),
1027            &[empty_range(3, "abcd".len())]
1028        );
1029
1030        view.move_down(&MoveDown, cx);
1031        assert_eq!(
1032            view.selections.display_ranges(cx),
1033            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
1034        );
1035
1036        view.move_up(&MoveUp, cx);
1037        assert_eq!(
1038            view.selections.display_ranges(cx),
1039            &[empty_range(3, "abcd".len())]
1040        );
1041
1042        view.move_up(&MoveUp, cx);
1043        assert_eq!(
1044            view.selections.display_ranges(cx),
1045            &[empty_range(2, "αβγ".len())]
1046        );
1047    });
1048}
1049
1050#[gpui::test]
1051fn test_beginning_end_of_line(cx: &mut TestAppContext) {
1052    init_test(cx, |_| {});
1053
1054    let view = cx.add_window(|cx| {
1055        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
1056        build_editor(buffer, cx)
1057    });
1058    _ = view.update(cx, |view, cx| {
1059        view.change_selections(None, cx, |s| {
1060            s.select_display_ranges([
1061                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
1062                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
1063            ]);
1064        });
1065    });
1066
1067    _ = view.update(cx, |view, cx| {
1068        view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
1069        assert_eq!(
1070            view.selections.display_ranges(cx),
1071            &[
1072                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
1073                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
1074            ]
1075        );
1076    });
1077
1078    _ = view.update(cx, |view, cx| {
1079        view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
1080        assert_eq!(
1081            view.selections.display_ranges(cx),
1082            &[
1083                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
1084                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
1085            ]
1086        );
1087    });
1088
1089    _ = view.update(cx, |view, cx| {
1090        view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
1091        assert_eq!(
1092            view.selections.display_ranges(cx),
1093            &[
1094                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
1095                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
1096            ]
1097        );
1098    });
1099
1100    _ = view.update(cx, |view, cx| {
1101        view.move_to_end_of_line(&MoveToEndOfLine, cx);
1102        assert_eq!(
1103            view.selections.display_ranges(cx),
1104            &[
1105                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
1106                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
1107            ]
1108        );
1109    });
1110
1111    // Moving to the end of line again is a no-op.
1112    _ = view.update(cx, |view, cx| {
1113        view.move_to_end_of_line(&MoveToEndOfLine, cx);
1114        assert_eq!(
1115            view.selections.display_ranges(cx),
1116            &[
1117                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
1118                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
1119            ]
1120        );
1121    });
1122
1123    _ = view.update(cx, |view, cx| {
1124        view.move_left(&MoveLeft, cx);
1125        view.select_to_beginning_of_line(
1126            &SelectToBeginningOfLine {
1127                stop_at_soft_wraps: true,
1128            },
1129            cx,
1130        );
1131        assert_eq!(
1132            view.selections.display_ranges(cx),
1133            &[
1134                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
1135                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
1136            ]
1137        );
1138    });
1139
1140    _ = view.update(cx, |view, cx| {
1141        view.select_to_beginning_of_line(
1142            &SelectToBeginningOfLine {
1143                stop_at_soft_wraps: true,
1144            },
1145            cx,
1146        );
1147        assert_eq!(
1148            view.selections.display_ranges(cx),
1149            &[
1150                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
1151                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
1152            ]
1153        );
1154    });
1155
1156    _ = view.update(cx, |view, cx| {
1157        view.select_to_beginning_of_line(
1158            &SelectToBeginningOfLine {
1159                stop_at_soft_wraps: true,
1160            },
1161            cx,
1162        );
1163        assert_eq!(
1164            view.selections.display_ranges(cx),
1165            &[
1166                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
1167                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
1168            ]
1169        );
1170    });
1171
1172    _ = view.update(cx, |view, cx| {
1173        view.select_to_end_of_line(
1174            &SelectToEndOfLine {
1175                stop_at_soft_wraps: true,
1176            },
1177            cx,
1178        );
1179        assert_eq!(
1180            view.selections.display_ranges(cx),
1181            &[
1182                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
1183                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
1184            ]
1185        );
1186    });
1187
1188    _ = view.update(cx, |view, cx| {
1189        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
1190        assert_eq!(view.display_text(cx), "ab\n  de");
1191        assert_eq!(
1192            view.selections.display_ranges(cx),
1193            &[
1194                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
1195                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
1196            ]
1197        );
1198    });
1199
1200    _ = view.update(cx, |view, cx| {
1201        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
1202        assert_eq!(view.display_text(cx), "\n");
1203        assert_eq!(
1204            view.selections.display_ranges(cx),
1205            &[
1206                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
1207                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
1208            ]
1209        );
1210    });
1211}
1212
1213#[gpui::test]
1214fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
1215    init_test(cx, |_| {});
1216
1217    let view = cx.add_window(|cx| {
1218        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
1219        build_editor(buffer, cx)
1220    });
1221    _ = view.update(cx, |view, cx| {
1222        view.change_selections(None, cx, |s| {
1223            s.select_display_ranges([
1224                DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
1225                DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
1226            ])
1227        });
1228
1229        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1230        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
1231
1232        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1233        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
1234
1235        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1236        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
1237
1238        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1239        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
1240
1241        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1242        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
1243
1244        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1245        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
1246
1247        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1248        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
1249
1250        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1251        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
1252
1253        view.move_right(&MoveRight, cx);
1254        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
1255        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
1256
1257        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
1258        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
1259
1260        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
1261        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
1262    });
1263}
1264
1265#[gpui::test]
1266fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
1267    init_test(cx, |_| {});
1268
1269    let view = cx.add_window(|cx| {
1270        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
1271        build_editor(buffer, cx)
1272    });
1273
1274    _ = view.update(cx, |view, cx| {
1275        view.set_wrap_width(Some(140.0.into()), cx);
1276        assert_eq!(
1277            view.display_text(cx),
1278            "use one::{\n    two::three::\n    four::five\n};"
1279        );
1280
1281        view.change_selections(None, cx, |s| {
1282            s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]);
1283        });
1284
1285        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1286        assert_eq!(
1287            view.selections.display_ranges(cx),
1288            &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
1289        );
1290
1291        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1292        assert_eq!(
1293            view.selections.display_ranges(cx),
1294            &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
1295        );
1296
1297        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1298        assert_eq!(
1299            view.selections.display_ranges(cx),
1300            &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
1301        );
1302
1303        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
1304        assert_eq!(
1305            view.selections.display_ranges(cx),
1306            &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
1307        );
1308
1309        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1310        assert_eq!(
1311            view.selections.display_ranges(cx),
1312            &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
1313        );
1314
1315        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
1316        assert_eq!(
1317            view.selections.display_ranges(cx),
1318            &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
1319        );
1320    });
1321}
1322
1323#[gpui::test]
1324async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
1325    init_test(cx, |_| {});
1326    let mut cx = EditorTestContext::new(cx).await;
1327
1328    let line_height = cx.editor(|editor, cx| {
1329        editor
1330            .style()
1331            .unwrap()
1332            .text
1333            .line_height_in_pixels(cx.rem_size())
1334    });
1335    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
1336
1337    cx.set_state(
1338        &r#"ˇone
1339        two
1340
1341        three
1342        fourˇ
1343        five
1344
1345        six"#
1346            .unindent(),
1347    );
1348
1349    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
1350    cx.assert_editor_state(
1351        &r#"one
1352        two
1353        ˇ
1354        three
1355        four
1356        five
1357        ˇ
1358        six"#
1359            .unindent(),
1360    );
1361
1362    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
1363    cx.assert_editor_state(
1364        &r#"one
1365        two
1366
1367        three
1368        four
1369        five
1370        ˇ
1371        sixˇ"#
1372            .unindent(),
1373    );
1374
1375    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
1376    cx.assert_editor_state(
1377        &r#"one
1378        two
1379
1380        three
1381        four
1382        five
1383
1384        sixˇ"#
1385            .unindent(),
1386    );
1387
1388    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
1389    cx.assert_editor_state(
1390        &r#"one
1391        two
1392
1393        three
1394        four
1395        five
1396        ˇ
1397        six"#
1398            .unindent(),
1399    );
1400
1401    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
1402    cx.assert_editor_state(
1403        &r#"one
1404        two
1405        ˇ
1406        three
1407        four
1408        five
1409
1410        six"#
1411            .unindent(),
1412    );
1413
1414    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
1415    cx.assert_editor_state(
1416        &r#"ˇone
1417        two
1418
1419        three
1420        four
1421        five
1422
1423        six"#
1424            .unindent(),
1425    );
1426}
1427
1428#[gpui::test]
1429async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
1430    init_test(cx, |_| {});
1431    let mut cx = EditorTestContext::new(cx).await;
1432    let line_height = cx.editor(|editor, cx| {
1433        editor
1434            .style()
1435            .unwrap()
1436            .text
1437            .line_height_in_pixels(cx.rem_size())
1438    });
1439    let window = cx.window;
1440    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
1441
1442    cx.set_state(
1443        &r#"ˇone
1444        two
1445        three
1446        four
1447        five
1448        six
1449        seven
1450        eight
1451        nine
1452        ten
1453        "#,
1454    );
1455
1456    cx.update_editor(|editor, cx| {
1457        assert_eq!(
1458            editor.snapshot(cx).scroll_position(),
1459            gpui::Point::new(0., 0.)
1460        );
1461        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
1462        assert_eq!(
1463            editor.snapshot(cx).scroll_position(),
1464            gpui::Point::new(0., 3.)
1465        );
1466        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
1467        assert_eq!(
1468            editor.snapshot(cx).scroll_position(),
1469            gpui::Point::new(0., 6.)
1470        );
1471        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
1472        assert_eq!(
1473            editor.snapshot(cx).scroll_position(),
1474            gpui::Point::new(0., 3.)
1475        );
1476
1477        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
1478        assert_eq!(
1479            editor.snapshot(cx).scroll_position(),
1480            gpui::Point::new(0., 1.)
1481        );
1482        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
1483        assert_eq!(
1484            editor.snapshot(cx).scroll_position(),
1485            gpui::Point::new(0., 3.)
1486        );
1487    });
1488}
1489
1490#[gpui::test]
1491async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
1492    init_test(cx, |_| {});
1493    let mut cx = EditorTestContext::new(cx).await;
1494
1495    let line_height = cx.update_editor(|editor, cx| {
1496        editor.set_vertical_scroll_margin(2, cx);
1497        editor
1498            .style()
1499            .unwrap()
1500            .text
1501            .line_height_in_pixels(cx.rem_size())
1502    });
1503    let window = cx.window;
1504    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
1505
1506    cx.set_state(
1507        &r#"ˇone
1508            two
1509            three
1510            four
1511            five
1512            six
1513            seven
1514            eight
1515            nine
1516            ten
1517        "#,
1518    );
1519    cx.update_editor(|editor, cx| {
1520        assert_eq!(
1521            editor.snapshot(cx).scroll_position(),
1522            gpui::Point::new(0., 0.0)
1523        );
1524    });
1525
1526    // Add a cursor below the visible area. Since both cursors cannot fit
1527    // on screen, the editor autoscrolls to reveal the newest cursor, and
1528    // allows the vertical scroll margin below that cursor.
1529    cx.update_editor(|editor, cx| {
1530        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
1531            selections.select_ranges([
1532                Point::new(0, 0)..Point::new(0, 0),
1533                Point::new(6, 0)..Point::new(6, 0),
1534            ]);
1535        })
1536    });
1537    cx.update_editor(|editor, cx| {
1538        assert_eq!(
1539            editor.snapshot(cx).scroll_position(),
1540            gpui::Point::new(0., 3.0)
1541        );
1542    });
1543
1544    // Move down. The editor cursor scrolls down to track the newest cursor.
1545    cx.update_editor(|editor, cx| {
1546        editor.move_down(&Default::default(), cx);
1547    });
1548    cx.update_editor(|editor, cx| {
1549        assert_eq!(
1550            editor.snapshot(cx).scroll_position(),
1551            gpui::Point::new(0., 4.0)
1552        );
1553    });
1554
1555    // Add a cursor above the visible area. Since both cursors fit on screen,
1556    // the editor scrolls to show both.
1557    cx.update_editor(|editor, cx| {
1558        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
1559            selections.select_ranges([
1560                Point::new(1, 0)..Point::new(1, 0),
1561                Point::new(6, 0)..Point::new(6, 0),
1562            ]);
1563        })
1564    });
1565    cx.update_editor(|editor, cx| {
1566        assert_eq!(
1567            editor.snapshot(cx).scroll_position(),
1568            gpui::Point::new(0., 1.0)
1569        );
1570    });
1571}
1572
1573#[gpui::test]
1574async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
1575    init_test(cx, |_| {});
1576    let mut cx = EditorTestContext::new(cx).await;
1577
1578    let line_height = cx.editor(|editor, cx| {
1579        editor
1580            .style()
1581            .unwrap()
1582            .text
1583            .line_height_in_pixels(cx.rem_size())
1584    });
1585    let window = cx.window;
1586    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
1587    cx.set_state(
1588        &r#"
1589        ˇone
1590        two
1591        threeˇ
1592        four
1593        five
1594        six
1595        seven
1596        eight
1597        nine
1598        ten
1599        "#
1600        .unindent(),
1601    );
1602
1603    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
1604    cx.assert_editor_state(
1605        &r#"
1606        one
1607        two
1608        three
1609        ˇfour
1610        five
1611        sixˇ
1612        seven
1613        eight
1614        nine
1615        ten
1616        "#
1617        .unindent(),
1618    );
1619
1620    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
1621    cx.assert_editor_state(
1622        &r#"
1623        one
1624        two
1625        three
1626        four
1627        five
1628        six
1629        ˇseven
1630        eight
1631        nineˇ
1632        ten
1633        "#
1634        .unindent(),
1635    );
1636
1637    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
1638    cx.assert_editor_state(
1639        &r#"
1640        one
1641        two
1642        three
1643        ˇfour
1644        five
1645        sixˇ
1646        seven
1647        eight
1648        nine
1649        ten
1650        "#
1651        .unindent(),
1652    );
1653
1654    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
1655    cx.assert_editor_state(
1656        &r#"
1657        ˇone
1658        two
1659        threeˇ
1660        four
1661        five
1662        six
1663        seven
1664        eight
1665        nine
1666        ten
1667        "#
1668        .unindent(),
1669    );
1670
1671    // Test select collapsing
1672    cx.update_editor(|editor, cx| {
1673        editor.move_page_down(&MovePageDown::default(), cx);
1674        editor.move_page_down(&MovePageDown::default(), cx);
1675        editor.move_page_down(&MovePageDown::default(), cx);
1676    });
1677    cx.assert_editor_state(
1678        &r#"
1679        one
1680        two
1681        three
1682        four
1683        five
1684        six
1685        seven
1686        eight
1687        nine
1688        ˇten
1689        ˇ"#
1690        .unindent(),
1691    );
1692}
1693
1694#[gpui::test]
1695async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
1696    init_test(cx, |_| {});
1697    let mut cx = EditorTestContext::new(cx).await;
1698    cx.set_state("one «two threeˇ» four");
1699    cx.update_editor(|editor, cx| {
1700        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
1701        assert_eq!(editor.text(cx), " four");
1702    });
1703}
1704
1705#[gpui::test]
1706fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
1707    init_test(cx, |_| {});
1708
1709    let view = cx.add_window(|cx| {
1710        let buffer = MultiBuffer::build_simple("one two three four", cx);
1711        build_editor(buffer.clone(), cx)
1712    });
1713
1714    _ = view.update(cx, |view, cx| {
1715        view.change_selections(None, cx, |s| {
1716            s.select_display_ranges([
1717                // an empty selection - the preceding word fragment is deleted
1718                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
1719                // characters selected - they are deleted
1720                DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
1721            ])
1722        });
1723        view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
1724        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
1725    });
1726
1727    _ = view.update(cx, |view, cx| {
1728        view.change_selections(None, cx, |s| {
1729            s.select_display_ranges([
1730                // an empty selection - the following word fragment is deleted
1731                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
1732                // characters selected - they are deleted
1733                DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
1734            ])
1735        });
1736        view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
1737        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
1738    });
1739}
1740
1741#[gpui::test]
1742fn test_newline(cx: &mut TestAppContext) {
1743    init_test(cx, |_| {});
1744
1745    let view = cx.add_window(|cx| {
1746        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
1747        build_editor(buffer.clone(), cx)
1748    });
1749
1750    _ = view.update(cx, |view, cx| {
1751        view.change_selections(None, cx, |s| {
1752            s.select_display_ranges([
1753                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
1754                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
1755                DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
1756            ])
1757        });
1758
1759        view.newline(&Newline, cx);
1760        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
1761    });
1762}
1763
1764#[gpui::test]
1765fn test_newline_with_old_selections(cx: &mut TestAppContext) {
1766    init_test(cx, |_| {});
1767
1768    let editor = cx.add_window(|cx| {
1769        let buffer = MultiBuffer::build_simple(
1770            "
1771                a
1772                b(
1773                    X
1774                )
1775                c(
1776                    X
1777                )
1778            "
1779            .unindent()
1780            .as_str(),
1781            cx,
1782        );
1783        let mut editor = build_editor(buffer.clone(), cx);
1784        editor.change_selections(None, cx, |s| {
1785            s.select_ranges([
1786                Point::new(2, 4)..Point::new(2, 5),
1787                Point::new(5, 4)..Point::new(5, 5),
1788            ])
1789        });
1790        editor
1791    });
1792
1793    _ = editor.update(cx, |editor, cx| {
1794        // Edit the buffer directly, deleting ranges surrounding the editor's selections
1795        editor.buffer.update(cx, |buffer, cx| {
1796            buffer.edit(
1797                [
1798                    (Point::new(1, 2)..Point::new(3, 0), ""),
1799                    (Point::new(4, 2)..Point::new(6, 0), ""),
1800                ],
1801                None,
1802                cx,
1803            );
1804            assert_eq!(
1805                buffer.read(cx).text(),
1806                "
1807                    a
1808                    b()
1809                    c()
1810                "
1811                .unindent()
1812            );
1813        });
1814        assert_eq!(
1815            editor.selections.ranges(cx),
1816            &[
1817                Point::new(1, 2)..Point::new(1, 2),
1818                Point::new(2, 2)..Point::new(2, 2),
1819            ],
1820        );
1821
1822        editor.newline(&Newline, cx);
1823        assert_eq!(
1824            editor.text(cx),
1825            "
1826                a
1827                b(
1828                )
1829                c(
1830                )
1831            "
1832            .unindent()
1833        );
1834
1835        // The selections are moved after the inserted newlines
1836        assert_eq!(
1837            editor.selections.ranges(cx),
1838            &[
1839                Point::new(2, 0)..Point::new(2, 0),
1840                Point::new(4, 0)..Point::new(4, 0),
1841            ],
1842        );
1843    });
1844}
1845
1846#[gpui::test]
1847async fn test_newline_above(cx: &mut gpui::TestAppContext) {
1848    init_test(cx, |settings| {
1849        settings.defaults.tab_size = NonZeroU32::new(4)
1850    });
1851
1852    let language = Arc::new(
1853        Language::new(
1854            LanguageConfig::default(),
1855            Some(tree_sitter_rust::language()),
1856        )
1857        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
1858        .unwrap(),
1859    );
1860
1861    let mut cx = EditorTestContext::new(cx).await;
1862    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1863    cx.set_state(indoc! {"
1864        const a: ˇA = (
18651866                «const_functionˇ»(ˇ),
1867                so«mˇ»et«hˇ»ing_ˇelse,ˇ
18681869        ˇ);ˇ
1870    "});
1871
1872    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
1873    cx.assert_editor_state(indoc! {"
1874        ˇ
1875        const a: A = (
1876            ˇ
1877            (
1878                ˇ
1879                ˇ
1880                const_function(),
1881                ˇ
1882                ˇ
1883                ˇ
1884                ˇ
1885                something_else,
1886                ˇ
1887            )
1888            ˇ
1889            ˇ
1890        );
1891    "});
1892}
1893
1894#[gpui::test]
1895async fn test_newline_below(cx: &mut gpui::TestAppContext) {
1896    init_test(cx, |settings| {
1897        settings.defaults.tab_size = NonZeroU32::new(4)
1898    });
1899
1900    let language = Arc::new(
1901        Language::new(
1902            LanguageConfig::default(),
1903            Some(tree_sitter_rust::language()),
1904        )
1905        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
1906        .unwrap(),
1907    );
1908
1909    let mut cx = EditorTestContext::new(cx).await;
1910    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1911    cx.set_state(indoc! {"
1912        const a: ˇA = (
19131914                «const_functionˇ»(ˇ),
1915                so«mˇ»et«hˇ»ing_ˇelse,ˇ
19161917        ˇ);ˇ
1918    "});
1919
1920    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
1921    cx.assert_editor_state(indoc! {"
1922        const a: A = (
1923            ˇ
1924            (
1925                ˇ
1926                const_function(),
1927                ˇ
1928                ˇ
1929                something_else,
1930                ˇ
1931                ˇ
1932                ˇ
1933                ˇ
1934            )
1935            ˇ
1936        );
1937        ˇ
1938        ˇ
1939    "});
1940}
1941
1942#[gpui::test]
1943async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
1944    init_test(cx, |settings| {
1945        settings.defaults.tab_size = NonZeroU32::new(4)
1946    });
1947
1948    let language = Arc::new(Language::new(
1949        LanguageConfig {
1950            line_comments: vec!["//".into()],
1951            ..LanguageConfig::default()
1952        },
1953        None,
1954    ));
1955    {
1956        let mut cx = EditorTestContext::new(cx).await;
1957        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1958        cx.set_state(indoc! {"
1959        // Fooˇ
1960    "});
1961
1962        cx.update_editor(|e, cx| e.newline(&Newline, cx));
1963        cx.assert_editor_state(indoc! {"
1964        // Foo
1965        //ˇ
1966    "});
1967        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
1968        cx.set_state(indoc! {"
1969        ˇ// Foo
1970    "});
1971        cx.update_editor(|e, cx| e.newline(&Newline, cx));
1972        cx.assert_editor_state(indoc! {"
1973
1974        ˇ// Foo
1975    "});
1976    }
1977    // Ensure that comment continuations can be disabled.
1978    update_test_language_settings(cx, |settings| {
1979        settings.defaults.extend_comment_on_newline = Some(false);
1980    });
1981    let mut cx = EditorTestContext::new(cx).await;
1982    cx.set_state(indoc! {"
1983        // Fooˇ
1984    "});
1985    cx.update_editor(|e, cx| e.newline(&Newline, cx));
1986    cx.assert_editor_state(indoc! {"
1987        // Foo
1988        ˇ
1989    "});
1990}
1991
1992#[gpui::test]
1993fn test_insert_with_old_selections(cx: &mut TestAppContext) {
1994    init_test(cx, |_| {});
1995
1996    let editor = cx.add_window(|cx| {
1997        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
1998        let mut editor = build_editor(buffer.clone(), cx);
1999        editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
2000        editor
2001    });
2002
2003    _ = editor.update(cx, |editor, cx| {
2004        // Edit the buffer directly, deleting ranges surrounding the editor's selections
2005        editor.buffer.update(cx, |buffer, cx| {
2006            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
2007            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
2008        });
2009        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
2010
2011        editor.insert("Z", cx);
2012        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
2013
2014        // The selections are moved after the inserted characters
2015        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
2016    });
2017}
2018
2019#[gpui::test]
2020async fn test_tab(cx: &mut gpui::TestAppContext) {
2021    init_test(cx, |settings| {
2022        settings.defaults.tab_size = NonZeroU32::new(3)
2023    });
2024
2025    let mut cx = EditorTestContext::new(cx).await;
2026    cx.set_state(indoc! {"
2027        ˇabˇc
2028        ˇ🏀ˇ🏀ˇefg
20292030    "});
2031    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2032    cx.assert_editor_state(indoc! {"
2033           ˇab ˇc
2034           ˇ🏀  ˇ🏀  ˇefg
2035        d  ˇ
2036    "});
2037
2038    cx.set_state(indoc! {"
2039        a
2040        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
2041    "});
2042    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2043    cx.assert_editor_state(indoc! {"
2044        a
2045           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
2046    "});
2047}
2048
2049#[gpui::test]
2050async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
2051    init_test(cx, |_| {});
2052
2053    let mut cx = EditorTestContext::new(cx).await;
2054    let language = Arc::new(
2055        Language::new(
2056            LanguageConfig::default(),
2057            Some(tree_sitter_rust::language()),
2058        )
2059        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
2060        .unwrap(),
2061    );
2062    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
2063
2064    // cursors that are already at the suggested indent level insert
2065    // a soft tab. cursors that are to the left of the suggested indent
2066    // auto-indent their line.
2067    cx.set_state(indoc! {"
2068        ˇ
2069        const a: B = (
2070            c(
2071                d(
2072        ˇ
2073                )
2074        ˇ
2075        ˇ    )
2076        );
2077    "});
2078    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2079    cx.assert_editor_state(indoc! {"
2080            ˇ
2081        const a: B = (
2082            c(
2083                d(
2084                    ˇ
2085                )
2086                ˇ
2087            ˇ)
2088        );
2089    "});
2090
2091    // handle auto-indent when there are multiple cursors on the same line
2092    cx.set_state(indoc! {"
2093        const a: B = (
2094            c(
2095        ˇ    ˇ
2096        ˇ    )
2097        );
2098    "});
2099    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2100    cx.assert_editor_state(indoc! {"
2101        const a: B = (
2102            c(
2103                ˇ
2104            ˇ)
2105        );
2106    "});
2107}
2108
2109#[gpui::test]
2110async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
2111    init_test(cx, |settings| {
2112        settings.defaults.tab_size = NonZeroU32::new(4)
2113    });
2114
2115    let language = Arc::new(
2116        Language::new(
2117            LanguageConfig::default(),
2118            Some(tree_sitter_rust::language()),
2119        )
2120        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
2121        .unwrap(),
2122    );
2123
2124    let mut cx = EditorTestContext::new(cx).await;
2125    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
2126    cx.set_state(indoc! {"
2127        fn a() {
2128            if b {
2129        \t ˇc
2130            }
2131        }
2132    "});
2133
2134    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2135    cx.assert_editor_state(indoc! {"
2136        fn a() {
2137            if b {
2138                ˇc
2139            }
2140        }
2141    "});
2142}
2143
2144#[gpui::test]
2145async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
2146    init_test(cx, |settings| {
2147        settings.defaults.tab_size = NonZeroU32::new(4);
2148    });
2149
2150    let mut cx = EditorTestContext::new(cx).await;
2151
2152    cx.set_state(indoc! {"
2153          «oneˇ» «twoˇ»
2154        three
2155         four
2156    "});
2157    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2158    cx.assert_editor_state(indoc! {"
2159            «oneˇ» «twoˇ»
2160        three
2161         four
2162    "});
2163
2164    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2165    cx.assert_editor_state(indoc! {"
2166        «oneˇ» «twoˇ»
2167        three
2168         four
2169    "});
2170
2171    // select across line ending
2172    cx.set_state(indoc! {"
2173        one two
2174        t«hree
2175        ˇ» four
2176    "});
2177    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2178    cx.assert_editor_state(indoc! {"
2179        one two
2180            t«hree
2181        ˇ» four
2182    "});
2183
2184    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2185    cx.assert_editor_state(indoc! {"
2186        one two
2187        t«hree
2188        ˇ» four
2189    "});
2190
2191    // Ensure that indenting/outdenting works when the cursor is at column 0.
2192    cx.set_state(indoc! {"
2193        one two
2194        ˇthree
2195            four
2196    "});
2197    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2198    cx.assert_editor_state(indoc! {"
2199        one two
2200            ˇthree
2201            four
2202    "});
2203
2204    cx.set_state(indoc! {"
2205        one two
2206        ˇ    three
2207            four
2208    "});
2209    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2210    cx.assert_editor_state(indoc! {"
2211        one two
2212        ˇthree
2213            four
2214    "});
2215}
2216
2217#[gpui::test]
2218async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
2219    init_test(cx, |settings| {
2220        settings.defaults.hard_tabs = Some(true);
2221    });
2222
2223    let mut cx = EditorTestContext::new(cx).await;
2224
2225    // select two ranges on one line
2226    cx.set_state(indoc! {"
2227        «oneˇ» «twoˇ»
2228        three
2229        four
2230    "});
2231    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2232    cx.assert_editor_state(indoc! {"
2233        \t«oneˇ» «twoˇ»
2234        three
2235        four
2236    "});
2237    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2238    cx.assert_editor_state(indoc! {"
2239        \t\t«oneˇ» «twoˇ»
2240        three
2241        four
2242    "});
2243    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2244    cx.assert_editor_state(indoc! {"
2245        \t«oneˇ» «twoˇ»
2246        three
2247        four
2248    "});
2249    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2250    cx.assert_editor_state(indoc! {"
2251        «oneˇ» «twoˇ»
2252        three
2253        four
2254    "});
2255
2256    // select across a line ending
2257    cx.set_state(indoc! {"
2258        one two
2259        t«hree
2260        ˇ»four
2261    "});
2262    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2263    cx.assert_editor_state(indoc! {"
2264        one two
2265        \tt«hree
2266        ˇ»four
2267    "});
2268    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2269    cx.assert_editor_state(indoc! {"
2270        one two
2271        \t\tt«hree
2272        ˇ»four
2273    "});
2274    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2275    cx.assert_editor_state(indoc! {"
2276        one two
2277        \tt«hree
2278        ˇ»four
2279    "});
2280    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2281    cx.assert_editor_state(indoc! {"
2282        one two
2283        t«hree
2284        ˇ»four
2285    "});
2286
2287    // Ensure that indenting/outdenting works when the cursor is at column 0.
2288    cx.set_state(indoc! {"
2289        one two
2290        ˇthree
2291        four
2292    "});
2293    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2294    cx.assert_editor_state(indoc! {"
2295        one two
2296        ˇthree
2297        four
2298    "});
2299    cx.update_editor(|e, cx| e.tab(&Tab, cx));
2300    cx.assert_editor_state(indoc! {"
2301        one two
2302        \tˇthree
2303        four
2304    "});
2305    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
2306    cx.assert_editor_state(indoc! {"
2307        one two
2308        ˇthree
2309        four
2310    "});
2311}
2312
2313#[gpui::test]
2314fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
2315    init_test(cx, |settings| {
2316        settings.languages.extend([
2317            (
2318                "TOML".into(),
2319                LanguageSettingsContent {
2320                    tab_size: NonZeroU32::new(2),
2321                    ..Default::default()
2322                },
2323            ),
2324            (
2325                "Rust".into(),
2326                LanguageSettingsContent {
2327                    tab_size: NonZeroU32::new(4),
2328                    ..Default::default()
2329                },
2330            ),
2331        ]);
2332    });
2333
2334    let toml_language = Arc::new(Language::new(
2335        LanguageConfig {
2336            name: "TOML".into(),
2337            ..Default::default()
2338        },
2339        None,
2340    ));
2341    let rust_language = Arc::new(Language::new(
2342        LanguageConfig {
2343            name: "Rust".into(),
2344            ..Default::default()
2345        },
2346        None,
2347    ));
2348
2349    let toml_buffer = cx.new_model(|cx| {
2350        Buffer::new(
2351            0,
2352            BufferId::new(cx.entity_id().as_u64()).unwrap(),
2353            "a = 1\nb = 2\n",
2354        )
2355        .with_language(toml_language, cx)
2356    });
2357    let rust_buffer = cx.new_model(|cx| {
2358        Buffer::new(
2359            0,
2360            BufferId::new(cx.entity_id().as_u64()).unwrap(),
2361            "const c: usize = 3;\n",
2362        )
2363        .with_language(rust_language, cx)
2364    });
2365    let multibuffer = cx.new_model(|cx| {
2366        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
2367        multibuffer.push_excerpts(
2368            toml_buffer.clone(),
2369            [ExcerptRange {
2370                context: Point::new(0, 0)..Point::new(2, 0),
2371                primary: None,
2372            }],
2373            cx,
2374        );
2375        multibuffer.push_excerpts(
2376            rust_buffer.clone(),
2377            [ExcerptRange {
2378                context: Point::new(0, 0)..Point::new(1, 0),
2379                primary: None,
2380            }],
2381            cx,
2382        );
2383        multibuffer
2384    });
2385
2386    cx.add_window(|cx| {
2387        let mut editor = build_editor(multibuffer, cx);
2388
2389        assert_eq!(
2390            editor.text(cx),
2391            indoc! {"
2392                a = 1
2393                b = 2
2394
2395                const c: usize = 3;
2396            "}
2397        );
2398
2399        select_ranges(
2400            &mut editor,
2401            indoc! {"
2402                «aˇ» = 1
2403                b = 2
2404
2405                «const c:ˇ» usize = 3;
2406            "},
2407            cx,
2408        );
2409
2410        editor.tab(&Tab, cx);
2411        assert_text_with_selections(
2412            &mut editor,
2413            indoc! {"
2414                  «aˇ» = 1
2415                b = 2
2416
2417                    «const c:ˇ» usize = 3;
2418            "},
2419            cx,
2420        );
2421        editor.tab_prev(&TabPrev, cx);
2422        assert_text_with_selections(
2423            &mut editor,
2424            indoc! {"
2425                «aˇ» = 1
2426                b = 2
2427
2428                «const c:ˇ» usize = 3;
2429            "},
2430            cx,
2431        );
2432
2433        editor
2434    });
2435}
2436
2437#[gpui::test]
2438async fn test_backspace(cx: &mut gpui::TestAppContext) {
2439    init_test(cx, |_| {});
2440
2441    let mut cx = EditorTestContext::new(cx).await;
2442
2443    // Basic backspace
2444    cx.set_state(indoc! {"
2445        onˇe two three
2446        fou«rˇ» five six
2447        seven «ˇeight nine
2448        »ten
2449    "});
2450    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2451    cx.assert_editor_state(indoc! {"
2452        oˇe two three
2453        fouˇ five six
2454        seven ˇten
2455    "});
2456
2457    // Test backspace inside and around indents
2458    cx.set_state(indoc! {"
2459        zero
2460            ˇone
2461                ˇtwo
2462            ˇ ˇ ˇ  three
2463        ˇ  ˇ  four
2464    "});
2465    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2466    cx.assert_editor_state(indoc! {"
2467        zero
2468        ˇone
2469            ˇtwo
2470        ˇ  threeˇ  four
2471    "});
2472
2473    // Test backspace with line_mode set to true
2474    cx.update_editor(|e, _| e.selections.line_mode = true);
2475    cx.set_state(indoc! {"
2476        The ˇquick ˇbrown
2477        fox jumps over
2478        the lazy dog
2479        ˇThe qu«ick bˇ»rown"});
2480    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2481    cx.assert_editor_state(indoc! {"
2482        ˇfox jumps over
2483        the lazy dogˇ"});
2484}
2485
2486#[gpui::test]
2487async fn test_delete(cx: &mut gpui::TestAppContext) {
2488    init_test(cx, |_| {});
2489
2490    let mut cx = EditorTestContext::new(cx).await;
2491    cx.set_state(indoc! {"
2492        onˇe two three
2493        fou«rˇ» five six
2494        seven «ˇeight nine
2495        »ten
2496    "});
2497    cx.update_editor(|e, cx| e.delete(&Delete, cx));
2498    cx.assert_editor_state(indoc! {"
2499        onˇ two three
2500        fouˇ five six
2501        seven ˇten
2502    "});
2503
2504    // Test backspace with line_mode set to true
2505    cx.update_editor(|e, _| e.selections.line_mode = true);
2506    cx.set_state(indoc! {"
2507        The ˇquick ˇbrown
2508        fox «ˇjum»ps over
2509        the lazy dog
2510        ˇThe qu«ick bˇ»rown"});
2511    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
2512    cx.assert_editor_state("ˇthe lazy dogˇ");
2513}
2514
2515#[gpui::test]
2516fn test_delete_line(cx: &mut TestAppContext) {
2517    init_test(cx, |_| {});
2518
2519    let view = cx.add_window(|cx| {
2520        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2521        build_editor(buffer, cx)
2522    });
2523    _ = view.update(cx, |view, cx| {
2524        view.change_selections(None, cx, |s| {
2525            s.select_display_ranges([
2526                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
2527                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
2528                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
2529            ])
2530        });
2531        view.delete_line(&DeleteLine, cx);
2532        assert_eq!(view.display_text(cx), "ghi");
2533        assert_eq!(
2534            view.selections.display_ranges(cx),
2535            vec![
2536                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
2537                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
2538            ]
2539        );
2540    });
2541
2542    let view = cx.add_window(|cx| {
2543        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
2544        build_editor(buffer, cx)
2545    });
2546    _ = view.update(cx, |view, cx| {
2547        view.change_selections(None, cx, |s| {
2548            s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
2549        });
2550        view.delete_line(&DeleteLine, cx);
2551        assert_eq!(view.display_text(cx), "ghi\n");
2552        assert_eq!(
2553            view.selections.display_ranges(cx),
2554            vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
2555        );
2556    });
2557}
2558
2559#[gpui::test]
2560fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
2561    init_test(cx, |_| {});
2562
2563    cx.add_window(|cx| {
2564        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
2565        let mut editor = build_editor(buffer.clone(), cx);
2566        let buffer = buffer.read(cx).as_singleton().unwrap();
2567
2568        assert_eq!(
2569            editor.selections.ranges::<Point>(cx),
2570            &[Point::new(0, 0)..Point::new(0, 0)]
2571        );
2572
2573        // When on single line, replace newline at end by space
2574        editor.join_lines(&JoinLines, cx);
2575        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
2576        assert_eq!(
2577            editor.selections.ranges::<Point>(cx),
2578            &[Point::new(0, 3)..Point::new(0, 3)]
2579        );
2580
2581        // When multiple lines are selected, remove newlines that are spanned by the selection
2582        editor.change_selections(None, cx, |s| {
2583            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
2584        });
2585        editor.join_lines(&JoinLines, cx);
2586        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
2587        assert_eq!(
2588            editor.selections.ranges::<Point>(cx),
2589            &[Point::new(0, 11)..Point::new(0, 11)]
2590        );
2591
2592        // Undo should be transactional
2593        editor.undo(&Undo, cx);
2594        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
2595        assert_eq!(
2596            editor.selections.ranges::<Point>(cx),
2597            &[Point::new(0, 5)..Point::new(2, 2)]
2598        );
2599
2600        // When joining an empty line don't insert a space
2601        editor.change_selections(None, cx, |s| {
2602            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
2603        });
2604        editor.join_lines(&JoinLines, cx);
2605        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
2606        assert_eq!(
2607            editor.selections.ranges::<Point>(cx),
2608            [Point::new(2, 3)..Point::new(2, 3)]
2609        );
2610
2611        // We can remove trailing newlines
2612        editor.join_lines(&JoinLines, cx);
2613        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
2614        assert_eq!(
2615            editor.selections.ranges::<Point>(cx),
2616            [Point::new(2, 3)..Point::new(2, 3)]
2617        );
2618
2619        // We don't blow up on the last line
2620        editor.join_lines(&JoinLines, cx);
2621        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
2622        assert_eq!(
2623            editor.selections.ranges::<Point>(cx),
2624            [Point::new(2, 3)..Point::new(2, 3)]
2625        );
2626
2627        // reset to test indentation
2628        editor.buffer.update(cx, |buffer, cx| {
2629            buffer.edit(
2630                [
2631                    (Point::new(1, 0)..Point::new(1, 2), "  "),
2632                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
2633                ],
2634                None,
2635                cx,
2636            )
2637        });
2638
2639        // We remove any leading spaces
2640        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
2641        editor.change_selections(None, cx, |s| {
2642            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
2643        });
2644        editor.join_lines(&JoinLines, cx);
2645        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
2646
2647        // We don't insert a space for a line containing only spaces
2648        editor.join_lines(&JoinLines, cx);
2649        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
2650
2651        // We ignore any leading tabs
2652        editor.join_lines(&JoinLines, cx);
2653        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
2654
2655        editor
2656    });
2657}
2658
2659#[gpui::test]
2660fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
2661    init_test(cx, |_| {});
2662
2663    cx.add_window(|cx| {
2664        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
2665        let mut editor = build_editor(buffer.clone(), cx);
2666        let buffer = buffer.read(cx).as_singleton().unwrap();
2667
2668        editor.change_selections(None, cx, |s| {
2669            s.select_ranges([
2670                Point::new(0, 2)..Point::new(1, 1),
2671                Point::new(1, 2)..Point::new(1, 2),
2672                Point::new(3, 1)..Point::new(3, 2),
2673            ])
2674        });
2675
2676        editor.join_lines(&JoinLines, cx);
2677        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
2678
2679        assert_eq!(
2680            editor.selections.ranges::<Point>(cx),
2681            [
2682                Point::new(0, 7)..Point::new(0, 7),
2683                Point::new(1, 3)..Point::new(1, 3)
2684            ]
2685        );
2686        editor
2687    });
2688}
2689
2690#[gpui::test]
2691async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
2692    init_test(cx, |_| {});
2693
2694    let mut cx = EditorTestContext::new(cx).await;
2695
2696    // Test sort_lines_case_insensitive()
2697    cx.set_state(indoc! {"
2698        «z
2699        y
2700        x
2701        Z
2702        Y
2703        Xˇ»
2704    "});
2705    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
2706    cx.assert_editor_state(indoc! {"
2707        «x
2708        X
2709        y
2710        Y
2711        z
2712        Zˇ»
2713    "});
2714
2715    // Test reverse_lines()
2716    cx.set_state(indoc! {"
2717        «5
2718        4
2719        3
2720        2
2721        1ˇ»
2722    "});
2723    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
2724    cx.assert_editor_state(indoc! {"
2725        «1
2726        2
2727        3
2728        4
2729        5ˇ»
2730    "});
2731
2732    // Skip testing shuffle_line()
2733
2734    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
2735    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
2736
2737    // Don't manipulate when cursor is on single line, but expand the selection
2738    cx.set_state(indoc! {"
2739        ddˇdd
2740        ccc
2741        bb
2742        a
2743    "});
2744    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
2745    cx.assert_editor_state(indoc! {"
2746        «ddddˇ»
2747        ccc
2748        bb
2749        a
2750    "});
2751
2752    // Basic manipulate case
2753    // Start selection moves to column 0
2754    // End of selection shrinks to fit shorter line
2755    cx.set_state(indoc! {"
2756        dd«d
2757        ccc
2758        bb
2759        aaaaaˇ»
2760    "});
2761    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
2762    cx.assert_editor_state(indoc! {"
2763        «aaaaa
2764        bb
2765        ccc
2766        dddˇ»
2767    "});
2768
2769    // Manipulate case with newlines
2770    cx.set_state(indoc! {"
2771        dd«d
2772        ccc
2773
2774        bb
2775        aaaaa
2776
2777        ˇ»
2778    "});
2779    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
2780    cx.assert_editor_state(indoc! {"
2781        «
2782
2783        aaaaa
2784        bb
2785        ccc
2786        dddˇ»
2787
2788    "});
2789
2790    // Adding new line
2791    cx.set_state(indoc! {"
2792        aa«a
2793        bbˇ»b
2794    "});
2795    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added_line")));
2796    cx.assert_editor_state(indoc! {"
2797        «aaa
2798        bbb
2799        added_lineˇ»
2800    "});
2801
2802    // Removing line
2803    cx.set_state(indoc! {"
2804        aa«a
2805        bbbˇ»
2806    "});
2807    cx.update_editor(|e, cx| {
2808        e.manipulate_lines(cx, |lines| {
2809            lines.pop();
2810        })
2811    });
2812    cx.assert_editor_state(indoc! {"
2813        «aaaˇ»
2814    "});
2815
2816    // Removing all lines
2817    cx.set_state(indoc! {"
2818        aa«a
2819        bbbˇ»
2820    "});
2821    cx.update_editor(|e, cx| {
2822        e.manipulate_lines(cx, |lines| {
2823            lines.drain(..);
2824        })
2825    });
2826    cx.assert_editor_state(indoc! {"
2827        ˇ
2828    "});
2829}
2830
2831#[gpui::test]
2832async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
2833    init_test(cx, |_| {});
2834
2835    let mut cx = EditorTestContext::new(cx).await;
2836
2837    // Consider continuous selection as single selection
2838    cx.set_state(indoc! {"
2839        Aaa«aa
2840        cˇ»c«c
2841        bb
2842        aaaˇ»aa
2843    "});
2844    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
2845    cx.assert_editor_state(indoc! {"
2846        «Aaaaa
2847        ccc
2848        bb
2849        aaaaaˇ»
2850    "});
2851
2852    cx.set_state(indoc! {"
2853        Aaa«aa
2854        cˇ»c«c
2855        bb
2856        aaaˇ»aa
2857    "});
2858    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
2859    cx.assert_editor_state(indoc! {"
2860        «Aaaaa
2861        ccc
2862        bbˇ»
2863    "});
2864
2865    // Consider non continuous selection as distinct dedup operations
2866    cx.set_state(indoc! {"
2867        «aaaaa
2868        bb
2869        aaaaa
2870        aaaaaˇ»
2871
2872        aaa«aaˇ»
2873    "});
2874    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
2875    cx.assert_editor_state(indoc! {"
2876        «aaaaa
2877        bbˇ»
2878
2879        «aaaaaˇ»
2880    "});
2881}
2882
2883#[gpui::test]
2884async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
2885    init_test(cx, |_| {});
2886
2887    let mut cx = EditorTestContext::new(cx).await;
2888
2889    cx.set_state(indoc! {"
2890        «Aaa
2891        aAa
2892        Aaaˇ»
2893    "});
2894    cx.update_editor(|e, cx| e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, cx));
2895    cx.assert_editor_state(indoc! {"
2896        «Aaa
2897        aAaˇ»
2898    "});
2899
2900    cx.set_state(indoc! {"
2901        «Aaa
2902        aAa
2903        aaAˇ»
2904    "});
2905    cx.update_editor(|e, cx| e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, cx));
2906    cx.assert_editor_state(indoc! {"
2907        «Aaaˇ»
2908    "});
2909}
2910
2911#[gpui::test]
2912async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
2913    init_test(cx, |_| {});
2914
2915    let mut cx = EditorTestContext::new(cx).await;
2916
2917    // Manipulate with multiple selections on a single line
2918    cx.set_state(indoc! {"
2919        dd«dd
2920        cˇ»c«c
2921        bb
2922        aaaˇ»aa
2923    "});
2924    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
2925    cx.assert_editor_state(indoc! {"
2926        «aaaaa
2927        bb
2928        ccc
2929        ddddˇ»
2930    "});
2931
2932    // Manipulate with multiple disjoin selections
2933    cx.set_state(indoc! {"
29342935        4
2936        3
2937        2
2938        1ˇ»
2939
2940        dd«dd
2941        ccc
2942        bb
2943        aaaˇ»aa
2944    "});
2945    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
2946    cx.assert_editor_state(indoc! {"
2947        «1
2948        2
2949        3
2950        4
2951        5ˇ»
2952
2953        «aaaaa
2954        bb
2955        ccc
2956        ddddˇ»
2957    "});
2958
2959    // Adding lines on each selection
2960    cx.set_state(indoc! {"
29612962        1ˇ»
2963
2964        bb«bb
2965        aaaˇ»aa
2966    "});
2967    cx.update_editor(|e, cx| e.manipulate_lines(cx, |lines| lines.push("added line")));
2968    cx.assert_editor_state(indoc! {"
2969        «2
2970        1
2971        added lineˇ»
2972
2973        «bbbb
2974        aaaaa
2975        added lineˇ»
2976    "});
2977
2978    // Removing lines on each selection
2979    cx.set_state(indoc! {"
29802981        1ˇ»
2982
2983        bb«bb
2984        aaaˇ»aa
2985    "});
2986    cx.update_editor(|e, cx| {
2987        e.manipulate_lines(cx, |lines| {
2988            lines.pop();
2989        })
2990    });
2991    cx.assert_editor_state(indoc! {"
2992        «2ˇ»
2993
2994        «bbbbˇ»
2995    "});
2996}
2997
2998#[gpui::test]
2999async fn test_manipulate_text(cx: &mut TestAppContext) {
3000    init_test(cx, |_| {});
3001
3002    let mut cx = EditorTestContext::new(cx).await;
3003
3004    // Test convert_to_upper_case()
3005    cx.set_state(indoc! {"
3006        «hello worldˇ»
3007    "});
3008    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
3009    cx.assert_editor_state(indoc! {"
3010        «HELLO WORLDˇ»
3011    "});
3012
3013    // Test convert_to_lower_case()
3014    cx.set_state(indoc! {"
3015        «HELLO WORLDˇ»
3016    "});
3017    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
3018    cx.assert_editor_state(indoc! {"
3019        «hello worldˇ»
3020    "});
3021
3022    // Test multiple line, single selection case
3023    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
3024    cx.set_state(indoc! {"
3025        «The quick brown
3026        fox jumps over
3027        the lazy dogˇ»
3028    "});
3029    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
3030    cx.assert_editor_state(indoc! {"
3031        «The Quick Brown
3032        Fox Jumps Over
3033        The Lazy Dogˇ»
3034    "});
3035
3036    // Test multiple line, single selection case
3037    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
3038    cx.set_state(indoc! {"
3039        «The quick brown
3040        fox jumps over
3041        the lazy dogˇ»
3042    "});
3043    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
3044    cx.assert_editor_state(indoc! {"
3045        «TheQuickBrown
3046        FoxJumpsOver
3047        TheLazyDogˇ»
3048    "});
3049
3050    // From here on out, test more complex cases of manipulate_text()
3051
3052    // Test no selection case - should affect words cursors are in
3053    // Cursor at beginning, middle, and end of word
3054    cx.set_state(indoc! {"
3055        ˇhello big beauˇtiful worldˇ
3056    "});
3057    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
3058    cx.assert_editor_state(indoc! {"
3059        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
3060    "});
3061
3062    // Test multiple selections on a single line and across multiple lines
3063    cx.set_state(indoc! {"
3064        «Theˇ» quick «brown
3065        foxˇ» jumps «overˇ»
3066        the «lazyˇ» dog
3067    "});
3068    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
3069    cx.assert_editor_state(indoc! {"
3070        «THEˇ» quick «BROWN
3071        FOXˇ» jumps «OVERˇ»
3072        the «LAZYˇ» dog
3073    "});
3074
3075    // Test case where text length grows
3076    cx.set_state(indoc! {"
3077        «tschüߡ»
3078    "});
3079    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
3080    cx.assert_editor_state(indoc! {"
3081        «TSCHÜSSˇ»
3082    "});
3083
3084    // Test to make sure we don't crash when text shrinks
3085    cx.set_state(indoc! {"
3086        aaa_bbbˇ
3087    "});
3088    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
3089    cx.assert_editor_state(indoc! {"
3090        «aaaBbbˇ»
3091    "});
3092
3093    // Test to make sure we all aware of the fact that each word can grow and shrink
3094    // Final selections should be aware of this fact
3095    cx.set_state(indoc! {"
3096        aaa_bˇbb bbˇb_ccc ˇccc_ddd
3097    "});
3098    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
3099    cx.assert_editor_state(indoc! {"
3100        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
3101    "});
3102}
3103
3104#[gpui::test]
3105fn test_duplicate_line(cx: &mut TestAppContext) {
3106    init_test(cx, |_| {});
3107
3108    let view = cx.add_window(|cx| {
3109        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
3110        build_editor(buffer, cx)
3111    });
3112    _ = view.update(cx, |view, cx| {
3113        view.change_selections(None, cx, |s| {
3114            s.select_display_ranges([
3115                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3116                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3117                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
3118                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
3119            ])
3120        });
3121        view.duplicate_line(&DuplicateLine::default(), cx);
3122        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
3123        assert_eq!(
3124            view.selections.display_ranges(cx),
3125            vec![
3126                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
3127                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
3128                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
3129                DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
3130            ]
3131        );
3132    });
3133
3134    let view = cx.add_window(|cx| {
3135        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
3136        build_editor(buffer, cx)
3137    });
3138    _ = view.update(cx, |view, cx| {
3139        view.change_selections(None, cx, |s| {
3140            s.select_display_ranges([
3141                DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
3142                DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
3143            ])
3144        });
3145        view.duplicate_line(&DuplicateLine::default(), cx);
3146        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
3147        assert_eq!(
3148            view.selections.display_ranges(cx),
3149            vec![
3150                DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
3151                DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
3152            ]
3153        );
3154    });
3155
3156    // With `move_upwards` the selections stay in place, except for
3157    // the lines inserted above them
3158    let view = cx.add_window(|cx| {
3159        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
3160        build_editor(buffer, cx)
3161    });
3162    _ = view.update(cx, |view, cx| {
3163        view.change_selections(None, cx, |s| {
3164            s.select_display_ranges([
3165                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3166                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3167                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
3168                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
3169            ])
3170        });
3171        view.duplicate_line(&DuplicateLine { move_upwards: true }, cx);
3172        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
3173        assert_eq!(
3174            view.selections.display_ranges(cx),
3175            vec![
3176                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3177                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3178                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
3179                DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
3180            ]
3181        );
3182    });
3183
3184    let view = cx.add_window(|cx| {
3185        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
3186        build_editor(buffer, cx)
3187    });
3188    _ = view.update(cx, |view, cx| {
3189        view.change_selections(None, cx, |s| {
3190            s.select_display_ranges([
3191                DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
3192                DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
3193            ])
3194        });
3195        view.duplicate_line(&DuplicateLine { move_upwards: true }, cx);
3196        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
3197        assert_eq!(
3198            view.selections.display_ranges(cx),
3199            vec![
3200                DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
3201                DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
3202            ]
3203        );
3204    });
3205}
3206
3207#[gpui::test]
3208fn test_move_line_up_down(cx: &mut TestAppContext) {
3209    init_test(cx, |_| {});
3210
3211    let view = cx.add_window(|cx| {
3212        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
3213        build_editor(buffer, cx)
3214    });
3215    _ = view.update(cx, |view, cx| {
3216        view.fold_ranges(
3217            vec![
3218                Point::new(0, 2)..Point::new(1, 2),
3219                Point::new(2, 3)..Point::new(4, 1),
3220                Point::new(7, 0)..Point::new(8, 4),
3221            ],
3222            true,
3223            cx,
3224        );
3225        view.change_selections(None, cx, |s| {
3226            s.select_display_ranges([
3227                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
3228                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
3229                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
3230                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
3231            ])
3232        });
3233        assert_eq!(
3234            view.display_text(cx),
3235            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
3236        );
3237
3238        view.move_line_up(&MoveLineUp, cx);
3239        assert_eq!(
3240            view.display_text(cx),
3241            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
3242        );
3243        assert_eq!(
3244            view.selections.display_ranges(cx),
3245            vec![
3246                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
3247                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
3248                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
3249                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
3250            ]
3251        );
3252    });
3253
3254    _ = view.update(cx, |view, cx| {
3255        view.move_line_down(&MoveLineDown, cx);
3256        assert_eq!(
3257            view.display_text(cx),
3258            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
3259        );
3260        assert_eq!(
3261            view.selections.display_ranges(cx),
3262            vec![
3263                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
3264                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
3265                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
3266                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
3267            ]
3268        );
3269    });
3270
3271    _ = view.update(cx, |view, cx| {
3272        view.move_line_down(&MoveLineDown, cx);
3273        assert_eq!(
3274            view.display_text(cx),
3275            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
3276        );
3277        assert_eq!(
3278            view.selections.display_ranges(cx),
3279            vec![
3280                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
3281                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
3282                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
3283                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
3284            ]
3285        );
3286    });
3287
3288    _ = view.update(cx, |view, cx| {
3289        view.move_line_up(&MoveLineUp, cx);
3290        assert_eq!(
3291            view.display_text(cx),
3292            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
3293        );
3294        assert_eq!(
3295            view.selections.display_ranges(cx),
3296            vec![
3297                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
3298                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
3299                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
3300                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
3301            ]
3302        );
3303    });
3304}
3305
3306#[gpui::test]
3307fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
3308    init_test(cx, |_| {});
3309
3310    let editor = cx.add_window(|cx| {
3311        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
3312        build_editor(buffer, cx)
3313    });
3314    _ = editor.update(cx, |editor, cx| {
3315        let snapshot = editor.buffer.read(cx).snapshot(cx);
3316        editor.insert_blocks(
3317            [BlockProperties {
3318                style: BlockStyle::Fixed,
3319                position: snapshot.anchor_after(Point::new(2, 0)),
3320                disposition: BlockDisposition::Below,
3321                height: 1,
3322                render: Arc::new(|_| div().into_any()),
3323            }],
3324            Some(Autoscroll::fit()),
3325            cx,
3326        );
3327        editor.change_selections(None, cx, |s| {
3328            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
3329        });
3330        editor.move_line_down(&MoveLineDown, cx);
3331    });
3332}
3333
3334#[gpui::test]
3335fn test_transpose(cx: &mut TestAppContext) {
3336    init_test(cx, |_| {});
3337
3338    _ = cx.add_window(|cx| {
3339        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
3340        editor.set_style(EditorStyle::default(), cx);
3341        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
3342        editor.transpose(&Default::default(), cx);
3343        assert_eq!(editor.text(cx), "bac");
3344        assert_eq!(editor.selections.ranges(cx), [2..2]);
3345
3346        editor.transpose(&Default::default(), cx);
3347        assert_eq!(editor.text(cx), "bca");
3348        assert_eq!(editor.selections.ranges(cx), [3..3]);
3349
3350        editor.transpose(&Default::default(), cx);
3351        assert_eq!(editor.text(cx), "bac");
3352        assert_eq!(editor.selections.ranges(cx), [3..3]);
3353
3354        editor
3355    });
3356
3357    _ = cx.add_window(|cx| {
3358        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
3359        editor.set_style(EditorStyle::default(), cx);
3360        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
3361        editor.transpose(&Default::default(), cx);
3362        assert_eq!(editor.text(cx), "acb\nde");
3363        assert_eq!(editor.selections.ranges(cx), [3..3]);
3364
3365        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
3366        editor.transpose(&Default::default(), cx);
3367        assert_eq!(editor.text(cx), "acbd\ne");
3368        assert_eq!(editor.selections.ranges(cx), [5..5]);
3369
3370        editor.transpose(&Default::default(), cx);
3371        assert_eq!(editor.text(cx), "acbde\n");
3372        assert_eq!(editor.selections.ranges(cx), [6..6]);
3373
3374        editor.transpose(&Default::default(), cx);
3375        assert_eq!(editor.text(cx), "acbd\ne");
3376        assert_eq!(editor.selections.ranges(cx), [6..6]);
3377
3378        editor
3379    });
3380
3381    _ = cx.add_window(|cx| {
3382        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
3383        editor.set_style(EditorStyle::default(), cx);
3384        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
3385        editor.transpose(&Default::default(), cx);
3386        assert_eq!(editor.text(cx), "bacd\ne");
3387        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
3388
3389        editor.transpose(&Default::default(), cx);
3390        assert_eq!(editor.text(cx), "bcade\n");
3391        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
3392
3393        editor.transpose(&Default::default(), cx);
3394        assert_eq!(editor.text(cx), "bcda\ne");
3395        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
3396
3397        editor.transpose(&Default::default(), cx);
3398        assert_eq!(editor.text(cx), "bcade\n");
3399        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
3400
3401        editor.transpose(&Default::default(), cx);
3402        assert_eq!(editor.text(cx), "bcaed\n");
3403        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
3404
3405        editor
3406    });
3407
3408    _ = cx.add_window(|cx| {
3409        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
3410        editor.set_style(EditorStyle::default(), cx);
3411        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
3412        editor.transpose(&Default::default(), cx);
3413        assert_eq!(editor.text(cx), "🏀🍐✋");
3414        assert_eq!(editor.selections.ranges(cx), [8..8]);
3415
3416        editor.transpose(&Default::default(), cx);
3417        assert_eq!(editor.text(cx), "🏀✋🍐");
3418        assert_eq!(editor.selections.ranges(cx), [11..11]);
3419
3420        editor.transpose(&Default::default(), cx);
3421        assert_eq!(editor.text(cx), "🏀🍐✋");
3422        assert_eq!(editor.selections.ranges(cx), [11..11]);
3423
3424        editor
3425    });
3426}
3427
3428#[gpui::test]
3429async fn test_clipboard(cx: &mut gpui::TestAppContext) {
3430    init_test(cx, |_| {});
3431
3432    let mut cx = EditorTestContext::new(cx).await;
3433
3434    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
3435    cx.update_editor(|e, cx| e.cut(&Cut, cx));
3436    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
3437
3438    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
3439    cx.set_state("two ˇfour ˇsix ˇ");
3440    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3441    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
3442
3443    // Paste again but with only two cursors. Since the number of cursors doesn't
3444    // match the number of slices in the clipboard, the entire clipboard text
3445    // is pasted at each cursor.
3446    cx.set_state("ˇtwo one✅ four three six five ˇ");
3447    cx.update_editor(|e, cx| {
3448        e.handle_input("( ", cx);
3449        e.paste(&Paste, cx);
3450        e.handle_input(") ", cx);
3451    });
3452    cx.assert_editor_state(
3453        &([
3454            "( one✅ ",
3455            "three ",
3456            "five ) ˇtwo one✅ four three six five ( one✅ ",
3457            "three ",
3458            "five ) ˇ",
3459        ]
3460        .join("\n")),
3461    );
3462
3463    // Cut with three selections, one of which is full-line.
3464    cx.set_state(indoc! {"
3465        1«2ˇ»3
3466        4ˇ567
3467        «8ˇ»9"});
3468    cx.update_editor(|e, cx| e.cut(&Cut, cx));
3469    cx.assert_editor_state(indoc! {"
3470        1ˇ3
3471        ˇ9"});
3472
3473    // Paste with three selections, noticing how the copied selection that was full-line
3474    // gets inserted before the second cursor.
3475    cx.set_state(indoc! {"
3476        1ˇ3
34773478        «oˇ»ne"});
3479    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3480    cx.assert_editor_state(indoc! {"
3481        12ˇ3
3482        4567
34833484        8ˇne"});
3485
3486    // Copy with a single cursor only, which writes the whole line into the clipboard.
3487    cx.set_state(indoc! {"
3488        The quick brown
3489        fox juˇmps over
3490        the lazy dog"});
3491    cx.update_editor(|e, cx| e.copy(&Copy, cx));
3492    assert_eq!(
3493        cx.read_from_clipboard().map(|item| item.text().to_owned()),
3494        Some("fox jumps over\n".to_owned())
3495    );
3496
3497    // Paste with three selections, noticing how the copied full-line selection is inserted
3498    // before the empty selections but replaces the selection that is non-empty.
3499    cx.set_state(indoc! {"
3500        Tˇhe quick brown
3501        «foˇ»x jumps over
3502        tˇhe lazy dog"});
3503    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3504    cx.assert_editor_state(indoc! {"
3505        fox jumps over
3506        Tˇhe quick brown
3507        fox jumps over
3508        ˇx jumps over
3509        fox jumps over
3510        tˇhe lazy dog"});
3511}
3512
3513#[gpui::test]
3514async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
3515    init_test(cx, |_| {});
3516
3517    let mut cx = EditorTestContext::new(cx).await;
3518    let language = Arc::new(Language::new(
3519        LanguageConfig::default(),
3520        Some(tree_sitter_rust::language()),
3521    ));
3522    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
3523
3524    // Cut an indented block, without the leading whitespace.
3525    cx.set_state(indoc! {"
3526        const a: B = (
3527            c(),
3528            «d(
3529                e,
3530                f
3531            )ˇ»
3532        );
3533    "});
3534    cx.update_editor(|e, cx| e.cut(&Cut, cx));
3535    cx.assert_editor_state(indoc! {"
3536        const a: B = (
3537            c(),
3538            ˇ
3539        );
3540    "});
3541
3542    // Paste it at the same position.
3543    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3544    cx.assert_editor_state(indoc! {"
3545        const a: B = (
3546            c(),
3547            d(
3548                e,
3549                f
35503551        );
3552    "});
3553
3554    // Paste it at a line with a lower indent level.
3555    cx.set_state(indoc! {"
3556        ˇ
3557        const a: B = (
3558            c(),
3559        );
3560    "});
3561    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3562    cx.assert_editor_state(indoc! {"
3563        d(
3564            e,
3565            f
35663567        const a: B = (
3568            c(),
3569        );
3570    "});
3571
3572    // Cut an indented block, with the leading whitespace.
3573    cx.set_state(indoc! {"
3574        const a: B = (
3575            c(),
3576        «    d(
3577                e,
3578                f
3579            )
3580        ˇ»);
3581    "});
3582    cx.update_editor(|e, cx| e.cut(&Cut, cx));
3583    cx.assert_editor_state(indoc! {"
3584        const a: B = (
3585            c(),
3586        ˇ);
3587    "});
3588
3589    // Paste it at the same position.
3590    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3591    cx.assert_editor_state(indoc! {"
3592        const a: B = (
3593            c(),
3594            d(
3595                e,
3596                f
3597            )
3598        ˇ);
3599    "});
3600
3601    // Paste it at a line with a higher indent level.
3602    cx.set_state(indoc! {"
3603        const a: B = (
3604            c(),
3605            d(
3606                e,
36073608            )
3609        );
3610    "});
3611    cx.update_editor(|e, cx| e.paste(&Paste, cx));
3612    cx.assert_editor_state(indoc! {"
3613        const a: B = (
3614            c(),
3615            d(
3616                e,
3617                f    d(
3618                    e,
3619                    f
3620                )
3621        ˇ
3622            )
3623        );
3624    "});
3625}
3626
3627#[gpui::test]
3628fn test_select_all(cx: &mut TestAppContext) {
3629    init_test(cx, |_| {});
3630
3631    let view = cx.add_window(|cx| {
3632        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
3633        build_editor(buffer, cx)
3634    });
3635    _ = view.update(cx, |view, cx| {
3636        view.select_all(&SelectAll, cx);
3637        assert_eq!(
3638            view.selections.display_ranges(cx),
3639            &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
3640        );
3641    });
3642}
3643
3644#[gpui::test]
3645fn test_select_line(cx: &mut TestAppContext) {
3646    init_test(cx, |_| {});
3647
3648    let view = cx.add_window(|cx| {
3649        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
3650        build_editor(buffer, cx)
3651    });
3652    _ = view.update(cx, |view, cx| {
3653        view.change_selections(None, cx, |s| {
3654            s.select_display_ranges([
3655                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3656                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3657                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
3658                DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
3659            ])
3660        });
3661        view.select_line(&SelectLine, cx);
3662        assert_eq!(
3663            view.selections.display_ranges(cx),
3664            vec![
3665                DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
3666                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
3667            ]
3668        );
3669    });
3670
3671    _ = view.update(cx, |view, cx| {
3672        view.select_line(&SelectLine, cx);
3673        assert_eq!(
3674            view.selections.display_ranges(cx),
3675            vec![
3676                DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
3677                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
3678            ]
3679        );
3680    });
3681
3682    _ = view.update(cx, |view, cx| {
3683        view.select_line(&SelectLine, cx);
3684        assert_eq!(
3685            view.selections.display_ranges(cx),
3686            vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
3687        );
3688    });
3689}
3690
3691#[gpui::test]
3692fn test_split_selection_into_lines(cx: &mut TestAppContext) {
3693    init_test(cx, |_| {});
3694
3695    let view = cx.add_window(|cx| {
3696        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
3697        build_editor(buffer, cx)
3698    });
3699    _ = view.update(cx, |view, cx| {
3700        view.fold_ranges(
3701            vec![
3702                Point::new(0, 2)..Point::new(1, 2),
3703                Point::new(2, 3)..Point::new(4, 1),
3704                Point::new(7, 0)..Point::new(8, 4),
3705            ],
3706            true,
3707            cx,
3708        );
3709        view.change_selections(None, cx, |s| {
3710            s.select_display_ranges([
3711                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
3712                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3713                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
3714                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
3715            ])
3716        });
3717        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
3718    });
3719
3720    _ = view.update(cx, |view, cx| {
3721        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
3722        assert_eq!(
3723            view.display_text(cx),
3724            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
3725        );
3726        assert_eq!(
3727            view.selections.display_ranges(cx),
3728            [
3729                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
3730                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
3731                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
3732                DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
3733            ]
3734        );
3735    });
3736
3737    _ = view.update(cx, |view, cx| {
3738        view.change_selections(None, cx, |s| {
3739            s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
3740        });
3741        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
3742        assert_eq!(
3743            view.display_text(cx),
3744            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
3745        );
3746        assert_eq!(
3747            view.selections.display_ranges(cx),
3748            [
3749                DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
3750                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
3751                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
3752                DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
3753                DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
3754                DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
3755                DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
3756                DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
3757            ]
3758        );
3759    });
3760}
3761
3762#[gpui::test]
3763async fn test_add_selection_above_below(cx: &mut TestAppContext) {
3764    init_test(cx, |_| {});
3765
3766    let mut cx = EditorTestContext::new(cx).await;
3767
3768    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
3769    cx.set_state(indoc!(
3770        r#"abc
3771           defˇghi
3772
3773           jk
3774           nlmo
3775           "#
3776    ));
3777
3778    cx.update_editor(|editor, cx| {
3779        editor.add_selection_above(&Default::default(), cx);
3780    });
3781
3782    cx.assert_editor_state(indoc!(
3783        r#"abcˇ
3784           defˇghi
3785
3786           jk
3787           nlmo
3788           "#
3789    ));
3790
3791    cx.update_editor(|editor, cx| {
3792        editor.add_selection_above(&Default::default(), cx);
3793    });
3794
3795    cx.assert_editor_state(indoc!(
3796        r#"abcˇ
3797            defˇghi
3798
3799            jk
3800            nlmo
3801            "#
3802    ));
3803
3804    cx.update_editor(|view, cx| {
3805        view.add_selection_below(&Default::default(), cx);
3806    });
3807
3808    cx.assert_editor_state(indoc!(
3809        r#"abc
3810           defˇghi
3811
3812           jk
3813           nlmo
3814           "#
3815    ));
3816
3817    cx.update_editor(|view, cx| {
3818        view.undo_selection(&Default::default(), cx);
3819    });
3820
3821    cx.assert_editor_state(indoc!(
3822        r#"abcˇ
3823           defˇghi
3824
3825           jk
3826           nlmo
3827           "#
3828    ));
3829
3830    cx.update_editor(|view, cx| {
3831        view.redo_selection(&Default::default(), cx);
3832    });
3833
3834    cx.assert_editor_state(indoc!(
3835        r#"abc
3836           defˇghi
3837
3838           jk
3839           nlmo
3840           "#
3841    ));
3842
3843    cx.update_editor(|view, cx| {
3844        view.add_selection_below(&Default::default(), cx);
3845    });
3846
3847    cx.assert_editor_state(indoc!(
3848        r#"abc
3849           defˇghi
3850
3851           jk
3852           nlmˇo
3853           "#
3854    ));
3855
3856    cx.update_editor(|view, cx| {
3857        view.add_selection_below(&Default::default(), cx);
3858    });
3859
3860    cx.assert_editor_state(indoc!(
3861        r#"abc
3862           defˇghi
3863
3864           jk
3865           nlmˇo
3866           "#
3867    ));
3868
3869    // change selections
3870    cx.set_state(indoc!(
3871        r#"abc
3872           def«ˇg»hi
3873
3874           jk
3875           nlmo
3876           "#
3877    ));
3878
3879    cx.update_editor(|view, cx| {
3880        view.add_selection_below(&Default::default(), cx);
3881    });
3882
3883    cx.assert_editor_state(indoc!(
3884        r#"abc
3885           def«ˇg»hi
3886
3887           jk
3888           nlm«ˇo»
3889           "#
3890    ));
3891
3892    cx.update_editor(|view, cx| {
3893        view.add_selection_below(&Default::default(), cx);
3894    });
3895
3896    cx.assert_editor_state(indoc!(
3897        r#"abc
3898           def«ˇg»hi
3899
3900           jk
3901           nlm«ˇo»
3902           "#
3903    ));
3904
3905    cx.update_editor(|view, cx| {
3906        view.add_selection_above(&Default::default(), cx);
3907    });
3908
3909    cx.assert_editor_state(indoc!(
3910        r#"abc
3911           def«ˇg»hi
3912
3913           jk
3914           nlmo
3915           "#
3916    ));
3917
3918    cx.update_editor(|view, cx| {
3919        view.add_selection_above(&Default::default(), cx);
3920    });
3921
3922    cx.assert_editor_state(indoc!(
3923        r#"abc
3924           def«ˇg»hi
3925
3926           jk
3927           nlmo
3928           "#
3929    ));
3930
3931    // Change selections again
3932    cx.set_state(indoc!(
3933        r#"a«bc
3934           defgˇ»hi
3935
3936           jk
3937           nlmo
3938           "#
3939    ));
3940
3941    cx.update_editor(|view, cx| {
3942        view.add_selection_below(&Default::default(), cx);
3943    });
3944
3945    cx.assert_editor_state(indoc!(
3946        r#"a«bcˇ»
3947           d«efgˇ»hi
3948
3949           j«kˇ»
3950           nlmo
3951           "#
3952    ));
3953
3954    cx.update_editor(|view, cx| {
3955        view.add_selection_below(&Default::default(), cx);
3956    });
3957    cx.assert_editor_state(indoc!(
3958        r#"a«bcˇ»
3959           d«efgˇ»hi
3960
3961           j«kˇ»
3962           n«lmoˇ»
3963           "#
3964    ));
3965    cx.update_editor(|view, cx| {
3966        view.add_selection_above(&Default::default(), cx);
3967    });
3968
3969    cx.assert_editor_state(indoc!(
3970        r#"a«bcˇ»
3971           d«efgˇ»hi
3972
3973           j«kˇ»
3974           nlmo
3975           "#
3976    ));
3977
3978    // Change selections again
3979    cx.set_state(indoc!(
3980        r#"abc
3981           d«ˇefghi
3982
3983           jk
3984           nlm»o
3985           "#
3986    ));
3987
3988    cx.update_editor(|view, cx| {
3989        view.add_selection_above(&Default::default(), cx);
3990    });
3991
3992    cx.assert_editor_state(indoc!(
3993        r#"a«ˇbc»
3994           d«ˇef»ghi
3995
3996           j«ˇk»
3997           n«ˇlm»o
3998           "#
3999    ));
4000
4001    cx.update_editor(|view, cx| {
4002        view.add_selection_below(&Default::default(), cx);
4003    });
4004
4005    cx.assert_editor_state(indoc!(
4006        r#"abc
4007           d«ˇef»ghi
4008
4009           j«ˇk»
4010           n«ˇlm»o
4011           "#
4012    ));
4013}
4014
4015#[gpui::test]
4016async fn test_select_next(cx: &mut gpui::TestAppContext) {
4017    init_test(cx, |_| {});
4018
4019    let mut cx = EditorTestContext::new(cx).await;
4020    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
4021
4022    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
4023        .unwrap();
4024    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
4025
4026    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
4027        .unwrap();
4028    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
4029
4030    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
4031    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
4032
4033    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
4034    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
4035
4036    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
4037        .unwrap();
4038    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
4039
4040    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
4041        .unwrap();
4042    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
4043}
4044
4045#[gpui::test]
4046async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
4047    init_test(cx, |_| {});
4048
4049    let mut cx = EditorTestContext::new(cx).await;
4050    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
4051
4052    cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx))
4053        .unwrap();
4054    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
4055}
4056
4057#[gpui::test]
4058async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
4059    init_test(cx, |_| {});
4060
4061    let mut cx = EditorTestContext::new(cx).await;
4062    cx.set_state(
4063        r#"let foo = 2;
4064lˇet foo = 2;
4065let fooˇ = 2;
4066let foo = 2;
4067let foo = ˇ2;"#,
4068    );
4069
4070    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
4071        .unwrap();
4072    cx.assert_editor_state(
4073        r#"let foo = 2;
4074«letˇ» foo = 2;
4075let «fooˇ» = 2;
4076let foo = 2;
4077let foo = «2ˇ»;"#,
4078    );
4079
4080    // noop for multiple selections with different contents
4081    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
4082        .unwrap();
4083    cx.assert_editor_state(
4084        r#"let foo = 2;
4085«letˇ» foo = 2;
4086let «fooˇ» = 2;
4087let foo = 2;
4088let foo = «2ˇ»;"#,
4089    );
4090}
4091
4092#[gpui::test]
4093async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
4094    init_test(cx, |_| {});
4095
4096    let mut cx = EditorTestContext::new(cx).await;
4097    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
4098
4099    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4100        .unwrap();
4101    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
4102
4103    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4104        .unwrap();
4105    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
4106
4107    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
4108    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
4109
4110    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
4111    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
4112
4113    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4114        .unwrap();
4115    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
4116
4117    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4118        .unwrap();
4119    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
4120
4121    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4122        .unwrap();
4123    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
4124}
4125
4126#[gpui::test]
4127async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
4128    init_test(cx, |_| {});
4129
4130    let mut cx = EditorTestContext::new(cx).await;
4131    cx.set_state(
4132        r#"let foo = 2;
4133lˇet foo = 2;
4134let fooˇ = 2;
4135let foo = 2;
4136let foo = ˇ2;"#,
4137    );
4138
4139    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4140        .unwrap();
4141    cx.assert_editor_state(
4142        r#"let foo = 2;
4143«letˇ» foo = 2;
4144let «fooˇ» = 2;
4145let foo = 2;
4146let foo = «2ˇ»;"#,
4147    );
4148
4149    // noop for multiple selections with different contents
4150    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4151        .unwrap();
4152    cx.assert_editor_state(
4153        r#"let foo = 2;
4154«letˇ» foo = 2;
4155let «fooˇ» = 2;
4156let foo = 2;
4157let foo = «2ˇ»;"#,
4158    );
4159}
4160
4161#[gpui::test]
4162async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
4163    init_test(cx, |_| {});
4164
4165    let mut cx = EditorTestContext::new(cx).await;
4166    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
4167
4168    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4169        .unwrap();
4170    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
4171
4172    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4173        .unwrap();
4174    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
4175
4176    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
4177    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
4178
4179    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
4180    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
4181
4182    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4183        .unwrap();
4184    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
4185
4186    cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
4187        .unwrap();
4188    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
4189}
4190
4191#[gpui::test]
4192async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
4193    init_test(cx, |_| {});
4194
4195    let language = Arc::new(Language::new(
4196        LanguageConfig::default(),
4197        Some(tree_sitter_rust::language()),
4198    ));
4199
4200    let text = r#"
4201        use mod1::mod2::{mod3, mod4};
4202
4203        fn fn_1(param1: bool, param2: &str) {
4204            let var1 = "text";
4205        }
4206    "#
4207    .unindent();
4208
4209    let buffer = cx.new_model(|cx| {
4210        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
4211            .with_language(language, cx)
4212    });
4213    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
4214    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
4215
4216    view.condition::<crate::EditorEvent>(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
4217        .await;
4218
4219    _ = view.update(cx, |view, cx| {
4220        view.change_selections(None, cx, |s| {
4221            s.select_display_ranges([
4222                DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
4223                DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
4224                DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
4225            ]);
4226        });
4227        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
4228    });
4229    assert_eq!(
4230        view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
4231        &[
4232            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
4233            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
4234            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
4235        ]
4236    );
4237
4238    _ = view.update(cx, |view, cx| {
4239        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
4240    });
4241    assert_eq!(
4242        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4243        &[
4244            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
4245            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
4246        ]
4247    );
4248
4249    _ = view.update(cx, |view, cx| {
4250        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
4251    });
4252    assert_eq!(
4253        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4254        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
4255    );
4256
4257    // Trying to expand the selected syntax node one more time has no effect.
4258    _ = view.update(cx, |view, cx| {
4259        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
4260    });
4261    assert_eq!(
4262        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4263        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
4264    );
4265
4266    _ = view.update(cx, |view, cx| {
4267        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
4268    });
4269    assert_eq!(
4270        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4271        &[
4272            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
4273            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
4274        ]
4275    );
4276
4277    _ = view.update(cx, |view, cx| {
4278        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
4279    });
4280    assert_eq!(
4281        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4282        &[
4283            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
4284            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
4285            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
4286        ]
4287    );
4288
4289    _ = view.update(cx, |view, cx| {
4290        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
4291    });
4292    assert_eq!(
4293        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4294        &[
4295            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
4296            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
4297            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
4298        ]
4299    );
4300
4301    // Trying to shrink the selected syntax node one more time has no effect.
4302    _ = view.update(cx, |view, cx| {
4303        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
4304    });
4305    assert_eq!(
4306        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4307        &[
4308            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
4309            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
4310            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
4311        ]
4312    );
4313
4314    // Ensure that we keep expanding the selection if the larger selection starts or ends within
4315    // a fold.
4316    _ = view.update(cx, |view, cx| {
4317        view.fold_ranges(
4318            vec![
4319                Point::new(0, 21)..Point::new(0, 24),
4320                Point::new(3, 20)..Point::new(3, 22),
4321            ],
4322            true,
4323            cx,
4324        );
4325        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
4326    });
4327    assert_eq!(
4328        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
4329        &[
4330            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
4331            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
4332            DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
4333        ]
4334    );
4335}
4336
4337#[gpui::test]
4338async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
4339    init_test(cx, |_| {});
4340
4341    let language = Arc::new(
4342        Language::new(
4343            LanguageConfig {
4344                brackets: BracketPairConfig {
4345                    pairs: vec![
4346                        BracketPair {
4347                            start: "{".to_string(),
4348                            end: "}".to_string(),
4349                            close: false,
4350                            newline: true,
4351                        },
4352                        BracketPair {
4353                            start: "(".to_string(),
4354                            end: ")".to_string(),
4355                            close: false,
4356                            newline: true,
4357                        },
4358                    ],
4359                    ..Default::default()
4360                },
4361                ..Default::default()
4362            },
4363            Some(tree_sitter_rust::language()),
4364        )
4365        .with_indents_query(
4366            r#"
4367                (_ "(" ")" @end) @indent
4368                (_ "{" "}" @end) @indent
4369            "#,
4370        )
4371        .unwrap(),
4372    );
4373
4374    let text = "fn a() {}";
4375
4376    let buffer = cx.new_model(|cx| {
4377        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
4378            .with_language(language, cx)
4379    });
4380    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
4381    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
4382    editor
4383        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
4384        .await;
4385
4386    _ = editor.update(cx, |editor, cx| {
4387        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
4388        editor.newline(&Newline, cx);
4389        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
4390        assert_eq!(
4391            editor.selections.ranges(cx),
4392            &[
4393                Point::new(1, 4)..Point::new(1, 4),
4394                Point::new(3, 4)..Point::new(3, 4),
4395                Point::new(5, 0)..Point::new(5, 0)
4396            ]
4397        );
4398    });
4399}
4400
4401#[gpui::test]
4402async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
4403    init_test(cx, |_| {});
4404
4405    let mut cx = EditorTestContext::new(cx).await;
4406
4407    let language = Arc::new(Language::new(
4408        LanguageConfig {
4409            brackets: BracketPairConfig {
4410                pairs: vec![
4411                    BracketPair {
4412                        start: "{".to_string(),
4413                        end: "}".to_string(),
4414                        close: true,
4415                        newline: true,
4416                    },
4417                    BracketPair {
4418                        start: "(".to_string(),
4419                        end: ")".to_string(),
4420                        close: true,
4421                        newline: true,
4422                    },
4423                    BracketPair {
4424                        start: "/*".to_string(),
4425                        end: " */".to_string(),
4426                        close: true,
4427                        newline: true,
4428                    },
4429                    BracketPair {
4430                        start: "[".to_string(),
4431                        end: "]".to_string(),
4432                        close: false,
4433                        newline: true,
4434                    },
4435                    BracketPair {
4436                        start: "\"".to_string(),
4437                        end: "\"".to_string(),
4438                        close: true,
4439                        newline: false,
4440                    },
4441                ],
4442                ..Default::default()
4443            },
4444            autoclose_before: "})]".to_string(),
4445            ..Default::default()
4446        },
4447        Some(tree_sitter_rust::language()),
4448    ));
4449
4450    let registry = Arc::new(LanguageRegistry::test());
4451    registry.add(language.clone());
4452    cx.update_buffer(|buffer, cx| {
4453        buffer.set_language_registry(registry);
4454        buffer.set_language(Some(language), cx);
4455    });
4456
4457    cx.set_state(
4458        &r#"
4459            🏀ˇ
4460            εˇ
4461            ❤️ˇ
4462        "#
4463        .unindent(),
4464    );
4465
4466    // autoclose multiple nested brackets at multiple cursors
4467    cx.update_editor(|view, cx| {
4468        view.handle_input("{", cx);
4469        view.handle_input("{", cx);
4470        view.handle_input("{", cx);
4471    });
4472    cx.assert_editor_state(
4473        &"
4474            🏀{{{ˇ}}}
4475            ε{{{ˇ}}}
4476            ❤️{{{ˇ}}}
4477        "
4478        .unindent(),
4479    );
4480
4481    // insert a different closing bracket
4482    cx.update_editor(|view, cx| {
4483        view.handle_input(")", cx);
4484    });
4485    cx.assert_editor_state(
4486        &"
4487            🏀{{{)ˇ}}}
4488            ε{{{)ˇ}}}
4489            ❤️{{{)ˇ}}}
4490        "
4491        .unindent(),
4492    );
4493
4494    // skip over the auto-closed brackets when typing a closing bracket
4495    cx.update_editor(|view, cx| {
4496        view.move_right(&MoveRight, cx);
4497        view.handle_input("}", cx);
4498        view.handle_input("}", cx);
4499        view.handle_input("}", cx);
4500    });
4501    cx.assert_editor_state(
4502        &"
4503            🏀{{{)}}}}ˇ
4504            ε{{{)}}}}ˇ
4505            ❤️{{{)}}}}ˇ
4506        "
4507        .unindent(),
4508    );
4509
4510    // autoclose multi-character pairs
4511    cx.set_state(
4512        &"
4513            ˇ
4514            ˇ
4515        "
4516        .unindent(),
4517    );
4518    cx.update_editor(|view, cx| {
4519        view.handle_input("/", cx);
4520        view.handle_input("*", cx);
4521    });
4522    cx.assert_editor_state(
4523        &"
4524            /*ˇ */
4525            /*ˇ */
4526        "
4527        .unindent(),
4528    );
4529
4530    // one cursor autocloses a multi-character pair, one cursor
4531    // does not autoclose.
4532    cx.set_state(
4533        &"
45344535            ˇ
4536        "
4537        .unindent(),
4538    );
4539    cx.update_editor(|view, cx| view.handle_input("*", cx));
4540    cx.assert_editor_state(
4541        &"
4542            /*ˇ */
45434544        "
4545        .unindent(),
4546    );
4547
4548    // Don't autoclose if the next character isn't whitespace and isn't
4549    // listed in the language's "autoclose_before" section.
4550    cx.set_state("ˇa b");
4551    cx.update_editor(|view, cx| view.handle_input("{", cx));
4552    cx.assert_editor_state("{ˇa b");
4553
4554    // Don't autoclose if `close` is false for the bracket pair
4555    cx.set_state("ˇ");
4556    cx.update_editor(|view, cx| view.handle_input("[", cx));
4557    cx.assert_editor_state("");
4558
4559    // Surround with brackets if text is selected
4560    cx.set_state("«aˇ» b");
4561    cx.update_editor(|view, cx| view.handle_input("{", cx));
4562    cx.assert_editor_state("{«aˇ»} b");
4563
4564    // Autclose pair where the start and end characters are the same
4565    cx.set_state("");
4566    cx.update_editor(|view, cx| view.handle_input("\"", cx));
4567    cx.assert_editor_state("a\"ˇ\"");
4568    cx.update_editor(|view, cx| view.handle_input("\"", cx));
4569    cx.assert_editor_state("a\"\"ˇ");
4570}
4571
4572#[gpui::test]
4573async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
4574    init_test(cx, |_| {});
4575
4576    let mut cx = EditorTestContext::new(cx).await;
4577
4578    let html_language = Arc::new(
4579        Language::new(
4580            LanguageConfig {
4581                name: "HTML".into(),
4582                brackets: BracketPairConfig {
4583                    pairs: vec![
4584                        BracketPair {
4585                            start: "<".into(),
4586                            end: ">".into(),
4587                            close: true,
4588                            ..Default::default()
4589                        },
4590                        BracketPair {
4591                            start: "{".into(),
4592                            end: "}".into(),
4593                            close: true,
4594                            ..Default::default()
4595                        },
4596                        BracketPair {
4597                            start: "(".into(),
4598                            end: ")".into(),
4599                            close: true,
4600                            ..Default::default()
4601                        },
4602                    ],
4603                    ..Default::default()
4604                },
4605                autoclose_before: "})]>".into(),
4606                ..Default::default()
4607            },
4608            Some(tree_sitter_html::language()),
4609        )
4610        .with_injection_query(
4611            r#"
4612            (script_element
4613                (raw_text) @content
4614                (#set! "language" "javascript"))
4615            "#,
4616        )
4617        .unwrap(),
4618    );
4619
4620    let javascript_language = Arc::new(Language::new(
4621        LanguageConfig {
4622            name: "JavaScript".into(),
4623            brackets: BracketPairConfig {
4624                pairs: vec![
4625                    BracketPair {
4626                        start: "/*".into(),
4627                        end: " */".into(),
4628                        close: true,
4629                        ..Default::default()
4630                    },
4631                    BracketPair {
4632                        start: "{".into(),
4633                        end: "}".into(),
4634                        close: true,
4635                        ..Default::default()
4636                    },
4637                    BracketPair {
4638                        start: "(".into(),
4639                        end: ")".into(),
4640                        close: true,
4641                        ..Default::default()
4642                    },
4643                ],
4644                ..Default::default()
4645            },
4646            autoclose_before: "})]>".into(),
4647            ..Default::default()
4648        },
4649        Some(tree_sitter_typescript::language_tsx()),
4650    ));
4651
4652    let registry = Arc::new(LanguageRegistry::test());
4653    registry.add(html_language.clone());
4654    registry.add(javascript_language.clone());
4655
4656    cx.update_buffer(|buffer, cx| {
4657        buffer.set_language_registry(registry);
4658        buffer.set_language(Some(html_language), cx);
4659    });
4660
4661    cx.set_state(
4662        &r#"
4663            <body>ˇ
4664                <script>
4665                    var x = 1;ˇ
4666                </script>
4667            </body>ˇ
4668        "#
4669        .unindent(),
4670    );
4671
4672    // Precondition: different languages are active at different locations.
4673    cx.update_editor(|editor, cx| {
4674        let snapshot = editor.snapshot(cx);
4675        let cursors = editor.selections.ranges::<usize>(cx);
4676        let languages = cursors
4677            .iter()
4678            .map(|c| snapshot.language_at(c.start).unwrap().name())
4679            .collect::<Vec<_>>();
4680        assert_eq!(
4681            languages,
4682            &["HTML".into(), "JavaScript".into(), "HTML".into()]
4683        );
4684    });
4685
4686    // Angle brackets autoclose in HTML, but not JavaScript.
4687    cx.update_editor(|editor, cx| {
4688        editor.handle_input("<", cx);
4689        editor.handle_input("a", cx);
4690    });
4691    cx.assert_editor_state(
4692        &r#"
4693            <body><aˇ>
4694                <script>
4695                    var x = 1;<aˇ
4696                </script>
4697            </body><aˇ>
4698        "#
4699        .unindent(),
4700    );
4701
4702    // Curly braces and parens autoclose in both HTML and JavaScript.
4703    cx.update_editor(|editor, cx| {
4704        editor.handle_input(" b=", cx);
4705        editor.handle_input("{", cx);
4706        editor.handle_input("c", cx);
4707        editor.handle_input("(", cx);
4708    });
4709    cx.assert_editor_state(
4710        &r#"
4711            <body><a b={c(ˇ)}>
4712                <script>
4713                    var x = 1;<a b={c(ˇ)}
4714                </script>
4715            </body><a b={c(ˇ)}>
4716        "#
4717        .unindent(),
4718    );
4719
4720    // Brackets that were already autoclosed are skipped.
4721    cx.update_editor(|editor, cx| {
4722        editor.handle_input(")", cx);
4723        editor.handle_input("d", cx);
4724        editor.handle_input("}", cx);
4725    });
4726    cx.assert_editor_state(
4727        &r#"
4728            <body><a b={c()d}ˇ>
4729                <script>
4730                    var x = 1;<a b={c()d}ˇ
4731                </script>
4732            </body><a b={c()d}ˇ>
4733        "#
4734        .unindent(),
4735    );
4736    cx.update_editor(|editor, cx| {
4737        editor.handle_input(">", cx);
4738    });
4739    cx.assert_editor_state(
4740        &r#"
4741            <body><a b={c()d}>ˇ
4742                <script>
4743                    var x = 1;<a b={c()d}>ˇ
4744                </script>
4745            </body><a b={c()d}>ˇ
4746        "#
4747        .unindent(),
4748    );
4749
4750    // Reset
4751    cx.set_state(
4752        &r#"
4753            <body>ˇ
4754                <script>
4755                    var x = 1;ˇ
4756                </script>
4757            </body>ˇ
4758        "#
4759        .unindent(),
4760    );
4761
4762    cx.update_editor(|editor, cx| {
4763        editor.handle_input("<", cx);
4764    });
4765    cx.assert_editor_state(
4766        &r#"
4767            <body><ˇ>
4768                <script>
4769                    var x = 1;<ˇ
4770                </script>
4771            </body><ˇ>
4772        "#
4773        .unindent(),
4774    );
4775
4776    // When backspacing, the closing angle brackets are removed.
4777    cx.update_editor(|editor, cx| {
4778        editor.backspace(&Backspace, cx);
4779    });
4780    cx.assert_editor_state(
4781        &r#"
4782            <body>ˇ
4783                <script>
4784                    var x = 1;ˇ
4785                </script>
4786            </body>ˇ
4787        "#
4788        .unindent(),
4789    );
4790
4791    // Block comments autoclose in JavaScript, but not HTML.
4792    cx.update_editor(|editor, cx| {
4793        editor.handle_input("/", cx);
4794        editor.handle_input("*", cx);
4795    });
4796    cx.assert_editor_state(
4797        &r#"
4798            <body>/*ˇ
4799                <script>
4800                    var x = 1;/*ˇ */
4801                </script>
4802            </body>/*ˇ
4803        "#
4804        .unindent(),
4805    );
4806}
4807
4808#[gpui::test]
4809async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
4810    init_test(cx, |_| {});
4811
4812    let mut cx = EditorTestContext::new(cx).await;
4813
4814    let rust_language = Arc::new(
4815        Language::new(
4816            LanguageConfig {
4817                name: "Rust".into(),
4818                brackets: serde_json::from_value(json!([
4819                    { "start": "{", "end": "}", "close": true, "newline": true },
4820                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
4821                ]))
4822                .unwrap(),
4823                autoclose_before: "})]>".into(),
4824                ..Default::default()
4825            },
4826            Some(tree_sitter_rust::language()),
4827        )
4828        .with_override_query("(string_literal) @string")
4829        .unwrap(),
4830    );
4831
4832    let registry = Arc::new(LanguageRegistry::test());
4833    registry.add(rust_language.clone());
4834
4835    cx.update_buffer(|buffer, cx| {
4836        buffer.set_language_registry(registry);
4837        buffer.set_language(Some(rust_language), cx);
4838    });
4839
4840    cx.set_state(
4841        &r#"
4842            let x = ˇ
4843        "#
4844        .unindent(),
4845    );
4846
4847    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
4848    cx.update_editor(|editor, cx| {
4849        editor.handle_input("\"", cx);
4850    });
4851    cx.assert_editor_state(
4852        &r#"
4853            let x = "ˇ"
4854        "#
4855        .unindent(),
4856    );
4857
4858    // Inserting another quotation mark. The cursor moves across the existing
4859    // automatically-inserted quotation mark.
4860    cx.update_editor(|editor, cx| {
4861        editor.handle_input("\"", cx);
4862    });
4863    cx.assert_editor_state(
4864        &r#"
4865            let x = ""ˇ
4866        "#
4867        .unindent(),
4868    );
4869
4870    // Reset
4871    cx.set_state(
4872        &r#"
4873            let x = ˇ
4874        "#
4875        .unindent(),
4876    );
4877
4878    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
4879    cx.update_editor(|editor, cx| {
4880        editor.handle_input("\"", cx);
4881        editor.handle_input(" ", cx);
4882        editor.move_left(&Default::default(), cx);
4883        editor.handle_input("\\", cx);
4884        editor.handle_input("\"", cx);
4885    });
4886    cx.assert_editor_state(
4887        &r#"
4888            let x = "\"ˇ "
4889        "#
4890        .unindent(),
4891    );
4892
4893    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
4894    // mark. Nothing is inserted.
4895    cx.update_editor(|editor, cx| {
4896        editor.move_right(&Default::default(), cx);
4897        editor.handle_input("\"", cx);
4898    });
4899    cx.assert_editor_state(
4900        &r#"
4901            let x = "\" "ˇ
4902        "#
4903        .unindent(),
4904    );
4905}
4906
4907#[gpui::test]
4908async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
4909    init_test(cx, |_| {});
4910
4911    let language = Arc::new(Language::new(
4912        LanguageConfig {
4913            brackets: BracketPairConfig {
4914                pairs: vec![
4915                    BracketPair {
4916                        start: "{".to_string(),
4917                        end: "}".to_string(),
4918                        close: true,
4919                        newline: true,
4920                    },
4921                    BracketPair {
4922                        start: "/* ".to_string(),
4923                        end: "*/".to_string(),
4924                        close: true,
4925                        ..Default::default()
4926                    },
4927                ],
4928                ..Default::default()
4929            },
4930            ..Default::default()
4931        },
4932        Some(tree_sitter_rust::language()),
4933    ));
4934
4935    let text = r#"
4936        a
4937        b
4938        c
4939    "#
4940    .unindent();
4941
4942    let buffer = cx.new_model(|cx| {
4943        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
4944            .with_language(language, cx)
4945    });
4946    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
4947    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
4948    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
4949        .await;
4950
4951    _ = view.update(cx, |view, cx| {
4952        view.change_selections(None, cx, |s| {
4953            s.select_display_ranges([
4954                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
4955                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
4956                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
4957            ])
4958        });
4959
4960        view.handle_input("{", cx);
4961        view.handle_input("{", cx);
4962        view.handle_input("{", cx);
4963        assert_eq!(
4964            view.text(cx),
4965            "
4966                {{{a}}}
4967                {{{b}}}
4968                {{{c}}}
4969            "
4970            .unindent()
4971        );
4972        assert_eq!(
4973            view.selections.display_ranges(cx),
4974            [
4975                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
4976                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
4977                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
4978            ]
4979        );
4980
4981        view.undo(&Undo, cx);
4982        view.undo(&Undo, cx);
4983        view.undo(&Undo, cx);
4984        assert_eq!(
4985            view.text(cx),
4986            "
4987                a
4988                b
4989                c
4990            "
4991            .unindent()
4992        );
4993        assert_eq!(
4994            view.selections.display_ranges(cx),
4995            [
4996                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
4997                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
4998                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
4999            ]
5000        );
5001
5002        // Ensure inserting the first character of a multi-byte bracket pair
5003        // doesn't surround the selections with the bracket.
5004        view.handle_input("/", cx);
5005        assert_eq!(
5006            view.text(cx),
5007            "
5008                /
5009                /
5010                /
5011            "
5012            .unindent()
5013        );
5014        assert_eq!(
5015            view.selections.display_ranges(cx),
5016            [
5017                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
5018                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
5019                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
5020            ]
5021        );
5022
5023        view.undo(&Undo, cx);
5024        assert_eq!(
5025            view.text(cx),
5026            "
5027                a
5028                b
5029                c
5030            "
5031            .unindent()
5032        );
5033        assert_eq!(
5034            view.selections.display_ranges(cx),
5035            [
5036                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
5037                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
5038                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
5039            ]
5040        );
5041
5042        // Ensure inserting the last character of a multi-byte bracket pair
5043        // doesn't surround the selections with the bracket.
5044        view.handle_input("*", cx);
5045        assert_eq!(
5046            view.text(cx),
5047            "
5048                *
5049                *
5050                *
5051            "
5052            .unindent()
5053        );
5054        assert_eq!(
5055            view.selections.display_ranges(cx),
5056            [
5057                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
5058                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
5059                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
5060            ]
5061        );
5062    });
5063}
5064
5065#[gpui::test]
5066async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
5067    init_test(cx, |_| {});
5068
5069    let language = Arc::new(Language::new(
5070        LanguageConfig {
5071            brackets: BracketPairConfig {
5072                pairs: vec![BracketPair {
5073                    start: "{".to_string(),
5074                    end: "}".to_string(),
5075                    close: true,
5076                    newline: true,
5077                }],
5078                ..Default::default()
5079            },
5080            autoclose_before: "}".to_string(),
5081            ..Default::default()
5082        },
5083        Some(tree_sitter_rust::language()),
5084    ));
5085
5086    let text = r#"
5087        a
5088        b
5089        c
5090    "#
5091    .unindent();
5092
5093    let buffer = cx.new_model(|cx| {
5094        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
5095            .with_language(language, cx)
5096    });
5097    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
5098    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5099    editor
5100        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
5101        .await;
5102
5103    _ = editor.update(cx, |editor, cx| {
5104        editor.change_selections(None, cx, |s| {
5105            s.select_ranges([
5106                Point::new(0, 1)..Point::new(0, 1),
5107                Point::new(1, 1)..Point::new(1, 1),
5108                Point::new(2, 1)..Point::new(2, 1),
5109            ])
5110        });
5111
5112        editor.handle_input("{", cx);
5113        editor.handle_input("{", cx);
5114        editor.handle_input("_", cx);
5115        assert_eq!(
5116            editor.text(cx),
5117            "
5118                a{{_}}
5119                b{{_}}
5120                c{{_}}
5121            "
5122            .unindent()
5123        );
5124        assert_eq!(
5125            editor.selections.ranges::<Point>(cx),
5126            [
5127                Point::new(0, 4)..Point::new(0, 4),
5128                Point::new(1, 4)..Point::new(1, 4),
5129                Point::new(2, 4)..Point::new(2, 4)
5130            ]
5131        );
5132
5133        editor.backspace(&Default::default(), cx);
5134        editor.backspace(&Default::default(), cx);
5135        assert_eq!(
5136            editor.text(cx),
5137            "
5138                a{}
5139                b{}
5140                c{}
5141            "
5142            .unindent()
5143        );
5144        assert_eq!(
5145            editor.selections.ranges::<Point>(cx),
5146            [
5147                Point::new(0, 2)..Point::new(0, 2),
5148                Point::new(1, 2)..Point::new(1, 2),
5149                Point::new(2, 2)..Point::new(2, 2)
5150            ]
5151        );
5152
5153        editor.delete_to_previous_word_start(&Default::default(), cx);
5154        assert_eq!(
5155            editor.text(cx),
5156            "
5157                a
5158                b
5159                c
5160            "
5161            .unindent()
5162        );
5163        assert_eq!(
5164            editor.selections.ranges::<Point>(cx),
5165            [
5166                Point::new(0, 1)..Point::new(0, 1),
5167                Point::new(1, 1)..Point::new(1, 1),
5168                Point::new(2, 1)..Point::new(2, 1)
5169            ]
5170        );
5171    });
5172}
5173
5174#[gpui::test]
5175async fn test_snippets(cx: &mut gpui::TestAppContext) {
5176    init_test(cx, |_| {});
5177
5178    let (text, insertion_ranges) = marked_text_ranges(
5179        indoc! {"
5180            a.ˇ b
5181            a.ˇ b
5182            a.ˇ b
5183        "},
5184        false,
5185    );
5186
5187    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
5188    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5189
5190    _ = editor.update(cx, |editor, cx| {
5191        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
5192
5193        editor
5194            .insert_snippet(&insertion_ranges, snippet, cx)
5195            .unwrap();
5196
5197        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
5198            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
5199            assert_eq!(editor.text(cx), expected_text);
5200            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
5201        }
5202
5203        assert(
5204            editor,
5205            cx,
5206            indoc! {"
5207                a.f(«one», two, «three») b
5208                a.f(«one», two, «three») b
5209                a.f(«one», two, «three») b
5210            "},
5211        );
5212
5213        // Can't move earlier than the first tab stop
5214        assert!(!editor.move_to_prev_snippet_tabstop(cx));
5215        assert(
5216            editor,
5217            cx,
5218            indoc! {"
5219                a.f(«one», two, «three») b
5220                a.f(«one», two, «three») b
5221                a.f(«one», two, «three») b
5222            "},
5223        );
5224
5225        assert!(editor.move_to_next_snippet_tabstop(cx));
5226        assert(
5227            editor,
5228            cx,
5229            indoc! {"
5230                a.f(one, «two», three) b
5231                a.f(one, «two», three) b
5232                a.f(one, «two», three) b
5233            "},
5234        );
5235
5236        editor.move_to_prev_snippet_tabstop(cx);
5237        assert(
5238            editor,
5239            cx,
5240            indoc! {"
5241                a.f(«one», two, «three») b
5242                a.f(«one», two, «three») b
5243                a.f(«one», two, «three») b
5244            "},
5245        );
5246
5247        assert!(editor.move_to_next_snippet_tabstop(cx));
5248        assert(
5249            editor,
5250            cx,
5251            indoc! {"
5252                a.f(one, «two», three) b
5253                a.f(one, «two», three) b
5254                a.f(one, «two», three) b
5255            "},
5256        );
5257        assert!(editor.move_to_next_snippet_tabstop(cx));
5258        assert(
5259            editor,
5260            cx,
5261            indoc! {"
5262                a.f(one, two, three)ˇ b
5263                a.f(one, two, three)ˇ b
5264                a.f(one, two, three)ˇ b
5265            "},
5266        );
5267
5268        // As soon as the last tab stop is reached, snippet state is gone
5269        editor.move_to_prev_snippet_tabstop(cx);
5270        assert(
5271            editor,
5272            cx,
5273            indoc! {"
5274                a.f(one, two, three)ˇ b
5275                a.f(one, two, three)ˇ b
5276                a.f(one, two, three)ˇ b
5277            "},
5278        );
5279    });
5280}
5281
5282#[gpui::test]
5283async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
5284    init_test(cx, |_| {});
5285
5286    let fs = FakeFs::new(cx.executor());
5287    fs.insert_file("/file.rs", Default::default()).await;
5288
5289    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5290
5291    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
5292    language_registry.add(rust_lang());
5293    let mut fake_servers = language_registry.register_fake_lsp_adapter(
5294        "Rust",
5295        FakeLspAdapter {
5296            capabilities: lsp::ServerCapabilities {
5297                document_formatting_provider: Some(lsp::OneOf::Left(true)),
5298                ..Default::default()
5299            },
5300            ..Default::default()
5301        },
5302    );
5303
5304    let buffer = project
5305        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
5306        .await
5307        .unwrap();
5308
5309    cx.executor().start_waiting();
5310    let fake_server = fake_servers.next().await.unwrap();
5311
5312    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
5313    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5314    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5315    assert!(cx.read(|cx| editor.is_dirty(cx)));
5316
5317    let save = editor
5318        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5319        .unwrap();
5320    fake_server
5321        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5322            assert_eq!(
5323                params.text_document.uri,
5324                lsp::Url::from_file_path("/file.rs").unwrap()
5325            );
5326            assert_eq!(params.options.tab_size, 4);
5327            Ok(Some(vec![lsp::TextEdit::new(
5328                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
5329                ", ".to_string(),
5330            )]))
5331        })
5332        .next()
5333        .await;
5334    cx.executor().start_waiting();
5335    let _x = save.await;
5336
5337    assert_eq!(
5338        editor.update(cx, |editor, cx| editor.text(cx)),
5339        "one, two\nthree\n"
5340    );
5341    assert!(!cx.read(|cx| editor.is_dirty(cx)));
5342
5343    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5344    assert!(cx.read(|cx| editor.is_dirty(cx)));
5345
5346    // Ensure we can still save even if formatting hangs.
5347    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5348        assert_eq!(
5349            params.text_document.uri,
5350            lsp::Url::from_file_path("/file.rs").unwrap()
5351        );
5352        futures::future::pending::<()>().await;
5353        unreachable!()
5354    });
5355    let save = editor
5356        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5357        .unwrap();
5358    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
5359    cx.executor().start_waiting();
5360    save.await;
5361    assert_eq!(
5362        editor.update(cx, |editor, cx| editor.text(cx)),
5363        "one\ntwo\nthree\n"
5364    );
5365    assert!(!cx.read(|cx| editor.is_dirty(cx)));
5366
5367    // Set rust language override and assert overridden tabsize is sent to language server
5368    update_test_language_settings(cx, |settings| {
5369        settings.languages.insert(
5370            "Rust".into(),
5371            LanguageSettingsContent {
5372                tab_size: NonZeroU32::new(8),
5373                ..Default::default()
5374            },
5375        );
5376    });
5377
5378    let save = editor
5379        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5380        .unwrap();
5381    fake_server
5382        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5383            assert_eq!(
5384                params.text_document.uri,
5385                lsp::Url::from_file_path("/file.rs").unwrap()
5386            );
5387            assert_eq!(params.options.tab_size, 8);
5388            Ok(Some(vec![]))
5389        })
5390        .next()
5391        .await;
5392    cx.executor().start_waiting();
5393    save.await;
5394}
5395
5396#[gpui::test]
5397async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
5398    init_test(cx, |_| {});
5399
5400    let fs = FakeFs::new(cx.executor());
5401    fs.insert_file("/file.rs", Default::default()).await;
5402
5403    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5404
5405    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
5406    language_registry.add(rust_lang());
5407    let mut fake_servers = language_registry.register_fake_lsp_adapter(
5408        "Rust",
5409        FakeLspAdapter {
5410            capabilities: lsp::ServerCapabilities {
5411                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
5412                ..Default::default()
5413            },
5414            ..Default::default()
5415        },
5416    );
5417
5418    let buffer = project
5419        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
5420        .await
5421        .unwrap();
5422
5423    cx.executor().start_waiting();
5424    let fake_server = fake_servers.next().await.unwrap();
5425
5426    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
5427    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5428    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5429    assert!(cx.read(|cx| editor.is_dirty(cx)));
5430
5431    let save = editor
5432        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5433        .unwrap();
5434    fake_server
5435        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
5436            assert_eq!(
5437                params.text_document.uri,
5438                lsp::Url::from_file_path("/file.rs").unwrap()
5439            );
5440            assert_eq!(params.options.tab_size, 4);
5441            Ok(Some(vec![lsp::TextEdit::new(
5442                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
5443                ", ".to_string(),
5444            )]))
5445        })
5446        .next()
5447        .await;
5448    cx.executor().start_waiting();
5449    save.await;
5450    assert_eq!(
5451        editor.update(cx, |editor, cx| editor.text(cx)),
5452        "one, two\nthree\n"
5453    );
5454    assert!(!cx.read(|cx| editor.is_dirty(cx)));
5455
5456    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5457    assert!(cx.read(|cx| editor.is_dirty(cx)));
5458
5459    // Ensure we can still save even if formatting hangs.
5460    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
5461        move |params, _| async move {
5462            assert_eq!(
5463                params.text_document.uri,
5464                lsp::Url::from_file_path("/file.rs").unwrap()
5465            );
5466            futures::future::pending::<()>().await;
5467            unreachable!()
5468        },
5469    );
5470    let save = editor
5471        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5472        .unwrap();
5473    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
5474    cx.executor().start_waiting();
5475    save.await;
5476    assert_eq!(
5477        editor.update(cx, |editor, cx| editor.text(cx)),
5478        "one\ntwo\nthree\n"
5479    );
5480    assert!(!cx.read(|cx| editor.is_dirty(cx)));
5481
5482    // Set rust language override and assert overridden tabsize is sent to language server
5483    update_test_language_settings(cx, |settings| {
5484        settings.languages.insert(
5485            "Rust".into(),
5486            LanguageSettingsContent {
5487                tab_size: NonZeroU32::new(8),
5488                ..Default::default()
5489            },
5490        );
5491    });
5492
5493    let save = editor
5494        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5495        .unwrap();
5496    fake_server
5497        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
5498            assert_eq!(
5499                params.text_document.uri,
5500                lsp::Url::from_file_path("/file.rs").unwrap()
5501            );
5502            assert_eq!(params.options.tab_size, 8);
5503            Ok(Some(vec![]))
5504        })
5505        .next()
5506        .await;
5507    cx.executor().start_waiting();
5508    save.await;
5509}
5510
5511#[gpui::test]
5512async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
5513    init_test(cx, |settings| {
5514        settings.defaults.formatter = Some(language_settings::Formatter::LanguageServer)
5515    });
5516
5517    let fs = FakeFs::new(cx.executor());
5518    fs.insert_file("/file.rs", Default::default()).await;
5519
5520    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5521
5522    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
5523    language_registry.add(Arc::new(Language::new(
5524        LanguageConfig {
5525            name: "Rust".into(),
5526            matcher: LanguageMatcher {
5527                path_suffixes: vec!["rs".to_string()],
5528                ..Default::default()
5529            },
5530            // Enable Prettier formatting for the same buffer, and ensure
5531            // LSP is called instead of Prettier.
5532            prettier_parser_name: Some("test_parser".to_string()),
5533            ..Default::default()
5534        },
5535        Some(tree_sitter_rust::language()),
5536    )));
5537    let mut fake_servers = language_registry.register_fake_lsp_adapter(
5538        "Rust",
5539        FakeLspAdapter {
5540            capabilities: lsp::ServerCapabilities {
5541                document_formatting_provider: Some(lsp::OneOf::Left(true)),
5542                ..Default::default()
5543            },
5544            ..Default::default()
5545        },
5546    );
5547
5548    let buffer = project
5549        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
5550        .await
5551        .unwrap();
5552
5553    cx.executor().start_waiting();
5554    let fake_server = fake_servers.next().await.unwrap();
5555
5556    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
5557    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5558    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5559
5560    let format = editor
5561        .update(cx, |editor, cx| {
5562            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
5563        })
5564        .unwrap();
5565    fake_server
5566        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5567            assert_eq!(
5568                params.text_document.uri,
5569                lsp::Url::from_file_path("/file.rs").unwrap()
5570            );
5571            assert_eq!(params.options.tab_size, 4);
5572            Ok(Some(vec![lsp::TextEdit::new(
5573                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
5574                ", ".to_string(),
5575            )]))
5576        })
5577        .next()
5578        .await;
5579    cx.executor().start_waiting();
5580    format.await;
5581    assert_eq!(
5582        editor.update(cx, |editor, cx| editor.text(cx)),
5583        "one, two\nthree\n"
5584    );
5585
5586    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5587    // Ensure we don't lock if formatting hangs.
5588    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5589        assert_eq!(
5590            params.text_document.uri,
5591            lsp::Url::from_file_path("/file.rs").unwrap()
5592        );
5593        futures::future::pending::<()>().await;
5594        unreachable!()
5595    });
5596    let format = editor
5597        .update(cx, |editor, cx| {
5598            editor.perform_format(project, FormatTrigger::Manual, cx)
5599        })
5600        .unwrap();
5601    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
5602    cx.executor().start_waiting();
5603    format.await;
5604    assert_eq!(
5605        editor.update(cx, |editor, cx| editor.text(cx)),
5606        "one\ntwo\nthree\n"
5607    );
5608}
5609
5610#[gpui::test]
5611async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
5612    init_test(cx, |_| {});
5613
5614    let mut cx = EditorLspTestContext::new_rust(
5615        lsp::ServerCapabilities {
5616            document_formatting_provider: Some(lsp::OneOf::Left(true)),
5617            ..Default::default()
5618        },
5619        cx,
5620    )
5621    .await;
5622
5623    cx.set_state(indoc! {"
5624        one.twoˇ
5625    "});
5626
5627    // The format request takes a long time. When it completes, it inserts
5628    // a newline and an indent before the `.`
5629    cx.lsp
5630        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
5631            let executor = cx.background_executor().clone();
5632            async move {
5633                executor.timer(Duration::from_millis(100)).await;
5634                Ok(Some(vec![lsp::TextEdit {
5635                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
5636                    new_text: "\n    ".into(),
5637                }]))
5638            }
5639        });
5640
5641    // Submit a format request.
5642    let format_1 = cx
5643        .update_editor(|editor, cx| editor.format(&Format, cx))
5644        .unwrap();
5645    cx.executor().run_until_parked();
5646
5647    // Submit a second format request.
5648    let format_2 = cx
5649        .update_editor(|editor, cx| editor.format(&Format, cx))
5650        .unwrap();
5651    cx.executor().run_until_parked();
5652
5653    // Wait for both format requests to complete
5654    cx.executor().advance_clock(Duration::from_millis(200));
5655    cx.executor().start_waiting();
5656    format_1.await.unwrap();
5657    cx.executor().start_waiting();
5658    format_2.await.unwrap();
5659
5660    // The formatting edits only happens once.
5661    cx.assert_editor_state(indoc! {"
5662        one
5663            .twoˇ
5664    "});
5665}
5666
5667#[gpui::test]
5668async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
5669    init_test(cx, |settings| {
5670        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
5671    });
5672
5673    let mut cx = EditorLspTestContext::new_rust(
5674        lsp::ServerCapabilities {
5675            document_formatting_provider: Some(lsp::OneOf::Left(true)),
5676            ..Default::default()
5677        },
5678        cx,
5679    )
5680    .await;
5681
5682    // Set up a buffer white some trailing whitespace and no trailing newline.
5683    cx.set_state(
5684        &[
5685            "one ",   //
5686            "twoˇ",   //
5687            "three ", //
5688            "four",   //
5689        ]
5690        .join("\n"),
5691    );
5692
5693    // Submit a format request.
5694    let format = cx
5695        .update_editor(|editor, cx| editor.format(&Format, cx))
5696        .unwrap();
5697
5698    // Record which buffer changes have been sent to the language server
5699    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
5700    cx.lsp
5701        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
5702            let buffer_changes = buffer_changes.clone();
5703            move |params, _| {
5704                buffer_changes.lock().extend(
5705                    params
5706                        .content_changes
5707                        .into_iter()
5708                        .map(|e| (e.range.unwrap(), e.text)),
5709                );
5710            }
5711        });
5712
5713    // Handle formatting requests to the language server.
5714    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
5715        let buffer_changes = buffer_changes.clone();
5716        move |_, _| {
5717            // When formatting is requested, trailing whitespace has already been stripped,
5718            // and the trailing newline has already been added.
5719            assert_eq!(
5720                &buffer_changes.lock()[1..],
5721                &[
5722                    (
5723                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
5724                        "".into()
5725                    ),
5726                    (
5727                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
5728                        "".into()
5729                    ),
5730                    (
5731                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
5732                        "\n".into()
5733                    ),
5734                ]
5735            );
5736
5737            // Insert blank lines between each line of the buffer.
5738            async move {
5739                Ok(Some(vec![
5740                    lsp::TextEdit {
5741                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
5742                        new_text: "\n".into(),
5743                    },
5744                    lsp::TextEdit {
5745                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
5746                        new_text: "\n".into(),
5747                    },
5748                ]))
5749            }
5750        }
5751    });
5752
5753    // After formatting the buffer, the trailing whitespace is stripped,
5754    // a newline is appended, and the edits provided by the language server
5755    // have been applied.
5756    format.await.unwrap();
5757    cx.assert_editor_state(
5758        &[
5759            "one",   //
5760            "",      //
5761            "twoˇ",  //
5762            "",      //
5763            "three", //
5764            "four",  //
5765            "",      //
5766        ]
5767        .join("\n"),
5768    );
5769
5770    // Undoing the formatting undoes the trailing whitespace removal, the
5771    // trailing newline, and the LSP edits.
5772    cx.update_buffer(|buffer, cx| buffer.undo(cx));
5773    cx.assert_editor_state(
5774        &[
5775            "one ",   //
5776            "twoˇ",   //
5777            "three ", //
5778            "four",   //
5779        ]
5780        .join("\n"),
5781    );
5782}
5783
5784#[gpui::test]
5785async fn test_completion(cx: &mut gpui::TestAppContext) {
5786    init_test(cx, |_| {});
5787
5788    let mut cx = EditorLspTestContext::new_rust(
5789        lsp::ServerCapabilities {
5790            completion_provider: Some(lsp::CompletionOptions {
5791                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
5792                resolve_provider: Some(true),
5793                ..Default::default()
5794            }),
5795            ..Default::default()
5796        },
5797        cx,
5798    )
5799    .await;
5800
5801    cx.set_state(indoc! {"
5802        oneˇ
5803        two
5804        three
5805    "});
5806    cx.simulate_keystroke(".");
5807    handle_completion_request(
5808        &mut cx,
5809        indoc! {"
5810            one.|<>
5811            two
5812            three
5813        "},
5814        vec!["first_completion", "second_completion"],
5815    )
5816    .await;
5817    cx.condition(|editor, _| editor.context_menu_visible())
5818        .await;
5819    let apply_additional_edits = cx.update_editor(|editor, cx| {
5820        editor.context_menu_next(&Default::default(), cx);
5821        editor
5822            .confirm_completion(&ConfirmCompletion::default(), cx)
5823            .unwrap()
5824    });
5825    cx.assert_editor_state(indoc! {"
5826        one.second_completionˇ
5827        two
5828        three
5829    "});
5830
5831    handle_resolve_completion_request(
5832        &mut cx,
5833        Some(vec![
5834            (
5835                //This overlaps with the primary completion edit which is
5836                //misbehavior from the LSP spec, test that we filter it out
5837                indoc! {"
5838                    one.second_ˇcompletion
5839                    two
5840                    threeˇ
5841                "},
5842                "overlapping additional edit",
5843            ),
5844            (
5845                indoc! {"
5846                    one.second_completion
5847                    two
5848                    threeˇ
5849                "},
5850                "\nadditional edit",
5851            ),
5852        ]),
5853    )
5854    .await;
5855    apply_additional_edits.await.unwrap();
5856    cx.assert_editor_state(indoc! {"
5857        one.second_completionˇ
5858        two
5859        three
5860        additional edit
5861    "});
5862
5863    cx.set_state(indoc! {"
5864        one.second_completion
5865        twoˇ
5866        threeˇ
5867        additional edit
5868    "});
5869    cx.simulate_keystroke(" ");
5870    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
5871    cx.simulate_keystroke("s");
5872    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
5873
5874    cx.assert_editor_state(indoc! {"
5875        one.second_completion
5876        two sˇ
5877        three sˇ
5878        additional edit
5879    "});
5880    handle_completion_request(
5881        &mut cx,
5882        indoc! {"
5883            one.second_completion
5884            two s
5885            three <s|>
5886            additional edit
5887        "},
5888        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
5889    )
5890    .await;
5891    cx.condition(|editor, _| editor.context_menu_visible())
5892        .await;
5893
5894    cx.simulate_keystroke("i");
5895
5896    handle_completion_request(
5897        &mut cx,
5898        indoc! {"
5899            one.second_completion
5900            two si
5901            three <si|>
5902            additional edit
5903        "},
5904        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
5905    )
5906    .await;
5907    cx.condition(|editor, _| editor.context_menu_visible())
5908        .await;
5909
5910    let apply_additional_edits = cx.update_editor(|editor, cx| {
5911        editor
5912            .confirm_completion(&ConfirmCompletion::default(), cx)
5913            .unwrap()
5914    });
5915    cx.assert_editor_state(indoc! {"
5916        one.second_completion
5917        two sixth_completionˇ
5918        three sixth_completionˇ
5919        additional edit
5920    "});
5921
5922    handle_resolve_completion_request(&mut cx, None).await;
5923    apply_additional_edits.await.unwrap();
5924
5925    _ = cx.update(|cx| {
5926        cx.update_global::<SettingsStore, _>(|settings, cx| {
5927            settings.update_user_settings::<EditorSettings>(cx, |settings| {
5928                settings.show_completions_on_input = Some(false);
5929            });
5930        })
5931    });
5932    cx.set_state("editorˇ");
5933    cx.simulate_keystroke(".");
5934    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
5935    cx.simulate_keystroke("c");
5936    cx.simulate_keystroke("l");
5937    cx.simulate_keystroke("o");
5938    cx.assert_editor_state("editor.cloˇ");
5939    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
5940    cx.update_editor(|editor, cx| {
5941        editor.show_completions(&ShowCompletions, cx);
5942    });
5943    handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
5944    cx.condition(|editor, _| editor.context_menu_visible())
5945        .await;
5946    let apply_additional_edits = cx.update_editor(|editor, cx| {
5947        editor
5948            .confirm_completion(&ConfirmCompletion::default(), cx)
5949            .unwrap()
5950    });
5951    cx.assert_editor_state("editor.closeˇ");
5952    handle_resolve_completion_request(&mut cx, None).await;
5953    apply_additional_edits.await.unwrap();
5954}
5955
5956#[gpui::test]
5957async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
5958    init_test(cx, |_| {});
5959    let mut cx = EditorTestContext::new(cx).await;
5960    let language = Arc::new(Language::new(
5961        LanguageConfig {
5962            line_comments: vec!["// ".into()],
5963            ..Default::default()
5964        },
5965        Some(tree_sitter_rust::language()),
5966    ));
5967    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
5968
5969    // If multiple selections intersect a line, the line is only toggled once.
5970    cx.set_state(indoc! {"
5971        fn a() {
5972            «//b();
5973            ˇ»// «c();
5974            //ˇ»  d();
5975        }
5976    "});
5977
5978    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5979
5980    cx.assert_editor_state(indoc! {"
5981        fn a() {
5982            «b();
5983            c();
5984            ˇ» d();
5985        }
5986    "});
5987
5988    // The comment prefix is inserted at the same column for every line in a
5989    // selection.
5990    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
5991
5992    cx.assert_editor_state(indoc! {"
5993        fn a() {
5994            // «b();
5995            // c();
5996            ˇ»//  d();
5997        }
5998    "});
5999
6000    // If a selection ends at the beginning of a line, that line is not toggled.
6001    cx.set_selections_state(indoc! {"
6002        fn a() {
6003            // b();
6004            «// c();
6005        ˇ»    //  d();
6006        }
6007    "});
6008
6009    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
6010
6011    cx.assert_editor_state(indoc! {"
6012        fn a() {
6013            // b();
6014            «c();
6015        ˇ»    //  d();
6016        }
6017    "});
6018
6019    // If a selection span a single line and is empty, the line is toggled.
6020    cx.set_state(indoc! {"
6021        fn a() {
6022            a();
6023            b();
6024        ˇ
6025        }
6026    "});
6027
6028    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
6029
6030    cx.assert_editor_state(indoc! {"
6031        fn a() {
6032            a();
6033            b();
6034        //•ˇ
6035        }
6036    "});
6037
6038    // If a selection span multiple lines, empty lines are not toggled.
6039    cx.set_state(indoc! {"
6040        fn a() {
6041            «a();
6042
6043            c();ˇ»
6044        }
6045    "});
6046
6047    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
6048
6049    cx.assert_editor_state(indoc! {"
6050        fn a() {
6051            // «a();
6052
6053            // c();ˇ»
6054        }
6055    "});
6056}
6057
6058#[gpui::test]
6059async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
6060    init_test(cx, |_| {});
6061
6062    let language = Arc::new(Language::new(
6063        LanguageConfig {
6064            line_comments: vec!["// ".into()],
6065            ..Default::default()
6066        },
6067        Some(tree_sitter_rust::language()),
6068    ));
6069
6070    let registry = Arc::new(LanguageRegistry::test());
6071    registry.add(language.clone());
6072
6073    let mut cx = EditorTestContext::new(cx).await;
6074    cx.update_buffer(|buffer, cx| {
6075        buffer.set_language_registry(registry);
6076        buffer.set_language(Some(language), cx);
6077    });
6078
6079    let toggle_comments = &ToggleComments {
6080        advance_downwards: true,
6081    };
6082
6083    // Single cursor on one line -> advance
6084    // Cursor moves horizontally 3 characters as well on non-blank line
6085    cx.set_state(indoc!(
6086        "fn a() {
6087             ˇdog();
6088             cat();
6089        }"
6090    ));
6091    cx.update_editor(|editor, cx| {
6092        editor.toggle_comments(toggle_comments, cx);
6093    });
6094    cx.assert_editor_state(indoc!(
6095        "fn a() {
6096             // dog();
6097             catˇ();
6098        }"
6099    ));
6100
6101    // Single selection on one line -> don't advance
6102    cx.set_state(indoc!(
6103        "fn a() {
6104             «dog()ˇ»;
6105             cat();
6106        }"
6107    ));
6108    cx.update_editor(|editor, cx| {
6109        editor.toggle_comments(toggle_comments, cx);
6110    });
6111    cx.assert_editor_state(indoc!(
6112        "fn a() {
6113             // «dog()ˇ»;
6114             cat();
6115        }"
6116    ));
6117
6118    // Multiple cursors on one line -> advance
6119    cx.set_state(indoc!(
6120        "fn a() {
6121             ˇdˇog();
6122             cat();
6123        }"
6124    ));
6125    cx.update_editor(|editor, cx| {
6126        editor.toggle_comments(toggle_comments, cx);
6127    });
6128    cx.assert_editor_state(indoc!(
6129        "fn a() {
6130             // dog();
6131             catˇ(ˇ);
6132        }"
6133    ));
6134
6135    // Multiple cursors on one line, with selection -> don't advance
6136    cx.set_state(indoc!(
6137        "fn a() {
6138             ˇdˇog«()ˇ»;
6139             cat();
6140        }"
6141    ));
6142    cx.update_editor(|editor, cx| {
6143        editor.toggle_comments(toggle_comments, cx);
6144    });
6145    cx.assert_editor_state(indoc!(
6146        "fn a() {
6147             // ˇdˇog«()ˇ»;
6148             cat();
6149        }"
6150    ));
6151
6152    // Single cursor on one line -> advance
6153    // Cursor moves to column 0 on blank line
6154    cx.set_state(indoc!(
6155        "fn a() {
6156             ˇdog();
6157
6158             cat();
6159        }"
6160    ));
6161    cx.update_editor(|editor, cx| {
6162        editor.toggle_comments(toggle_comments, cx);
6163    });
6164    cx.assert_editor_state(indoc!(
6165        "fn a() {
6166             // dog();
6167        ˇ
6168             cat();
6169        }"
6170    ));
6171
6172    // Single cursor on one line -> advance
6173    // Cursor starts and ends at column 0
6174    cx.set_state(indoc!(
6175        "fn a() {
6176         ˇ    dog();
6177             cat();
6178        }"
6179    ));
6180    cx.update_editor(|editor, cx| {
6181        editor.toggle_comments(toggle_comments, cx);
6182    });
6183    cx.assert_editor_state(indoc!(
6184        "fn a() {
6185             // dog();
6186         ˇ    cat();
6187        }"
6188    ));
6189}
6190
6191#[gpui::test]
6192async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
6193    init_test(cx, |_| {});
6194
6195    let mut cx = EditorTestContext::new(cx).await;
6196
6197    let html_language = Arc::new(
6198        Language::new(
6199            LanguageConfig {
6200                name: "HTML".into(),
6201                block_comment: Some(("<!-- ".into(), " -->".into())),
6202                ..Default::default()
6203            },
6204            Some(tree_sitter_html::language()),
6205        )
6206        .with_injection_query(
6207            r#"
6208            (script_element
6209                (raw_text) @content
6210                (#set! "language" "javascript"))
6211            "#,
6212        )
6213        .unwrap(),
6214    );
6215
6216    let javascript_language = Arc::new(Language::new(
6217        LanguageConfig {
6218            name: "JavaScript".into(),
6219            line_comments: vec!["// ".into()],
6220            ..Default::default()
6221        },
6222        Some(tree_sitter_typescript::language_tsx()),
6223    ));
6224
6225    let registry = Arc::new(LanguageRegistry::test());
6226    registry.add(html_language.clone());
6227    registry.add(javascript_language.clone());
6228
6229    cx.update_buffer(|buffer, cx| {
6230        buffer.set_language_registry(registry);
6231        buffer.set_language(Some(html_language), cx);
6232    });
6233
6234    // Toggle comments for empty selections
6235    cx.set_state(
6236        &r#"
6237            <p>A</p>ˇ
6238            <p>B</p>ˇ
6239            <p>C</p>ˇ
6240        "#
6241        .unindent(),
6242    );
6243    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6244    cx.assert_editor_state(
6245        &r#"
6246            <!-- <p>A</p>ˇ -->
6247            <!-- <p>B</p>ˇ -->
6248            <!-- <p>C</p>ˇ -->
6249        "#
6250        .unindent(),
6251    );
6252    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6253    cx.assert_editor_state(
6254        &r#"
6255            <p>A</p>ˇ
6256            <p>B</p>ˇ
6257            <p>C</p>ˇ
6258        "#
6259        .unindent(),
6260    );
6261
6262    // Toggle comments for mixture of empty and non-empty selections, where
6263    // multiple selections occupy a given line.
6264    cx.set_state(
6265        &r#"
6266            <p>A«</p>
6267            <p>ˇ»B</p>ˇ
6268            <p>C«</p>
6269            <p>ˇ»D</p>ˇ
6270        "#
6271        .unindent(),
6272    );
6273
6274    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6275    cx.assert_editor_state(
6276        &r#"
6277            <!-- <p>A«</p>
6278            <p>ˇ»B</p>ˇ -->
6279            <!-- <p>C«</p>
6280            <p>ˇ»D</p>ˇ -->
6281        "#
6282        .unindent(),
6283    );
6284    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6285    cx.assert_editor_state(
6286        &r#"
6287            <p>A«</p>
6288            <p>ˇ»B</p>ˇ
6289            <p>C«</p>
6290            <p>ˇ»D</p>ˇ
6291        "#
6292        .unindent(),
6293    );
6294
6295    // Toggle comments when different languages are active for different
6296    // selections.
6297    cx.set_state(
6298        &r#"
6299            ˇ<script>
6300                ˇvar x = new Y();
6301            ˇ</script>
6302        "#
6303        .unindent(),
6304    );
6305    cx.executor().run_until_parked();
6306    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6307    cx.assert_editor_state(
6308        &r#"
6309            <!-- ˇ<script> -->
6310                // ˇvar x = new Y();
6311            <!-- ˇ</script> -->
6312        "#
6313        .unindent(),
6314    );
6315}
6316
6317#[gpui::test]
6318fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
6319    init_test(cx, |_| {});
6320
6321    let buffer = cx.new_model(|cx| {
6322        Buffer::new(
6323            0,
6324            BufferId::new(cx.entity_id().as_u64()).unwrap(),
6325            sample_text(3, 4, 'a'),
6326        )
6327    });
6328    let multibuffer = cx.new_model(|cx| {
6329        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
6330        multibuffer.push_excerpts(
6331            buffer.clone(),
6332            [
6333                ExcerptRange {
6334                    context: Point::new(0, 0)..Point::new(0, 4),
6335                    primary: None,
6336                },
6337                ExcerptRange {
6338                    context: Point::new(1, 0)..Point::new(1, 4),
6339                    primary: None,
6340                },
6341            ],
6342            cx,
6343        );
6344        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
6345        multibuffer
6346    });
6347
6348    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
6349    _ = view.update(cx, |view, cx| {
6350        assert_eq!(view.text(cx), "aaaa\nbbbb");
6351        view.change_selections(None, cx, |s| {
6352            s.select_ranges([
6353                Point::new(0, 0)..Point::new(0, 0),
6354                Point::new(1, 0)..Point::new(1, 0),
6355            ])
6356        });
6357
6358        view.handle_input("X", cx);
6359        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
6360        assert_eq!(
6361            view.selections.ranges(cx),
6362            [
6363                Point::new(0, 1)..Point::new(0, 1),
6364                Point::new(1, 1)..Point::new(1, 1),
6365            ]
6366        );
6367
6368        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
6369        view.change_selections(None, cx, |s| {
6370            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
6371        });
6372        view.backspace(&Default::default(), cx);
6373        assert_eq!(view.text(cx), "Xa\nbbb");
6374        assert_eq!(
6375            view.selections.ranges(cx),
6376            [Point::new(1, 0)..Point::new(1, 0)]
6377        );
6378
6379        view.change_selections(None, cx, |s| {
6380            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
6381        });
6382        view.backspace(&Default::default(), cx);
6383        assert_eq!(view.text(cx), "X\nbb");
6384        assert_eq!(
6385            view.selections.ranges(cx),
6386            [Point::new(0, 1)..Point::new(0, 1)]
6387        );
6388    });
6389}
6390
6391#[gpui::test]
6392fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
6393    init_test(cx, |_| {});
6394
6395    let markers = vec![('[', ']').into(), ('(', ')').into()];
6396    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
6397        indoc! {"
6398            [aaaa
6399            (bbbb]
6400            cccc)",
6401        },
6402        markers.clone(),
6403    );
6404    let excerpt_ranges = markers.into_iter().map(|marker| {
6405        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
6406        ExcerptRange {
6407            context,
6408            primary: None,
6409        }
6410    });
6411    let buffer = cx.new_model(|cx| {
6412        Buffer::new(
6413            0,
6414            BufferId::new(cx.entity_id().as_u64()).unwrap(),
6415            initial_text,
6416        )
6417    });
6418    let multibuffer = cx.new_model(|cx| {
6419        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
6420        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
6421        multibuffer
6422    });
6423
6424    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
6425    _ = view.update(cx, |view, cx| {
6426        let (expected_text, selection_ranges) = marked_text_ranges(
6427            indoc! {"
6428                aaaa
6429                bˇbbb
6430                bˇbbˇb
6431                cccc"
6432            },
6433            true,
6434        );
6435        assert_eq!(view.text(cx), expected_text);
6436        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
6437
6438        view.handle_input("X", cx);
6439
6440        let (expected_text, expected_selections) = marked_text_ranges(
6441            indoc! {"
6442                aaaa
6443                bXˇbbXb
6444                bXˇbbXˇb
6445                cccc"
6446            },
6447            false,
6448        );
6449        assert_eq!(view.text(cx), expected_text);
6450        assert_eq!(view.selections.ranges(cx), expected_selections);
6451
6452        view.newline(&Newline, cx);
6453        let (expected_text, expected_selections) = marked_text_ranges(
6454            indoc! {"
6455                aaaa
6456                bX
6457                ˇbbX
6458                b
6459                bX
6460                ˇbbX
6461                ˇb
6462                cccc"
6463            },
6464            false,
6465        );
6466        assert_eq!(view.text(cx), expected_text);
6467        assert_eq!(view.selections.ranges(cx), expected_selections);
6468    });
6469}
6470
6471#[gpui::test]
6472fn test_refresh_selections(cx: &mut TestAppContext) {
6473    init_test(cx, |_| {});
6474
6475    let buffer = cx.new_model(|cx| {
6476        Buffer::new(
6477            0,
6478            BufferId::new(cx.entity_id().as_u64()).unwrap(),
6479            sample_text(3, 4, 'a'),
6480        )
6481    });
6482    let mut excerpt1_id = None;
6483    let multibuffer = cx.new_model(|cx| {
6484        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
6485        excerpt1_id = multibuffer
6486            .push_excerpts(
6487                buffer.clone(),
6488                [
6489                    ExcerptRange {
6490                        context: Point::new(0, 0)..Point::new(1, 4),
6491                        primary: None,
6492                    },
6493                    ExcerptRange {
6494                        context: Point::new(1, 0)..Point::new(2, 4),
6495                        primary: None,
6496                    },
6497                ],
6498                cx,
6499            )
6500            .into_iter()
6501            .next();
6502        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
6503        multibuffer
6504    });
6505
6506    let editor = cx.add_window(|cx| {
6507        let mut editor = build_editor(multibuffer.clone(), cx);
6508        let snapshot = editor.snapshot(cx);
6509        editor.change_selections(None, cx, |s| {
6510            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
6511        });
6512        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
6513        assert_eq!(
6514            editor.selections.ranges(cx),
6515            [
6516                Point::new(1, 3)..Point::new(1, 3),
6517                Point::new(2, 1)..Point::new(2, 1),
6518            ]
6519        );
6520        editor
6521    });
6522
6523    // Refreshing selections is a no-op when excerpts haven't changed.
6524    _ = editor.update(cx, |editor, cx| {
6525        editor.change_selections(None, cx, |s| s.refresh());
6526        assert_eq!(
6527            editor.selections.ranges(cx),
6528            [
6529                Point::new(1, 3)..Point::new(1, 3),
6530                Point::new(2, 1)..Point::new(2, 1),
6531            ]
6532        );
6533    });
6534
6535    _ = multibuffer.update(cx, |multibuffer, cx| {
6536        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
6537    });
6538    _ = editor.update(cx, |editor, cx| {
6539        // Removing an excerpt causes the first selection to become degenerate.
6540        assert_eq!(
6541            editor.selections.ranges(cx),
6542            [
6543                Point::new(0, 0)..Point::new(0, 0),
6544                Point::new(0, 1)..Point::new(0, 1)
6545            ]
6546        );
6547
6548        // Refreshing selections will relocate the first selection to the original buffer
6549        // location.
6550        editor.change_selections(None, cx, |s| s.refresh());
6551        assert_eq!(
6552            editor.selections.ranges(cx),
6553            [
6554                Point::new(0, 1)..Point::new(0, 1),
6555                Point::new(0, 3)..Point::new(0, 3)
6556            ]
6557        );
6558        assert!(editor.selections.pending_anchor().is_some());
6559    });
6560}
6561
6562#[gpui::test]
6563fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
6564    init_test(cx, |_| {});
6565
6566    let buffer = cx.new_model(|cx| {
6567        Buffer::new(
6568            0,
6569            BufferId::new(cx.entity_id().as_u64()).unwrap(),
6570            sample_text(3, 4, 'a'),
6571        )
6572    });
6573    let mut excerpt1_id = None;
6574    let multibuffer = cx.new_model(|cx| {
6575        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
6576        excerpt1_id = multibuffer
6577            .push_excerpts(
6578                buffer.clone(),
6579                [
6580                    ExcerptRange {
6581                        context: Point::new(0, 0)..Point::new(1, 4),
6582                        primary: None,
6583                    },
6584                    ExcerptRange {
6585                        context: Point::new(1, 0)..Point::new(2, 4),
6586                        primary: None,
6587                    },
6588                ],
6589                cx,
6590            )
6591            .into_iter()
6592            .next();
6593        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
6594        multibuffer
6595    });
6596
6597    let editor = cx.add_window(|cx| {
6598        let mut editor = build_editor(multibuffer.clone(), cx);
6599        let snapshot = editor.snapshot(cx);
6600        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
6601        assert_eq!(
6602            editor.selections.ranges(cx),
6603            [Point::new(1, 3)..Point::new(1, 3)]
6604        );
6605        editor
6606    });
6607
6608    _ = multibuffer.update(cx, |multibuffer, cx| {
6609        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
6610    });
6611    _ = editor.update(cx, |editor, cx| {
6612        assert_eq!(
6613            editor.selections.ranges(cx),
6614            [Point::new(0, 0)..Point::new(0, 0)]
6615        );
6616
6617        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
6618        editor.change_selections(None, cx, |s| s.refresh());
6619        assert_eq!(
6620            editor.selections.ranges(cx),
6621            [Point::new(0, 3)..Point::new(0, 3)]
6622        );
6623        assert!(editor.selections.pending_anchor().is_some());
6624    });
6625}
6626
6627#[gpui::test]
6628async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
6629    init_test(cx, |_| {});
6630
6631    let language = Arc::new(
6632        Language::new(
6633            LanguageConfig {
6634                brackets: BracketPairConfig {
6635                    pairs: vec![
6636                        BracketPair {
6637                            start: "{".to_string(),
6638                            end: "}".to_string(),
6639                            close: true,
6640                            newline: true,
6641                        },
6642                        BracketPair {
6643                            start: "/* ".to_string(),
6644                            end: " */".to_string(),
6645                            close: true,
6646                            newline: true,
6647                        },
6648                    ],
6649                    ..Default::default()
6650                },
6651                ..Default::default()
6652            },
6653            Some(tree_sitter_rust::language()),
6654        )
6655        .with_indents_query("")
6656        .unwrap(),
6657    );
6658
6659    let text = concat!(
6660        "{   }\n",     //
6661        "  x\n",       //
6662        "  /*   */\n", //
6663        "x\n",         //
6664        "{{} }\n",     //
6665    );
6666
6667    let buffer = cx.new_model(|cx| {
6668        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
6669            .with_language(language, cx)
6670    });
6671    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
6672    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
6673    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
6674        .await;
6675
6676    _ = view.update(cx, |view, cx| {
6677        view.change_selections(None, cx, |s| {
6678            s.select_display_ranges([
6679                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
6680                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
6681                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
6682            ])
6683        });
6684        view.newline(&Newline, cx);
6685
6686        assert_eq!(
6687            view.buffer().read(cx).read(cx).text(),
6688            concat!(
6689                "{ \n",    // Suppress rustfmt
6690                "\n",      //
6691                "}\n",     //
6692                "  x\n",   //
6693                "  /* \n", //
6694                "  \n",    //
6695                "  */\n",  //
6696                "x\n",     //
6697                "{{} \n",  //
6698                "}\n",     //
6699            )
6700        );
6701    });
6702}
6703
6704#[gpui::test]
6705fn test_highlighted_ranges(cx: &mut TestAppContext) {
6706    init_test(cx, |_| {});
6707
6708    let editor = cx.add_window(|cx| {
6709        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
6710        build_editor(buffer.clone(), cx)
6711    });
6712
6713    _ = editor.update(cx, |editor, cx| {
6714        struct Type1;
6715        struct Type2;
6716
6717        let buffer = editor.buffer.read(cx).snapshot(cx);
6718
6719        let anchor_range =
6720            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
6721
6722        editor.highlight_background::<Type1>(
6723            vec![
6724                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
6725                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
6726                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
6727                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
6728            ],
6729            |_| Hsla::red(),
6730            cx,
6731        );
6732        editor.highlight_background::<Type2>(
6733            vec![
6734                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
6735                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
6736                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
6737                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
6738            ],
6739            |_| Hsla::green(),
6740            cx,
6741        );
6742
6743        let snapshot = editor.snapshot(cx);
6744        let mut highlighted_ranges = editor.background_highlights_in_range(
6745            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
6746            &snapshot,
6747            cx.theme().colors(),
6748        );
6749        // Enforce a consistent ordering based on color without relying on the ordering of the
6750        // highlight's `TypeId` which is non-executor.
6751        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
6752        assert_eq!(
6753            highlighted_ranges,
6754            &[
6755                (
6756                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
6757                    Hsla::red(),
6758                ),
6759                (
6760                    DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
6761                    Hsla::red(),
6762                ),
6763                (
6764                    DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
6765                    Hsla::green(),
6766                ),
6767                (
6768                    DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
6769                    Hsla::green(),
6770                ),
6771            ]
6772        );
6773        assert_eq!(
6774            editor.background_highlights_in_range(
6775                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
6776                &snapshot,
6777                cx.theme().colors(),
6778            ),
6779            &[(
6780                DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
6781                Hsla::red(),
6782            )]
6783        );
6784    });
6785}
6786
6787#[gpui::test]
6788async fn test_following(cx: &mut gpui::TestAppContext) {
6789    init_test(cx, |_| {});
6790
6791    let fs = FakeFs::new(cx.executor());
6792    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
6793
6794    let buffer = project.update(cx, |project, cx| {
6795        let buffer = project
6796            .create_buffer(&sample_text(16, 8, 'a'), None, cx)
6797            .unwrap();
6798        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
6799    });
6800    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
6801    let follower = cx.update(|cx| {
6802        cx.open_window(
6803            WindowOptions {
6804                bounds: WindowBounds::Fixed(Bounds::from_corners(
6805                    gpui::Point::new(0_f64.into(), 0_f64.into()),
6806                    gpui::Point::new(10_f64.into(), 80_f64.into()),
6807                )),
6808                ..Default::default()
6809            },
6810            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
6811        )
6812    });
6813
6814    let is_still_following = Rc::new(RefCell::new(true));
6815    let follower_edit_event_count = Rc::new(RefCell::new(0));
6816    let pending_update = Rc::new(RefCell::new(None));
6817    _ = follower.update(cx, {
6818        let update = pending_update.clone();
6819        let is_still_following = is_still_following.clone();
6820        let follower_edit_event_count = follower_edit_event_count.clone();
6821        |_, cx| {
6822            cx.subscribe(
6823                &leader.root_view(cx).unwrap(),
6824                move |_, leader, event, cx| {
6825                    leader
6826                        .read(cx)
6827                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
6828                },
6829            )
6830            .detach();
6831
6832            cx.subscribe(
6833                &follower.root_view(cx).unwrap(),
6834                move |_, _, event: &EditorEvent, _cx| {
6835                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
6836                        *is_still_following.borrow_mut() = false;
6837                    }
6838
6839                    if let EditorEvent::BufferEdited = event {
6840                        *follower_edit_event_count.borrow_mut() += 1;
6841                    }
6842                },
6843            )
6844            .detach();
6845        }
6846    });
6847
6848    // Update the selections only
6849    _ = leader.update(cx, |leader, cx| {
6850        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
6851    });
6852    follower
6853        .update(cx, |follower, cx| {
6854            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6855        })
6856        .unwrap()
6857        .await
6858        .unwrap();
6859    _ = follower.update(cx, |follower, cx| {
6860        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
6861    });
6862    assert_eq!(*is_still_following.borrow(), true);
6863    assert_eq!(*follower_edit_event_count.borrow(), 0);
6864
6865    // Update the scroll position only
6866    _ = leader.update(cx, |leader, cx| {
6867        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
6868    });
6869    follower
6870        .update(cx, |follower, cx| {
6871            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6872        })
6873        .unwrap()
6874        .await
6875        .unwrap();
6876    assert_eq!(
6877        follower
6878            .update(cx, |follower, cx| follower.scroll_position(cx))
6879            .unwrap(),
6880        gpui::Point::new(1.5, 3.5)
6881    );
6882    assert_eq!(*is_still_following.borrow(), true);
6883    assert_eq!(*follower_edit_event_count.borrow(), 0);
6884
6885    // Update the selections and scroll position. The follower's scroll position is updated
6886    // via autoscroll, not via the leader's exact scroll position.
6887    _ = leader.update(cx, |leader, cx| {
6888        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
6889        leader.request_autoscroll(Autoscroll::newest(), cx);
6890        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
6891    });
6892    follower
6893        .update(cx, |follower, cx| {
6894            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6895        })
6896        .unwrap()
6897        .await
6898        .unwrap();
6899    _ = follower.update(cx, |follower, cx| {
6900        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
6901        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
6902    });
6903    assert_eq!(*is_still_following.borrow(), true);
6904
6905    // Creating a pending selection that precedes another selection
6906    _ = leader.update(cx, |leader, cx| {
6907        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
6908        leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
6909    });
6910    follower
6911        .update(cx, |follower, cx| {
6912            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6913        })
6914        .unwrap()
6915        .await
6916        .unwrap();
6917    _ = follower.update(cx, |follower, cx| {
6918        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
6919    });
6920    assert_eq!(*is_still_following.borrow(), true);
6921
6922    // Extend the pending selection so that it surrounds another selection
6923    _ = leader.update(cx, |leader, cx| {
6924        leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
6925    });
6926    follower
6927        .update(cx, |follower, cx| {
6928            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6929        })
6930        .unwrap()
6931        .await
6932        .unwrap();
6933    _ = follower.update(cx, |follower, cx| {
6934        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
6935    });
6936
6937    // Scrolling locally breaks the follow
6938    _ = follower.update(cx, |follower, cx| {
6939        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
6940        follower.set_scroll_anchor(
6941            ScrollAnchor {
6942                anchor: top_anchor,
6943                offset: gpui::Point::new(0.0, 0.5),
6944            },
6945            cx,
6946        );
6947    });
6948    assert_eq!(*is_still_following.borrow(), false);
6949}
6950
6951#[gpui::test]
6952async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
6953    init_test(cx, |_| {});
6954
6955    let fs = FakeFs::new(cx.executor());
6956    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
6957    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
6958    let pane = workspace
6959        .update(cx, |workspace, _| workspace.active_pane().clone())
6960        .unwrap();
6961
6962    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
6963
6964    let leader = pane.update(cx, |_, cx| {
6965        let multibuffer = cx.new_model(|_| MultiBuffer::new(0, ReadWrite));
6966        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
6967    });
6968
6969    // Start following the editor when it has no excerpts.
6970    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
6971    let follower_1 = cx
6972        .update_window(*workspace.deref(), |_, cx| {
6973            Editor::from_state_proto(
6974                pane.clone(),
6975                workspace.root_view(cx).unwrap(),
6976                ViewId {
6977                    creator: Default::default(),
6978                    id: 0,
6979                },
6980                &mut state_message,
6981                cx,
6982            )
6983        })
6984        .unwrap()
6985        .unwrap()
6986        .await
6987        .unwrap();
6988
6989    let update_message = Rc::new(RefCell::new(None));
6990    follower_1.update(cx, {
6991        let update = update_message.clone();
6992        |_, cx| {
6993            cx.subscribe(&leader, move |_, leader, event, cx| {
6994                leader
6995                    .read(cx)
6996                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
6997            })
6998            .detach();
6999        }
7000    });
7001
7002    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
7003        (
7004            project
7005                .create_buffer("abc\ndef\nghi\njkl\n", None, cx)
7006                .unwrap(),
7007            project
7008                .create_buffer("mno\npqr\nstu\nvwx\n", None, cx)
7009                .unwrap(),
7010        )
7011    });
7012
7013    // Insert some excerpts.
7014    _ = leader.update(cx, |leader, cx| {
7015        leader.buffer.update(cx, |multibuffer, cx| {
7016            let excerpt_ids = multibuffer.push_excerpts(
7017                buffer_1.clone(),
7018                [
7019                    ExcerptRange {
7020                        context: 1..6,
7021                        primary: None,
7022                    },
7023                    ExcerptRange {
7024                        context: 12..15,
7025                        primary: None,
7026                    },
7027                    ExcerptRange {
7028                        context: 0..3,
7029                        primary: None,
7030                    },
7031                ],
7032                cx,
7033            );
7034            multibuffer.insert_excerpts_after(
7035                excerpt_ids[0],
7036                buffer_2.clone(),
7037                [
7038                    ExcerptRange {
7039                        context: 8..12,
7040                        primary: None,
7041                    },
7042                    ExcerptRange {
7043                        context: 0..6,
7044                        primary: None,
7045                    },
7046                ],
7047                cx,
7048            );
7049        });
7050    });
7051
7052    // Apply the update of adding the excerpts.
7053    follower_1
7054        .update(cx, |follower, cx| {
7055            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
7056        })
7057        .await
7058        .unwrap();
7059    assert_eq!(
7060        follower_1.update(cx, |editor, cx| editor.text(cx)),
7061        leader.update(cx, |editor, cx| editor.text(cx))
7062    );
7063    update_message.borrow_mut().take();
7064
7065    // Start following separately after it already has excerpts.
7066    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
7067    let follower_2 = cx
7068        .update_window(*workspace.deref(), |_, cx| {
7069            Editor::from_state_proto(
7070                pane.clone(),
7071                workspace.root_view(cx).unwrap().clone(),
7072                ViewId {
7073                    creator: Default::default(),
7074                    id: 0,
7075                },
7076                &mut state_message,
7077                cx,
7078            )
7079        })
7080        .unwrap()
7081        .unwrap()
7082        .await
7083        .unwrap();
7084    assert_eq!(
7085        follower_2.update(cx, |editor, cx| editor.text(cx)),
7086        leader.update(cx, |editor, cx| editor.text(cx))
7087    );
7088
7089    // Remove some excerpts.
7090    _ = leader.update(cx, |leader, cx| {
7091        leader.buffer.update(cx, |multibuffer, cx| {
7092            let excerpt_ids = multibuffer.excerpt_ids();
7093            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
7094            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
7095        });
7096    });
7097
7098    // Apply the update of removing the excerpts.
7099    follower_1
7100        .update(cx, |follower, cx| {
7101            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
7102        })
7103        .await
7104        .unwrap();
7105    follower_2
7106        .update(cx, |follower, cx| {
7107            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
7108        })
7109        .await
7110        .unwrap();
7111    update_message.borrow_mut().take();
7112    assert_eq!(
7113        follower_1.update(cx, |editor, cx| editor.text(cx)),
7114        leader.update(cx, |editor, cx| editor.text(cx))
7115    );
7116}
7117
7118#[gpui::test]
7119async fn go_to_prev_overlapping_diagnostic(
7120    executor: BackgroundExecutor,
7121    cx: &mut gpui::TestAppContext,
7122) {
7123    init_test(cx, |_| {});
7124
7125    let mut cx = EditorTestContext::new(cx).await;
7126    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
7127
7128    cx.set_state(indoc! {"
7129        ˇfn func(abc def: i32) -> u32 {
7130        }
7131    "});
7132
7133    _ = cx.update(|cx| {
7134        _ = project.update(cx, |project, cx| {
7135            project
7136                .update_diagnostics(
7137                    LanguageServerId(0),
7138                    lsp::PublishDiagnosticsParams {
7139                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
7140                        version: None,
7141                        diagnostics: vec![
7142                            lsp::Diagnostic {
7143                                range: lsp::Range::new(
7144                                    lsp::Position::new(0, 11),
7145                                    lsp::Position::new(0, 12),
7146                                ),
7147                                severity: Some(lsp::DiagnosticSeverity::ERROR),
7148                                ..Default::default()
7149                            },
7150                            lsp::Diagnostic {
7151                                range: lsp::Range::new(
7152                                    lsp::Position::new(0, 12),
7153                                    lsp::Position::new(0, 15),
7154                                ),
7155                                severity: Some(lsp::DiagnosticSeverity::ERROR),
7156                                ..Default::default()
7157                            },
7158                            lsp::Diagnostic {
7159                                range: lsp::Range::new(
7160                                    lsp::Position::new(0, 25),
7161                                    lsp::Position::new(0, 28),
7162                                ),
7163                                severity: Some(lsp::DiagnosticSeverity::ERROR),
7164                                ..Default::default()
7165                            },
7166                        ],
7167                    },
7168                    &[],
7169                    cx,
7170                )
7171                .unwrap()
7172        });
7173    });
7174
7175    executor.run_until_parked();
7176
7177    cx.update_editor(|editor, cx| {
7178        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
7179    });
7180
7181    cx.assert_editor_state(indoc! {"
7182        fn func(abc def: i32) -> ˇu32 {
7183        }
7184    "});
7185
7186    cx.update_editor(|editor, cx| {
7187        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
7188    });
7189
7190    cx.assert_editor_state(indoc! {"
7191        fn func(abc ˇdef: i32) -> u32 {
7192        }
7193    "});
7194
7195    cx.update_editor(|editor, cx| {
7196        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
7197    });
7198
7199    cx.assert_editor_state(indoc! {"
7200        fn func(abcˇ def: i32) -> u32 {
7201        }
7202    "});
7203
7204    cx.update_editor(|editor, cx| {
7205        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
7206    });
7207
7208    cx.assert_editor_state(indoc! {"
7209        fn func(abc def: i32) -> ˇu32 {
7210        }
7211    "});
7212}
7213
7214#[gpui::test]
7215async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
7216    init_test(cx, |_| {});
7217
7218    let mut cx = EditorTestContext::new(cx).await;
7219
7220    let diff_base = r#"
7221        use some::mod;
7222
7223        const A: u32 = 42;
7224
7225        fn main() {
7226            println!("hello");
7227
7228            println!("world");
7229        }
7230        "#
7231    .unindent();
7232
7233    // Edits are modified, removed, modified, added
7234    cx.set_state(
7235        &r#"
7236        use some::modified;
7237
7238        ˇ
7239        fn main() {
7240            println!("hello there");
7241
7242            println!("around the");
7243            println!("world");
7244        }
7245        "#
7246        .unindent(),
7247    );
7248
7249    cx.set_diff_base(Some(&diff_base));
7250    executor.run_until_parked();
7251
7252    cx.update_editor(|editor, cx| {
7253        //Wrap around the bottom of the buffer
7254        for _ in 0..3 {
7255            editor.go_to_hunk(&GoToHunk, cx);
7256        }
7257    });
7258
7259    cx.assert_editor_state(
7260        &r#"
7261        ˇuse some::modified;
7262
7263
7264        fn main() {
7265            println!("hello there");
7266
7267            println!("around the");
7268            println!("world");
7269        }
7270        "#
7271        .unindent(),
7272    );
7273
7274    cx.update_editor(|editor, cx| {
7275        //Wrap around the top of the buffer
7276        for _ in 0..2 {
7277            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
7278        }
7279    });
7280
7281    cx.assert_editor_state(
7282        &r#"
7283        use some::modified;
7284
7285
7286        fn main() {
7287        ˇ    println!("hello there");
7288
7289            println!("around the");
7290            println!("world");
7291        }
7292        "#
7293        .unindent(),
7294    );
7295
7296    cx.update_editor(|editor, cx| {
7297        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
7298    });
7299
7300    cx.assert_editor_state(
7301        &r#"
7302        use some::modified;
7303
7304        ˇ
7305        fn main() {
7306            println!("hello there");
7307
7308            println!("around the");
7309            println!("world");
7310        }
7311        "#
7312        .unindent(),
7313    );
7314
7315    cx.update_editor(|editor, cx| {
7316        for _ in 0..3 {
7317            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
7318        }
7319    });
7320
7321    cx.assert_editor_state(
7322        &r#"
7323        use some::modified;
7324
7325
7326        fn main() {
7327        ˇ    println!("hello there");
7328
7329            println!("around the");
7330            println!("world");
7331        }
7332        "#
7333        .unindent(),
7334    );
7335
7336    cx.update_editor(|editor, cx| {
7337        editor.fold(&Fold, cx);
7338
7339        //Make sure that the fold only gets one hunk
7340        for _ in 0..4 {
7341            editor.go_to_hunk(&GoToHunk, cx);
7342        }
7343    });
7344
7345    cx.assert_editor_state(
7346        &r#"
7347        ˇuse some::modified;
7348
7349
7350        fn main() {
7351            println!("hello there");
7352
7353            println!("around the");
7354            println!("world");
7355        }
7356        "#
7357        .unindent(),
7358    );
7359}
7360
7361#[test]
7362fn test_split_words() {
7363    fn split(text: &str) -> Vec<&str> {
7364        split_words(text).collect()
7365    }
7366
7367    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
7368    assert_eq!(split("hello_world"), &["hello_", "world"]);
7369    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
7370    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
7371    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
7372    assert_eq!(split("helloworld"), &["helloworld"]);
7373}
7374
7375#[gpui::test]
7376async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
7377    init_test(cx, |_| {});
7378
7379    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
7380    let mut assert = |before, after| {
7381        let _state_context = cx.set_state(before);
7382        cx.update_editor(|editor, cx| {
7383            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
7384        });
7385        cx.assert_editor_state(after);
7386    };
7387
7388    // Outside bracket jumps to outside of matching bracket
7389    assert("console.logˇ(var);", "console.log(var)ˇ;");
7390    assert("console.log(var)ˇ;", "console.logˇ(var);");
7391
7392    // Inside bracket jumps to inside of matching bracket
7393    assert("console.log(ˇvar);", "console.log(varˇ);");
7394    assert("console.log(varˇ);", "console.log(ˇvar);");
7395
7396    // When outside a bracket and inside, favor jumping to the inside bracket
7397    assert(
7398        "console.log('foo', [1, 2, 3]ˇ);",
7399        "console.log(ˇ'foo', [1, 2, 3]);",
7400    );
7401    assert(
7402        "console.log(ˇ'foo', [1, 2, 3]);",
7403        "console.log('foo', [1, 2, 3]ˇ);",
7404    );
7405
7406    // Bias forward if two options are equally likely
7407    assert(
7408        "let result = curried_fun()ˇ();",
7409        "let result = curried_fun()()ˇ;",
7410    );
7411
7412    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
7413    assert(
7414        indoc! {"
7415            function test() {
7416                console.log('test')ˇ
7417            }"},
7418        indoc! {"
7419            function test() {
7420                console.logˇ('test')
7421            }"},
7422    );
7423}
7424
7425#[gpui::test(iterations = 10)]
7426async fn test_copilot(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
7427    // flaky
7428    init_test(cx, |_| {});
7429
7430    let (copilot, copilot_lsp) = Copilot::fake(cx);
7431    _ = cx.update(|cx| Copilot::set_global(copilot, cx));
7432    let mut cx = EditorLspTestContext::new_rust(
7433        lsp::ServerCapabilities {
7434            completion_provider: Some(lsp::CompletionOptions {
7435                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
7436                ..Default::default()
7437            }),
7438            ..Default::default()
7439        },
7440        cx,
7441    )
7442    .await;
7443
7444    // When inserting, ensure autocompletion is favored over Copilot suggestions.
7445    cx.set_state(indoc! {"
7446        oneˇ
7447        two
7448        three
7449    "});
7450    cx.simulate_keystroke(".");
7451    let _ = handle_completion_request(
7452        &mut cx,
7453        indoc! {"
7454            one.|<>
7455            two
7456            three
7457        "},
7458        vec!["completion_a", "completion_b"],
7459    );
7460    handle_copilot_completion_request(
7461        &copilot_lsp,
7462        vec![copilot::request::Completion {
7463            text: "one.copilot1".into(),
7464            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
7465            ..Default::default()
7466        }],
7467        vec![],
7468    );
7469    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7470    cx.update_editor(|editor, cx| {
7471        assert!(editor.context_menu_visible());
7472        assert!(!editor.has_active_copilot_suggestion(cx));
7473
7474        // Confirming a completion inserts it and hides the context menu, without showing
7475        // the copilot suggestion afterwards.
7476        editor
7477            .confirm_completion(&Default::default(), cx)
7478            .unwrap()
7479            .detach();
7480        assert!(!editor.context_menu_visible());
7481        assert!(!editor.has_active_copilot_suggestion(cx));
7482        assert_eq!(editor.text(cx), "one.completion_a\ntwo\nthree\n");
7483        assert_eq!(editor.display_text(cx), "one.completion_a\ntwo\nthree\n");
7484    });
7485
7486    // Ensure Copilot suggestions are shown right away if no autocompletion is available.
7487    cx.set_state(indoc! {"
7488        oneˇ
7489        two
7490        three
7491    "});
7492    cx.simulate_keystroke(".");
7493    let _ = handle_completion_request(
7494        &mut cx,
7495        indoc! {"
7496            one.|<>
7497            two
7498            three
7499        "},
7500        vec![],
7501    );
7502    handle_copilot_completion_request(
7503        &copilot_lsp,
7504        vec![copilot::request::Completion {
7505            text: "one.copilot1".into(),
7506            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
7507            ..Default::default()
7508        }],
7509        vec![],
7510    );
7511    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7512    cx.update_editor(|editor, cx| {
7513        assert!(!editor.context_menu_visible());
7514        assert!(editor.has_active_copilot_suggestion(cx));
7515        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
7516        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
7517    });
7518
7519    // Reset editor, and ensure autocompletion is still favored over Copilot suggestions.
7520    cx.set_state(indoc! {"
7521        oneˇ
7522        two
7523        three
7524    "});
7525    cx.simulate_keystroke(".");
7526    let _ = handle_completion_request(
7527        &mut cx,
7528        indoc! {"
7529            one.|<>
7530            two
7531            three
7532        "},
7533        vec!["completion_a", "completion_b"],
7534    );
7535    handle_copilot_completion_request(
7536        &copilot_lsp,
7537        vec![copilot::request::Completion {
7538            text: "one.copilot1".into(),
7539            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
7540            ..Default::default()
7541        }],
7542        vec![],
7543    );
7544    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7545    cx.update_editor(|editor, cx| {
7546        assert!(editor.context_menu_visible());
7547        assert!(!editor.has_active_copilot_suggestion(cx));
7548
7549        // When hiding the context menu, the Copilot suggestion becomes visible.
7550        editor.hide_context_menu(cx);
7551        assert!(!editor.context_menu_visible());
7552        assert!(editor.has_active_copilot_suggestion(cx));
7553        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
7554        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
7555    });
7556
7557    // Ensure existing completion is interpolated when inserting again.
7558    cx.simulate_keystroke("c");
7559    executor.run_until_parked();
7560    cx.update_editor(|editor, cx| {
7561        assert!(!editor.context_menu_visible());
7562        assert!(editor.has_active_copilot_suggestion(cx));
7563        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
7564        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7565    });
7566
7567    // After debouncing, new Copilot completions should be requested.
7568    handle_copilot_completion_request(
7569        &copilot_lsp,
7570        vec![copilot::request::Completion {
7571            text: "one.copilot2".into(),
7572            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 5)),
7573            ..Default::default()
7574        }],
7575        vec![],
7576    );
7577    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7578    cx.update_editor(|editor, cx| {
7579        assert!(!editor.context_menu_visible());
7580        assert!(editor.has_active_copilot_suggestion(cx));
7581        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7582        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7583
7584        // Canceling should remove the active Copilot suggestion.
7585        editor.cancel(&Default::default(), cx);
7586        assert!(!editor.has_active_copilot_suggestion(cx));
7587        assert_eq!(editor.display_text(cx), "one.c\ntwo\nthree\n");
7588        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7589
7590        // After canceling, tabbing shouldn't insert the previously shown suggestion.
7591        editor.tab(&Default::default(), cx);
7592        assert!(!editor.has_active_copilot_suggestion(cx));
7593        assert_eq!(editor.display_text(cx), "one.c   \ntwo\nthree\n");
7594        assert_eq!(editor.text(cx), "one.c   \ntwo\nthree\n");
7595
7596        // When undoing the previously active suggestion is shown again.
7597        editor.undo(&Default::default(), cx);
7598        assert!(editor.has_active_copilot_suggestion(cx));
7599        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7600        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7601    });
7602
7603    // If an edit occurs outside of this editor, the suggestion is still correctly interpolated.
7604    cx.update_buffer(|buffer, cx| buffer.edit([(5..5, "o")], None, cx));
7605    cx.update_editor(|editor, cx| {
7606        assert!(editor.has_active_copilot_suggestion(cx));
7607        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7608        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
7609
7610        // Tabbing when there is an active suggestion inserts it.
7611        editor.tab(&Default::default(), cx);
7612        assert!(!editor.has_active_copilot_suggestion(cx));
7613        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7614        assert_eq!(editor.text(cx), "one.copilot2\ntwo\nthree\n");
7615
7616        // When undoing the previously active suggestion is shown again.
7617        editor.undo(&Default::default(), cx);
7618        assert!(editor.has_active_copilot_suggestion(cx));
7619        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7620        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
7621
7622        // Hide suggestion.
7623        editor.cancel(&Default::default(), cx);
7624        assert!(!editor.has_active_copilot_suggestion(cx));
7625        assert_eq!(editor.display_text(cx), "one.co\ntwo\nthree\n");
7626        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
7627    });
7628
7629    // If an edit occurs outside of this editor but no suggestion is being shown,
7630    // we won't make it visible.
7631    cx.update_buffer(|buffer, cx| buffer.edit([(6..6, "p")], None, cx));
7632    cx.update_editor(|editor, cx| {
7633        assert!(!editor.has_active_copilot_suggestion(cx));
7634        assert_eq!(editor.display_text(cx), "one.cop\ntwo\nthree\n");
7635        assert_eq!(editor.text(cx), "one.cop\ntwo\nthree\n");
7636    });
7637
7638    // Reset the editor to verify how suggestions behave when tabbing on leading indentation.
7639    cx.update_editor(|editor, cx| {
7640        editor.set_text("fn foo() {\n  \n}", cx);
7641        editor.change_selections(None, cx, |s| {
7642            s.select_ranges([Point::new(1, 2)..Point::new(1, 2)])
7643        });
7644    });
7645    handle_copilot_completion_request(
7646        &copilot_lsp,
7647        vec![copilot::request::Completion {
7648            text: "    let x = 4;".into(),
7649            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
7650            ..Default::default()
7651        }],
7652        vec![],
7653    );
7654
7655    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
7656    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7657    cx.update_editor(|editor, cx| {
7658        assert!(editor.has_active_copilot_suggestion(cx));
7659        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
7660        assert_eq!(editor.text(cx), "fn foo() {\n  \n}");
7661
7662        // Tabbing inside of leading whitespace inserts indentation without accepting the suggestion.
7663        editor.tab(&Default::default(), cx);
7664        assert!(editor.has_active_copilot_suggestion(cx));
7665        assert_eq!(editor.text(cx), "fn foo() {\n    \n}");
7666        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
7667
7668        // Tabbing again accepts the suggestion.
7669        editor.tab(&Default::default(), cx);
7670        assert!(!editor.has_active_copilot_suggestion(cx));
7671        assert_eq!(editor.text(cx), "fn foo() {\n    let x = 4;\n}");
7672        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
7673    });
7674}
7675
7676#[gpui::test(iterations = 10)]
7677async fn test_accept_partial_copilot_suggestion(
7678    executor: BackgroundExecutor,
7679    cx: &mut gpui::TestAppContext,
7680) {
7681    // flaky
7682    init_test(cx, |_| {});
7683
7684    let (copilot, copilot_lsp) = Copilot::fake(cx);
7685    _ = cx.update(|cx| Copilot::set_global(copilot, cx));
7686    let mut cx = EditorLspTestContext::new_rust(
7687        lsp::ServerCapabilities {
7688            completion_provider: Some(lsp::CompletionOptions {
7689                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
7690                ..Default::default()
7691            }),
7692            ..Default::default()
7693        },
7694        cx,
7695    )
7696    .await;
7697
7698    // Setup the editor with a completion request.
7699    cx.set_state(indoc! {"
7700        oneˇ
7701        two
7702        three
7703    "});
7704    cx.simulate_keystroke(".");
7705    let _ = handle_completion_request(
7706        &mut cx,
7707        indoc! {"
7708            one.|<>
7709            two
7710            three
7711        "},
7712        vec![],
7713    );
7714    handle_copilot_completion_request(
7715        &copilot_lsp,
7716        vec![copilot::request::Completion {
7717            text: "one.copilot1".into(),
7718            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
7719            ..Default::default()
7720        }],
7721        vec![],
7722    );
7723    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7724    cx.update_editor(|editor, cx| {
7725        assert!(editor.has_active_copilot_suggestion(cx));
7726
7727        // Accepting the first word of the suggestion should only accept the first word and still show the rest.
7728        editor.accept_partial_copilot_suggestion(&Default::default(), cx);
7729        assert!(editor.has_active_copilot_suggestion(cx));
7730        assert_eq!(editor.text(cx), "one.copilot\ntwo\nthree\n");
7731        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
7732
7733        // Accepting next word should accept the non-word and copilot suggestion should be gone
7734        editor.accept_partial_copilot_suggestion(&Default::default(), cx);
7735        assert!(!editor.has_active_copilot_suggestion(cx));
7736        assert_eq!(editor.text(cx), "one.copilot1\ntwo\nthree\n");
7737        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
7738    });
7739
7740    // Reset the editor and check non-word and whitespace completion
7741    cx.set_state(indoc! {"
7742        oneˇ
7743        two
7744        three
7745    "});
7746    cx.simulate_keystroke(".");
7747    let _ = handle_completion_request(
7748        &mut cx,
7749        indoc! {"
7750            one.|<>
7751            two
7752            three
7753        "},
7754        vec![],
7755    );
7756    handle_copilot_completion_request(
7757        &copilot_lsp,
7758        vec![copilot::request::Completion {
7759            text: "one.123. copilot\n 456".into(),
7760            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
7761            ..Default::default()
7762        }],
7763        vec![],
7764    );
7765    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7766    cx.update_editor(|editor, cx| {
7767        assert!(editor.has_active_copilot_suggestion(cx));
7768
7769        // Accepting the first word (non-word) of the suggestion should only accept the first word and still show the rest.
7770        editor.accept_partial_copilot_suggestion(&Default::default(), cx);
7771        assert!(editor.has_active_copilot_suggestion(cx));
7772        assert_eq!(editor.text(cx), "one.123. \ntwo\nthree\n");
7773        assert_eq!(
7774            editor.display_text(cx),
7775            "one.123. copilot\n 456\ntwo\nthree\n"
7776        );
7777
7778        // Accepting next word should accept the next word and copilot suggestion should still exist
7779        editor.accept_partial_copilot_suggestion(&Default::default(), cx);
7780        assert!(editor.has_active_copilot_suggestion(cx));
7781        assert_eq!(editor.text(cx), "one.123. copilot\ntwo\nthree\n");
7782        assert_eq!(
7783            editor.display_text(cx),
7784            "one.123. copilot\n 456\ntwo\nthree\n"
7785        );
7786
7787        // Accepting the whitespace should accept the non-word/whitespaces with newline and copilot suggestion should be gone
7788        editor.accept_partial_copilot_suggestion(&Default::default(), cx);
7789        assert!(!editor.has_active_copilot_suggestion(cx));
7790        assert_eq!(editor.text(cx), "one.123. copilot\n 456\ntwo\nthree\n");
7791        assert_eq!(
7792            editor.display_text(cx),
7793            "one.123. copilot\n 456\ntwo\nthree\n"
7794        );
7795    });
7796}
7797
7798#[gpui::test]
7799async fn test_copilot_completion_invalidation(
7800    executor: BackgroundExecutor,
7801    cx: &mut gpui::TestAppContext,
7802) {
7803    init_test(cx, |_| {});
7804
7805    let (copilot, copilot_lsp) = Copilot::fake(cx);
7806    _ = cx.update(|cx| Copilot::set_global(copilot, cx));
7807    let mut cx = EditorLspTestContext::new_rust(
7808        lsp::ServerCapabilities {
7809            completion_provider: Some(lsp::CompletionOptions {
7810                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
7811                ..Default::default()
7812            }),
7813            ..Default::default()
7814        },
7815        cx,
7816    )
7817    .await;
7818
7819    cx.set_state(indoc! {"
7820        one
7821        twˇ
7822        three
7823    "});
7824
7825    handle_copilot_completion_request(
7826        &copilot_lsp,
7827        vec![copilot::request::Completion {
7828            text: "two.foo()".into(),
7829            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
7830            ..Default::default()
7831        }],
7832        vec![],
7833    );
7834    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
7835    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7836    cx.update_editor(|editor, cx| {
7837        assert!(editor.has_active_copilot_suggestion(cx));
7838        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7839        assert_eq!(editor.text(cx), "one\ntw\nthree\n");
7840
7841        editor.backspace(&Default::default(), cx);
7842        assert!(editor.has_active_copilot_suggestion(cx));
7843        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7844        assert_eq!(editor.text(cx), "one\nt\nthree\n");
7845
7846        editor.backspace(&Default::default(), cx);
7847        assert!(editor.has_active_copilot_suggestion(cx));
7848        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7849        assert_eq!(editor.text(cx), "one\n\nthree\n");
7850
7851        // Deleting across the original suggestion range invalidates it.
7852        editor.backspace(&Default::default(), cx);
7853        assert!(!editor.has_active_copilot_suggestion(cx));
7854        assert_eq!(editor.display_text(cx), "one\nthree\n");
7855        assert_eq!(editor.text(cx), "one\nthree\n");
7856
7857        // Undoing the deletion restores the suggestion.
7858        editor.undo(&Default::default(), cx);
7859        assert!(editor.has_active_copilot_suggestion(cx));
7860        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7861        assert_eq!(editor.text(cx), "one\n\nthree\n");
7862    });
7863}
7864
7865#[gpui::test]
7866async fn test_copilot_multibuffer(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
7867    init_test(cx, |_| {});
7868
7869    let (copilot, copilot_lsp) = Copilot::fake(cx);
7870    _ = cx.update(|cx| Copilot::set_global(copilot, cx));
7871
7872    let buffer_1 = cx.new_model(|cx| {
7873        Buffer::new(
7874            0,
7875            BufferId::new(cx.entity_id().as_u64()).unwrap(),
7876            "a = 1\nb = 2\n",
7877        )
7878    });
7879    let buffer_2 = cx.new_model(|cx| {
7880        Buffer::new(
7881            0,
7882            BufferId::new(cx.entity_id().as_u64()).unwrap(),
7883            "c = 3\nd = 4\n",
7884        )
7885    });
7886    let multibuffer = cx.new_model(|cx| {
7887        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
7888        multibuffer.push_excerpts(
7889            buffer_1.clone(),
7890            [ExcerptRange {
7891                context: Point::new(0, 0)..Point::new(2, 0),
7892                primary: None,
7893            }],
7894            cx,
7895        );
7896        multibuffer.push_excerpts(
7897            buffer_2.clone(),
7898            [ExcerptRange {
7899                context: Point::new(0, 0)..Point::new(2, 0),
7900                primary: None,
7901            }],
7902            cx,
7903        );
7904        multibuffer
7905    });
7906    let editor = cx.add_window(|cx| build_editor(multibuffer, cx));
7907
7908    handle_copilot_completion_request(
7909        &copilot_lsp,
7910        vec![copilot::request::Completion {
7911            text: "b = 2 + a".into(),
7912            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 5)),
7913            ..Default::default()
7914        }],
7915        vec![],
7916    );
7917    _ = editor.update(cx, |editor, cx| {
7918        // Ensure copilot suggestions are shown for the first excerpt.
7919        editor.change_selections(None, cx, |s| {
7920            s.select_ranges([Point::new(1, 5)..Point::new(1, 5)])
7921        });
7922        editor.next_copilot_suggestion(&Default::default(), cx);
7923    });
7924    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7925    _ = editor.update(cx, |editor, cx| {
7926        assert!(editor.has_active_copilot_suggestion(cx));
7927        assert_eq!(
7928            editor.display_text(cx),
7929            "\n\na = 1\nb = 2 + a\n\n\n\nc = 3\nd = 4\n"
7930        );
7931        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
7932    });
7933
7934    handle_copilot_completion_request(
7935        &copilot_lsp,
7936        vec![copilot::request::Completion {
7937            text: "d = 4 + c".into(),
7938            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 6)),
7939            ..Default::default()
7940        }],
7941        vec![],
7942    );
7943    _ = editor.update(cx, |editor, cx| {
7944        // Move to another excerpt, ensuring the suggestion gets cleared.
7945        editor.change_selections(None, cx, |s| {
7946            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
7947        });
7948        assert!(!editor.has_active_copilot_suggestion(cx));
7949        assert_eq!(
7950            editor.display_text(cx),
7951            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4\n"
7952        );
7953        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
7954
7955        // Type a character, ensuring we don't even try to interpolate the previous suggestion.
7956        editor.handle_input(" ", cx);
7957        assert!(!editor.has_active_copilot_suggestion(cx));
7958        assert_eq!(
7959            editor.display_text(cx),
7960            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 \n"
7961        );
7962        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
7963    });
7964
7965    // Ensure the new suggestion is displayed when the debounce timeout expires.
7966    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7967    _ = editor.update(cx, |editor, cx| {
7968        assert!(editor.has_active_copilot_suggestion(cx));
7969        assert_eq!(
7970            editor.display_text(cx),
7971            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 + c\n"
7972        );
7973        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
7974    });
7975}
7976
7977#[gpui::test]
7978async fn test_copilot_disabled_globs(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
7979    init_test(cx, |settings| {
7980        settings
7981            .copilot
7982            .get_or_insert(Default::default())
7983            .disabled_globs = Some(vec![".env*".to_string()]);
7984    });
7985
7986    let (copilot, copilot_lsp) = Copilot::fake(cx);
7987    _ = cx.update(|cx| Copilot::set_global(copilot, cx));
7988
7989    let fs = FakeFs::new(cx.executor());
7990    fs.insert_tree(
7991        "/test",
7992        json!({
7993            ".env": "SECRET=something\n",
7994            "README.md": "hello\n"
7995        }),
7996    )
7997    .await;
7998    let project = Project::test(fs, ["/test".as_ref()], cx).await;
7999
8000    let private_buffer = project
8001        .update(cx, |project, cx| {
8002            project.open_local_buffer("/test/.env", cx)
8003        })
8004        .await
8005        .unwrap();
8006    let public_buffer = project
8007        .update(cx, |project, cx| {
8008            project.open_local_buffer("/test/README.md", cx)
8009        })
8010        .await
8011        .unwrap();
8012
8013    let multibuffer = cx.new_model(|cx| {
8014        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
8015        multibuffer.push_excerpts(
8016            private_buffer.clone(),
8017            [ExcerptRange {
8018                context: Point::new(0, 0)..Point::new(1, 0),
8019                primary: None,
8020            }],
8021            cx,
8022        );
8023        multibuffer.push_excerpts(
8024            public_buffer.clone(),
8025            [ExcerptRange {
8026                context: Point::new(0, 0)..Point::new(1, 0),
8027                primary: None,
8028            }],
8029            cx,
8030        );
8031        multibuffer
8032    });
8033    let editor = cx.add_window(|cx| build_editor(multibuffer, cx));
8034
8035    let mut copilot_requests = copilot_lsp
8036        .handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| async move {
8037            Ok(copilot::request::GetCompletionsResult {
8038                completions: vec![copilot::request::Completion {
8039                    text: "next line".into(),
8040                    range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
8041                    ..Default::default()
8042                }],
8043            })
8044        });
8045
8046    _ = editor.update(cx, |editor, cx| {
8047        editor.change_selections(None, cx, |selections| {
8048            selections.select_ranges([Point::new(0, 0)..Point::new(0, 0)])
8049        });
8050        editor.next_copilot_suggestion(&Default::default(), cx);
8051    });
8052
8053    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
8054    assert!(copilot_requests.try_next().is_err());
8055
8056    _ = editor.update(cx, |editor, cx| {
8057        editor.change_selections(None, cx, |s| {
8058            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
8059        });
8060        editor.next_copilot_suggestion(&Default::default(), cx);
8061    });
8062
8063    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
8064    assert!(copilot_requests.try_next().is_ok());
8065}
8066
8067#[gpui::test]
8068async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
8069    init_test(cx, |_| {});
8070
8071    let fs = FakeFs::new(cx.executor());
8072    fs.insert_tree(
8073        "/a",
8074        json!({
8075            "main.rs": "fn main() { let a = 5; }",
8076            "other.rs": "// Test file",
8077        }),
8078    )
8079    .await;
8080    let project = Project::test(fs, ["/a".as_ref()], cx).await;
8081
8082    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
8083    language_registry.add(Arc::new(Language::new(
8084        LanguageConfig {
8085            name: "Rust".into(),
8086            matcher: LanguageMatcher {
8087                path_suffixes: vec!["rs".to_string()],
8088                ..Default::default()
8089            },
8090            brackets: BracketPairConfig {
8091                pairs: vec![BracketPair {
8092                    start: "{".to_string(),
8093                    end: "}".to_string(),
8094                    close: true,
8095                    newline: true,
8096                }],
8097                disabled_scopes_by_bracket_ix: Vec::new(),
8098            },
8099            ..Default::default()
8100        },
8101        Some(tree_sitter_rust::language()),
8102    )));
8103    let mut fake_servers = language_registry.register_fake_lsp_adapter(
8104        "Rust",
8105        FakeLspAdapter {
8106            capabilities: lsp::ServerCapabilities {
8107                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
8108                    first_trigger_character: "{".to_string(),
8109                    more_trigger_character: None,
8110                }),
8111                ..Default::default()
8112            },
8113            ..Default::default()
8114        },
8115    );
8116
8117    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
8118
8119    let cx = &mut VisualTestContext::from_window(*workspace, cx);
8120
8121    let worktree_id = workspace
8122        .update(cx, |workspace, cx| {
8123            workspace.project().update(cx, |project, cx| {
8124                project.worktrees().next().unwrap().read(cx).id()
8125            })
8126        })
8127        .unwrap();
8128
8129    let buffer = project
8130        .update(cx, |project, cx| {
8131            project.open_local_buffer("/a/main.rs", cx)
8132        })
8133        .await
8134        .unwrap();
8135    cx.executor().run_until_parked();
8136    cx.executor().start_waiting();
8137    let fake_server = fake_servers.next().await.unwrap();
8138    let editor_handle = workspace
8139        .update(cx, |workspace, cx| {
8140            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
8141        })
8142        .unwrap()
8143        .await
8144        .unwrap()
8145        .downcast::<Editor>()
8146        .unwrap();
8147
8148    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
8149        assert_eq!(
8150            params.text_document_position.text_document.uri,
8151            lsp::Url::from_file_path("/a/main.rs").unwrap(),
8152        );
8153        assert_eq!(
8154            params.text_document_position.position,
8155            lsp::Position::new(0, 21),
8156        );
8157
8158        Ok(Some(vec![lsp::TextEdit {
8159            new_text: "]".to_string(),
8160            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
8161        }]))
8162    });
8163
8164    editor_handle.update(cx, |editor, cx| {
8165        editor.focus(cx);
8166        editor.change_selections(None, cx, |s| {
8167            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
8168        });
8169        editor.handle_input("{", cx);
8170    });
8171
8172    cx.executor().run_until_parked();
8173
8174    _ = buffer.update(cx, |buffer, _| {
8175        assert_eq!(
8176            buffer.text(),
8177            "fn main() { let a = {5}; }",
8178            "No extra braces from on type formatting should appear in the buffer"
8179        )
8180    });
8181}
8182
8183#[gpui::test]
8184async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
8185    init_test(cx, |_| {});
8186
8187    let fs = FakeFs::new(cx.executor());
8188    fs.insert_tree(
8189        "/a",
8190        json!({
8191            "main.rs": "fn main() { let a = 5; }",
8192            "other.rs": "// Test file",
8193        }),
8194    )
8195    .await;
8196
8197    let project = Project::test(fs, ["/a".as_ref()], cx).await;
8198
8199    let server_restarts = Arc::new(AtomicUsize::new(0));
8200    let closure_restarts = Arc::clone(&server_restarts);
8201    let language_server_name = "test language server";
8202    let language_name: Arc<str> = "Rust".into();
8203
8204    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
8205    language_registry.add(Arc::new(Language::new(
8206        LanguageConfig {
8207            name: Arc::clone(&language_name),
8208            matcher: LanguageMatcher {
8209                path_suffixes: vec!["rs".to_string()],
8210                ..Default::default()
8211            },
8212            ..Default::default()
8213        },
8214        Some(tree_sitter_rust::language()),
8215    )));
8216    let mut fake_servers = language_registry.register_fake_lsp_adapter(
8217        "Rust",
8218        FakeLspAdapter {
8219            name: language_server_name,
8220            initialization_options: Some(json!({
8221                "testOptionValue": true
8222            })),
8223            initializer: Some(Box::new(move |fake_server| {
8224                let task_restarts = Arc::clone(&closure_restarts);
8225                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
8226                    task_restarts.fetch_add(1, atomic::Ordering::Release);
8227                    futures::future::ready(Ok(()))
8228                });
8229            })),
8230            ..Default::default()
8231        },
8232    );
8233
8234    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
8235    let _buffer = project
8236        .update(cx, |project, cx| {
8237            project.open_local_buffer("/a/main.rs", cx)
8238        })
8239        .await
8240        .unwrap();
8241    let _fake_server = fake_servers.next().await.unwrap();
8242    update_test_language_settings(cx, |language_settings| {
8243        language_settings.languages.insert(
8244            Arc::clone(&language_name),
8245            LanguageSettingsContent {
8246                tab_size: NonZeroU32::new(8),
8247                ..Default::default()
8248            },
8249        );
8250    });
8251    cx.executor().run_until_parked();
8252    assert_eq!(
8253        server_restarts.load(atomic::Ordering::Acquire),
8254        0,
8255        "Should not restart LSP server on an unrelated change"
8256    );
8257
8258    update_test_project_settings(cx, |project_settings| {
8259        project_settings.lsp.insert(
8260            "Some other server name".into(),
8261            LspSettings {
8262                settings: None,
8263                initialization_options: Some(json!({
8264                    "some other init value": false
8265                })),
8266            },
8267        );
8268    });
8269    cx.executor().run_until_parked();
8270    assert_eq!(
8271        server_restarts.load(atomic::Ordering::Acquire),
8272        0,
8273        "Should not restart LSP server on an unrelated LSP settings change"
8274    );
8275
8276    update_test_project_settings(cx, |project_settings| {
8277        project_settings.lsp.insert(
8278            language_server_name.into(),
8279            LspSettings {
8280                settings: None,
8281                initialization_options: Some(json!({
8282                    "anotherInitValue": false
8283                })),
8284            },
8285        );
8286    });
8287    cx.executor().run_until_parked();
8288    assert_eq!(
8289        server_restarts.load(atomic::Ordering::Acquire),
8290        1,
8291        "Should restart LSP server on a related LSP settings change"
8292    );
8293
8294    update_test_project_settings(cx, |project_settings| {
8295        project_settings.lsp.insert(
8296            language_server_name.into(),
8297            LspSettings {
8298                settings: None,
8299                initialization_options: Some(json!({
8300                    "anotherInitValue": false
8301                })),
8302            },
8303        );
8304    });
8305    cx.executor().run_until_parked();
8306    assert_eq!(
8307        server_restarts.load(atomic::Ordering::Acquire),
8308        1,
8309        "Should not restart LSP server on a related LSP settings change that is the same"
8310    );
8311
8312    update_test_project_settings(cx, |project_settings| {
8313        project_settings.lsp.insert(
8314            language_server_name.into(),
8315            LspSettings {
8316                settings: None,
8317                initialization_options: None,
8318            },
8319        );
8320    });
8321    cx.executor().run_until_parked();
8322    assert_eq!(
8323        server_restarts.load(atomic::Ordering::Acquire),
8324        2,
8325        "Should restart LSP server on another related LSP settings change"
8326    );
8327}
8328
8329#[gpui::test]
8330async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
8331    init_test(cx, |_| {});
8332
8333    let mut cx = EditorLspTestContext::new_rust(
8334        lsp::ServerCapabilities {
8335            completion_provider: Some(lsp::CompletionOptions {
8336                trigger_characters: Some(vec![".".to_string()]),
8337                resolve_provider: Some(true),
8338                ..Default::default()
8339            }),
8340            ..Default::default()
8341        },
8342        cx,
8343    )
8344    .await;
8345
8346    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
8347    cx.simulate_keystroke(".");
8348    let completion_item = lsp::CompletionItem {
8349        label: "some".into(),
8350        kind: Some(lsp::CompletionItemKind::SNIPPET),
8351        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
8352        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
8353            kind: lsp::MarkupKind::Markdown,
8354            value: "```rust\nSome(2)\n```".to_string(),
8355        })),
8356        deprecated: Some(false),
8357        sort_text: Some("fffffff2".to_string()),
8358        filter_text: Some("some".to_string()),
8359        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
8360        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
8361            range: lsp::Range {
8362                start: lsp::Position {
8363                    line: 0,
8364                    character: 22,
8365                },
8366                end: lsp::Position {
8367                    line: 0,
8368                    character: 22,
8369                },
8370            },
8371            new_text: "Some(2)".to_string(),
8372        })),
8373        additional_text_edits: Some(vec![lsp::TextEdit {
8374            range: lsp::Range {
8375                start: lsp::Position {
8376                    line: 0,
8377                    character: 20,
8378                },
8379                end: lsp::Position {
8380                    line: 0,
8381                    character: 22,
8382                },
8383            },
8384            new_text: "".to_string(),
8385        }]),
8386        ..Default::default()
8387    };
8388
8389    let closure_completion_item = completion_item.clone();
8390    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
8391        let task_completion_item = closure_completion_item.clone();
8392        async move {
8393            Ok(Some(lsp::CompletionResponse::Array(vec![
8394                task_completion_item,
8395            ])))
8396        }
8397    });
8398
8399    request.next().await;
8400
8401    cx.condition(|editor, _| editor.context_menu_visible())
8402        .await;
8403    let apply_additional_edits = cx.update_editor(|editor, cx| {
8404        editor
8405            .confirm_completion(&ConfirmCompletion::default(), cx)
8406            .unwrap()
8407    });
8408    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
8409
8410    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
8411        let task_completion_item = completion_item.clone();
8412        async move { Ok(task_completion_item) }
8413    })
8414    .next()
8415    .await
8416    .unwrap();
8417    apply_additional_edits.await.unwrap();
8418    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
8419}
8420
8421#[gpui::test]
8422async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
8423    init_test(cx, |_| {});
8424
8425    let mut cx = EditorLspTestContext::new(
8426        Language::new(
8427            LanguageConfig {
8428                matcher: LanguageMatcher {
8429                    path_suffixes: vec!["jsx".into()],
8430                    ..Default::default()
8431                },
8432                overrides: [(
8433                    "element".into(),
8434                    LanguageConfigOverride {
8435                        word_characters: Override::Set(['-'].into_iter().collect()),
8436                        ..Default::default()
8437                    },
8438                )]
8439                .into_iter()
8440                .collect(),
8441                ..Default::default()
8442            },
8443            Some(tree_sitter_typescript::language_tsx()),
8444        )
8445        .with_override_query("(jsx_self_closing_element) @element")
8446        .unwrap(),
8447        lsp::ServerCapabilities {
8448            completion_provider: Some(lsp::CompletionOptions {
8449                trigger_characters: Some(vec![":".to_string()]),
8450                ..Default::default()
8451            }),
8452            ..Default::default()
8453        },
8454        cx,
8455    )
8456    .await;
8457
8458    cx.lsp
8459        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
8460            Ok(Some(lsp::CompletionResponse::Array(vec![
8461                lsp::CompletionItem {
8462                    label: "bg-blue".into(),
8463                    ..Default::default()
8464                },
8465                lsp::CompletionItem {
8466                    label: "bg-red".into(),
8467                    ..Default::default()
8468                },
8469                lsp::CompletionItem {
8470                    label: "bg-yellow".into(),
8471                    ..Default::default()
8472                },
8473            ])))
8474        });
8475
8476    cx.set_state(r#"<p class="bgˇ" />"#);
8477
8478    // Trigger completion when typing a dash, because the dash is an extra
8479    // word character in the 'element' scope, which contains the cursor.
8480    cx.simulate_keystroke("-");
8481    cx.executor().run_until_parked();
8482    cx.update_editor(|editor, _| {
8483        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
8484            assert_eq!(
8485                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
8486                &["bg-red", "bg-blue", "bg-yellow"]
8487            );
8488        } else {
8489            panic!("expected completion menu to be open");
8490        }
8491    });
8492
8493    cx.simulate_keystroke("l");
8494    cx.executor().run_until_parked();
8495    cx.update_editor(|editor, _| {
8496        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
8497            assert_eq!(
8498                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
8499                &["bg-blue", "bg-yellow"]
8500            );
8501        } else {
8502            panic!("expected completion menu to be open");
8503        }
8504    });
8505
8506    // When filtering completions, consider the character after the '-' to
8507    // be the start of a subword.
8508    cx.set_state(r#"<p class="yelˇ" />"#);
8509    cx.simulate_keystroke("l");
8510    cx.executor().run_until_parked();
8511    cx.update_editor(|editor, _| {
8512        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
8513            assert_eq!(
8514                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
8515                &["bg-yellow"]
8516            );
8517        } else {
8518            panic!("expected completion menu to be open");
8519        }
8520    });
8521}
8522
8523#[gpui::test]
8524async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
8525    init_test(cx, |settings| {
8526        settings.defaults.formatter = Some(language_settings::Formatter::Prettier)
8527    });
8528
8529    let fs = FakeFs::new(cx.executor());
8530    fs.insert_file("/file.rs", Default::default()).await;
8531
8532    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
8533    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
8534
8535    language_registry.add(Arc::new(Language::new(
8536        LanguageConfig {
8537            name: "Rust".into(),
8538            matcher: LanguageMatcher {
8539                path_suffixes: vec!["rs".to_string()],
8540                ..Default::default()
8541            },
8542            prettier_parser_name: Some("test_parser".to_string()),
8543            ..Default::default()
8544        },
8545        Some(tree_sitter_rust::language()),
8546    )));
8547
8548    let test_plugin = "test_plugin";
8549    let _ = language_registry.register_fake_lsp_adapter(
8550        "Rust",
8551        FakeLspAdapter {
8552            prettier_plugins: vec![test_plugin],
8553            ..Default::default()
8554        },
8555    );
8556
8557    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
8558    let buffer = project
8559        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
8560        .await
8561        .unwrap();
8562
8563    let buffer_text = "one\ntwo\nthree\n";
8564    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
8565    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
8566    _ = editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
8567
8568    editor
8569        .update(cx, |editor, cx| {
8570            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
8571        })
8572        .unwrap()
8573        .await;
8574    assert_eq!(
8575        editor.update(cx, |editor, cx| editor.text(cx)),
8576        buffer_text.to_string() + prettier_format_suffix,
8577        "Test prettier formatting was not applied to the original buffer text",
8578    );
8579
8580    update_test_language_settings(cx, |settings| {
8581        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
8582    });
8583    let format = editor.update(cx, |editor, cx| {
8584        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
8585    });
8586    format.await.unwrap();
8587    assert_eq!(
8588        editor.update(cx, |editor, cx| editor.text(cx)),
8589        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
8590        "Autoformatting (via test prettier) was not applied to the original buffer text",
8591    );
8592}
8593
8594#[gpui::test]
8595async fn test_find_all_references(cx: &mut gpui::TestAppContext) {
8596    init_test(cx, |_| {});
8597
8598    let mut cx = EditorLspTestContext::new_rust(
8599        lsp::ServerCapabilities {
8600            document_formatting_provider: Some(lsp::OneOf::Left(true)),
8601            ..Default::default()
8602        },
8603        cx,
8604    )
8605    .await;
8606
8607    cx.set_state(indoc! {"
8608        fn foo(«paramˇ»: i64) {
8609            println!(param);
8610        }
8611    "});
8612
8613    cx.lsp
8614        .handle_request::<lsp::request::References, _, _>(move |_, _| async move {
8615            Ok(Some(vec![
8616                lsp::Location {
8617                    uri: lsp::Url::from_file_path("/root/dir/file.rs").unwrap(),
8618                    range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 12)),
8619                },
8620                lsp::Location {
8621                    uri: lsp::Url::from_file_path("/root/dir/file.rs").unwrap(),
8622                    range: lsp::Range::new(lsp::Position::new(1, 13), lsp::Position::new(1, 18)),
8623                },
8624            ]))
8625        });
8626
8627    let references = cx
8628        .update_editor(|editor, cx| editor.find_all_references(&FindAllReferences, cx))
8629        .unwrap();
8630
8631    cx.executor().run_until_parked();
8632
8633    cx.executor().start_waiting();
8634    references.await.unwrap();
8635
8636    cx.assert_editor_state(indoc! {"
8637        fn foo(param: i64) {
8638            println!(«paramˇ»);
8639        }
8640    "});
8641
8642    let references = cx
8643        .update_editor(|editor, cx| editor.find_all_references(&FindAllReferences, cx))
8644        .unwrap();
8645
8646    cx.executor().run_until_parked();
8647
8648    cx.executor().start_waiting();
8649    references.await.unwrap();
8650
8651    cx.assert_editor_state(indoc! {"
8652        fn foo(«paramˇ»: i64) {
8653            println!(param);
8654        }
8655    "});
8656
8657    cx.set_state(indoc! {"
8658        fn foo(param: i64) {
8659            let a = param;
8660            let aˇ = param;
8661            let a = param;
8662            println!(param);
8663        }
8664    "});
8665
8666    cx.lsp
8667        .handle_request::<lsp::request::References, _, _>(move |_, _| async move {
8668            Ok(Some(vec![lsp::Location {
8669                uri: lsp::Url::from_file_path("/root/dir/file.rs").unwrap(),
8670                range: lsp::Range::new(lsp::Position::new(2, 8), lsp::Position::new(2, 9)),
8671            }]))
8672        });
8673
8674    let references = cx
8675        .update_editor(|editor, cx| editor.find_all_references(&FindAllReferences, cx))
8676        .unwrap();
8677
8678    cx.executor().run_until_parked();
8679
8680    cx.executor().start_waiting();
8681    references.await.unwrap();
8682
8683    cx.assert_editor_state(indoc! {"
8684        fn foo(param: i64) {
8685            let a = param;
8686            let «aˇ» = param;
8687            let a = param;
8688            println!(param);
8689        }
8690    "});
8691}
8692
8693fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
8694    let point = DisplayPoint::new(row as u32, column as u32);
8695    point..point
8696}
8697
8698fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
8699    let (text, ranges) = marked_text_ranges(marked_text, true);
8700    assert_eq!(view.text(cx), text);
8701    assert_eq!(
8702        view.selections.ranges(cx),
8703        ranges,
8704        "Assert selections are {}",
8705        marked_text
8706    );
8707}
8708
8709/// Handle completion request passing a marked string specifying where the completion
8710/// should be triggered from using '|' character, what range should be replaced, and what completions
8711/// should be returned using '<' and '>' to delimit the range
8712pub fn handle_completion_request(
8713    cx: &mut EditorLspTestContext,
8714    marked_string: &str,
8715    completions: Vec<&'static str>,
8716) -> impl Future<Output = ()> {
8717    let complete_from_marker: TextRangeMarker = '|'.into();
8718    let replace_range_marker: TextRangeMarker = ('<', '>').into();
8719    let (_, mut marked_ranges) = marked_text_ranges_by(
8720        marked_string,
8721        vec![complete_from_marker.clone(), replace_range_marker.clone()],
8722    );
8723
8724    let complete_from_position =
8725        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
8726    let replace_range =
8727        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
8728
8729    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
8730        let completions = completions.clone();
8731        async move {
8732            assert_eq!(params.text_document_position.text_document.uri, url.clone());
8733            assert_eq!(
8734                params.text_document_position.position,
8735                complete_from_position
8736            );
8737            Ok(Some(lsp::CompletionResponse::Array(
8738                completions
8739                    .iter()
8740                    .map(|completion_text| lsp::CompletionItem {
8741                        label: completion_text.to_string(),
8742                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
8743                            range: replace_range,
8744                            new_text: completion_text.to_string(),
8745                        })),
8746                        ..Default::default()
8747                    })
8748                    .collect(),
8749            )))
8750        }
8751    });
8752
8753    async move {
8754        request.next().await;
8755    }
8756}
8757
8758fn handle_resolve_completion_request(
8759    cx: &mut EditorLspTestContext,
8760    edits: Option<Vec<(&'static str, &'static str)>>,
8761) -> impl Future<Output = ()> {
8762    let edits = edits.map(|edits| {
8763        edits
8764            .iter()
8765            .map(|(marked_string, new_text)| {
8766                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
8767                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
8768                lsp::TextEdit::new(replace_range, new_text.to_string())
8769            })
8770            .collect::<Vec<_>>()
8771    });
8772
8773    let mut request =
8774        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
8775            let edits = edits.clone();
8776            async move {
8777                Ok(lsp::CompletionItem {
8778                    additional_text_edits: edits,
8779                    ..Default::default()
8780                })
8781            }
8782        });
8783
8784    async move {
8785        request.next().await;
8786    }
8787}
8788
8789fn handle_copilot_completion_request(
8790    lsp: &lsp::FakeLanguageServer,
8791    completions: Vec<copilot::request::Completion>,
8792    completions_cycling: Vec<copilot::request::Completion>,
8793) {
8794    lsp.handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| {
8795        let completions = completions.clone();
8796        async move {
8797            Ok(copilot::request::GetCompletionsResult {
8798                completions: completions.clone(),
8799            })
8800        }
8801    });
8802    lsp.handle_request::<copilot::request::GetCompletionsCycling, _, _>(move |_params, _cx| {
8803        let completions_cycling = completions_cycling.clone();
8804        async move {
8805            Ok(copilot::request::GetCompletionsResult {
8806                completions: completions_cycling.clone(),
8807            })
8808        }
8809    });
8810}
8811
8812pub(crate) fn update_test_language_settings(
8813    cx: &mut TestAppContext,
8814    f: impl Fn(&mut AllLanguageSettingsContent),
8815) {
8816    _ = cx.update(|cx| {
8817        cx.update_global(|store: &mut SettingsStore, cx| {
8818            store.update_user_settings::<AllLanguageSettings>(cx, f);
8819        });
8820    });
8821}
8822
8823pub(crate) fn update_test_project_settings(
8824    cx: &mut TestAppContext,
8825    f: impl Fn(&mut ProjectSettings),
8826) {
8827    _ = cx.update(|cx| {
8828        cx.update_global(|store: &mut SettingsStore, cx| {
8829            store.update_user_settings::<ProjectSettings>(cx, f);
8830        });
8831    });
8832}
8833
8834pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
8835    _ = cx.update(|cx| {
8836        let store = SettingsStore::test(cx);
8837        cx.set_global(store);
8838        theme::init(theme::LoadThemes::JustBase, cx);
8839        release_channel::init("0.0.0", cx);
8840        client::init_settings(cx);
8841        language::init(cx);
8842        Project::init_settings(cx);
8843        workspace::init_settings(cx);
8844        crate::init(cx);
8845    });
8846
8847    update_test_language_settings(cx, f);
8848}
8849
8850pub(crate) fn rust_lang() -> Arc<Language> {
8851    Arc::new(Language::new(
8852        LanguageConfig {
8853            name: "Rust".into(),
8854            matcher: LanguageMatcher {
8855                path_suffixes: vec!["rs".to_string()],
8856                ..Default::default()
8857            },
8858            ..Default::default()
8859        },
8860        Some(tree_sitter_rust::language()),
8861    ))
8862}