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