test.rs

   1mod neovim_backed_binding_test_context;
   2mod neovim_backed_test_context;
   3mod neovim_connection;
   4mod vim_test_context;
   5
   6use std::time::Duration;
   7
   8use command_palette::CommandPalette;
   9use editor::DisplayPoint;
  10use futures::StreamExt;
  11use gpui::KeyBinding;
  12pub use neovim_backed_binding_test_context::*;
  13pub use neovim_backed_test_context::*;
  14pub use vim_test_context::*;
  15
  16use indoc::indoc;
  17use search::BufferSearchBar;
  18
  19use crate::{insert::NormalBefore, motion, state::Mode, ModeIndicator};
  20
  21#[gpui::test]
  22async fn test_initially_disabled(cx: &mut gpui::TestAppContext) {
  23    let mut cx = VimTestContext::new(cx, false).await;
  24    cx.simulate_keystrokes(["h", "j", "k", "l"]);
  25    cx.assert_editor_state("hjklˇ");
  26}
  27
  28#[gpui::test]
  29async fn test_neovim(cx: &mut gpui::TestAppContext) {
  30    let mut cx = NeovimBackedTestContext::new(cx).await;
  31
  32    cx.simulate_shared_keystroke("i").await;
  33    cx.assert_state_matches().await;
  34    cx.simulate_shared_keystrokes([
  35        "shift-T", "e", "s", "t", " ", "t", "e", "s", "t", "escape", "0", "d", "w",
  36    ])
  37    .await;
  38    cx.assert_state_matches().await;
  39    cx.assert_editor_state("ˇtest");
  40}
  41
  42#[gpui::test]
  43async fn test_toggle_through_settings(cx: &mut gpui::TestAppContext) {
  44    let mut cx = VimTestContext::new(cx, true).await;
  45
  46    cx.simulate_keystroke("i");
  47    assert_eq!(cx.mode(), Mode::Insert);
  48
  49    // Editor acts as though vim is disabled
  50    cx.disable_vim();
  51    cx.simulate_keystrokes(["h", "j", "k", "l"]);
  52    cx.assert_editor_state("hjklˇ");
  53
  54    // Selections aren't changed if editor is blurred but vim-mode is still disabled.
  55    cx.set_state("«hjklˇ»", Mode::Normal);
  56    cx.assert_editor_state("«hjklˇ»");
  57    cx.update_editor(|_, cx| cx.blur());
  58    cx.assert_editor_state("«hjklˇ»");
  59    cx.update_editor(|_, cx| cx.focus_self());
  60    cx.assert_editor_state("«hjklˇ»");
  61
  62    // Enabling dynamically sets vim mode again and restores normal mode
  63    cx.enable_vim();
  64    assert_eq!(cx.mode(), Mode::Normal);
  65    cx.simulate_keystrokes(["h", "h", "h", "l"]);
  66    assert_eq!(cx.buffer_text(), "hjkl".to_owned());
  67    cx.assert_editor_state("hˇjkl");
  68    cx.simulate_keystrokes(["i", "T", "e", "s", "t"]);
  69    cx.assert_editor_state("hTestˇjkl");
  70
  71    // Disabling and enabling resets to normal mode
  72    assert_eq!(cx.mode(), Mode::Insert);
  73    cx.disable_vim();
  74    cx.enable_vim();
  75    assert_eq!(cx.mode(), Mode::Normal);
  76}
  77
  78#[gpui::test]
  79async fn test_cancel_selection(cx: &mut gpui::TestAppContext) {
  80    let mut cx = VimTestContext::new(cx, true).await;
  81
  82    cx.set_state(
  83        indoc! {"The quick brown fox juˇmps over the lazy dog"},
  84        Mode::Normal,
  85    );
  86    // jumps
  87    cx.simulate_keystrokes(["v", "l", "l"]);
  88    cx.assert_editor_state("The quick brown fox ju«mpsˇ» over the lazy dog");
  89
  90    cx.simulate_keystrokes(["escape"]);
  91    cx.assert_editor_state("The quick brown fox jumpˇs over the lazy dog");
  92
  93    // go back to the same selection state
  94    cx.simulate_keystrokes(["v", "h", "h"]);
  95    cx.assert_editor_state("The quick brown fox ju«ˇmps» over the lazy dog");
  96
  97    // Ctrl-[ should behave like Esc
  98    cx.simulate_keystrokes(["ctrl-["]);
  99    cx.assert_editor_state("The quick brown fox juˇmps over the lazy dog");
 100}
 101
 102#[gpui::test]
 103async fn test_buffer_search(cx: &mut gpui::TestAppContext) {
 104    let mut cx = VimTestContext::new(cx, true).await;
 105
 106    cx.set_state(
 107        indoc! {"
 108            The quick brown
 109            fox juˇmps over
 110            the lazy dog"},
 111        Mode::Normal,
 112    );
 113    cx.simulate_keystroke("/");
 114
 115    let search_bar = cx.workspace(|workspace, cx| {
 116        workspace
 117            .active_pane()
 118            .read(cx)
 119            .toolbar()
 120            .read(cx)
 121            .item_of_type::<BufferSearchBar>()
 122            .expect("Buffer search bar should be deployed")
 123    });
 124
 125    cx.update_view(search_bar, |bar, cx| {
 126        assert_eq!(bar.query(cx), "");
 127    })
 128}
 129
 130#[gpui::test]
 131async fn test_count_down(cx: &mut gpui::TestAppContext) {
 132    let mut cx = VimTestContext::new(cx, true).await;
 133
 134    cx.set_state(indoc! {"aˇa\nbb\ncc\ndd\nee"}, Mode::Normal);
 135    cx.simulate_keystrokes(["2", "down"]);
 136    cx.assert_editor_state("aa\nbb\ncˇc\ndd\nee");
 137    cx.simulate_keystrokes(["9", "down"]);
 138    cx.assert_editor_state("aa\nbb\ncc\ndd\neˇe");
 139}
 140
 141#[gpui::test]
 142async fn test_end_of_document_710(cx: &mut gpui::TestAppContext) {
 143    let mut cx = VimTestContext::new(cx, true).await;
 144
 145    // goes to end by default
 146    cx.set_state(indoc! {"aˇa\nbb\ncc"}, Mode::Normal);
 147    cx.simulate_keystrokes(["shift-g"]);
 148    cx.assert_editor_state("aa\nbb\ncˇc");
 149
 150    // can go to line 1 (https://github.com/zed-industries/zed/issues/5812)
 151    cx.simulate_keystrokes(["1", "shift-g"]);
 152    cx.assert_editor_state("aˇa\nbb\ncc");
 153}
 154
 155#[gpui::test]
 156async fn test_end_of_line_with_times(cx: &mut gpui::TestAppContext) {
 157    let mut cx = VimTestContext::new(cx, true).await;
 158
 159    // goes to current line end
 160    cx.set_state(indoc! {"ˇaa\nbb\ncc"}, Mode::Normal);
 161    cx.simulate_keystrokes(["$"]);
 162    cx.assert_editor_state("aˇa\nbb\ncc");
 163
 164    // goes to next line end
 165    cx.simulate_keystrokes(["2", "$"]);
 166    cx.assert_editor_state("aa\nbˇb\ncc");
 167
 168    // try to exceed the final line.
 169    cx.simulate_keystrokes(["4", "$"]);
 170    cx.assert_editor_state("aa\nbb\ncˇc");
 171}
 172
 173#[gpui::test]
 174async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 175    let mut cx = VimTestContext::new(cx, true).await;
 176
 177    // works in normal mode
 178    cx.set_state(indoc! {"aa\nbˇb\ncc"}, Mode::Normal);
 179    cx.simulate_keystrokes([">", ">"]);
 180    cx.assert_editor_state("aa\n    bˇb\ncc");
 181    cx.simulate_keystrokes(["<", "<"]);
 182    cx.assert_editor_state("aa\nbˇb\ncc");
 183
 184    // works in visual mode
 185    cx.simulate_keystrokes(["shift-v", "down", ">"]);
 186    cx.assert_editor_state("aa\n    bb\n    cˇc");
 187}
 188
 189#[gpui::test]
 190async fn test_escape_command_palette(cx: &mut gpui::TestAppContext) {
 191    let mut cx = VimTestContext::new(cx, true).await;
 192
 193    cx.set_state("aˇbc\n", Mode::Normal);
 194    cx.simulate_keystrokes(["i", "cmd-shift-p"]);
 195
 196    assert!(cx.workspace(|workspace, cx| workspace.active_modal::<CommandPalette>(cx).is_some()));
 197    cx.simulate_keystroke("escape");
 198    cx.run_until_parked();
 199    assert!(!cx.workspace(|workspace, cx| workspace.active_modal::<CommandPalette>(cx).is_some()));
 200    cx.assert_state("aˇbc\n", Mode::Insert);
 201}
 202
 203#[gpui::test]
 204async fn test_escape_cancels(cx: &mut gpui::TestAppContext) {
 205    let mut cx = VimTestContext::new(cx, true).await;
 206
 207    cx.set_state("aˇbˇc", Mode::Normal);
 208    cx.simulate_keystrokes(["escape"]);
 209
 210    cx.assert_state("aˇbc", Mode::Normal);
 211}
 212
 213#[gpui::test]
 214async fn test_selection_on_search(cx: &mut gpui::TestAppContext) {
 215    let mut cx = VimTestContext::new(cx, true).await;
 216
 217    cx.set_state(indoc! {"aa\nbˇb\ncc\ncc\ncc\n"}, Mode::Normal);
 218    cx.simulate_keystrokes(["/", "c", "c"]);
 219
 220    let search_bar = cx.workspace(|workspace, cx| {
 221        workspace
 222            .active_pane()
 223            .read(cx)
 224            .toolbar()
 225            .read(cx)
 226            .item_of_type::<BufferSearchBar>()
 227            .expect("Buffer search bar should be deployed")
 228    });
 229
 230    cx.update_view(search_bar, |bar, cx| {
 231        assert_eq!(bar.query(cx), "cc");
 232    });
 233
 234    cx.update_editor(|editor, cx| {
 235        let highlights = editor.all_text_background_highlights(cx);
 236        assert_eq!(3, highlights.len());
 237        assert_eq!(
 238            DisplayPoint::new(2, 0)..DisplayPoint::new(2, 2),
 239            highlights[0].0
 240        )
 241    });
 242    cx.simulate_keystrokes(["enter"]);
 243
 244    cx.assert_state(indoc! {"aa\nbb\nˇcc\ncc\ncc\n"}, Mode::Normal);
 245    cx.simulate_keystrokes(["n"]);
 246    cx.assert_state(indoc! {"aa\nbb\ncc\nˇcc\ncc\n"}, Mode::Normal);
 247    cx.simulate_keystrokes(["shift-n"]);
 248    cx.assert_state(indoc! {"aa\nbb\nˇcc\ncc\ncc\n"}, Mode::Normal);
 249}
 250
 251#[gpui::test]
 252async fn test_status_indicator(cx: &mut gpui::TestAppContext) {
 253    let mut cx = VimTestContext::new(cx, true).await;
 254
 255    let mode_indicator = cx.workspace(|workspace, cx| {
 256        let status_bar = workspace.status_bar().read(cx);
 257        let mode_indicator = status_bar.item_of_type::<ModeIndicator>();
 258        assert!(mode_indicator.is_some());
 259        mode_indicator.unwrap()
 260    });
 261
 262    assert_eq!(
 263        cx.workspace(|_, cx| mode_indicator.read(cx).mode),
 264        Some(Mode::Normal)
 265    );
 266
 267    // shows the correct mode
 268    cx.simulate_keystrokes(["i"]);
 269    assert_eq!(
 270        cx.workspace(|_, cx| mode_indicator.read(cx).mode),
 271        Some(Mode::Insert)
 272    );
 273
 274    // shows even in search
 275    cx.simulate_keystrokes(["escape", "v", "/"]);
 276    assert_eq!(
 277        cx.workspace(|_, cx| mode_indicator.read(cx).mode),
 278        Some(Mode::Visual)
 279    );
 280
 281    // hides if vim mode is disabled
 282    cx.disable_vim();
 283    cx.run_until_parked();
 284    cx.workspace(|workspace, cx| {
 285        let status_bar = workspace.status_bar().read(cx);
 286        let mode_indicator = status_bar.item_of_type::<ModeIndicator>().unwrap();
 287        assert!(mode_indicator.read(cx).mode.is_none());
 288    });
 289
 290    cx.enable_vim();
 291    cx.run_until_parked();
 292    cx.workspace(|workspace, cx| {
 293        let status_bar = workspace.status_bar().read(cx);
 294        let mode_indicator = status_bar.item_of_type::<ModeIndicator>().unwrap();
 295        assert!(mode_indicator.read(cx).mode.is_some());
 296    });
 297}
 298
 299#[gpui::test]
 300async fn test_word_characters(cx: &mut gpui::TestAppContext) {
 301    let mut cx = VimTestContext::new_typescript(cx).await;
 302    cx.set_state(
 303        indoc! { "
 304        class A {
 305            #ˇgoop = 99;
 306            $ˇgoop () { return this.#gˇoop };
 307        };
 308        console.log(new A().$gooˇp())
 309    "},
 310        Mode::Normal,
 311    );
 312    cx.simulate_keystrokes(["v", "i", "w"]);
 313    cx.assert_state(
 314        indoc! {"
 315        class A {
 316            «#goopˇ» = 99;
 317            «$goopˇ» () { return this.«#goopˇ» };
 318        };
 319        console.log(new A().«$goopˇ»())
 320    "},
 321        Mode::Visual,
 322    )
 323}
 324
 325#[gpui::test]
 326async fn test_join_lines(cx: &mut gpui::TestAppContext) {
 327    let mut cx = NeovimBackedTestContext::new(cx).await;
 328
 329    cx.set_shared_state(indoc! {"
 330      ˇone
 331      two
 332      three
 333      four
 334      five
 335      six
 336      "})
 337        .await;
 338    cx.simulate_shared_keystrokes(["shift-j"]).await;
 339    cx.assert_shared_state(indoc! {"
 340          oneˇ two
 341          three
 342          four
 343          five
 344          six
 345          "})
 346        .await;
 347    cx.simulate_shared_keystrokes(["3", "shift-j"]).await;
 348    cx.assert_shared_state(indoc! {"
 349          one two threeˇ four
 350          five
 351          six
 352          "})
 353        .await;
 354
 355    cx.set_shared_state(indoc! {"
 356      ˇone
 357      two
 358      three
 359      four
 360      five
 361      six
 362      "})
 363        .await;
 364    cx.simulate_shared_keystrokes(["j", "v", "3", "j", "shift-j"])
 365        .await;
 366    cx.assert_shared_state(indoc! {"
 367      one
 368      two three fourˇ five
 369      six
 370      "})
 371        .await;
 372}
 373
 374#[gpui::test]
 375async fn test_wrapped_lines(cx: &mut gpui::TestAppContext) {
 376    let mut cx = NeovimBackedTestContext::new(cx).await;
 377
 378    cx.set_shared_wrap(12).await;
 379    // tests line wrap as follows:
 380    //  1: twelve char
 381    //     twelve char
 382    //  2: twelve char
 383    cx.set_shared_state(indoc! { "
 384        tˇwelve char twelve char
 385        twelve char
 386    "})
 387        .await;
 388    cx.simulate_shared_keystrokes(["j"]).await;
 389    cx.assert_shared_state(indoc! { "
 390        twelve char twelve char
 391        tˇwelve char
 392    "})
 393        .await;
 394    cx.simulate_shared_keystrokes(["k"]).await;
 395    cx.assert_shared_state(indoc! { "
 396        tˇwelve char twelve char
 397        twelve char
 398    "})
 399        .await;
 400    cx.simulate_shared_keystrokes(["g", "j"]).await;
 401    cx.assert_shared_state(indoc! { "
 402        twelve char tˇwelve char
 403        twelve char
 404    "})
 405        .await;
 406    cx.simulate_shared_keystrokes(["g", "j"]).await;
 407    cx.assert_shared_state(indoc! { "
 408        twelve char twelve char
 409        tˇwelve char
 410    "})
 411        .await;
 412
 413    cx.simulate_shared_keystrokes(["g", "k"]).await;
 414    cx.assert_shared_state(indoc! { "
 415        twelve char tˇwelve char
 416        twelve char
 417    "})
 418        .await;
 419
 420    cx.simulate_shared_keystrokes(["g", "^"]).await;
 421    cx.assert_shared_state(indoc! { "
 422        twelve char ˇtwelve char
 423        twelve char
 424    "})
 425        .await;
 426
 427    cx.simulate_shared_keystrokes(["^"]).await;
 428    cx.assert_shared_state(indoc! { "
 429        ˇtwelve char twelve char
 430        twelve char
 431    "})
 432        .await;
 433
 434    cx.simulate_shared_keystrokes(["g", "$"]).await;
 435    cx.assert_shared_state(indoc! { "
 436        twelve charˇ twelve char
 437        twelve char
 438    "})
 439        .await;
 440    cx.simulate_shared_keystrokes(["$"]).await;
 441    cx.assert_shared_state(indoc! { "
 442        twelve char twelve chaˇr
 443        twelve char
 444    "})
 445        .await;
 446
 447    cx.set_shared_state(indoc! { "
 448        tˇwelve char twelve char
 449        twelve char
 450    "})
 451        .await;
 452    cx.simulate_shared_keystrokes(["enter"]).await;
 453    cx.assert_shared_state(indoc! { "
 454            twelve char twelve char
 455            ˇtwelve char
 456        "})
 457        .await;
 458
 459    cx.set_shared_state(indoc! { "
 460        twelve char
 461        tˇwelve char twelve char
 462        twelve char
 463    "})
 464        .await;
 465    cx.simulate_shared_keystrokes(["o", "o", "escape"]).await;
 466    cx.assert_shared_state(indoc! { "
 467        twelve char
 468        twelve char twelve char
 469        ˇo
 470        twelve char
 471    "})
 472        .await;
 473
 474    cx.set_shared_state(indoc! { "
 475        twelve char
 476        tˇwelve char twelve char
 477        twelve char
 478    "})
 479        .await;
 480    cx.simulate_shared_keystrokes(["shift-a", "a", "escape"])
 481        .await;
 482    cx.assert_shared_state(indoc! { "
 483        twelve char
 484        twelve char twelve charˇa
 485        twelve char
 486    "})
 487        .await;
 488    cx.simulate_shared_keystrokes(["shift-i", "i", "escape"])
 489        .await;
 490    cx.assert_shared_state(indoc! { "
 491        twelve char
 492        ˇitwelve char twelve chara
 493        twelve char
 494    "})
 495        .await;
 496    cx.simulate_shared_keystrokes(["shift-d"]).await;
 497    cx.assert_shared_state(indoc! { "
 498        twelve char
 499        ˇ
 500        twelve char
 501    "})
 502        .await;
 503
 504    cx.set_shared_state(indoc! { "
 505        twelve char
 506        twelve char tˇwelve char
 507        twelve char
 508    "})
 509        .await;
 510    cx.simulate_shared_keystrokes(["shift-o", "o", "escape"])
 511        .await;
 512    cx.assert_shared_state(indoc! { "
 513        twelve char
 514        ˇo
 515        twelve char twelve char
 516        twelve char
 517    "})
 518        .await;
 519
 520    // line wraps as:
 521    // fourteen ch
 522    // ar
 523    // fourteen ch
 524    // ar
 525    cx.set_shared_state(indoc! { "
 526        fourteen chaˇr
 527        fourteen char
 528    "})
 529        .await;
 530
 531    cx.simulate_shared_keystrokes(["d", "i", "w"]).await;
 532    cx.assert_shared_state(indoc! {"
 533        fourteenˇ•
 534        fourteen char
 535    "})
 536        .await;
 537    cx.simulate_shared_keystrokes(["j", "shift-f", "e", "f", "r"])
 538        .await;
 539    cx.assert_shared_state(indoc! {"
 540        fourteen•
 541        fourteen chaˇr
 542    "})
 543        .await;
 544}
 545
 546#[gpui::test]
 547async fn test_folds(cx: &mut gpui::TestAppContext) {
 548    let mut cx = NeovimBackedTestContext::new(cx).await;
 549    cx.set_neovim_option("foldmethod=manual").await;
 550
 551    cx.set_shared_state(indoc! { "
 552        fn boop() {
 553          ˇbarp()
 554          bazp()
 555        }
 556    "})
 557        .await;
 558    cx.simulate_shared_keystrokes(["shift-v", "j", "z", "f"])
 559        .await;
 560
 561    // visual display is now:
 562    // fn boop () {
 563    //  [FOLDED]
 564    // }
 565
 566    // TODO: this should not be needed but currently zf does not
 567    // return to normal mode.
 568    cx.simulate_shared_keystrokes(["escape"]).await;
 569
 570    // skip over fold downward
 571    cx.simulate_shared_keystrokes(["g", "g"]).await;
 572    cx.assert_shared_state(indoc! { "
 573        ˇfn boop() {
 574          barp()
 575          bazp()
 576        }
 577    "})
 578        .await;
 579
 580    cx.simulate_shared_keystrokes(["j", "j"]).await;
 581    cx.assert_shared_state(indoc! { "
 582        fn boop() {
 583          barp()
 584          bazp()
 585        ˇ}
 586    "})
 587        .await;
 588
 589    // skip over fold upward
 590    cx.simulate_shared_keystrokes(["2", "k"]).await;
 591    cx.assert_shared_state(indoc! { "
 592        ˇfn boop() {
 593          barp()
 594          bazp()
 595        }
 596    "})
 597        .await;
 598
 599    // yank the fold
 600    cx.simulate_shared_keystrokes(["down", "y", "y"]).await;
 601    cx.assert_shared_clipboard("  barp()\n  bazp()\n").await;
 602
 603    // re-open
 604    cx.simulate_shared_keystrokes(["z", "o"]).await;
 605    cx.assert_shared_state(indoc! { "
 606        fn boop() {
 607        ˇ  barp()
 608          bazp()
 609        }
 610    "})
 611        .await;
 612}
 613
 614#[gpui::test]
 615async fn test_folds_panic(cx: &mut gpui::TestAppContext) {
 616    let mut cx = NeovimBackedTestContext::new(cx).await;
 617    cx.set_neovim_option("foldmethod=manual").await;
 618
 619    cx.set_shared_state(indoc! { "
 620        fn boop() {
 621          ˇbarp()
 622          bazp()
 623        }
 624    "})
 625        .await;
 626    cx.simulate_shared_keystrokes(["shift-v", "j", "z", "f"])
 627        .await;
 628    cx.simulate_shared_keystrokes(["escape"]).await;
 629    cx.simulate_shared_keystrokes(["g", "g"]).await;
 630    cx.simulate_shared_keystrokes(["5", "d", "j"]).await;
 631    cx.assert_shared_state(indoc! { "ˇ"}).await;
 632}
 633
 634#[gpui::test]
 635async fn test_clear_counts(cx: &mut gpui::TestAppContext) {
 636    let mut cx = NeovimBackedTestContext::new(cx).await;
 637
 638    cx.set_shared_state(indoc! {"
 639        The quick brown
 640        fox juˇmps over
 641        the lazy dog"})
 642        .await;
 643
 644    cx.simulate_shared_keystrokes(["4", "escape", "3", "d", "l"])
 645        .await;
 646    cx.assert_shared_state(indoc! {"
 647        The quick brown
 648        fox juˇ over
 649        the lazy dog"})
 650        .await;
 651}
 652
 653#[gpui::test]
 654async fn test_zero(cx: &mut gpui::TestAppContext) {
 655    let mut cx = NeovimBackedTestContext::new(cx).await;
 656
 657    cx.set_shared_state(indoc! {"
 658        The quˇick brown
 659        fox jumps over
 660        the lazy dog"})
 661        .await;
 662
 663    cx.simulate_shared_keystrokes(["0"]).await;
 664    cx.assert_shared_state(indoc! {"
 665        ˇThe quick brown
 666        fox jumps over
 667        the lazy dog"})
 668        .await;
 669
 670    cx.simulate_shared_keystrokes(["1", "0", "l"]).await;
 671    cx.assert_shared_state(indoc! {"
 672        The quick ˇbrown
 673        fox jumps over
 674        the lazy dog"})
 675        .await;
 676}
 677
 678#[gpui::test]
 679async fn test_selection_goal(cx: &mut gpui::TestAppContext) {
 680    let mut cx = NeovimBackedTestContext::new(cx).await;
 681
 682    cx.set_shared_state(indoc! {"
 683        ;;ˇ;
 684        Lorem Ipsum"})
 685        .await;
 686
 687    cx.simulate_shared_keystrokes(["a", "down", "up", ";", "down", "up"])
 688        .await;
 689    cx.assert_shared_state(indoc! {"
 690        ;;;;ˇ
 691        Lorem Ipsum"})
 692        .await;
 693}
 694
 695#[gpui::test]
 696async fn test_wrapped_motions(cx: &mut gpui::TestAppContext) {
 697    let mut cx = NeovimBackedTestContext::new(cx).await;
 698
 699    cx.set_shared_wrap(12).await;
 700
 701    cx.set_shared_state(indoc! {"
 702                aaˇaa
 703                😃😃"
 704    })
 705    .await;
 706    cx.simulate_shared_keystrokes(["j"]).await;
 707    cx.assert_shared_state(indoc! {"
 708                aaaa
 709                😃ˇ😃"
 710    })
 711    .await;
 712
 713    cx.set_shared_state(indoc! {"
 714                123456789012aaˇaa
 715                123456789012😃😃"
 716    })
 717    .await;
 718    cx.simulate_shared_keystrokes(["j"]).await;
 719    cx.assert_shared_state(indoc! {"
 720        123456789012aaaa
 721        123456789012😃ˇ😃"
 722    })
 723    .await;
 724
 725    cx.set_shared_state(indoc! {"
 726                123456789012aaˇaa
 727                123456789012😃😃"
 728    })
 729    .await;
 730    cx.simulate_shared_keystrokes(["j"]).await;
 731    cx.assert_shared_state(indoc! {"
 732        123456789012aaaa
 733        123456789012😃ˇ😃"
 734    })
 735    .await;
 736
 737    cx.set_shared_state(indoc! {"
 738        123456789012aaaaˇaaaaaaaa123456789012
 739        wow
 740        123456789012😃😃😃😃😃😃123456789012"
 741    })
 742    .await;
 743    cx.simulate_shared_keystrokes(["j", "j"]).await;
 744    cx.assert_shared_state(indoc! {"
 745        123456789012aaaaaaaaaaaa123456789012
 746        wow
 747        123456789012😃😃ˇ😃😃😃😃123456789012"
 748    })
 749    .await;
 750}
 751
 752#[gpui::test]
 753async fn test_paragraphs_dont_wrap(cx: &mut gpui::TestAppContext) {
 754    let mut cx = NeovimBackedTestContext::new(cx).await;
 755
 756    cx.set_shared_state(indoc! {"
 757        one
 758        ˇ
 759        two"})
 760        .await;
 761
 762    cx.simulate_shared_keystrokes(["}", "}"]).await;
 763    cx.assert_shared_state(indoc! {"
 764        one
 765
 766        twˇo"})
 767        .await;
 768
 769    cx.simulate_shared_keystrokes(["{", "{", "{"]).await;
 770    cx.assert_shared_state(indoc! {"
 771        ˇone
 772
 773        two"})
 774        .await;
 775}
 776
 777#[gpui::test]
 778async fn test_select_all_issue_2170(cx: &mut gpui::TestAppContext) {
 779    let mut cx = VimTestContext::new(cx, true).await;
 780
 781    cx.set_state(
 782        indoc! {"
 783        defmodule Test do
 784            def test(a, ˇ[_, _] = b), do: IO.puts('hi')
 785        end
 786    "},
 787        Mode::Normal,
 788    );
 789    cx.simulate_keystrokes(["g", "a"]);
 790    cx.assert_state(
 791        indoc! {"
 792        defmodule Test do
 793            def test(a, «[ˇ»_, _] = b), do: IO.puts('hi')
 794        end
 795    "},
 796        Mode::Visual,
 797    );
 798}
 799
 800#[gpui::test]
 801async fn test_jk(cx: &mut gpui::TestAppContext) {
 802    let mut cx = NeovimBackedTestContext::new(cx).await;
 803
 804    cx.update(|cx| {
 805        cx.bind_keys([KeyBinding::new(
 806            "j k",
 807            NormalBefore,
 808            Some("vim_mode == insert"),
 809        )])
 810    });
 811    cx.neovim.exec("imap jk <esc>").await;
 812
 813    cx.set_shared_state("ˇhello").await;
 814    cx.simulate_shared_keystrokes(["i", "j", "o", "j", "k"])
 815        .await;
 816    cx.assert_shared_state("jˇohello").await;
 817}
 818
 819#[gpui::test]
 820async fn test_jk_delay(cx: &mut gpui::TestAppContext) {
 821    let mut cx = VimTestContext::new(cx, true).await;
 822
 823    cx.update(|cx| {
 824        cx.bind_keys([KeyBinding::new(
 825            "j k",
 826            NormalBefore,
 827            Some("vim_mode == insert"),
 828        )])
 829    });
 830
 831    cx.set_state("ˇhello", Mode::Normal);
 832    cx.simulate_keystrokes(["i", "j"]);
 833    cx.executor().advance_clock(Duration::from_millis(500));
 834    cx.run_until_parked();
 835    cx.assert_state("ˇhello", Mode::Insert);
 836    cx.executor().advance_clock(Duration::from_millis(500));
 837    cx.run_until_parked();
 838    cx.assert_state("jˇhello", Mode::Insert);
 839    cx.simulate_keystrokes(["k", "j", "k"]);
 840    cx.assert_state("jˇkhello", Mode::Normal);
 841}
 842
 843#[gpui::test]
 844async fn test_comma_w(cx: &mut gpui::TestAppContext) {
 845    let mut cx = NeovimBackedTestContext::new(cx).await;
 846
 847    cx.update(|cx| {
 848        cx.bind_keys([KeyBinding::new(
 849            ", w",
 850            motion::Down {
 851                display_lines: false,
 852            },
 853            Some("vim_mode == normal"),
 854        )])
 855    });
 856    cx.neovim.exec("map ,w j").await;
 857
 858    cx.set_shared_state("ˇhello hello\nhello hello").await;
 859    cx.simulate_shared_keystrokes(["f", "o", ";", ",", "w"])
 860        .await;
 861    cx.assert_shared_state("hello hello\nhello hellˇo").await;
 862
 863    cx.set_shared_state("ˇhello hello\nhello hello").await;
 864    cx.simulate_shared_keystrokes(["f", "o", ";", ",", "i"])
 865        .await;
 866    cx.assert_shared_state("hellˇo hello\nhello hello").await;
 867    cx.assert_shared_mode(Mode::Insert).await;
 868}
 869
 870#[gpui::test]
 871async fn test_rename(cx: &mut gpui::TestAppContext) {
 872    let mut cx = VimTestContext::new_typescript(cx).await;
 873
 874    cx.set_state("const beˇfore = 2; console.log(before)", Mode::Normal);
 875    let def_range = cx.lsp_range("const «beforeˇ» = 2; console.log(before)");
 876    let tgt_range = cx.lsp_range("const before = 2; console.log(«beforeˇ»)");
 877    let mut prepare_request =
 878        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
 879            Ok(Some(lsp::PrepareRenameResponse::Range(def_range)))
 880        });
 881    let mut rename_request =
 882        cx.handle_request::<lsp::request::Rename, _, _>(move |url, params, _| async move {
 883            Ok(Some(lsp::WorkspaceEdit {
 884                changes: Some(
 885                    [(
 886                        url.clone(),
 887                        vec![
 888                            lsp::TextEdit::new(def_range, params.new_name.clone()),
 889                            lsp::TextEdit::new(tgt_range, params.new_name),
 890                        ],
 891                    )]
 892                    .into(),
 893                ),
 894                ..Default::default()
 895            }))
 896        });
 897
 898    cx.simulate_keystrokes(["c", "d"]);
 899    prepare_request.next().await.unwrap();
 900    cx.simulate_input("after");
 901    cx.simulate_keystrokes(["enter"]);
 902    rename_request.next().await.unwrap();
 903    cx.assert_state("const afterˇ = 2; console.log(after)", Mode::Normal)
 904}
 905
 906#[gpui::test]
 907async fn test_remap(cx: &mut gpui::TestAppContext) {
 908    let mut cx = VimTestContext::new(cx, true).await;
 909
 910    // test moving the cursor
 911    cx.update(|cx| {
 912        cx.bind_keys([KeyBinding::new(
 913            "g z",
 914            workspace::SendKeystrokes("l l l l".to_string()),
 915            None,
 916        )])
 917    });
 918    cx.set_state("ˇ123456789", Mode::Normal);
 919    cx.simulate_keystrokes(["g", "z"]);
 920    cx.assert_state("1234ˇ56789", Mode::Normal);
 921
 922    // test switching modes
 923    cx.update(|cx| {
 924        cx.bind_keys([KeyBinding::new(
 925            "g y",
 926            workspace::SendKeystrokes("i f o o escape l".to_string()),
 927            None,
 928        )])
 929    });
 930    cx.set_state("ˇ123456789", Mode::Normal);
 931    cx.simulate_keystrokes(["g", "y"]);
 932    cx.assert_state("fooˇ123456789", Mode::Normal);
 933
 934    // test recursion
 935    cx.update(|cx| {
 936        cx.bind_keys([KeyBinding::new(
 937            "g x",
 938            workspace::SendKeystrokes("g z g y".to_string()),
 939            None,
 940        )])
 941    });
 942    cx.set_state("ˇ123456789", Mode::Normal);
 943    cx.simulate_keystrokes(["g", "x"]);
 944    cx.assert_state("1234fooˇ56789", Mode::Normal);
 945
 946    cx.executor().allow_parking();
 947
 948    // test command
 949    cx.update(|cx| {
 950        cx.bind_keys([KeyBinding::new(
 951            "g w",
 952            workspace::SendKeystrokes(": j enter".to_string()),
 953            None,
 954        )])
 955    });
 956    cx.set_state("ˇ1234\n56789", Mode::Normal);
 957    cx.simulate_keystrokes(["g", "w"]);
 958    cx.assert_state("1234ˇ 56789", Mode::Normal);
 959
 960    // test leaving command
 961    cx.update(|cx| {
 962        cx.bind_keys([KeyBinding::new(
 963            "g u",
 964            workspace::SendKeystrokes("g w g z".to_string()),
 965            None,
 966        )])
 967    });
 968    cx.set_state("ˇ1234\n56789", Mode::Normal);
 969    cx.simulate_keystrokes(["g", "u"]);
 970    cx.assert_state("1234 567ˇ89", Mode::Normal);
 971
 972    // test leaving command
 973    cx.update(|cx| {
 974        cx.bind_keys([KeyBinding::new(
 975            "g t",
 976            workspace::SendKeystrokes("i space escape".to_string()),
 977            None,
 978        )])
 979    });
 980    cx.set_state("12ˇ34", Mode::Normal);
 981    cx.simulate_keystrokes(["g", "t"]);
 982    cx.assert_state("12ˇ 34", Mode::Normal);
 983}
 984
 985#[gpui::test]
 986async fn test_undo(cx: &mut gpui::TestAppContext) {
 987    let mut cx = NeovimBackedTestContext::new(cx).await;
 988
 989    cx.set_shared_state("hello quˇoel world").await;
 990    cx.simulate_shared_keystrokes(["v", "i", "w", "s", "c", "o", "escape", "u"])
 991        .await;
 992    cx.assert_shared_state("hello ˇquoel world").await;
 993    cx.simulate_shared_keystrokes(["ctrl-r"]).await;
 994    cx.assert_shared_state("hello ˇco world").await;
 995    cx.simulate_shared_keystrokes(["a", "o", "right", "l", "escape"])
 996        .await;
 997    cx.assert_shared_state("hello cooˇl world").await;
 998    cx.simulate_shared_keystrokes(["u"]).await;
 999    cx.assert_shared_state("hello cooˇ world").await;
1000    cx.simulate_shared_keystrokes(["u"]).await;
1001    cx.assert_shared_state("hello cˇo world").await;
1002    cx.simulate_shared_keystrokes(["u"]).await;
1003    cx.assert_shared_state("hello ˇquoel world").await;
1004
1005    cx.set_shared_state("hello quˇoel world").await;
1006    cx.simulate_shared_keystrokes(["v", "i", "w", "~", "u"])
1007        .await;
1008    cx.assert_shared_state("hello ˇquoel world").await;
1009
1010    cx.set_shared_state("\nhello quˇoel world\n").await;
1011    cx.simulate_shared_keystrokes(["shift-v", "s", "c", "escape", "u"])
1012        .await;
1013    cx.assert_shared_state("\nˇhello quoel world\n").await;
1014
1015    cx.set_shared_state(indoc! {"
1016        ˇ1
1017        2
1018        3"})
1019        .await;
1020
1021    cx.simulate_shared_keystrokes(["ctrl-v", "shift-g", "ctrl-a"])
1022        .await;
1023    cx.assert_shared_state(indoc! {"
1024        ˇ2
1025        3
1026        4"})
1027        .await;
1028
1029    cx.simulate_shared_keystrokes(["u"]).await;
1030    cx.assert_shared_state(indoc! {"
1031        ˇ1
1032        2
1033        3"})
1034        .await;
1035}