editor_tests.rs

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