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    AnchorRangeExt, DisplayPoint, Editor, EditorMode, MultiBuffer, actions::DeleteLine,
  11    code_context_menus::CodeContextMenu, display_map::DisplayRow,
  12    test::editor_test_context::EditorTestContext,
  13};
  14use futures::StreamExt;
  15use gpui::{KeyBinding, Modifiers, MouseButton, TestAppContext, px};
  16use language::Point;
  17pub use neovim_backed_test_context::*;
  18use settings::SettingsStore;
  19use ui::Pixels;
  20use util::test::marked_text_ranges;
  21pub use vim_test_context::*;
  22
  23use indoc::indoc;
  24use search::BufferSearchBar;
  25use workspace::WorkspaceSettings;
  26
  27use crate::{PushSneak, PushSneakBackward, insert::NormalBefore, motion, state::Mode};
  28
  29#[gpui::test]
  30async fn test_initially_disabled(cx: &mut gpui::TestAppContext) {
  31    let mut cx = VimTestContext::new(cx, false).await;
  32    cx.simulate_keystrokes("h j k l");
  33    cx.assert_editor_state("hjklˇ");
  34}
  35
  36#[gpui::test]
  37async fn test_neovim(cx: &mut gpui::TestAppContext) {
  38    let mut cx = NeovimBackedTestContext::new(cx).await;
  39
  40    cx.simulate_shared_keystrokes("i").await;
  41    cx.shared_state().await.assert_matches();
  42    cx.simulate_shared_keystrokes("shift-t e s t space t e s t escape 0 d w")
  43        .await;
  44    cx.shared_state().await.assert_matches();
  45    cx.assert_editor_state("ˇtest");
  46}
  47
  48#[gpui::test]
  49async fn test_toggle_through_settings(cx: &mut gpui::TestAppContext) {
  50    let mut cx = VimTestContext::new(cx, true).await;
  51
  52    cx.simulate_keystrokes("i");
  53    assert_eq!(cx.mode(), Mode::Insert);
  54
  55    // Editor acts as though vim is disabled
  56    cx.disable_vim();
  57    cx.simulate_keystrokes("h j k l");
  58    cx.assert_editor_state("hjklˇ");
  59
  60    // Selections aren't changed if editor is blurred but vim-mode is still disabled.
  61    cx.cx.set_state("«hjklˇ»");
  62    cx.assert_editor_state("«hjklˇ»");
  63    cx.update_editor(|_, window, _cx| window.blur());
  64    cx.assert_editor_state("«hjklˇ»");
  65    cx.update_editor(|_, window, cx| cx.focus_self(window));
  66    cx.assert_editor_state("«hjklˇ»");
  67
  68    // Enabling dynamically sets vim mode again and restores normal mode
  69    cx.enable_vim();
  70    assert_eq!(cx.mode(), Mode::Normal);
  71    cx.simulate_keystrokes("h h h l");
  72    assert_eq!(cx.buffer_text(), "hjkl".to_owned());
  73    cx.assert_editor_state("hˇjkl");
  74    cx.simulate_keystrokes("i T e s t");
  75    cx.assert_editor_state("hTestˇjkl");
  76
  77    // Disabling and enabling resets to normal mode
  78    assert_eq!(cx.mode(), Mode::Insert);
  79    cx.disable_vim();
  80    cx.enable_vim();
  81    assert_eq!(cx.mode(), Mode::Normal);
  82}
  83
  84#[gpui::test]
  85async fn test_cancel_selection(cx: &mut gpui::TestAppContext) {
  86    let mut cx = VimTestContext::new(cx, true).await;
  87
  88    cx.set_state(
  89        indoc! {"The quick brown fox juˇmps over the lazy dog"},
  90        Mode::Normal,
  91    );
  92    // jumps
  93    cx.simulate_keystrokes("v l l");
  94    cx.assert_editor_state("The quick brown fox ju«mpsˇ» over the lazy dog");
  95
  96    cx.simulate_keystrokes("escape");
  97    cx.assert_editor_state("The quick brown fox jumpˇs over the lazy dog");
  98
  99    // go back to the same selection state
 100    cx.simulate_keystrokes("v h h");
 101    cx.assert_editor_state("The quick brown fox ju«ˇmps» over the lazy dog");
 102
 103    // Ctrl-[ should behave like Esc
 104    cx.simulate_keystrokes("ctrl-[");
 105    cx.assert_editor_state("The quick brown fox juˇmps over the lazy dog");
 106}
 107
 108#[gpui::test]
 109async fn test_buffer_search(cx: &mut gpui::TestAppContext) {
 110    let mut cx = VimTestContext::new(cx, true).await;
 111
 112    cx.set_state(
 113        indoc! {"
 114            The quick brown
 115            fox juˇmps over
 116            the lazy dog"},
 117        Mode::Normal,
 118    );
 119    cx.simulate_keystrokes("/");
 120
 121    let search_bar = cx.workspace(|workspace, _, cx| {
 122        workspace
 123            .active_pane()
 124            .read(cx)
 125            .toolbar()
 126            .read(cx)
 127            .item_of_type::<BufferSearchBar>()
 128            .expect("Buffer search bar should be deployed")
 129    });
 130
 131    cx.update_entity(search_bar, |bar, _, cx| {
 132        assert_eq!(bar.query(cx), "");
 133    })
 134}
 135
 136#[gpui::test]
 137async fn test_count_down(cx: &mut gpui::TestAppContext) {
 138    let mut cx = VimTestContext::new(cx, true).await;
 139
 140    cx.set_state(indoc! {"aˇa\nbb\ncc\ndd\nee"}, Mode::Normal);
 141    cx.simulate_keystrokes("2 down");
 142    cx.assert_editor_state("aa\nbb\ncˇc\ndd\nee");
 143    cx.simulate_keystrokes("9 down");
 144    cx.assert_editor_state("aa\nbb\ncc\ndd\neˇe");
 145}
 146
 147#[gpui::test]
 148async fn test_end_of_document_710(cx: &mut gpui::TestAppContext) {
 149    let mut cx = VimTestContext::new(cx, true).await;
 150
 151    // goes to end by default
 152    cx.set_state(indoc! {"aˇa\nbb\ncc"}, Mode::Normal);
 153    cx.simulate_keystrokes("shift-g");
 154    cx.assert_editor_state("aa\nbb\ncˇc");
 155
 156    // can go to line 1 (https://github.com/zed-industries/zed/issues/5812)
 157    cx.simulate_keystrokes("1 shift-g");
 158    cx.assert_editor_state("aˇa\nbb\ncc");
 159}
 160
 161#[gpui::test]
 162async fn test_end_of_line_with_times(cx: &mut gpui::TestAppContext) {
 163    let mut cx = VimTestContext::new(cx, true).await;
 164
 165    // goes to current line end
 166    cx.set_state(indoc! {"ˇaa\nbb\ncc"}, Mode::Normal);
 167    cx.simulate_keystrokes("$");
 168    cx.assert_editor_state("aˇa\nbb\ncc");
 169
 170    // goes to next line end
 171    cx.simulate_keystrokes("2 $");
 172    cx.assert_editor_state("aa\nbˇb\ncc");
 173
 174    // try to exceed the final line.
 175    cx.simulate_keystrokes("4 $");
 176    cx.assert_editor_state("aa\nbb\ncˇc");
 177}
 178
 179#[gpui::test]
 180async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 181    let mut cx = VimTestContext::new(cx, true).await;
 182
 183    // works in normal mode
 184    cx.set_state(indoc! {"aa\nbˇb\ncc"}, Mode::Normal);
 185    cx.simulate_keystrokes("> >");
 186    cx.assert_editor_state("aa\n    bˇb\ncc");
 187    cx.simulate_keystrokes("< <");
 188    cx.assert_editor_state("aa\nbˇb\ncc");
 189
 190    // works in visual mode
 191    cx.simulate_keystrokes("shift-v down >");
 192    cx.assert_editor_state("aa\n    bˇb\n    cc");
 193
 194    // works as operator
 195    cx.set_state("aa\nbˇb\ncc\n", Mode::Normal);
 196    cx.simulate_keystrokes("> j");
 197    cx.assert_editor_state("aa\n    bˇb\n    cc\n");
 198    cx.simulate_keystrokes("< k");
 199    cx.assert_editor_state("aa\nbˇb\n    cc\n");
 200    cx.simulate_keystrokes("> i p");
 201    cx.assert_editor_state("    aa\n    bˇb\n        cc\n");
 202    cx.simulate_keystrokes("< i p");
 203    cx.assert_editor_state("aa\nbˇb\n    cc\n");
 204    cx.simulate_keystrokes("< i p");
 205    cx.assert_editor_state("aa\nbˇb\ncc\n");
 206
 207    cx.set_state("ˇaa\nbb\ncc\n", Mode::Normal);
 208    cx.simulate_keystrokes("> 2 j");
 209    cx.assert_editor_state("    ˇaa\n    bb\n    cc\n");
 210
 211    cx.set_state("aa\nbb\nˇcc\n", Mode::Normal);
 212    cx.simulate_keystrokes("> 2 k");
 213    cx.assert_editor_state("    aa\n    bb\n    ˇcc\n");
 214
 215    // works with repeat
 216    cx.set_state("a\nb\nccˇc\n", Mode::Normal);
 217    cx.simulate_keystrokes("> 2 k");
 218    cx.assert_editor_state("    a\n    b\n    ccˇc\n");
 219    cx.simulate_keystrokes(".");
 220    cx.assert_editor_state("        a\n        b\n        ccˇc\n");
 221    cx.simulate_keystrokes("v k <");
 222    cx.assert_editor_state("        a\n\n    ccc\n");
 223    cx.simulate_keystrokes(".");
 224    cx.assert_editor_state("        a\n\nccc\n");
 225}
 226
 227#[gpui::test]
 228async fn test_escape_command_palette(cx: &mut gpui::TestAppContext) {
 229    let mut cx = VimTestContext::new(cx, true).await;
 230
 231    cx.set_state("aˇbc\n", Mode::Normal);
 232    cx.simulate_keystrokes("i cmd-shift-p");
 233
 234    assert!(
 235        cx.workspace(|workspace, _, cx| workspace.active_modal::<CommandPalette>(cx).is_some())
 236    );
 237    cx.simulate_keystrokes("escape");
 238    cx.run_until_parked();
 239    assert!(
 240        !cx.workspace(|workspace, _, cx| workspace.active_modal::<CommandPalette>(cx).is_some())
 241    );
 242    cx.assert_state("aˇbc\n", Mode::Insert);
 243}
 244
 245#[gpui::test]
 246async fn test_escape_cancels(cx: &mut gpui::TestAppContext) {
 247    let mut cx = VimTestContext::new(cx, true).await;
 248
 249    cx.set_state("aˇbˇc", Mode::Normal);
 250    cx.simulate_keystrokes("escape");
 251
 252    cx.assert_state("aˇbc", Mode::Normal);
 253}
 254
 255#[gpui::test]
 256async fn test_selection_on_search(cx: &mut gpui::TestAppContext) {
 257    let mut cx = VimTestContext::new(cx, true).await;
 258
 259    cx.set_state(indoc! {"aa\nbˇb\ncc\ncc\ncc\n"}, Mode::Normal);
 260    cx.simulate_keystrokes("/ c c");
 261
 262    let search_bar = cx.workspace(|workspace, _, cx| {
 263        workspace
 264            .active_pane()
 265            .read(cx)
 266            .toolbar()
 267            .read(cx)
 268            .item_of_type::<BufferSearchBar>()
 269            .expect("Buffer search bar should be deployed")
 270    });
 271
 272    cx.update_entity(search_bar, |bar, _, cx| {
 273        assert_eq!(bar.query(cx), "cc");
 274    });
 275
 276    cx.update_editor(|editor, window, cx| {
 277        let highlights = editor.all_text_background_highlights(window, cx);
 278        assert_eq!(3, highlights.len());
 279        assert_eq!(
 280            DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 2),
 281            highlights[0].0
 282        )
 283    });
 284    cx.simulate_keystrokes("enter");
 285
 286    cx.assert_state(indoc! {"aa\nbb\nˇcc\ncc\ncc\n"}, Mode::Normal);
 287    cx.simulate_keystrokes("n");
 288    cx.assert_state(indoc! {"aa\nbb\ncc\nˇcc\ncc\n"}, Mode::Normal);
 289    cx.simulate_keystrokes("shift-n");
 290    cx.assert_state(indoc! {"aa\nbb\nˇcc\ncc\ncc\n"}, Mode::Normal);
 291}
 292
 293#[gpui::test]
 294async fn test_word_characters(cx: &mut gpui::TestAppContext) {
 295    let mut cx = VimTestContext::new_typescript(cx).await;
 296    cx.set_state(
 297        indoc! { "
 298        class A {
 299            #ˇgoop = 99;
 300            $ˇgoop () { return this.#gˇoop };
 301        };
 302        console.log(new A().$gooˇp())
 303    "},
 304        Mode::Normal,
 305    );
 306    cx.simulate_keystrokes("v i w");
 307    cx.assert_state(
 308        indoc! {"
 309        class A {
 310            «#goopˇ» = 99;
 311            «$goopˇ» () { return this.«#goopˇ» };
 312        };
 313        console.log(new A().«$goopˇ»())
 314    "},
 315        Mode::Visual,
 316    )
 317}
 318
 319#[gpui::test]
 320async fn test_kebab_case(cx: &mut gpui::TestAppContext) {
 321    let mut cx = VimTestContext::new_html(cx).await;
 322    cx.set_state(
 323        indoc! { r#"
 324            <div><a class="bg-rˇed"></a></div>
 325            "#},
 326        Mode::Normal,
 327    );
 328    cx.simulate_keystrokes("v i w");
 329    cx.assert_state(
 330        indoc! { r#"
 331        <div><a class="bg-«redˇ»"></a></div>
 332        "#
 333        },
 334        Mode::Visual,
 335    )
 336}
 337
 338#[gpui::test]
 339async fn test_join_lines(cx: &mut gpui::TestAppContext) {
 340    let mut cx = NeovimBackedTestContext::new(cx).await;
 341
 342    cx.set_shared_state(indoc! {"
 343      ˇone
 344      two
 345      three
 346      four
 347      five
 348      six
 349      "})
 350        .await;
 351    cx.simulate_shared_keystrokes("shift-j").await;
 352    cx.shared_state().await.assert_eq(indoc! {"
 353          oneˇ two
 354          three
 355          four
 356          five
 357          six
 358          "});
 359    cx.simulate_shared_keystrokes("3 shift-j").await;
 360    cx.shared_state().await.assert_eq(indoc! {"
 361          one two threeˇ four
 362          five
 363          six
 364          "});
 365
 366    cx.set_shared_state(indoc! {"
 367      ˇone
 368      two
 369      three
 370      four
 371      five
 372      six
 373      "})
 374        .await;
 375    cx.simulate_shared_keystrokes("j v 3 j shift-j").await;
 376    cx.shared_state().await.assert_eq(indoc! {"
 377      one
 378      two three fourˇ five
 379      six
 380      "});
 381
 382    cx.set_shared_state(indoc! {"
 383      ˇone
 384      two
 385      three
 386      four
 387      five
 388      six
 389      "})
 390        .await;
 391    cx.simulate_shared_keystrokes("g shift-j").await;
 392    cx.shared_state().await.assert_eq(indoc! {"
 393          oneˇtwo
 394          three
 395          four
 396          five
 397          six
 398          "});
 399    cx.simulate_shared_keystrokes("3 g shift-j").await;
 400    cx.shared_state().await.assert_eq(indoc! {"
 401          onetwothreeˇfour
 402          five
 403          six
 404          "});
 405
 406    cx.set_shared_state(indoc! {"
 407      ˇone
 408      two
 409      three
 410      four
 411      five
 412      six
 413      "})
 414        .await;
 415    cx.simulate_shared_keystrokes("j v 3 j g shift-j").await;
 416    cx.shared_state().await.assert_eq(indoc! {"
 417      one
 418      twothreefourˇfive
 419      six
 420      "});
 421}
 422
 423#[cfg(target_os = "macos")]
 424#[gpui::test]
 425async fn test_wrapped_lines(cx: &mut gpui::TestAppContext) {
 426    let mut cx = NeovimBackedTestContext::new(cx).await;
 427
 428    cx.set_shared_wrap(12).await;
 429    // tests line wrap as follows:
 430    //  1: twelve char
 431    //     twelve char
 432    //  2: twelve char
 433    cx.set_shared_state(indoc! { "
 434        tˇwelve char twelve char
 435        twelve char
 436    "})
 437        .await;
 438    cx.simulate_shared_keystrokes("j").await;
 439    cx.shared_state().await.assert_eq(indoc! {"
 440        twelve char twelve char
 441        tˇwelve char
 442    "});
 443    cx.simulate_shared_keystrokes("k").await;
 444    cx.shared_state().await.assert_eq(indoc! {"
 445        tˇwelve char twelve char
 446        twelve char
 447    "});
 448    cx.simulate_shared_keystrokes("g j").await;
 449    cx.shared_state().await.assert_eq(indoc! {"
 450        twelve char tˇwelve char
 451        twelve char
 452    "});
 453    cx.simulate_shared_keystrokes("g j").await;
 454    cx.shared_state().await.assert_eq(indoc! {"
 455        twelve char twelve char
 456        tˇwelve char
 457    "});
 458
 459    cx.simulate_shared_keystrokes("g k").await;
 460    cx.shared_state().await.assert_eq(indoc! {"
 461        twelve char tˇwelve char
 462        twelve char
 463    "});
 464
 465    cx.simulate_shared_keystrokes("g ^").await;
 466    cx.shared_state().await.assert_eq(indoc! {"
 467        twelve char ˇtwelve char
 468        twelve char
 469    "});
 470
 471    cx.simulate_shared_keystrokes("^").await;
 472    cx.shared_state().await.assert_eq(indoc! {"
 473        ˇtwelve char twelve char
 474        twelve char
 475    "});
 476
 477    cx.simulate_shared_keystrokes("g $").await;
 478    cx.shared_state().await.assert_eq(indoc! {"
 479        twelve charˇ twelve char
 480        twelve char
 481    "});
 482    cx.simulate_shared_keystrokes("$").await;
 483    cx.shared_state().await.assert_eq(indoc! {"
 484        twelve char twelve chaˇr
 485        twelve char
 486    "});
 487
 488    cx.set_shared_state(indoc! { "
 489        tˇwelve char twelve char
 490        twelve char
 491    "})
 492        .await;
 493    cx.simulate_shared_keystrokes("enter").await;
 494    cx.shared_state().await.assert_eq(indoc! {"
 495            twelve char twelve char
 496            ˇtwelve char
 497        "});
 498
 499    cx.set_shared_state(indoc! { "
 500        twelve char
 501        tˇwelve char twelve char
 502        twelve char
 503    "})
 504        .await;
 505    cx.simulate_shared_keystrokes("o o escape").await;
 506    cx.shared_state().await.assert_eq(indoc! {"
 507        twelve char
 508        twelve char twelve char
 509        ˇo
 510        twelve char
 511    "});
 512
 513    cx.set_shared_state(indoc! { "
 514        twelve char
 515        tˇwelve char twelve char
 516        twelve char
 517    "})
 518        .await;
 519    cx.simulate_shared_keystrokes("shift-a a escape").await;
 520    cx.shared_state().await.assert_eq(indoc! {"
 521        twelve char
 522        twelve char twelve charˇa
 523        twelve char
 524    "});
 525    cx.simulate_shared_keystrokes("shift-i i escape").await;
 526    cx.shared_state().await.assert_eq(indoc! {"
 527        twelve char
 528        ˇitwelve char twelve chara
 529        twelve char
 530    "});
 531    cx.simulate_shared_keystrokes("shift-d").await;
 532    cx.shared_state().await.assert_eq(indoc! {"
 533        twelve char
 534        ˇ
 535        twelve char
 536    "});
 537
 538    cx.set_shared_state(indoc! { "
 539        twelve char
 540        twelve char tˇwelve char
 541        twelve char
 542    "})
 543        .await;
 544    cx.simulate_shared_keystrokes("shift-o o escape").await;
 545    cx.shared_state().await.assert_eq(indoc! {"
 546        twelve char
 547        ˇo
 548        twelve char twelve char
 549        twelve char
 550    "});
 551
 552    // line wraps as:
 553    // fourteen ch
 554    // ar
 555    // fourteen ch
 556    // ar
 557    cx.set_shared_state(indoc! { "
 558        fourteen chaˇr
 559        fourteen char
 560    "})
 561        .await;
 562
 563    cx.simulate_shared_keystrokes("d i w").await;
 564    cx.shared_state().await.assert_eq(indoc! {"
 565        fourteenˇ•
 566        fourteen char
 567    "});
 568    cx.simulate_shared_keystrokes("j shift-f e f r").await;
 569    cx.shared_state().await.assert_eq(indoc! {"
 570        fourteen•
 571        fourteen chaˇr
 572    "});
 573}
 574
 575#[gpui::test]
 576async fn test_folds(cx: &mut gpui::TestAppContext) {
 577    let mut cx = NeovimBackedTestContext::new(cx).await;
 578    cx.set_neovim_option("foldmethod=manual").await;
 579
 580    cx.set_shared_state(indoc! { "
 581        fn boop() {
 582          ˇbarp()
 583          bazp()
 584        }
 585    "})
 586        .await;
 587    cx.simulate_shared_keystrokes("shift-v j z f").await;
 588
 589    // visual display is now:
 590    // fn boop () {
 591    //  [FOLDED]
 592    // }
 593
 594    // TODO: this should not be needed but currently zf does not
 595    // return to normal mode.
 596    cx.simulate_shared_keystrokes("escape").await;
 597
 598    // skip over fold downward
 599    cx.simulate_shared_keystrokes("g g").await;
 600    cx.shared_state().await.assert_eq(indoc! {"
 601        ˇfn boop() {
 602          barp()
 603          bazp()
 604        }
 605    "});
 606
 607    cx.simulate_shared_keystrokes("j j").await;
 608    cx.shared_state().await.assert_eq(indoc! {"
 609        fn boop() {
 610          barp()
 611          bazp()
 612        ˇ}
 613    "});
 614
 615    // skip over fold upward
 616    cx.simulate_shared_keystrokes("2 k").await;
 617    cx.shared_state().await.assert_eq(indoc! {"
 618        ˇfn boop() {
 619          barp()
 620          bazp()
 621        }
 622    "});
 623
 624    // yank the fold
 625    cx.simulate_shared_keystrokes("down y y").await;
 626    cx.shared_clipboard()
 627        .await
 628        .assert_eq("  barp()\n  bazp()\n");
 629
 630    // re-open
 631    cx.simulate_shared_keystrokes("z o").await;
 632    cx.shared_state().await.assert_eq(indoc! {"
 633        fn boop() {
 634        ˇ  barp()
 635          bazp()
 636        }
 637    "});
 638}
 639
 640#[gpui::test]
 641async fn test_folds_panic(cx: &mut gpui::TestAppContext) {
 642    let mut cx = NeovimBackedTestContext::new(cx).await;
 643    cx.set_neovim_option("foldmethod=manual").await;
 644
 645    cx.set_shared_state(indoc! { "
 646        fn boop() {
 647          ˇbarp()
 648          bazp()
 649        }
 650    "})
 651        .await;
 652    cx.simulate_shared_keystrokes("shift-v j z f").await;
 653    cx.simulate_shared_keystrokes("escape").await;
 654    cx.simulate_shared_keystrokes("g g").await;
 655    cx.simulate_shared_keystrokes("5 d j").await;
 656    cx.shared_state().await.assert_eq("ˇ");
 657    cx.set_shared_state(indoc! {"
 658        fn boop() {
 659          ˇbarp()
 660          bazp()
 661        }
 662    "})
 663        .await;
 664    cx.simulate_shared_keystrokes("shift-v j j z f").await;
 665    cx.simulate_shared_keystrokes("escape").await;
 666    cx.simulate_shared_keystrokes("shift-g shift-v").await;
 667    cx.shared_state().await.assert_eq(indoc! {"
 668        fn boop() {
 669          barp()
 670          bazp()
 671        }
 672        ˇ"});
 673}
 674
 675#[gpui::test]
 676async fn test_clear_counts(cx: &mut gpui::TestAppContext) {
 677    let mut cx = NeovimBackedTestContext::new(cx).await;
 678
 679    cx.set_shared_state(indoc! {"
 680        The quick brown
 681        fox juˇmps over
 682        the lazy dog"})
 683        .await;
 684
 685    cx.simulate_shared_keystrokes("4 escape 3 d l").await;
 686    cx.shared_state().await.assert_eq(indoc! {"
 687        The quick brown
 688        fox juˇ over
 689        the lazy dog"});
 690}
 691
 692#[gpui::test]
 693async fn test_zero(cx: &mut gpui::TestAppContext) {
 694    let mut cx = NeovimBackedTestContext::new(cx).await;
 695
 696    cx.set_shared_state(indoc! {"
 697        The quˇick brown
 698        fox jumps over
 699        the lazy dog"})
 700        .await;
 701
 702    cx.simulate_shared_keystrokes("0").await;
 703    cx.shared_state().await.assert_eq(indoc! {"
 704        ˇThe quick brown
 705        fox jumps over
 706        the lazy dog"});
 707
 708    cx.simulate_shared_keystrokes("1 0 l").await;
 709    cx.shared_state().await.assert_eq(indoc! {"
 710        The quick ˇbrown
 711        fox jumps over
 712        the lazy dog"});
 713}
 714
 715#[gpui::test]
 716async fn test_selection_goal(cx: &mut gpui::TestAppContext) {
 717    let mut cx = NeovimBackedTestContext::new(cx).await;
 718
 719    cx.set_shared_state(indoc! {"
 720        ;;ˇ;
 721        Lorem Ipsum"})
 722        .await;
 723
 724    cx.simulate_shared_keystrokes("a down up ; down up").await;
 725    cx.shared_state().await.assert_eq(indoc! {"
 726        ;;;;ˇ
 727        Lorem Ipsum"});
 728}
 729
 730#[cfg(target_os = "macos")]
 731#[gpui::test]
 732async fn test_wrapped_motions(cx: &mut gpui::TestAppContext) {
 733    let mut cx = NeovimBackedTestContext::new(cx).await;
 734
 735    cx.set_shared_wrap(12).await;
 736
 737    cx.set_shared_state(indoc! {"
 738                aaˇaa
 739                😃😃"
 740    })
 741    .await;
 742    cx.simulate_shared_keystrokes("j").await;
 743    cx.shared_state().await.assert_eq(indoc! {"
 744                aaaa
 745                😃ˇ😃"
 746    });
 747
 748    cx.set_shared_state(indoc! {"
 749                123456789012aaˇaa
 750                123456789012😃😃"
 751    })
 752    .await;
 753    cx.simulate_shared_keystrokes("j").await;
 754    cx.shared_state().await.assert_eq(indoc! {"
 755        123456789012aaaa
 756        123456789012😃ˇ😃"
 757    });
 758
 759    cx.set_shared_state(indoc! {"
 760                123456789012aaˇaa
 761                123456789012😃😃"
 762    })
 763    .await;
 764    cx.simulate_shared_keystrokes("j").await;
 765    cx.shared_state().await.assert_eq(indoc! {"
 766        123456789012aaaa
 767        123456789012😃ˇ😃"
 768    });
 769
 770    cx.set_shared_state(indoc! {"
 771        123456789012aaaaˇaaaaaaaa123456789012
 772        wow
 773        123456789012😃😃😃😃😃😃123456789012"
 774    })
 775    .await;
 776    cx.simulate_shared_keystrokes("j j").await;
 777    cx.shared_state().await.assert_eq(indoc! {"
 778        123456789012aaaaaaaaaaaa123456789012
 779        wow
 780        123456789012😃😃ˇ😃😃😃😃123456789012"
 781    });
 782}
 783
 784#[gpui::test]
 785async fn test_wrapped_delete_end_document(cx: &mut gpui::TestAppContext) {
 786    let mut cx = NeovimBackedTestContext::new(cx).await;
 787
 788    cx.set_shared_wrap(12).await;
 789
 790    cx.set_shared_state(indoc! {"
 791                aaˇaaaaaaaaaaaaaaaaaa
 792                bbbbbbbbbbbbbbbbbbbb
 793                cccccccccccccccccccc"
 794    })
 795    .await;
 796    cx.simulate_shared_keystrokes("d shift-g i z z z").await;
 797    cx.shared_state().await.assert_eq(indoc! {"
 798                zzzˇ"
 799    });
 800}
 801
 802#[gpui::test]
 803async fn test_paragraphs_dont_wrap(cx: &mut gpui::TestAppContext) {
 804    let mut cx = NeovimBackedTestContext::new(cx).await;
 805
 806    cx.set_shared_state(indoc! {"
 807        one
 808        ˇ
 809        two"})
 810        .await;
 811
 812    cx.simulate_shared_keystrokes("} }").await;
 813    cx.shared_state().await.assert_eq(indoc! {"
 814        one
 815
 816        twˇo"});
 817
 818    cx.simulate_shared_keystrokes("{ { {").await;
 819    cx.shared_state().await.assert_eq(indoc! {"
 820        ˇone
 821
 822        two"});
 823}
 824
 825#[gpui::test]
 826async fn test_select_all_issue_2170(cx: &mut gpui::TestAppContext) {
 827    let mut cx = VimTestContext::new(cx, true).await;
 828
 829    cx.set_state(
 830        indoc! {"
 831        defmodule Test do
 832            def test(a, ˇ[_, _] = b), do: IO.puts('hi')
 833        end
 834    "},
 835        Mode::Normal,
 836    );
 837    cx.simulate_keystrokes("g a");
 838    cx.assert_state(
 839        indoc! {"
 840        defmodule Test do
 841            def test(a, «[ˇ»_, _] = b), do: IO.puts('hi')
 842        end
 843    "},
 844        Mode::Visual,
 845    );
 846}
 847
 848#[gpui::test]
 849async fn test_jk(cx: &mut gpui::TestAppContext) {
 850    let mut cx = NeovimBackedTestContext::new(cx).await;
 851
 852    cx.update(|_, cx| {
 853        cx.bind_keys([KeyBinding::new(
 854            "j k",
 855            NormalBefore,
 856            Some("vim_mode == insert"),
 857        )])
 858    });
 859    cx.neovim.exec("imap jk <esc>").await;
 860
 861    cx.set_shared_state("ˇhello").await;
 862    cx.simulate_shared_keystrokes("i j o j k").await;
 863    cx.shared_state().await.assert_eq("jˇohello");
 864}
 865
 866fn assert_pending_input(cx: &mut VimTestContext, expected: &str) {
 867    cx.update_editor(|editor, window, cx| {
 868        let snapshot = editor.snapshot(window, cx);
 869        let highlights = editor
 870            .text_highlights::<editor::PendingInput>(cx)
 871            .unwrap()
 872            .1;
 873        let (_, ranges) = marked_text_ranges(expected, false);
 874
 875        assert_eq!(
 876            highlights
 877                .iter()
 878                .map(|highlight| highlight.to_offset(&snapshot.buffer_snapshot))
 879                .collect::<Vec<_>>(),
 880            ranges
 881        )
 882    });
 883}
 884
 885#[gpui::test]
 886async fn test_jk_multi(cx: &mut gpui::TestAppContext) {
 887    let mut cx = VimTestContext::new(cx, true).await;
 888
 889    cx.update(|_, cx| {
 890        cx.bind_keys([KeyBinding::new(
 891            "j k l",
 892            NormalBefore,
 893            Some("vim_mode == insert"),
 894        )])
 895    });
 896
 897    cx.set_state("ˇone ˇone ˇone", Mode::Normal);
 898    cx.simulate_keystrokes("i j");
 899    cx.simulate_keystrokes("k");
 900    cx.assert_state("ˇjkone ˇjkone ˇjkone", Mode::Insert);
 901    assert_pending_input(&mut cx, "«jk»one «jk»one «jk»one");
 902    cx.simulate_keystrokes("o j k");
 903    cx.assert_state("jkoˇjkone jkoˇjkone jkoˇjkone", Mode::Insert);
 904    assert_pending_input(&mut cx, "jko«jk»one jko«jk»one jko«jk»one");
 905    cx.simulate_keystrokes("l");
 906    cx.assert_state("jkˇoone jkˇoone jkˇoone", Mode::Normal);
 907}
 908
 909#[gpui::test]
 910async fn test_jk_delay(cx: &mut gpui::TestAppContext) {
 911    let mut cx = VimTestContext::new(cx, true).await;
 912
 913    cx.update(|_, cx| {
 914        cx.bind_keys([KeyBinding::new(
 915            "j k",
 916            NormalBefore,
 917            Some("vim_mode == insert"),
 918        )])
 919    });
 920
 921    cx.set_state("ˇhello", Mode::Normal);
 922    cx.simulate_keystrokes("i j");
 923    cx.executor().advance_clock(Duration::from_millis(500));
 924    cx.run_until_parked();
 925    cx.assert_state("ˇjhello", Mode::Insert);
 926    cx.update_editor(|editor, window, cx| {
 927        let snapshot = editor.snapshot(window, cx);
 928        let highlights = editor
 929            .text_highlights::<editor::PendingInput>(cx)
 930            .unwrap()
 931            .1;
 932
 933        assert_eq!(
 934            highlights
 935                .iter()
 936                .map(|highlight| highlight.to_offset(&snapshot.buffer_snapshot))
 937                .collect::<Vec<_>>(),
 938            vec![0..1]
 939        )
 940    });
 941    cx.executor().advance_clock(Duration::from_millis(500));
 942    cx.run_until_parked();
 943    cx.assert_state("jˇhello", Mode::Insert);
 944    cx.simulate_keystrokes("k j k");
 945    cx.assert_state("jˇkhello", Mode::Normal);
 946}
 947
 948#[gpui::test]
 949async fn test_comma_w(cx: &mut gpui::TestAppContext) {
 950    let mut cx = NeovimBackedTestContext::new(cx).await;
 951
 952    cx.update(|_, cx| {
 953        cx.bind_keys([KeyBinding::new(
 954            ", w",
 955            motion::Down {
 956                display_lines: false,
 957            },
 958            Some("vim_mode == normal"),
 959        )])
 960    });
 961    cx.neovim.exec("map ,w j").await;
 962
 963    cx.set_shared_state("ˇhello hello\nhello hello").await;
 964    cx.simulate_shared_keystrokes("f o ; , w").await;
 965    cx.shared_state()
 966        .await
 967        .assert_eq("hello hello\nhello hellˇo");
 968
 969    cx.set_shared_state("ˇhello hello\nhello hello").await;
 970    cx.simulate_shared_keystrokes("f o ; , i").await;
 971    cx.shared_state()
 972        .await
 973        .assert_eq("hellˇo hello\nhello hello");
 974}
 975
 976#[gpui::test]
 977async fn test_completion_menu_scroll_aside(cx: &mut TestAppContext) {
 978    let mut cx = VimTestContext::new_typescript(cx).await;
 979
 980    cx.lsp
 981        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
 982            Ok(Some(lsp::CompletionResponse::Array(vec![
 983                lsp::CompletionItem {
 984                    label: "Test Item".to_string(),
 985                    documentation: Some(lsp::Documentation::String(
 986                        "This is some very long documentation content that will be displayed in the aside panel for scrolling.\n".repeat(50)
 987                    )),
 988                    ..Default::default()
 989                },
 990            ])))
 991        });
 992
 993    cx.set_state("variableˇ", Mode::Insert);
 994    cx.simulate_keystroke(".");
 995    cx.executor().run_until_parked();
 996
 997    let mut initial_offset: Pixels = px(0.0);
 998
 999    cx.update_editor(|editor, _, _| {
1000        let binding = editor.context_menu().borrow();
1001        let Some(CodeContextMenu::Completions(menu)) = binding.as_ref() else {
1002            panic!("Should have completions menu open");
1003        };
1004
1005        initial_offset = menu.scroll_handle_aside.offset().y;
1006    });
1007
1008    // The `ctrl-e` shortcut should scroll the completion menu's aside content
1009    // down, so the updated offset should be lower than the initial offset.
1010    cx.simulate_keystroke("ctrl-e");
1011    cx.update_editor(|editor, _, _| {
1012        let binding = editor.context_menu().borrow();
1013        let Some(CodeContextMenu::Completions(menu)) = binding.as_ref() else {
1014            panic!("Should have completions menu open");
1015        };
1016
1017        assert!(menu.scroll_handle_aside.offset().y < initial_offset);
1018    });
1019
1020    // The `ctrl-y` shortcut should do the inverse scrolling as `ctrl-e`, so the
1021    // offset should now be the same as the initial offset.
1022    cx.simulate_keystroke("ctrl-y");
1023    cx.update_editor(|editor, _, _| {
1024        let binding = editor.context_menu().borrow();
1025        let Some(CodeContextMenu::Completions(menu)) = binding.as_ref() else {
1026            panic!("Should have completions menu open");
1027        };
1028
1029        assert_eq!(menu.scroll_handle_aside.offset().y, initial_offset);
1030    });
1031
1032    // The `ctrl-d` shortcut should scroll the completion menu's aside content
1033    // down, so the updated offset should be lower than the initial offset.
1034    cx.simulate_keystroke("ctrl-d");
1035    cx.update_editor(|editor, _, _| {
1036        let binding = editor.context_menu().borrow();
1037        let Some(CodeContextMenu::Completions(menu)) = binding.as_ref() else {
1038            panic!("Should have completions menu open");
1039        };
1040
1041        assert!(menu.scroll_handle_aside.offset().y < initial_offset);
1042    });
1043
1044    // The `ctrl-u` shortcut should do the inverse scrolling as `ctrl-u`, so the
1045    // offset should now be the same as the initial offset.
1046    cx.simulate_keystroke("ctrl-u");
1047    cx.update_editor(|editor, _, _| {
1048        let binding = editor.context_menu().borrow();
1049        let Some(CodeContextMenu::Completions(menu)) = binding.as_ref() else {
1050            panic!("Should have completions menu open");
1051        };
1052
1053        assert_eq!(menu.scroll_handle_aside.offset().y, initial_offset);
1054    });
1055}
1056
1057#[gpui::test]
1058async fn test_rename(cx: &mut gpui::TestAppContext) {
1059    let mut cx = VimTestContext::new_typescript(cx).await;
1060
1061    cx.set_state("const beˇfore = 2; console.log(before)", Mode::Normal);
1062    let def_range = cx.lsp_range("const «beforeˇ» = 2; console.log(before)");
1063    let tgt_range = cx.lsp_range("const before = 2; console.log(«beforeˇ»)");
1064    let mut prepare_request = cx.set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
1065        move |_, _, _| async move { Ok(Some(lsp::PrepareRenameResponse::Range(def_range))) },
1066    );
1067    let mut rename_request =
1068        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, params, _| async move {
1069            Ok(Some(lsp::WorkspaceEdit {
1070                changes: Some(
1071                    [(
1072                        url.clone(),
1073                        vec![
1074                            lsp::TextEdit::new(def_range, params.new_name.clone()),
1075                            lsp::TextEdit::new(tgt_range, params.new_name),
1076                        ],
1077                    )]
1078                    .into(),
1079                ),
1080                ..Default::default()
1081            }))
1082        });
1083
1084    cx.simulate_keystrokes("c d");
1085    prepare_request.next().await.unwrap();
1086    cx.simulate_input("after");
1087    cx.simulate_keystrokes("enter");
1088    rename_request.next().await.unwrap();
1089    cx.assert_state("const afterˇ = 2; console.log(after)", Mode::Normal)
1090}
1091
1092#[gpui::test]
1093async fn test_remap(cx: &mut gpui::TestAppContext) {
1094    let mut cx = VimTestContext::new(cx, true).await;
1095
1096    // test moving the cursor
1097    cx.update(|_, cx| {
1098        cx.bind_keys([KeyBinding::new(
1099            "g z",
1100            workspace::SendKeystrokes("l l l l".to_string()),
1101            None,
1102        )])
1103    });
1104    cx.set_state("ˇ123456789", Mode::Normal);
1105    cx.simulate_keystrokes("g z");
1106    cx.assert_state("1234ˇ56789", Mode::Normal);
1107
1108    // test switching modes
1109    cx.update(|_, cx| {
1110        cx.bind_keys([KeyBinding::new(
1111            "g y",
1112            workspace::SendKeystrokes("i f o o escape l".to_string()),
1113            None,
1114        )])
1115    });
1116    cx.set_state("ˇ123456789", Mode::Normal);
1117    cx.simulate_keystrokes("g y");
1118    cx.assert_state("fooˇ123456789", Mode::Normal);
1119
1120    // test recursion
1121    cx.update(|_, cx| {
1122        cx.bind_keys([KeyBinding::new(
1123            "g x",
1124            workspace::SendKeystrokes("g z g y".to_string()),
1125            None,
1126        )])
1127    });
1128    cx.set_state("ˇ123456789", Mode::Normal);
1129    cx.simulate_keystrokes("g x");
1130    cx.assert_state("1234fooˇ56789", Mode::Normal);
1131
1132    // test command
1133    cx.update(|_, cx| {
1134        cx.bind_keys([KeyBinding::new(
1135            "g w",
1136            workspace::SendKeystrokes(": j enter".to_string()),
1137            None,
1138        )])
1139    });
1140    cx.set_state("ˇ1234\n56789", Mode::Normal);
1141    cx.simulate_keystrokes("g w");
1142    cx.assert_state("1234ˇ 56789", Mode::Normal);
1143
1144    // test leaving command
1145    cx.update(|_, cx| {
1146        cx.bind_keys([KeyBinding::new(
1147            "g u",
1148            workspace::SendKeystrokes("g w g z".to_string()),
1149            None,
1150        )])
1151    });
1152    cx.set_state("ˇ1234\n56789", Mode::Normal);
1153    cx.simulate_keystrokes("g u");
1154    cx.assert_state("1234 567ˇ89", Mode::Normal);
1155
1156    // test leaving command
1157    cx.update(|_, cx| {
1158        cx.bind_keys([KeyBinding::new(
1159            "g t",
1160            workspace::SendKeystrokes("i space escape".to_string()),
1161            None,
1162        )])
1163    });
1164    cx.set_state("12ˇ34", Mode::Normal);
1165    cx.simulate_keystrokes("g t");
1166    cx.assert_state("12ˇ 34", Mode::Normal);
1167}
1168
1169#[gpui::test]
1170async fn test_undo(cx: &mut gpui::TestAppContext) {
1171    let mut cx = NeovimBackedTestContext::new(cx).await;
1172
1173    cx.set_shared_state("hello quˇoel world").await;
1174    cx.simulate_shared_keystrokes("v i w s c o escape u").await;
1175    cx.shared_state().await.assert_eq("hello ˇquoel world");
1176    cx.simulate_shared_keystrokes("ctrl-r").await;
1177    cx.shared_state().await.assert_eq("hello ˇco world");
1178    cx.simulate_shared_keystrokes("a o right l escape").await;
1179    cx.shared_state().await.assert_eq("hello cooˇl world");
1180    cx.simulate_shared_keystrokes("u").await;
1181    cx.shared_state().await.assert_eq("hello cooˇ world");
1182    cx.simulate_shared_keystrokes("u").await;
1183    cx.shared_state().await.assert_eq("hello cˇo world");
1184    cx.simulate_shared_keystrokes("u").await;
1185    cx.shared_state().await.assert_eq("hello ˇquoel world");
1186
1187    cx.set_shared_state("hello quˇoel world").await;
1188    cx.simulate_shared_keystrokes("v i w ~ u").await;
1189    cx.shared_state().await.assert_eq("hello ˇquoel world");
1190
1191    cx.set_shared_state("\nhello quˇoel world\n").await;
1192    cx.simulate_shared_keystrokes("shift-v s c escape u").await;
1193    cx.shared_state().await.assert_eq("\nˇhello quoel world\n");
1194
1195    cx.set_shared_state(indoc! {"
1196        ˇ1
1197        2
1198        3"})
1199        .await;
1200
1201    cx.simulate_shared_keystrokes("ctrl-v shift-g ctrl-a").await;
1202    cx.shared_state().await.assert_eq(indoc! {"
1203        ˇ2
1204        3
1205        4"});
1206
1207    cx.simulate_shared_keystrokes("u").await;
1208    cx.shared_state().await.assert_eq(indoc! {"
1209        ˇ1
1210        2
1211        3"});
1212}
1213
1214#[gpui::test]
1215async fn test_mouse_selection(cx: &mut TestAppContext) {
1216    let mut cx = VimTestContext::new(cx, true).await;
1217
1218    cx.set_state("ˇone two three", Mode::Normal);
1219
1220    let start_point = cx.pixel_position("one twˇo three");
1221    let end_point = cx.pixel_position("one ˇtwo three");
1222
1223    cx.simulate_mouse_down(start_point, MouseButton::Left, Modifiers::none());
1224    cx.simulate_mouse_move(end_point, MouseButton::Left, Modifiers::none());
1225    cx.simulate_mouse_up(end_point, MouseButton::Left, Modifiers::none());
1226
1227    cx.assert_state("one «ˇtwo» three", Mode::Visual)
1228}
1229
1230#[gpui::test]
1231async fn test_lowercase_marks(cx: &mut TestAppContext) {
1232    let mut cx = NeovimBackedTestContext::new(cx).await;
1233
1234    cx.set_shared_state("line one\nline ˇtwo\nline three").await;
1235    cx.simulate_shared_keystrokes("m a l ' a").await;
1236    cx.shared_state()
1237        .await
1238        .assert_eq("line one\nˇline two\nline three");
1239    cx.simulate_shared_keystrokes("` a").await;
1240    cx.shared_state()
1241        .await
1242        .assert_eq("line one\nline ˇtwo\nline three");
1243
1244    cx.simulate_shared_keystrokes("^ d ` a").await;
1245    cx.shared_state()
1246        .await
1247        .assert_eq("line one\nˇtwo\nline three");
1248}
1249
1250#[gpui::test]
1251async fn test_lt_gt_marks(cx: &mut TestAppContext) {
1252    let mut cx = NeovimBackedTestContext::new(cx).await;
1253
1254    cx.set_shared_state(indoc!(
1255        "
1256        Line one
1257        Line two
1258        Line ˇthree
1259        Line four
1260        Line five
1261    "
1262    ))
1263    .await;
1264
1265    cx.simulate_shared_keystrokes("v j escape k k").await;
1266
1267    cx.simulate_shared_keystrokes("' <").await;
1268    cx.shared_state().await.assert_eq(indoc! {"
1269        Line one
1270        Line two
1271        ˇLine three
1272        Line four
1273        Line five
1274    "});
1275
1276    cx.simulate_shared_keystrokes("` <").await;
1277    cx.shared_state().await.assert_eq(indoc! {"
1278        Line one
1279        Line two
1280        Line ˇthree
1281        Line four
1282        Line five
1283    "});
1284
1285    cx.simulate_shared_keystrokes("' >").await;
1286    cx.shared_state().await.assert_eq(indoc! {"
1287        Line one
1288        Line two
1289        Line three
1290        ˇLine four
1291        Line five
1292    "
1293    });
1294
1295    cx.simulate_shared_keystrokes("` >").await;
1296    cx.shared_state().await.assert_eq(indoc! {"
1297        Line one
1298        Line two
1299        Line three
1300        Line ˇfour
1301        Line five
1302    "
1303    });
1304
1305    cx.simulate_shared_keystrokes("v i w o escape").await;
1306    cx.simulate_shared_keystrokes("` >").await;
1307    cx.shared_state().await.assert_eq(indoc! {"
1308        Line one
1309        Line two
1310        Line three
1311        Line fouˇr
1312        Line five
1313    "
1314    });
1315    cx.simulate_shared_keystrokes("` <").await;
1316    cx.shared_state().await.assert_eq(indoc! {"
1317        Line one
1318        Line two
1319        Line three
1320        Line ˇfour
1321        Line five
1322    "
1323    });
1324}
1325
1326#[gpui::test]
1327async fn test_caret_mark(cx: &mut TestAppContext) {
1328    let mut cx = NeovimBackedTestContext::new(cx).await;
1329
1330    cx.set_shared_state(indoc!(
1331        "
1332        Line one
1333        Line two
1334        Line three
1335        ˇLine four
1336        Line five
1337    "
1338    ))
1339    .await;
1340
1341    cx.simulate_shared_keystrokes("c w shift-s t r a i g h t space t h i n g escape j j")
1342        .await;
1343
1344    cx.simulate_shared_keystrokes("' ^").await;
1345    cx.shared_state().await.assert_eq(indoc! {"
1346        Line one
1347        Line two
1348        Line three
1349        ˇStraight thing four
1350        Line five
1351    "
1352    });
1353
1354    cx.simulate_shared_keystrokes("` ^").await;
1355    cx.shared_state().await.assert_eq(indoc! {"
1356        Line one
1357        Line two
1358        Line three
1359        Straight thingˇ four
1360        Line five
1361    "
1362    });
1363
1364    cx.simulate_shared_keystrokes("k a ! escape k g i ?").await;
1365    cx.shared_state().await.assert_eq(indoc! {"
1366        Line one
1367        Line two
1368        Line three!?ˇ
1369        Straight thing four
1370        Line five
1371    "
1372    });
1373}
1374
1375#[cfg(target_os = "macos")]
1376#[gpui::test]
1377async fn test_dw_eol(cx: &mut gpui::TestAppContext) {
1378    let mut cx = NeovimBackedTestContext::new(cx).await;
1379
1380    cx.set_shared_wrap(12).await;
1381    cx.set_shared_state("twelve ˇchar twelve char\ntwelve char")
1382        .await;
1383    cx.simulate_shared_keystrokes("d w").await;
1384    cx.shared_state()
1385        .await
1386        .assert_eq("twelve ˇtwelve char\ntwelve char");
1387}
1388
1389#[gpui::test]
1390async fn test_toggle_comments(cx: &mut gpui::TestAppContext) {
1391    let mut cx = VimTestContext::new(cx, true).await;
1392
1393    let language = std::sync::Arc::new(language::Language::new(
1394        language::LanguageConfig {
1395            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
1396            ..Default::default()
1397        },
1398        Some(language::tree_sitter_rust::LANGUAGE.into()),
1399    ));
1400    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1401
1402    // works in normal model
1403    cx.set_state(
1404        indoc! {"
1405      ˇone
1406      two
1407      three
1408      "},
1409        Mode::Normal,
1410    );
1411    cx.simulate_keystrokes("g c c");
1412    cx.assert_state(
1413        indoc! {"
1414          // ˇone
1415          two
1416          three
1417          "},
1418        Mode::Normal,
1419    );
1420
1421    // works in visual mode
1422    cx.simulate_keystrokes("v j g c");
1423    cx.assert_state(
1424        indoc! {"
1425          // // ˇone
1426          // two
1427          three
1428          "},
1429        Mode::Normal,
1430    );
1431
1432    // works in visual line mode
1433    cx.simulate_keystrokes("shift-v j g c");
1434    cx.assert_state(
1435        indoc! {"
1436          // ˇone
1437          two
1438          three
1439          "},
1440        Mode::Normal,
1441    );
1442
1443    // works with count
1444    cx.simulate_keystrokes("g c 2 j");
1445    cx.assert_state(
1446        indoc! {"
1447            // // ˇone
1448            // two
1449            // three
1450            "},
1451        Mode::Normal,
1452    );
1453
1454    // works with motion object
1455    cx.simulate_keystrokes("shift-g");
1456    cx.simulate_keystrokes("g c g g");
1457    cx.assert_state(
1458        indoc! {"
1459            // one
1460            two
1461            three
1462            ˇ"},
1463        Mode::Normal,
1464    );
1465}
1466
1467#[gpui::test]
1468async fn test_find_multibyte(cx: &mut gpui::TestAppContext) {
1469    let mut cx = NeovimBackedTestContext::new(cx).await;
1470
1471    cx.set_shared_state(r#"<label for="guests">ˇPočet hostů</label>"#)
1472        .await;
1473
1474    cx.simulate_shared_keystrokes("c t < o escape").await;
1475    cx.shared_state()
1476        .await
1477        .assert_eq(r#"<label for="guests">ˇo</label>"#);
1478}
1479
1480#[gpui::test]
1481async fn test_sneak(cx: &mut gpui::TestAppContext) {
1482    let mut cx = VimTestContext::new(cx, true).await;
1483
1484    cx.update(|_window, cx| {
1485        cx.bind_keys([
1486            KeyBinding::new(
1487                "s",
1488                PushSneak { first_char: None },
1489                Some("vim_mode == normal"),
1490            ),
1491            KeyBinding::new(
1492                "shift-s",
1493                PushSneakBackward { first_char: None },
1494                Some("vim_mode == normal"),
1495            ),
1496            KeyBinding::new(
1497                "shift-s",
1498                PushSneakBackward { first_char: None },
1499                Some("vim_mode == visual"),
1500            ),
1501        ])
1502    });
1503
1504    // Sneak forwards multibyte & multiline
1505    cx.set_state(
1506        indoc! {
1507            r#"<labelˇ for="guests">
1508                    Počet hostů
1509                </label>"#
1510        },
1511        Mode::Normal,
1512    );
1513    cx.simulate_keystrokes("s t ů");
1514    cx.assert_state(
1515        indoc! {
1516            r#"<label for="guests">
1517                Počet hosˇtů
1518            </label>"#
1519        },
1520        Mode::Normal,
1521    );
1522
1523    // Visual sneak backwards multibyte & multiline
1524    cx.simulate_keystrokes("v S < l");
1525    cx.assert_state(
1526        indoc! {
1527            r#"«ˇ<label for="guests">
1528                Počet host»ů
1529            </label>"#
1530        },
1531        Mode::Visual,
1532    );
1533
1534    // Sneak backwards repeated
1535    cx.set_state(r#"11 12 13 ˇ14"#, Mode::Normal);
1536    cx.simulate_keystrokes("S space 1");
1537    cx.assert_state(r#"11 12ˇ 13 14"#, Mode::Normal);
1538    cx.simulate_keystrokes(";");
1539    cx.assert_state(r#"11ˇ 12 13 14"#, Mode::Normal);
1540}
1541
1542#[gpui::test]
1543async fn test_plus_minus(cx: &mut gpui::TestAppContext) {
1544    let mut cx = NeovimBackedTestContext::new(cx).await;
1545
1546    cx.set_shared_state(indoc! {
1547        "one
1548           two
1549        thrˇee
1550    "})
1551        .await;
1552
1553    cx.simulate_shared_keystrokes("-").await;
1554    cx.shared_state().await.assert_matches();
1555    cx.simulate_shared_keystrokes("-").await;
1556    cx.shared_state().await.assert_matches();
1557    cx.simulate_shared_keystrokes("+").await;
1558    cx.shared_state().await.assert_matches();
1559}
1560
1561#[gpui::test]
1562async fn test_command_alias(cx: &mut gpui::TestAppContext) {
1563    let mut cx = VimTestContext::new(cx, true).await;
1564    cx.update_global(|store: &mut SettingsStore, cx| {
1565        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
1566            let mut aliases = HashMap::default();
1567            aliases.insert("Q".to_string(), "upper".to_string());
1568            s.command_aliases = Some(aliases)
1569        });
1570    });
1571
1572    cx.set_state("ˇhello world", Mode::Normal);
1573    cx.simulate_keystrokes(": Q");
1574    cx.set_state("ˇHello world", Mode::Normal);
1575}
1576
1577#[gpui::test]
1578async fn test_remap_adjacent_dog_cat(cx: &mut gpui::TestAppContext) {
1579    let mut cx = NeovimBackedTestContext::new(cx).await;
1580    cx.update(|_, cx| {
1581        cx.bind_keys([
1582            KeyBinding::new(
1583                "d o g",
1584                workspace::SendKeystrokes("🐶".to_string()),
1585                Some("vim_mode == insert"),
1586            ),
1587            KeyBinding::new(
1588                "c a t",
1589                workspace::SendKeystrokes("🐱".to_string()),
1590                Some("vim_mode == insert"),
1591            ),
1592        ])
1593    });
1594    cx.neovim.exec("imap dog 🐶").await;
1595    cx.neovim.exec("imap cat 🐱").await;
1596
1597    cx.set_shared_state("ˇ").await;
1598    cx.simulate_shared_keystrokes("i d o g").await;
1599    cx.shared_state().await.assert_eq("🐶ˇ");
1600
1601    cx.set_shared_state("ˇ").await;
1602    cx.simulate_shared_keystrokes("i d o d o g").await;
1603    cx.shared_state().await.assert_eq("do🐶ˇ");
1604
1605    cx.set_shared_state("ˇ").await;
1606    cx.simulate_shared_keystrokes("i d o c a t").await;
1607    cx.shared_state().await.assert_eq("do🐱ˇ");
1608}
1609
1610#[gpui::test]
1611async fn test_remap_nested_pineapple(cx: &mut gpui::TestAppContext) {
1612    let mut cx = NeovimBackedTestContext::new(cx).await;
1613    cx.update(|_, cx| {
1614        cx.bind_keys([
1615            KeyBinding::new(
1616                "p i n",
1617                workspace::SendKeystrokes("📌".to_string()),
1618                Some("vim_mode == insert"),
1619            ),
1620            KeyBinding::new(
1621                "p i n e",
1622                workspace::SendKeystrokes("🌲".to_string()),
1623                Some("vim_mode == insert"),
1624            ),
1625            KeyBinding::new(
1626                "p i n e a p p l e",
1627                workspace::SendKeystrokes("🍍".to_string()),
1628                Some("vim_mode == insert"),
1629            ),
1630        ])
1631    });
1632    cx.neovim.exec("imap pin 📌").await;
1633    cx.neovim.exec("imap pine 🌲").await;
1634    cx.neovim.exec("imap pineapple 🍍").await;
1635
1636    cx.set_shared_state("ˇ").await;
1637    cx.simulate_shared_keystrokes("i p i n").await;
1638    cx.executor().advance_clock(Duration::from_millis(1000));
1639    cx.run_until_parked();
1640    cx.shared_state().await.assert_eq("📌ˇ");
1641
1642    cx.set_shared_state("ˇ").await;
1643    cx.simulate_shared_keystrokes("i p i n e").await;
1644    cx.executor().advance_clock(Duration::from_millis(1000));
1645    cx.run_until_parked();
1646    cx.shared_state().await.assert_eq("🌲ˇ");
1647
1648    cx.set_shared_state("ˇ").await;
1649    cx.simulate_shared_keystrokes("i p i n e a p p l e").await;
1650    cx.shared_state().await.assert_eq("🍍ˇ");
1651}
1652
1653#[gpui::test]
1654async fn test_remap_recursion(cx: &mut gpui::TestAppContext) {
1655    let mut cx = NeovimBackedTestContext::new(cx).await;
1656    cx.update(|_, cx| {
1657        cx.bind_keys([KeyBinding::new(
1658            "x",
1659            workspace::SendKeystrokes("\" _ x".to_string()),
1660            Some("VimControl"),
1661        )]);
1662        cx.bind_keys([KeyBinding::new(
1663            "y",
1664            workspace::SendKeystrokes("2 x".to_string()),
1665            Some("VimControl"),
1666        )])
1667    });
1668    cx.neovim.exec("noremap x \"_x").await;
1669    cx.neovim.exec("map y 2x").await;
1670
1671    cx.set_shared_state("ˇhello").await;
1672    cx.simulate_shared_keystrokes("d l").await;
1673    cx.shared_clipboard().await.assert_eq("h");
1674    cx.simulate_shared_keystrokes("y").await;
1675    cx.shared_clipboard().await.assert_eq("h");
1676    cx.shared_state().await.assert_eq("ˇlo");
1677}
1678
1679#[gpui::test]
1680async fn test_escape_while_waiting(cx: &mut gpui::TestAppContext) {
1681    let mut cx = NeovimBackedTestContext::new(cx).await;
1682    cx.set_shared_state("ˇhi").await;
1683    cx.simulate_shared_keystrokes("\" + escape x").await;
1684    cx.shared_state().await.assert_eq("ˇi");
1685}
1686
1687#[gpui::test]
1688async fn test_ctrl_w_override(cx: &mut gpui::TestAppContext) {
1689    let mut cx = NeovimBackedTestContext::new(cx).await;
1690    cx.update(|_, cx| {
1691        cx.bind_keys([KeyBinding::new("ctrl-w", DeleteLine, None)]);
1692    });
1693    cx.neovim.exec("map <c-w> D").await;
1694    cx.set_shared_state("ˇhi").await;
1695    cx.simulate_shared_keystrokes("ctrl-w").await;
1696    cx.shared_state().await.assert_eq("ˇ");
1697}
1698
1699#[gpui::test]
1700async fn test_visual_indent_count(cx: &mut gpui::TestAppContext) {
1701    let mut cx = VimTestContext::new(cx, true).await;
1702    cx.set_state("ˇhi", Mode::Normal);
1703    cx.simulate_keystrokes("shift-v 3 >");
1704    cx.assert_state("            ˇhi", Mode::Normal);
1705    cx.simulate_keystrokes("shift-v 2 <");
1706    cx.assert_state("    ˇhi", Mode::Normal);
1707}
1708
1709#[gpui::test]
1710async fn test_record_replay_recursion(cx: &mut gpui::TestAppContext) {
1711    let mut cx = NeovimBackedTestContext::new(cx).await;
1712
1713    cx.set_shared_state("ˇhello world").await;
1714    cx.simulate_shared_keystrokes(">").await;
1715    cx.simulate_shared_keystrokes(".").await;
1716    cx.simulate_shared_keystrokes(".").await;
1717    cx.simulate_shared_keystrokes(".").await;
1718    cx.shared_state().await.assert_eq("ˇhello world");
1719}
1720
1721#[gpui::test]
1722async fn test_blackhole_register(cx: &mut gpui::TestAppContext) {
1723    let mut cx = NeovimBackedTestContext::new(cx).await;
1724
1725    cx.set_shared_state("ˇhello world").await;
1726    cx.simulate_shared_keystrokes("d i w \" _ d a w").await;
1727    cx.simulate_shared_keystrokes("p").await;
1728    cx.shared_state().await.assert_eq("hellˇo");
1729}
1730
1731#[gpui::test]
1732async fn test_sentence_backwards(cx: &mut gpui::TestAppContext) {
1733    let mut cx = NeovimBackedTestContext::new(cx).await;
1734
1735    cx.set_shared_state("one\n\ntwo\nthree\nˇ\nfour").await;
1736    cx.simulate_shared_keystrokes("(").await;
1737    cx.shared_state()
1738        .await
1739        .assert_eq("one\n\nˇtwo\nthree\n\nfour");
1740
1741    cx.set_shared_state("hello.\n\n\nworˇld.").await;
1742    cx.simulate_shared_keystrokes("(").await;
1743    cx.shared_state().await.assert_eq("hello.\n\n\nˇworld.");
1744    cx.simulate_shared_keystrokes("(").await;
1745    cx.shared_state().await.assert_eq("hello.\n\nˇ\nworld.");
1746    cx.simulate_shared_keystrokes("(").await;
1747    cx.shared_state().await.assert_eq("ˇhello.\n\n\nworld.");
1748
1749    cx.set_shared_state("hello. worlˇd.").await;
1750    cx.simulate_shared_keystrokes("(").await;
1751    cx.shared_state().await.assert_eq("hello. ˇworld.");
1752    cx.simulate_shared_keystrokes("(").await;
1753    cx.shared_state().await.assert_eq("ˇhello. world.");
1754
1755    cx.set_shared_state(". helˇlo.").await;
1756    cx.simulate_shared_keystrokes("(").await;
1757    cx.shared_state().await.assert_eq(". ˇhello.");
1758    cx.simulate_shared_keystrokes("(").await;
1759    cx.shared_state().await.assert_eq(". ˇhello.");
1760
1761    cx.set_shared_state(indoc! {
1762        "{
1763            hello_world();
1764        ˇ}"
1765    })
1766    .await;
1767    cx.simulate_shared_keystrokes("(").await;
1768    cx.shared_state().await.assert_eq(indoc! {
1769        "ˇ{
1770            hello_world();
1771        }"
1772    });
1773
1774    cx.set_shared_state(indoc! {
1775        "Hello! World..?
1776
1777        \tHello! World... ˇ"
1778    })
1779    .await;
1780    cx.simulate_shared_keystrokes("(").await;
1781    cx.shared_state().await.assert_eq(indoc! {
1782        "Hello! World..?
1783
1784        \tHello! ˇWorld... "
1785    });
1786    cx.simulate_shared_keystrokes("(").await;
1787    cx.shared_state().await.assert_eq(indoc! {
1788        "Hello! World..?
1789
1790        \tˇHello! World... "
1791    });
1792    cx.simulate_shared_keystrokes("(").await;
1793    cx.shared_state().await.assert_eq(indoc! {
1794        "Hello! World..?
1795        ˇ
1796        \tHello! World... "
1797    });
1798    cx.simulate_shared_keystrokes("(").await;
1799    cx.shared_state().await.assert_eq(indoc! {
1800        "Hello! ˇWorld..?
1801
1802        \tHello! World... "
1803    });
1804}
1805
1806#[gpui::test]
1807async fn test_sentence_forwards(cx: &mut gpui::TestAppContext) {
1808    let mut cx = NeovimBackedTestContext::new(cx).await;
1809
1810    cx.set_shared_state("helˇlo.\n\n\nworld.").await;
1811    cx.simulate_shared_keystrokes(")").await;
1812    cx.shared_state().await.assert_eq("hello.\nˇ\n\nworld.");
1813    cx.simulate_shared_keystrokes(")").await;
1814    cx.shared_state().await.assert_eq("hello.\n\n\nˇworld.");
1815    cx.simulate_shared_keystrokes(")").await;
1816    cx.shared_state().await.assert_eq("hello.\n\n\nworldˇ.");
1817
1818    cx.set_shared_state("helˇlo.\n\n\nworld.").await;
1819}
1820
1821#[gpui::test]
1822async fn test_ctrl_o_visual(cx: &mut gpui::TestAppContext) {
1823    let mut cx = NeovimBackedTestContext::new(cx).await;
1824
1825    cx.set_shared_state("helloˇ world.").await;
1826    cx.simulate_shared_keystrokes("i ctrl-o v b r l").await;
1827    cx.shared_state().await.assert_eq("ˇllllllworld.");
1828    cx.simulate_shared_keystrokes("ctrl-o v f w d").await;
1829    cx.shared_state().await.assert_eq("ˇorld.");
1830}
1831
1832#[gpui::test]
1833async fn test_ctrl_o_position(cx: &mut gpui::TestAppContext) {
1834    let mut cx = NeovimBackedTestContext::new(cx).await;
1835
1836    cx.set_shared_state("helˇlo world.").await;
1837    cx.simulate_shared_keystrokes("i ctrl-o d i w").await;
1838    cx.shared_state().await.assert_eq("ˇ world.");
1839    cx.simulate_shared_keystrokes("ctrl-o p").await;
1840    cx.shared_state().await.assert_eq(" helloˇworld.");
1841}
1842
1843#[gpui::test]
1844async fn test_ctrl_o_dot(cx: &mut gpui::TestAppContext) {
1845    let mut cx = NeovimBackedTestContext::new(cx).await;
1846
1847    cx.set_shared_state("heˇllo world.").await;
1848    cx.simulate_shared_keystrokes("x i ctrl-o .").await;
1849    cx.shared_state().await.assert_eq("heˇo world.");
1850    cx.simulate_shared_keystrokes("l l escape .").await;
1851    cx.shared_state().await.assert_eq("hellˇllo world.");
1852}
1853
1854#[gpui::test]
1855async fn test_folded_multibuffer_excerpts(cx: &mut gpui::TestAppContext) {
1856    VimTestContext::init(cx);
1857    cx.update(|cx| {
1858        VimTestContext::init_keybindings(true, cx);
1859    });
1860    let (editor, cx) = cx.add_window_view(|window, cx| {
1861        let multi_buffer = MultiBuffer::build_multi(
1862            [
1863                ("111\n222\n333\n444\n", vec![Point::row_range(0..2)]),
1864                ("aaa\nbbb\nccc\nddd\n", vec![Point::row_range(0..2)]),
1865                ("AAA\nBBB\nCCC\nDDD\n", vec![Point::row_range(0..2)]),
1866                ("one\ntwo\nthr\nfou\n", vec![Point::row_range(0..2)]),
1867            ],
1868            cx,
1869        );
1870        let mut editor = Editor::new(EditorMode::full(), multi_buffer.clone(), None, window, cx);
1871
1872        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
1873        // fold all but the second buffer, so that we test navigating between two
1874        // adjacent folded buffers, as well as folded buffers at the start and
1875        // end the multibuffer
1876        editor.fold_buffer(buffer_ids[0], cx);
1877        editor.fold_buffer(buffer_ids[2], cx);
1878        editor.fold_buffer(buffer_ids[3], cx);
1879
1880        editor
1881    });
1882    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
1883
1884    cx.assert_excerpts_with_selections(indoc! {"
1885        [EXCERPT]
1886        ˇ[FOLDED]
1887        [EXCERPT]
1888        aaa
1889        bbb
1890        [EXCERPT]
1891        [FOLDED]
1892        [EXCERPT]
1893        [FOLDED]
1894        "
1895    });
1896    cx.simulate_keystroke("j");
1897    cx.assert_excerpts_with_selections(indoc! {"
1898        [EXCERPT]
1899        [FOLDED]
1900        [EXCERPT]
1901        ˇaaa
1902        bbb
1903        [EXCERPT]
1904        [FOLDED]
1905        [EXCERPT]
1906        [FOLDED]
1907        "
1908    });
1909    cx.simulate_keystroke("j");
1910    cx.simulate_keystroke("j");
1911    cx.assert_excerpts_with_selections(indoc! {"
1912        [EXCERPT]
1913        [FOLDED]
1914        [EXCERPT]
1915        aaa
1916        bbb
1917        ˇ[EXCERPT]
1918        [FOLDED]
1919        [EXCERPT]
1920        [FOLDED]
1921        "
1922    });
1923    cx.simulate_keystroke("j");
1924    cx.assert_excerpts_with_selections(indoc! {"
1925        [EXCERPT]
1926        [FOLDED]
1927        [EXCERPT]
1928        aaa
1929        bbb
1930        [EXCERPT]
1931        ˇ[FOLDED]
1932        [EXCERPT]
1933        [FOLDED]
1934        "
1935    });
1936    cx.simulate_keystroke("j");
1937    cx.assert_excerpts_with_selections(indoc! {"
1938        [EXCERPT]
1939        [FOLDED]
1940        [EXCERPT]
1941        aaa
1942        bbb
1943        [EXCERPT]
1944        [FOLDED]
1945        [EXCERPT]
1946        ˇ[FOLDED]
1947        "
1948    });
1949    cx.simulate_keystroke("k");
1950    cx.assert_excerpts_with_selections(indoc! {"
1951        [EXCERPT]
1952        [FOLDED]
1953        [EXCERPT]
1954        aaa
1955        bbb
1956        [EXCERPT]
1957        ˇ[FOLDED]
1958        [EXCERPT]
1959        [FOLDED]
1960        "
1961    });
1962    cx.simulate_keystroke("k");
1963    cx.simulate_keystroke("k");
1964    cx.simulate_keystroke("k");
1965    cx.assert_excerpts_with_selections(indoc! {"
1966        [EXCERPT]
1967        [FOLDED]
1968        [EXCERPT]
1969        ˇaaa
1970        bbb
1971        [EXCERPT]
1972        [FOLDED]
1973        [EXCERPT]
1974        [FOLDED]
1975        "
1976    });
1977    cx.simulate_keystroke("k");
1978    cx.assert_excerpts_with_selections(indoc! {"
1979        [EXCERPT]
1980        ˇ[FOLDED]
1981        [EXCERPT]
1982        aaa
1983        bbb
1984        [EXCERPT]
1985        [FOLDED]
1986        [EXCERPT]
1987        [FOLDED]
1988        "
1989    });
1990    cx.simulate_keystroke("shift-g");
1991    cx.assert_excerpts_with_selections(indoc! {"
1992        [EXCERPT]
1993        [FOLDED]
1994        [EXCERPT]
1995        aaa
1996        bbb
1997        [EXCERPT]
1998        [FOLDED]
1999        [EXCERPT]
2000        ˇ[FOLDED]
2001        "
2002    });
2003    cx.simulate_keystrokes("g g");
2004    cx.assert_excerpts_with_selections(indoc! {"
2005        [EXCERPT]
2006        ˇ[FOLDED]
2007        [EXCERPT]
2008        aaa
2009        bbb
2010        [EXCERPT]
2011        [FOLDED]
2012        [EXCERPT]
2013        [FOLDED]
2014        "
2015    });
2016    cx.update_editor(|editor, _, cx| {
2017        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
2018        editor.fold_buffer(buffer_ids[1], cx);
2019    });
2020
2021    cx.assert_excerpts_with_selections(indoc! {"
2022        [EXCERPT]
2023        ˇ[FOLDED]
2024        [EXCERPT]
2025        [FOLDED]
2026        [EXCERPT]
2027        [FOLDED]
2028        [EXCERPT]
2029        [FOLDED]
2030        "
2031    });
2032    cx.simulate_keystrokes("2 j");
2033    cx.assert_excerpts_with_selections(indoc! {"
2034        [EXCERPT]
2035        [FOLDED]
2036        [EXCERPT]
2037        [FOLDED]
2038        [EXCERPT]
2039        ˇ[FOLDED]
2040        [EXCERPT]
2041        [FOLDED]
2042        "
2043    });
2044}
2045
2046#[gpui::test]
2047async fn test_delete_paragraph_motion(cx: &mut gpui::TestAppContext) {
2048    let mut cx = NeovimBackedTestContext::new(cx).await;
2049    cx.set_shared_state(indoc! {
2050        "ˇhello world.
2051
2052        hello world.
2053        "
2054    })
2055    .await;
2056    cx.simulate_shared_keystrokes("y }").await;
2057    cx.shared_clipboard().await.assert_eq("hello world.\n");
2058    cx.simulate_shared_keystrokes("d }").await;
2059    cx.shared_state().await.assert_eq("ˇ\nhello world.\n");
2060    cx.shared_clipboard().await.assert_eq("hello world.\n");
2061
2062    cx.set_shared_state(indoc! {
2063        "helˇlo world.
2064
2065            hello world.
2066            "
2067    })
2068    .await;
2069    cx.simulate_shared_keystrokes("y }").await;
2070    cx.shared_clipboard().await.assert_eq("lo world.");
2071    cx.simulate_shared_keystrokes("d }").await;
2072    cx.shared_state().await.assert_eq("heˇl\n\nhello world.\n");
2073    cx.shared_clipboard().await.assert_eq("lo world.");
2074}
2075
2076#[gpui::test]
2077async fn test_delete_unmatched_brace(cx: &mut gpui::TestAppContext) {
2078    let mut cx = NeovimBackedTestContext::new(cx).await;
2079    cx.set_shared_state(indoc! {
2080        "fn o(wow: i32) {
2081          othˇ(wow)
2082          oth(wow)
2083        }
2084        "
2085    })
2086    .await;
2087    cx.simulate_shared_keystrokes("d ] }").await;
2088    cx.shared_state().await.assert_eq(indoc! {
2089        "fn o(wow: i32) {
2090          otˇh
2091        }
2092        "
2093    });
2094    cx.shared_clipboard().await.assert_eq("(wow)\n  oth(wow)");
2095    cx.set_shared_state(indoc! {
2096        "fn o(wow: i32) {
2097          ˇoth(wow)
2098          oth(wow)
2099        }
2100        "
2101    })
2102    .await;
2103    cx.simulate_shared_keystrokes("d ] }").await;
2104    cx.shared_state().await.assert_eq(indoc! {
2105        "fn o(wow: i32) {
2106         ˇ}
2107        "
2108    });
2109    cx.shared_clipboard()
2110        .await
2111        .assert_eq("  oth(wow)\n  oth(wow)\n");
2112}
2113
2114#[gpui::test]
2115async fn test_paragraph_multi_delete(cx: &mut gpui::TestAppContext) {
2116    let mut cx = NeovimBackedTestContext::new(cx).await;
2117    cx.set_shared_state(indoc! {
2118        "
2119        Emacs is
2120        ˇa great
2121
2122        operating system
2123
2124        all it lacks
2125        is a
2126
2127        decent text editor
2128        "
2129    })
2130    .await;
2131
2132    cx.simulate_shared_keystrokes("2 d a p").await;
2133    cx.shared_state().await.assert_eq(indoc! {
2134        "
2135        ˇall it lacks
2136        is a
2137
2138        decent text editor
2139        "
2140    });
2141
2142    cx.simulate_shared_keystrokes("d a p").await;
2143    cx.shared_clipboard()
2144        .await
2145        .assert_eq("all it lacks\nis a\n\n");
2146
2147    //reset to initial state
2148    cx.simulate_shared_keystrokes("2 u").await;
2149
2150    cx.simulate_shared_keystrokes("4 d a p").await;
2151    cx.shared_state().await.assert_eq(indoc! {"ˇ"});
2152}
2153
2154#[gpui::test]
2155async fn test_multi_cursor_replay(cx: &mut gpui::TestAppContext) {
2156    let mut cx = VimTestContext::new(cx, true).await;
2157    cx.set_state(
2158        indoc! {
2159            "
2160        oˇne one one
2161
2162        two two two
2163        "
2164        },
2165        Mode::Normal,
2166    );
2167
2168    cx.simulate_keystrokes("3 g l s wow escape escape");
2169    cx.assert_state(
2170        indoc! {
2171            "
2172        woˇw wow wow
2173
2174        two two two
2175        "
2176        },
2177        Mode::Normal,
2178    );
2179
2180    cx.simulate_keystrokes("2 j 3 g l .");
2181    cx.assert_state(
2182        indoc! {
2183            "
2184        wow wow wow
2185
2186        woˇw woˇw woˇw
2187        "
2188        },
2189        Mode::Normal,
2190    );
2191}