test.rs

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