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