test.rs

   1mod neovim_backed_test_context;
   2mod neovim_connection;
   3mod vim_test_context;
   4
   5use std::time::Duration;
   6
   7use collections::HashMap;
   8use command_palette::CommandPalette;
   9use editor::{
  10    AnchorRangeExt, DisplayPoint, Editor, EditorMode, MultiBuffer, actions::DeleteLine,
  11    code_context_menus::CodeContextMenu, display_map::DisplayRow,
  12    test::editor_test_context::EditorTestContext,
  13};
  14use futures::StreamExt;
  15use gpui::{KeyBinding, Modifiers, MouseButton, TestAppContext, px};
  16use language::Point;
  17pub use neovim_backed_test_context::*;
  18use settings::SettingsStore;
  19use ui::Pixels;
  20use util::test::marked_text_ranges;
  21pub use vim_test_context::*;
  22
  23use indoc::indoc;
  24use search::BufferSearchBar;
  25
  26use crate::{PushSneak, PushSneakBackward, insert::NormalBefore, motion, state::Mode};
  27
  28use util_macros::perf;
  29
  30#[perf]
  31#[gpui::test]
  32async fn test_initially_disabled(cx: &mut gpui::TestAppContext) {
  33    let mut cx = VimTestContext::new(cx, false).await;
  34    cx.simulate_keystrokes("h j k l");
  35    cx.assert_editor_state("hjklˇ");
  36}
  37
  38#[gpui::test]
  39async fn test_neovim(cx: &mut gpui::TestAppContext) {
  40    let mut cx = NeovimBackedTestContext::new(cx).await;
  41
  42    cx.simulate_shared_keystrokes("i").await;
  43    cx.shared_state().await.assert_matches();
  44    cx.simulate_shared_keystrokes("shift-t e s t space t e s t escape 0 d w")
  45        .await;
  46    cx.shared_state().await.assert_matches();
  47    cx.assert_editor_state("ˇtest");
  48}
  49
  50#[perf]
  51#[gpui::test]
  52async fn test_toggle_through_settings(cx: &mut gpui::TestAppContext) {
  53    let mut cx = VimTestContext::new(cx, true).await;
  54
  55    cx.simulate_keystrokes("i");
  56    assert_eq!(cx.mode(), Mode::Insert);
  57
  58    // Editor acts as though vim is disabled
  59    cx.disable_vim();
  60    cx.simulate_keystrokes("h j k l");
  61    cx.assert_editor_state("hjklˇ");
  62
  63    // Selections aren't changed if editor is blurred but vim-mode is still disabled.
  64    cx.cx.set_state("«hjklˇ»");
  65    cx.assert_editor_state("«hjklˇ»");
  66    cx.update_editor(|_, window, _cx| window.blur());
  67    cx.assert_editor_state("«hjklˇ»");
  68    cx.update_editor(|_, window, cx| cx.focus_self(window));
  69    cx.assert_editor_state("«hjklˇ»");
  70
  71    // Enabling dynamically sets vim mode again and restores normal mode
  72    cx.enable_vim();
  73    assert_eq!(cx.mode(), Mode::Normal);
  74    cx.simulate_keystrokes("h h h l");
  75    assert_eq!(cx.buffer_text(), "hjkl".to_owned());
  76    cx.assert_editor_state("hˇjkl");
  77    cx.simulate_keystrokes("i T e s t");
  78    cx.assert_editor_state("hTestˇjkl");
  79
  80    // Disabling and enabling resets to normal mode
  81    assert_eq!(cx.mode(), Mode::Insert);
  82    cx.disable_vim();
  83    cx.enable_vim();
  84    assert_eq!(cx.mode(), Mode::Normal);
  85}
  86
  87#[perf]
  88#[gpui::test]
  89async fn test_cancel_selection(cx: &mut gpui::TestAppContext) {
  90    let mut cx = VimTestContext::new(cx, true).await;
  91
  92    cx.set_state(
  93        indoc! {"The quick brown fox juˇmps over the lazy dog"},
  94        Mode::Normal,
  95    );
  96    // jumps
  97    cx.simulate_keystrokes("v l l");
  98    cx.assert_editor_state("The quick brown fox ju«mpsˇ» over the lazy dog");
  99
 100    cx.simulate_keystrokes("escape");
 101    cx.assert_editor_state("The quick brown fox jumpˇs over the lazy dog");
 102
 103    // go back to the same selection state
 104    cx.simulate_keystrokes("v h h");
 105    cx.assert_editor_state("The quick brown fox ju«ˇmps» over the lazy dog");
 106
 107    // Ctrl-[ should behave like Esc
 108    cx.simulate_keystrokes("ctrl-[");
 109    cx.assert_editor_state("The quick brown fox juˇmps over the lazy dog");
 110}
 111
 112#[perf]
 113#[gpui::test]
 114async fn test_buffer_search(cx: &mut gpui::TestAppContext) {
 115    let mut cx = VimTestContext::new(cx, true).await;
 116
 117    cx.set_state(
 118        indoc! {"
 119            The quick brown
 120            fox juˇmps over
 121            the lazy dog"},
 122        Mode::Normal,
 123    );
 124    cx.simulate_keystrokes("/");
 125
 126    let search_bar = cx.workspace(|workspace, _, cx| {
 127        workspace
 128            .active_pane()
 129            .read(cx)
 130            .toolbar()
 131            .read(cx)
 132            .item_of_type::<BufferSearchBar>()
 133            .expect("Buffer search bar should be deployed")
 134    });
 135
 136    cx.update_entity(search_bar, |bar, _, cx| {
 137        assert_eq!(bar.query(cx), "");
 138    })
 139}
 140
 141#[perf]
 142#[gpui::test]
 143async fn test_count_down(cx: &mut gpui::TestAppContext) {
 144    let mut cx = VimTestContext::new(cx, true).await;
 145
 146    cx.set_state(indoc! {"aˇa\nbb\ncc\ndd\nee"}, Mode::Normal);
 147    cx.simulate_keystrokes("2 down");
 148    cx.assert_editor_state("aa\nbb\ncˇc\ndd\nee");
 149    cx.simulate_keystrokes("9 down");
 150    cx.assert_editor_state("aa\nbb\ncc\ndd\neˇe");
 151}
 152
 153#[perf]
 154#[gpui::test]
 155async fn test_end_of_document_710(cx: &mut gpui::TestAppContext) {
 156    let mut cx = VimTestContext::new(cx, true).await;
 157
 158    // goes to end by default
 159    cx.set_state(indoc! {"aˇa\nbb\ncc"}, Mode::Normal);
 160    cx.simulate_keystrokes("shift-g");
 161    cx.assert_editor_state("aa\nbb\ncˇc");
 162
 163    // can go to line 1 (https://github.com/zed-industries/zed/issues/5812)
 164    cx.simulate_keystrokes("1 shift-g");
 165    cx.assert_editor_state("aˇa\nbb\ncc");
 166}
 167
 168#[perf]
 169#[gpui::test]
 170async fn test_end_of_line_with_times(cx: &mut gpui::TestAppContext) {
 171    let mut cx = VimTestContext::new(cx, true).await;
 172
 173    // goes to current line end
 174    cx.set_state(indoc! {"ˇaa\nbb\ncc"}, Mode::Normal);
 175    cx.simulate_keystrokes("$");
 176    cx.assert_editor_state("aˇa\nbb\ncc");
 177
 178    // goes to next line end
 179    cx.simulate_keystrokes("2 $");
 180    cx.assert_editor_state("aa\nbˇb\ncc");
 181
 182    // try to exceed the final line.
 183    cx.simulate_keystrokes("4 $");
 184    cx.assert_editor_state("aa\nbb\ncˇc");
 185}
 186
 187#[perf]
 188#[gpui::test]
 189async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 190    let mut cx = VimTestContext::new(cx, true).await;
 191
 192    // works in normal mode
 193    cx.set_state(indoc! {"aa\nbˇb\ncc"}, Mode::Normal);
 194    cx.simulate_keystrokes("> >");
 195    cx.assert_editor_state("aa\n    bˇb\ncc");
 196    cx.simulate_keystrokes("< <");
 197    cx.assert_editor_state("aa\nbˇb\ncc");
 198
 199    // works in visual mode
 200    cx.simulate_keystrokes("shift-v down >");
 201    cx.assert_editor_state("aa\n    bˇb\n    cc");
 202
 203    // works as operator
 204    cx.set_state("aa\nbˇb\ncc\n", Mode::Normal);
 205    cx.simulate_keystrokes("> j");
 206    cx.assert_editor_state("aa\n    bˇb\n    cc\n");
 207    cx.simulate_keystrokes("< k");
 208    cx.assert_editor_state("aa\nbˇb\n    cc\n");
 209    cx.simulate_keystrokes("> i p");
 210    cx.assert_editor_state("    aa\n    bˇb\n        cc\n");
 211    cx.simulate_keystrokes("< i p");
 212    cx.assert_editor_state("aa\nbˇb\n    cc\n");
 213    cx.simulate_keystrokes("< i p");
 214    cx.assert_editor_state("aa\nbˇb\ncc\n");
 215
 216    cx.set_state("ˇaa\nbb\ncc\n", Mode::Normal);
 217    cx.simulate_keystrokes("> 2 j");
 218    cx.assert_editor_state("    ˇaa\n    bb\n    cc\n");
 219
 220    cx.set_state("aa\nbb\nˇcc\n", Mode::Normal);
 221    cx.simulate_keystrokes("> 2 k");
 222    cx.assert_editor_state("    aa\n    bb\n    ˇcc\n");
 223
 224    // works with repeat
 225    cx.set_state("a\nb\nccˇc\n", Mode::Normal);
 226    cx.simulate_keystrokes("> 2 k");
 227    cx.assert_editor_state("    a\n    b\n    ccˇc\n");
 228    cx.simulate_keystrokes(".");
 229    cx.assert_editor_state("        a\n        b\n        ccˇc\n");
 230    cx.simulate_keystrokes("v k <");
 231    cx.assert_editor_state("        a\n\n    ccc\n");
 232    cx.simulate_keystrokes(".");
 233    cx.assert_editor_state("        a\n\nccc\n");
 234}
 235
 236#[gpui::test]
 237async fn test_escape_command_palette(cx: &mut gpui::TestAppContext) {
 238    let mut cx = VimTestContext::new(cx, true).await;
 239
 240    cx.set_state("aˇbc\n", Mode::Normal);
 241    cx.simulate_keystrokes("i cmd-shift-p");
 242
 243    assert!(
 244        cx.workspace(|workspace, _, cx| workspace.active_modal::<CommandPalette>(cx).is_some())
 245    );
 246    cx.simulate_keystrokes("escape");
 247    cx.run_until_parked();
 248    assert!(
 249        !cx.workspace(|workspace, _, cx| workspace.active_modal::<CommandPalette>(cx).is_some())
 250    );
 251    cx.assert_state("aˇbc\n", Mode::Insert);
 252}
 253
 254#[perf]
 255#[gpui::test]
 256async fn test_escape_cancels(cx: &mut gpui::TestAppContext) {
 257    let mut cx = VimTestContext::new(cx, true).await;
 258
 259    cx.set_state("aˇbˇc", Mode::Normal);
 260    cx.simulate_keystrokes("escape");
 261
 262    cx.assert_state("aˇbc", Mode::Normal);
 263}
 264
 265#[perf]
 266#[gpui::test]
 267async fn test_selection_on_search(cx: &mut gpui::TestAppContext) {
 268    let mut cx = VimTestContext::new(cx, true).await;
 269
 270    cx.set_state(indoc! {"aa\nbˇb\ncc\ncc\ncc\n"}, Mode::Normal);
 271    cx.simulate_keystrokes("/ c c");
 272
 273    let search_bar = cx.workspace(|workspace, _, cx| {
 274        workspace
 275            .active_pane()
 276            .read(cx)
 277            .toolbar()
 278            .read(cx)
 279            .item_of_type::<BufferSearchBar>()
 280            .expect("Buffer search bar should be deployed")
 281    });
 282
 283    cx.update_entity(search_bar, |bar, _, cx| {
 284        assert_eq!(bar.query(cx), "cc");
 285    });
 286
 287    cx.update_editor(|editor, window, cx| {
 288        let highlights = editor.all_text_background_highlights(window, cx);
 289        assert_eq!(3, highlights.len());
 290        assert_eq!(
 291            DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 2),
 292            highlights[0].0
 293        )
 294    });
 295    cx.simulate_keystrokes("enter");
 296
 297    cx.assert_state(indoc! {"aa\nbb\nˇcc\ncc\ncc\n"}, Mode::Normal);
 298    cx.simulate_keystrokes("n");
 299    cx.assert_state(indoc! {"aa\nbb\ncc\nˇcc\ncc\n"}, Mode::Normal);
 300    cx.simulate_keystrokes("shift-n");
 301    cx.assert_state(indoc! {"aa\nbb\nˇcc\ncc\ncc\n"}, Mode::Normal);
 302}
 303
 304#[perf]
 305#[gpui::test]
 306async fn test_word_characters(cx: &mut gpui::TestAppContext) {
 307    let mut cx = VimTestContext::new_typescript(cx).await;
 308    cx.set_state(
 309        indoc! { "
 310        class A {
 311            #ˇgoop = 99;
 312            $ˇgoop () { return this.#gˇoop };
 313        };
 314        console.log(new A().$gooˇp())
 315    "},
 316        Mode::Normal,
 317    );
 318    cx.simulate_keystrokes("v i w");
 319    cx.assert_state(
 320        indoc! {"
 321        class A {
 322            «#goopˇ» = 99;
 323            «$goopˇ» () { return this.«#goopˇ» };
 324        };
 325        console.log(new A().«$goopˇ»())
 326    "},
 327        Mode::Visual,
 328    )
 329}
 330
 331#[perf]
 332#[gpui::test]
 333async fn test_kebab_case(cx: &mut gpui::TestAppContext) {
 334    let mut cx = VimTestContext::new_html(cx).await;
 335    cx.set_state(
 336        indoc! { r#"
 337            <div><a class="bg-rˇed"></a></div>
 338            "#},
 339        Mode::Normal,
 340    );
 341    cx.simulate_keystrokes("v i w");
 342    cx.assert_state(
 343        indoc! { r#"
 344        <div><a class="bg-«redˇ»"></a></div>
 345        "#
 346        },
 347        Mode::Visual,
 348    )
 349}
 350
 351#[gpui::test]
 352async fn test_join_lines(cx: &mut gpui::TestAppContext) {
 353    let mut cx = NeovimBackedTestContext::new(cx).await;
 354
 355    cx.set_shared_state(indoc! {"
 356      ˇone
 357      two
 358      three
 359      four
 360      five
 361      six
 362      "})
 363        .await;
 364    cx.simulate_shared_keystrokes("shift-j").await;
 365    cx.shared_state().await.assert_eq(indoc! {"
 366          oneˇ two
 367          three
 368          four
 369          five
 370          six
 371          "});
 372    cx.simulate_shared_keystrokes("3 shift-j").await;
 373    cx.shared_state().await.assert_eq(indoc! {"
 374          one two threeˇ four
 375          five
 376          six
 377          "});
 378
 379    cx.set_shared_state(indoc! {"
 380      ˇone
 381      two
 382      three
 383      four
 384      five
 385      six
 386      "})
 387        .await;
 388    cx.simulate_shared_keystrokes("j v 3 j shift-j").await;
 389    cx.shared_state().await.assert_eq(indoc! {"
 390      one
 391      two three fourˇ five
 392      six
 393      "});
 394
 395    cx.set_shared_state(indoc! {"
 396      ˇone
 397      two
 398      three
 399      four
 400      five
 401      six
 402      "})
 403        .await;
 404    cx.simulate_shared_keystrokes("g shift-j").await;
 405    cx.shared_state().await.assert_eq(indoc! {"
 406          oneˇtwo
 407          three
 408          four
 409          five
 410          six
 411          "});
 412    cx.simulate_shared_keystrokes("3 g shift-j").await;
 413    cx.shared_state().await.assert_eq(indoc! {"
 414          onetwothreeˇfour
 415          five
 416          six
 417          "});
 418
 419    cx.set_shared_state(indoc! {"
 420      ˇone
 421      two
 422      three
 423      four
 424      five
 425      six
 426      "})
 427        .await;
 428    cx.simulate_shared_keystrokes("j v 3 j g shift-j").await;
 429    cx.shared_state().await.assert_eq(indoc! {"
 430      one
 431      twothreefourˇfive
 432      six
 433      "});
 434}
 435
 436#[cfg(target_os = "macos")]
 437#[gpui::test]
 438async fn test_wrapped_lines(cx: &mut gpui::TestAppContext) {
 439    let mut cx = NeovimBackedTestContext::new(cx).await;
 440
 441    cx.set_shared_wrap(12).await;
 442    // tests line wrap as follows:
 443    //  1: twelve char
 444    //     twelve char
 445    //  2: twelve char
 446    cx.set_shared_state(indoc! { "
 447        tˇwelve char twelve char
 448        twelve char
 449    "})
 450        .await;
 451    cx.simulate_shared_keystrokes("j").await;
 452    cx.shared_state().await.assert_eq(indoc! {"
 453        twelve char twelve char
 454        tˇwelve char
 455    "});
 456    cx.simulate_shared_keystrokes("k").await;
 457    cx.shared_state().await.assert_eq(indoc! {"
 458        tˇwelve char twelve char
 459        twelve char
 460    "});
 461    cx.simulate_shared_keystrokes("g j").await;
 462    cx.shared_state().await.assert_eq(indoc! {"
 463        twelve char tˇwelve char
 464        twelve char
 465    "});
 466    cx.simulate_shared_keystrokes("g j").await;
 467    cx.shared_state().await.assert_eq(indoc! {"
 468        twelve char twelve char
 469        tˇwelve char
 470    "});
 471
 472    cx.simulate_shared_keystrokes("g k").await;
 473    cx.shared_state().await.assert_eq(indoc! {"
 474        twelve char tˇwelve char
 475        twelve char
 476    "});
 477
 478    cx.simulate_shared_keystrokes("g ^").await;
 479    cx.shared_state().await.assert_eq(indoc! {"
 480        twelve char ˇtwelve char
 481        twelve char
 482    "});
 483
 484    cx.simulate_shared_keystrokes("^").await;
 485    cx.shared_state().await.assert_eq(indoc! {"
 486        ˇtwelve char twelve char
 487        twelve char
 488    "});
 489
 490    cx.simulate_shared_keystrokes("g $").await;
 491    cx.shared_state().await.assert_eq(indoc! {"
 492        twelve charˇ twelve char
 493        twelve char
 494    "});
 495    cx.simulate_shared_keystrokes("$").await;
 496    cx.shared_state().await.assert_eq(indoc! {"
 497        twelve char twelve chaˇr
 498        twelve char
 499    "});
 500
 501    cx.set_shared_state(indoc! { "
 502        tˇwelve char twelve char
 503        twelve char
 504    "})
 505        .await;
 506    cx.simulate_shared_keystrokes("enter").await;
 507    cx.shared_state().await.assert_eq(indoc! {"
 508            twelve char twelve char
 509            ˇtwelve char
 510        "});
 511
 512    cx.set_shared_state(indoc! { "
 513        twelve char
 514        tˇwelve char twelve char
 515        twelve char
 516    "})
 517        .await;
 518    cx.simulate_shared_keystrokes("o o escape").await;
 519    cx.shared_state().await.assert_eq(indoc! {"
 520        twelve char
 521        twelve char twelve char
 522        ˇo
 523        twelve char
 524    "});
 525
 526    cx.set_shared_state(indoc! { "
 527        twelve char
 528        tˇwelve char twelve char
 529        twelve char
 530    "})
 531        .await;
 532    cx.simulate_shared_keystrokes("shift-a a escape").await;
 533    cx.shared_state().await.assert_eq(indoc! {"
 534        twelve char
 535        twelve char twelve charˇa
 536        twelve char
 537    "});
 538    cx.simulate_shared_keystrokes("shift-i i escape").await;
 539    cx.shared_state().await.assert_eq(indoc! {"
 540        twelve char
 541        ˇitwelve char twelve chara
 542        twelve char
 543    "});
 544    cx.simulate_shared_keystrokes("shift-d").await;
 545    cx.shared_state().await.assert_eq(indoc! {"
 546        twelve char
 547        ˇ
 548        twelve char
 549    "});
 550
 551    cx.set_shared_state(indoc! { "
 552        twelve char
 553        twelve char tˇwelve char
 554        twelve char
 555    "})
 556        .await;
 557    cx.simulate_shared_keystrokes("shift-o o escape").await;
 558    cx.shared_state().await.assert_eq(indoc! {"
 559        twelve char
 560        ˇo
 561        twelve char twelve char
 562        twelve char
 563    "});
 564
 565    // line wraps as:
 566    // fourteen ch
 567    // ar
 568    // fourteen ch
 569    // ar
 570    cx.set_shared_state(indoc! { "
 571        fourteen chaˇr
 572        fourteen char
 573    "})
 574        .await;
 575
 576    cx.simulate_shared_keystrokes("d i w").await;
 577    cx.shared_state().await.assert_eq(indoc! {"
 578        fourteenˇ•
 579        fourteen char
 580    "});
 581    cx.simulate_shared_keystrokes("j shift-f e f r").await;
 582    cx.shared_state().await.assert_eq(indoc! {"
 583        fourteen•
 584        fourteen chaˇr
 585    "});
 586}
 587
 588#[gpui::test]
 589async fn test_folds(cx: &mut gpui::TestAppContext) {
 590    let mut cx = NeovimBackedTestContext::new(cx).await;
 591    cx.set_neovim_option("foldmethod=manual").await;
 592
 593    cx.set_shared_state(indoc! { "
 594        fn boop() {
 595          ˇbarp()
 596          bazp()
 597        }
 598    "})
 599        .await;
 600    cx.simulate_shared_keystrokes("shift-v j z f").await;
 601
 602    // visual display is now:
 603    // fn boop () {
 604    //  [FOLDED]
 605    // }
 606
 607    // TODO: this should not be needed but currently zf does not
 608    // return to normal mode.
 609    cx.simulate_shared_keystrokes("escape").await;
 610
 611    // skip over fold downward
 612    cx.simulate_shared_keystrokes("g g").await;
 613    cx.shared_state().await.assert_eq(indoc! {"
 614        ˇfn boop() {
 615          barp()
 616          bazp()
 617        }
 618    "});
 619
 620    cx.simulate_shared_keystrokes("j j").await;
 621    cx.shared_state().await.assert_eq(indoc! {"
 622        fn boop() {
 623          barp()
 624          bazp()
 625        ˇ}
 626    "});
 627
 628    // skip over fold upward
 629    cx.simulate_shared_keystrokes("2 k").await;
 630    cx.shared_state().await.assert_eq(indoc! {"
 631        ˇfn boop() {
 632          barp()
 633          bazp()
 634        }
 635    "});
 636
 637    // yank the fold
 638    cx.simulate_shared_keystrokes("down y y").await;
 639    cx.shared_clipboard()
 640        .await
 641        .assert_eq("  barp()\n  bazp()\n");
 642
 643    // re-open
 644    cx.simulate_shared_keystrokes("z o").await;
 645    cx.shared_state().await.assert_eq(indoc! {"
 646        fn boop() {
 647        ˇ  barp()
 648          bazp()
 649        }
 650    "});
 651}
 652
 653#[gpui::test]
 654async fn test_folds_panic(cx: &mut gpui::TestAppContext) {
 655    let mut cx = NeovimBackedTestContext::new(cx).await;
 656    cx.set_neovim_option("foldmethod=manual").await;
 657
 658    cx.set_shared_state(indoc! { "
 659        fn boop() {
 660          ˇbarp()
 661          bazp()
 662        }
 663    "})
 664        .await;
 665    cx.simulate_shared_keystrokes("shift-v j z f").await;
 666    cx.simulate_shared_keystrokes("escape").await;
 667    cx.simulate_shared_keystrokes("g g").await;
 668    cx.simulate_shared_keystrokes("5 d j").await;
 669    cx.shared_state().await.assert_eq("ˇ");
 670    cx.set_shared_state(indoc! {"
 671        fn boop() {
 672          ˇbarp()
 673          bazp()
 674        }
 675    "})
 676        .await;
 677    cx.simulate_shared_keystrokes("shift-v j j z f").await;
 678    cx.simulate_shared_keystrokes("escape").await;
 679    cx.simulate_shared_keystrokes("shift-g shift-v").await;
 680    cx.shared_state().await.assert_eq(indoc! {"
 681        fn boop() {
 682          barp()
 683          bazp()
 684        }
 685        ˇ"});
 686}
 687
 688#[gpui::test]
 689async fn test_clear_counts(cx: &mut gpui::TestAppContext) {
 690    let mut cx = NeovimBackedTestContext::new(cx).await;
 691
 692    cx.set_shared_state(indoc! {"
 693        The quick brown
 694        fox juˇmps over
 695        the lazy dog"})
 696        .await;
 697
 698    cx.simulate_shared_keystrokes("4 escape 3 d l").await;
 699    cx.shared_state().await.assert_eq(indoc! {"
 700        The quick brown
 701        fox juˇ over
 702        the lazy dog"});
 703}
 704
 705#[gpui::test]
 706async fn test_zero(cx: &mut gpui::TestAppContext) {
 707    let mut cx = NeovimBackedTestContext::new(cx).await;
 708
 709    cx.set_shared_state(indoc! {"
 710        The quˇick brown
 711        fox jumps over
 712        the lazy dog"})
 713        .await;
 714
 715    cx.simulate_shared_keystrokes("0").await;
 716    cx.shared_state().await.assert_eq(indoc! {"
 717        ˇThe quick brown
 718        fox jumps over
 719        the lazy dog"});
 720
 721    cx.simulate_shared_keystrokes("1 0 l").await;
 722    cx.shared_state().await.assert_eq(indoc! {"
 723        The quick ˇbrown
 724        fox jumps over
 725        the lazy dog"});
 726}
 727
 728#[gpui::test]
 729async fn test_selection_goal(cx: &mut gpui::TestAppContext) {
 730    let mut cx = NeovimBackedTestContext::new(cx).await;
 731
 732    cx.set_shared_state(indoc! {"
 733        ;;ˇ;
 734        Lorem Ipsum"})
 735        .await;
 736
 737    cx.simulate_shared_keystrokes("a down up ; down up").await;
 738    cx.shared_state().await.assert_eq(indoc! {"
 739        ;;;;ˇ
 740        Lorem Ipsum"});
 741}
 742
 743#[cfg(target_os = "macos")]
 744#[gpui::test]
 745async fn test_wrapped_motions(cx: &mut gpui::TestAppContext) {
 746    let mut cx = NeovimBackedTestContext::new(cx).await;
 747
 748    cx.set_shared_wrap(12).await;
 749
 750    cx.set_shared_state(indoc! {"
 751                aaˇaa
 752                😃😃"
 753    })
 754    .await;
 755    cx.simulate_shared_keystrokes("j").await;
 756    cx.shared_state().await.assert_eq(indoc! {"
 757                aaaa
 758                😃ˇ😃"
 759    });
 760
 761    cx.set_shared_state(indoc! {"
 762                123456789012aaˇaa
 763                123456789012😃😃"
 764    })
 765    .await;
 766    cx.simulate_shared_keystrokes("j").await;
 767    cx.shared_state().await.assert_eq(indoc! {"
 768        123456789012aaaa
 769        123456789012😃ˇ😃"
 770    });
 771
 772    cx.set_shared_state(indoc! {"
 773                123456789012aaˇaa
 774                123456789012😃😃"
 775    })
 776    .await;
 777    cx.simulate_shared_keystrokes("j").await;
 778    cx.shared_state().await.assert_eq(indoc! {"
 779        123456789012aaaa
 780        123456789012😃ˇ😃"
 781    });
 782
 783    cx.set_shared_state(indoc! {"
 784        123456789012aaaaˇaaaaaaaa123456789012
 785        wow
 786        123456789012😃😃😃😃😃😃123456789012"
 787    })
 788    .await;
 789    cx.simulate_shared_keystrokes("j j").await;
 790    cx.shared_state().await.assert_eq(indoc! {"
 791        123456789012aaaaaaaaaaaa123456789012
 792        wow
 793        123456789012😃😃ˇ😃😃😃😃123456789012"
 794    });
 795}
 796
 797#[gpui::test]
 798async fn test_wrapped_delete_end_document(cx: &mut gpui::TestAppContext) {
 799    let mut cx = NeovimBackedTestContext::new(cx).await;
 800
 801    cx.set_shared_wrap(12).await;
 802
 803    cx.set_shared_state(indoc! {"
 804                aaˇaaaaaaaaaaaaaaaaaa
 805                bbbbbbbbbbbbbbbbbbbb
 806                cccccccccccccccccccc"
 807    })
 808    .await;
 809    cx.simulate_shared_keystrokes("d shift-g i z z z").await;
 810    cx.shared_state().await.assert_eq(indoc! {"
 811                zzzˇ"
 812    });
 813}
 814
 815#[gpui::test]
 816async fn test_paragraphs_dont_wrap(cx: &mut gpui::TestAppContext) {
 817    let mut cx = NeovimBackedTestContext::new(cx).await;
 818
 819    cx.set_shared_state(indoc! {"
 820        one
 821        ˇ
 822        two"})
 823        .await;
 824
 825    cx.simulate_shared_keystrokes("} }").await;
 826    cx.shared_state().await.assert_eq(indoc! {"
 827        one
 828
 829        twˇo"});
 830
 831    cx.simulate_shared_keystrokes("{ { {").await;
 832    cx.shared_state().await.assert_eq(indoc! {"
 833        ˇone
 834
 835        two"});
 836}
 837
 838#[perf]
 839#[gpui::test]
 840async fn test_select_all_issue_2170(cx: &mut gpui::TestAppContext) {
 841    let mut cx = VimTestContext::new(cx, true).await;
 842
 843    cx.set_state(
 844        indoc! {"
 845        defmodule Test do
 846            def test(a, ˇ[_, _] = b), do: IO.puts('hi')
 847        end
 848    "},
 849        Mode::Normal,
 850    );
 851    cx.simulate_keystrokes("g a");
 852    cx.assert_state(
 853        indoc! {"
 854        defmodule Test do
 855            def test(a, «[ˇ»_, _] = b), do: IO.puts('hi')
 856        end
 857    "},
 858        Mode::Visual,
 859    );
 860}
 861
 862#[gpui::test]
 863async fn test_jk(cx: &mut gpui::TestAppContext) {
 864    let mut cx = NeovimBackedTestContext::new(cx).await;
 865
 866    cx.update(|_, cx| {
 867        cx.bind_keys([KeyBinding::new(
 868            "j k",
 869            NormalBefore,
 870            Some("vim_mode == insert"),
 871        )])
 872    });
 873    cx.neovim.exec("imap jk <esc>").await;
 874
 875    cx.set_shared_state("ˇhello").await;
 876    cx.simulate_shared_keystrokes("i j o j k").await;
 877    cx.shared_state().await.assert_eq("jˇohello");
 878}
 879
 880fn assert_pending_input(cx: &mut VimTestContext, expected: &str) {
 881    cx.update_editor(|editor, window, cx| {
 882        let snapshot = editor.snapshot(window, cx);
 883        let highlights = editor
 884            .text_highlights::<editor::PendingInput>(cx)
 885            .unwrap()
 886            .1;
 887        let (_, ranges) = marked_text_ranges(expected, false);
 888
 889        assert_eq!(
 890            highlights
 891                .iter()
 892                .map(|highlight| highlight.to_offset(&snapshot.buffer_snapshot))
 893                .collect::<Vec<_>>(),
 894            ranges
 895        )
 896    });
 897}
 898
 899#[perf]
 900#[gpui::test]
 901async fn test_jk_multi(cx: &mut gpui::TestAppContext) {
 902    let mut cx = VimTestContext::new(cx, true).await;
 903
 904    cx.update(|_, cx| {
 905        cx.bind_keys([KeyBinding::new(
 906            "j k l",
 907            NormalBefore,
 908            Some("vim_mode == insert"),
 909        )])
 910    });
 911
 912    cx.set_state("ˇone ˇone ˇone", Mode::Normal);
 913    cx.simulate_keystrokes("i j");
 914    cx.simulate_keystrokes("k");
 915    cx.assert_state("ˇjkone ˇjkone ˇjkone", Mode::Insert);
 916    assert_pending_input(&mut cx, "«jk»one «jk»one «jk»one");
 917    cx.simulate_keystrokes("o j k");
 918    cx.assert_state("jkoˇjkone jkoˇjkone jkoˇjkone", Mode::Insert);
 919    assert_pending_input(&mut cx, "jko«jk»one jko«jk»one jko«jk»one");
 920    cx.simulate_keystrokes("l");
 921    cx.assert_state("jkˇoone jkˇoone jkˇoone", Mode::Normal);
 922}
 923
 924#[gpui::test]
 925async fn test_jk_delay(cx: &mut gpui::TestAppContext) {
 926    let mut cx = VimTestContext::new(cx, true).await;
 927
 928    cx.update(|_, cx| {
 929        cx.bind_keys([KeyBinding::new(
 930            "j k",
 931            NormalBefore,
 932            Some("vim_mode == insert"),
 933        )])
 934    });
 935
 936    cx.set_state("ˇhello", Mode::Normal);
 937    cx.simulate_keystrokes("i j");
 938    cx.executor().advance_clock(Duration::from_millis(500));
 939    cx.run_until_parked();
 940    cx.assert_state("ˇjhello", Mode::Insert);
 941    cx.update_editor(|editor, window, cx| {
 942        let snapshot = editor.snapshot(window, cx);
 943        let highlights = editor
 944            .text_highlights::<editor::PendingInput>(cx)
 945            .unwrap()
 946            .1;
 947
 948        assert_eq!(
 949            highlights
 950                .iter()
 951                .map(|highlight| highlight.to_offset(&snapshot.buffer_snapshot))
 952                .collect::<Vec<_>>(),
 953            vec![0..1]
 954        )
 955    });
 956    cx.executor().advance_clock(Duration::from_millis(500));
 957    cx.run_until_parked();
 958    cx.assert_state("jˇhello", Mode::Insert);
 959    cx.simulate_keystrokes("k j k");
 960    cx.assert_state("jˇkhello", Mode::Normal);
 961}
 962
 963#[gpui::test]
 964async fn test_comma_w(cx: &mut gpui::TestAppContext) {
 965    let mut cx = NeovimBackedTestContext::new(cx).await;
 966
 967    cx.update(|_, cx| {
 968        cx.bind_keys([KeyBinding::new(
 969            ", w",
 970            motion::Down {
 971                display_lines: false,
 972            },
 973            Some("vim_mode == normal"),
 974        )])
 975    });
 976    cx.neovim.exec("map ,w j").await;
 977
 978    cx.set_shared_state("ˇhello hello\nhello hello").await;
 979    cx.simulate_shared_keystrokes("f o ; , w").await;
 980    cx.shared_state()
 981        .await
 982        .assert_eq("hello hello\nhello hellˇo");
 983
 984    cx.set_shared_state("ˇhello hello\nhello hello").await;
 985    cx.simulate_shared_keystrokes("f o ; , i").await;
 986    cx.shared_state()
 987        .await
 988        .assert_eq("hellˇo hello\nhello hello");
 989}
 990
 991#[perf]
 992#[gpui::test]
 993async fn test_completion_menu_scroll_aside(cx: &mut TestAppContext) {
 994    let mut cx = VimTestContext::new_typescript(cx).await;
 995
 996    cx.lsp
 997        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
 998            Ok(Some(lsp::CompletionResponse::Array(vec![
 999                lsp::CompletionItem {
1000                    label: "Test Item".to_string(),
1001                    documentation: Some(lsp::Documentation::String(
1002                        "This is some very long documentation content that will be displayed in the aside panel for scrolling.\n".repeat(50)
1003                    )),
1004                    ..Default::default()
1005                },
1006            ])))
1007        });
1008
1009    cx.set_state("variableˇ", Mode::Insert);
1010    cx.simulate_keystroke(".");
1011    cx.executor().run_until_parked();
1012
1013    let mut initial_offset: Pixels = px(0.0);
1014
1015    cx.update_editor(|editor, _, _| {
1016        let binding = editor.context_menu().borrow();
1017        let Some(CodeContextMenu::Completions(menu)) = binding.as_ref() else {
1018            panic!("Should have completions menu open");
1019        };
1020
1021        initial_offset = menu.scroll_handle_aside.offset().y;
1022    });
1023
1024    // The `ctrl-e` shortcut should scroll the completion menu's aside content
1025    // down, so the updated offset should be lower than the initial offset.
1026    cx.simulate_keystroke("ctrl-e");
1027    cx.update_editor(|editor, _, _| {
1028        let binding = editor.context_menu().borrow();
1029        let Some(CodeContextMenu::Completions(menu)) = binding.as_ref() else {
1030            panic!("Should have completions menu open");
1031        };
1032
1033        assert!(menu.scroll_handle_aside.offset().y < initial_offset);
1034    });
1035
1036    // The `ctrl-y` shortcut should do the inverse scrolling as `ctrl-e`, so the
1037    // offset should now be the same as the initial offset.
1038    cx.simulate_keystroke("ctrl-y");
1039    cx.update_editor(|editor, _, _| {
1040        let binding = editor.context_menu().borrow();
1041        let Some(CodeContextMenu::Completions(menu)) = binding.as_ref() else {
1042            panic!("Should have completions menu open");
1043        };
1044
1045        assert_eq!(menu.scroll_handle_aside.offset().y, initial_offset);
1046    });
1047
1048    // The `ctrl-d` shortcut should scroll the completion menu's aside content
1049    // down, so the updated offset should be lower than the initial offset.
1050    cx.simulate_keystroke("ctrl-d");
1051    cx.update_editor(|editor, _, _| {
1052        let binding = editor.context_menu().borrow();
1053        let Some(CodeContextMenu::Completions(menu)) = binding.as_ref() else {
1054            panic!("Should have completions menu open");
1055        };
1056
1057        assert!(menu.scroll_handle_aside.offset().y < initial_offset);
1058    });
1059
1060    // The `ctrl-u` shortcut should do the inverse scrolling as `ctrl-u`, so the
1061    // offset should now be the same as the initial offset.
1062    cx.simulate_keystroke("ctrl-u");
1063    cx.update_editor(|editor, _, _| {
1064        let binding = editor.context_menu().borrow();
1065        let Some(CodeContextMenu::Completions(menu)) = binding.as_ref() else {
1066            panic!("Should have completions menu open");
1067        };
1068
1069        assert_eq!(menu.scroll_handle_aside.offset().y, initial_offset);
1070    });
1071}
1072
1073#[perf]
1074#[gpui::test]
1075async fn test_rename(cx: &mut gpui::TestAppContext) {
1076    let mut cx = VimTestContext::new_typescript(cx).await;
1077
1078    cx.set_state("const beˇfore = 2; console.log(before)", Mode::Normal);
1079    let def_range = cx.lsp_range("const «beforeˇ» = 2; console.log(before)");
1080    let tgt_range = cx.lsp_range("const before = 2; console.log(«beforeˇ»)");
1081    let mut prepare_request = cx.set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
1082        move |_, _, _| async move { Ok(Some(lsp::PrepareRenameResponse::Range(def_range))) },
1083    );
1084    let mut rename_request =
1085        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, params, _| async move {
1086            Ok(Some(lsp::WorkspaceEdit {
1087                changes: Some(
1088                    [(
1089                        url.clone(),
1090                        vec![
1091                            lsp::TextEdit::new(def_range, params.new_name.clone()),
1092                            lsp::TextEdit::new(tgt_range, params.new_name),
1093                        ],
1094                    )]
1095                    .into(),
1096                ),
1097                ..Default::default()
1098            }))
1099        });
1100
1101    cx.simulate_keystrokes("c d");
1102    prepare_request.next().await.unwrap();
1103    cx.simulate_input("after");
1104    cx.simulate_keystrokes("enter");
1105    rename_request.next().await.unwrap();
1106    cx.assert_state("const afterˇ = 2; console.log(after)", Mode::Normal)
1107}
1108
1109#[perf(iterations = 1)]
1110#[gpui::test]
1111async fn test_remap(cx: &mut gpui::TestAppContext) {
1112    let mut cx = VimTestContext::new(cx, true).await;
1113
1114    // test moving the cursor
1115    cx.update(|_, cx| {
1116        cx.bind_keys([KeyBinding::new(
1117            "g z",
1118            workspace::SendKeystrokes("l l l l".to_string()),
1119            None,
1120        )])
1121    });
1122    cx.set_state("ˇ123456789", Mode::Normal);
1123    cx.simulate_keystrokes("g z");
1124    cx.assert_state("1234ˇ56789", Mode::Normal);
1125
1126    // test switching modes
1127    cx.update(|_, cx| {
1128        cx.bind_keys([KeyBinding::new(
1129            "g y",
1130            workspace::SendKeystrokes("i f o o escape l".to_string()),
1131            None,
1132        )])
1133    });
1134    cx.set_state("ˇ123456789", Mode::Normal);
1135    cx.simulate_keystrokes("g y");
1136    cx.assert_state("fooˇ123456789", Mode::Normal);
1137
1138    // test recursion
1139    cx.update(|_, cx| {
1140        cx.bind_keys([KeyBinding::new(
1141            "g x",
1142            workspace::SendKeystrokes("g z g y".to_string()),
1143            None,
1144        )])
1145    });
1146    cx.set_state("ˇ123456789", Mode::Normal);
1147    cx.simulate_keystrokes("g x");
1148    cx.assert_state("1234fooˇ56789", Mode::Normal);
1149
1150    // test command
1151    cx.update(|_, cx| {
1152        cx.bind_keys([KeyBinding::new(
1153            "g w",
1154            workspace::SendKeystrokes(": j enter".to_string()),
1155            None,
1156        )])
1157    });
1158    cx.set_state("ˇ1234\n56789", Mode::Normal);
1159    cx.simulate_keystrokes("g w");
1160    cx.assert_state("1234ˇ 56789", Mode::Normal);
1161
1162    // test leaving command
1163    cx.update(|_, cx| {
1164        cx.bind_keys([KeyBinding::new(
1165            "g u",
1166            workspace::SendKeystrokes("g w g z".to_string()),
1167            None,
1168        )])
1169    });
1170    cx.set_state("ˇ1234\n56789", Mode::Normal);
1171    cx.simulate_keystrokes("g u");
1172    cx.assert_state("1234 567ˇ89", Mode::Normal);
1173
1174    // test leaving command
1175    cx.update(|_, cx| {
1176        cx.bind_keys([KeyBinding::new(
1177            "g t",
1178            workspace::SendKeystrokes("i space escape".to_string()),
1179            None,
1180        )])
1181    });
1182    cx.set_state("12ˇ34", Mode::Normal);
1183    cx.simulate_keystrokes("g t");
1184    cx.assert_state("12ˇ 34", Mode::Normal);
1185}
1186
1187#[gpui::test]
1188async fn test_undo(cx: &mut gpui::TestAppContext) {
1189    let mut cx = NeovimBackedTestContext::new(cx).await;
1190
1191    cx.set_shared_state("hello quˇoel world").await;
1192    cx.simulate_shared_keystrokes("v i w s c o escape u").await;
1193    cx.shared_state().await.assert_eq("hello ˇquoel world");
1194    cx.simulate_shared_keystrokes("ctrl-r").await;
1195    cx.shared_state().await.assert_eq("hello ˇco world");
1196    cx.simulate_shared_keystrokes("a o right l escape").await;
1197    cx.shared_state().await.assert_eq("hello cooˇl world");
1198    cx.simulate_shared_keystrokes("u").await;
1199    cx.shared_state().await.assert_eq("hello cooˇ world");
1200    cx.simulate_shared_keystrokes("u").await;
1201    cx.shared_state().await.assert_eq("hello cˇo world");
1202    cx.simulate_shared_keystrokes("u").await;
1203    cx.shared_state().await.assert_eq("hello ˇquoel world");
1204
1205    cx.set_shared_state("hello quˇoel world").await;
1206    cx.simulate_shared_keystrokes("v i w ~ u").await;
1207    cx.shared_state().await.assert_eq("hello ˇquoel world");
1208
1209    cx.set_shared_state("\nhello quˇoel world\n").await;
1210    cx.simulate_shared_keystrokes("shift-v s c escape u").await;
1211    cx.shared_state().await.assert_eq("\nˇhello quoel world\n");
1212
1213    cx.set_shared_state(indoc! {"
1214        ˇ1
1215        2
1216        3"})
1217        .await;
1218
1219    cx.simulate_shared_keystrokes("ctrl-v shift-g ctrl-a").await;
1220    cx.shared_state().await.assert_eq(indoc! {"
1221        ˇ2
1222        3
1223        4"});
1224
1225    cx.simulate_shared_keystrokes("u").await;
1226    cx.shared_state().await.assert_eq(indoc! {"
1227        ˇ1
1228        2
1229        3"});
1230}
1231
1232#[perf]
1233#[gpui::test]
1234async fn test_mouse_selection(cx: &mut TestAppContext) {
1235    let mut cx = VimTestContext::new(cx, true).await;
1236
1237    cx.set_state("ˇone two three", Mode::Normal);
1238
1239    let start_point = cx.pixel_position("one twˇo three");
1240    let end_point = cx.pixel_position("one ˇtwo three");
1241
1242    cx.simulate_mouse_down(start_point, MouseButton::Left, Modifiers::none());
1243    cx.simulate_mouse_move(end_point, MouseButton::Left, Modifiers::none());
1244    cx.simulate_mouse_up(end_point, MouseButton::Left, Modifiers::none());
1245
1246    cx.assert_state("one «ˇtwo» three", Mode::Visual)
1247}
1248
1249#[gpui::test]
1250async fn test_lowercase_marks(cx: &mut TestAppContext) {
1251    let mut cx = NeovimBackedTestContext::new(cx).await;
1252
1253    cx.set_shared_state("line one\nline ˇtwo\nline three").await;
1254    cx.simulate_shared_keystrokes("m a l ' a").await;
1255    cx.shared_state()
1256        .await
1257        .assert_eq("line one\nˇline two\nline three");
1258    cx.simulate_shared_keystrokes("` a").await;
1259    cx.shared_state()
1260        .await
1261        .assert_eq("line one\nline ˇtwo\nline three");
1262
1263    cx.simulate_shared_keystrokes("^ d ` a").await;
1264    cx.shared_state()
1265        .await
1266        .assert_eq("line one\nˇtwo\nline three");
1267}
1268
1269#[gpui::test]
1270async fn test_lt_gt_marks(cx: &mut TestAppContext) {
1271    let mut cx = NeovimBackedTestContext::new(cx).await;
1272
1273    cx.set_shared_state(indoc!(
1274        "
1275        Line one
1276        Line two
1277        Line ˇthree
1278        Line four
1279        Line five
1280    "
1281    ))
1282    .await;
1283
1284    cx.simulate_shared_keystrokes("v j escape k k").await;
1285
1286    cx.simulate_shared_keystrokes("' <").await;
1287    cx.shared_state().await.assert_eq(indoc! {"
1288        Line one
1289        Line two
1290        ˇLine three
1291        Line four
1292        Line five
1293    "});
1294
1295    cx.simulate_shared_keystrokes("` <").await;
1296    cx.shared_state().await.assert_eq(indoc! {"
1297        Line one
1298        Line two
1299        Line ˇthree
1300        Line four
1301        Line five
1302    "});
1303
1304    cx.simulate_shared_keystrokes("' >").await;
1305    cx.shared_state().await.assert_eq(indoc! {"
1306        Line one
1307        Line two
1308        Line three
1309        ˇLine four
1310        Line five
1311    "
1312    });
1313
1314    cx.simulate_shared_keystrokes("` >").await;
1315    cx.shared_state().await.assert_eq(indoc! {"
1316        Line one
1317        Line two
1318        Line three
1319        Line ˇfour
1320        Line five
1321    "
1322    });
1323
1324    cx.simulate_shared_keystrokes("v i w o escape").await;
1325    cx.simulate_shared_keystrokes("` >").await;
1326    cx.shared_state().await.assert_eq(indoc! {"
1327        Line one
1328        Line two
1329        Line three
1330        Line fouˇr
1331        Line five
1332    "
1333    });
1334    cx.simulate_shared_keystrokes("` <").await;
1335    cx.shared_state().await.assert_eq(indoc! {"
1336        Line one
1337        Line two
1338        Line three
1339        Line ˇfour
1340        Line five
1341    "
1342    });
1343}
1344
1345#[gpui::test]
1346async fn test_caret_mark(cx: &mut TestAppContext) {
1347    let mut cx = NeovimBackedTestContext::new(cx).await;
1348
1349    cx.set_shared_state(indoc!(
1350        "
1351        Line one
1352        Line two
1353        Line three
1354        ˇLine four
1355        Line five
1356    "
1357    ))
1358    .await;
1359
1360    cx.simulate_shared_keystrokes("c w shift-s t r a i g h t space t h i n g escape j j")
1361        .await;
1362
1363    cx.simulate_shared_keystrokes("' ^").await;
1364    cx.shared_state().await.assert_eq(indoc! {"
1365        Line one
1366        Line two
1367        Line three
1368        ˇStraight thing four
1369        Line five
1370    "
1371    });
1372
1373    cx.simulate_shared_keystrokes("` ^").await;
1374    cx.shared_state().await.assert_eq(indoc! {"
1375        Line one
1376        Line two
1377        Line three
1378        Straight thingˇ four
1379        Line five
1380    "
1381    });
1382
1383    cx.simulate_shared_keystrokes("k a ! escape k g i ?").await;
1384    cx.shared_state().await.assert_eq(indoc! {"
1385        Line one
1386        Line two
1387        Line three!?ˇ
1388        Straight thing four
1389        Line five
1390    "
1391    });
1392}
1393
1394#[cfg(target_os = "macos")]
1395#[gpui::test]
1396async fn test_dw_eol(cx: &mut gpui::TestAppContext) {
1397    let mut cx = NeovimBackedTestContext::new(cx).await;
1398
1399    cx.set_shared_wrap(12).await;
1400    cx.set_shared_state("twelve ˇchar twelve char\ntwelve char")
1401        .await;
1402    cx.simulate_shared_keystrokes("d w").await;
1403    cx.shared_state()
1404        .await
1405        .assert_eq("twelve ˇtwelve char\ntwelve char");
1406}
1407
1408#[perf]
1409#[gpui::test]
1410async fn test_toggle_comments(cx: &mut gpui::TestAppContext) {
1411    let mut cx = VimTestContext::new(cx, true).await;
1412
1413    let language = std::sync::Arc::new(language::Language::new(
1414        language::LanguageConfig {
1415            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
1416            ..Default::default()
1417        },
1418        Some(language::tree_sitter_rust::LANGUAGE.into()),
1419    ));
1420    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1421
1422    // works in normal model
1423    cx.set_state(
1424        indoc! {"
1425      ˇone
1426      two
1427      three
1428      "},
1429        Mode::Normal,
1430    );
1431    cx.simulate_keystrokes("g c c");
1432    cx.assert_state(
1433        indoc! {"
1434          // ˇone
1435          two
1436          three
1437          "},
1438        Mode::Normal,
1439    );
1440
1441    // works in visual mode
1442    cx.simulate_keystrokes("v j g c");
1443    cx.assert_state(
1444        indoc! {"
1445          // // ˇone
1446          // two
1447          three
1448          "},
1449        Mode::Normal,
1450    );
1451
1452    // works in visual line mode
1453    cx.simulate_keystrokes("shift-v j g c");
1454    cx.assert_state(
1455        indoc! {"
1456          // ˇone
1457          two
1458          three
1459          "},
1460        Mode::Normal,
1461    );
1462
1463    // works with count
1464    cx.simulate_keystrokes("g c 2 j");
1465    cx.assert_state(
1466        indoc! {"
1467            // // ˇone
1468            // two
1469            // three
1470            "},
1471        Mode::Normal,
1472    );
1473
1474    // works with motion object
1475    cx.simulate_keystrokes("shift-g");
1476    cx.simulate_keystrokes("g c g g");
1477    cx.assert_state(
1478        indoc! {"
1479            // one
1480            two
1481            three
1482            ˇ"},
1483        Mode::Normal,
1484    );
1485}
1486
1487#[gpui::test]
1488async fn test_find_multibyte(cx: &mut gpui::TestAppContext) {
1489    let mut cx = NeovimBackedTestContext::new(cx).await;
1490
1491    cx.set_shared_state(r#"<label for="guests">ˇPočet hostů</label>"#)
1492        .await;
1493
1494    cx.simulate_shared_keystrokes("c t < o escape").await;
1495    cx.shared_state()
1496        .await
1497        .assert_eq(r#"<label for="guests">ˇo</label>"#);
1498}
1499
1500#[perf]
1501#[gpui::test]
1502async fn test_sneak(cx: &mut gpui::TestAppContext) {
1503    let mut cx = VimTestContext::new(cx, true).await;
1504
1505    cx.update(|_window, cx| {
1506        cx.bind_keys([
1507            KeyBinding::new(
1508                "s",
1509                PushSneak { first_char: None },
1510                Some("vim_mode == normal"),
1511            ),
1512            KeyBinding::new(
1513                "shift-s",
1514                PushSneakBackward { first_char: None },
1515                Some("vim_mode == normal"),
1516            ),
1517            KeyBinding::new(
1518                "shift-s",
1519                PushSneakBackward { first_char: None },
1520                Some("vim_mode == visual"),
1521            ),
1522        ])
1523    });
1524
1525    // Sneak forwards multibyte & multiline
1526    cx.set_state(
1527        indoc! {
1528            r#"<labelˇ for="guests">
1529                    Počet hostů
1530                </label>"#
1531        },
1532        Mode::Normal,
1533    );
1534    cx.simulate_keystrokes("s t ů");
1535    cx.assert_state(
1536        indoc! {
1537            r#"<label for="guests">
1538                Počet hosˇtů
1539            </label>"#
1540        },
1541        Mode::Normal,
1542    );
1543
1544    // Visual sneak backwards multibyte & multiline
1545    cx.simulate_keystrokes("v S < l");
1546    cx.assert_state(
1547        indoc! {
1548            r#"«ˇ<label for="guests">
1549                Počet host»ů
1550            </label>"#
1551        },
1552        Mode::Visual,
1553    );
1554
1555    // Sneak backwards repeated
1556    cx.set_state(r#"11 12 13 ˇ14"#, Mode::Normal);
1557    cx.simulate_keystrokes("S space 1");
1558    cx.assert_state(r#"11 12ˇ 13 14"#, Mode::Normal);
1559    cx.simulate_keystrokes(";");
1560    cx.assert_state(r#"11ˇ 12 13 14"#, Mode::Normal);
1561}
1562
1563#[gpui::test]
1564async fn test_plus_minus(cx: &mut gpui::TestAppContext) {
1565    let mut cx = NeovimBackedTestContext::new(cx).await;
1566
1567    cx.set_shared_state(indoc! {
1568        "one
1569           two
1570        thrˇee
1571    "})
1572        .await;
1573
1574    cx.simulate_shared_keystrokes("-").await;
1575    cx.shared_state().await.assert_matches();
1576    cx.simulate_shared_keystrokes("-").await;
1577    cx.shared_state().await.assert_matches();
1578    cx.simulate_shared_keystrokes("+").await;
1579    cx.shared_state().await.assert_matches();
1580}
1581
1582#[gpui::test]
1583async fn test_command_alias(cx: &mut gpui::TestAppContext) {
1584    let mut cx = VimTestContext::new(cx, true).await;
1585    cx.update_global(|store: &mut SettingsStore, cx| {
1586        store.update_user_settings(cx, |s| {
1587            let mut aliases = HashMap::default();
1588            aliases.insert("Q".to_string(), "upper".to_string());
1589            s.workspace.command_aliases = aliases
1590        });
1591    });
1592
1593    cx.set_state("ˇhello world", Mode::Normal);
1594    cx.simulate_keystrokes(": Q");
1595    cx.set_state("ˇHello world", Mode::Normal);
1596}
1597
1598#[gpui::test]
1599async fn test_remap_adjacent_dog_cat(cx: &mut gpui::TestAppContext) {
1600    let mut cx = NeovimBackedTestContext::new(cx).await;
1601    cx.update(|_, cx| {
1602        cx.bind_keys([
1603            KeyBinding::new(
1604                "d o g",
1605                workspace::SendKeystrokes("🐶".to_string()),
1606                Some("vim_mode == insert"),
1607            ),
1608            KeyBinding::new(
1609                "c a t",
1610                workspace::SendKeystrokes("🐱".to_string()),
1611                Some("vim_mode == insert"),
1612            ),
1613        ])
1614    });
1615    cx.neovim.exec("imap dog 🐶").await;
1616    cx.neovim.exec("imap cat 🐱").await;
1617
1618    cx.set_shared_state("ˇ").await;
1619    cx.simulate_shared_keystrokes("i d o g").await;
1620    cx.shared_state().await.assert_eq("🐶ˇ");
1621
1622    cx.set_shared_state("ˇ").await;
1623    cx.simulate_shared_keystrokes("i d o d o g").await;
1624    cx.shared_state().await.assert_eq("do🐶ˇ");
1625
1626    cx.set_shared_state("ˇ").await;
1627    cx.simulate_shared_keystrokes("i d o c a t").await;
1628    cx.shared_state().await.assert_eq("do🐱ˇ");
1629}
1630
1631#[gpui::test]
1632async fn test_remap_nested_pineapple(cx: &mut gpui::TestAppContext) {
1633    let mut cx = NeovimBackedTestContext::new(cx).await;
1634    cx.update(|_, cx| {
1635        cx.bind_keys([
1636            KeyBinding::new(
1637                "p i n",
1638                workspace::SendKeystrokes("📌".to_string()),
1639                Some("vim_mode == insert"),
1640            ),
1641            KeyBinding::new(
1642                "p i n e",
1643                workspace::SendKeystrokes("🌲".to_string()),
1644                Some("vim_mode == insert"),
1645            ),
1646            KeyBinding::new(
1647                "p i n e a p p l e",
1648                workspace::SendKeystrokes("🍍".to_string()),
1649                Some("vim_mode == insert"),
1650            ),
1651        ])
1652    });
1653    cx.neovim.exec("imap pin 📌").await;
1654    cx.neovim.exec("imap pine 🌲").await;
1655    cx.neovim.exec("imap pineapple 🍍").await;
1656
1657    cx.set_shared_state("ˇ").await;
1658    cx.simulate_shared_keystrokes("i p i n").await;
1659    cx.executor().advance_clock(Duration::from_millis(1000));
1660    cx.run_until_parked();
1661    cx.shared_state().await.assert_eq("📌ˇ");
1662
1663    cx.set_shared_state("ˇ").await;
1664    cx.simulate_shared_keystrokes("i p i n e").await;
1665    cx.executor().advance_clock(Duration::from_millis(1000));
1666    cx.run_until_parked();
1667    cx.shared_state().await.assert_eq("🌲ˇ");
1668
1669    cx.set_shared_state("ˇ").await;
1670    cx.simulate_shared_keystrokes("i p i n e a p p l e").await;
1671    cx.shared_state().await.assert_eq("🍍ˇ");
1672}
1673
1674#[gpui::test]
1675async fn test_remap_recursion(cx: &mut gpui::TestAppContext) {
1676    let mut cx = NeovimBackedTestContext::new(cx).await;
1677    cx.update(|_, cx| {
1678        cx.bind_keys([KeyBinding::new(
1679            "x",
1680            workspace::SendKeystrokes("\" _ x".to_string()),
1681            Some("VimControl"),
1682        )]);
1683        cx.bind_keys([KeyBinding::new(
1684            "y",
1685            workspace::SendKeystrokes("2 x".to_string()),
1686            Some("VimControl"),
1687        )])
1688    });
1689    cx.neovim.exec("noremap x \"_x").await;
1690    cx.neovim.exec("map y 2x").await;
1691
1692    cx.set_shared_state("ˇhello").await;
1693    cx.simulate_shared_keystrokes("d l").await;
1694    cx.shared_clipboard().await.assert_eq("h");
1695    cx.simulate_shared_keystrokes("y").await;
1696    cx.shared_clipboard().await.assert_eq("h");
1697    cx.shared_state().await.assert_eq("ˇlo");
1698}
1699
1700#[gpui::test]
1701async fn test_escape_while_waiting(cx: &mut gpui::TestAppContext) {
1702    let mut cx = NeovimBackedTestContext::new(cx).await;
1703    cx.set_shared_state("ˇhi").await;
1704    cx.simulate_shared_keystrokes("\" + escape x").await;
1705    cx.shared_state().await.assert_eq("ˇi");
1706}
1707
1708#[gpui::test]
1709async fn test_ctrl_w_override(cx: &mut gpui::TestAppContext) {
1710    let mut cx = NeovimBackedTestContext::new(cx).await;
1711    cx.update(|_, cx| {
1712        cx.bind_keys([KeyBinding::new("ctrl-w", DeleteLine, None)]);
1713    });
1714    cx.neovim.exec("map <c-w> D").await;
1715    cx.set_shared_state("ˇhi").await;
1716    cx.simulate_shared_keystrokes("ctrl-w").await;
1717    cx.shared_state().await.assert_eq("ˇ");
1718}
1719
1720#[perf]
1721#[gpui::test]
1722async fn test_visual_indent_count(cx: &mut gpui::TestAppContext) {
1723    let mut cx = VimTestContext::new(cx, true).await;
1724    cx.set_state("ˇhi", Mode::Normal);
1725    cx.simulate_keystrokes("shift-v 3 >");
1726    cx.assert_state("            ˇhi", Mode::Normal);
1727    cx.simulate_keystrokes("shift-v 2 <");
1728    cx.assert_state("    ˇhi", Mode::Normal);
1729}
1730
1731#[gpui::test]
1732async fn test_record_replay_recursion(cx: &mut gpui::TestAppContext) {
1733    let mut cx = NeovimBackedTestContext::new(cx).await;
1734
1735    cx.set_shared_state("ˇhello world").await;
1736    cx.simulate_shared_keystrokes(">").await;
1737    cx.simulate_shared_keystrokes(".").await;
1738    cx.simulate_shared_keystrokes(".").await;
1739    cx.simulate_shared_keystrokes(".").await;
1740    cx.shared_state().await.assert_eq("ˇhello world");
1741}
1742
1743#[gpui::test]
1744async fn test_blackhole_register(cx: &mut gpui::TestAppContext) {
1745    let mut cx = NeovimBackedTestContext::new(cx).await;
1746
1747    cx.set_shared_state("ˇhello world").await;
1748    cx.simulate_shared_keystrokes("d i w \" _ d a w").await;
1749    cx.simulate_shared_keystrokes("p").await;
1750    cx.shared_state().await.assert_eq("hellˇo");
1751}
1752
1753#[gpui::test]
1754async fn test_sentence_backwards(cx: &mut gpui::TestAppContext) {
1755    let mut cx = NeovimBackedTestContext::new(cx).await;
1756
1757    cx.set_shared_state("one\n\ntwo\nthree\nˇ\nfour").await;
1758    cx.simulate_shared_keystrokes("(").await;
1759    cx.shared_state()
1760        .await
1761        .assert_eq("one\n\nˇtwo\nthree\n\nfour");
1762
1763    cx.set_shared_state("hello.\n\n\nworˇld.").await;
1764    cx.simulate_shared_keystrokes("(").await;
1765    cx.shared_state().await.assert_eq("hello.\n\n\nˇworld.");
1766    cx.simulate_shared_keystrokes("(").await;
1767    cx.shared_state().await.assert_eq("hello.\n\nˇ\nworld.");
1768    cx.simulate_shared_keystrokes("(").await;
1769    cx.shared_state().await.assert_eq("ˇhello.\n\n\nworld.");
1770
1771    cx.set_shared_state("hello. worlˇd.").await;
1772    cx.simulate_shared_keystrokes("(").await;
1773    cx.shared_state().await.assert_eq("hello. ˇworld.");
1774    cx.simulate_shared_keystrokes("(").await;
1775    cx.shared_state().await.assert_eq("ˇhello. world.");
1776
1777    cx.set_shared_state(". helˇlo.").await;
1778    cx.simulate_shared_keystrokes("(").await;
1779    cx.shared_state().await.assert_eq(". ˇhello.");
1780    cx.simulate_shared_keystrokes("(").await;
1781    cx.shared_state().await.assert_eq(". ˇhello.");
1782
1783    cx.set_shared_state(indoc! {
1784        "{
1785            hello_world();
1786        ˇ}"
1787    })
1788    .await;
1789    cx.simulate_shared_keystrokes("(").await;
1790    cx.shared_state().await.assert_eq(indoc! {
1791        "ˇ{
1792            hello_world();
1793        }"
1794    });
1795
1796    cx.set_shared_state(indoc! {
1797        "Hello! World..?
1798
1799        \tHello! World... ˇ"
1800    })
1801    .await;
1802    cx.simulate_shared_keystrokes("(").await;
1803    cx.shared_state().await.assert_eq(indoc! {
1804        "Hello! World..?
1805
1806        \tHello! ˇWorld... "
1807    });
1808    cx.simulate_shared_keystrokes("(").await;
1809    cx.shared_state().await.assert_eq(indoc! {
1810        "Hello! World..?
1811
1812        \tˇHello! World... "
1813    });
1814    cx.simulate_shared_keystrokes("(").await;
1815    cx.shared_state().await.assert_eq(indoc! {
1816        "Hello! World..?
1817        ˇ
1818        \tHello! World... "
1819    });
1820    cx.simulate_shared_keystrokes("(").await;
1821    cx.shared_state().await.assert_eq(indoc! {
1822        "Hello! ˇWorld..?
1823
1824        \tHello! World... "
1825    });
1826}
1827
1828#[gpui::test]
1829async fn test_sentence_forwards(cx: &mut gpui::TestAppContext) {
1830    let mut cx = NeovimBackedTestContext::new(cx).await;
1831
1832    cx.set_shared_state("helˇlo.\n\n\nworld.").await;
1833    cx.simulate_shared_keystrokes(")").await;
1834    cx.shared_state().await.assert_eq("hello.\nˇ\n\nworld.");
1835    cx.simulate_shared_keystrokes(")").await;
1836    cx.shared_state().await.assert_eq("hello.\n\n\nˇworld.");
1837    cx.simulate_shared_keystrokes(")").await;
1838    cx.shared_state().await.assert_eq("hello.\n\n\nworldˇ.");
1839
1840    cx.set_shared_state("helˇlo.\n\n\nworld.").await;
1841}
1842
1843#[gpui::test]
1844async fn test_ctrl_o_visual(cx: &mut gpui::TestAppContext) {
1845    let mut cx = NeovimBackedTestContext::new(cx).await;
1846
1847    cx.set_shared_state("helloˇ world.").await;
1848    cx.simulate_shared_keystrokes("i ctrl-o v b r l").await;
1849    cx.shared_state().await.assert_eq("ˇllllllworld.");
1850    cx.simulate_shared_keystrokes("ctrl-o v f w d").await;
1851    cx.shared_state().await.assert_eq("ˇorld.");
1852}
1853
1854#[gpui::test]
1855async fn test_ctrl_o_position(cx: &mut gpui::TestAppContext) {
1856    let mut cx = NeovimBackedTestContext::new(cx).await;
1857
1858    cx.set_shared_state("helˇlo world.").await;
1859    cx.simulate_shared_keystrokes("i ctrl-o d i w").await;
1860    cx.shared_state().await.assert_eq("ˇ world.");
1861    cx.simulate_shared_keystrokes("ctrl-o p").await;
1862    cx.shared_state().await.assert_eq(" helloˇworld.");
1863}
1864
1865#[gpui::test]
1866async fn test_ctrl_o_dot(cx: &mut gpui::TestAppContext) {
1867    let mut cx = NeovimBackedTestContext::new(cx).await;
1868
1869    cx.set_shared_state("heˇllo world.").await;
1870    cx.simulate_shared_keystrokes("x i ctrl-o .").await;
1871    cx.shared_state().await.assert_eq("heˇo world.");
1872    cx.simulate_shared_keystrokes("l l escape .").await;
1873    cx.shared_state().await.assert_eq("hellˇllo world.");
1874}
1875
1876#[perf(iterations = 1)]
1877#[gpui::test]
1878async fn test_folded_multibuffer_excerpts(cx: &mut gpui::TestAppContext) {
1879    VimTestContext::init(cx);
1880    cx.update(|cx| {
1881        VimTestContext::init_keybindings(true, cx);
1882    });
1883    let (editor, cx) = cx.add_window_view(|window, cx| {
1884        let multi_buffer = MultiBuffer::build_multi(
1885            [
1886                ("111\n222\n333\n444\n", vec![Point::row_range(0..2)]),
1887                ("aaa\nbbb\nccc\nddd\n", vec![Point::row_range(0..2)]),
1888                ("AAA\nBBB\nCCC\nDDD\n", vec![Point::row_range(0..2)]),
1889                ("one\ntwo\nthr\nfou\n", vec![Point::row_range(0..2)]),
1890            ],
1891            cx,
1892        );
1893        let mut editor = Editor::new(EditorMode::full(), multi_buffer.clone(), None, window, cx);
1894
1895        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
1896        // fold all but the second buffer, so that we test navigating between two
1897        // adjacent folded buffers, as well as folded buffers at the start and
1898        // end the multibuffer
1899        editor.fold_buffer(buffer_ids[0], cx);
1900        editor.fold_buffer(buffer_ids[2], cx);
1901        editor.fold_buffer(buffer_ids[3], cx);
1902
1903        editor
1904    });
1905    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
1906
1907    cx.assert_excerpts_with_selections(indoc! {"
1908        [EXCERPT]
1909        ˇ[FOLDED]
1910        [EXCERPT]
1911        aaa
1912        bbb
1913        [EXCERPT]
1914        [FOLDED]
1915        [EXCERPT]
1916        [FOLDED]
1917        "
1918    });
1919    cx.simulate_keystroke("j");
1920    cx.assert_excerpts_with_selections(indoc! {"
1921        [EXCERPT]
1922        [FOLDED]
1923        [EXCERPT]
1924        ˇaaa
1925        bbb
1926        [EXCERPT]
1927        [FOLDED]
1928        [EXCERPT]
1929        [FOLDED]
1930        "
1931    });
1932    cx.simulate_keystroke("j");
1933    cx.simulate_keystroke("j");
1934    cx.assert_excerpts_with_selections(indoc! {"
1935        [EXCERPT]
1936        [FOLDED]
1937        [EXCERPT]
1938        aaa
1939        bbb
1940        ˇ[EXCERPT]
1941        [FOLDED]
1942        [EXCERPT]
1943        [FOLDED]
1944        "
1945    });
1946    cx.simulate_keystroke("j");
1947    cx.assert_excerpts_with_selections(indoc! {"
1948        [EXCERPT]
1949        [FOLDED]
1950        [EXCERPT]
1951        aaa
1952        bbb
1953        [EXCERPT]
1954        ˇ[FOLDED]
1955        [EXCERPT]
1956        [FOLDED]
1957        "
1958    });
1959    cx.simulate_keystroke("j");
1960    cx.assert_excerpts_with_selections(indoc! {"
1961        [EXCERPT]
1962        [FOLDED]
1963        [EXCERPT]
1964        aaa
1965        bbb
1966        [EXCERPT]
1967        [FOLDED]
1968        [EXCERPT]
1969        ˇ[FOLDED]
1970        "
1971    });
1972    cx.simulate_keystroke("k");
1973    cx.assert_excerpts_with_selections(indoc! {"
1974        [EXCERPT]
1975        [FOLDED]
1976        [EXCERPT]
1977        aaa
1978        bbb
1979        [EXCERPT]
1980        ˇ[FOLDED]
1981        [EXCERPT]
1982        [FOLDED]
1983        "
1984    });
1985    cx.simulate_keystroke("k");
1986    cx.simulate_keystroke("k");
1987    cx.simulate_keystroke("k");
1988    cx.assert_excerpts_with_selections(indoc! {"
1989        [EXCERPT]
1990        [FOLDED]
1991        [EXCERPT]
1992        ˇaaa
1993        bbb
1994        [EXCERPT]
1995        [FOLDED]
1996        [EXCERPT]
1997        [FOLDED]
1998        "
1999    });
2000    cx.simulate_keystroke("k");
2001    cx.assert_excerpts_with_selections(indoc! {"
2002        [EXCERPT]
2003        ˇ[FOLDED]
2004        [EXCERPT]
2005        aaa
2006        bbb
2007        [EXCERPT]
2008        [FOLDED]
2009        [EXCERPT]
2010        [FOLDED]
2011        "
2012    });
2013    cx.simulate_keystroke("shift-g");
2014    cx.assert_excerpts_with_selections(indoc! {"
2015        [EXCERPT]
2016        [FOLDED]
2017        [EXCERPT]
2018        aaa
2019        bbb
2020        [EXCERPT]
2021        [FOLDED]
2022        [EXCERPT]
2023        ˇ[FOLDED]
2024        "
2025    });
2026    cx.simulate_keystrokes("g g");
2027    cx.assert_excerpts_with_selections(indoc! {"
2028        [EXCERPT]
2029        ˇ[FOLDED]
2030        [EXCERPT]
2031        aaa
2032        bbb
2033        [EXCERPT]
2034        [FOLDED]
2035        [EXCERPT]
2036        [FOLDED]
2037        "
2038    });
2039    cx.update_editor(|editor, _, cx| {
2040        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
2041        editor.fold_buffer(buffer_ids[1], cx);
2042    });
2043
2044    cx.assert_excerpts_with_selections(indoc! {"
2045        [EXCERPT]
2046        ˇ[FOLDED]
2047        [EXCERPT]
2048        [FOLDED]
2049        [EXCERPT]
2050        [FOLDED]
2051        [EXCERPT]
2052        [FOLDED]
2053        "
2054    });
2055    cx.simulate_keystrokes("2 j");
2056    cx.assert_excerpts_with_selections(indoc! {"
2057        [EXCERPT]
2058        [FOLDED]
2059        [EXCERPT]
2060        [FOLDED]
2061        [EXCERPT]
2062        ˇ[FOLDED]
2063        [EXCERPT]
2064        [FOLDED]
2065        "
2066    });
2067}
2068
2069#[gpui::test]
2070async fn test_delete_paragraph_motion(cx: &mut gpui::TestAppContext) {
2071    let mut cx = NeovimBackedTestContext::new(cx).await;
2072    cx.set_shared_state(indoc! {
2073        "ˇhello world.
2074
2075        hello world.
2076        "
2077    })
2078    .await;
2079    cx.simulate_shared_keystrokes("y }").await;
2080    cx.shared_clipboard().await.assert_eq("hello world.\n");
2081    cx.simulate_shared_keystrokes("d }").await;
2082    cx.shared_state().await.assert_eq("ˇ\nhello world.\n");
2083    cx.shared_clipboard().await.assert_eq("hello world.\n");
2084
2085    cx.set_shared_state(indoc! {
2086        "helˇlo world.
2087
2088            hello world.
2089            "
2090    })
2091    .await;
2092    cx.simulate_shared_keystrokes("y }").await;
2093    cx.shared_clipboard().await.assert_eq("lo world.");
2094    cx.simulate_shared_keystrokes("d }").await;
2095    cx.shared_state().await.assert_eq("heˇl\n\nhello world.\n");
2096    cx.shared_clipboard().await.assert_eq("lo world.");
2097}
2098
2099#[gpui::test]
2100async fn test_delete_unmatched_brace(cx: &mut gpui::TestAppContext) {
2101    let mut cx = NeovimBackedTestContext::new(cx).await;
2102    cx.set_shared_state(indoc! {
2103        "fn o(wow: i32) {
2104          othˇ(wow)
2105          oth(wow)
2106        }
2107        "
2108    })
2109    .await;
2110    cx.simulate_shared_keystrokes("d ] }").await;
2111    cx.shared_state().await.assert_eq(indoc! {
2112        "fn o(wow: i32) {
2113          otˇh
2114        }
2115        "
2116    });
2117    cx.shared_clipboard().await.assert_eq("(wow)\n  oth(wow)");
2118    cx.set_shared_state(indoc! {
2119        "fn o(wow: i32) {
2120          ˇoth(wow)
2121          oth(wow)
2122        }
2123        "
2124    })
2125    .await;
2126    cx.simulate_shared_keystrokes("d ] }").await;
2127    cx.shared_state().await.assert_eq(indoc! {
2128        "fn o(wow: i32) {
2129         ˇ}
2130        "
2131    });
2132    cx.shared_clipboard()
2133        .await
2134        .assert_eq("  oth(wow)\n  oth(wow)\n");
2135}
2136
2137#[gpui::test]
2138async fn test_paragraph_multi_delete(cx: &mut gpui::TestAppContext) {
2139    let mut cx = NeovimBackedTestContext::new(cx).await;
2140    cx.set_shared_state(indoc! {
2141        "
2142        Emacs is
2143        ˇa great
2144
2145        operating system
2146
2147        all it lacks
2148        is a
2149
2150        decent text editor
2151        "
2152    })
2153    .await;
2154
2155    cx.simulate_shared_keystrokes("2 d a p").await;
2156    cx.shared_state().await.assert_eq(indoc! {
2157        "
2158        ˇall it lacks
2159        is a
2160
2161        decent text editor
2162        "
2163    });
2164
2165    cx.simulate_shared_keystrokes("d a p").await;
2166    cx.shared_clipboard()
2167        .await
2168        .assert_eq("all it lacks\nis a\n\n");
2169
2170    //reset to initial state
2171    cx.simulate_shared_keystrokes("2 u").await;
2172
2173    cx.simulate_shared_keystrokes("4 d a p").await;
2174    cx.shared_state().await.assert_eq(indoc! {"ˇ"});
2175}
2176
2177#[perf]
2178#[gpui::test]
2179async fn test_multi_cursor_replay(cx: &mut gpui::TestAppContext) {
2180    let mut cx = VimTestContext::new(cx, true).await;
2181    cx.set_state(
2182        indoc! {
2183            "
2184        oˇne one one
2185
2186        two two two
2187        "
2188        },
2189        Mode::Normal,
2190    );
2191
2192    cx.simulate_keystrokes("3 g l s wow escape escape");
2193    cx.assert_state(
2194        indoc! {
2195            "
2196        woˇw wow wow
2197
2198        two two two
2199        "
2200        },
2201        Mode::Normal,
2202    );
2203
2204    cx.simulate_keystrokes("2 j 3 g l .");
2205    cx.assert_state(
2206        indoc! {
2207            "
2208        wow wow wow
2209
2210        woˇw woˇw woˇw
2211        "
2212        },
2213        Mode::Normal,
2214    );
2215}
2216
2217#[gpui::test]
2218async fn test_clipping_on_mode_change(cx: &mut gpui::TestAppContext) {
2219    let mut cx = VimTestContext::new(cx, true).await;
2220
2221    cx.set_state(
2222        indoc! {
2223        "
2224        ˇverylongline
2225        andsomelinebelow
2226        "
2227        },
2228        Mode::Normal,
2229    );
2230
2231    cx.simulate_keystrokes("v e");
2232    cx.assert_state(
2233        indoc! {
2234        "
2235        «verylonglineˇ»
2236        andsomelinebelow
2237        "
2238        },
2239        Mode::Visual,
2240    );
2241
2242    let mut pixel_position = cx.update_editor(|editor, window, cx| {
2243        let snapshot = editor.snapshot(window, cx);
2244        let current_head = editor.selections.newest_display(cx).end;
2245        editor.last_bounds().unwrap().origin
2246            + editor
2247                .display_to_pixel_point(current_head, &snapshot, window)
2248                .unwrap()
2249    });
2250    pixel_position.x += px(100.);
2251    // click beyond end of the line
2252    cx.simulate_click(pixel_position, Modifiers::default());
2253    cx.run_until_parked();
2254
2255    cx.assert_state(
2256        indoc! {
2257        "
2258        verylonglinˇe
2259        andsomelinebelow
2260        "
2261        },
2262        Mode::Normal,
2263    );
2264}