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::{actions::DeleteLine, display_map::DisplayRow, DisplayPoint};
  10use futures::StreamExt;
  11use gpui::{KeyBinding, Modifiers, MouseButton, TestAppContext};
  12pub use neovim_backed_test_context::*;
  13use settings::SettingsStore;
  14pub use vim_test_context::*;
  15
  16use indoc::indoc;
  17use search::BufferSearchBar;
  18use workspace::WorkspaceSettings;
  19
  20use crate::{insert::NormalBefore, motion, state::Mode};
  21
  22#[gpui::test]
  23async fn test_initially_disabled(cx: &mut gpui::TestAppContext) {
  24    let mut cx = VimTestContext::new(cx, false).await;
  25    cx.simulate_keystrokes("h j k l");
  26    cx.assert_editor_state("hjklˇ");
  27}
  28
  29#[gpui::test]
  30async fn test_neovim(cx: &mut gpui::TestAppContext) {
  31    let mut cx = NeovimBackedTestContext::new(cx).await;
  32
  33    cx.simulate_shared_keystrokes("i").await;
  34    cx.shared_state().await.assert_matches();
  35    cx.simulate_shared_keystrokes("shift-t e s t space t e s t escape 0 d w")
  36        .await;
  37    cx.shared_state().await.assert_matches();
  38    cx.assert_editor_state("ˇtest");
  39}
  40
  41#[gpui::test]
  42async fn test_toggle_through_settings(cx: &mut gpui::TestAppContext) {
  43    let mut cx = VimTestContext::new(cx, true).await;
  44
  45    cx.simulate_keystrokes("i");
  46    assert_eq!(cx.mode(), Mode::Insert);
  47
  48    // Editor acts as though vim is disabled
  49    cx.disable_vim();
  50    cx.simulate_keystrokes("h j k l");
  51    cx.assert_editor_state("hjklˇ");
  52
  53    // Selections aren't changed if editor is blurred but vim-mode is still disabled.
  54    cx.cx.set_state("«hjklˇ»");
  55    cx.assert_editor_state("«hjklˇ»");
  56    cx.update_editor(|_, cx| cx.blur());
  57    cx.assert_editor_state("«hjklˇ»");
  58    cx.update_editor(|_, cx| cx.focus_self());
  59    cx.assert_editor_state("«hjklˇ»");
  60
  61    // Enabling dynamically sets vim mode again and restores normal mode
  62    cx.enable_vim();
  63    assert_eq!(cx.mode(), Mode::Normal);
  64    cx.simulate_keystrokes("h h h l");
  65    assert_eq!(cx.buffer_text(), "hjkl".to_owned());
  66    cx.assert_editor_state("hˇjkl");
  67    cx.simulate_keystrokes("i T e s t");
  68    cx.assert_editor_state("hTestˇjkl");
  69
  70    // Disabling and enabling resets to normal mode
  71    assert_eq!(cx.mode(), Mode::Insert);
  72    cx.disable_vim();
  73    cx.enable_vim();
  74    assert_eq!(cx.mode(), Mode::Normal);
  75}
  76
  77#[gpui::test]
  78async fn test_cancel_selection(cx: &mut gpui::TestAppContext) {
  79    let mut cx = VimTestContext::new(cx, true).await;
  80
  81    cx.set_state(
  82        indoc! {"The quick brown fox juˇmps over the lazy dog"},
  83        Mode::Normal,
  84    );
  85    // jumps
  86    cx.simulate_keystrokes("v l l");
  87    cx.assert_editor_state("The quick brown fox ju«mpsˇ» over the lazy dog");
  88
  89    cx.simulate_keystrokes("escape");
  90    cx.assert_editor_state("The quick brown fox jumpˇs over the lazy dog");
  91
  92    // go back to the same selection state
  93    cx.simulate_keystrokes("v h h");
  94    cx.assert_editor_state("The quick brown fox ju«ˇmps» over the lazy dog");
  95
  96    // Ctrl-[ should behave like Esc
  97    cx.simulate_keystrokes("ctrl-[");
  98    cx.assert_editor_state("The quick brown fox juˇmps over the lazy dog");
  99}
 100
 101#[gpui::test]
 102async fn test_buffer_search(cx: &mut gpui::TestAppContext) {
 103    let mut cx = VimTestContext::new(cx, true).await;
 104
 105    cx.set_state(
 106        indoc! {"
 107            The quick brown
 108            fox juˇmps over
 109            the lazy dog"},
 110        Mode::Normal,
 111    );
 112    cx.simulate_keystrokes("/");
 113
 114    let search_bar = cx.workspace(|workspace, cx| {
 115        workspace
 116            .active_pane()
 117            .read(cx)
 118            .toolbar()
 119            .read(cx)
 120            .item_of_type::<BufferSearchBar>()
 121            .expect("Buffer search bar should be deployed")
 122    });
 123
 124    cx.update_view(search_bar, |bar, cx| {
 125        assert_eq!(bar.query(cx), "");
 126    })
 127}
 128
 129#[gpui::test]
 130async fn test_count_down(cx: &mut gpui::TestAppContext) {
 131    let mut cx = VimTestContext::new(cx, true).await;
 132
 133    cx.set_state(indoc! {"aˇa\nbb\ncc\ndd\nee"}, Mode::Normal);
 134    cx.simulate_keystrokes("2 down");
 135    cx.assert_editor_state("aa\nbb\ncˇc\ndd\nee");
 136    cx.simulate_keystrokes("9 down");
 137    cx.assert_editor_state("aa\nbb\ncc\ndd\neˇe");
 138}
 139
 140#[gpui::test]
 141async fn test_end_of_document_710(cx: &mut gpui::TestAppContext) {
 142    let mut cx = VimTestContext::new(cx, true).await;
 143
 144    // goes to end by default
 145    cx.set_state(indoc! {"aˇa\nbb\ncc"}, Mode::Normal);
 146    cx.simulate_keystrokes("shift-g");
 147    cx.assert_editor_state("aa\nbb\ncˇc");
 148
 149    // can go to line 1 (https://github.com/zed-industries/zed/issues/5812)
 150    cx.simulate_keystrokes("1 shift-g");
 151    cx.assert_editor_state("aˇa\nbb\ncc");
 152}
 153
 154#[gpui::test]
 155async fn test_end_of_line_with_times(cx: &mut gpui::TestAppContext) {
 156    let mut cx = VimTestContext::new(cx, true).await;
 157
 158    // goes to current line end
 159    cx.set_state(indoc! {"ˇaa\nbb\ncc"}, Mode::Normal);
 160    cx.simulate_keystrokes("$");
 161    cx.assert_editor_state("aˇa\nbb\ncc");
 162
 163    // goes to next line end
 164    cx.simulate_keystrokes("2 $");
 165    cx.assert_editor_state("aa\nbˇb\ncc");
 166
 167    // try to exceed the final line.
 168    cx.simulate_keystrokes("4 $");
 169    cx.assert_editor_state("aa\nbb\ncˇc");
 170}
 171
 172#[gpui::test]
 173async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 174    let mut cx = VimTestContext::new(cx, true).await;
 175
 176    // works in normal mode
 177    cx.set_state(indoc! {"aa\nbˇb\ncc"}, Mode::Normal);
 178    cx.simulate_keystrokes("> >");
 179    cx.assert_editor_state("aa\n    bˇb\ncc");
 180    cx.simulate_keystrokes("< <");
 181    cx.assert_editor_state("aa\nbˇb\ncc");
 182
 183    // works in visual mode
 184    cx.simulate_keystrokes("shift-v down >");
 185    cx.assert_editor_state("aa\n    bˇb\n    cc");
 186
 187    // works as operator
 188    cx.set_state("aa\nbˇb\ncc\n", Mode::Normal);
 189    cx.simulate_keystrokes("> j");
 190    cx.assert_editor_state("aa\n    bˇb\n    cc\n");
 191    cx.simulate_keystrokes("< k");
 192    cx.assert_editor_state("aa\nbˇb\n    cc\n");
 193    cx.simulate_keystrokes("> i p");
 194    cx.assert_editor_state("    aa\n    bˇb\n        cc\n");
 195    cx.simulate_keystrokes("< i p");
 196    cx.assert_editor_state("aa\nbˇb\n    cc\n");
 197    cx.simulate_keystrokes("< i p");
 198    cx.assert_editor_state("aa\nbˇb\ncc\n");
 199
 200    cx.set_state("ˇaa\nbb\ncc\n", Mode::Normal);
 201    cx.simulate_keystrokes("> 2 j");
 202    cx.assert_editor_state("    ˇaa\n    bb\n    cc\n");
 203
 204    cx.set_state("aa\nbb\nˇcc\n", Mode::Normal);
 205    cx.simulate_keystrokes("> 2 k");
 206    cx.assert_editor_state("    aa\n    bb\n    ˇcc\n");
 207
 208    // works with repeat
 209    cx.set_state("a\nb\nccˇc\n", Mode::Normal);
 210    cx.simulate_keystrokes("> 2 k");
 211    cx.assert_editor_state("    a\n    b\n    ccˇc\n");
 212    cx.simulate_keystrokes(".");
 213    cx.assert_editor_state("        a\n        b\n        ccˇc\n");
 214    cx.simulate_keystrokes("v k <");
 215    cx.assert_editor_state("        a\n\n    ccc\n");
 216    cx.simulate_keystrokes(".");
 217    cx.assert_editor_state("        a\n\nccc\n");
 218}
 219
 220#[gpui::test]
 221async fn test_escape_command_palette(cx: &mut gpui::TestAppContext) {
 222    let mut cx = VimTestContext::new(cx, true).await;
 223
 224    cx.set_state("aˇbc\n", Mode::Normal);
 225    cx.simulate_keystrokes("i cmd-shift-p");
 226
 227    assert!(cx.workspace(|workspace, cx| workspace.active_modal::<CommandPalette>(cx).is_some()));
 228    cx.simulate_keystrokes("escape");
 229    cx.run_until_parked();
 230    assert!(!cx.workspace(|workspace, cx| workspace.active_modal::<CommandPalette>(cx).is_some()));
 231    cx.assert_state("aˇbc\n", Mode::Insert);
 232}
 233
 234#[gpui::test]
 235async fn test_escape_cancels(cx: &mut gpui::TestAppContext) {
 236    let mut cx = VimTestContext::new(cx, true).await;
 237
 238    cx.set_state("aˇbˇc", Mode::Normal);
 239    cx.simulate_keystrokes("escape");
 240
 241    cx.assert_state("aˇbc", Mode::Normal);
 242}
 243
 244#[gpui::test]
 245async fn test_selection_on_search(cx: &mut gpui::TestAppContext) {
 246    let mut cx = VimTestContext::new(cx, true).await;
 247
 248    cx.set_state(indoc! {"aa\nbˇb\ncc\ncc\ncc\n"}, Mode::Normal);
 249    cx.simulate_keystrokes("/ c c");
 250
 251    let search_bar = cx.workspace(|workspace, cx| {
 252        workspace
 253            .active_pane()
 254            .read(cx)
 255            .toolbar()
 256            .read(cx)
 257            .item_of_type::<BufferSearchBar>()
 258            .expect("Buffer search bar should be deployed")
 259    });
 260
 261    cx.update_view(search_bar, |bar, cx| {
 262        assert_eq!(bar.query(cx), "cc");
 263    });
 264
 265    cx.update_editor(|editor, cx| {
 266        let highlights = editor.all_text_background_highlights(cx);
 267        assert_eq!(3, highlights.len());
 268        assert_eq!(
 269            DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 2),
 270            highlights[0].0
 271        )
 272    });
 273    cx.simulate_keystrokes("enter");
 274
 275    cx.assert_state(indoc! {"aa\nbb\nˇcc\ncc\ncc\n"}, Mode::Normal);
 276    cx.simulate_keystrokes("n");
 277    cx.assert_state(indoc! {"aa\nbb\ncc\nˇcc\ncc\n"}, Mode::Normal);
 278    cx.simulate_keystrokes("shift-n");
 279    cx.assert_state(indoc! {"aa\nbb\nˇcc\ncc\ncc\n"}, Mode::Normal);
 280}
 281
 282#[gpui::test]
 283async fn test_word_characters(cx: &mut gpui::TestAppContext) {
 284    let mut cx = VimTestContext::new_typescript(cx).await;
 285    cx.set_state(
 286        indoc! { "
 287        class A {
 288            #ˇgoop = 99;
 289            $ˇgoop () { return this.#gˇoop };
 290        };
 291        console.log(new A().$gooˇp())
 292    "},
 293        Mode::Normal,
 294    );
 295    cx.simulate_keystrokes("v i w");
 296    cx.assert_state(
 297        indoc! {"
 298        class A {
 299            «#goopˇ» = 99;
 300            «$goopˇ» () { return this.«#goopˇ» };
 301        };
 302        console.log(new A().«$goopˇ»())
 303    "},
 304        Mode::Visual,
 305    )
 306}
 307
 308#[gpui::test]
 309async fn test_kebab_case(cx: &mut gpui::TestAppContext) {
 310    let mut cx = VimTestContext::new_html(cx).await;
 311    cx.set_state(
 312        indoc! { r#"
 313            <div><a class="bg-rˇed"></a></div>
 314            "#},
 315        Mode::Normal,
 316    );
 317    cx.simulate_keystrokes("v i w");
 318    cx.assert_state(
 319        indoc! { r#"
 320        <div><a class="bg-«redˇ»"></a></div>
 321        "#
 322        },
 323        Mode::Visual,
 324    )
 325}
 326
 327#[gpui::test]
 328async fn test_join_lines(cx: &mut gpui::TestAppContext) {
 329    let mut cx = NeovimBackedTestContext::new(cx).await;
 330
 331    cx.set_shared_state(indoc! {"
 332      ˇone
 333      two
 334      three
 335      four
 336      five
 337      six
 338      "})
 339        .await;
 340    cx.simulate_shared_keystrokes("shift-j").await;
 341    cx.shared_state().await.assert_eq(indoc! {"
 342          oneˇ two
 343          three
 344          four
 345          five
 346          six
 347          "});
 348    cx.simulate_shared_keystrokes("3 shift-j").await;
 349    cx.shared_state().await.assert_eq(indoc! {"
 350          one two threeˇ four
 351          five
 352          six
 353          "});
 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("j v 3 j shift-j").await;
 365    cx.shared_state().await.assert_eq(indoc! {"
 366      one
 367      two three fourˇ five
 368      six
 369      "});
 370
 371    cx.set_shared_state(indoc! {"
 372      ˇone
 373      two
 374      three
 375      four
 376      five
 377      six
 378      "})
 379        .await;
 380    cx.simulate_shared_keystrokes("g shift-j").await;
 381    cx.shared_state().await.assert_eq(indoc! {"
 382          oneˇtwo
 383          three
 384          four
 385          five
 386          six
 387          "});
 388    cx.simulate_shared_keystrokes("3 g shift-j").await;
 389    cx.shared_state().await.assert_eq(indoc! {"
 390          onetwothreeˇfour
 391          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("j v 3 j g shift-j").await;
 405    cx.shared_state().await.assert_eq(indoc! {"
 406      one
 407      twothreefourˇfive
 408      six
 409      "});
 410}
 411
 412#[cfg(target_os = "macos")]
 413#[gpui::test]
 414async fn test_wrapped_lines(cx: &mut gpui::TestAppContext) {
 415    let mut cx = NeovimBackedTestContext::new(cx).await;
 416
 417    cx.set_shared_wrap(12).await;
 418    // tests line wrap as follows:
 419    //  1: twelve char
 420    //     twelve char
 421    //  2: twelve char
 422    cx.set_shared_state(indoc! { "
 423        tˇwelve char twelve char
 424        twelve char
 425    "})
 426        .await;
 427    cx.simulate_shared_keystrokes("j").await;
 428    cx.shared_state().await.assert_eq(indoc! {"
 429        twelve char twelve char
 430        tˇwelve char
 431    "});
 432    cx.simulate_shared_keystrokes("k").await;
 433    cx.shared_state().await.assert_eq(indoc! {"
 434        tˇwelve char twelve char
 435        twelve char
 436    "});
 437    cx.simulate_shared_keystrokes("g j").await;
 438    cx.shared_state().await.assert_eq(indoc! {"
 439        twelve char tˇwelve char
 440        twelve char
 441    "});
 442    cx.simulate_shared_keystrokes("g j").await;
 443    cx.shared_state().await.assert_eq(indoc! {"
 444        twelve char twelve char
 445        tˇwelve char
 446    "});
 447
 448    cx.simulate_shared_keystrokes("g k").await;
 449    cx.shared_state().await.assert_eq(indoc! {"
 450        twelve char tˇwelve char
 451        twelve char
 452    "});
 453
 454    cx.simulate_shared_keystrokes("g ^").await;
 455    cx.shared_state().await.assert_eq(indoc! {"
 456        twelve char ˇtwelve char
 457        twelve char
 458    "});
 459
 460    cx.simulate_shared_keystrokes("^").await;
 461    cx.shared_state().await.assert_eq(indoc! {"
 462        ˇtwelve char twelve char
 463        twelve char
 464    "});
 465
 466    cx.simulate_shared_keystrokes("g $").await;
 467    cx.shared_state().await.assert_eq(indoc! {"
 468        twelve charˇ twelve char
 469        twelve char
 470    "});
 471    cx.simulate_shared_keystrokes("$").await;
 472    cx.shared_state().await.assert_eq(indoc! {"
 473        twelve char twelve chaˇr
 474        twelve char
 475    "});
 476
 477    cx.set_shared_state(indoc! { "
 478        tˇwelve char twelve char
 479        twelve char
 480    "})
 481        .await;
 482    cx.simulate_shared_keystrokes("enter").await;
 483    cx.shared_state().await.assert_eq(indoc! {"
 484            twelve char twelve char
 485            ˇtwelve char
 486        "});
 487
 488    cx.set_shared_state(indoc! { "
 489        twelve char
 490        tˇwelve char twelve char
 491        twelve char
 492    "})
 493        .await;
 494    cx.simulate_shared_keystrokes("o o escape").await;
 495    cx.shared_state().await.assert_eq(indoc! {"
 496        twelve char
 497        twelve char twelve char
 498        ˇo
 499        twelve char
 500    "});
 501
 502    cx.set_shared_state(indoc! { "
 503        twelve char
 504        tˇwelve char twelve char
 505        twelve char
 506    "})
 507        .await;
 508    cx.simulate_shared_keystrokes("shift-a a escape").await;
 509    cx.shared_state().await.assert_eq(indoc! {"
 510        twelve char
 511        twelve char twelve charˇa
 512        twelve char
 513    "});
 514    cx.simulate_shared_keystrokes("shift-i i escape").await;
 515    cx.shared_state().await.assert_eq(indoc! {"
 516        twelve char
 517        ˇitwelve char twelve chara
 518        twelve char
 519    "});
 520    cx.simulate_shared_keystrokes("shift-d").await;
 521    cx.shared_state().await.assert_eq(indoc! {"
 522        twelve char
 523        ˇ
 524        twelve char
 525    "});
 526
 527    cx.set_shared_state(indoc! { "
 528        twelve char
 529        twelve char tˇwelve char
 530        twelve char
 531    "})
 532        .await;
 533    cx.simulate_shared_keystrokes("shift-o o escape").await;
 534    cx.shared_state().await.assert_eq(indoc! {"
 535        twelve char
 536        ˇo
 537        twelve char twelve char
 538        twelve char
 539    "});
 540
 541    // line wraps as:
 542    // fourteen ch
 543    // ar
 544    // fourteen ch
 545    // ar
 546    cx.set_shared_state(indoc! { "
 547        fourteen chaˇr
 548        fourteen char
 549    "})
 550        .await;
 551
 552    cx.simulate_shared_keystrokes("d i w").await;
 553    cx.shared_state().await.assert_eq(indoc! {"
 554        fourteenˇ•
 555        fourteen char
 556    "});
 557    cx.simulate_shared_keystrokes("j shift-f e f r").await;
 558    cx.shared_state().await.assert_eq(indoc! {"
 559        fourteen•
 560        fourteen chaˇr
 561    "});
 562}
 563
 564#[gpui::test]
 565async fn test_folds(cx: &mut gpui::TestAppContext) {
 566    let mut cx = NeovimBackedTestContext::new(cx).await;
 567    cx.set_neovim_option("foldmethod=manual").await;
 568
 569    cx.set_shared_state(indoc! { "
 570        fn boop() {
 571          ˇbarp()
 572          bazp()
 573        }
 574    "})
 575        .await;
 576    cx.simulate_shared_keystrokes("shift-v j z f").await;
 577
 578    // visual display is now:
 579    // fn boop () {
 580    //  [FOLDED]
 581    // }
 582
 583    // TODO: this should not be needed but currently zf does not
 584    // return to normal mode.
 585    cx.simulate_shared_keystrokes("escape").await;
 586
 587    // skip over fold downward
 588    cx.simulate_shared_keystrokes("g g").await;
 589    cx.shared_state().await.assert_eq(indoc! {"
 590        ˇfn boop() {
 591          barp()
 592          bazp()
 593        }
 594    "});
 595
 596    cx.simulate_shared_keystrokes("j j").await;
 597    cx.shared_state().await.assert_eq(indoc! {"
 598        fn boop() {
 599          barp()
 600          bazp()
 601        ˇ}
 602    "});
 603
 604    // skip over fold upward
 605    cx.simulate_shared_keystrokes("2 k").await;
 606    cx.shared_state().await.assert_eq(indoc! {"
 607        ˇfn boop() {
 608          barp()
 609          bazp()
 610        }
 611    "});
 612
 613    // yank the fold
 614    cx.simulate_shared_keystrokes("down y y").await;
 615    cx.shared_clipboard()
 616        .await
 617        .assert_eq("  barp()\n  bazp()\n");
 618
 619    // re-open
 620    cx.simulate_shared_keystrokes("z o").await;
 621    cx.shared_state().await.assert_eq(indoc! {"
 622        fn boop() {
 623        ˇ  barp()
 624          bazp()
 625        }
 626    "});
 627}
 628
 629#[gpui::test]
 630async fn test_folds_panic(cx: &mut gpui::TestAppContext) {
 631    let mut cx = NeovimBackedTestContext::new(cx).await;
 632    cx.set_neovim_option("foldmethod=manual").await;
 633
 634    cx.set_shared_state(indoc! { "
 635        fn boop() {
 636          ˇbarp()
 637          bazp()
 638        }
 639    "})
 640        .await;
 641    cx.simulate_shared_keystrokes("shift-v j z f").await;
 642    cx.simulate_shared_keystrokes("escape").await;
 643    cx.simulate_shared_keystrokes("g g").await;
 644    cx.simulate_shared_keystrokes("5 d j").await;
 645    cx.shared_state().await.assert_eq("ˇ");
 646    cx.set_shared_state(indoc! {"
 647        fn boop() {
 648          ˇbarp()
 649          bazp()
 650        }
 651    "})
 652        .await;
 653    cx.simulate_shared_keystrokes("shift-v j j z f").await;
 654    cx.simulate_shared_keystrokes("escape").await;
 655    cx.simulate_shared_keystrokes("shift-g shift-v").await;
 656    cx.shared_state().await.assert_eq(indoc! {"
 657        fn boop() {
 658          barp()
 659          bazp()
 660        }
 661        ˇ"});
 662}
 663
 664#[gpui::test]
 665async fn test_clear_counts(cx: &mut gpui::TestAppContext) {
 666    let mut cx = NeovimBackedTestContext::new(cx).await;
 667
 668    cx.set_shared_state(indoc! {"
 669        The quick brown
 670        fox juˇmps over
 671        the lazy dog"})
 672        .await;
 673
 674    cx.simulate_shared_keystrokes("4 escape 3 d l").await;
 675    cx.shared_state().await.assert_eq(indoc! {"
 676        The quick brown
 677        fox juˇ over
 678        the lazy dog"});
 679}
 680
 681#[gpui::test]
 682async fn test_zero(cx: &mut gpui::TestAppContext) {
 683    let mut cx = NeovimBackedTestContext::new(cx).await;
 684
 685    cx.set_shared_state(indoc! {"
 686        The quˇick brown
 687        fox jumps over
 688        the lazy dog"})
 689        .await;
 690
 691    cx.simulate_shared_keystrokes("0").await;
 692    cx.shared_state().await.assert_eq(indoc! {"
 693        ˇThe quick brown
 694        fox jumps over
 695        the lazy dog"});
 696
 697    cx.simulate_shared_keystrokes("1 0 l").await;
 698    cx.shared_state().await.assert_eq(indoc! {"
 699        The quick ˇbrown
 700        fox jumps over
 701        the lazy dog"});
 702}
 703
 704#[gpui::test]
 705async fn test_selection_goal(cx: &mut gpui::TestAppContext) {
 706    let mut cx = NeovimBackedTestContext::new(cx).await;
 707
 708    cx.set_shared_state(indoc! {"
 709        ;;ˇ;
 710        Lorem Ipsum"})
 711        .await;
 712
 713    cx.simulate_shared_keystrokes("a down up ; down up").await;
 714    cx.shared_state().await.assert_eq(indoc! {"
 715        ;;;;ˇ
 716        Lorem Ipsum"});
 717}
 718
 719#[cfg(target_os = "macos")]
 720#[gpui::test]
 721async fn test_wrapped_motions(cx: &mut gpui::TestAppContext) {
 722    let mut cx = NeovimBackedTestContext::new(cx).await;
 723
 724    cx.set_shared_wrap(12).await;
 725
 726    cx.set_shared_state(indoc! {"
 727                aaˇaa
 728                😃😃"
 729    })
 730    .await;
 731    cx.simulate_shared_keystrokes("j").await;
 732    cx.shared_state().await.assert_eq(indoc! {"
 733                aaaa
 734                😃ˇ😃"
 735    });
 736
 737    cx.set_shared_state(indoc! {"
 738                123456789012aaˇaa
 739                123456789012😃😃"
 740    })
 741    .await;
 742    cx.simulate_shared_keystrokes("j").await;
 743    cx.shared_state().await.assert_eq(indoc! {"
 744        123456789012aaaa
 745        123456789012😃ˇ😃"
 746    });
 747
 748    cx.set_shared_state(indoc! {"
 749                123456789012aaˇaa
 750                123456789012😃😃"
 751    })
 752    .await;
 753    cx.simulate_shared_keystrokes("j").await;
 754    cx.shared_state().await.assert_eq(indoc! {"
 755        123456789012aaaa
 756        123456789012😃ˇ😃"
 757    });
 758
 759    cx.set_shared_state(indoc! {"
 760        123456789012aaaaˇaaaaaaaa123456789012
 761        wow
 762        123456789012😃😃😃😃😃😃123456789012"
 763    })
 764    .await;
 765    cx.simulate_shared_keystrokes("j j").await;
 766    cx.shared_state().await.assert_eq(indoc! {"
 767        123456789012aaaaaaaaaaaa123456789012
 768        wow
 769        123456789012😃😃ˇ😃😃😃😃123456789012"
 770    });
 771}
 772
 773#[gpui::test]
 774async fn test_wrapped_delete_end_document(cx: &mut gpui::TestAppContext) {
 775    let mut cx = NeovimBackedTestContext::new(cx).await;
 776
 777    cx.set_shared_wrap(12).await;
 778
 779    cx.set_shared_state(indoc! {"
 780                aaˇaaaaaaaaaaaaaaaaaa
 781                bbbbbbbbbbbbbbbbbbbb
 782                cccccccccccccccccccc"
 783    })
 784    .await;
 785    cx.simulate_shared_keystrokes("d shift-g i z z z").await;
 786    cx.shared_state().await.assert_eq(indoc! {"
 787                zzzˇ"
 788    });
 789}
 790
 791#[gpui::test]
 792async fn test_paragraphs_dont_wrap(cx: &mut gpui::TestAppContext) {
 793    let mut cx = NeovimBackedTestContext::new(cx).await;
 794
 795    cx.set_shared_state(indoc! {"
 796        one
 797        ˇ
 798        two"})
 799        .await;
 800
 801    cx.simulate_shared_keystrokes("} }").await;
 802    cx.shared_state().await.assert_eq(indoc! {"
 803        one
 804
 805        twˇo"});
 806
 807    cx.simulate_shared_keystrokes("{ { {").await;
 808    cx.shared_state().await.assert_eq(indoc! {"
 809        ˇone
 810
 811        two"});
 812}
 813
 814#[gpui::test]
 815async fn test_select_all_issue_2170(cx: &mut gpui::TestAppContext) {
 816    let mut cx = VimTestContext::new(cx, true).await;
 817
 818    cx.set_state(
 819        indoc! {"
 820        defmodule Test do
 821            def test(a, ˇ[_, _] = b), do: IO.puts('hi')
 822        end
 823    "},
 824        Mode::Normal,
 825    );
 826    cx.simulate_keystrokes("g a");
 827    cx.assert_state(
 828        indoc! {"
 829        defmodule Test do
 830            def test(a, «[ˇ»_, _] = b), do: IO.puts('hi')
 831        end
 832    "},
 833        Mode::Visual,
 834    );
 835}
 836
 837#[gpui::test]
 838async fn test_jk(cx: &mut gpui::TestAppContext) {
 839    let mut cx = NeovimBackedTestContext::new(cx).await;
 840
 841    cx.update(|cx| {
 842        cx.bind_keys([KeyBinding::new(
 843            "j k",
 844            NormalBefore,
 845            Some("vim_mode == insert"),
 846        )])
 847    });
 848    cx.neovim.exec("imap jk <esc>").await;
 849
 850    cx.set_shared_state("ˇhello").await;
 851    cx.simulate_shared_keystrokes("i j o j k").await;
 852    cx.shared_state().await.assert_eq("jˇohello");
 853}
 854
 855#[gpui::test]
 856async fn test_jk_delay(cx: &mut gpui::TestAppContext) {
 857    let mut cx = VimTestContext::new(cx, true).await;
 858
 859    cx.update(|cx| {
 860        cx.bind_keys([KeyBinding::new(
 861            "j k",
 862            NormalBefore,
 863            Some("vim_mode == insert"),
 864        )])
 865    });
 866
 867    cx.set_state("ˇhello", Mode::Normal);
 868    cx.simulate_keystrokes("i j");
 869    cx.executor().advance_clock(Duration::from_millis(500));
 870    cx.run_until_parked();
 871    cx.assert_state("ˇhello", Mode::Insert);
 872    cx.executor().advance_clock(Duration::from_millis(500));
 873    cx.run_until_parked();
 874    cx.assert_state("jˇhello", Mode::Insert);
 875    cx.simulate_keystrokes("k j k");
 876    cx.assert_state("jˇkhello", Mode::Normal);
 877}
 878
 879#[gpui::test]
 880async fn test_comma_w(cx: &mut gpui::TestAppContext) {
 881    let mut cx = NeovimBackedTestContext::new(cx).await;
 882
 883    cx.update(|cx| {
 884        cx.bind_keys([KeyBinding::new(
 885            ", w",
 886            motion::Down {
 887                display_lines: false,
 888            },
 889            Some("vim_mode == normal"),
 890        )])
 891    });
 892    cx.neovim.exec("map ,w j").await;
 893
 894    cx.set_shared_state("ˇhello hello\nhello hello").await;
 895    cx.simulate_shared_keystrokes("f o ; , w").await;
 896    cx.shared_state()
 897        .await
 898        .assert_eq("hello hello\nhello hellˇo");
 899
 900    cx.set_shared_state("ˇhello hello\nhello hello").await;
 901    cx.simulate_shared_keystrokes("f o ; , i").await;
 902    cx.shared_state()
 903        .await
 904        .assert_eq("hellˇo hello\nhello hello");
 905}
 906
 907#[gpui::test]
 908async fn test_rename(cx: &mut gpui::TestAppContext) {
 909    let mut cx = VimTestContext::new_typescript(cx).await;
 910
 911    cx.set_state("const beˇfore = 2; console.log(before)", Mode::Normal);
 912    let def_range = cx.lsp_range("const «beforeˇ» = 2; console.log(before)");
 913    let tgt_range = cx.lsp_range("const before = 2; console.log(«beforeˇ»)");
 914    let mut prepare_request =
 915        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
 916            Ok(Some(lsp::PrepareRenameResponse::Range(def_range)))
 917        });
 918    let mut rename_request =
 919        cx.handle_request::<lsp::request::Rename, _, _>(move |url, params, _| async move {
 920            Ok(Some(lsp::WorkspaceEdit {
 921                changes: Some(
 922                    [(
 923                        url.clone(),
 924                        vec![
 925                            lsp::TextEdit::new(def_range, params.new_name.clone()),
 926                            lsp::TextEdit::new(tgt_range, params.new_name),
 927                        ],
 928                    )]
 929                    .into(),
 930                ),
 931                ..Default::default()
 932            }))
 933        });
 934
 935    cx.simulate_keystrokes("c d");
 936    prepare_request.next().await.unwrap();
 937    cx.simulate_input("after");
 938    cx.simulate_keystrokes("enter");
 939    rename_request.next().await.unwrap();
 940    cx.assert_state("const afterˇ = 2; console.log(after)", Mode::Normal)
 941}
 942
 943// TODO: this test is flaky on our linux CI machines
 944#[cfg(target_os = "macos")]
 945#[gpui::test]
 946async fn test_remap(cx: &mut gpui::TestAppContext) {
 947    let mut cx = VimTestContext::new(cx, true).await;
 948
 949    // test moving the cursor
 950    cx.update(|cx| {
 951        cx.bind_keys([KeyBinding::new(
 952            "g z",
 953            workspace::SendKeystrokes("l l l l".to_string()),
 954            None,
 955        )])
 956    });
 957    cx.set_state("ˇ123456789", Mode::Normal);
 958    cx.simulate_keystrokes("g z");
 959    cx.assert_state("1234ˇ56789", Mode::Normal);
 960
 961    // test switching modes
 962    cx.update(|cx| {
 963        cx.bind_keys([KeyBinding::new(
 964            "g y",
 965            workspace::SendKeystrokes("i f o o escape l".to_string()),
 966            None,
 967        )])
 968    });
 969    cx.set_state("ˇ123456789", Mode::Normal);
 970    cx.simulate_keystrokes("g y");
 971    cx.assert_state("fooˇ123456789", Mode::Normal);
 972
 973    // test recursion
 974    cx.update(|cx| {
 975        cx.bind_keys([KeyBinding::new(
 976            "g x",
 977            workspace::SendKeystrokes("g z g y".to_string()),
 978            None,
 979        )])
 980    });
 981    cx.set_state("ˇ123456789", Mode::Normal);
 982    cx.simulate_keystrokes("g x");
 983    cx.assert_state("1234fooˇ56789", Mode::Normal);
 984
 985    cx.executor().allow_parking();
 986
 987    // test command
 988    cx.update(|cx| {
 989        cx.bind_keys([KeyBinding::new(
 990            "g w",
 991            workspace::SendKeystrokes(": j enter".to_string()),
 992            None,
 993        )])
 994    });
 995    cx.set_state("ˇ1234\n56789", Mode::Normal);
 996    cx.simulate_keystrokes("g w");
 997    cx.assert_state("1234ˇ 56789", Mode::Normal);
 998
 999    // test leaving command
1000    cx.update(|cx| {
1001        cx.bind_keys([KeyBinding::new(
1002            "g u",
1003            workspace::SendKeystrokes("g w g z".to_string()),
1004            None,
1005        )])
1006    });
1007    cx.set_state("ˇ1234\n56789", Mode::Normal);
1008    cx.simulate_keystrokes("g u");
1009    cx.assert_state("1234 567ˇ89", Mode::Normal);
1010
1011    // test leaving command
1012    cx.update(|cx| {
1013        cx.bind_keys([KeyBinding::new(
1014            "g t",
1015            workspace::SendKeystrokes("i space escape".to_string()),
1016            None,
1017        )])
1018    });
1019    cx.set_state("12ˇ34", Mode::Normal);
1020    cx.simulate_keystrokes("g t");
1021    cx.assert_state("12ˇ 34", Mode::Normal);
1022}
1023
1024#[gpui::test]
1025async fn test_undo(cx: &mut gpui::TestAppContext) {
1026    let mut cx = NeovimBackedTestContext::new(cx).await;
1027
1028    cx.set_shared_state("hello quˇoel world").await;
1029    cx.simulate_shared_keystrokes("v i w s c o escape u").await;
1030    cx.shared_state().await.assert_eq("hello ˇquoel world");
1031    cx.simulate_shared_keystrokes("ctrl-r").await;
1032    cx.shared_state().await.assert_eq("hello ˇco world");
1033    cx.simulate_shared_keystrokes("a o right l escape").await;
1034    cx.shared_state().await.assert_eq("hello cooˇl world");
1035    cx.simulate_shared_keystrokes("u").await;
1036    cx.shared_state().await.assert_eq("hello cooˇ world");
1037    cx.simulate_shared_keystrokes("u").await;
1038    cx.shared_state().await.assert_eq("hello cˇo world");
1039    cx.simulate_shared_keystrokes("u").await;
1040    cx.shared_state().await.assert_eq("hello ˇquoel world");
1041
1042    cx.set_shared_state("hello quˇoel world").await;
1043    cx.simulate_shared_keystrokes("v i w ~ u").await;
1044    cx.shared_state().await.assert_eq("hello ˇquoel world");
1045
1046    cx.set_shared_state("\nhello quˇoel world\n").await;
1047    cx.simulate_shared_keystrokes("shift-v s c escape u").await;
1048    cx.shared_state().await.assert_eq("\nˇhello quoel world\n");
1049
1050    cx.set_shared_state(indoc! {"
1051        ˇ1
1052        2
1053        3"})
1054        .await;
1055
1056    cx.simulate_shared_keystrokes("ctrl-v shift-g ctrl-a").await;
1057    cx.shared_state().await.assert_eq(indoc! {"
1058        ˇ2
1059        3
1060        4"});
1061
1062    cx.simulate_shared_keystrokes("u").await;
1063    cx.shared_state().await.assert_eq(indoc! {"
1064        ˇ1
1065        2
1066        3"});
1067}
1068
1069#[gpui::test]
1070async fn test_mouse_selection(cx: &mut TestAppContext) {
1071    let mut cx = VimTestContext::new(cx, true).await;
1072
1073    cx.set_state("ˇone two three", Mode::Normal);
1074
1075    let start_point = cx.pixel_position("one twˇo three");
1076    let end_point = cx.pixel_position("one ˇtwo three");
1077
1078    cx.simulate_mouse_down(start_point, MouseButton::Left, Modifiers::none());
1079    cx.simulate_mouse_move(end_point, MouseButton::Left, Modifiers::none());
1080    cx.simulate_mouse_up(end_point, MouseButton::Left, Modifiers::none());
1081
1082    cx.assert_state("one «ˇtwo» three", Mode::Visual)
1083}
1084
1085#[gpui::test]
1086async fn test_lowercase_marks(cx: &mut TestAppContext) {
1087    let mut cx = NeovimBackedTestContext::new(cx).await;
1088
1089    cx.set_shared_state("line one\nline ˇtwo\nline three").await;
1090    cx.simulate_shared_keystrokes("m a l ' a").await;
1091    cx.shared_state()
1092        .await
1093        .assert_eq("line one\nˇline two\nline three");
1094    cx.simulate_shared_keystrokes("` a").await;
1095    cx.shared_state()
1096        .await
1097        .assert_eq("line one\nline ˇtwo\nline three");
1098
1099    cx.simulate_shared_keystrokes("^ d ` a").await;
1100    cx.shared_state()
1101        .await
1102        .assert_eq("line one\nˇtwo\nline three");
1103}
1104
1105#[gpui::test]
1106async fn test_lt_gt_marks(cx: &mut TestAppContext) {
1107    let mut cx = NeovimBackedTestContext::new(cx).await;
1108
1109    cx.set_shared_state(indoc!(
1110        "
1111        Line one
1112        Line two
1113        Line ˇthree
1114        Line four
1115        Line five
1116    "
1117    ))
1118    .await;
1119
1120    cx.simulate_shared_keystrokes("v j escape k k").await;
1121
1122    cx.simulate_shared_keystrokes("' <").await;
1123    cx.shared_state().await.assert_eq(indoc! {"
1124        Line one
1125        Line two
1126        ˇLine three
1127        Line four
1128        Line five
1129    "});
1130
1131    cx.simulate_shared_keystrokes("` <").await;
1132    cx.shared_state().await.assert_eq(indoc! {"
1133        Line one
1134        Line two
1135        Line ˇthree
1136        Line four
1137        Line five
1138    "});
1139
1140    cx.simulate_shared_keystrokes("' >").await;
1141    cx.shared_state().await.assert_eq(indoc! {"
1142        Line one
1143        Line two
1144        Line three
1145        ˇLine four
1146        Line five
1147    "
1148    });
1149
1150    cx.simulate_shared_keystrokes("` >").await;
1151    cx.shared_state().await.assert_eq(indoc! {"
1152        Line one
1153        Line two
1154        Line three
1155        Line ˇfour
1156        Line five
1157    "
1158    });
1159
1160    cx.simulate_shared_keystrokes("v i w o escape").await;
1161    cx.simulate_shared_keystrokes("` >").await;
1162    cx.shared_state().await.assert_eq(indoc! {"
1163        Line one
1164        Line two
1165        Line three
1166        Line fouˇr
1167        Line five
1168    "
1169    });
1170    cx.simulate_shared_keystrokes("` <").await;
1171    cx.shared_state().await.assert_eq(indoc! {"
1172        Line one
1173        Line two
1174        Line three
1175        Line ˇfour
1176        Line five
1177    "
1178    });
1179}
1180
1181#[gpui::test]
1182async fn test_caret_mark(cx: &mut TestAppContext) {
1183    let mut cx = NeovimBackedTestContext::new(cx).await;
1184
1185    cx.set_shared_state(indoc!(
1186        "
1187        Line one
1188        Line two
1189        Line three
1190        ˇLine four
1191        Line five
1192    "
1193    ))
1194    .await;
1195
1196    cx.simulate_shared_keystrokes("c w shift-s t r a i g h t space t h i n g escape j j")
1197        .await;
1198
1199    cx.simulate_shared_keystrokes("' ^").await;
1200    cx.shared_state().await.assert_eq(indoc! {"
1201        Line one
1202        Line two
1203        Line three
1204        ˇStraight thing four
1205        Line five
1206    "
1207    });
1208
1209    cx.simulate_shared_keystrokes("` ^").await;
1210    cx.shared_state().await.assert_eq(indoc! {"
1211        Line one
1212        Line two
1213        Line three
1214        Straight thingˇ four
1215        Line five
1216    "
1217    });
1218
1219    cx.simulate_shared_keystrokes("k a ! escape k g i ?").await;
1220    cx.shared_state().await.assert_eq(indoc! {"
1221        Line one
1222        Line two
1223        Line three!?ˇ
1224        Straight thing four
1225        Line five
1226    "
1227    });
1228}
1229
1230#[cfg(target_os = "macos")]
1231#[gpui::test]
1232async fn test_dw_eol(cx: &mut gpui::TestAppContext) {
1233    let mut cx = NeovimBackedTestContext::new(cx).await;
1234
1235    cx.set_shared_wrap(12).await;
1236    cx.set_shared_state("twelve ˇchar twelve char\ntwelve char")
1237        .await;
1238    cx.simulate_shared_keystrokes("d w").await;
1239    cx.shared_state()
1240        .await
1241        .assert_eq("twelve ˇtwelve char\ntwelve char");
1242}
1243
1244#[gpui::test]
1245async fn test_toggle_comments(cx: &mut gpui::TestAppContext) {
1246    let mut cx = VimTestContext::new(cx, true).await;
1247
1248    let language = std::sync::Arc::new(language::Language::new(
1249        language::LanguageConfig {
1250            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
1251            ..Default::default()
1252        },
1253        Some(language::tree_sitter_rust::LANGUAGE.into()),
1254    ));
1255    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1256
1257    // works in normal model
1258    cx.set_state(
1259        indoc! {"
1260      ˇone
1261      two
1262      three
1263      "},
1264        Mode::Normal,
1265    );
1266    cx.simulate_keystrokes("g c c");
1267    cx.assert_state(
1268        indoc! {"
1269          // ˇone
1270          two
1271          three
1272          "},
1273        Mode::Normal,
1274    );
1275
1276    // works in visual mode
1277    cx.simulate_keystrokes("v j g c");
1278    cx.assert_state(
1279        indoc! {"
1280          // // ˇone
1281          // two
1282          three
1283          "},
1284        Mode::Normal,
1285    );
1286
1287    // works in visual line mode
1288    cx.simulate_keystrokes("shift-v j g c");
1289    cx.assert_state(
1290        indoc! {"
1291          // ˇone
1292          two
1293          three
1294          "},
1295        Mode::Normal,
1296    );
1297
1298    // works with count
1299    cx.simulate_keystrokes("g c 2 j");
1300    cx.assert_state(
1301        indoc! {"
1302            // // ˇone
1303            // two
1304            // three
1305            "},
1306        Mode::Normal,
1307    );
1308
1309    // works with motion object
1310    cx.simulate_keystrokes("shift-g");
1311    cx.simulate_keystrokes("g c g g");
1312    cx.assert_state(
1313        indoc! {"
1314            // one
1315            two
1316            three
1317            ˇ"},
1318        Mode::Normal,
1319    );
1320}
1321
1322#[gpui::test]
1323async fn test_find_multibyte(cx: &mut gpui::TestAppContext) {
1324    let mut cx = NeovimBackedTestContext::new(cx).await;
1325
1326    cx.set_shared_state(r#"<label for="guests">ˇPočet hostů</label>"#)
1327        .await;
1328
1329    cx.simulate_shared_keystrokes("c t < o escape").await;
1330    cx.shared_state()
1331        .await
1332        .assert_eq(r#"<label for="guests">ˇo</label>"#);
1333}
1334
1335#[gpui::test]
1336async fn test_plus_minus(cx: &mut gpui::TestAppContext) {
1337    let mut cx = NeovimBackedTestContext::new(cx).await;
1338
1339    cx.set_shared_state(indoc! {
1340        "one
1341           two
1342        thrˇee
1343    "})
1344        .await;
1345
1346    cx.simulate_shared_keystrokes("-").await;
1347    cx.shared_state().await.assert_matches();
1348    cx.simulate_shared_keystrokes("-").await;
1349    cx.shared_state().await.assert_matches();
1350    cx.simulate_shared_keystrokes("+").await;
1351    cx.shared_state().await.assert_matches();
1352}
1353
1354#[gpui::test]
1355async fn test_command_alias(cx: &mut gpui::TestAppContext) {
1356    let mut cx = VimTestContext::new(cx, true).await;
1357    cx.update_global(|store: &mut SettingsStore, cx| {
1358        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
1359            let mut aliases = HashMap::default();
1360            aliases.insert("Q".to_string(), "upper".to_string());
1361            s.command_aliases = Some(aliases)
1362        });
1363    });
1364
1365    cx.set_state("ˇhello world", Mode::Normal);
1366    cx.simulate_keystrokes(": Q");
1367    cx.set_state("ˇHello world", Mode::Normal);
1368}
1369
1370#[gpui::test]
1371async fn test_remap_adjacent_dog_cat(cx: &mut gpui::TestAppContext) {
1372    let mut cx = NeovimBackedTestContext::new(cx).await;
1373    cx.update(|cx| {
1374        cx.bind_keys([
1375            KeyBinding::new(
1376                "d o g",
1377                workspace::SendKeystrokes("🐶".to_string()),
1378                Some("vim_mode == insert"),
1379            ),
1380            KeyBinding::new(
1381                "c a t",
1382                workspace::SendKeystrokes("🐱".to_string()),
1383                Some("vim_mode == insert"),
1384            ),
1385        ])
1386    });
1387    cx.neovim.exec("imap dog 🐶").await;
1388    cx.neovim.exec("imap cat 🐱").await;
1389
1390    cx.set_shared_state("ˇ").await;
1391    cx.simulate_shared_keystrokes("i d o g").await;
1392    cx.shared_state().await.assert_eq("🐶ˇ");
1393
1394    cx.set_shared_state("ˇ").await;
1395    cx.simulate_shared_keystrokes("i d o d o g").await;
1396    cx.shared_state().await.assert_eq("do🐶ˇ");
1397
1398    cx.set_shared_state("ˇ").await;
1399    cx.simulate_shared_keystrokes("i d o c a t").await;
1400    cx.shared_state().await.assert_eq("do🐱ˇ");
1401}
1402
1403#[gpui::test]
1404async fn test_remap_nested_pineapple(cx: &mut gpui::TestAppContext) {
1405    let mut cx = NeovimBackedTestContext::new(cx).await;
1406    cx.update(|cx| {
1407        cx.bind_keys([
1408            KeyBinding::new(
1409                "p i n",
1410                workspace::SendKeystrokes("📌".to_string()),
1411                Some("vim_mode == insert"),
1412            ),
1413            KeyBinding::new(
1414                "p i n e",
1415                workspace::SendKeystrokes("🌲".to_string()),
1416                Some("vim_mode == insert"),
1417            ),
1418            KeyBinding::new(
1419                "p i n e a p p l e",
1420                workspace::SendKeystrokes("🍍".to_string()),
1421                Some("vim_mode == insert"),
1422            ),
1423        ])
1424    });
1425    cx.neovim.exec("imap pin 📌").await;
1426    cx.neovim.exec("imap pine 🌲").await;
1427    cx.neovim.exec("imap pineapple 🍍").await;
1428
1429    cx.set_shared_state("ˇ").await;
1430    cx.simulate_shared_keystrokes("i p i n").await;
1431    cx.executor().advance_clock(Duration::from_millis(1000));
1432    cx.run_until_parked();
1433    cx.shared_state().await.assert_eq("📌ˇ");
1434
1435    cx.set_shared_state("ˇ").await;
1436    cx.simulate_shared_keystrokes("i p i n e").await;
1437    cx.executor().advance_clock(Duration::from_millis(1000));
1438    cx.run_until_parked();
1439    cx.shared_state().await.assert_eq("🌲ˇ");
1440
1441    cx.set_shared_state("ˇ").await;
1442    cx.simulate_shared_keystrokes("i p i n e a p p l e").await;
1443    cx.shared_state().await.assert_eq("🍍ˇ");
1444}
1445
1446#[gpui::test]
1447async fn test_remap_recursion(cx: &mut gpui::TestAppContext) {
1448    let mut cx = NeovimBackedTestContext::new(cx).await;
1449    cx.update(|cx| {
1450        cx.bind_keys([KeyBinding::new(
1451            "x",
1452            workspace::SendKeystrokes("\" _ x".to_string()),
1453            Some("VimControl"),
1454        )]);
1455        cx.bind_keys([KeyBinding::new(
1456            "y",
1457            workspace::SendKeystrokes("2 x".to_string()),
1458            Some("VimControl"),
1459        )])
1460    });
1461    cx.neovim.exec("noremap x \"_x").await;
1462    cx.neovim.exec("map y 2x").await;
1463
1464    cx.set_shared_state("ˇhello").await;
1465    cx.simulate_shared_keystrokes("d l").await;
1466    cx.shared_clipboard().await.assert_eq("h");
1467    cx.simulate_shared_keystrokes("y").await;
1468    cx.shared_clipboard().await.assert_eq("h");
1469    cx.shared_state().await.assert_eq("ˇlo");
1470}
1471
1472#[gpui::test]
1473async fn test_escape_while_waiting(cx: &mut gpui::TestAppContext) {
1474    let mut cx = NeovimBackedTestContext::new(cx).await;
1475    cx.set_shared_state("ˇhi").await;
1476    cx.simulate_shared_keystrokes("\" + escape x").await;
1477    cx.shared_state().await.assert_eq("ˇi");
1478}
1479
1480#[gpui::test]
1481async fn test_ctrl_w_override(cx: &mut gpui::TestAppContext) {
1482    let mut cx = NeovimBackedTestContext::new(cx).await;
1483    cx.update(|cx| {
1484        cx.bind_keys([KeyBinding::new("ctrl-w", DeleteLine, None)]);
1485    });
1486    cx.neovim.exec("map <c-w> D").await;
1487    cx.set_shared_state("ˇhi").await;
1488    cx.simulate_shared_keystrokes("ctrl-w").await;
1489    cx.shared_state().await.assert_eq("ˇ");
1490}
1491
1492#[gpui::test]
1493async fn test_visual_indent_count(cx: &mut gpui::TestAppContext) {
1494    let mut cx = VimTestContext::new(cx, true).await;
1495    cx.set_state("ˇhi", Mode::Normal);
1496    cx.simulate_keystrokes("shift-v 3 >");
1497    cx.assert_state("            ˇhi", Mode::Normal);
1498    cx.simulate_keystrokes("shift-v 2 <");
1499    cx.assert_state("    ˇhi", Mode::Normal);
1500}
1501
1502#[gpui::test]
1503async fn test_record_replay_recursion(cx: &mut gpui::TestAppContext) {
1504    let mut cx = NeovimBackedTestContext::new(cx).await;
1505
1506    cx.set_shared_state("ˇhello world").await;
1507    cx.simulate_shared_keystrokes(">").await;
1508    cx.simulate_shared_keystrokes(".").await;
1509    cx.simulate_shared_keystrokes(".").await;
1510    cx.simulate_shared_keystrokes(".").await;
1511    cx.shared_state().await.assert_eq("ˇhello world");
1512}
1513
1514#[gpui::test]
1515async fn test_blackhole_register(cx: &mut gpui::TestAppContext) {
1516    let mut cx = NeovimBackedTestContext::new(cx).await;
1517
1518    cx.set_shared_state("ˇhello world").await;
1519    cx.simulate_shared_keystrokes("d i w \" _ d a w").await;
1520    cx.simulate_shared_keystrokes("p").await;
1521    cx.shared_state().await.assert_eq("hellˇo");
1522}
1523
1524#[gpui::test]
1525async fn test_sentence_backwards(cx: &mut gpui::TestAppContext) {
1526    let mut cx = NeovimBackedTestContext::new(cx).await;
1527
1528    cx.set_shared_state("one\n\ntwo\nthree\nˇ\nfour").await;
1529    cx.simulate_shared_keystrokes("(").await;
1530    cx.shared_state()
1531        .await
1532        .assert_eq("one\n\nˇtwo\nthree\n\nfour");
1533
1534    cx.set_shared_state("hello.\n\n\nworˇld.").await;
1535    cx.simulate_shared_keystrokes("(").await;
1536    cx.shared_state().await.assert_eq("hello.\n\n\nˇworld.");
1537    cx.simulate_shared_keystrokes("(").await;
1538    cx.shared_state().await.assert_eq("hello.\n\nˇ\nworld.");
1539    cx.simulate_shared_keystrokes("(").await;
1540    cx.shared_state().await.assert_eq("ˇhello.\n\n\nworld.");
1541
1542    cx.set_shared_state("hello. worlˇd.").await;
1543    cx.simulate_shared_keystrokes("(").await;
1544    cx.shared_state().await.assert_eq("hello. ˇworld.");
1545    cx.simulate_shared_keystrokes("(").await;
1546    cx.shared_state().await.assert_eq("ˇhello. world.");
1547
1548    cx.set_shared_state(". helˇlo.").await;
1549    cx.simulate_shared_keystrokes("(").await;
1550    cx.shared_state().await.assert_eq(". ˇhello.");
1551    cx.simulate_shared_keystrokes("(").await;
1552    cx.shared_state().await.assert_eq(". ˇhello.");
1553
1554    cx.set_shared_state(indoc! {
1555        "{
1556            hello_world();
1557        ˇ}"
1558    })
1559    .await;
1560    cx.simulate_shared_keystrokes("(").await;
1561    cx.shared_state().await.assert_eq(indoc! {
1562        "ˇ{
1563            hello_world();
1564        }"
1565    });
1566
1567    cx.set_shared_state(indoc! {
1568        "Hello! World..?
1569
1570        \tHello! World... ˇ"
1571    })
1572    .await;
1573    cx.simulate_shared_keystrokes("(").await;
1574    cx.shared_state().await.assert_eq(indoc! {
1575        "Hello! World..?
1576
1577        \tHello! ˇWorld... "
1578    });
1579    cx.simulate_shared_keystrokes("(").await;
1580    cx.shared_state().await.assert_eq(indoc! {
1581        "Hello! World..?
1582
1583        \tˇHello! World... "
1584    });
1585    cx.simulate_shared_keystrokes("(").await;
1586    cx.shared_state().await.assert_eq(indoc! {
1587        "Hello! World..?
1588        ˇ
1589        \tHello! World... "
1590    });
1591    cx.simulate_shared_keystrokes("(").await;
1592    cx.shared_state().await.assert_eq(indoc! {
1593        "Hello! ˇWorld..?
1594
1595        \tHello! World... "
1596    });
1597}
1598
1599#[gpui::test]
1600async fn test_sentence_forwards(cx: &mut gpui::TestAppContext) {
1601    let mut cx = NeovimBackedTestContext::new(cx).await;
1602
1603    cx.set_shared_state("helˇlo.\n\n\nworld.").await;
1604    cx.simulate_shared_keystrokes(")").await;
1605    cx.shared_state().await.assert_eq("hello.\nˇ\n\nworld.");
1606    cx.simulate_shared_keystrokes(")").await;
1607    cx.shared_state().await.assert_eq("hello.\n\n\nˇworld.");
1608    cx.simulate_shared_keystrokes(")").await;
1609    cx.shared_state().await.assert_eq("hello.\n\n\nworldˇ.");
1610
1611    cx.set_shared_state("helˇlo.\n\n\nworld.").await;
1612}
1613
1614#[gpui::test]
1615async fn test_ctrl_o_visual(cx: &mut gpui::TestAppContext) {
1616    let mut cx = NeovimBackedTestContext::new(cx).await;
1617
1618    cx.set_shared_state("helloˇ world.").await;
1619    cx.simulate_shared_keystrokes("i ctrl-o v b r l").await;
1620    cx.shared_state().await.assert_eq("ˇllllllworld.");
1621    cx.simulate_shared_keystrokes("ctrl-o v f w d").await;
1622    cx.shared_state().await.assert_eq("ˇorld.");
1623}
1624
1625#[gpui::test]
1626async fn test_ctrl_o_position(cx: &mut gpui::TestAppContext) {
1627    let mut cx = NeovimBackedTestContext::new(cx).await;
1628
1629    cx.set_shared_state("helˇlo world.").await;
1630    cx.simulate_shared_keystrokes("i ctrl-o d i w").await;
1631    cx.shared_state().await.assert_eq("ˇ world.");
1632    cx.simulate_shared_keystrokes("ctrl-o p").await;
1633    cx.shared_state().await.assert_eq(" helloˇworld.");
1634}
1635
1636#[gpui::test]
1637async fn test_ctrl_o_dot(cx: &mut gpui::TestAppContext) {
1638    let mut cx = NeovimBackedTestContext::new(cx).await;
1639
1640    cx.set_shared_state("heˇllo world.").await;
1641    cx.simulate_shared_keystrokes("x i ctrl-o .").await;
1642    cx.shared_state().await.assert_eq("heˇo world.");
1643    cx.simulate_shared_keystrokes("l l escape .").await;
1644    cx.shared_state().await.assert_eq("hellˇllo world.");
1645}