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