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