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::{
  10    DisplayPoint, Editor, EditorMode, MultiBuffer, actions::DeleteLine, display_map::DisplayRow,
  11    test::editor_test_context::EditorTestContext,
  12};
  13use futures::StreamExt;
  14use gpui::{KeyBinding, Modifiers, MouseButton, TestAppContext};
  15use language::Point;
  16pub use neovim_backed_test_context::*;
  17use settings::SettingsStore;
  18pub use vim_test_context::*;
  19
  20use indoc::indoc;
  21use search::BufferSearchBar;
  22use workspace::WorkspaceSettings;
  23
  24use crate::{PushSneak, PushSneakBackward, insert::NormalBefore, motion, state::Mode};
  25
  26#[gpui::test]
  27async fn test_initially_disabled(cx: &mut gpui::TestAppContext) {
  28    let mut cx = VimTestContext::new(cx, false).await;
  29    cx.simulate_keystrokes("h j k l");
  30    cx.assert_editor_state("hjklˇ");
  31}
  32
  33#[gpui::test]
  34async fn test_neovim(cx: &mut gpui::TestAppContext) {
  35    let mut cx = NeovimBackedTestContext::new(cx).await;
  36
  37    cx.simulate_shared_keystrokes("i").await;
  38    cx.shared_state().await.assert_matches();
  39    cx.simulate_shared_keystrokes("shift-t e s t space t e s t escape 0 d w")
  40        .await;
  41    cx.shared_state().await.assert_matches();
  42    cx.assert_editor_state("ˇtest");
  43}
  44
  45#[gpui::test]
  46async fn test_toggle_through_settings(cx: &mut gpui::TestAppContext) {
  47    let mut cx = VimTestContext::new(cx, true).await;
  48
  49    cx.simulate_keystrokes("i");
  50    assert_eq!(cx.mode(), Mode::Insert);
  51
  52    // Editor acts as though vim is disabled
  53    cx.disable_vim();
  54    cx.simulate_keystrokes("h j k l");
  55    cx.assert_editor_state("hjklˇ");
  56
  57    // Selections aren't changed if editor is blurred but vim-mode is still disabled.
  58    cx.cx.set_state("«hjklˇ»");
  59    cx.assert_editor_state("«hjklˇ»");
  60    cx.update_editor(|_, window, _cx| window.blur());
  61    cx.assert_editor_state("«hjklˇ»");
  62    cx.update_editor(|_, window, cx| cx.focus_self(window));
  63    cx.assert_editor_state("«hjklˇ»");
  64
  65    // Enabling dynamically sets vim mode again and restores normal mode
  66    cx.enable_vim();
  67    assert_eq!(cx.mode(), Mode::Normal);
  68    cx.simulate_keystrokes("h h h l");
  69    assert_eq!(cx.buffer_text(), "hjkl".to_owned());
  70    cx.assert_editor_state("hˇjkl");
  71    cx.simulate_keystrokes("i T e s t");
  72    cx.assert_editor_state("hTestˇjkl");
  73
  74    // Disabling and enabling resets to normal mode
  75    assert_eq!(cx.mode(), Mode::Insert);
  76    cx.disable_vim();
  77    cx.enable_vim();
  78    assert_eq!(cx.mode(), Mode::Normal);
  79}
  80
  81#[gpui::test]
  82async fn test_cancel_selection(cx: &mut gpui::TestAppContext) {
  83    let mut cx = VimTestContext::new(cx, true).await;
  84
  85    cx.set_state(
  86        indoc! {"The quick brown fox juˇmps over the lazy dog"},
  87        Mode::Normal,
  88    );
  89    // jumps
  90    cx.simulate_keystrokes("v l l");
  91    cx.assert_editor_state("The quick brown fox ju«mpsˇ» over the lazy dog");
  92
  93    cx.simulate_keystrokes("escape");
  94    cx.assert_editor_state("The quick brown fox jumpˇs over the lazy dog");
  95
  96    // go back to the same selection state
  97    cx.simulate_keystrokes("v h h");
  98    cx.assert_editor_state("The quick brown fox ju«ˇmps» over the lazy dog");
  99
 100    // Ctrl-[ should behave like Esc
 101    cx.simulate_keystrokes("ctrl-[");
 102    cx.assert_editor_state("The quick brown fox juˇmps over the lazy dog");
 103}
 104
 105#[gpui::test]
 106async fn test_buffer_search(cx: &mut gpui::TestAppContext) {
 107    let mut cx = VimTestContext::new(cx, true).await;
 108
 109    cx.set_state(
 110        indoc! {"
 111            The quick brown
 112            fox juˇmps over
 113            the lazy dog"},
 114        Mode::Normal,
 115    );
 116    cx.simulate_keystrokes("/");
 117
 118    let search_bar = cx.workspace(|workspace, _, cx| {
 119        workspace
 120            .active_pane()
 121            .read(cx)
 122            .toolbar()
 123            .read(cx)
 124            .item_of_type::<BufferSearchBar>()
 125            .expect("Buffer search bar should be deployed")
 126    });
 127
 128    cx.update_entity(search_bar, |bar, _, cx| {
 129        assert_eq!(bar.query(cx), "");
 130    })
 131}
 132
 133#[gpui::test]
 134async fn test_count_down(cx: &mut gpui::TestAppContext) {
 135    let mut cx = VimTestContext::new(cx, true).await;
 136
 137    cx.set_state(indoc! {"aˇa\nbb\ncc\ndd\nee"}, Mode::Normal);
 138    cx.simulate_keystrokes("2 down");
 139    cx.assert_editor_state("aa\nbb\ncˇc\ndd\nee");
 140    cx.simulate_keystrokes("9 down");
 141    cx.assert_editor_state("aa\nbb\ncc\ndd\neˇe");
 142}
 143
 144#[gpui::test]
 145async fn test_end_of_document_710(cx: &mut gpui::TestAppContext) {
 146    let mut cx = VimTestContext::new(cx, true).await;
 147
 148    // goes to end by default
 149    cx.set_state(indoc! {"aˇa\nbb\ncc"}, Mode::Normal);
 150    cx.simulate_keystrokes("shift-g");
 151    cx.assert_editor_state("aa\nbb\ncˇc");
 152
 153    // can go to line 1 (https://github.com/zed-industries/zed/issues/5812)
 154    cx.simulate_keystrokes("1 shift-g");
 155    cx.assert_editor_state("aˇa\nbb\ncc");
 156}
 157
 158#[gpui::test]
 159async fn test_end_of_line_with_times(cx: &mut gpui::TestAppContext) {
 160    let mut cx = VimTestContext::new(cx, true).await;
 161
 162    // goes to current line end
 163    cx.set_state(indoc! {"ˇaa\nbb\ncc"}, Mode::Normal);
 164    cx.simulate_keystrokes("$");
 165    cx.assert_editor_state("aˇa\nbb\ncc");
 166
 167    // goes to next line end
 168    cx.simulate_keystrokes("2 $");
 169    cx.assert_editor_state("aa\nbˇb\ncc");
 170
 171    // try to exceed the final line.
 172    cx.simulate_keystrokes("4 $");
 173    cx.assert_editor_state("aa\nbb\ncˇc");
 174}
 175
 176#[gpui::test]
 177async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 178    let mut cx = VimTestContext::new(cx, true).await;
 179
 180    // works in normal mode
 181    cx.set_state(indoc! {"aa\nbˇb\ncc"}, Mode::Normal);
 182    cx.simulate_keystrokes("> >");
 183    cx.assert_editor_state("aa\n    bˇb\ncc");
 184    cx.simulate_keystrokes("< <");
 185    cx.assert_editor_state("aa\nbˇb\ncc");
 186
 187    // works in visual mode
 188    cx.simulate_keystrokes("shift-v down >");
 189    cx.assert_editor_state("aa\n    bˇb\n    cc");
 190
 191    // works as operator
 192    cx.set_state("aa\nbˇb\ncc\n", Mode::Normal);
 193    cx.simulate_keystrokes("> j");
 194    cx.assert_editor_state("aa\n    bˇb\n    cc\n");
 195    cx.simulate_keystrokes("< k");
 196    cx.assert_editor_state("aa\nbˇb\n    cc\n");
 197    cx.simulate_keystrokes("> i p");
 198    cx.assert_editor_state("    aa\n    bˇb\n        cc\n");
 199    cx.simulate_keystrokes("< i p");
 200    cx.assert_editor_state("aa\nbˇb\n    cc\n");
 201    cx.simulate_keystrokes("< i p");
 202    cx.assert_editor_state("aa\nbˇb\ncc\n");
 203
 204    cx.set_state("ˇaa\nbb\ncc\n", Mode::Normal);
 205    cx.simulate_keystrokes("> 2 j");
 206    cx.assert_editor_state("    ˇaa\n    bb\n    cc\n");
 207
 208    cx.set_state("aa\nbb\nˇcc\n", Mode::Normal);
 209    cx.simulate_keystrokes("> 2 k");
 210    cx.assert_editor_state("    aa\n    bb\n    ˇcc\n");
 211
 212    // works with repeat
 213    cx.set_state("a\nb\nccˇc\n", Mode::Normal);
 214    cx.simulate_keystrokes("> 2 k");
 215    cx.assert_editor_state("    a\n    b\n    ccˇc\n");
 216    cx.simulate_keystrokes(".");
 217    cx.assert_editor_state("        a\n        b\n        ccˇc\n");
 218    cx.simulate_keystrokes("v k <");
 219    cx.assert_editor_state("        a\n\n    ccc\n");
 220    cx.simulate_keystrokes(".");
 221    cx.assert_editor_state("        a\n\nccc\n");
 222}
 223
 224#[gpui::test]
 225async fn test_escape_command_palette(cx: &mut gpui::TestAppContext) {
 226    let mut cx = VimTestContext::new(cx, true).await;
 227
 228    cx.set_state("aˇbc\n", Mode::Normal);
 229    cx.simulate_keystrokes("i cmd-shift-p");
 230
 231    assert!(
 232        cx.workspace(|workspace, _, cx| workspace.active_modal::<CommandPalette>(cx).is_some())
 233    );
 234    cx.simulate_keystrokes("escape");
 235    cx.run_until_parked();
 236    assert!(
 237        !cx.workspace(|workspace, _, cx| workspace.active_modal::<CommandPalette>(cx).is_some())
 238    );
 239    cx.assert_state("aˇbc\n", Mode::Insert);
 240}
 241
 242#[gpui::test]
 243async fn test_escape_cancels(cx: &mut gpui::TestAppContext) {
 244    let mut cx = VimTestContext::new(cx, true).await;
 245
 246    cx.set_state("aˇbˇc", Mode::Normal);
 247    cx.simulate_keystrokes("escape");
 248
 249    cx.assert_state("aˇbc", Mode::Normal);
 250}
 251
 252#[gpui::test]
 253async fn test_selection_on_search(cx: &mut gpui::TestAppContext) {
 254    let mut cx = VimTestContext::new(cx, true).await;
 255
 256    cx.set_state(indoc! {"aa\nbˇb\ncc\ncc\ncc\n"}, Mode::Normal);
 257    cx.simulate_keystrokes("/ c c");
 258
 259    let search_bar = cx.workspace(|workspace, _, cx| {
 260        workspace
 261            .active_pane()
 262            .read(cx)
 263            .toolbar()
 264            .read(cx)
 265            .item_of_type::<BufferSearchBar>()
 266            .expect("Buffer search bar should be deployed")
 267    });
 268
 269    cx.update_entity(search_bar, |bar, _, cx| {
 270        assert_eq!(bar.query(cx), "cc");
 271    });
 272
 273    cx.update_editor(|editor, window, cx| {
 274        let highlights = editor.all_text_background_highlights(window, cx);
 275        assert_eq!(3, highlights.len());
 276        assert_eq!(
 277            DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 2),
 278            highlights[0].0
 279        )
 280    });
 281    cx.simulate_keystrokes("enter");
 282
 283    cx.assert_state(indoc! {"aa\nbb\nˇcc\ncc\ncc\n"}, Mode::Normal);
 284    cx.simulate_keystrokes("n");
 285    cx.assert_state(indoc! {"aa\nbb\ncc\nˇcc\ncc\n"}, Mode::Normal);
 286    cx.simulate_keystrokes("shift-n");
 287    cx.assert_state(indoc! {"aa\nbb\nˇcc\ncc\ncc\n"}, Mode::Normal);
 288}
 289
 290#[gpui::test]
 291async fn test_word_characters(cx: &mut gpui::TestAppContext) {
 292    let mut cx = VimTestContext::new_typescript(cx).await;
 293    cx.set_state(
 294        indoc! { "
 295        class A {
 296            #ˇgoop = 99;
 297            $ˇgoop () { return this.#gˇoop };
 298        };
 299        console.log(new A().$gooˇp())
 300    "},
 301        Mode::Normal,
 302    );
 303    cx.simulate_keystrokes("v i w");
 304    cx.assert_state(
 305        indoc! {"
 306        class A {
 307            «#goopˇ» = 99;
 308            «$goopˇ» () { return this.«#goopˇ» };
 309        };
 310        console.log(new A().«$goopˇ»())
 311    "},
 312        Mode::Visual,
 313    )
 314}
 315
 316#[gpui::test]
 317async fn test_kebab_case(cx: &mut gpui::TestAppContext) {
 318    let mut cx = VimTestContext::new_html(cx).await;
 319    cx.set_state(
 320        indoc! { r#"
 321            <div><a class="bg-rˇed"></a></div>
 322            "#},
 323        Mode::Normal,
 324    );
 325    cx.simulate_keystrokes("v i w");
 326    cx.assert_state(
 327        indoc! { r#"
 328        <div><a class="bg-«redˇ»"></a></div>
 329        "#
 330        },
 331        Mode::Visual,
 332    )
 333}
 334
 335#[gpui::test]
 336async fn test_join_lines(cx: &mut gpui::TestAppContext) {
 337    let mut cx = NeovimBackedTestContext::new(cx).await;
 338
 339    cx.set_shared_state(indoc! {"
 340      ˇone
 341      two
 342      three
 343      four
 344      five
 345      six
 346      "})
 347        .await;
 348    cx.simulate_shared_keystrokes("shift-j").await;
 349    cx.shared_state().await.assert_eq(indoc! {"
 350          oneˇ two
 351          three
 352          four
 353          five
 354          six
 355          "});
 356    cx.simulate_shared_keystrokes("3 shift-j").await;
 357    cx.shared_state().await.assert_eq(indoc! {"
 358          one two threeˇ four
 359          five
 360          six
 361          "});
 362
 363    cx.set_shared_state(indoc! {"
 364      ˇone
 365      two
 366      three
 367      four
 368      five
 369      six
 370      "})
 371        .await;
 372    cx.simulate_shared_keystrokes("j v 3 j shift-j").await;
 373    cx.shared_state().await.assert_eq(indoc! {"
 374      one
 375      two three fourˇ five
 376      six
 377      "});
 378
 379    cx.set_shared_state(indoc! {"
 380      ˇone
 381      two
 382      three
 383      four
 384      five
 385      six
 386      "})
 387        .await;
 388    cx.simulate_shared_keystrokes("g shift-j").await;
 389    cx.shared_state().await.assert_eq(indoc! {"
 390          oneˇtwo
 391          three
 392          four
 393          five
 394          six
 395          "});
 396    cx.simulate_shared_keystrokes("3 g shift-j").await;
 397    cx.shared_state().await.assert_eq(indoc! {"
 398          onetwothreeˇfour
 399          five
 400          six
 401          "});
 402
 403    cx.set_shared_state(indoc! {"
 404      ˇone
 405      two
 406      three
 407      four
 408      five
 409      six
 410      "})
 411        .await;
 412    cx.simulate_shared_keystrokes("j v 3 j g shift-j").await;
 413    cx.shared_state().await.assert_eq(indoc! {"
 414      one
 415      twothreefourˇfive
 416      six
 417      "});
 418}
 419
 420#[cfg(target_os = "macos")]
 421#[gpui::test]
 422async fn test_wrapped_lines(cx: &mut gpui::TestAppContext) {
 423    let mut cx = NeovimBackedTestContext::new(cx).await;
 424
 425    cx.set_shared_wrap(12).await;
 426    // tests line wrap as follows:
 427    //  1: twelve char
 428    //     twelve char
 429    //  2: twelve char
 430    cx.set_shared_state(indoc! { "
 431        tˇwelve char twelve char
 432        twelve char
 433    "})
 434        .await;
 435    cx.simulate_shared_keystrokes("j").await;
 436    cx.shared_state().await.assert_eq(indoc! {"
 437        twelve char twelve char
 438        tˇwelve char
 439    "});
 440    cx.simulate_shared_keystrokes("k").await;
 441    cx.shared_state().await.assert_eq(indoc! {"
 442        tˇwelve char twelve char
 443        twelve char
 444    "});
 445    cx.simulate_shared_keystrokes("g j").await;
 446    cx.shared_state().await.assert_eq(indoc! {"
 447        twelve char tˇwelve char
 448        twelve char
 449    "});
 450    cx.simulate_shared_keystrokes("g j").await;
 451    cx.shared_state().await.assert_eq(indoc! {"
 452        twelve char twelve char
 453        tˇwelve char
 454    "});
 455
 456    cx.simulate_shared_keystrokes("g k").await;
 457    cx.shared_state().await.assert_eq(indoc! {"
 458        twelve char tˇwelve char
 459        twelve char
 460    "});
 461
 462    cx.simulate_shared_keystrokes("g ^").await;
 463    cx.shared_state().await.assert_eq(indoc! {"
 464        twelve char ˇtwelve char
 465        twelve char
 466    "});
 467
 468    cx.simulate_shared_keystrokes("^").await;
 469    cx.shared_state().await.assert_eq(indoc! {"
 470        ˇtwelve char twelve char
 471        twelve char
 472    "});
 473
 474    cx.simulate_shared_keystrokes("g $").await;
 475    cx.shared_state().await.assert_eq(indoc! {"
 476        twelve charˇ twelve char
 477        twelve char
 478    "});
 479    cx.simulate_shared_keystrokes("$").await;
 480    cx.shared_state().await.assert_eq(indoc! {"
 481        twelve char twelve chaˇr
 482        twelve char
 483    "});
 484
 485    cx.set_shared_state(indoc! { "
 486        tˇwelve char twelve char
 487        twelve char
 488    "})
 489        .await;
 490    cx.simulate_shared_keystrokes("enter").await;
 491    cx.shared_state().await.assert_eq(indoc! {"
 492            twelve char twelve char
 493            ˇtwelve char
 494        "});
 495
 496    cx.set_shared_state(indoc! { "
 497        twelve char
 498        tˇwelve char twelve char
 499        twelve char
 500    "})
 501        .await;
 502    cx.simulate_shared_keystrokes("o o escape").await;
 503    cx.shared_state().await.assert_eq(indoc! {"
 504        twelve char
 505        twelve char twelve char
 506        ˇo
 507        twelve char
 508    "});
 509
 510    cx.set_shared_state(indoc! { "
 511        twelve char
 512        tˇwelve char twelve char
 513        twelve char
 514    "})
 515        .await;
 516    cx.simulate_shared_keystrokes("shift-a a escape").await;
 517    cx.shared_state().await.assert_eq(indoc! {"
 518        twelve char
 519        twelve char twelve charˇa
 520        twelve char
 521    "});
 522    cx.simulate_shared_keystrokes("shift-i i escape").await;
 523    cx.shared_state().await.assert_eq(indoc! {"
 524        twelve char
 525        ˇitwelve char twelve chara
 526        twelve char
 527    "});
 528    cx.simulate_shared_keystrokes("shift-d").await;
 529    cx.shared_state().await.assert_eq(indoc! {"
 530        twelve char
 531        ˇ
 532        twelve char
 533    "});
 534
 535    cx.set_shared_state(indoc! { "
 536        twelve char
 537        twelve char tˇwelve char
 538        twelve char
 539    "})
 540        .await;
 541    cx.simulate_shared_keystrokes("shift-o o escape").await;
 542    cx.shared_state().await.assert_eq(indoc! {"
 543        twelve char
 544        ˇo
 545        twelve char twelve char
 546        twelve char
 547    "});
 548
 549    // line wraps as:
 550    // fourteen ch
 551    // ar
 552    // fourteen ch
 553    // ar
 554    cx.set_shared_state(indoc! { "
 555        fourteen chaˇr
 556        fourteen char
 557    "})
 558        .await;
 559
 560    cx.simulate_shared_keystrokes("d i w").await;
 561    cx.shared_state().await.assert_eq(indoc! {"
 562        fourteenˇ•
 563        fourteen char
 564    "});
 565    cx.simulate_shared_keystrokes("j shift-f e f r").await;
 566    cx.shared_state().await.assert_eq(indoc! {"
 567        fourteen•
 568        fourteen chaˇr
 569    "});
 570}
 571
 572#[gpui::test]
 573async fn test_folds(cx: &mut gpui::TestAppContext) {
 574    let mut cx = NeovimBackedTestContext::new(cx).await;
 575    cx.set_neovim_option("foldmethod=manual").await;
 576
 577    cx.set_shared_state(indoc! { "
 578        fn boop() {
 579          ˇbarp()
 580          bazp()
 581        }
 582    "})
 583        .await;
 584    cx.simulate_shared_keystrokes("shift-v j z f").await;
 585
 586    // visual display is now:
 587    // fn boop () {
 588    //  [FOLDED]
 589    // }
 590
 591    // TODO: this should not be needed but currently zf does not
 592    // return to normal mode.
 593    cx.simulate_shared_keystrokes("escape").await;
 594
 595    // skip over fold downward
 596    cx.simulate_shared_keystrokes("g g").await;
 597    cx.shared_state().await.assert_eq(indoc! {"
 598        ˇfn boop() {
 599          barp()
 600          bazp()
 601        }
 602    "});
 603
 604    cx.simulate_shared_keystrokes("j j").await;
 605    cx.shared_state().await.assert_eq(indoc! {"
 606        fn boop() {
 607          barp()
 608          bazp()
 609        ˇ}
 610    "});
 611
 612    // skip over fold upward
 613    cx.simulate_shared_keystrokes("2 k").await;
 614    cx.shared_state().await.assert_eq(indoc! {"
 615        ˇfn boop() {
 616          barp()
 617          bazp()
 618        }
 619    "});
 620
 621    // yank the fold
 622    cx.simulate_shared_keystrokes("down y y").await;
 623    cx.shared_clipboard()
 624        .await
 625        .assert_eq("  barp()\n  bazp()\n");
 626
 627    // re-open
 628    cx.simulate_shared_keystrokes("z o").await;
 629    cx.shared_state().await.assert_eq(indoc! {"
 630        fn boop() {
 631        ˇ  barp()
 632          bazp()
 633        }
 634    "});
 635}
 636
 637#[gpui::test]
 638async fn test_folds_panic(cx: &mut gpui::TestAppContext) {
 639    let mut cx = NeovimBackedTestContext::new(cx).await;
 640    cx.set_neovim_option("foldmethod=manual").await;
 641
 642    cx.set_shared_state(indoc! { "
 643        fn boop() {
 644          ˇbarp()
 645          bazp()
 646        }
 647    "})
 648        .await;
 649    cx.simulate_shared_keystrokes("shift-v j z f").await;
 650    cx.simulate_shared_keystrokes("escape").await;
 651    cx.simulate_shared_keystrokes("g g").await;
 652    cx.simulate_shared_keystrokes("5 d j").await;
 653    cx.shared_state().await.assert_eq("ˇ");
 654    cx.set_shared_state(indoc! {"
 655        fn boop() {
 656          ˇbarp()
 657          bazp()
 658        }
 659    "})
 660        .await;
 661    cx.simulate_shared_keystrokes("shift-v j j z f").await;
 662    cx.simulate_shared_keystrokes("escape").await;
 663    cx.simulate_shared_keystrokes("shift-g shift-v").await;
 664    cx.shared_state().await.assert_eq(indoc! {"
 665        fn boop() {
 666          barp()
 667          bazp()
 668        }
 669        ˇ"});
 670}
 671
 672#[gpui::test]
 673async fn test_clear_counts(cx: &mut gpui::TestAppContext) {
 674    let mut cx = NeovimBackedTestContext::new(cx).await;
 675
 676    cx.set_shared_state(indoc! {"
 677        The quick brown
 678        fox juˇmps over
 679        the lazy dog"})
 680        .await;
 681
 682    cx.simulate_shared_keystrokes("4 escape 3 d l").await;
 683    cx.shared_state().await.assert_eq(indoc! {"
 684        The quick brown
 685        fox juˇ over
 686        the lazy dog"});
 687}
 688
 689#[gpui::test]
 690async fn test_zero(cx: &mut gpui::TestAppContext) {
 691    let mut cx = NeovimBackedTestContext::new(cx).await;
 692
 693    cx.set_shared_state(indoc! {"
 694        The quˇick brown
 695        fox jumps over
 696        the lazy dog"})
 697        .await;
 698
 699    cx.simulate_shared_keystrokes("0").await;
 700    cx.shared_state().await.assert_eq(indoc! {"
 701        ˇThe quick brown
 702        fox jumps over
 703        the lazy dog"});
 704
 705    cx.simulate_shared_keystrokes("1 0 l").await;
 706    cx.shared_state().await.assert_eq(indoc! {"
 707        The quick ˇbrown
 708        fox jumps over
 709        the lazy dog"});
 710}
 711
 712#[gpui::test]
 713async fn test_selection_goal(cx: &mut gpui::TestAppContext) {
 714    let mut cx = NeovimBackedTestContext::new(cx).await;
 715
 716    cx.set_shared_state(indoc! {"
 717        ;;ˇ;
 718        Lorem Ipsum"})
 719        .await;
 720
 721    cx.simulate_shared_keystrokes("a down up ; down up").await;
 722    cx.shared_state().await.assert_eq(indoc! {"
 723        ;;;;ˇ
 724        Lorem Ipsum"});
 725}
 726
 727#[cfg(target_os = "macos")]
 728#[gpui::test]
 729async fn test_wrapped_motions(cx: &mut gpui::TestAppContext) {
 730    let mut cx = NeovimBackedTestContext::new(cx).await;
 731
 732    cx.set_shared_wrap(12).await;
 733
 734    cx.set_shared_state(indoc! {"
 735                aaˇaa
 736                😃😃"
 737    })
 738    .await;
 739    cx.simulate_shared_keystrokes("j").await;
 740    cx.shared_state().await.assert_eq(indoc! {"
 741                aaaa
 742                😃ˇ😃"
 743    });
 744
 745    cx.set_shared_state(indoc! {"
 746                123456789012aaˇaa
 747                123456789012😃😃"
 748    })
 749    .await;
 750    cx.simulate_shared_keystrokes("j").await;
 751    cx.shared_state().await.assert_eq(indoc! {"
 752        123456789012aaaa
 753        123456789012😃ˇ😃"
 754    });
 755
 756    cx.set_shared_state(indoc! {"
 757                123456789012aaˇaa
 758                123456789012😃😃"
 759    })
 760    .await;
 761    cx.simulate_shared_keystrokes("j").await;
 762    cx.shared_state().await.assert_eq(indoc! {"
 763        123456789012aaaa
 764        123456789012😃ˇ😃"
 765    });
 766
 767    cx.set_shared_state(indoc! {"
 768        123456789012aaaaˇaaaaaaaa123456789012
 769        wow
 770        123456789012😃😃😃😃😃😃123456789012"
 771    })
 772    .await;
 773    cx.simulate_shared_keystrokes("j j").await;
 774    cx.shared_state().await.assert_eq(indoc! {"
 775        123456789012aaaaaaaaaaaa123456789012
 776        wow
 777        123456789012😃😃ˇ😃😃😃😃123456789012"
 778    });
 779}
 780
 781#[gpui::test]
 782async fn test_wrapped_delete_end_document(cx: &mut gpui::TestAppContext) {
 783    let mut cx = NeovimBackedTestContext::new(cx).await;
 784
 785    cx.set_shared_wrap(12).await;
 786
 787    cx.set_shared_state(indoc! {"
 788                aaˇaaaaaaaaaaaaaaaaaa
 789                bbbbbbbbbbbbbbbbbbbb
 790                cccccccccccccccccccc"
 791    })
 792    .await;
 793    cx.simulate_shared_keystrokes("d shift-g i z z z").await;
 794    cx.shared_state().await.assert_eq(indoc! {"
 795                zzzˇ"
 796    });
 797}
 798
 799#[gpui::test]
 800async fn test_paragraphs_dont_wrap(cx: &mut gpui::TestAppContext) {
 801    let mut cx = NeovimBackedTestContext::new(cx).await;
 802
 803    cx.set_shared_state(indoc! {"
 804        one
 805        ˇ
 806        two"})
 807        .await;
 808
 809    cx.simulate_shared_keystrokes("} }").await;
 810    cx.shared_state().await.assert_eq(indoc! {"
 811        one
 812
 813        twˇo"});
 814
 815    cx.simulate_shared_keystrokes("{ { {").await;
 816    cx.shared_state().await.assert_eq(indoc! {"
 817        ˇone
 818
 819        two"});
 820}
 821
 822#[gpui::test]
 823async fn test_select_all_issue_2170(cx: &mut gpui::TestAppContext) {
 824    let mut cx = VimTestContext::new(cx, true).await;
 825
 826    cx.set_state(
 827        indoc! {"
 828        defmodule Test do
 829            def test(a, ˇ[_, _] = b), do: IO.puts('hi')
 830        end
 831    "},
 832        Mode::Normal,
 833    );
 834    cx.simulate_keystrokes("g a");
 835    cx.assert_state(
 836        indoc! {"
 837        defmodule Test do
 838            def test(a, «[ˇ»_, _] = b), do: IO.puts('hi')
 839        end
 840    "},
 841        Mode::Visual,
 842    );
 843}
 844
 845#[gpui::test]
 846async fn test_jk(cx: &mut gpui::TestAppContext) {
 847    let mut cx = NeovimBackedTestContext::new(cx).await;
 848
 849    cx.update(|_, cx| {
 850        cx.bind_keys([KeyBinding::new(
 851            "j k",
 852            NormalBefore,
 853            Some("vim_mode == insert"),
 854        )])
 855    });
 856    cx.neovim.exec("imap jk <esc>").await;
 857
 858    cx.set_shared_state("ˇhello").await;
 859    cx.simulate_shared_keystrokes("i j o j k").await;
 860    cx.shared_state().await.assert_eq("jˇohello");
 861}
 862
 863#[gpui::test]
 864async fn test_jk_delay(cx: &mut gpui::TestAppContext) {
 865    let mut cx = VimTestContext::new(cx, true).await;
 866
 867    cx.update(|_, cx| {
 868        cx.bind_keys([KeyBinding::new(
 869            "j k",
 870            NormalBefore,
 871            Some("vim_mode == insert"),
 872        )])
 873    });
 874
 875    cx.set_state("ˇhello", Mode::Normal);
 876    cx.simulate_keystrokes("i j");
 877    cx.executor().advance_clock(Duration::from_millis(500));
 878    cx.run_until_parked();
 879    cx.assert_state("ˇhello", Mode::Insert);
 880    cx.executor().advance_clock(Duration::from_millis(500));
 881    cx.run_until_parked();
 882    cx.assert_state("jˇhello", Mode::Insert);
 883    cx.simulate_keystrokes("k j k");
 884    cx.assert_state("jˇkhello", Mode::Normal);
 885}
 886
 887#[gpui::test]
 888async fn test_comma_w(cx: &mut gpui::TestAppContext) {
 889    let mut cx = NeovimBackedTestContext::new(cx).await;
 890
 891    cx.update(|_, cx| {
 892        cx.bind_keys([KeyBinding::new(
 893            ", w",
 894            motion::Down {
 895                display_lines: false,
 896            },
 897            Some("vim_mode == normal"),
 898        )])
 899    });
 900    cx.neovim.exec("map ,w j").await;
 901
 902    cx.set_shared_state("ˇhello hello\nhello hello").await;
 903    cx.simulate_shared_keystrokes("f o ; , w").await;
 904    cx.shared_state()
 905        .await
 906        .assert_eq("hello hello\nhello hellˇo");
 907
 908    cx.set_shared_state("ˇhello hello\nhello hello").await;
 909    cx.simulate_shared_keystrokes("f o ; , i").await;
 910    cx.shared_state()
 911        .await
 912        .assert_eq("hellˇo hello\nhello hello");
 913}
 914
 915#[gpui::test]
 916async fn test_rename(cx: &mut gpui::TestAppContext) {
 917    let mut cx = VimTestContext::new_typescript(cx).await;
 918
 919    cx.set_state("const beˇfore = 2; console.log(before)", Mode::Normal);
 920    let def_range = cx.lsp_range("const «beforeˇ» = 2; console.log(before)");
 921    let tgt_range = cx.lsp_range("const before = 2; console.log(«beforeˇ»)");
 922    let mut prepare_request = cx.set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
 923        move |_, _, _| async move { Ok(Some(lsp::PrepareRenameResponse::Range(def_range))) },
 924    );
 925    let mut rename_request =
 926        cx.set_request_handler::<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                PushSneak { first_char: None },
1351                Some("vim_mode == normal"),
1352            ),
1353            KeyBinding::new(
1354                "shift-s",
1355                PushSneakBackward { first_char: None },
1356                Some("vim_mode == normal"),
1357            ),
1358            KeyBinding::new(
1359                "shift-s",
1360                PushSneakBackward { 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}
1715
1716#[gpui::test]
1717async fn test_folded_multibuffer_excerpts(cx: &mut gpui::TestAppContext) {
1718    VimTestContext::init(cx);
1719    cx.update(|cx| {
1720        VimTestContext::init_keybindings(true, cx);
1721    });
1722    let (editor, cx) = cx.add_window_view(|window, cx| {
1723        let multi_buffer = MultiBuffer::build_multi(
1724            [
1725                ("111\n222\n333\n444\n", vec![Point::row_range(0..2)]),
1726                ("aaa\nbbb\nccc\nddd\n", vec![Point::row_range(0..2)]),
1727                ("AAA\nBBB\nCCC\nDDD\n", vec![Point::row_range(0..2)]),
1728                ("one\ntwo\nthr\nfou\n", vec![Point::row_range(0..2)]),
1729            ],
1730            cx,
1731        );
1732        let mut editor = Editor::new(EditorMode::full(), multi_buffer.clone(), None, window, cx);
1733
1734        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
1735        // fold all but the second buffer, so that we test navigating between two
1736        // adjacent folded buffers, as well as folded buffers at the start and
1737        // end the multibuffer
1738        editor.fold_buffer(buffer_ids[0], cx);
1739        editor.fold_buffer(buffer_ids[2], cx);
1740        editor.fold_buffer(buffer_ids[3], cx);
1741
1742        editor
1743    });
1744    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
1745
1746    cx.assert_excerpts_with_selections(indoc! {"
1747        [EXCERPT]
1748        ˇ[FOLDED]
1749        [EXCERPT]
1750        aaa
1751        bbb
1752        [EXCERPT]
1753        [FOLDED]
1754        [EXCERPT]
1755        [FOLDED]
1756        "
1757    });
1758    cx.simulate_keystroke("j");
1759    cx.assert_excerpts_with_selections(indoc! {"
1760        [EXCERPT]
1761        [FOLDED]
1762        [EXCERPT]
1763        ˇaaa
1764        bbb
1765        [EXCERPT]
1766        [FOLDED]
1767        [EXCERPT]
1768        [FOLDED]
1769        "
1770    });
1771    cx.simulate_keystroke("j");
1772    cx.simulate_keystroke("j");
1773    cx.assert_excerpts_with_selections(indoc! {"
1774        [EXCERPT]
1775        [FOLDED]
1776        [EXCERPT]
1777        aaa
1778        bbb
1779        ˇ[EXCERPT]
1780        [FOLDED]
1781        [EXCERPT]
1782        [FOLDED]
1783        "
1784    });
1785    cx.simulate_keystroke("j");
1786    cx.assert_excerpts_with_selections(indoc! {"
1787        [EXCERPT]
1788        [FOLDED]
1789        [EXCERPT]
1790        aaa
1791        bbb
1792        [EXCERPT]
1793        ˇ[FOLDED]
1794        [EXCERPT]
1795        [FOLDED]
1796        "
1797    });
1798    cx.simulate_keystroke("j");
1799    cx.assert_excerpts_with_selections(indoc! {"
1800        [EXCERPT]
1801        [FOLDED]
1802        [EXCERPT]
1803        aaa
1804        bbb
1805        [EXCERPT]
1806        [FOLDED]
1807        [EXCERPT]
1808        ˇ[FOLDED]
1809        "
1810    });
1811    cx.simulate_keystroke("k");
1812    cx.assert_excerpts_with_selections(indoc! {"
1813        [EXCERPT]
1814        [FOLDED]
1815        [EXCERPT]
1816        aaa
1817        bbb
1818        [EXCERPT]
1819        ˇ[FOLDED]
1820        [EXCERPT]
1821        [FOLDED]
1822        "
1823    });
1824    cx.simulate_keystroke("k");
1825    cx.simulate_keystroke("k");
1826    cx.simulate_keystroke("k");
1827    cx.assert_excerpts_with_selections(indoc! {"
1828        [EXCERPT]
1829        [FOLDED]
1830        [EXCERPT]
1831        ˇaaa
1832        bbb
1833        [EXCERPT]
1834        [FOLDED]
1835        [EXCERPT]
1836        [FOLDED]
1837        "
1838    });
1839    cx.simulate_keystroke("k");
1840    cx.assert_excerpts_with_selections(indoc! {"
1841        [EXCERPT]
1842        ˇ[FOLDED]
1843        [EXCERPT]
1844        aaa
1845        bbb
1846        [EXCERPT]
1847        [FOLDED]
1848        [EXCERPT]
1849        [FOLDED]
1850        "
1851    });
1852    cx.simulate_keystroke("shift-g");
1853    cx.assert_excerpts_with_selections(indoc! {"
1854        [EXCERPT]
1855        [FOLDED]
1856        [EXCERPT]
1857        aaa
1858        bbb
1859        [EXCERPT]
1860        [FOLDED]
1861        [EXCERPT]
1862        ˇ[FOLDED]
1863        "
1864    });
1865    cx.simulate_keystrokes("g g");
1866    cx.assert_excerpts_with_selections(indoc! {"
1867        [EXCERPT]
1868        ˇ[FOLDED]
1869        [EXCERPT]
1870        aaa
1871        bbb
1872        [EXCERPT]
1873        [FOLDED]
1874        [EXCERPT]
1875        [FOLDED]
1876        "
1877    });
1878    cx.update_editor(|editor, _, cx| {
1879        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
1880        editor.fold_buffer(buffer_ids[1], cx);
1881    });
1882
1883    cx.assert_excerpts_with_selections(indoc! {"
1884        [EXCERPT]
1885        ˇ[FOLDED]
1886        [EXCERPT]
1887        [FOLDED]
1888        [EXCERPT]
1889        [FOLDED]
1890        [EXCERPT]
1891        [FOLDED]
1892        "
1893    });
1894    cx.simulate_keystrokes("2 j");
1895    cx.assert_excerpts_with_selections(indoc! {"
1896        [EXCERPT]
1897        [FOLDED]
1898        [EXCERPT]
1899        [FOLDED]
1900        [EXCERPT]
1901        ˇ[FOLDED]
1902        [EXCERPT]
1903        [FOLDED]
1904        "
1905    });
1906}
1907
1908#[gpui::test]
1909async fn test_delete_paragraph_motion(cx: &mut gpui::TestAppContext) {
1910    let mut cx = NeovimBackedTestContext::new(cx).await;
1911    cx.set_shared_state(indoc! {
1912        "ˇhello world.
1913
1914        hello world.
1915        "
1916    })
1917    .await;
1918    cx.simulate_shared_keystrokes("y }").await;
1919    cx.shared_clipboard().await.assert_eq("hello world.\n");
1920    cx.simulate_shared_keystrokes("d }").await;
1921    cx.shared_state().await.assert_eq("ˇ\nhello world.\n");
1922    cx.shared_clipboard().await.assert_eq("hello world.\n");
1923
1924    cx.set_shared_state(indoc! {
1925        "helˇlo world.
1926
1927            hello world.
1928            "
1929    })
1930    .await;
1931    cx.simulate_shared_keystrokes("y }").await;
1932    cx.shared_clipboard().await.assert_eq("lo world.");
1933    cx.simulate_shared_keystrokes("d }").await;
1934    cx.shared_state().await.assert_eq("heˇl\n\nhello world.\n");
1935    cx.shared_clipboard().await.assert_eq("lo world.");
1936}
1937
1938#[gpui::test]
1939async fn test_delete_unmatched_brace(cx: &mut gpui::TestAppContext) {
1940    let mut cx = NeovimBackedTestContext::new(cx).await;
1941    cx.set_shared_state(indoc! {
1942        "fn o(wow: i32) {
1943          othˇ(wow)
1944          oth(wow)
1945        }
1946        "
1947    })
1948    .await;
1949    cx.simulate_shared_keystrokes("d ] }").await;
1950    cx.shared_state().await.assert_eq(indoc! {
1951        "fn o(wow: i32) {
1952          otˇh
1953        }
1954        "
1955    });
1956    cx.shared_clipboard().await.assert_eq("(wow)\n  oth(wow)");
1957    cx.set_shared_state(indoc! {
1958        "fn o(wow: i32) {
1959          ˇoth(wow)
1960          oth(wow)
1961        }
1962        "
1963    })
1964    .await;
1965    cx.simulate_shared_keystrokes("d ] }").await;
1966    cx.shared_state().await.assert_eq(indoc! {
1967        "fn o(wow: i32) {
1968         ˇ}
1969        "
1970    });
1971    cx.shared_clipboard()
1972        .await
1973        .assert_eq("  oth(wow)\n  oth(wow)\n");
1974}