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, 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_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
5176    init_test(cx, |_| {});
5177
5178    let language = Arc::new(Language::new(
5179        LanguageConfig::default(),
5180        Some(tree_sitter_rust::language()),
5181    ));
5182
5183    let buffer = cx.new_model(|cx| {
5184        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "")
5185            .with_language(language, cx)
5186    });
5187    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
5188    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5189    editor
5190        .condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
5191        .await;
5192
5193    _ = editor.update(cx, |editor, cx| {
5194        editor.set_auto_replace_emoji_shortcode(true);
5195
5196        editor.handle_input("Hello ", cx);
5197        editor.handle_input(":wave", cx);
5198        assert_eq!(editor.text(cx), "Hello :wave".unindent());
5199
5200        editor.handle_input(":", cx);
5201        assert_eq!(editor.text(cx), "Hello 👋".unindent());
5202
5203        editor.handle_input(" :smile", cx);
5204        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
5205
5206        editor.handle_input(":", cx);
5207        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
5208
5209        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
5210        editor.handle_input(":wave", cx);
5211        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
5212
5213        editor.handle_input(":", cx);
5214        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
5215
5216        editor.handle_input(":1", cx);
5217        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
5218
5219        editor.handle_input(":", cx);
5220        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
5221
5222        // Ensure shortcode does not get replaced when it is part of a word
5223        editor.handle_input(" Test:wave", cx);
5224        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
5225
5226        editor.handle_input(":", cx);
5227        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
5228
5229        editor.set_auto_replace_emoji_shortcode(false);
5230
5231        // Ensure shortcode does not get replaced when auto replace is off
5232        editor.handle_input(" :wave", cx);
5233        assert_eq!(
5234            editor.text(cx),
5235            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
5236        );
5237
5238        editor.handle_input(":", cx);
5239        assert_eq!(
5240            editor.text(cx),
5241            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
5242        );
5243    });
5244}
5245
5246#[gpui::test]
5247async fn test_snippets(cx: &mut gpui::TestAppContext) {
5248    init_test(cx, |_| {});
5249
5250    let (text, insertion_ranges) = marked_text_ranges(
5251        indoc! {"
5252            a.ˇ b
5253            a.ˇ b
5254            a.ˇ b
5255        "},
5256        false,
5257    );
5258
5259    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
5260    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5261
5262    _ = editor.update(cx, |editor, cx| {
5263        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
5264
5265        editor
5266            .insert_snippet(&insertion_ranges, snippet, cx)
5267            .unwrap();
5268
5269        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
5270            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
5271            assert_eq!(editor.text(cx), expected_text);
5272            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
5273        }
5274
5275        assert(
5276            editor,
5277            cx,
5278            indoc! {"
5279                a.f(«one», two, «three») b
5280                a.f(«one», two, «three») b
5281                a.f(«one», two, «three») b
5282            "},
5283        );
5284
5285        // Can't move earlier than the first tab stop
5286        assert!(!editor.move_to_prev_snippet_tabstop(cx));
5287        assert(
5288            editor,
5289            cx,
5290            indoc! {"
5291                a.f(«one», two, «three») b
5292                a.f(«one», two, «three») b
5293                a.f(«one», two, «three») b
5294            "},
5295        );
5296
5297        assert!(editor.move_to_next_snippet_tabstop(cx));
5298        assert(
5299            editor,
5300            cx,
5301            indoc! {"
5302                a.f(one, «two», three) b
5303                a.f(one, «two», three) b
5304                a.f(one, «two», three) b
5305            "},
5306        );
5307
5308        editor.move_to_prev_snippet_tabstop(cx);
5309        assert(
5310            editor,
5311            cx,
5312            indoc! {"
5313                a.f(«one», two, «three») b
5314                a.f(«one», two, «three») b
5315                a.f(«one», two, «three») b
5316            "},
5317        );
5318
5319        assert!(editor.move_to_next_snippet_tabstop(cx));
5320        assert(
5321            editor,
5322            cx,
5323            indoc! {"
5324                a.f(one, «two», three) b
5325                a.f(one, «two», three) b
5326                a.f(one, «two», three) b
5327            "},
5328        );
5329        assert!(editor.move_to_next_snippet_tabstop(cx));
5330        assert(
5331            editor,
5332            cx,
5333            indoc! {"
5334                a.f(one, two, three)ˇ b
5335                a.f(one, two, three)ˇ b
5336                a.f(one, two, three)ˇ b
5337            "},
5338        );
5339
5340        // As soon as the last tab stop is reached, snippet state is gone
5341        editor.move_to_prev_snippet_tabstop(cx);
5342        assert(
5343            editor,
5344            cx,
5345            indoc! {"
5346                a.f(one, two, three)ˇ b
5347                a.f(one, two, three)ˇ b
5348                a.f(one, two, three)ˇ b
5349            "},
5350        );
5351    });
5352}
5353
5354#[gpui::test]
5355async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
5356    init_test(cx, |_| {});
5357
5358    let fs = FakeFs::new(cx.executor());
5359    fs.insert_file("/file.rs", Default::default()).await;
5360
5361    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5362
5363    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
5364    language_registry.add(rust_lang());
5365    let mut fake_servers = language_registry.register_fake_lsp_adapter(
5366        "Rust",
5367        FakeLspAdapter {
5368            capabilities: lsp::ServerCapabilities {
5369                document_formatting_provider: Some(lsp::OneOf::Left(true)),
5370                ..Default::default()
5371            },
5372            ..Default::default()
5373        },
5374    );
5375
5376    let buffer = project
5377        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
5378        .await
5379        .unwrap();
5380
5381    cx.executor().start_waiting();
5382    let fake_server = fake_servers.next().await.unwrap();
5383
5384    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
5385    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5386    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5387    assert!(cx.read(|cx| editor.is_dirty(cx)));
5388
5389    let save = editor
5390        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5391        .unwrap();
5392    fake_server
5393        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5394            assert_eq!(
5395                params.text_document.uri,
5396                lsp::Url::from_file_path("/file.rs").unwrap()
5397            );
5398            assert_eq!(params.options.tab_size, 4);
5399            Ok(Some(vec![lsp::TextEdit::new(
5400                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
5401                ", ".to_string(),
5402            )]))
5403        })
5404        .next()
5405        .await;
5406    cx.executor().start_waiting();
5407    let _x = save.await;
5408
5409    assert_eq!(
5410        editor.update(cx, |editor, cx| editor.text(cx)),
5411        "one, two\nthree\n"
5412    );
5413    assert!(!cx.read(|cx| editor.is_dirty(cx)));
5414
5415    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5416    assert!(cx.read(|cx| editor.is_dirty(cx)));
5417
5418    // Ensure we can still save even if formatting hangs.
5419    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5420        assert_eq!(
5421            params.text_document.uri,
5422            lsp::Url::from_file_path("/file.rs").unwrap()
5423        );
5424        futures::future::pending::<()>().await;
5425        unreachable!()
5426    });
5427    let save = editor
5428        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5429        .unwrap();
5430    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
5431    cx.executor().start_waiting();
5432    save.await;
5433    assert_eq!(
5434        editor.update(cx, |editor, cx| editor.text(cx)),
5435        "one\ntwo\nthree\n"
5436    );
5437    assert!(!cx.read(|cx| editor.is_dirty(cx)));
5438
5439    // Set rust language override and assert overridden tabsize is sent to language server
5440    update_test_language_settings(cx, |settings| {
5441        settings.languages.insert(
5442            "Rust".into(),
5443            LanguageSettingsContent {
5444                tab_size: NonZeroU32::new(8),
5445                ..Default::default()
5446            },
5447        );
5448    });
5449
5450    let save = editor
5451        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5452        .unwrap();
5453    fake_server
5454        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5455            assert_eq!(
5456                params.text_document.uri,
5457                lsp::Url::from_file_path("/file.rs").unwrap()
5458            );
5459            assert_eq!(params.options.tab_size, 8);
5460            Ok(Some(vec![]))
5461        })
5462        .next()
5463        .await;
5464    cx.executor().start_waiting();
5465    save.await;
5466}
5467
5468#[gpui::test]
5469async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
5470    init_test(cx, |_| {});
5471
5472    let fs = FakeFs::new(cx.executor());
5473    fs.insert_file("/file.rs", Default::default()).await;
5474
5475    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5476
5477    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
5478    language_registry.add(rust_lang());
5479    let mut fake_servers = language_registry.register_fake_lsp_adapter(
5480        "Rust",
5481        FakeLspAdapter {
5482            capabilities: lsp::ServerCapabilities {
5483                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
5484                ..Default::default()
5485            },
5486            ..Default::default()
5487        },
5488    );
5489
5490    let buffer = project
5491        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
5492        .await
5493        .unwrap();
5494
5495    cx.executor().start_waiting();
5496    let fake_server = fake_servers.next().await.unwrap();
5497
5498    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
5499    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5500    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5501    assert!(cx.read(|cx| editor.is_dirty(cx)));
5502
5503    let save = editor
5504        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5505        .unwrap();
5506    fake_server
5507        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
5508            assert_eq!(
5509                params.text_document.uri,
5510                lsp::Url::from_file_path("/file.rs").unwrap()
5511            );
5512            assert_eq!(params.options.tab_size, 4);
5513            Ok(Some(vec![lsp::TextEdit::new(
5514                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
5515                ", ".to_string(),
5516            )]))
5517        })
5518        .next()
5519        .await;
5520    cx.executor().start_waiting();
5521    save.await;
5522    assert_eq!(
5523        editor.update(cx, |editor, cx| editor.text(cx)),
5524        "one, two\nthree\n"
5525    );
5526    assert!(!cx.read(|cx| editor.is_dirty(cx)));
5527
5528    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5529    assert!(cx.read(|cx| editor.is_dirty(cx)));
5530
5531    // Ensure we can still save even if formatting hangs.
5532    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
5533        move |params, _| async move {
5534            assert_eq!(
5535                params.text_document.uri,
5536                lsp::Url::from_file_path("/file.rs").unwrap()
5537            );
5538            futures::future::pending::<()>().await;
5539            unreachable!()
5540        },
5541    );
5542    let save = editor
5543        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5544        .unwrap();
5545    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
5546    cx.executor().start_waiting();
5547    save.await;
5548    assert_eq!(
5549        editor.update(cx, |editor, cx| editor.text(cx)),
5550        "one\ntwo\nthree\n"
5551    );
5552    assert!(!cx.read(|cx| editor.is_dirty(cx)));
5553
5554    // Set rust language override and assert overridden tabsize is sent to language server
5555    update_test_language_settings(cx, |settings| {
5556        settings.languages.insert(
5557            "Rust".into(),
5558            LanguageSettingsContent {
5559                tab_size: NonZeroU32::new(8),
5560                ..Default::default()
5561            },
5562        );
5563    });
5564
5565    let save = editor
5566        .update(cx, |editor, cx| editor.save(true, project.clone(), cx))
5567        .unwrap();
5568    fake_server
5569        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
5570            assert_eq!(
5571                params.text_document.uri,
5572                lsp::Url::from_file_path("/file.rs").unwrap()
5573            );
5574            assert_eq!(params.options.tab_size, 8);
5575            Ok(Some(vec![]))
5576        })
5577        .next()
5578        .await;
5579    cx.executor().start_waiting();
5580    save.await;
5581}
5582
5583#[gpui::test]
5584async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
5585    init_test(cx, |settings| {
5586        settings.defaults.formatter = Some(language_settings::Formatter::LanguageServer)
5587    });
5588
5589    let fs = FakeFs::new(cx.executor());
5590    fs.insert_file("/file.rs", Default::default()).await;
5591
5592    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
5593
5594    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
5595    language_registry.add(Arc::new(Language::new(
5596        LanguageConfig {
5597            name: "Rust".into(),
5598            matcher: LanguageMatcher {
5599                path_suffixes: vec!["rs".to_string()],
5600                ..Default::default()
5601            },
5602            // Enable Prettier formatting for the same buffer, and ensure
5603            // LSP is called instead of Prettier.
5604            prettier_parser_name: Some("test_parser".to_string()),
5605            ..Default::default()
5606        },
5607        Some(tree_sitter_rust::language()),
5608    )));
5609    let mut fake_servers = language_registry.register_fake_lsp_adapter(
5610        "Rust",
5611        FakeLspAdapter {
5612            capabilities: lsp::ServerCapabilities {
5613                document_formatting_provider: Some(lsp::OneOf::Left(true)),
5614                ..Default::default()
5615            },
5616            ..Default::default()
5617        },
5618    );
5619
5620    let buffer = project
5621        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
5622        .await
5623        .unwrap();
5624
5625    cx.executor().start_waiting();
5626    let fake_server = fake_servers.next().await.unwrap();
5627
5628    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
5629    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
5630    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5631
5632    let format = editor
5633        .update(cx, |editor, cx| {
5634            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
5635        })
5636        .unwrap();
5637    fake_server
5638        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5639            assert_eq!(
5640                params.text_document.uri,
5641                lsp::Url::from_file_path("/file.rs").unwrap()
5642            );
5643            assert_eq!(params.options.tab_size, 4);
5644            Ok(Some(vec![lsp::TextEdit::new(
5645                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
5646                ", ".to_string(),
5647            )]))
5648        })
5649        .next()
5650        .await;
5651    cx.executor().start_waiting();
5652    format.await;
5653    assert_eq!(
5654        editor.update(cx, |editor, cx| editor.text(cx)),
5655        "one, two\nthree\n"
5656    );
5657
5658    _ = editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
5659    // Ensure we don't lock if formatting hangs.
5660    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
5661        assert_eq!(
5662            params.text_document.uri,
5663            lsp::Url::from_file_path("/file.rs").unwrap()
5664        );
5665        futures::future::pending::<()>().await;
5666        unreachable!()
5667    });
5668    let format = editor
5669        .update(cx, |editor, cx| {
5670            editor.perform_format(project, FormatTrigger::Manual, cx)
5671        })
5672        .unwrap();
5673    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
5674    cx.executor().start_waiting();
5675    format.await;
5676    assert_eq!(
5677        editor.update(cx, |editor, cx| editor.text(cx)),
5678        "one\ntwo\nthree\n"
5679    );
5680}
5681
5682#[gpui::test]
5683async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
5684    init_test(cx, |_| {});
5685
5686    let mut cx = EditorLspTestContext::new_rust(
5687        lsp::ServerCapabilities {
5688            document_formatting_provider: Some(lsp::OneOf::Left(true)),
5689            ..Default::default()
5690        },
5691        cx,
5692    )
5693    .await;
5694
5695    cx.set_state(indoc! {"
5696        one.twoˇ
5697    "});
5698
5699    // The format request takes a long time. When it completes, it inserts
5700    // a newline and an indent before the `.`
5701    cx.lsp
5702        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
5703            let executor = cx.background_executor().clone();
5704            async move {
5705                executor.timer(Duration::from_millis(100)).await;
5706                Ok(Some(vec![lsp::TextEdit {
5707                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
5708                    new_text: "\n    ".into(),
5709                }]))
5710            }
5711        });
5712
5713    // Submit a format request.
5714    let format_1 = cx
5715        .update_editor(|editor, cx| editor.format(&Format, cx))
5716        .unwrap();
5717    cx.executor().run_until_parked();
5718
5719    // Submit a second format request.
5720    let format_2 = cx
5721        .update_editor(|editor, cx| editor.format(&Format, cx))
5722        .unwrap();
5723    cx.executor().run_until_parked();
5724
5725    // Wait for both format requests to complete
5726    cx.executor().advance_clock(Duration::from_millis(200));
5727    cx.executor().start_waiting();
5728    format_1.await.unwrap();
5729    cx.executor().start_waiting();
5730    format_2.await.unwrap();
5731
5732    // The formatting edits only happens once.
5733    cx.assert_editor_state(indoc! {"
5734        one
5735            .twoˇ
5736    "});
5737}
5738
5739#[gpui::test]
5740async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
5741    init_test(cx, |settings| {
5742        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
5743    });
5744
5745    let mut cx = EditorLspTestContext::new_rust(
5746        lsp::ServerCapabilities {
5747            document_formatting_provider: Some(lsp::OneOf::Left(true)),
5748            ..Default::default()
5749        },
5750        cx,
5751    )
5752    .await;
5753
5754    // Set up a buffer white some trailing whitespace and no trailing newline.
5755    cx.set_state(
5756        &[
5757            "one ",   //
5758            "twoˇ",   //
5759            "three ", //
5760            "four",   //
5761        ]
5762        .join("\n"),
5763    );
5764
5765    // Submit a format request.
5766    let format = cx
5767        .update_editor(|editor, cx| editor.format(&Format, cx))
5768        .unwrap();
5769
5770    // Record which buffer changes have been sent to the language server
5771    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
5772    cx.lsp
5773        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
5774            let buffer_changes = buffer_changes.clone();
5775            move |params, _| {
5776                buffer_changes.lock().extend(
5777                    params
5778                        .content_changes
5779                        .into_iter()
5780                        .map(|e| (e.range.unwrap(), e.text)),
5781                );
5782            }
5783        });
5784
5785    // Handle formatting requests to the language server.
5786    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
5787        let buffer_changes = buffer_changes.clone();
5788        move |_, _| {
5789            // When formatting is requested, trailing whitespace has already been stripped,
5790            // and the trailing newline has already been added.
5791            assert_eq!(
5792                &buffer_changes.lock()[1..],
5793                &[
5794                    (
5795                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
5796                        "".into()
5797                    ),
5798                    (
5799                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
5800                        "".into()
5801                    ),
5802                    (
5803                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
5804                        "\n".into()
5805                    ),
5806                ]
5807            );
5808
5809            // Insert blank lines between each line of the buffer.
5810            async move {
5811                Ok(Some(vec![
5812                    lsp::TextEdit {
5813                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
5814                        new_text: "\n".into(),
5815                    },
5816                    lsp::TextEdit {
5817                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
5818                        new_text: "\n".into(),
5819                    },
5820                ]))
5821            }
5822        }
5823    });
5824
5825    // After formatting the buffer, the trailing whitespace is stripped,
5826    // a newline is appended, and the edits provided by the language server
5827    // have been applied.
5828    format.await.unwrap();
5829    cx.assert_editor_state(
5830        &[
5831            "one",   //
5832            "",      //
5833            "twoˇ",  //
5834            "",      //
5835            "three", //
5836            "four",  //
5837            "",      //
5838        ]
5839        .join("\n"),
5840    );
5841
5842    // Undoing the formatting undoes the trailing whitespace removal, the
5843    // trailing newline, and the LSP edits.
5844    cx.update_buffer(|buffer, cx| buffer.undo(cx));
5845    cx.assert_editor_state(
5846        &[
5847            "one ",   //
5848            "twoˇ",   //
5849            "three ", //
5850            "four",   //
5851        ]
5852        .join("\n"),
5853    );
5854}
5855
5856#[gpui::test]
5857async fn test_completion(cx: &mut gpui::TestAppContext) {
5858    init_test(cx, |_| {});
5859
5860    let mut cx = EditorLspTestContext::new_rust(
5861        lsp::ServerCapabilities {
5862            completion_provider: Some(lsp::CompletionOptions {
5863                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
5864                resolve_provider: Some(true),
5865                ..Default::default()
5866            }),
5867            ..Default::default()
5868        },
5869        cx,
5870    )
5871    .await;
5872
5873    cx.set_state(indoc! {"
5874        oneˇ
5875        two
5876        three
5877    "});
5878    cx.simulate_keystroke(".");
5879    handle_completion_request(
5880        &mut cx,
5881        indoc! {"
5882            one.|<>
5883            two
5884            three
5885        "},
5886        vec!["first_completion", "second_completion"],
5887    )
5888    .await;
5889    cx.condition(|editor, _| editor.context_menu_visible())
5890        .await;
5891    let apply_additional_edits = cx.update_editor(|editor, cx| {
5892        editor.context_menu_next(&Default::default(), cx);
5893        editor
5894            .confirm_completion(&ConfirmCompletion::default(), cx)
5895            .unwrap()
5896    });
5897    cx.assert_editor_state(indoc! {"
5898        one.second_completionˇ
5899        two
5900        three
5901    "});
5902
5903    handle_resolve_completion_request(
5904        &mut cx,
5905        Some(vec![
5906            (
5907                //This overlaps with the primary completion edit which is
5908                //misbehavior from the LSP spec, test that we filter it out
5909                indoc! {"
5910                    one.second_ˇcompletion
5911                    two
5912                    threeˇ
5913                "},
5914                "overlapping additional edit",
5915            ),
5916            (
5917                indoc! {"
5918                    one.second_completion
5919                    two
5920                    threeˇ
5921                "},
5922                "\nadditional edit",
5923            ),
5924        ]),
5925    )
5926    .await;
5927    apply_additional_edits.await.unwrap();
5928    cx.assert_editor_state(indoc! {"
5929        one.second_completionˇ
5930        two
5931        three
5932        additional edit
5933    "});
5934
5935    cx.set_state(indoc! {"
5936        one.second_completion
5937        twoˇ
5938        threeˇ
5939        additional edit
5940    "});
5941    cx.simulate_keystroke(" ");
5942    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
5943    cx.simulate_keystroke("s");
5944    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
5945
5946    cx.assert_editor_state(indoc! {"
5947        one.second_completion
5948        two sˇ
5949        three sˇ
5950        additional edit
5951    "});
5952    handle_completion_request(
5953        &mut cx,
5954        indoc! {"
5955            one.second_completion
5956            two s
5957            three <s|>
5958            additional edit
5959        "},
5960        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
5961    )
5962    .await;
5963    cx.condition(|editor, _| editor.context_menu_visible())
5964        .await;
5965
5966    cx.simulate_keystroke("i");
5967
5968    handle_completion_request(
5969        &mut cx,
5970        indoc! {"
5971            one.second_completion
5972            two si
5973            three <si|>
5974            additional edit
5975        "},
5976        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
5977    )
5978    .await;
5979    cx.condition(|editor, _| editor.context_menu_visible())
5980        .await;
5981
5982    let apply_additional_edits = cx.update_editor(|editor, cx| {
5983        editor
5984            .confirm_completion(&ConfirmCompletion::default(), cx)
5985            .unwrap()
5986    });
5987    cx.assert_editor_state(indoc! {"
5988        one.second_completion
5989        two sixth_completionˇ
5990        three sixth_completionˇ
5991        additional edit
5992    "});
5993
5994    handle_resolve_completion_request(&mut cx, None).await;
5995    apply_additional_edits.await.unwrap();
5996
5997    _ = cx.update(|cx| {
5998        cx.update_global::<SettingsStore, _>(|settings, cx| {
5999            settings.update_user_settings::<EditorSettings>(cx, |settings| {
6000                settings.show_completions_on_input = Some(false);
6001            });
6002        })
6003    });
6004    cx.set_state("editorˇ");
6005    cx.simulate_keystroke(".");
6006    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
6007    cx.simulate_keystroke("c");
6008    cx.simulate_keystroke("l");
6009    cx.simulate_keystroke("o");
6010    cx.assert_editor_state("editor.cloˇ");
6011    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
6012    cx.update_editor(|editor, cx| {
6013        editor.show_completions(&ShowCompletions, cx);
6014    });
6015    handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
6016    cx.condition(|editor, _| editor.context_menu_visible())
6017        .await;
6018    let apply_additional_edits = cx.update_editor(|editor, cx| {
6019        editor
6020            .confirm_completion(&ConfirmCompletion::default(), cx)
6021            .unwrap()
6022    });
6023    cx.assert_editor_state("editor.closeˇ");
6024    handle_resolve_completion_request(&mut cx, None).await;
6025    apply_additional_edits.await.unwrap();
6026}
6027
6028#[gpui::test]
6029async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
6030    init_test(cx, |_| {});
6031    let mut cx = EditorTestContext::new(cx).await;
6032    let language = Arc::new(Language::new(
6033        LanguageConfig {
6034            line_comments: vec!["// ".into()],
6035            ..Default::default()
6036        },
6037        Some(tree_sitter_rust::language()),
6038    ));
6039    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
6040
6041    // If multiple selections intersect a line, the line is only toggled once.
6042    cx.set_state(indoc! {"
6043        fn a() {
6044            «//b();
6045            ˇ»// «c();
6046            //ˇ»  d();
6047        }
6048    "});
6049
6050    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
6051
6052    cx.assert_editor_state(indoc! {"
6053        fn a() {
6054            «b();
6055            c();
6056            ˇ» d();
6057        }
6058    "});
6059
6060    // The comment prefix is inserted at the same column for every line in a
6061    // selection.
6062    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
6063
6064    cx.assert_editor_state(indoc! {"
6065        fn a() {
6066            // «b();
6067            // c();
6068            ˇ»//  d();
6069        }
6070    "});
6071
6072    // If a selection ends at the beginning of a line, that line is not toggled.
6073    cx.set_selections_state(indoc! {"
6074        fn a() {
6075            // b();
6076            «// c();
6077        ˇ»    //  d();
6078        }
6079    "});
6080
6081    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
6082
6083    cx.assert_editor_state(indoc! {"
6084        fn a() {
6085            // b();
6086            «c();
6087        ˇ»    //  d();
6088        }
6089    "});
6090
6091    // If a selection span a single line and is empty, the line is toggled.
6092    cx.set_state(indoc! {"
6093        fn a() {
6094            a();
6095            b();
6096        ˇ
6097        }
6098    "});
6099
6100    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
6101
6102    cx.assert_editor_state(indoc! {"
6103        fn a() {
6104            a();
6105            b();
6106        //•ˇ
6107        }
6108    "});
6109
6110    // If a selection span multiple lines, empty lines are not toggled.
6111    cx.set_state(indoc! {"
6112        fn a() {
6113            «a();
6114
6115            c();ˇ»
6116        }
6117    "});
6118
6119    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
6120
6121    cx.assert_editor_state(indoc! {"
6122        fn a() {
6123            // «a();
6124
6125            // c();ˇ»
6126        }
6127    "});
6128}
6129
6130#[gpui::test]
6131async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
6132    init_test(cx, |_| {});
6133
6134    let language = Arc::new(Language::new(
6135        LanguageConfig {
6136            line_comments: vec!["// ".into()],
6137            ..Default::default()
6138        },
6139        Some(tree_sitter_rust::language()),
6140    ));
6141
6142    let registry = Arc::new(LanguageRegistry::test());
6143    registry.add(language.clone());
6144
6145    let mut cx = EditorTestContext::new(cx).await;
6146    cx.update_buffer(|buffer, cx| {
6147        buffer.set_language_registry(registry);
6148        buffer.set_language(Some(language), cx);
6149    });
6150
6151    let toggle_comments = &ToggleComments {
6152        advance_downwards: true,
6153    };
6154
6155    // Single cursor on one line -> advance
6156    // Cursor moves horizontally 3 characters as well on non-blank line
6157    cx.set_state(indoc!(
6158        "fn a() {
6159             ˇdog();
6160             cat();
6161        }"
6162    ));
6163    cx.update_editor(|editor, cx| {
6164        editor.toggle_comments(toggle_comments, cx);
6165    });
6166    cx.assert_editor_state(indoc!(
6167        "fn a() {
6168             // dog();
6169             catˇ();
6170        }"
6171    ));
6172
6173    // Single selection on one line -> don't advance
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    // Multiple cursors on one line -> advance
6191    cx.set_state(indoc!(
6192        "fn a() {
6193             ˇdˇog();
6194             cat();
6195        }"
6196    ));
6197    cx.update_editor(|editor, cx| {
6198        editor.toggle_comments(toggle_comments, cx);
6199    });
6200    cx.assert_editor_state(indoc!(
6201        "fn a() {
6202             // dog();
6203             catˇ(ˇ);
6204        }"
6205    ));
6206
6207    // Multiple cursors on one line, with selection -> don't advance
6208    cx.set_state(indoc!(
6209        "fn a() {
6210             ˇdˇog«()ˇ»;
6211             cat();
6212        }"
6213    ));
6214    cx.update_editor(|editor, cx| {
6215        editor.toggle_comments(toggle_comments, cx);
6216    });
6217    cx.assert_editor_state(indoc!(
6218        "fn a() {
6219             // ˇdˇog«()ˇ»;
6220             cat();
6221        }"
6222    ));
6223
6224    // Single cursor on one line -> advance
6225    // Cursor moves to column 0 on blank line
6226    cx.set_state(indoc!(
6227        "fn a() {
6228             ˇdog();
6229
6230             cat();
6231        }"
6232    ));
6233    cx.update_editor(|editor, cx| {
6234        editor.toggle_comments(toggle_comments, cx);
6235    });
6236    cx.assert_editor_state(indoc!(
6237        "fn a() {
6238             // dog();
6239        ˇ
6240             cat();
6241        }"
6242    ));
6243
6244    // Single cursor on one line -> advance
6245    // Cursor starts and ends at column 0
6246    cx.set_state(indoc!(
6247        "fn a() {
6248         ˇ    dog();
6249             cat();
6250        }"
6251    ));
6252    cx.update_editor(|editor, cx| {
6253        editor.toggle_comments(toggle_comments, cx);
6254    });
6255    cx.assert_editor_state(indoc!(
6256        "fn a() {
6257             // dog();
6258         ˇ    cat();
6259        }"
6260    ));
6261}
6262
6263#[gpui::test]
6264async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
6265    init_test(cx, |_| {});
6266
6267    let mut cx = EditorTestContext::new(cx).await;
6268
6269    let html_language = Arc::new(
6270        Language::new(
6271            LanguageConfig {
6272                name: "HTML".into(),
6273                block_comment: Some(("<!-- ".into(), " -->".into())),
6274                ..Default::default()
6275            },
6276            Some(tree_sitter_html::language()),
6277        )
6278        .with_injection_query(
6279            r#"
6280            (script_element
6281                (raw_text) @content
6282                (#set! "language" "javascript"))
6283            "#,
6284        )
6285        .unwrap(),
6286    );
6287
6288    let javascript_language = Arc::new(Language::new(
6289        LanguageConfig {
6290            name: "JavaScript".into(),
6291            line_comments: vec!["// ".into()],
6292            ..Default::default()
6293        },
6294        Some(tree_sitter_typescript::language_tsx()),
6295    ));
6296
6297    let registry = Arc::new(LanguageRegistry::test());
6298    registry.add(html_language.clone());
6299    registry.add(javascript_language.clone());
6300
6301    cx.update_buffer(|buffer, cx| {
6302        buffer.set_language_registry(registry);
6303        buffer.set_language(Some(html_language), cx);
6304    });
6305
6306    // Toggle comments for empty selections
6307    cx.set_state(
6308        &r#"
6309            <p>A</p>ˇ
6310            <p>B</p>ˇ
6311            <p>C</p>ˇ
6312        "#
6313        .unindent(),
6314    );
6315    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6316    cx.assert_editor_state(
6317        &r#"
6318            <!-- <p>A</p>ˇ -->
6319            <!-- <p>B</p>ˇ -->
6320            <!-- <p>C</p>ˇ -->
6321        "#
6322        .unindent(),
6323    );
6324    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6325    cx.assert_editor_state(
6326        &r#"
6327            <p>A</p>ˇ
6328            <p>B</p>ˇ
6329            <p>C</p>ˇ
6330        "#
6331        .unindent(),
6332    );
6333
6334    // Toggle comments for mixture of empty and non-empty selections, where
6335    // multiple selections occupy a given line.
6336    cx.set_state(
6337        &r#"
6338            <p>A«</p>
6339            <p>ˇ»B</p>ˇ
6340            <p>C«</p>
6341            <p>ˇ»D</p>ˇ
6342        "#
6343        .unindent(),
6344    );
6345
6346    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6347    cx.assert_editor_state(
6348        &r#"
6349            <!-- <p>A«</p>
6350            <p>ˇ»B</p>ˇ -->
6351            <!-- <p>C«</p>
6352            <p>ˇ»D</p>ˇ -->
6353        "#
6354        .unindent(),
6355    );
6356    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6357    cx.assert_editor_state(
6358        &r#"
6359            <p>A«</p>
6360            <p>ˇ»B</p>ˇ
6361            <p>C«</p>
6362            <p>ˇ»D</p>ˇ
6363        "#
6364        .unindent(),
6365    );
6366
6367    // Toggle comments when different languages are active for different
6368    // selections.
6369    cx.set_state(
6370        &r#"
6371            ˇ<script>
6372                ˇvar x = new Y();
6373            ˇ</script>
6374        "#
6375        .unindent(),
6376    );
6377    cx.executor().run_until_parked();
6378    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
6379    cx.assert_editor_state(
6380        &r#"
6381            <!-- ˇ<script> -->
6382                // ˇvar x = new Y();
6383            <!-- ˇ</script> -->
6384        "#
6385        .unindent(),
6386    );
6387}
6388
6389#[gpui::test]
6390fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
6391    init_test(cx, |_| {});
6392
6393    let buffer = cx.new_model(|cx| {
6394        Buffer::new(
6395            0,
6396            BufferId::new(cx.entity_id().as_u64()).unwrap(),
6397            sample_text(3, 4, 'a'),
6398        )
6399    });
6400    let multibuffer = cx.new_model(|cx| {
6401        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
6402        multibuffer.push_excerpts(
6403            buffer.clone(),
6404            [
6405                ExcerptRange {
6406                    context: Point::new(0, 0)..Point::new(0, 4),
6407                    primary: None,
6408                },
6409                ExcerptRange {
6410                    context: Point::new(1, 0)..Point::new(1, 4),
6411                    primary: None,
6412                },
6413            ],
6414            cx,
6415        );
6416        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
6417        multibuffer
6418    });
6419
6420    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
6421    _ = view.update(cx, |view, cx| {
6422        assert_eq!(view.text(cx), "aaaa\nbbbb");
6423        view.change_selections(None, cx, |s| {
6424            s.select_ranges([
6425                Point::new(0, 0)..Point::new(0, 0),
6426                Point::new(1, 0)..Point::new(1, 0),
6427            ])
6428        });
6429
6430        view.handle_input("X", cx);
6431        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
6432        assert_eq!(
6433            view.selections.ranges(cx),
6434            [
6435                Point::new(0, 1)..Point::new(0, 1),
6436                Point::new(1, 1)..Point::new(1, 1),
6437            ]
6438        );
6439
6440        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
6441        view.change_selections(None, cx, |s| {
6442            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
6443        });
6444        view.backspace(&Default::default(), cx);
6445        assert_eq!(view.text(cx), "Xa\nbbb");
6446        assert_eq!(
6447            view.selections.ranges(cx),
6448            [Point::new(1, 0)..Point::new(1, 0)]
6449        );
6450
6451        view.change_selections(None, cx, |s| {
6452            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
6453        });
6454        view.backspace(&Default::default(), cx);
6455        assert_eq!(view.text(cx), "X\nbb");
6456        assert_eq!(
6457            view.selections.ranges(cx),
6458            [Point::new(0, 1)..Point::new(0, 1)]
6459        );
6460    });
6461}
6462
6463#[gpui::test]
6464fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
6465    init_test(cx, |_| {});
6466
6467    let markers = vec![('[', ']').into(), ('(', ')').into()];
6468    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
6469        indoc! {"
6470            [aaaa
6471            (bbbb]
6472            cccc)",
6473        },
6474        markers.clone(),
6475    );
6476    let excerpt_ranges = markers.into_iter().map(|marker| {
6477        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
6478        ExcerptRange {
6479            context,
6480            primary: None,
6481        }
6482    });
6483    let buffer = cx.new_model(|cx| {
6484        Buffer::new(
6485            0,
6486            BufferId::new(cx.entity_id().as_u64()).unwrap(),
6487            initial_text,
6488        )
6489    });
6490    let multibuffer = cx.new_model(|cx| {
6491        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
6492        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
6493        multibuffer
6494    });
6495
6496    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
6497    _ = view.update(cx, |view, cx| {
6498        let (expected_text, selection_ranges) = marked_text_ranges(
6499            indoc! {"
6500                aaaa
6501                bˇbbb
6502                bˇbbˇb
6503                cccc"
6504            },
6505            true,
6506        );
6507        assert_eq!(view.text(cx), expected_text);
6508        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
6509
6510        view.handle_input("X", cx);
6511
6512        let (expected_text, expected_selections) = marked_text_ranges(
6513            indoc! {"
6514                aaaa
6515                bXˇbbXb
6516                bXˇbbXˇb
6517                cccc"
6518            },
6519            false,
6520        );
6521        assert_eq!(view.text(cx), expected_text);
6522        assert_eq!(view.selections.ranges(cx), expected_selections);
6523
6524        view.newline(&Newline, cx);
6525        let (expected_text, expected_selections) = marked_text_ranges(
6526            indoc! {"
6527                aaaa
6528                bX
6529                ˇbbX
6530                b
6531                bX
6532                ˇbbX
6533                ˇb
6534                cccc"
6535            },
6536            false,
6537        );
6538        assert_eq!(view.text(cx), expected_text);
6539        assert_eq!(view.selections.ranges(cx), expected_selections);
6540    });
6541}
6542
6543#[gpui::test]
6544fn test_refresh_selections(cx: &mut TestAppContext) {
6545    init_test(cx, |_| {});
6546
6547    let buffer = cx.new_model(|cx| {
6548        Buffer::new(
6549            0,
6550            BufferId::new(cx.entity_id().as_u64()).unwrap(),
6551            sample_text(3, 4, 'a'),
6552        )
6553    });
6554    let mut excerpt1_id = None;
6555    let multibuffer = cx.new_model(|cx| {
6556        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
6557        excerpt1_id = multibuffer
6558            .push_excerpts(
6559                buffer.clone(),
6560                [
6561                    ExcerptRange {
6562                        context: Point::new(0, 0)..Point::new(1, 4),
6563                        primary: None,
6564                    },
6565                    ExcerptRange {
6566                        context: Point::new(1, 0)..Point::new(2, 4),
6567                        primary: None,
6568                    },
6569                ],
6570                cx,
6571            )
6572            .into_iter()
6573            .next();
6574        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
6575        multibuffer
6576    });
6577
6578    let editor = cx.add_window(|cx| {
6579        let mut editor = build_editor(multibuffer.clone(), cx);
6580        let snapshot = editor.snapshot(cx);
6581        editor.change_selections(None, cx, |s| {
6582            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
6583        });
6584        editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
6585        assert_eq!(
6586            editor.selections.ranges(cx),
6587            [
6588                Point::new(1, 3)..Point::new(1, 3),
6589                Point::new(2, 1)..Point::new(2, 1),
6590            ]
6591        );
6592        editor
6593    });
6594
6595    // Refreshing selections is a no-op when excerpts haven't changed.
6596    _ = editor.update(cx, |editor, cx| {
6597        editor.change_selections(None, cx, |s| s.refresh());
6598        assert_eq!(
6599            editor.selections.ranges(cx),
6600            [
6601                Point::new(1, 3)..Point::new(1, 3),
6602                Point::new(2, 1)..Point::new(2, 1),
6603            ]
6604        );
6605    });
6606
6607    _ = multibuffer.update(cx, |multibuffer, cx| {
6608        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
6609    });
6610    _ = editor.update(cx, |editor, cx| {
6611        // Removing an excerpt causes the first selection to become degenerate.
6612        assert_eq!(
6613            editor.selections.ranges(cx),
6614            [
6615                Point::new(0, 0)..Point::new(0, 0),
6616                Point::new(0, 1)..Point::new(0, 1)
6617            ]
6618        );
6619
6620        // Refreshing selections will relocate the first selection to the original buffer
6621        // location.
6622        editor.change_selections(None, cx, |s| s.refresh());
6623        assert_eq!(
6624            editor.selections.ranges(cx),
6625            [
6626                Point::new(0, 1)..Point::new(0, 1),
6627                Point::new(0, 3)..Point::new(0, 3)
6628            ]
6629        );
6630        assert!(editor.selections.pending_anchor().is_some());
6631    });
6632}
6633
6634#[gpui::test]
6635fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
6636    init_test(cx, |_| {});
6637
6638    let buffer = cx.new_model(|cx| {
6639        Buffer::new(
6640            0,
6641            BufferId::new(cx.entity_id().as_u64()).unwrap(),
6642            sample_text(3, 4, 'a'),
6643        )
6644    });
6645    let mut excerpt1_id = None;
6646    let multibuffer = cx.new_model(|cx| {
6647        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
6648        excerpt1_id = multibuffer
6649            .push_excerpts(
6650                buffer.clone(),
6651                [
6652                    ExcerptRange {
6653                        context: Point::new(0, 0)..Point::new(1, 4),
6654                        primary: None,
6655                    },
6656                    ExcerptRange {
6657                        context: Point::new(1, 0)..Point::new(2, 4),
6658                        primary: None,
6659                    },
6660                ],
6661                cx,
6662            )
6663            .into_iter()
6664            .next();
6665        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
6666        multibuffer
6667    });
6668
6669    let editor = cx.add_window(|cx| {
6670        let mut editor = build_editor(multibuffer.clone(), cx);
6671        let snapshot = editor.snapshot(cx);
6672        editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
6673        assert_eq!(
6674            editor.selections.ranges(cx),
6675            [Point::new(1, 3)..Point::new(1, 3)]
6676        );
6677        editor
6678    });
6679
6680    _ = multibuffer.update(cx, |multibuffer, cx| {
6681        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
6682    });
6683    _ = editor.update(cx, |editor, cx| {
6684        assert_eq!(
6685            editor.selections.ranges(cx),
6686            [Point::new(0, 0)..Point::new(0, 0)]
6687        );
6688
6689        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
6690        editor.change_selections(None, cx, |s| s.refresh());
6691        assert_eq!(
6692            editor.selections.ranges(cx),
6693            [Point::new(0, 3)..Point::new(0, 3)]
6694        );
6695        assert!(editor.selections.pending_anchor().is_some());
6696    });
6697}
6698
6699#[gpui::test]
6700async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
6701    init_test(cx, |_| {});
6702
6703    let language = Arc::new(
6704        Language::new(
6705            LanguageConfig {
6706                brackets: BracketPairConfig {
6707                    pairs: vec![
6708                        BracketPair {
6709                            start: "{".to_string(),
6710                            end: "}".to_string(),
6711                            close: true,
6712                            newline: true,
6713                        },
6714                        BracketPair {
6715                            start: "/* ".to_string(),
6716                            end: " */".to_string(),
6717                            close: true,
6718                            newline: true,
6719                        },
6720                    ],
6721                    ..Default::default()
6722                },
6723                ..Default::default()
6724            },
6725            Some(tree_sitter_rust::language()),
6726        )
6727        .with_indents_query("")
6728        .unwrap(),
6729    );
6730
6731    let text = concat!(
6732        "{   }\n",     //
6733        "  x\n",       //
6734        "  /*   */\n", //
6735        "x\n",         //
6736        "{{} }\n",     //
6737    );
6738
6739    let buffer = cx.new_model(|cx| {
6740        Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
6741            .with_language(language, cx)
6742    });
6743    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
6744    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
6745    view.condition::<crate::EditorEvent>(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
6746        .await;
6747
6748    _ = view.update(cx, |view, cx| {
6749        view.change_selections(None, cx, |s| {
6750            s.select_display_ranges([
6751                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
6752                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
6753                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
6754            ])
6755        });
6756        view.newline(&Newline, cx);
6757
6758        assert_eq!(
6759            view.buffer().read(cx).read(cx).text(),
6760            concat!(
6761                "{ \n",    // Suppress rustfmt
6762                "\n",      //
6763                "}\n",     //
6764                "  x\n",   //
6765                "  /* \n", //
6766                "  \n",    //
6767                "  */\n",  //
6768                "x\n",     //
6769                "{{} \n",  //
6770                "}\n",     //
6771            )
6772        );
6773    });
6774}
6775
6776#[gpui::test]
6777fn test_highlighted_ranges(cx: &mut TestAppContext) {
6778    init_test(cx, |_| {});
6779
6780    let editor = cx.add_window(|cx| {
6781        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
6782        build_editor(buffer.clone(), cx)
6783    });
6784
6785    _ = editor.update(cx, |editor, cx| {
6786        struct Type1;
6787        struct Type2;
6788
6789        let buffer = editor.buffer.read(cx).snapshot(cx);
6790
6791        let anchor_range =
6792            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
6793
6794        editor.highlight_background::<Type1>(
6795            vec![
6796                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
6797                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
6798                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
6799                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
6800            ],
6801            |_| Hsla::red(),
6802            cx,
6803        );
6804        editor.highlight_background::<Type2>(
6805            vec![
6806                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
6807                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
6808                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
6809                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
6810            ],
6811            |_| Hsla::green(),
6812            cx,
6813        );
6814
6815        let snapshot = editor.snapshot(cx);
6816        let mut highlighted_ranges = editor.background_highlights_in_range(
6817            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
6818            &snapshot,
6819            cx.theme().colors(),
6820        );
6821        // Enforce a consistent ordering based on color without relying on the ordering of the
6822        // highlight's `TypeId` which is non-executor.
6823        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
6824        assert_eq!(
6825            highlighted_ranges,
6826            &[
6827                (
6828                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
6829                    Hsla::red(),
6830                ),
6831                (
6832                    DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
6833                    Hsla::red(),
6834                ),
6835                (
6836                    DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
6837                    Hsla::green(),
6838                ),
6839                (
6840                    DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
6841                    Hsla::green(),
6842                ),
6843            ]
6844        );
6845        assert_eq!(
6846            editor.background_highlights_in_range(
6847                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
6848                &snapshot,
6849                cx.theme().colors(),
6850            ),
6851            &[(
6852                DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
6853                Hsla::red(),
6854            )]
6855        );
6856    });
6857}
6858
6859#[gpui::test]
6860async fn test_following(cx: &mut gpui::TestAppContext) {
6861    init_test(cx, |_| {});
6862
6863    let fs = FakeFs::new(cx.executor());
6864    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
6865
6866    let buffer = project.update(cx, |project, cx| {
6867        let buffer = project
6868            .create_buffer(&sample_text(16, 8, 'a'), None, cx)
6869            .unwrap();
6870        cx.new_model(|cx| MultiBuffer::singleton(buffer, cx))
6871    });
6872    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
6873    let follower = cx.update(|cx| {
6874        cx.open_window(
6875            WindowOptions {
6876                bounds: Some(Bounds::from_corners(
6877                    gpui::Point::new(0_f64.into(), 0_f64.into()),
6878                    gpui::Point::new(10_f64.into(), 80_f64.into()),
6879                )),
6880                ..Default::default()
6881            },
6882            |cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
6883        )
6884    });
6885
6886    let is_still_following = Rc::new(RefCell::new(true));
6887    let follower_edit_event_count = Rc::new(RefCell::new(0));
6888    let pending_update = Rc::new(RefCell::new(None));
6889    _ = follower.update(cx, {
6890        let update = pending_update.clone();
6891        let is_still_following = is_still_following.clone();
6892        let follower_edit_event_count = follower_edit_event_count.clone();
6893        |_, cx| {
6894            cx.subscribe(
6895                &leader.root_view(cx).unwrap(),
6896                move |_, leader, event, cx| {
6897                    leader
6898                        .read(cx)
6899                        .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
6900                },
6901            )
6902            .detach();
6903
6904            cx.subscribe(
6905                &follower.root_view(cx).unwrap(),
6906                move |_, _, event: &EditorEvent, _cx| {
6907                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
6908                        *is_still_following.borrow_mut() = false;
6909                    }
6910
6911                    if let EditorEvent::BufferEdited = event {
6912                        *follower_edit_event_count.borrow_mut() += 1;
6913                    }
6914                },
6915            )
6916            .detach();
6917        }
6918    });
6919
6920    // Update the selections only
6921    _ = leader.update(cx, |leader, cx| {
6922        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
6923    });
6924    follower
6925        .update(cx, |follower, cx| {
6926            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6927        })
6928        .unwrap()
6929        .await
6930        .unwrap();
6931    _ = follower.update(cx, |follower, cx| {
6932        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
6933    });
6934    assert_eq!(*is_still_following.borrow(), true);
6935    assert_eq!(*follower_edit_event_count.borrow(), 0);
6936
6937    // Update the scroll position only
6938    _ = leader.update(cx, |leader, cx| {
6939        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
6940    });
6941    follower
6942        .update(cx, |follower, cx| {
6943            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6944        })
6945        .unwrap()
6946        .await
6947        .unwrap();
6948    assert_eq!(
6949        follower
6950            .update(cx, |follower, cx| follower.scroll_position(cx))
6951            .unwrap(),
6952        gpui::Point::new(1.5, 3.5)
6953    );
6954    assert_eq!(*is_still_following.borrow(), true);
6955    assert_eq!(*follower_edit_event_count.borrow(), 0);
6956
6957    // Update the selections and scroll position. The follower's scroll position is updated
6958    // via autoscroll, not via the leader's exact scroll position.
6959    _ = leader.update(cx, |leader, cx| {
6960        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
6961        leader.request_autoscroll(Autoscroll::newest(), cx);
6962        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), cx);
6963    });
6964    follower
6965        .update(cx, |follower, cx| {
6966            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6967        })
6968        .unwrap()
6969        .await
6970        .unwrap();
6971    _ = follower.update(cx, |follower, cx| {
6972        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
6973        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
6974    });
6975    assert_eq!(*is_still_following.borrow(), true);
6976
6977    // Creating a pending selection that precedes another selection
6978    _ = leader.update(cx, |leader, cx| {
6979        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
6980        leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
6981    });
6982    follower
6983        .update(cx, |follower, cx| {
6984            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
6985        })
6986        .unwrap()
6987        .await
6988        .unwrap();
6989    _ = follower.update(cx, |follower, cx| {
6990        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
6991    });
6992    assert_eq!(*is_still_following.borrow(), true);
6993
6994    // Extend the pending selection so that it surrounds another selection
6995    _ = leader.update(cx, |leader, cx| {
6996        leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
6997    });
6998    follower
6999        .update(cx, |follower, cx| {
7000            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
7001        })
7002        .unwrap()
7003        .await
7004        .unwrap();
7005    _ = follower.update(cx, |follower, cx| {
7006        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
7007    });
7008
7009    // Scrolling locally breaks the follow
7010    _ = follower.update(cx, |follower, cx| {
7011        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
7012        follower.set_scroll_anchor(
7013            ScrollAnchor {
7014                anchor: top_anchor,
7015                offset: gpui::Point::new(0.0, 0.5),
7016            },
7017            cx,
7018        );
7019    });
7020    assert_eq!(*is_still_following.borrow(), false);
7021}
7022
7023#[gpui::test]
7024async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
7025    init_test(cx, |_| {});
7026
7027    let fs = FakeFs::new(cx.executor());
7028    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
7029    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
7030    let pane = workspace
7031        .update(cx, |workspace, _| workspace.active_pane().clone())
7032        .unwrap();
7033
7034    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
7035
7036    let leader = pane.update(cx, |_, cx| {
7037        let multibuffer = cx.new_model(|_| MultiBuffer::new(0, ReadWrite));
7038        cx.new_view(|cx| build_editor(multibuffer.clone(), cx))
7039    });
7040
7041    // Start following the editor when it has no excerpts.
7042    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
7043    let follower_1 = cx
7044        .update_window(*workspace.deref(), |_, cx| {
7045            Editor::from_state_proto(
7046                pane.clone(),
7047                workspace.root_view(cx).unwrap(),
7048                ViewId {
7049                    creator: Default::default(),
7050                    id: 0,
7051                },
7052                &mut state_message,
7053                cx,
7054            )
7055        })
7056        .unwrap()
7057        .unwrap()
7058        .await
7059        .unwrap();
7060
7061    let update_message = Rc::new(RefCell::new(None));
7062    follower_1.update(cx, {
7063        let update = update_message.clone();
7064        |_, cx| {
7065            cx.subscribe(&leader, move |_, leader, event, cx| {
7066                leader
7067                    .read(cx)
7068                    .add_event_to_update_proto(event, &mut update.borrow_mut(), cx);
7069            })
7070            .detach();
7071        }
7072    });
7073
7074    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
7075        (
7076            project
7077                .create_buffer("abc\ndef\nghi\njkl\n", None, cx)
7078                .unwrap(),
7079            project
7080                .create_buffer("mno\npqr\nstu\nvwx\n", None, cx)
7081                .unwrap(),
7082        )
7083    });
7084
7085    // Insert some excerpts.
7086    _ = leader.update(cx, |leader, cx| {
7087        leader.buffer.update(cx, |multibuffer, cx| {
7088            let excerpt_ids = multibuffer.push_excerpts(
7089                buffer_1.clone(),
7090                [
7091                    ExcerptRange {
7092                        context: 1..6,
7093                        primary: None,
7094                    },
7095                    ExcerptRange {
7096                        context: 12..15,
7097                        primary: None,
7098                    },
7099                    ExcerptRange {
7100                        context: 0..3,
7101                        primary: None,
7102                    },
7103                ],
7104                cx,
7105            );
7106            multibuffer.insert_excerpts_after(
7107                excerpt_ids[0],
7108                buffer_2.clone(),
7109                [
7110                    ExcerptRange {
7111                        context: 8..12,
7112                        primary: None,
7113                    },
7114                    ExcerptRange {
7115                        context: 0..6,
7116                        primary: None,
7117                    },
7118                ],
7119                cx,
7120            );
7121        });
7122    });
7123
7124    // Apply the update of adding the excerpts.
7125    follower_1
7126        .update(cx, |follower, cx| {
7127            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
7128        })
7129        .await
7130        .unwrap();
7131    assert_eq!(
7132        follower_1.update(cx, |editor, cx| editor.text(cx)),
7133        leader.update(cx, |editor, cx| editor.text(cx))
7134    );
7135    update_message.borrow_mut().take();
7136
7137    // Start following separately after it already has excerpts.
7138    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
7139    let follower_2 = cx
7140        .update_window(*workspace.deref(), |_, cx| {
7141            Editor::from_state_proto(
7142                pane.clone(),
7143                workspace.root_view(cx).unwrap().clone(),
7144                ViewId {
7145                    creator: Default::default(),
7146                    id: 0,
7147                },
7148                &mut state_message,
7149                cx,
7150            )
7151        })
7152        .unwrap()
7153        .unwrap()
7154        .await
7155        .unwrap();
7156    assert_eq!(
7157        follower_2.update(cx, |editor, cx| editor.text(cx)),
7158        leader.update(cx, |editor, cx| editor.text(cx))
7159    );
7160
7161    // Remove some excerpts.
7162    _ = leader.update(cx, |leader, cx| {
7163        leader.buffer.update(cx, |multibuffer, cx| {
7164            let excerpt_ids = multibuffer.excerpt_ids();
7165            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
7166            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
7167        });
7168    });
7169
7170    // Apply the update of removing the excerpts.
7171    follower_1
7172        .update(cx, |follower, cx| {
7173            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
7174        })
7175        .await
7176        .unwrap();
7177    follower_2
7178        .update(cx, |follower, cx| {
7179            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
7180        })
7181        .await
7182        .unwrap();
7183    update_message.borrow_mut().take();
7184    assert_eq!(
7185        follower_1.update(cx, |editor, cx| editor.text(cx)),
7186        leader.update(cx, |editor, cx| editor.text(cx))
7187    );
7188}
7189
7190#[gpui::test]
7191async fn go_to_prev_overlapping_diagnostic(
7192    executor: BackgroundExecutor,
7193    cx: &mut gpui::TestAppContext,
7194) {
7195    init_test(cx, |_| {});
7196
7197    let mut cx = EditorTestContext::new(cx).await;
7198    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
7199
7200    cx.set_state(indoc! {"
7201        ˇfn func(abc def: i32) -> u32 {
7202        }
7203    "});
7204
7205    _ = cx.update(|cx| {
7206        _ = project.update(cx, |project, cx| {
7207            project
7208                .update_diagnostics(
7209                    LanguageServerId(0),
7210                    lsp::PublishDiagnosticsParams {
7211                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
7212                        version: None,
7213                        diagnostics: vec![
7214                            lsp::Diagnostic {
7215                                range: lsp::Range::new(
7216                                    lsp::Position::new(0, 11),
7217                                    lsp::Position::new(0, 12),
7218                                ),
7219                                severity: Some(lsp::DiagnosticSeverity::ERROR),
7220                                ..Default::default()
7221                            },
7222                            lsp::Diagnostic {
7223                                range: lsp::Range::new(
7224                                    lsp::Position::new(0, 12),
7225                                    lsp::Position::new(0, 15),
7226                                ),
7227                                severity: Some(lsp::DiagnosticSeverity::ERROR),
7228                                ..Default::default()
7229                            },
7230                            lsp::Diagnostic {
7231                                range: lsp::Range::new(
7232                                    lsp::Position::new(0, 25),
7233                                    lsp::Position::new(0, 28),
7234                                ),
7235                                severity: Some(lsp::DiagnosticSeverity::ERROR),
7236                                ..Default::default()
7237                            },
7238                        ],
7239                    },
7240                    &[],
7241                    cx,
7242                )
7243                .unwrap()
7244        });
7245    });
7246
7247    executor.run_until_parked();
7248
7249    cx.update_editor(|editor, cx| {
7250        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
7251    });
7252
7253    cx.assert_editor_state(indoc! {"
7254        fn func(abc def: i32) -> ˇu32 {
7255        }
7256    "});
7257
7258    cx.update_editor(|editor, cx| {
7259        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
7260    });
7261
7262    cx.assert_editor_state(indoc! {"
7263        fn func(abc ˇdef: i32) -> u32 {
7264        }
7265    "});
7266
7267    cx.update_editor(|editor, cx| {
7268        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
7269    });
7270
7271    cx.assert_editor_state(indoc! {"
7272        fn func(abcˇ def: i32) -> u32 {
7273        }
7274    "});
7275
7276    cx.update_editor(|editor, cx| {
7277        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
7278    });
7279
7280    cx.assert_editor_state(indoc! {"
7281        fn func(abc def: i32) -> ˇu32 {
7282        }
7283    "});
7284}
7285
7286#[gpui::test]
7287async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
7288    init_test(cx, |_| {});
7289
7290    let mut cx = EditorTestContext::new(cx).await;
7291
7292    let diff_base = r#"
7293        use some::mod;
7294
7295        const A: u32 = 42;
7296
7297        fn main() {
7298            println!("hello");
7299
7300            println!("world");
7301        }
7302        "#
7303    .unindent();
7304
7305    // Edits are modified, removed, modified, added
7306    cx.set_state(
7307        &r#"
7308        use some::modified;
7309
7310        ˇ
7311        fn main() {
7312            println!("hello there");
7313
7314            println!("around the");
7315            println!("world");
7316        }
7317        "#
7318        .unindent(),
7319    );
7320
7321    cx.set_diff_base(Some(&diff_base));
7322    executor.run_until_parked();
7323
7324    cx.update_editor(|editor, cx| {
7325        //Wrap around the bottom of the buffer
7326        for _ in 0..3 {
7327            editor.go_to_hunk(&GoToHunk, cx);
7328        }
7329    });
7330
7331    cx.assert_editor_state(
7332        &r#"
7333        ˇuse some::modified;
7334
7335
7336        fn main() {
7337            println!("hello there");
7338
7339            println!("around the");
7340            println!("world");
7341        }
7342        "#
7343        .unindent(),
7344    );
7345
7346    cx.update_editor(|editor, cx| {
7347        //Wrap around the top of the buffer
7348        for _ in 0..2 {
7349            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
7350        }
7351    });
7352
7353    cx.assert_editor_state(
7354        &r#"
7355        use some::modified;
7356
7357
7358        fn main() {
7359        ˇ    println!("hello there");
7360
7361            println!("around the");
7362            println!("world");
7363        }
7364        "#
7365        .unindent(),
7366    );
7367
7368    cx.update_editor(|editor, cx| {
7369        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
7370    });
7371
7372    cx.assert_editor_state(
7373        &r#"
7374        use some::modified;
7375
7376        ˇ
7377        fn main() {
7378            println!("hello there");
7379
7380            println!("around the");
7381            println!("world");
7382        }
7383        "#
7384        .unindent(),
7385    );
7386
7387    cx.update_editor(|editor, cx| {
7388        for _ in 0..3 {
7389            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
7390        }
7391    });
7392
7393    cx.assert_editor_state(
7394        &r#"
7395        use some::modified;
7396
7397
7398        fn main() {
7399        ˇ    println!("hello there");
7400
7401            println!("around the");
7402            println!("world");
7403        }
7404        "#
7405        .unindent(),
7406    );
7407
7408    cx.update_editor(|editor, cx| {
7409        editor.fold(&Fold, cx);
7410
7411        //Make sure that the fold only gets one hunk
7412        for _ in 0..4 {
7413            editor.go_to_hunk(&GoToHunk, cx);
7414        }
7415    });
7416
7417    cx.assert_editor_state(
7418        &r#"
7419        ˇuse some::modified;
7420
7421
7422        fn main() {
7423            println!("hello there");
7424
7425            println!("around the");
7426            println!("world");
7427        }
7428        "#
7429        .unindent(),
7430    );
7431}
7432
7433#[test]
7434fn test_split_words() {
7435    fn split(text: &str) -> Vec<&str> {
7436        split_words(text).collect()
7437    }
7438
7439    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
7440    assert_eq!(split("hello_world"), &["hello_", "world"]);
7441    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
7442    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
7443    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
7444    assert_eq!(split("helloworld"), &["helloworld"]);
7445
7446    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
7447}
7448
7449#[gpui::test]
7450async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
7451    init_test(cx, |_| {});
7452
7453    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
7454    let mut assert = |before, after| {
7455        let _state_context = cx.set_state(before);
7456        cx.update_editor(|editor, cx| {
7457            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
7458        });
7459        cx.assert_editor_state(after);
7460    };
7461
7462    // Outside bracket jumps to outside of matching bracket
7463    assert("console.logˇ(var);", "console.log(var)ˇ;");
7464    assert("console.log(var)ˇ;", "console.logˇ(var);");
7465
7466    // Inside bracket jumps to inside of matching bracket
7467    assert("console.log(ˇvar);", "console.log(varˇ);");
7468    assert("console.log(varˇ);", "console.log(ˇvar);");
7469
7470    // When outside a bracket and inside, favor jumping to the inside bracket
7471    assert(
7472        "console.log('foo', [1, 2, 3]ˇ);",
7473        "console.log(ˇ'foo', [1, 2, 3]);",
7474    );
7475    assert(
7476        "console.log(ˇ'foo', [1, 2, 3]);",
7477        "console.log('foo', [1, 2, 3]ˇ);",
7478    );
7479
7480    // Bias forward if two options are equally likely
7481    assert(
7482        "let result = curried_fun()ˇ();",
7483        "let result = curried_fun()()ˇ;",
7484    );
7485
7486    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
7487    assert(
7488        indoc! {"
7489            function test() {
7490                console.log('test')ˇ
7491            }"},
7492        indoc! {"
7493            function test() {
7494                console.logˇ('test')
7495            }"},
7496    );
7497}
7498
7499#[gpui::test(iterations = 10)]
7500async fn test_copilot(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
7501    // flaky
7502    init_test(cx, |_| {});
7503
7504    let (copilot, copilot_lsp) = Copilot::fake(cx);
7505    _ = cx.update(|cx| Copilot::set_global(copilot, cx));
7506    let mut cx = EditorLspTestContext::new_rust(
7507        lsp::ServerCapabilities {
7508            completion_provider: Some(lsp::CompletionOptions {
7509                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
7510                ..Default::default()
7511            }),
7512            ..Default::default()
7513        },
7514        cx,
7515    )
7516    .await;
7517
7518    // When inserting, ensure autocompletion is favored over Copilot suggestions.
7519    cx.set_state(indoc! {"
7520        oneˇ
7521        two
7522        three
7523    "});
7524    cx.simulate_keystroke(".");
7525    let _ = handle_completion_request(
7526        &mut cx,
7527        indoc! {"
7528            one.|<>
7529            two
7530            three
7531        "},
7532        vec!["completion_a", "completion_b"],
7533    );
7534    handle_copilot_completion_request(
7535        &copilot_lsp,
7536        vec![copilot::request::Completion {
7537            text: "one.copilot1".into(),
7538            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
7539            ..Default::default()
7540        }],
7541        vec![],
7542    );
7543    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7544    cx.update_editor(|editor, cx| {
7545        assert!(editor.context_menu_visible());
7546        assert!(!editor.has_active_copilot_suggestion(cx));
7547
7548        // Confirming a completion inserts it and hides the context menu, without showing
7549        // the copilot suggestion afterwards.
7550        editor
7551            .confirm_completion(&Default::default(), cx)
7552            .unwrap()
7553            .detach();
7554        assert!(!editor.context_menu_visible());
7555        assert!(!editor.has_active_copilot_suggestion(cx));
7556        assert_eq!(editor.text(cx), "one.completion_a\ntwo\nthree\n");
7557        assert_eq!(editor.display_text(cx), "one.completion_a\ntwo\nthree\n");
7558    });
7559
7560    // Ensure Copilot suggestions are shown right away if no autocompletion is available.
7561    cx.set_state(indoc! {"
7562        oneˇ
7563        two
7564        three
7565    "});
7566    cx.simulate_keystroke(".");
7567    let _ = handle_completion_request(
7568        &mut cx,
7569        indoc! {"
7570            one.|<>
7571            two
7572            three
7573        "},
7574        vec![],
7575    );
7576    handle_copilot_completion_request(
7577        &copilot_lsp,
7578        vec![copilot::request::Completion {
7579            text: "one.copilot1".into(),
7580            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
7581            ..Default::default()
7582        }],
7583        vec![],
7584    );
7585    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7586    cx.update_editor(|editor, cx| {
7587        assert!(!editor.context_menu_visible());
7588        assert!(editor.has_active_copilot_suggestion(cx));
7589        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
7590        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
7591    });
7592
7593    // Reset editor, and ensure autocompletion is still favored over Copilot suggestions.
7594    cx.set_state(indoc! {"
7595        oneˇ
7596        two
7597        three
7598    "});
7599    cx.simulate_keystroke(".");
7600    let _ = handle_completion_request(
7601        &mut cx,
7602        indoc! {"
7603            one.|<>
7604            two
7605            three
7606        "},
7607        vec!["completion_a", "completion_b"],
7608    );
7609    handle_copilot_completion_request(
7610        &copilot_lsp,
7611        vec![copilot::request::Completion {
7612            text: "one.copilot1".into(),
7613            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
7614            ..Default::default()
7615        }],
7616        vec![],
7617    );
7618    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7619    cx.update_editor(|editor, cx| {
7620        assert!(editor.context_menu_visible());
7621        assert!(!editor.has_active_copilot_suggestion(cx));
7622
7623        // When hiding the context menu, the Copilot suggestion becomes visible.
7624        editor.hide_context_menu(cx);
7625        assert!(!editor.context_menu_visible());
7626        assert!(editor.has_active_copilot_suggestion(cx));
7627        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
7628        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
7629    });
7630
7631    // Ensure existing completion is interpolated when inserting again.
7632    cx.simulate_keystroke("c");
7633    executor.run_until_parked();
7634    cx.update_editor(|editor, cx| {
7635        assert!(!editor.context_menu_visible());
7636        assert!(editor.has_active_copilot_suggestion(cx));
7637        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
7638        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7639    });
7640
7641    // After debouncing, new Copilot completions should be requested.
7642    handle_copilot_completion_request(
7643        &copilot_lsp,
7644        vec![copilot::request::Completion {
7645            text: "one.copilot2".into(),
7646            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 5)),
7647            ..Default::default()
7648        }],
7649        vec![],
7650    );
7651    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7652    cx.update_editor(|editor, cx| {
7653        assert!(!editor.context_menu_visible());
7654        assert!(editor.has_active_copilot_suggestion(cx));
7655        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7656        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7657
7658        // Canceling should remove the active Copilot suggestion.
7659        editor.cancel(&Default::default(), cx);
7660        assert!(!editor.has_active_copilot_suggestion(cx));
7661        assert_eq!(editor.display_text(cx), "one.c\ntwo\nthree\n");
7662        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7663
7664        // After canceling, tabbing shouldn't insert the previously shown suggestion.
7665        editor.tab(&Default::default(), cx);
7666        assert!(!editor.has_active_copilot_suggestion(cx));
7667        assert_eq!(editor.display_text(cx), "one.c   \ntwo\nthree\n");
7668        assert_eq!(editor.text(cx), "one.c   \ntwo\nthree\n");
7669
7670        // When undoing the previously active suggestion is shown again.
7671        editor.undo(&Default::default(), cx);
7672        assert!(editor.has_active_copilot_suggestion(cx));
7673        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7674        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
7675    });
7676
7677    // If an edit occurs outside of this editor, the suggestion is still correctly interpolated.
7678    cx.update_buffer(|buffer, cx| buffer.edit([(5..5, "o")], None, cx));
7679    cx.update_editor(|editor, cx| {
7680        assert!(editor.has_active_copilot_suggestion(cx));
7681        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7682        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
7683
7684        // Tabbing when there is an active suggestion inserts it.
7685        editor.tab(&Default::default(), cx);
7686        assert!(!editor.has_active_copilot_suggestion(cx));
7687        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7688        assert_eq!(editor.text(cx), "one.copilot2\ntwo\nthree\n");
7689
7690        // When undoing the previously active suggestion is shown again.
7691        editor.undo(&Default::default(), cx);
7692        assert!(editor.has_active_copilot_suggestion(cx));
7693        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
7694        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
7695
7696        // Hide suggestion.
7697        editor.cancel(&Default::default(), cx);
7698        assert!(!editor.has_active_copilot_suggestion(cx));
7699        assert_eq!(editor.display_text(cx), "one.co\ntwo\nthree\n");
7700        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
7701    });
7702
7703    // If an edit occurs outside of this editor but no suggestion is being shown,
7704    // we won't make it visible.
7705    cx.update_buffer(|buffer, cx| buffer.edit([(6..6, "p")], None, cx));
7706    cx.update_editor(|editor, cx| {
7707        assert!(!editor.has_active_copilot_suggestion(cx));
7708        assert_eq!(editor.display_text(cx), "one.cop\ntwo\nthree\n");
7709        assert_eq!(editor.text(cx), "one.cop\ntwo\nthree\n");
7710    });
7711
7712    // Reset the editor to verify how suggestions behave when tabbing on leading indentation.
7713    cx.update_editor(|editor, cx| {
7714        editor.set_text("fn foo() {\n  \n}", cx);
7715        editor.change_selections(None, cx, |s| {
7716            s.select_ranges([Point::new(1, 2)..Point::new(1, 2)])
7717        });
7718    });
7719    handle_copilot_completion_request(
7720        &copilot_lsp,
7721        vec![copilot::request::Completion {
7722            text: "    let x = 4;".into(),
7723            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
7724            ..Default::default()
7725        }],
7726        vec![],
7727    );
7728
7729    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
7730    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7731    cx.update_editor(|editor, cx| {
7732        assert!(editor.has_active_copilot_suggestion(cx));
7733        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
7734        assert_eq!(editor.text(cx), "fn foo() {\n  \n}");
7735
7736        // Tabbing inside of leading whitespace inserts indentation without accepting the suggestion.
7737        editor.tab(&Default::default(), cx);
7738        assert!(editor.has_active_copilot_suggestion(cx));
7739        assert_eq!(editor.text(cx), "fn foo() {\n    \n}");
7740        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
7741
7742        // Tabbing again accepts the suggestion.
7743        editor.tab(&Default::default(), cx);
7744        assert!(!editor.has_active_copilot_suggestion(cx));
7745        assert_eq!(editor.text(cx), "fn foo() {\n    let x = 4;\n}");
7746        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
7747    });
7748}
7749
7750#[gpui::test(iterations = 10)]
7751async fn test_accept_partial_copilot_suggestion(
7752    executor: BackgroundExecutor,
7753    cx: &mut gpui::TestAppContext,
7754) {
7755    // flaky
7756    init_test(cx, |_| {});
7757
7758    let (copilot, copilot_lsp) = Copilot::fake(cx);
7759    _ = cx.update(|cx| Copilot::set_global(copilot, cx));
7760    let mut cx = EditorLspTestContext::new_rust(
7761        lsp::ServerCapabilities {
7762            completion_provider: Some(lsp::CompletionOptions {
7763                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
7764                ..Default::default()
7765            }),
7766            ..Default::default()
7767        },
7768        cx,
7769    )
7770    .await;
7771
7772    // Setup the editor with a completion request.
7773    cx.set_state(indoc! {"
7774        oneˇ
7775        two
7776        three
7777    "});
7778    cx.simulate_keystroke(".");
7779    let _ = handle_completion_request(
7780        &mut cx,
7781        indoc! {"
7782            one.|<>
7783            two
7784            three
7785        "},
7786        vec![],
7787    );
7788    handle_copilot_completion_request(
7789        &copilot_lsp,
7790        vec![copilot::request::Completion {
7791            text: "one.copilot1".into(),
7792            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
7793            ..Default::default()
7794        }],
7795        vec![],
7796    );
7797    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7798    cx.update_editor(|editor, cx| {
7799        assert!(editor.has_active_copilot_suggestion(cx));
7800
7801        // Accepting the first word of the suggestion should only accept the first word and still show the rest.
7802        editor.accept_partial_copilot_suggestion(&Default::default(), cx);
7803        assert!(editor.has_active_copilot_suggestion(cx));
7804        assert_eq!(editor.text(cx), "one.copilot\ntwo\nthree\n");
7805        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
7806
7807        // Accepting next word should accept the non-word and copilot suggestion should be gone
7808        editor.accept_partial_copilot_suggestion(&Default::default(), cx);
7809        assert!(!editor.has_active_copilot_suggestion(cx));
7810        assert_eq!(editor.text(cx), "one.copilot1\ntwo\nthree\n");
7811        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
7812    });
7813
7814    // Reset the editor and check non-word and whitespace completion
7815    cx.set_state(indoc! {"
7816        oneˇ
7817        two
7818        three
7819    "});
7820    cx.simulate_keystroke(".");
7821    let _ = handle_completion_request(
7822        &mut cx,
7823        indoc! {"
7824            one.|<>
7825            two
7826            three
7827        "},
7828        vec![],
7829    );
7830    handle_copilot_completion_request(
7831        &copilot_lsp,
7832        vec![copilot::request::Completion {
7833            text: "one.123. copilot\n 456".into(),
7834            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
7835            ..Default::default()
7836        }],
7837        vec![],
7838    );
7839    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7840    cx.update_editor(|editor, cx| {
7841        assert!(editor.has_active_copilot_suggestion(cx));
7842
7843        // Accepting the first word (non-word) of the suggestion should only accept the first word and still show the rest.
7844        editor.accept_partial_copilot_suggestion(&Default::default(), cx);
7845        assert!(editor.has_active_copilot_suggestion(cx));
7846        assert_eq!(editor.text(cx), "one.123. \ntwo\nthree\n");
7847        assert_eq!(
7848            editor.display_text(cx),
7849            "one.123. copilot\n 456\ntwo\nthree\n"
7850        );
7851
7852        // Accepting next word should accept the next word and copilot suggestion should still exist
7853        editor.accept_partial_copilot_suggestion(&Default::default(), cx);
7854        assert!(editor.has_active_copilot_suggestion(cx));
7855        assert_eq!(editor.text(cx), "one.123. copilot\ntwo\nthree\n");
7856        assert_eq!(
7857            editor.display_text(cx),
7858            "one.123. copilot\n 456\ntwo\nthree\n"
7859        );
7860
7861        // Accepting the whitespace should accept the non-word/whitespaces with newline and copilot suggestion should be gone
7862        editor.accept_partial_copilot_suggestion(&Default::default(), cx);
7863        assert!(!editor.has_active_copilot_suggestion(cx));
7864        assert_eq!(editor.text(cx), "one.123. copilot\n 456\ntwo\nthree\n");
7865        assert_eq!(
7866            editor.display_text(cx),
7867            "one.123. copilot\n 456\ntwo\nthree\n"
7868        );
7869    });
7870}
7871
7872#[gpui::test]
7873async fn test_copilot_completion_invalidation(
7874    executor: BackgroundExecutor,
7875    cx: &mut gpui::TestAppContext,
7876) {
7877    init_test(cx, |_| {});
7878
7879    let (copilot, copilot_lsp) = Copilot::fake(cx);
7880    _ = cx.update(|cx| Copilot::set_global(copilot, cx));
7881    let mut cx = EditorLspTestContext::new_rust(
7882        lsp::ServerCapabilities {
7883            completion_provider: Some(lsp::CompletionOptions {
7884                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
7885                ..Default::default()
7886            }),
7887            ..Default::default()
7888        },
7889        cx,
7890    )
7891    .await;
7892
7893    cx.set_state(indoc! {"
7894        one
7895        twˇ
7896        three
7897    "});
7898
7899    handle_copilot_completion_request(
7900        &copilot_lsp,
7901        vec![copilot::request::Completion {
7902            text: "two.foo()".into(),
7903            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
7904            ..Default::default()
7905        }],
7906        vec![],
7907    );
7908    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
7909    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7910    cx.update_editor(|editor, cx| {
7911        assert!(editor.has_active_copilot_suggestion(cx));
7912        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7913        assert_eq!(editor.text(cx), "one\ntw\nthree\n");
7914
7915        editor.backspace(&Default::default(), cx);
7916        assert!(editor.has_active_copilot_suggestion(cx));
7917        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7918        assert_eq!(editor.text(cx), "one\nt\nthree\n");
7919
7920        editor.backspace(&Default::default(), cx);
7921        assert!(editor.has_active_copilot_suggestion(cx));
7922        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7923        assert_eq!(editor.text(cx), "one\n\nthree\n");
7924
7925        // Deleting across the original suggestion range invalidates it.
7926        editor.backspace(&Default::default(), cx);
7927        assert!(!editor.has_active_copilot_suggestion(cx));
7928        assert_eq!(editor.display_text(cx), "one\nthree\n");
7929        assert_eq!(editor.text(cx), "one\nthree\n");
7930
7931        // Undoing the deletion restores the suggestion.
7932        editor.undo(&Default::default(), cx);
7933        assert!(editor.has_active_copilot_suggestion(cx));
7934        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
7935        assert_eq!(editor.text(cx), "one\n\nthree\n");
7936    });
7937}
7938
7939#[gpui::test]
7940async fn test_copilot_multibuffer(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
7941    init_test(cx, |_| {});
7942
7943    let (copilot, copilot_lsp) = Copilot::fake(cx);
7944    _ = cx.update(|cx| Copilot::set_global(copilot, cx));
7945
7946    let buffer_1 = cx.new_model(|cx| {
7947        Buffer::new(
7948            0,
7949            BufferId::new(cx.entity_id().as_u64()).unwrap(),
7950            "a = 1\nb = 2\n",
7951        )
7952    });
7953    let buffer_2 = cx.new_model(|cx| {
7954        Buffer::new(
7955            0,
7956            BufferId::new(cx.entity_id().as_u64()).unwrap(),
7957            "c = 3\nd = 4\n",
7958        )
7959    });
7960    let multibuffer = cx.new_model(|cx| {
7961        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
7962        multibuffer.push_excerpts(
7963            buffer_1.clone(),
7964            [ExcerptRange {
7965                context: Point::new(0, 0)..Point::new(2, 0),
7966                primary: None,
7967            }],
7968            cx,
7969        );
7970        multibuffer.push_excerpts(
7971            buffer_2.clone(),
7972            [ExcerptRange {
7973                context: Point::new(0, 0)..Point::new(2, 0),
7974                primary: None,
7975            }],
7976            cx,
7977        );
7978        multibuffer
7979    });
7980    let editor = cx.add_window(|cx| build_editor(multibuffer, cx));
7981
7982    handle_copilot_completion_request(
7983        &copilot_lsp,
7984        vec![copilot::request::Completion {
7985            text: "b = 2 + a".into(),
7986            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 5)),
7987            ..Default::default()
7988        }],
7989        vec![],
7990    );
7991    _ = editor.update(cx, |editor, cx| {
7992        // Ensure copilot suggestions are shown for the first excerpt.
7993        editor.change_selections(None, cx, |s| {
7994            s.select_ranges([Point::new(1, 5)..Point::new(1, 5)])
7995        });
7996        editor.next_copilot_suggestion(&Default::default(), cx);
7997    });
7998    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
7999    _ = editor.update(cx, |editor, cx| {
8000        assert!(editor.has_active_copilot_suggestion(cx));
8001        assert_eq!(
8002            editor.display_text(cx),
8003            "\n\na = 1\nb = 2 + a\n\n\n\nc = 3\nd = 4\n"
8004        );
8005        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
8006    });
8007
8008    handle_copilot_completion_request(
8009        &copilot_lsp,
8010        vec![copilot::request::Completion {
8011            text: "d = 4 + c".into(),
8012            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 6)),
8013            ..Default::default()
8014        }],
8015        vec![],
8016    );
8017    _ = editor.update(cx, |editor, cx| {
8018        // Move to another excerpt, ensuring the suggestion gets cleared.
8019        editor.change_selections(None, cx, |s| {
8020            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
8021        });
8022        assert!(!editor.has_active_copilot_suggestion(cx));
8023        assert_eq!(
8024            editor.display_text(cx),
8025            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4\n"
8026        );
8027        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
8028
8029        // Type a character, ensuring we don't even try to interpolate the previous suggestion.
8030        editor.handle_input(" ", cx);
8031        assert!(!editor.has_active_copilot_suggestion(cx));
8032        assert_eq!(
8033            editor.display_text(cx),
8034            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 \n"
8035        );
8036        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
8037    });
8038
8039    // Ensure the new suggestion is displayed when the debounce timeout expires.
8040    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
8041    _ = editor.update(cx, |editor, cx| {
8042        assert!(editor.has_active_copilot_suggestion(cx));
8043        assert_eq!(
8044            editor.display_text(cx),
8045            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 + c\n"
8046        );
8047        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
8048    });
8049}
8050
8051#[gpui::test]
8052async fn test_copilot_disabled_globs(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
8053    init_test(cx, |settings| {
8054        settings
8055            .copilot
8056            .get_or_insert(Default::default())
8057            .disabled_globs = Some(vec![".env*".to_string()]);
8058    });
8059
8060    let (copilot, copilot_lsp) = Copilot::fake(cx);
8061    _ = cx.update(|cx| Copilot::set_global(copilot, cx));
8062
8063    let fs = FakeFs::new(cx.executor());
8064    fs.insert_tree(
8065        "/test",
8066        json!({
8067            ".env": "SECRET=something\n",
8068            "README.md": "hello\n"
8069        }),
8070    )
8071    .await;
8072    let project = Project::test(fs, ["/test".as_ref()], cx).await;
8073
8074    let private_buffer = project
8075        .update(cx, |project, cx| {
8076            project.open_local_buffer("/test/.env", cx)
8077        })
8078        .await
8079        .unwrap();
8080    let public_buffer = project
8081        .update(cx, |project, cx| {
8082            project.open_local_buffer("/test/README.md", cx)
8083        })
8084        .await
8085        .unwrap();
8086
8087    let multibuffer = cx.new_model(|cx| {
8088        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
8089        multibuffer.push_excerpts(
8090            private_buffer.clone(),
8091            [ExcerptRange {
8092                context: Point::new(0, 0)..Point::new(1, 0),
8093                primary: None,
8094            }],
8095            cx,
8096        );
8097        multibuffer.push_excerpts(
8098            public_buffer.clone(),
8099            [ExcerptRange {
8100                context: Point::new(0, 0)..Point::new(1, 0),
8101                primary: None,
8102            }],
8103            cx,
8104        );
8105        multibuffer
8106    });
8107    let editor = cx.add_window(|cx| build_editor(multibuffer, cx));
8108
8109    let mut copilot_requests = copilot_lsp
8110        .handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| async move {
8111            Ok(copilot::request::GetCompletionsResult {
8112                completions: vec![copilot::request::Completion {
8113                    text: "next line".into(),
8114                    range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
8115                    ..Default::default()
8116                }],
8117            })
8118        });
8119
8120    _ = editor.update(cx, |editor, cx| {
8121        editor.change_selections(None, cx, |selections| {
8122            selections.select_ranges([Point::new(0, 0)..Point::new(0, 0)])
8123        });
8124        editor.next_copilot_suggestion(&Default::default(), cx);
8125    });
8126
8127    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
8128    assert!(copilot_requests.try_next().is_err());
8129
8130    _ = editor.update(cx, |editor, cx| {
8131        editor.change_selections(None, cx, |s| {
8132            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
8133        });
8134        editor.next_copilot_suggestion(&Default::default(), cx);
8135    });
8136
8137    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
8138    assert!(copilot_requests.try_next().is_ok());
8139}
8140
8141#[gpui::test]
8142async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
8143    init_test(cx, |_| {});
8144
8145    let fs = FakeFs::new(cx.executor());
8146    fs.insert_tree(
8147        "/a",
8148        json!({
8149            "main.rs": "fn main() { let a = 5; }",
8150            "other.rs": "// Test file",
8151        }),
8152    )
8153    .await;
8154    let project = Project::test(fs, ["/a".as_ref()], cx).await;
8155
8156    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
8157    language_registry.add(Arc::new(Language::new(
8158        LanguageConfig {
8159            name: "Rust".into(),
8160            matcher: LanguageMatcher {
8161                path_suffixes: vec!["rs".to_string()],
8162                ..Default::default()
8163            },
8164            brackets: BracketPairConfig {
8165                pairs: vec![BracketPair {
8166                    start: "{".to_string(),
8167                    end: "}".to_string(),
8168                    close: true,
8169                    newline: true,
8170                }],
8171                disabled_scopes_by_bracket_ix: Vec::new(),
8172            },
8173            ..Default::default()
8174        },
8175        Some(tree_sitter_rust::language()),
8176    )));
8177    let mut fake_servers = language_registry.register_fake_lsp_adapter(
8178        "Rust",
8179        FakeLspAdapter {
8180            capabilities: lsp::ServerCapabilities {
8181                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
8182                    first_trigger_character: "{".to_string(),
8183                    more_trigger_character: None,
8184                }),
8185                ..Default::default()
8186            },
8187            ..Default::default()
8188        },
8189    );
8190
8191    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
8192
8193    let cx = &mut VisualTestContext::from_window(*workspace, cx);
8194
8195    let worktree_id = workspace
8196        .update(cx, |workspace, cx| {
8197            workspace.project().update(cx, |project, cx| {
8198                project.worktrees().next().unwrap().read(cx).id()
8199            })
8200        })
8201        .unwrap();
8202
8203    let buffer = project
8204        .update(cx, |project, cx| {
8205            project.open_local_buffer("/a/main.rs", cx)
8206        })
8207        .await
8208        .unwrap();
8209    cx.executor().run_until_parked();
8210    cx.executor().start_waiting();
8211    let fake_server = fake_servers.next().await.unwrap();
8212    let editor_handle = workspace
8213        .update(cx, |workspace, cx| {
8214            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
8215        })
8216        .unwrap()
8217        .await
8218        .unwrap()
8219        .downcast::<Editor>()
8220        .unwrap();
8221
8222    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
8223        assert_eq!(
8224            params.text_document_position.text_document.uri,
8225            lsp::Url::from_file_path("/a/main.rs").unwrap(),
8226        );
8227        assert_eq!(
8228            params.text_document_position.position,
8229            lsp::Position::new(0, 21),
8230        );
8231
8232        Ok(Some(vec![lsp::TextEdit {
8233            new_text: "]".to_string(),
8234            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
8235        }]))
8236    });
8237
8238    editor_handle.update(cx, |editor, cx| {
8239        editor.focus(cx);
8240        editor.change_selections(None, cx, |s| {
8241            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
8242        });
8243        editor.handle_input("{", cx);
8244    });
8245
8246    cx.executor().run_until_parked();
8247
8248    _ = buffer.update(cx, |buffer, _| {
8249        assert_eq!(
8250            buffer.text(),
8251            "fn main() { let a = {5}; }",
8252            "No extra braces from on type formatting should appear in the buffer"
8253        )
8254    });
8255}
8256
8257#[gpui::test]
8258async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
8259    init_test(cx, |_| {});
8260
8261    let fs = FakeFs::new(cx.executor());
8262    fs.insert_tree(
8263        "/a",
8264        json!({
8265            "main.rs": "fn main() { let a = 5; }",
8266            "other.rs": "// Test file",
8267        }),
8268    )
8269    .await;
8270
8271    let project = Project::test(fs, ["/a".as_ref()], cx).await;
8272
8273    let server_restarts = Arc::new(AtomicUsize::new(0));
8274    let closure_restarts = Arc::clone(&server_restarts);
8275    let language_server_name = "test language server";
8276    let language_name: Arc<str> = "Rust".into();
8277
8278    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
8279    language_registry.add(Arc::new(Language::new(
8280        LanguageConfig {
8281            name: Arc::clone(&language_name),
8282            matcher: LanguageMatcher {
8283                path_suffixes: vec!["rs".to_string()],
8284                ..Default::default()
8285            },
8286            ..Default::default()
8287        },
8288        Some(tree_sitter_rust::language()),
8289    )));
8290    let mut fake_servers = language_registry.register_fake_lsp_adapter(
8291        "Rust",
8292        FakeLspAdapter {
8293            name: language_server_name,
8294            initialization_options: Some(json!({
8295                "testOptionValue": true
8296            })),
8297            initializer: Some(Box::new(move |fake_server| {
8298                let task_restarts = Arc::clone(&closure_restarts);
8299                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
8300                    task_restarts.fetch_add(1, atomic::Ordering::Release);
8301                    futures::future::ready(Ok(()))
8302                });
8303            })),
8304            ..Default::default()
8305        },
8306    );
8307
8308    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
8309    let _buffer = project
8310        .update(cx, |project, cx| {
8311            project.open_local_buffer("/a/main.rs", cx)
8312        })
8313        .await
8314        .unwrap();
8315    let _fake_server = fake_servers.next().await.unwrap();
8316    update_test_language_settings(cx, |language_settings| {
8317        language_settings.languages.insert(
8318            Arc::clone(&language_name),
8319            LanguageSettingsContent {
8320                tab_size: NonZeroU32::new(8),
8321                ..Default::default()
8322            },
8323        );
8324    });
8325    cx.executor().run_until_parked();
8326    assert_eq!(
8327        server_restarts.load(atomic::Ordering::Acquire),
8328        0,
8329        "Should not restart LSP server on an unrelated change"
8330    );
8331
8332    update_test_project_settings(cx, |project_settings| {
8333        project_settings.lsp.insert(
8334            "Some other server name".into(),
8335            LspSettings {
8336                settings: None,
8337                initialization_options: Some(json!({
8338                    "some other init value": false
8339                })),
8340            },
8341        );
8342    });
8343    cx.executor().run_until_parked();
8344    assert_eq!(
8345        server_restarts.load(atomic::Ordering::Acquire),
8346        0,
8347        "Should not restart LSP server on an unrelated LSP settings change"
8348    );
8349
8350    update_test_project_settings(cx, |project_settings| {
8351        project_settings.lsp.insert(
8352            language_server_name.into(),
8353            LspSettings {
8354                settings: None,
8355                initialization_options: Some(json!({
8356                    "anotherInitValue": false
8357                })),
8358            },
8359        );
8360    });
8361    cx.executor().run_until_parked();
8362    assert_eq!(
8363        server_restarts.load(atomic::Ordering::Acquire),
8364        1,
8365        "Should restart LSP server on a related LSP settings change"
8366    );
8367
8368    update_test_project_settings(cx, |project_settings| {
8369        project_settings.lsp.insert(
8370            language_server_name.into(),
8371            LspSettings {
8372                settings: None,
8373                initialization_options: Some(json!({
8374                    "anotherInitValue": false
8375                })),
8376            },
8377        );
8378    });
8379    cx.executor().run_until_parked();
8380    assert_eq!(
8381        server_restarts.load(atomic::Ordering::Acquire),
8382        1,
8383        "Should not restart LSP server on a related LSP settings change that is the same"
8384    );
8385
8386    update_test_project_settings(cx, |project_settings| {
8387        project_settings.lsp.insert(
8388            language_server_name.into(),
8389            LspSettings {
8390                settings: None,
8391                initialization_options: None,
8392            },
8393        );
8394    });
8395    cx.executor().run_until_parked();
8396    assert_eq!(
8397        server_restarts.load(atomic::Ordering::Acquire),
8398        2,
8399        "Should restart LSP server on another related LSP settings change"
8400    );
8401}
8402
8403#[gpui::test]
8404async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
8405    init_test(cx, |_| {});
8406
8407    let mut cx = EditorLspTestContext::new_rust(
8408        lsp::ServerCapabilities {
8409            completion_provider: Some(lsp::CompletionOptions {
8410                trigger_characters: Some(vec![".".to_string()]),
8411                resolve_provider: Some(true),
8412                ..Default::default()
8413            }),
8414            ..Default::default()
8415        },
8416        cx,
8417    )
8418    .await;
8419
8420    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
8421    cx.simulate_keystroke(".");
8422    let completion_item = lsp::CompletionItem {
8423        label: "some".into(),
8424        kind: Some(lsp::CompletionItemKind::SNIPPET),
8425        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
8426        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
8427            kind: lsp::MarkupKind::Markdown,
8428            value: "```rust\nSome(2)\n```".to_string(),
8429        })),
8430        deprecated: Some(false),
8431        sort_text: Some("fffffff2".to_string()),
8432        filter_text: Some("some".to_string()),
8433        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
8434        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
8435            range: lsp::Range {
8436                start: lsp::Position {
8437                    line: 0,
8438                    character: 22,
8439                },
8440                end: lsp::Position {
8441                    line: 0,
8442                    character: 22,
8443                },
8444            },
8445            new_text: "Some(2)".to_string(),
8446        })),
8447        additional_text_edits: Some(vec![lsp::TextEdit {
8448            range: lsp::Range {
8449                start: lsp::Position {
8450                    line: 0,
8451                    character: 20,
8452                },
8453                end: lsp::Position {
8454                    line: 0,
8455                    character: 22,
8456                },
8457            },
8458            new_text: "".to_string(),
8459        }]),
8460        ..Default::default()
8461    };
8462
8463    let closure_completion_item = completion_item.clone();
8464    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
8465        let task_completion_item = closure_completion_item.clone();
8466        async move {
8467            Ok(Some(lsp::CompletionResponse::Array(vec![
8468                task_completion_item,
8469            ])))
8470        }
8471    });
8472
8473    request.next().await;
8474
8475    cx.condition(|editor, _| editor.context_menu_visible())
8476        .await;
8477    let apply_additional_edits = cx.update_editor(|editor, cx| {
8478        editor
8479            .confirm_completion(&ConfirmCompletion::default(), cx)
8480            .unwrap()
8481    });
8482    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
8483
8484    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
8485        let task_completion_item = completion_item.clone();
8486        async move { Ok(task_completion_item) }
8487    })
8488    .next()
8489    .await
8490    .unwrap();
8491    apply_additional_edits.await.unwrap();
8492    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
8493}
8494
8495#[gpui::test]
8496async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
8497    init_test(cx, |_| {});
8498
8499    let mut cx = EditorLspTestContext::new(
8500        Language::new(
8501            LanguageConfig {
8502                matcher: LanguageMatcher {
8503                    path_suffixes: vec!["jsx".into()],
8504                    ..Default::default()
8505                },
8506                overrides: [(
8507                    "element".into(),
8508                    LanguageConfigOverride {
8509                        word_characters: Override::Set(['-'].into_iter().collect()),
8510                        ..Default::default()
8511                    },
8512                )]
8513                .into_iter()
8514                .collect(),
8515                ..Default::default()
8516            },
8517            Some(tree_sitter_typescript::language_tsx()),
8518        )
8519        .with_override_query("(jsx_self_closing_element) @element")
8520        .unwrap(),
8521        lsp::ServerCapabilities {
8522            completion_provider: Some(lsp::CompletionOptions {
8523                trigger_characters: Some(vec![":".to_string()]),
8524                ..Default::default()
8525            }),
8526            ..Default::default()
8527        },
8528        cx,
8529    )
8530    .await;
8531
8532    cx.lsp
8533        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
8534            Ok(Some(lsp::CompletionResponse::Array(vec![
8535                lsp::CompletionItem {
8536                    label: "bg-blue".into(),
8537                    ..Default::default()
8538                },
8539                lsp::CompletionItem {
8540                    label: "bg-red".into(),
8541                    ..Default::default()
8542                },
8543                lsp::CompletionItem {
8544                    label: "bg-yellow".into(),
8545                    ..Default::default()
8546                },
8547            ])))
8548        });
8549
8550    cx.set_state(r#"<p class="bgˇ" />"#);
8551
8552    // Trigger completion when typing a dash, because the dash is an extra
8553    // word character in the 'element' scope, which contains the cursor.
8554    cx.simulate_keystroke("-");
8555    cx.executor().run_until_parked();
8556    cx.update_editor(|editor, _| {
8557        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
8558            assert_eq!(
8559                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
8560                &["bg-red", "bg-blue", "bg-yellow"]
8561            );
8562        } else {
8563            panic!("expected completion menu to be open");
8564        }
8565    });
8566
8567    cx.simulate_keystroke("l");
8568    cx.executor().run_until_parked();
8569    cx.update_editor(|editor, _| {
8570        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
8571            assert_eq!(
8572                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
8573                &["bg-blue", "bg-yellow"]
8574            );
8575        } else {
8576            panic!("expected completion menu to be open");
8577        }
8578    });
8579
8580    // When filtering completions, consider the character after the '-' to
8581    // be the start of a subword.
8582    cx.set_state(r#"<p class="yelˇ" />"#);
8583    cx.simulate_keystroke("l");
8584    cx.executor().run_until_parked();
8585    cx.update_editor(|editor, _| {
8586        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
8587            assert_eq!(
8588                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
8589                &["bg-yellow"]
8590            );
8591        } else {
8592            panic!("expected completion menu to be open");
8593        }
8594    });
8595}
8596
8597#[gpui::test]
8598async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
8599    init_test(cx, |settings| {
8600        settings.defaults.formatter = Some(language_settings::Formatter::Prettier)
8601    });
8602
8603    let fs = FakeFs::new(cx.executor());
8604    fs.insert_file("/file.rs", Default::default()).await;
8605
8606    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
8607    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
8608
8609    language_registry.add(Arc::new(Language::new(
8610        LanguageConfig {
8611            name: "Rust".into(),
8612            matcher: LanguageMatcher {
8613                path_suffixes: vec!["rs".to_string()],
8614                ..Default::default()
8615            },
8616            prettier_parser_name: Some("test_parser".to_string()),
8617            ..Default::default()
8618        },
8619        Some(tree_sitter_rust::language()),
8620    )));
8621
8622    let test_plugin = "test_plugin";
8623    let _ = language_registry.register_fake_lsp_adapter(
8624        "Rust",
8625        FakeLspAdapter {
8626            prettier_plugins: vec![test_plugin],
8627            ..Default::default()
8628        },
8629    );
8630
8631    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
8632    let buffer = project
8633        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
8634        .await
8635        .unwrap();
8636
8637    let buffer_text = "one\ntwo\nthree\n";
8638    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
8639    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
8640    _ = editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
8641
8642    editor
8643        .update(cx, |editor, cx| {
8644            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
8645        })
8646        .unwrap()
8647        .await;
8648    assert_eq!(
8649        editor.update(cx, |editor, cx| editor.text(cx)),
8650        buffer_text.to_string() + prettier_format_suffix,
8651        "Test prettier formatting was not applied to the original buffer text",
8652    );
8653
8654    update_test_language_settings(cx, |settings| {
8655        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
8656    });
8657    let format = editor.update(cx, |editor, cx| {
8658        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
8659    });
8660    format.await.unwrap();
8661    assert_eq!(
8662        editor.update(cx, |editor, cx| editor.text(cx)),
8663        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
8664        "Autoformatting (via test prettier) was not applied to the original buffer text",
8665    );
8666}
8667
8668#[gpui::test]
8669async fn test_find_all_references(cx: &mut gpui::TestAppContext) {
8670    init_test(cx, |_| {});
8671
8672    let mut cx = EditorLspTestContext::new_rust(
8673        lsp::ServerCapabilities {
8674            document_formatting_provider: Some(lsp::OneOf::Left(true)),
8675            ..Default::default()
8676        },
8677        cx,
8678    )
8679    .await;
8680
8681    cx.set_state(indoc! {"
8682        fn foo(«paramˇ»: i64) {
8683            println!(param);
8684        }
8685    "});
8686
8687    cx.lsp
8688        .handle_request::<lsp::request::References, _, _>(move |_, _| async move {
8689            Ok(Some(vec![
8690                lsp::Location {
8691                    uri: lsp::Url::from_file_path("/root/dir/file.rs").unwrap(),
8692                    range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 12)),
8693                },
8694                lsp::Location {
8695                    uri: lsp::Url::from_file_path("/root/dir/file.rs").unwrap(),
8696                    range: lsp::Range::new(lsp::Position::new(1, 13), lsp::Position::new(1, 18)),
8697                },
8698            ]))
8699        });
8700
8701    let references = cx
8702        .update_editor(|editor, cx| editor.find_all_references(&FindAllReferences, cx))
8703        .unwrap();
8704
8705    cx.executor().run_until_parked();
8706
8707    cx.executor().start_waiting();
8708    references.await.unwrap();
8709
8710    cx.assert_editor_state(indoc! {"
8711        fn foo(param: i64) {
8712            println!(«paramˇ»);
8713        }
8714    "});
8715
8716    let references = cx
8717        .update_editor(|editor, cx| editor.find_all_references(&FindAllReferences, cx))
8718        .unwrap();
8719
8720    cx.executor().run_until_parked();
8721
8722    cx.executor().start_waiting();
8723    references.await.unwrap();
8724
8725    cx.assert_editor_state(indoc! {"
8726        fn foo(«paramˇ»: i64) {
8727            println!(param);
8728        }
8729    "});
8730
8731    cx.set_state(indoc! {"
8732        fn foo(param: i64) {
8733            let a = param;
8734            let aˇ = param;
8735            let a = param;
8736            println!(param);
8737        }
8738    "});
8739
8740    cx.lsp
8741        .handle_request::<lsp::request::References, _, _>(move |_, _| async move {
8742            Ok(Some(vec![lsp::Location {
8743                uri: lsp::Url::from_file_path("/root/dir/file.rs").unwrap(),
8744                range: lsp::Range::new(lsp::Position::new(2, 8), lsp::Position::new(2, 9)),
8745            }]))
8746        });
8747
8748    let references = cx
8749        .update_editor(|editor, cx| editor.find_all_references(&FindAllReferences, cx))
8750        .unwrap();
8751
8752    cx.executor().run_until_parked();
8753
8754    cx.executor().start_waiting();
8755    references.await.unwrap();
8756
8757    cx.assert_editor_state(indoc! {"
8758        fn foo(param: i64) {
8759            let a = param;
8760            let «aˇ» = param;
8761            let a = param;
8762            println!(param);
8763        }
8764    "});
8765}
8766
8767#[gpui::test]
8768async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
8769    init_test(cx, |_| {});
8770    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
8771    let base_text = indoc! {r#"struct Row;
8772struct Row1;
8773struct Row2;
8774
8775struct Row4;
8776struct Row5;
8777struct Row6;
8778
8779struct Row8;
8780struct Row9;
8781struct Row10;"#};
8782
8783    // When addition hunks are not adjacent to carets, no hunk revert is performed
8784    assert_hunk_revert(
8785        indoc! {r#"struct Row;
8786                   struct Row1;
8787                   struct Row1.1;
8788                   struct Row1.2;
8789                   struct Row2;ˇ
8790
8791                   struct Row4;
8792                   struct Row5;
8793                   struct Row6;
8794
8795                   struct Row8;
8796                   ˇstruct Row9;
8797                   struct Row9.1;
8798                   struct Row9.2;
8799                   struct Row9.3;
8800                   struct Row10;"#},
8801        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
8802        indoc! {r#"struct Row;
8803                   struct Row1;
8804                   struct Row1.1;
8805                   struct Row1.2;
8806                   struct Row2;ˇ
8807
8808                   struct Row4;
8809                   struct Row5;
8810                   struct Row6;
8811
8812                   struct Row8;
8813                   ˇstruct Row9;
8814                   struct Row9.1;
8815                   struct Row9.2;
8816                   struct Row9.3;
8817                   struct Row10;"#},
8818        base_text,
8819        &mut cx,
8820    );
8821    // Same for selections
8822    assert_hunk_revert(
8823        indoc! {r#"struct Row;
8824                   struct Row1;
8825                   struct Row2;
8826                   struct Row2.1;
8827                   struct Row2.2;
8828                   «ˇ
8829                   struct Row4;
8830                   struct» Row5;
8831                   «struct Row6;
8832                   ˇ»
8833                   struct Row9.1;
8834                   struct Row9.2;
8835                   struct Row9.3;
8836                   struct Row8;
8837                   struct Row9;
8838                   struct Row10;"#},
8839        vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
8840        indoc! {r#"struct Row;
8841                   struct Row1;
8842                   struct Row2;
8843                   struct Row2.1;
8844                   struct Row2.2;
8845                   «ˇ
8846                   struct Row4;
8847                   struct» Row5;
8848                   «struct Row6;
8849                   ˇ»
8850                   struct Row9.1;
8851                   struct Row9.2;
8852                   struct Row9.3;
8853                   struct Row8;
8854                   struct Row9;
8855                   struct Row10;"#},
8856        base_text,
8857        &mut cx,
8858    );
8859
8860    // When carets and selections intersect the addition hunks, those are reverted.
8861    // Adjacent carets got merged.
8862    assert_hunk_revert(
8863        indoc! {r#"struct Row;
8864                   ˇ// something on the top
8865                   struct Row1;
8866                   struct Row2;
8867                   struct Roˇw3.1;
8868                   struct Row2.2;
8869                   struct Row2.3;ˇ
8870
8871                   struct Row4;
8872                   struct ˇRow5.1;
8873                   struct Row5.2;
8874                   struct «Rowˇ»5.3;
8875                   struct Row5;
8876                   struct Row6;
8877                   ˇ
8878                   struct Row9.1;
8879                   struct «Rowˇ»9.2;
8880                   struct «ˇRow»9.3;
8881                   struct Row8;
8882                   struct Row9;
8883                   «ˇ// something on bottom»
8884                   struct Row10;"#},
8885        vec![
8886            DiffHunkStatus::Added,
8887            DiffHunkStatus::Added,
8888            DiffHunkStatus::Added,
8889            DiffHunkStatus::Added,
8890            DiffHunkStatus::Added,
8891        ],
8892        indoc! {r#"struct Row;
8893                   ˇstruct Row1;
8894                   struct Row2;
8895                   ˇ
8896                   struct Row4;
8897                   ˇstruct Row5;
8898                   struct Row6;
8899                   ˇ
8900                   ˇstruct Row8;
8901                   struct Row9;
8902                   ˇstruct Row10;"#},
8903        base_text,
8904        &mut cx,
8905    );
8906}
8907
8908#[gpui::test]
8909async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
8910    init_test(cx, |_| {});
8911    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
8912    let base_text = indoc! {r#"struct Row;
8913struct Row1;
8914struct Row2;
8915
8916struct Row4;
8917struct Row5;
8918struct Row6;
8919
8920struct Row8;
8921struct Row9;
8922struct Row10;"#};
8923
8924    // Modification hunks behave the same as the addition ones.
8925    assert_hunk_revert(
8926        indoc! {r#"struct Row;
8927                   struct Row1;
8928                   struct Row33;
8929                   ˇ
8930                   struct Row4;
8931                   struct Row5;
8932                   struct Row6;
8933                   ˇ
8934                   struct Row99;
8935                   struct Row9;
8936                   struct Row10;"#},
8937        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
8938        indoc! {r#"struct Row;
8939                   struct Row1;
8940                   struct Row33;
8941                   ˇ
8942                   struct Row4;
8943                   struct Row5;
8944                   struct Row6;
8945                   ˇ
8946                   struct Row99;
8947                   struct Row9;
8948                   struct Row10;"#},
8949        base_text,
8950        &mut cx,
8951    );
8952    assert_hunk_revert(
8953        indoc! {r#"struct Row;
8954                   struct Row1;
8955                   struct Row33;
8956                   «ˇ
8957                   struct Row4;
8958                   struct» Row5;
8959                   «struct Row6;
8960                   ˇ»
8961                   struct Row99;
8962                   struct Row9;
8963                   struct Row10;"#},
8964        vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
8965        indoc! {r#"struct Row;
8966                   struct Row1;
8967                   struct Row33;
8968                   «ˇ
8969                   struct Row4;
8970                   struct» Row5;
8971                   «struct Row6;
8972                   ˇ»
8973                   struct Row99;
8974                   struct Row9;
8975                   struct Row10;"#},
8976        base_text,
8977        &mut cx,
8978    );
8979
8980    assert_hunk_revert(
8981        indoc! {r#"ˇstruct Row1.1;
8982                   struct Row1;
8983                   «ˇstr»uct Row22;
8984
8985                   struct ˇRow44;
8986                   struct Row5;
8987                   struct «Rˇ»ow66;ˇ
8988
8989                   «struˇ»ct Row88;
8990                   struct Row9;
8991                   struct Row1011;ˇ"#},
8992        vec![
8993            DiffHunkStatus::Modified,
8994            DiffHunkStatus::Modified,
8995            DiffHunkStatus::Modified,
8996            DiffHunkStatus::Modified,
8997            DiffHunkStatus::Modified,
8998            DiffHunkStatus::Modified,
8999        ],
9000        indoc! {r#"struct Row;
9001                   ˇstruct Row1;
9002                   struct Row2;
9003                   ˇ
9004                   struct Row4;
9005                   ˇstruct Row5;
9006                   struct Row6;
9007                   ˇ
9008                   struct Row8;
9009                   ˇstruct Row9;
9010                   struct Row10;ˇ"#},
9011        base_text,
9012        &mut cx,
9013    );
9014}
9015
9016#[gpui::test]
9017async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
9018    init_test(cx, |_| {});
9019    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
9020    let base_text = indoc! {r#"struct Row;
9021struct Row1;
9022struct Row2;
9023
9024struct Row4;
9025struct Row5;
9026struct Row6;
9027
9028struct Row8;
9029struct Row9;
9030struct Row10;"#};
9031
9032    // Deletion hunks trigger with carets on ajacent rows, so carets and selections have to stay farther to avoid the revert
9033    assert_hunk_revert(
9034        indoc! {r#"struct Row;
9035                   struct Row2;
9036
9037                   ˇstruct Row4;
9038                   struct Row5;
9039                   struct Row6;
9040                   ˇ
9041                   struct Row8;
9042                   struct Row10;"#},
9043        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
9044        indoc! {r#"struct Row;
9045                   struct Row2;
9046
9047                   ˇstruct Row4;
9048                   struct Row5;
9049                   struct Row6;
9050                   ˇ
9051                   struct Row8;
9052                   struct Row10;"#},
9053        base_text,
9054        &mut cx,
9055    );
9056    assert_hunk_revert(
9057        indoc! {r#"struct Row;
9058                   struct Row2;
9059
9060                   «ˇstruct Row4;
9061                   struct» Row5;
9062                   «struct Row6;
9063                   ˇ»
9064                   struct Row8;
9065                   struct Row10;"#},
9066        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
9067        indoc! {r#"struct Row;
9068                   struct Row2;
9069
9070                   «ˇstruct Row4;
9071                   struct» Row5;
9072                   «struct Row6;
9073                   ˇ»
9074                   struct Row8;
9075                   struct Row10;"#},
9076        base_text,
9077        &mut cx,
9078    );
9079
9080    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
9081    assert_hunk_revert(
9082        indoc! {r#"struct Row;
9083                   ˇstruct Row2;
9084
9085                   struct Row4;
9086                   struct Row5;
9087                   struct Row6;
9088
9089                   struct Row8;ˇ
9090                   struct Row10;"#},
9091        vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
9092        indoc! {r#"struct Row;
9093                   struct Row1;
9094                   ˇstruct Row2;
9095
9096                   struct Row4;
9097                   struct Row5;
9098                   struct Row6;
9099
9100                   struct Row8;ˇ
9101                   struct Row9;
9102                   struct Row10;"#},
9103        base_text,
9104        &mut cx,
9105    );
9106    assert_hunk_revert(
9107        indoc! {r#"struct Row;
9108                   struct Row2«ˇ;
9109                   struct Row4;
9110                   struct» Row5;
9111                   «struct Row6;
9112
9113                   struct Row8;ˇ»
9114                   struct Row10;"#},
9115        vec![
9116            DiffHunkStatus::Removed,
9117            DiffHunkStatus::Removed,
9118            DiffHunkStatus::Removed,
9119        ],
9120        indoc! {r#"struct Row;
9121                   struct Row1;
9122                   struct Row2«ˇ;
9123
9124                   struct Row4;
9125                   struct» Row5;
9126                   «struct Row6;
9127
9128                   struct Row8;ˇ»
9129                   struct Row9;
9130                   struct Row10;"#},
9131        base_text,
9132        &mut cx,
9133    );
9134}
9135
9136#[gpui::test]
9137async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
9138    init_test(cx, |_| {});
9139
9140    let cols = 4;
9141    let rows = 10;
9142    let sample_text_1 = sample_text(rows, cols, 'a');
9143    assert_eq!(
9144        sample_text_1,
9145        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
9146    );
9147    let sample_text_2 = sample_text(rows, cols, 'l');
9148    assert_eq!(
9149        sample_text_2,
9150        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
9151    );
9152    let sample_text_3 = sample_text(rows, cols, 'v');
9153    assert_eq!(
9154        sample_text_3,
9155        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
9156    );
9157
9158    fn diff_every_buffer_row(
9159        buffer: &Model<Buffer>,
9160        sample_text: String,
9161        cols: usize,
9162        cx: &mut gpui::TestAppContext,
9163    ) {
9164        // revert first character in each row, creating one large diff hunk per buffer
9165        let is_first_char = |offset: usize| offset % cols == 0;
9166        buffer.update(cx, |buffer, cx| {
9167            buffer.set_text(
9168                sample_text
9169                    .chars()
9170                    .enumerate()
9171                    .map(|(offset, c)| if is_first_char(offset) { 'X' } else { c })
9172                    .collect::<String>(),
9173                cx,
9174            );
9175            buffer.set_diff_base(Some(sample_text), cx);
9176        });
9177        cx.executor().run_until_parked();
9178    }
9179
9180    let buffer_1 = cx.new_model(|cx| {
9181        Buffer::new(
9182            0,
9183            BufferId::new(cx.entity_id().as_u64()).unwrap(),
9184            sample_text_1.clone(),
9185        )
9186    });
9187    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
9188
9189    let buffer_2 = cx.new_model(|cx| {
9190        Buffer::new(
9191            1,
9192            BufferId::new(cx.entity_id().as_u64() + 1).unwrap(),
9193            sample_text_2.clone(),
9194        )
9195    });
9196    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
9197
9198    let buffer_3 = cx.new_model(|cx| {
9199        Buffer::new(
9200            2,
9201            BufferId::new(cx.entity_id().as_u64() + 2).unwrap(),
9202            sample_text_3.clone(),
9203        )
9204    });
9205    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
9206
9207    let multibuffer = cx.new_model(|cx| {
9208        let mut multibuffer = MultiBuffer::new(0, ReadWrite);
9209        multibuffer.push_excerpts(
9210            buffer_1.clone(),
9211            [
9212                ExcerptRange {
9213                    context: Point::new(0, 0)..Point::new(3, 0),
9214                    primary: None,
9215                },
9216                ExcerptRange {
9217                    context: Point::new(5, 0)..Point::new(7, 0),
9218                    primary: None,
9219                },
9220                ExcerptRange {
9221                    context: Point::new(9, 0)..Point::new(10, 4),
9222                    primary: None,
9223                },
9224            ],
9225            cx,
9226        );
9227        multibuffer.push_excerpts(
9228            buffer_2.clone(),
9229            [
9230                ExcerptRange {
9231                    context: Point::new(0, 0)..Point::new(3, 0),
9232                    primary: None,
9233                },
9234                ExcerptRange {
9235                    context: Point::new(5, 0)..Point::new(7, 0),
9236                    primary: None,
9237                },
9238                ExcerptRange {
9239                    context: Point::new(9, 0)..Point::new(10, 4),
9240                    primary: None,
9241                },
9242            ],
9243            cx,
9244        );
9245        multibuffer.push_excerpts(
9246            buffer_3.clone(),
9247            [
9248                ExcerptRange {
9249                    context: Point::new(0, 0)..Point::new(3, 0),
9250                    primary: None,
9251                },
9252                ExcerptRange {
9253                    context: Point::new(5, 0)..Point::new(7, 0),
9254                    primary: None,
9255                },
9256                ExcerptRange {
9257                    context: Point::new(9, 0)..Point::new(10, 4),
9258                    primary: None,
9259                },
9260            ],
9261            cx,
9262        );
9263        multibuffer
9264    });
9265
9266    let (editor, cx) = cx.add_window_view(|cx| build_editor(multibuffer, cx));
9267    editor.update(cx, |editor, cx| {
9268        assert_eq!(editor.text(cx), "XaaaXbbbX\nccXc\ndXdd\n\nhXhh\nXiiiXjjjX\n\nXlllXmmmX\nnnXn\noXoo\n\nsXss\nXtttXuuuX\n\nXvvvXwwwX\nxxXx\nyXyy\n\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X\n");
9269        editor.select_all(&SelectAll, cx);
9270        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
9271    });
9272    cx.executor().run_until_parked();
9273    // When all ranges are selected, all buffer hunks are reverted.
9274    editor.update(cx, |editor, cx| {
9275        assert_eq!(editor.text(cx), "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n\n\nllll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu\n\n\nvvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}\n\n");
9276    });
9277    buffer_1.update(cx, |buffer, _| {
9278        assert_eq!(buffer.text(), sample_text_1);
9279    });
9280    buffer_2.update(cx, |buffer, _| {
9281        assert_eq!(buffer.text(), sample_text_2);
9282    });
9283    buffer_3.update(cx, |buffer, _| {
9284        assert_eq!(buffer.text(), sample_text_3);
9285    });
9286
9287    diff_every_buffer_row(&buffer_1, sample_text_1.clone(), cols, cx);
9288    diff_every_buffer_row(&buffer_2, sample_text_2.clone(), cols, cx);
9289    diff_every_buffer_row(&buffer_3, sample_text_3.clone(), cols, cx);
9290    editor.update(cx, |editor, cx| {
9291        editor.change_selections(None, cx, |s| {
9292            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
9293        });
9294        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
9295    });
9296    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
9297    // but not affect buffer_2 and its related excerpts.
9298    editor.update(cx, |editor, cx| {
9299        assert_eq!(
9300            editor.text(cx),
9301            "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n\n\nXlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX\n\n\nXvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X\n\n"
9302        );
9303    });
9304    buffer_1.update(cx, |buffer, _| {
9305        assert_eq!(buffer.text(), sample_text_1);
9306    });
9307    buffer_2.update(cx, |buffer, _| {
9308        assert_eq!(
9309            buffer.text(),
9310            "XlllXmmmX\nnnXn\noXoo\nXpppXqqqX\nrrXr\nsXss\nXtttXuuuX"
9311        );
9312    });
9313    buffer_3.update(cx, |buffer, _| {
9314        assert_eq!(
9315            buffer.text(),
9316            "XvvvXwwwX\nxxXx\nyXyy\nXzzzX{{{X\n||X|\n}X}}\nX~~~X\u{7f}\u{7f}\u{7f}X"
9317        );
9318    });
9319}
9320
9321fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
9322    let point = DisplayPoint::new(row as u32, column as u32);
9323    point..point
9324}
9325
9326fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
9327    let (text, ranges) = marked_text_ranges(marked_text, true);
9328    assert_eq!(view.text(cx), text);
9329    assert_eq!(
9330        view.selections.ranges(cx),
9331        ranges,
9332        "Assert selections are {}",
9333        marked_text
9334    );
9335}
9336
9337/// Handle completion request passing a marked string specifying where the completion
9338/// should be triggered from using '|' character, what range should be replaced, and what completions
9339/// should be returned using '<' and '>' to delimit the range
9340pub fn handle_completion_request(
9341    cx: &mut EditorLspTestContext,
9342    marked_string: &str,
9343    completions: Vec<&'static str>,
9344) -> impl Future<Output = ()> {
9345    let complete_from_marker: TextRangeMarker = '|'.into();
9346    let replace_range_marker: TextRangeMarker = ('<', '>').into();
9347    let (_, mut marked_ranges) = marked_text_ranges_by(
9348        marked_string,
9349        vec![complete_from_marker.clone(), replace_range_marker.clone()],
9350    );
9351
9352    let complete_from_position =
9353        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
9354    let replace_range =
9355        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
9356
9357    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
9358        let completions = completions.clone();
9359        async move {
9360            assert_eq!(params.text_document_position.text_document.uri, url.clone());
9361            assert_eq!(
9362                params.text_document_position.position,
9363                complete_from_position
9364            );
9365            Ok(Some(lsp::CompletionResponse::Array(
9366                completions
9367                    .iter()
9368                    .map(|completion_text| lsp::CompletionItem {
9369                        label: completion_text.to_string(),
9370                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
9371                            range: replace_range,
9372                            new_text: completion_text.to_string(),
9373                        })),
9374                        ..Default::default()
9375                    })
9376                    .collect(),
9377            )))
9378        }
9379    });
9380
9381    async move {
9382        request.next().await;
9383    }
9384}
9385
9386fn handle_resolve_completion_request(
9387    cx: &mut EditorLspTestContext,
9388    edits: Option<Vec<(&'static str, &'static str)>>,
9389) -> impl Future<Output = ()> {
9390    let edits = edits.map(|edits| {
9391        edits
9392            .iter()
9393            .map(|(marked_string, new_text)| {
9394                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
9395                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
9396                lsp::TextEdit::new(replace_range, new_text.to_string())
9397            })
9398            .collect::<Vec<_>>()
9399    });
9400
9401    let mut request =
9402        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
9403            let edits = edits.clone();
9404            async move {
9405                Ok(lsp::CompletionItem {
9406                    additional_text_edits: edits,
9407                    ..Default::default()
9408                })
9409            }
9410        });
9411
9412    async move {
9413        request.next().await;
9414    }
9415}
9416
9417fn handle_copilot_completion_request(
9418    lsp: &lsp::FakeLanguageServer,
9419    completions: Vec<copilot::request::Completion>,
9420    completions_cycling: Vec<copilot::request::Completion>,
9421) {
9422    lsp.handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| {
9423        let completions = completions.clone();
9424        async move {
9425            Ok(copilot::request::GetCompletionsResult {
9426                completions: completions.clone(),
9427            })
9428        }
9429    });
9430    lsp.handle_request::<copilot::request::GetCompletionsCycling, _, _>(move |_params, _cx| {
9431        let completions_cycling = completions_cycling.clone();
9432        async move {
9433            Ok(copilot::request::GetCompletionsResult {
9434                completions: completions_cycling.clone(),
9435            })
9436        }
9437    });
9438}
9439
9440pub(crate) fn update_test_language_settings(
9441    cx: &mut TestAppContext,
9442    f: impl Fn(&mut AllLanguageSettingsContent),
9443) {
9444    _ = cx.update(|cx| {
9445        cx.update_global(|store: &mut SettingsStore, cx| {
9446            store.update_user_settings::<AllLanguageSettings>(cx, f);
9447        });
9448    });
9449}
9450
9451pub(crate) fn update_test_project_settings(
9452    cx: &mut TestAppContext,
9453    f: impl Fn(&mut ProjectSettings),
9454) {
9455    _ = cx.update(|cx| {
9456        cx.update_global(|store: &mut SettingsStore, cx| {
9457            store.update_user_settings::<ProjectSettings>(cx, f);
9458        });
9459    });
9460}
9461
9462pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
9463    _ = cx.update(|cx| {
9464        let store = SettingsStore::test(cx);
9465        cx.set_global(store);
9466        theme::init(theme::LoadThemes::JustBase, cx);
9467        release_channel::init("0.0.0", cx);
9468        client::init_settings(cx);
9469        language::init(cx);
9470        Project::init_settings(cx);
9471        workspace::init_settings(cx);
9472        crate::init(cx);
9473    });
9474
9475    update_test_language_settings(cx, f);
9476}
9477
9478pub(crate) fn rust_lang() -> Arc<Language> {
9479    Arc::new(Language::new(
9480        LanguageConfig {
9481            name: "Rust".into(),
9482            matcher: LanguageMatcher {
9483                path_suffixes: vec!["rs".to_string()],
9484                ..Default::default()
9485            },
9486            ..Default::default()
9487        },
9488        Some(tree_sitter_rust::language()),
9489    ))
9490}
9491
9492#[track_caller]
9493fn assert_hunk_revert(
9494    not_reverted_text_with_selections: &str,
9495    expected_not_reverted_hunk_statuses: Vec<DiffHunkStatus>,
9496    expected_reverted_text_with_selections: &str,
9497    base_text: &str,
9498    cx: &mut EditorLspTestContext,
9499) {
9500    cx.set_state(not_reverted_text_with_selections);
9501    cx.update_editor(|editor, cx| {
9502        editor
9503            .buffer()
9504            .read(cx)
9505            .as_singleton()
9506            .unwrap()
9507            .update(cx, |buffer, cx| {
9508                buffer.set_diff_base(Some(base_text.to_string()), cx);
9509            });
9510    });
9511    cx.executor().run_until_parked();
9512
9513    let reverted_hunk_statuses = cx.update_editor(|editor, cx| {
9514        let snapshot = editor
9515            .buffer()
9516            .read(cx)
9517            .as_singleton()
9518            .unwrap()
9519            .read(cx)
9520            .snapshot();
9521        let reverted_hunk_statuses = snapshot
9522            .git_diff_hunks_in_row_range(0..u32::MAX)
9523            .map(|hunk| hunk.status())
9524            .collect::<Vec<_>>();
9525
9526        editor.revert_selected_hunks(&RevertSelectedHunks, cx);
9527        reverted_hunk_statuses
9528    });
9529    cx.executor().run_until_parked();
9530    cx.assert_editor_state(expected_reverted_text_with_selections);
9531    assert_eq!(reverted_hunk_statuses, expected_not_reverted_hunk_statuses);
9532}