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