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, ModeIndicator};
  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.set_state("«hjklˇ»", Mode::Normal);
  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_status_indicator(cx: &mut gpui::TestAppContext) {
 284    let mut cx = VimTestContext::new(cx, true).await;
 285
 286    let mode_indicator = cx.workspace(|workspace, cx| {
 287        let status_bar = workspace.status_bar().read(cx);
 288        let mode_indicator = status_bar.item_of_type::<ModeIndicator>();
 289        assert!(mode_indicator.is_some());
 290        mode_indicator.unwrap()
 291    });
 292
 293    assert_eq!(
 294        cx.workspace(|_, cx| mode_indicator.read(cx).mode),
 295        Some(Mode::Normal)
 296    );
 297
 298    // shows the correct mode
 299    cx.simulate_keystrokes("i");
 300    assert_eq!(
 301        cx.workspace(|_, cx| mode_indicator.read(cx).mode),
 302        Some(Mode::Insert)
 303    );
 304    cx.simulate_keystrokes("escape shift-r");
 305    assert_eq!(
 306        cx.workspace(|_, cx| mode_indicator.read(cx).mode),
 307        Some(Mode::Replace)
 308    );
 309
 310    // shows even in search
 311    cx.simulate_keystrokes("escape v /");
 312    assert_eq!(
 313        cx.workspace(|_, cx| mode_indicator.read(cx).mode),
 314        Some(Mode::Visual)
 315    );
 316
 317    // hides if vim mode is disabled
 318    cx.disable_vim();
 319    cx.run_until_parked();
 320    cx.workspace(|workspace, cx| {
 321        let status_bar = workspace.status_bar().read(cx);
 322        let mode_indicator = status_bar.item_of_type::<ModeIndicator>().unwrap();
 323        assert!(mode_indicator.read(cx).mode.is_none());
 324    });
 325
 326    cx.enable_vim();
 327    cx.run_until_parked();
 328    cx.workspace(|workspace, cx| {
 329        let status_bar = workspace.status_bar().read(cx);
 330        let mode_indicator = status_bar.item_of_type::<ModeIndicator>().unwrap();
 331        assert!(mode_indicator.read(cx).mode.is_some());
 332    });
 333}
 334
 335#[gpui::test]
 336async fn test_word_characters(cx: &mut gpui::TestAppContext) {
 337    let mut cx = VimTestContext::new_typescript(cx).await;
 338    cx.set_state(
 339        indoc! { "
 340        class A {
 341            #ˇgoop = 99;
 342            $ˇgoop () { return this.#gˇoop };
 343        };
 344        console.log(new A().$gooˇp())
 345    "},
 346        Mode::Normal,
 347    );
 348    cx.simulate_keystrokes("v i w");
 349    cx.assert_state(
 350        indoc! {"
 351        class A {
 352            «#goopˇ» = 99;
 353            «$goopˇ» () { return this.«#goopˇ» };
 354        };
 355        console.log(new A().«$goopˇ»())
 356    "},
 357        Mode::Visual,
 358    )
 359}
 360
 361#[gpui::test]
 362async fn test_join_lines(cx: &mut gpui::TestAppContext) {
 363    let mut cx = NeovimBackedTestContext::new(cx).await;
 364
 365    cx.set_shared_state(indoc! {"
 366      ˇone
 367      two
 368      three
 369      four
 370      five
 371      six
 372      "})
 373        .await;
 374    cx.simulate_shared_keystrokes("shift-j").await;
 375    cx.shared_state().await.assert_eq(indoc! {"
 376          oneˇ two
 377          three
 378          four
 379          five
 380          six
 381          "});
 382    cx.simulate_shared_keystrokes("3 shift-j").await;
 383    cx.shared_state().await.assert_eq(indoc! {"
 384          one two threeˇ four
 385          five
 386          six
 387          "});
 388
 389    cx.set_shared_state(indoc! {"
 390      ˇone
 391      two
 392      three
 393      four
 394      five
 395      six
 396      "})
 397        .await;
 398    cx.simulate_shared_keystrokes("j v 3 j shift-j").await;
 399    cx.shared_state().await.assert_eq(indoc! {"
 400      one
 401      two three fourˇ five
 402      six
 403      "});
 404}
 405
 406#[cfg(target_os = "macos")]
 407#[gpui::test]
 408async fn test_wrapped_lines(cx: &mut gpui::TestAppContext) {
 409    let mut cx = NeovimBackedTestContext::new(cx).await;
 410
 411    cx.set_shared_wrap(12).await;
 412    // tests line wrap as follows:
 413    //  1: twelve char
 414    //     twelve char
 415    //  2: twelve char
 416    cx.set_shared_state(indoc! { "
 417        tˇwelve char twelve char
 418        twelve char
 419    "})
 420        .await;
 421    cx.simulate_shared_keystrokes("j").await;
 422    cx.shared_state().await.assert_eq(indoc! {"
 423        twelve char twelve char
 424        tˇwelve char
 425    "});
 426    cx.simulate_shared_keystrokes("k").await;
 427    cx.shared_state().await.assert_eq(indoc! {"
 428        tˇwelve char twelve char
 429        twelve char
 430    "});
 431    cx.simulate_shared_keystrokes("g j").await;
 432    cx.shared_state().await.assert_eq(indoc! {"
 433        twelve char tˇwelve char
 434        twelve char
 435    "});
 436    cx.simulate_shared_keystrokes("g j").await;
 437    cx.shared_state().await.assert_eq(indoc! {"
 438        twelve char twelve char
 439        tˇwelve char
 440    "});
 441
 442    cx.simulate_shared_keystrokes("g k").await;
 443    cx.shared_state().await.assert_eq(indoc! {"
 444        twelve char tˇwelve char
 445        twelve char
 446    "});
 447
 448    cx.simulate_shared_keystrokes("g ^").await;
 449    cx.shared_state().await.assert_eq(indoc! {"
 450        twelve char ˇtwelve char
 451        twelve char
 452    "});
 453
 454    cx.simulate_shared_keystrokes("^").await;
 455    cx.shared_state().await.assert_eq(indoc! {"
 456        ˇtwelve char twelve char
 457        twelve char
 458    "});
 459
 460    cx.simulate_shared_keystrokes("g $").await;
 461    cx.shared_state().await.assert_eq(indoc! {"
 462        twelve charˇ twelve char
 463        twelve char
 464    "});
 465    cx.simulate_shared_keystrokes("$").await;
 466    cx.shared_state().await.assert_eq(indoc! {"
 467        twelve char twelve chaˇr
 468        twelve char
 469    "});
 470
 471    cx.set_shared_state(indoc! { "
 472        tˇwelve char twelve char
 473        twelve char
 474    "})
 475        .await;
 476    cx.simulate_shared_keystrokes("enter").await;
 477    cx.shared_state().await.assert_eq(indoc! {"
 478            twelve char twelve char
 479            ˇtwelve char
 480        "});
 481
 482    cx.set_shared_state(indoc! { "
 483        twelve char
 484        tˇwelve char twelve char
 485        twelve char
 486    "})
 487        .await;
 488    cx.simulate_shared_keystrokes("o o escape").await;
 489    cx.shared_state().await.assert_eq(indoc! {"
 490        twelve char
 491        twelve char twelve char
 492        ˇo
 493        twelve char
 494    "});
 495
 496    cx.set_shared_state(indoc! { "
 497        twelve char
 498        tˇwelve char twelve char
 499        twelve char
 500    "})
 501        .await;
 502    cx.simulate_shared_keystrokes("shift-a a escape").await;
 503    cx.shared_state().await.assert_eq(indoc! {"
 504        twelve char
 505        twelve char twelve charˇa
 506        twelve char
 507    "});
 508    cx.simulate_shared_keystrokes("shift-i i escape").await;
 509    cx.shared_state().await.assert_eq(indoc! {"
 510        twelve char
 511        ˇitwelve char twelve chara
 512        twelve char
 513    "});
 514    cx.simulate_shared_keystrokes("shift-d").await;
 515    cx.shared_state().await.assert_eq(indoc! {"
 516        twelve char
 517        ˇ
 518        twelve char
 519    "});
 520
 521    cx.set_shared_state(indoc! { "
 522        twelve char
 523        twelve char tˇwelve char
 524        twelve char
 525    "})
 526        .await;
 527    cx.simulate_shared_keystrokes("shift-o o escape").await;
 528    cx.shared_state().await.assert_eq(indoc! {"
 529        twelve char
 530        ˇo
 531        twelve char twelve char
 532        twelve char
 533    "});
 534
 535    // line wraps as:
 536    // fourteen ch
 537    // ar
 538    // fourteen ch
 539    // ar
 540    cx.set_shared_state(indoc! { "
 541        fourteen chaˇr
 542        fourteen char
 543    "})
 544        .await;
 545
 546    cx.simulate_shared_keystrokes("d i w").await;
 547    cx.shared_state().await.assert_eq(indoc! {"
 548        fourteenˇ•
 549        fourteen char
 550    "});
 551    cx.simulate_shared_keystrokes("j shift-f e f r").await;
 552    cx.shared_state().await.assert_eq(indoc! {"
 553        fourteen•
 554        fourteen chaˇr
 555    "});
 556}
 557
 558#[gpui::test]
 559async fn test_folds(cx: &mut gpui::TestAppContext) {
 560    let mut cx = NeovimBackedTestContext::new(cx).await;
 561    cx.set_neovim_option("foldmethod=manual").await;
 562
 563    cx.set_shared_state(indoc! { "
 564        fn boop() {
 565          ˇbarp()
 566          bazp()
 567        }
 568    "})
 569        .await;
 570    cx.simulate_shared_keystrokes("shift-v j z f").await;
 571
 572    // visual display is now:
 573    // fn boop () {
 574    //  [FOLDED]
 575    // }
 576
 577    // TODO: this should not be needed but currently zf does not
 578    // return to normal mode.
 579    cx.simulate_shared_keystrokes("escape").await;
 580
 581    // skip over fold downward
 582    cx.simulate_shared_keystrokes("g g").await;
 583    cx.shared_state().await.assert_eq(indoc! {"
 584        ˇfn boop() {
 585          barp()
 586          bazp()
 587        }
 588    "});
 589
 590    cx.simulate_shared_keystrokes("j j").await;
 591    cx.shared_state().await.assert_eq(indoc! {"
 592        fn boop() {
 593          barp()
 594          bazp()
 595        ˇ}
 596    "});
 597
 598    // skip over fold upward
 599    cx.simulate_shared_keystrokes("2 k").await;
 600    cx.shared_state().await.assert_eq(indoc! {"
 601        ˇfn boop() {
 602          barp()
 603          bazp()
 604        }
 605    "});
 606
 607    // yank the fold
 608    cx.simulate_shared_keystrokes("down y y").await;
 609    cx.shared_clipboard()
 610        .await
 611        .assert_eq("  barp()\n  bazp()\n");
 612
 613    // re-open
 614    cx.simulate_shared_keystrokes("z o").await;
 615    cx.shared_state().await.assert_eq(indoc! {"
 616        fn boop() {
 617        ˇ  barp()
 618          bazp()
 619        }
 620    "});
 621}
 622
 623#[gpui::test]
 624async fn test_folds_panic(cx: &mut gpui::TestAppContext) {
 625    let mut cx = NeovimBackedTestContext::new(cx).await;
 626    cx.set_neovim_option("foldmethod=manual").await;
 627
 628    cx.set_shared_state(indoc! { "
 629        fn boop() {
 630          ˇbarp()
 631          bazp()
 632        }
 633    "})
 634        .await;
 635    cx.simulate_shared_keystrokes("shift-v j z f").await;
 636    cx.simulate_shared_keystrokes("escape").await;
 637    cx.simulate_shared_keystrokes("g g").await;
 638    cx.simulate_shared_keystrokes("5 d j").await;
 639    cx.shared_state().await.assert_eq("ˇ");
 640    cx.set_shared_state(indoc! {"
 641        fn boop() {
 642          ˇbarp()
 643          bazp()
 644        }
 645    "})
 646        .await;
 647    cx.simulate_shared_keystrokes("shift-v j j z f").await;
 648    cx.simulate_shared_keystrokes("escape").await;
 649    cx.simulate_shared_keystrokes("shift-g shift-v").await;
 650    cx.shared_state().await.assert_eq(indoc! {"
 651        fn boop() {
 652          barp()
 653          bazp()
 654        }
 655        ˇ"});
 656}
 657
 658#[gpui::test]
 659async fn test_clear_counts(cx: &mut gpui::TestAppContext) {
 660    let mut cx = NeovimBackedTestContext::new(cx).await;
 661
 662    cx.set_shared_state(indoc! {"
 663        The quick brown
 664        fox juˇmps over
 665        the lazy dog"})
 666        .await;
 667
 668    cx.simulate_shared_keystrokes("4 escape 3 d l").await;
 669    cx.shared_state().await.assert_eq(indoc! {"
 670        The quick brown
 671        fox juˇ over
 672        the lazy dog"});
 673}
 674
 675#[gpui::test]
 676async fn test_zero(cx: &mut gpui::TestAppContext) {
 677    let mut cx = NeovimBackedTestContext::new(cx).await;
 678
 679    cx.set_shared_state(indoc! {"
 680        The quˇick brown
 681        fox jumps over
 682        the lazy dog"})
 683        .await;
 684
 685    cx.simulate_shared_keystrokes("0").await;
 686    cx.shared_state().await.assert_eq(indoc! {"
 687        ˇThe quick brown
 688        fox jumps over
 689        the lazy dog"});
 690
 691    cx.simulate_shared_keystrokes("1 0 l").await;
 692    cx.shared_state().await.assert_eq(indoc! {"
 693        The quick ˇbrown
 694        fox jumps over
 695        the lazy dog"});
 696}
 697
 698#[gpui::test]
 699async fn test_selection_goal(cx: &mut gpui::TestAppContext) {
 700    let mut cx = NeovimBackedTestContext::new(cx).await;
 701
 702    cx.set_shared_state(indoc! {"
 703        ;;ˇ;
 704        Lorem Ipsum"})
 705        .await;
 706
 707    cx.simulate_shared_keystrokes("a down up ; down up").await;
 708    cx.shared_state().await.assert_eq(indoc! {"
 709        ;;;;ˇ
 710        Lorem Ipsum"});
 711}
 712
 713#[cfg(target_os = "macos")]
 714#[gpui::test]
 715async fn test_wrapped_motions(cx: &mut gpui::TestAppContext) {
 716    let mut cx = NeovimBackedTestContext::new(cx).await;
 717
 718    cx.set_shared_wrap(12).await;
 719
 720    cx.set_shared_state(indoc! {"
 721                aaˇaa
 722                😃😃"
 723    })
 724    .await;
 725    cx.simulate_shared_keystrokes("j").await;
 726    cx.shared_state().await.assert_eq(indoc! {"
 727                aaaa
 728                😃ˇ😃"
 729    });
 730
 731    cx.set_shared_state(indoc! {"
 732                123456789012aaˇaa
 733                123456789012😃😃"
 734    })
 735    .await;
 736    cx.simulate_shared_keystrokes("j").await;
 737    cx.shared_state().await.assert_eq(indoc! {"
 738        123456789012aaaa
 739        123456789012😃ˇ😃"
 740    });
 741
 742    cx.set_shared_state(indoc! {"
 743                123456789012aaˇaa
 744                123456789012😃😃"
 745    })
 746    .await;
 747    cx.simulate_shared_keystrokes("j").await;
 748    cx.shared_state().await.assert_eq(indoc! {"
 749        123456789012aaaa
 750        123456789012😃ˇ😃"
 751    });
 752
 753    cx.set_shared_state(indoc! {"
 754        123456789012aaaaˇaaaaaaaa123456789012
 755        wow
 756        123456789012😃😃😃😃😃😃123456789012"
 757    })
 758    .await;
 759    cx.simulate_shared_keystrokes("j j").await;
 760    cx.shared_state().await.assert_eq(indoc! {"
 761        123456789012aaaaaaaaaaaa123456789012
 762        wow
 763        123456789012😃😃ˇ😃😃😃😃123456789012"
 764    });
 765}
 766
 767#[gpui::test]
 768async fn test_paragraphs_dont_wrap(cx: &mut gpui::TestAppContext) {
 769    let mut cx = NeovimBackedTestContext::new(cx).await;
 770
 771    cx.set_shared_state(indoc! {"
 772        one
 773        ˇ
 774        two"})
 775        .await;
 776
 777    cx.simulate_shared_keystrokes("} }").await;
 778    cx.shared_state().await.assert_eq(indoc! {"
 779        one
 780
 781        twˇo"});
 782
 783    cx.simulate_shared_keystrokes("{ { {").await;
 784    cx.shared_state().await.assert_eq(indoc! {"
 785        ˇone
 786
 787        two"});
 788}
 789
 790#[gpui::test]
 791async fn test_select_all_issue_2170(cx: &mut gpui::TestAppContext) {
 792    let mut cx = VimTestContext::new(cx, true).await;
 793
 794    cx.set_state(
 795        indoc! {"
 796        defmodule Test do
 797            def test(a, ˇ[_, _] = b), do: IO.puts('hi')
 798        end
 799    "},
 800        Mode::Normal,
 801    );
 802    cx.simulate_keystrokes("g a");
 803    cx.assert_state(
 804        indoc! {"
 805        defmodule Test do
 806            def test(a, «[ˇ»_, _] = b), do: IO.puts('hi')
 807        end
 808    "},
 809        Mode::Visual,
 810    );
 811}
 812
 813#[gpui::test]
 814async fn test_jk(cx: &mut gpui::TestAppContext) {
 815    let mut cx = NeovimBackedTestContext::new(cx).await;
 816
 817    cx.update(|cx| {
 818        cx.bind_keys([KeyBinding::new(
 819            "j k",
 820            NormalBefore,
 821            Some("vim_mode == insert"),
 822        )])
 823    });
 824    cx.neovim.exec("imap jk <esc>").await;
 825
 826    cx.set_shared_state("ˇhello").await;
 827    cx.simulate_shared_keystrokes("i j o j k").await;
 828    cx.shared_state().await.assert_eq("jˇohello");
 829}
 830
 831#[gpui::test]
 832async fn test_jk_delay(cx: &mut gpui::TestAppContext) {
 833    let mut cx = VimTestContext::new(cx, true).await;
 834
 835    cx.update(|cx| {
 836        cx.bind_keys([KeyBinding::new(
 837            "j k",
 838            NormalBefore,
 839            Some("vim_mode == insert"),
 840        )])
 841    });
 842
 843    cx.set_state("ˇhello", Mode::Normal);
 844    cx.simulate_keystrokes("i j");
 845    cx.executor().advance_clock(Duration::from_millis(500));
 846    cx.run_until_parked();
 847    cx.assert_state("ˇhello", Mode::Insert);
 848    cx.executor().advance_clock(Duration::from_millis(500));
 849    cx.run_until_parked();
 850    cx.assert_state("jˇhello", Mode::Insert);
 851    cx.simulate_keystrokes("k j k");
 852    cx.assert_state("jˇkhello", Mode::Normal);
 853}
 854
 855#[gpui::test]
 856async fn test_comma_w(cx: &mut gpui::TestAppContext) {
 857    let mut cx = NeovimBackedTestContext::new(cx).await;
 858
 859    cx.update(|cx| {
 860        cx.bind_keys([KeyBinding::new(
 861            ", w",
 862            motion::Down {
 863                display_lines: false,
 864            },
 865            Some("vim_mode == normal"),
 866        )])
 867    });
 868    cx.neovim.exec("map ,w j").await;
 869
 870    cx.set_shared_state("ˇhello hello\nhello hello").await;
 871    cx.simulate_shared_keystrokes("f o ; , w").await;
 872    cx.shared_state()
 873        .await
 874        .assert_eq("hello hello\nhello hellˇo");
 875
 876    cx.set_shared_state("ˇhello hello\nhello hello").await;
 877    cx.simulate_shared_keystrokes("f o ; , i").await;
 878    cx.shared_state()
 879        .await
 880        .assert_eq("hellˇo hello\nhello hello");
 881}
 882
 883#[gpui::test]
 884async fn test_rename(cx: &mut gpui::TestAppContext) {
 885    let mut cx = VimTestContext::new_typescript(cx).await;
 886
 887    cx.set_state("const beˇfore = 2; console.log(before)", Mode::Normal);
 888    let def_range = cx.lsp_range("const «beforeˇ» = 2; console.log(before)");
 889    let tgt_range = cx.lsp_range("const before = 2; console.log(«beforeˇ»)");
 890    let mut prepare_request =
 891        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
 892            Ok(Some(lsp::PrepareRenameResponse::Range(def_range)))
 893        });
 894    let mut rename_request =
 895        cx.handle_request::<lsp::request::Rename, _, _>(move |url, params, _| async move {
 896            Ok(Some(lsp::WorkspaceEdit {
 897                changes: Some(
 898                    [(
 899                        url.clone(),
 900                        vec![
 901                            lsp::TextEdit::new(def_range, params.new_name.clone()),
 902                            lsp::TextEdit::new(tgt_range, params.new_name),
 903                        ],
 904                    )]
 905                    .into(),
 906                ),
 907                ..Default::default()
 908            }))
 909        });
 910
 911    cx.simulate_keystrokes("c d");
 912    prepare_request.next().await.unwrap();
 913    cx.simulate_input("after");
 914    cx.simulate_keystrokes("enter");
 915    rename_request.next().await.unwrap();
 916    cx.assert_state("const afterˇ = 2; console.log(after)", Mode::Normal)
 917}
 918
 919// TODO: this test is flaky on our linux CI machines
 920#[cfg(target_os = "macos")]
 921#[gpui::test]
 922async fn test_remap(cx: &mut gpui::TestAppContext) {
 923    let mut cx = VimTestContext::new(cx, true).await;
 924
 925    // test moving the cursor
 926    cx.update(|cx| {
 927        cx.bind_keys([KeyBinding::new(
 928            "g z",
 929            workspace::SendKeystrokes("l l l l".to_string()),
 930            None,
 931        )])
 932    });
 933    cx.set_state("ˇ123456789", Mode::Normal);
 934    cx.simulate_keystrokes("g z");
 935    cx.assert_state("1234ˇ56789", Mode::Normal);
 936
 937    // test switching modes
 938    cx.update(|cx| {
 939        cx.bind_keys([KeyBinding::new(
 940            "g y",
 941            workspace::SendKeystrokes("i f o o escape l".to_string()),
 942            None,
 943        )])
 944    });
 945    cx.set_state("ˇ123456789", Mode::Normal);
 946    cx.simulate_keystrokes("g y");
 947    cx.assert_state("fooˇ123456789", Mode::Normal);
 948
 949    // test recursion
 950    cx.update(|cx| {
 951        cx.bind_keys([KeyBinding::new(
 952            "g x",
 953            workspace::SendKeystrokes("g z g y".to_string()),
 954            None,
 955        )])
 956    });
 957    cx.set_state("ˇ123456789", Mode::Normal);
 958    cx.simulate_keystrokes("g x");
 959    cx.assert_state("1234fooˇ56789", Mode::Normal);
 960
 961    cx.executor().allow_parking();
 962
 963    // test command
 964    cx.update(|cx| {
 965        cx.bind_keys([KeyBinding::new(
 966            "g w",
 967            workspace::SendKeystrokes(": j enter".to_string()),
 968            None,
 969        )])
 970    });
 971    cx.set_state("ˇ1234\n56789", Mode::Normal);
 972    cx.simulate_keystrokes("g w");
 973    cx.assert_state("1234ˇ 56789", Mode::Normal);
 974
 975    // test leaving command
 976    cx.update(|cx| {
 977        cx.bind_keys([KeyBinding::new(
 978            "g u",
 979            workspace::SendKeystrokes("g w g z".to_string()),
 980            None,
 981        )])
 982    });
 983    cx.set_state("ˇ1234\n56789", Mode::Normal);
 984    cx.simulate_keystrokes("g u");
 985    cx.assert_state("1234 567ˇ89", Mode::Normal);
 986
 987    // test leaving command
 988    cx.update(|cx| {
 989        cx.bind_keys([KeyBinding::new(
 990            "g t",
 991            workspace::SendKeystrokes("i space escape".to_string()),
 992            None,
 993        )])
 994    });
 995    cx.set_state("12ˇ34", Mode::Normal);
 996    cx.simulate_keystrokes("g t");
 997    cx.assert_state("12ˇ 34", Mode::Normal);
 998}
 999
1000#[gpui::test]
1001async fn test_undo(cx: &mut gpui::TestAppContext) {
1002    let mut cx = NeovimBackedTestContext::new(cx).await;
1003
1004    cx.set_shared_state("hello quˇoel world").await;
1005    cx.simulate_shared_keystrokes("v i w s c o escape u").await;
1006    cx.shared_state().await.assert_eq("hello ˇquoel world");
1007    cx.simulate_shared_keystrokes("ctrl-r").await;
1008    cx.shared_state().await.assert_eq("hello ˇco world");
1009    cx.simulate_shared_keystrokes("a o right l escape").await;
1010    cx.shared_state().await.assert_eq("hello cooˇl world");
1011    cx.simulate_shared_keystrokes("u").await;
1012    cx.shared_state().await.assert_eq("hello cooˇ world");
1013    cx.simulate_shared_keystrokes("u").await;
1014    cx.shared_state().await.assert_eq("hello cˇo world");
1015    cx.simulate_shared_keystrokes("u").await;
1016    cx.shared_state().await.assert_eq("hello ˇquoel world");
1017
1018    cx.set_shared_state("hello quˇoel world").await;
1019    cx.simulate_shared_keystrokes("v i w ~ u").await;
1020    cx.shared_state().await.assert_eq("hello ˇquoel world");
1021
1022    cx.set_shared_state("\nhello quˇoel world\n").await;
1023    cx.simulate_shared_keystrokes("shift-v s c escape u").await;
1024    cx.shared_state().await.assert_eq("\nˇhello quoel world\n");
1025
1026    cx.set_shared_state(indoc! {"
1027        ˇ1
1028        2
1029        3"})
1030        .await;
1031
1032    cx.simulate_shared_keystrokes("ctrl-v shift-g ctrl-a").await;
1033    cx.shared_state().await.assert_eq(indoc! {"
1034        ˇ2
1035        3
1036        4"});
1037
1038    cx.simulate_shared_keystrokes("u").await;
1039    cx.shared_state().await.assert_eq(indoc! {"
1040        ˇ1
1041        2
1042        3"});
1043}
1044
1045#[gpui::test]
1046async fn test_mouse_selection(cx: &mut TestAppContext) {
1047    let mut cx = VimTestContext::new(cx, true).await;
1048
1049    cx.set_state("ˇone two three", Mode::Normal);
1050
1051    let start_point = cx.pixel_position("one twˇo three");
1052    let end_point = cx.pixel_position("one ˇtwo three");
1053
1054    cx.simulate_mouse_down(start_point, MouseButton::Left, Modifiers::none());
1055    cx.simulate_mouse_move(end_point, MouseButton::Left, Modifiers::none());
1056    cx.simulate_mouse_up(end_point, MouseButton::Left, Modifiers::none());
1057
1058    cx.assert_state("one «ˇtwo» three", Mode::Visual)
1059}
1060
1061#[gpui::test]
1062async fn test_lowercase_marks(cx: &mut TestAppContext) {
1063    let mut cx = NeovimBackedTestContext::new(cx).await;
1064
1065    cx.set_shared_state("line one\nline ˇtwo\nline three").await;
1066    cx.simulate_shared_keystrokes("m a l ' a").await;
1067    cx.shared_state()
1068        .await
1069        .assert_eq("line one\nˇline two\nline three");
1070    cx.simulate_shared_keystrokes("` a").await;
1071    cx.shared_state()
1072        .await
1073        .assert_eq("line one\nline ˇtwo\nline three");
1074
1075    cx.simulate_shared_keystrokes("^ d ` a").await;
1076    cx.shared_state()
1077        .await
1078        .assert_eq("line one\nˇtwo\nline three");
1079}
1080
1081#[gpui::test]
1082async fn test_lt_gt_marks(cx: &mut TestAppContext) {
1083    let mut cx = NeovimBackedTestContext::new(cx).await;
1084
1085    cx.set_shared_state(indoc!(
1086        "
1087        Line one
1088        Line two
1089        Line ˇthree
1090        Line four
1091        Line five
1092    "
1093    ))
1094    .await;
1095
1096    cx.simulate_shared_keystrokes("v j escape k k").await;
1097
1098    cx.simulate_shared_keystrokes("' <").await;
1099    cx.shared_state().await.assert_eq(indoc! {"
1100        Line one
1101        Line two
1102        ˇLine three
1103        Line four
1104        Line five
1105    "});
1106
1107    cx.simulate_shared_keystrokes("` <").await;
1108    cx.shared_state().await.assert_eq(indoc! {"
1109        Line one
1110        Line two
1111        Line ˇthree
1112        Line four
1113        Line five
1114    "});
1115
1116    cx.simulate_shared_keystrokes("' >").await;
1117    cx.shared_state().await.assert_eq(indoc! {"
1118        Line one
1119        Line two
1120        Line three
1121        ˇLine four
1122        Line five
1123    "
1124    });
1125
1126    cx.simulate_shared_keystrokes("` >").await;
1127    cx.shared_state().await.assert_eq(indoc! {"
1128        Line one
1129        Line two
1130        Line three
1131        Line ˇfour
1132        Line five
1133    "
1134    });
1135
1136    cx.simulate_shared_keystrokes("v i w o escape").await;
1137    cx.simulate_shared_keystrokes("` >").await;
1138    cx.shared_state().await.assert_eq(indoc! {"
1139        Line one
1140        Line two
1141        Line three
1142        Line fouˇr
1143        Line five
1144    "
1145    });
1146    cx.simulate_shared_keystrokes("` <").await;
1147    cx.shared_state().await.assert_eq(indoc! {"
1148        Line one
1149        Line two
1150        Line three
1151        Line ˇfour
1152        Line five
1153    "
1154    });
1155}
1156
1157#[gpui::test]
1158async fn test_caret_mark(cx: &mut TestAppContext) {
1159    let mut cx = NeovimBackedTestContext::new(cx).await;
1160
1161    cx.set_shared_state(indoc!(
1162        "
1163        Line one
1164        Line two
1165        Line three
1166        ˇLine four
1167        Line five
1168    "
1169    ))
1170    .await;
1171
1172    cx.simulate_shared_keystrokes("c w shift-s t r a i g h t space t h i n g escape j j")
1173        .await;
1174
1175    cx.simulate_shared_keystrokes("' ^").await;
1176    cx.shared_state().await.assert_eq(indoc! {"
1177        Line one
1178        Line two
1179        Line three
1180        ˇStraight thing four
1181        Line five
1182    "
1183    });
1184
1185    cx.simulate_shared_keystrokes("` ^").await;
1186    cx.shared_state().await.assert_eq(indoc! {"
1187        Line one
1188        Line two
1189        Line three
1190        Straight thingˇ four
1191        Line five
1192    "
1193    });
1194
1195    cx.simulate_shared_keystrokes("k a ! escape k g i ?").await;
1196    cx.shared_state().await.assert_eq(indoc! {"
1197        Line one
1198        Line two
1199        Line three!?ˇ
1200        Straight thing four
1201        Line five
1202    "
1203    });
1204}
1205
1206#[cfg(target_os = "macos")]
1207#[gpui::test]
1208async fn test_dw_eol(cx: &mut gpui::TestAppContext) {
1209    let mut cx = NeovimBackedTestContext::new(cx).await;
1210
1211    cx.set_shared_wrap(12).await;
1212    cx.set_shared_state("twelve ˇchar twelve char\ntwelve char")
1213        .await;
1214    cx.simulate_shared_keystrokes("d w").await;
1215    cx.shared_state()
1216        .await
1217        .assert_eq("twelve ˇtwelve char\ntwelve char");
1218}
1219
1220#[gpui::test]
1221async fn test_toggle_comments(cx: &mut gpui::TestAppContext) {
1222    let mut cx = VimTestContext::new(cx, true).await;
1223
1224    let language = std::sync::Arc::new(language::Language::new(
1225        language::LanguageConfig {
1226            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
1227            ..Default::default()
1228        },
1229        Some(language::tree_sitter_rust::language()),
1230    ));
1231    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1232
1233    // works in normal model
1234    cx.set_state(
1235        indoc! {"
1236      ˇone
1237      two
1238      three
1239      "},
1240        Mode::Normal,
1241    );
1242    cx.simulate_keystrokes("g c c");
1243    cx.assert_state(
1244        indoc! {"
1245          // ˇone
1246          two
1247          three
1248          "},
1249        Mode::Normal,
1250    );
1251
1252    // works in visual mode
1253    cx.simulate_keystrokes("v j g c");
1254    cx.assert_state(
1255        indoc! {"
1256          // // ˇone
1257          // two
1258          three
1259          "},
1260        Mode::Normal,
1261    );
1262
1263    // works in visual line mode
1264    cx.simulate_keystrokes("shift-v j g c");
1265    cx.assert_state(
1266        indoc! {"
1267          // ˇone
1268          two
1269          three
1270          "},
1271        Mode::Normal,
1272    );
1273
1274    // works with count
1275    cx.simulate_keystrokes("g c 2 j");
1276    cx.assert_state(
1277        indoc! {"
1278            // // ˇone
1279            // two
1280            // three
1281            "},
1282        Mode::Normal,
1283    );
1284
1285    // works with motion object
1286    cx.simulate_keystrokes("shift-g");
1287    cx.simulate_keystrokes("g c g g");
1288    cx.assert_state(
1289        indoc! {"
1290            // one
1291            two
1292            three
1293            ˇ"},
1294        Mode::Normal,
1295    );
1296}
1297
1298#[gpui::test]
1299async fn test_find_multibyte(cx: &mut gpui::TestAppContext) {
1300    let mut cx = NeovimBackedTestContext::new(cx).await;
1301
1302    cx.set_shared_state(r#"<label for="guests">ˇPočet hostů</label>"#)
1303        .await;
1304
1305    cx.simulate_shared_keystrokes("c t < o escape").await;
1306    cx.shared_state()
1307        .await
1308        .assert_eq(r#"<label for="guests">ˇo</label>"#);
1309}
1310
1311#[gpui::test]
1312async fn test_plus_minus(cx: &mut gpui::TestAppContext) {
1313    let mut cx = NeovimBackedTestContext::new(cx).await;
1314
1315    cx.set_shared_state(indoc! {
1316        "one
1317           two
1318        thrˇee
1319    "})
1320        .await;
1321
1322    cx.simulate_shared_keystrokes("-").await;
1323    cx.shared_state().await.assert_matches();
1324    cx.simulate_shared_keystrokes("-").await;
1325    cx.shared_state().await.assert_matches();
1326    cx.simulate_shared_keystrokes("+").await;
1327    cx.shared_state().await.assert_matches();
1328}
1329
1330#[gpui::test]
1331async fn test_command_alias(cx: &mut gpui::TestAppContext) {
1332    let mut cx = VimTestContext::new(cx, true).await;
1333    cx.update_global(|store: &mut SettingsStore, cx| {
1334        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
1335            let mut aliases = HashMap::default();
1336            aliases.insert("Q".to_string(), "upper".to_string());
1337            s.command_aliases = Some(aliases)
1338        });
1339    });
1340
1341    cx.set_state("ˇhello world", Mode::Normal);
1342    cx.simulate_keystrokes(": Q");
1343    cx.set_state("ˇHello world", Mode::Normal);
1344}
1345
1346#[gpui::test]
1347async fn test_remap_adjacent_dog_cat(cx: &mut gpui::TestAppContext) {
1348    let mut cx = NeovimBackedTestContext::new(cx).await;
1349    cx.update(|cx| {
1350        cx.bind_keys([
1351            KeyBinding::new(
1352                "d o g",
1353                workspace::SendKeystrokes("🐶".to_string()),
1354                Some("vim_mode == insert"),
1355            ),
1356            KeyBinding::new(
1357                "c a t",
1358                workspace::SendKeystrokes("🐱".to_string()),
1359                Some("vim_mode == insert"),
1360            ),
1361        ])
1362    });
1363    cx.neovim.exec("imap dog 🐶").await;
1364    cx.neovim.exec("imap cat 🐱").await;
1365
1366    cx.set_shared_state("ˇ").await;
1367    cx.simulate_shared_keystrokes("i d o g").await;
1368    cx.shared_state().await.assert_eq("🐶ˇ");
1369
1370    cx.set_shared_state("ˇ").await;
1371    cx.simulate_shared_keystrokes("i d o d o g").await;
1372    cx.shared_state().await.assert_eq("do🐶ˇ");
1373
1374    cx.set_shared_state("ˇ").await;
1375    cx.simulate_shared_keystrokes("i d o c a t").await;
1376    cx.shared_state().await.assert_eq("do🐱ˇ");
1377}
1378
1379#[gpui::test]
1380async fn test_remap_nested_pineapple(cx: &mut gpui::TestAppContext) {
1381    let mut cx = NeovimBackedTestContext::new(cx).await;
1382    cx.update(|cx| {
1383        cx.bind_keys([
1384            KeyBinding::new(
1385                "p i n",
1386                workspace::SendKeystrokes("📌".to_string()),
1387                Some("vim_mode == insert"),
1388            ),
1389            KeyBinding::new(
1390                "p i n e",
1391                workspace::SendKeystrokes("🌲".to_string()),
1392                Some("vim_mode == insert"),
1393            ),
1394            KeyBinding::new(
1395                "p i n e a p p l e",
1396                workspace::SendKeystrokes("🍍".to_string()),
1397                Some("vim_mode == insert"),
1398            ),
1399        ])
1400    });
1401    cx.neovim.exec("imap pin 📌").await;
1402    cx.neovim.exec("imap pine 🌲").await;
1403    cx.neovim.exec("imap pineapple 🍍").await;
1404
1405    cx.set_shared_state("ˇ").await;
1406    cx.simulate_shared_keystrokes("i p i n").await;
1407    cx.executor().advance_clock(Duration::from_millis(1000));
1408    cx.run_until_parked();
1409    cx.shared_state().await.assert_eq("📌ˇ");
1410
1411    cx.set_shared_state("ˇ").await;
1412    cx.simulate_shared_keystrokes("i p i n e").await;
1413    cx.executor().advance_clock(Duration::from_millis(1000));
1414    cx.run_until_parked();
1415    cx.shared_state().await.assert_eq("🌲ˇ");
1416
1417    cx.set_shared_state("ˇ").await;
1418    cx.simulate_shared_keystrokes("i p i n e a p p l e").await;
1419    cx.shared_state().await.assert_eq("🍍ˇ");
1420}
1421
1422#[gpui::test]
1423async fn test_escape_while_waiting(cx: &mut gpui::TestAppContext) {
1424    let mut cx = NeovimBackedTestContext::new(cx).await;
1425    cx.set_shared_state("ˇhi").await;
1426    cx.simulate_shared_keystrokes("\" + escape x").await;
1427    cx.shared_state().await.assert_eq("ˇi");
1428}
1429
1430#[gpui::test]
1431async fn test_ctrl_w_override(cx: &mut gpui::TestAppContext) {
1432    let mut cx = NeovimBackedTestContext::new(cx).await;
1433    cx.update(|cx| {
1434        cx.bind_keys([KeyBinding::new("ctrl-w", DeleteLine, None)]);
1435    });
1436    cx.neovim.exec("map <c-w> D").await;
1437    cx.set_shared_state("ˇhi").await;
1438    cx.simulate_shared_keystrokes("ctrl-w").await;
1439    cx.shared_state().await.assert_eq("ˇ");
1440}
1441
1442#[gpui::test]
1443async fn test_visual_indent_count(cx: &mut gpui::TestAppContext) {
1444    let mut cx = VimTestContext::new(cx, true).await;
1445    cx.set_state("ˇhi", Mode::Normal);
1446    cx.simulate_keystrokes("shift-v 3 >");
1447    cx.assert_state("            ˇhi", Mode::Normal);
1448    cx.simulate_keystrokes("shift-v 2 <");
1449    cx.assert_state("    ˇhi", Mode::Normal);
1450}