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