editor_tests.rs

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