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