mod neovim_backed_test_context;mod neovim_connection;mod vim_test_context; use std::{sync::Arc, time::Duration}; use collections::HashMap;use command_palette::CommandPalette;use editor::{ AnchorRangeExt, DisplayPoint, Editor, EditorMode, MultiBuffer, MultiBufferOffset, actions::{DeleteLine, WrapSelectionsInTag}, code_context_menus::CodeContextMenu, display_map::DisplayRow, test::editor_test_context::EditorTestContext,};use futures::StreamExt;use gpui::{KeyBinding, Modifiers, MouseButton, TestAppContext, px};use itertools::Itertools;use language::{CursorShape, Language, LanguageConfig, Point};pub use neovim_backed_test_context::*;use settings::SettingsStore;use ui::Pixels;use util::{path, test::marked_text_ranges};pub use vim_test_context::*; use gpui::VisualTestContext;use indoc::indoc;use project::FakeFs;use search::BufferSearchBar;use search::{ProjectSearchView, project_search};use serde_json::json;use workspace::{DeploySearch, MultiWorkspace}; use crate::{PushSneak, PushSneakBackward, VimAddon, insert::NormalBefore, motion, state::Mode}; use util_macros::perf; #[perf]#[gpui::test]async fn test_initially_disabled(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, false).await; cx.simulate_keystrokes("h j k l"); cx.assert_editor_state("hjklˇ");} #[perf]#[gpui::test]async fn test_neovim(cx: &mut gpui::TestAppContext) { let mut cx = NeovimBackedTestContext::new(cx).await; cx.simulate_shared_keystrokes("i").await; cx.shared_state().await.assert_matches(); cx.simulate_shared_keystrokes("shift-t e s t space t e s t escape 0 d w") .await; cx.shared_state().await.assert_matches(); cx.assert_editor_state("ˇtest");} #[perf]#[gpui::test]async fn test_toggle_through_settings(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, true).await; cx.simulate_keystrokes("i"); assert_eq!(cx.mode(), Mode::Insert); // Editor acts as though vim is disabled cx.disable_vim(); cx.simulate_keystrokes("h j k l"); cx.assert_editor_state("hjklˇ"); // Selections aren't changed if editor is blurred but vim-mode is still disabled. cx.cx.set_state("«hjklˇ»"); cx.assert_editor_state("«hjklˇ»"); cx.update_editor(|_, window, _cx| window.blur()); cx.assert_editor_state("«hjklˇ»"); cx.update_editor(|_, window, cx| cx.focus_self(window)); cx.assert_editor_state("«hjklˇ»"); // Enabling dynamically sets vim mode again and restores normal mode cx.enable_vim(); assert_eq!(cx.mode(), Mode::Normal); cx.simulate_keystrokes("h h h l"); assert_eq!(cx.buffer_text(), "hjkl".to_owned()); cx.assert_editor_state("hˇjkl"); cx.simulate_keystrokes("i T e s t"); cx.assert_editor_state("hTestˇjkl"); // Disabling and enabling resets to normal mode assert_eq!(cx.mode(), Mode::Insert); cx.disable_vim(); cx.enable_vim(); assert_eq!(cx.mode(), Mode::Normal);} #[perf]#[gpui::test]async fn test_vim_linked_edits_delete_x(app_cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new_html(app_cx).await; cx.set_state("<diˇv></div>", Mode::Normal); cx.update_editor(|editor, _window, cx| { editor .set_linked_edit_ranges_for_testing( vec![( Point::new(0, 1)..Point::new(0, 4), vec![Point::new(0, 7)..Point::new(0, 10)], )], cx, ) .expect("linked edit ranges should be set"); }); cx.simulate_keystrokes("x"); cx.assert_editor_state("<diˇ></di>");} #[perf]#[gpui::test]async fn test_vim_linked_edits_change_iw(app_cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new_html(app_cx).await; cx.set_state("<diˇv></div>", Mode::Normal); cx.update_editor(|editor, _window, cx| { editor .set_linked_edit_ranges_for_testing( vec![( Point::new(0, 1)..Point::new(0, 4), vec![Point::new(0, 7)..Point::new(0, 10)], )], cx, ) .expect("linked edit ranges should be set"); }); cx.simulate_keystrokes("c i w s p a n escape"); cx.assert_editor_state("<spaˇn></span>");} #[perf]#[gpui::test]async fn test_vim_linked_edits_substitute_s(app_cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new_html(app_cx).await; cx.set_state("<diˇv></div>", Mode::Normal); cx.update_editor(|editor, _window, cx| { editor .set_linked_edit_ranges_for_testing( vec![( Point::new(0, 1)..Point::new(0, 4), vec![Point::new(0, 7)..Point::new(0, 10)], )], cx, ) .expect("linked edit ranges should be set"); }); cx.simulate_keystrokes("s s p a n escape"); cx.assert_editor_state("<dispaˇn></dispan>");} #[perf]#[gpui::test]async fn test_vim_linked_edits_visual_change(app_cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new_html(app_cx).await; cx.set_state("<diˇv></div>", Mode::Normal); cx.update_editor(|editor, _window, cx| { editor .set_linked_edit_ranges_for_testing( vec![( Point::new(0, 1)..Point::new(0, 4), vec![Point::new(0, 7)..Point::new(0, 10)], )], cx, ) .expect("linked edit ranges should be set"); }); // Visual change routes through substitute; visual `s` shares this path. cx.simulate_keystrokes("v i w c s p a n escape"); cx.assert_editor_state("<spaˇn></span>");} #[perf]#[gpui::test]async fn test_vim_linked_edits_visual_substitute_s(app_cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new_html(app_cx).await; cx.set_state("<diˇv></div>", Mode::Normal); cx.update_editor(|editor, _window, cx| { editor .set_linked_edit_ranges_for_testing( vec![( Point::new(0, 1)..Point::new(0, 4), vec![Point::new(0, 7)..Point::new(0, 10)], )], cx, ) .expect("linked edit ranges should be set"); }); cx.simulate_keystrokes("v i w s s p a n escape"); cx.assert_editor_state("<spaˇn></span>");} #[perf]#[gpui::test]async fn test_cancel_selection(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, true).await; cx.set_state( indoc! {"The quick brown fox juˇmps over the lazy dog"}, Mode::Normal, ); // jumps cx.simulate_keystrokes("v l l"); cx.assert_editor_state("The quick brown fox ju«mpsˇ» over the lazy dog"); cx.simulate_keystrokes("escape"); cx.assert_editor_state("The quick brown fox jumpˇs over the lazy dog"); // go back to the same selection state cx.simulate_keystrokes("v h h"); cx.assert_editor_state("The quick brown fox ju«ˇmps» over the lazy dog"); // Ctrl-[ should behave like Esc cx.simulate_keystrokes("ctrl-["); cx.assert_editor_state("The quick brown fox juˇmps over the lazy dog");} #[perf]#[gpui::test]async fn test_buffer_search(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, true).await; cx.set_state( indoc! {" The quick brown fox juˇmps over the lazy dog"}, Mode::Normal, ); cx.simulate_keystrokes("/"); let search_bar = cx.workspace(|workspace, _, cx| { workspace .active_pane() .read(cx) .toolbar() .read(cx) .item_of_type::<BufferSearchBar>() .expect("Buffer search bar should be deployed") }); cx.update_entity(search_bar, |bar, _, cx| { assert_eq!(bar.query(cx), ""); })} #[perf]#[gpui::test]async fn test_count_down(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, true).await; cx.set_state(indoc! {"aˇa\nbb\ncc\ndd\nee"}, Mode::Normal); cx.simulate_keystrokes("2 down"); cx.assert_editor_state("aa\nbb\ncˇc\ndd\nee"); cx.simulate_keystrokes("9 down"); cx.assert_editor_state("aa\nbb\ncc\ndd\neˇe");} #[perf]#[gpui::test]async fn test_end_of_document_710(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, true).await; // goes to end by default cx.set_state(indoc! {"aˇa\nbb\ncc"}, Mode::Normal); cx.simulate_keystrokes("shift-g"); cx.assert_editor_state("aa\nbb\ncˇc"); // can go to line 1 (https://github.com/zed-industries/zed/issues/5812) cx.simulate_keystrokes("1 shift-g"); cx.assert_editor_state("aˇa\nbb\ncc");} #[perf]#[gpui::test]async fn test_end_of_line_with_times(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, true).await; // goes to current line end cx.set_state(indoc! {"ˇaa\nbb\ncc"}, Mode::Normal); cx.simulate_keystrokes("$"); cx.assert_editor_state("aˇa\nbb\ncc"); // goes to next line end cx.simulate_keystrokes("2 $"); cx.assert_editor_state("aa\nbˇb\ncc"); // try to exceed the final line. cx.simulate_keystrokes("4 $"); cx.assert_editor_state("aa\nbb\ncˇc");} #[perf]#[gpui::test]async fn test_indent_outdent(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, true).await; // works in normal mode cx.set_state(indoc! {"aa\nbˇb\ncc"}, Mode::Normal); cx.simulate_keystrokes("> >"); cx.assert_editor_state("aa\n bˇb\ncc"); cx.simulate_keystrokes("< <"); cx.assert_editor_state("aa\nbˇb\ncc"); // works in visual mode cx.simulate_keystrokes("shift-v down >"); cx.assert_editor_state("aa\n bˇb\n cc"); // works as operator cx.set_state("aa\nbˇb\ncc\n", Mode::Normal); cx.simulate_keystrokes("> j"); cx.assert_editor_state("aa\n bˇb\n cc\n"); cx.simulate_keystrokes("< k"); cx.assert_editor_state("aa\nbˇb\n cc\n"); cx.simulate_keystrokes("> i p"); cx.assert_editor_state(" aa\n bˇb\n cc\n"); cx.simulate_keystrokes("< i p"); cx.assert_editor_state("aa\nbˇb\n cc\n"); cx.simulate_keystrokes("< i p"); cx.assert_editor_state("aa\nbˇb\ncc\n"); cx.set_state("ˇaa\nbb\ncc\n", Mode::Normal); cx.simulate_keystrokes("> 2 j"); cx.assert_editor_state(" ˇaa\n bb\n cc\n"); cx.set_state("aa\nbb\nˇcc\n", Mode::Normal); cx.simulate_keystrokes("> 2 k"); cx.assert_editor_state(" aa\n bb\n ˇcc\n"); // works with repeat cx.set_state("a\nb\nccˇc\n", Mode::Normal); cx.simulate_keystrokes("> 2 k"); cx.assert_editor_state(" a\n b\n ccˇc\n"); cx.simulate_keystrokes("."); cx.assert_editor_state(" a\n b\n ccˇc\n"); cx.simulate_keystrokes("v k <"); cx.assert_editor_state(" a\n bˇ\n ccc\n"); cx.simulate_keystrokes("."); cx.assert_editor_state(" a\nbˇ\nccc\n");} #[perf]#[gpui::test]async fn test_escape_command_palette(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, true).await; cx.set_state("aˇbc\n", Mode::Normal); cx.simulate_keystrokes("i cmd-shift-p"); assert!( cx.workspace(|workspace, _, cx| workspace.active_modal::<CommandPalette>(cx).is_some()) ); cx.simulate_keystrokes("escape"); cx.run_until_parked(); assert!( !cx.workspace(|workspace, _, cx| workspace.active_modal::<CommandPalette>(cx).is_some()) ); cx.assert_state("aˇbc\n", Mode::Insert);} #[perf]#[gpui::test]async fn test_escape_cancels(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, true).await; cx.set_state("aˇbˇc", Mode::Normal); cx.simulate_keystrokes("escape"); cx.assert_state("aˇbc", Mode::Normal);} #[perf]#[gpui::test]async fn test_selection_on_search(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, true).await; cx.set_state(indoc! {"aa\nbˇb\ncc\ncc\ncc\n"}, Mode::Normal); cx.simulate_keystrokes("/ c c"); let search_bar = cx.workspace(|workspace, _, cx| { workspace .active_pane() .read(cx) .toolbar() .read(cx) .item_of_type::<BufferSearchBar>() .expect("Buffer search bar should be deployed") }); cx.update_entity(search_bar, |bar, _, cx| { assert_eq!(bar.query(cx), "cc"); }); cx.update_editor(|editor, window, cx| { let highlights = editor.all_text_background_highlights(window, cx); assert_eq!(3, highlights.len()); assert_eq!( DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 2), highlights[0].0 ) }); cx.simulate_keystrokes("enter"); cx.assert_state(indoc! {"aa\nbb\nˇcc\ncc\ncc\n"}, Mode::Normal); cx.simulate_keystrokes("n"); cx.assert_state(indoc! {"aa\nbb\ncc\nˇcc\ncc\n"}, Mode::Normal); cx.simulate_keystrokes("shift-n"); cx.assert_state(indoc! {"aa\nbb\nˇcc\ncc\ncc\n"}, Mode::Normal);} #[perf]#[gpui::test]async fn test_word_characters(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new_typescript(cx).await; cx.set_state( indoc! { " class A { #ˇgoop = 99; $ˇgoop () { return this.#gˇoop }; }; console.log(new A().$gooˇp()) "}, Mode::Normal, ); cx.simulate_keystrokes("v i w"); cx.assert_state( indoc! {" class A { «#goopˇ» = 99; «$goopˇ» () { return this.«#goopˇ» }; }; console.log(new A().«$goopˇ»()) "}, Mode::Visual, )} #[perf]#[gpui::test]async fn test_kebab_case(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new_html(cx).await; cx.set_state( indoc! { r#" <div><a class="bg-rˇed"></a></div> "#}, Mode::Normal, ); cx.simulate_keystrokes("v i w"); cx.assert_state( indoc! { r#" <div><a class="bg-«redˇ»"></a></div> "# }, Mode::Visual, )} #[perf]#[gpui::test]async fn test_join_lines(cx: &mut gpui::TestAppContext) { let mut cx = NeovimBackedTestContext::new(cx).await; cx.set_shared_state(indoc! {" ˇone two three four five six "}) .await; cx.simulate_shared_keystrokes("shift-j").await; cx.shared_state().await.assert_eq(indoc! {" oneˇ two three four five six "}); cx.simulate_shared_keystrokes("3 shift-j").await; cx.shared_state().await.assert_eq(indoc! {" one two threeˇ four five six "}); Lines 1–500 of 2824. Load more
mod neovim_backed_test_context; mod neovim_connection; mod vim_test_context; use std::{sync::Arc, time::Duration}; use collections::HashMap; use command_palette::CommandPalette; use editor::{ AnchorRangeExt, DisplayPoint, Editor, EditorMode, MultiBuffer, MultiBufferOffset, actions::{DeleteLine, WrapSelectionsInTag}, code_context_menus::CodeContextMenu, display_map::DisplayRow, test::editor_test_context::EditorTestContext, }; use futures::StreamExt; use gpui::{KeyBinding, Modifiers, MouseButton, TestAppContext, px}; use itertools::Itertools; use language::{CursorShape, Language, LanguageConfig, Point}; pub use neovim_backed_test_context::*; use settings::SettingsStore; use ui::Pixels; use util::{path, test::marked_text_ranges}; pub use vim_test_context::*; use gpui::VisualTestContext; use indoc::indoc; use project::FakeFs; use search::BufferSearchBar; use search::{ProjectSearchView, project_search}; use serde_json::json; use workspace::{DeploySearch, MultiWorkspace}; use crate::{PushSneak, PushSneakBackward, VimAddon, insert::NormalBefore, motion, state::Mode}; use util_macros::perf; #[perf] #[gpui::test] async fn test_initially_disabled(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, false).await; cx.simulate_keystrokes("h j k l"); cx.assert_editor_state("hjklˇ"); } #[perf] #[gpui::test] async fn test_neovim(cx: &mut gpui::TestAppContext) { let mut cx = NeovimBackedTestContext::new(cx).await; cx.simulate_shared_keystrokes("i").await; cx.shared_state().await.assert_matches(); cx.simulate_shared_keystrokes("shift-t e s t space t e s t escape 0 d w") .await; cx.shared_state().await.assert_matches(); cx.assert_editor_state("ˇtest"); } #[perf] #[gpui::test] async fn test_toggle_through_settings(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, true).await; cx.simulate_keystrokes("i"); assert_eq!(cx.mode(), Mode::Insert); // Editor acts as though vim is disabled cx.disable_vim(); cx.simulate_keystrokes("h j k l"); cx.assert_editor_state("hjklˇ"); // Selections aren't changed if editor is blurred but vim-mode is still disabled. cx.cx.set_state("«hjklˇ»"); cx.assert_editor_state("«hjklˇ»"); cx.update_editor(|_, window, _cx| window.blur()); cx.assert_editor_state("«hjklˇ»"); cx.update_editor(|_, window, cx| cx.focus_self(window)); cx.assert_editor_state("«hjklˇ»"); // Enabling dynamically sets vim mode again and restores normal mode cx.enable_vim(); assert_eq!(cx.mode(), Mode::Normal); cx.simulate_keystrokes("h h h l"); assert_eq!(cx.buffer_text(), "hjkl".to_owned()); cx.assert_editor_state("hˇjkl"); cx.simulate_keystrokes("i T e s t"); cx.assert_editor_state("hTestˇjkl"); // Disabling and enabling resets to normal mode assert_eq!(cx.mode(), Mode::Insert); cx.disable_vim(); cx.enable_vim(); assert_eq!(cx.mode(), Mode::Normal); } #[perf] #[gpui::test] async fn test_vim_linked_edits_delete_x(app_cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new_html(app_cx).await; cx.set_state("<diˇv></div>", Mode::Normal); cx.update_editor(|editor, _window, cx| { editor .set_linked_edit_ranges_for_testing( vec![( Point::new(0, 1)..Point::new(0, 4), vec![Point::new(0, 7)..Point::new(0, 10)], )], cx, ) .expect("linked edit ranges should be set"); }); cx.simulate_keystrokes("x"); cx.assert_editor_state("<diˇ></di>"); } #[perf] #[gpui::test] async fn test_vim_linked_edits_change_iw(app_cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new_html(app_cx).await; cx.set_state("<diˇv></div>", Mode::Normal); cx.update_editor(|editor, _window, cx| { editor .set_linked_edit_ranges_for_testing( vec![( Point::new(0, 1)..Point::new(0, 4), vec![Point::new(0, 7)..Point::new(0, 10)], )], cx, ) .expect("linked edit ranges should be set"); }); cx.simulate_keystrokes("c i w s p a n escape"); cx.assert_editor_state("<spaˇn></span>"); } #[perf] #[gpui::test] async fn test_vim_linked_edits_substitute_s(app_cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new_html(app_cx).await; cx.set_state("<diˇv></div>", Mode::Normal); cx.update_editor(|editor, _window, cx| { editor .set_linked_edit_ranges_for_testing( vec![( Point::new(0, 1)..Point::new(0, 4), vec![Point::new(0, 7)..Point::new(0, 10)], )], cx, ) .expect("linked edit ranges should be set"); }); cx.simulate_keystrokes("s s p a n escape"); cx.assert_editor_state("<dispaˇn></dispan>"); } #[perf] #[gpui::test] async fn test_vim_linked_edits_visual_change(app_cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new_html(app_cx).await; cx.set_state("<diˇv></div>", Mode::Normal); cx.update_editor(|editor, _window, cx| { editor .set_linked_edit_ranges_for_testing( vec![( Point::new(0, 1)..Point::new(0, 4), vec![Point::new(0, 7)..Point::new(0, 10)], )], cx, ) .expect("linked edit ranges should be set"); }); // Visual change routes through substitute; visual `s` shares this path. cx.simulate_keystrokes("v i w c s p a n escape"); cx.assert_editor_state("<spaˇn></span>"); } #[perf] #[gpui::test] async fn test_vim_linked_edits_visual_substitute_s(app_cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new_html(app_cx).await; cx.set_state("<diˇv></div>", Mode::Normal); cx.update_editor(|editor, _window, cx| { editor .set_linked_edit_ranges_for_testing( vec![( Point::new(0, 1)..Point::new(0, 4), vec![Point::new(0, 7)..Point::new(0, 10)], )], cx, ) .expect("linked edit ranges should be set"); }); cx.simulate_keystrokes("v i w s s p a n escape"); cx.assert_editor_state("<spaˇn></span>"); } #[perf] #[gpui::test] async fn test_cancel_selection(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, true).await; cx.set_state( indoc! {"The quick brown fox juˇmps over the lazy dog"}, Mode::Normal, ); // jumps cx.simulate_keystrokes("v l l"); cx.assert_editor_state("The quick brown fox ju«mpsˇ» over the lazy dog"); cx.simulate_keystrokes("escape"); cx.assert_editor_state("The quick brown fox jumpˇs over the lazy dog"); // go back to the same selection state cx.simulate_keystrokes("v h h"); cx.assert_editor_state("The quick brown fox ju«ˇmps» over the lazy dog"); // Ctrl-[ should behave like Esc cx.simulate_keystrokes("ctrl-["); cx.assert_editor_state("The quick brown fox juˇmps over the lazy dog"); } #[perf] #[gpui::test] async fn test_buffer_search(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, true).await; cx.set_state( indoc! {" The quick brown fox juˇmps over the lazy dog"}, Mode::Normal, ); cx.simulate_keystrokes("/"); let search_bar = cx.workspace(|workspace, _, cx| { workspace .active_pane() .read(cx) .toolbar() .read(cx) .item_of_type::<BufferSearchBar>() .expect("Buffer search bar should be deployed") }); cx.update_entity(search_bar, |bar, _, cx| { assert_eq!(bar.query(cx), ""); }) } #[perf] #[gpui::test] async fn test_count_down(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, true).await; cx.set_state(indoc! {"aˇa\nbb\ncc\ndd\nee"}, Mode::Normal); cx.simulate_keystrokes("2 down"); cx.assert_editor_state("aa\nbb\ncˇc\ndd\nee"); cx.simulate_keystrokes("9 down"); cx.assert_editor_state("aa\nbb\ncc\ndd\neˇe"); } #[perf] #[gpui::test] async fn test_end_of_document_710(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, true).await; // goes to end by default cx.set_state(indoc! {"aˇa\nbb\ncc"}, Mode::Normal); cx.simulate_keystrokes("shift-g"); cx.assert_editor_state("aa\nbb\ncˇc"); // can go to line 1 (https://github.com/zed-industries/zed/issues/5812) cx.simulate_keystrokes("1 shift-g"); cx.assert_editor_state("aˇa\nbb\ncc"); } #[perf] #[gpui::test] async fn test_end_of_line_with_times(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, true).await; // goes to current line end cx.set_state(indoc! {"ˇaa\nbb\ncc"}, Mode::Normal); cx.simulate_keystrokes("$"); cx.assert_editor_state("aˇa\nbb\ncc"); // goes to next line end cx.simulate_keystrokes("2 $"); cx.assert_editor_state("aa\nbˇb\ncc"); // try to exceed the final line. cx.simulate_keystrokes("4 $"); cx.assert_editor_state("aa\nbb\ncˇc"); } #[perf] #[gpui::test] async fn test_indent_outdent(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, true).await; // works in normal mode cx.set_state(indoc! {"aa\nbˇb\ncc"}, Mode::Normal); cx.simulate_keystrokes("> >"); cx.assert_editor_state("aa\n bˇb\ncc"); cx.simulate_keystrokes("< <"); cx.assert_editor_state("aa\nbˇb\ncc"); // works in visual mode cx.simulate_keystrokes("shift-v down >"); cx.assert_editor_state("aa\n bˇb\n cc"); // works as operator cx.set_state("aa\nbˇb\ncc\n", Mode::Normal); cx.simulate_keystrokes("> j"); cx.assert_editor_state("aa\n bˇb\n cc\n"); cx.simulate_keystrokes("< k"); cx.assert_editor_state("aa\nbˇb\n cc\n"); cx.simulate_keystrokes("> i p"); cx.assert_editor_state(" aa\n bˇb\n cc\n"); cx.simulate_keystrokes("< i p"); cx.assert_editor_state("aa\nbˇb\n cc\n"); cx.simulate_keystrokes("< i p"); cx.assert_editor_state("aa\nbˇb\ncc\n"); cx.set_state("ˇaa\nbb\ncc\n", Mode::Normal); cx.simulate_keystrokes("> 2 j"); cx.assert_editor_state(" ˇaa\n bb\n cc\n"); cx.set_state("aa\nbb\nˇcc\n", Mode::Normal); cx.simulate_keystrokes("> 2 k"); cx.assert_editor_state(" aa\n bb\n ˇcc\n"); // works with repeat cx.set_state("a\nb\nccˇc\n", Mode::Normal); cx.simulate_keystrokes("> 2 k"); cx.assert_editor_state(" a\n b\n ccˇc\n"); cx.simulate_keystrokes("."); cx.assert_editor_state(" a\n b\n ccˇc\n"); cx.simulate_keystrokes("v k <"); cx.assert_editor_state(" a\n bˇ\n ccc\n"); cx.simulate_keystrokes("."); cx.assert_editor_state(" a\nbˇ\nccc\n"); } #[perf] #[gpui::test] async fn test_escape_command_palette(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, true).await; cx.set_state("aˇbc\n", Mode::Normal); cx.simulate_keystrokes("i cmd-shift-p"); assert!( cx.workspace(|workspace, _, cx| workspace.active_modal::<CommandPalette>(cx).is_some()) ); cx.simulate_keystrokes("escape"); cx.run_until_parked(); assert!( !cx.workspace(|workspace, _, cx| workspace.active_modal::<CommandPalette>(cx).is_some()) ); cx.assert_state("aˇbc\n", Mode::Insert); } #[perf] #[gpui::test] async fn test_escape_cancels(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, true).await; cx.set_state("aˇbˇc", Mode::Normal); cx.simulate_keystrokes("escape"); cx.assert_state("aˇbc", Mode::Normal); } #[perf] #[gpui::test] async fn test_selection_on_search(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, true).await; cx.set_state(indoc! {"aa\nbˇb\ncc\ncc\ncc\n"}, Mode::Normal); cx.simulate_keystrokes("/ c c"); let search_bar = cx.workspace(|workspace, _, cx| { workspace .active_pane() .read(cx) .toolbar() .read(cx) .item_of_type::<BufferSearchBar>() .expect("Buffer search bar should be deployed") }); cx.update_entity(search_bar, |bar, _, cx| { assert_eq!(bar.query(cx), "cc"); }); cx.update_editor(|editor, window, cx| { let highlights = editor.all_text_background_highlights(window, cx); assert_eq!(3, highlights.len()); assert_eq!( DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 2), highlights[0].0 ) }); cx.simulate_keystrokes("enter"); cx.assert_state(indoc! {"aa\nbb\nˇcc\ncc\ncc\n"}, Mode::Normal); cx.simulate_keystrokes("n"); cx.assert_state(indoc! {"aa\nbb\ncc\nˇcc\ncc\n"}, Mode::Normal); cx.simulate_keystrokes("shift-n"); cx.assert_state(indoc! {"aa\nbb\nˇcc\ncc\ncc\n"}, Mode::Normal); } #[perf] #[gpui::test] async fn test_word_characters(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new_typescript(cx).await; cx.set_state( indoc! { " class A { #ˇgoop = 99; $ˇgoop () { return this.#gˇoop }; }; console.log(new A().$gooˇp()) "}, Mode::Normal, ); cx.simulate_keystrokes("v i w"); cx.assert_state( indoc! {" class A { «#goopˇ» = 99; «$goopˇ» () { return this.«#goopˇ» }; }; console.log(new A().«$goopˇ»()) "}, Mode::Visual, ) } #[perf] #[gpui::test] async fn test_kebab_case(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new_html(cx).await; cx.set_state( indoc! { r#" <div><a class="bg-rˇed"></a></div> "#}, Mode::Normal, ); cx.simulate_keystrokes("v i w"); cx.assert_state( indoc! { r#" <div><a class="bg-«redˇ»"></a></div> "# }, Mode::Visual, ) } #[perf] #[gpui::test] async fn test_join_lines(cx: &mut gpui::TestAppContext) { let mut cx = NeovimBackedTestContext::new(cx).await; cx.set_shared_state(indoc! {" ˇone two three four five six "}) .await; cx.simulate_shared_keystrokes("shift-j").await; cx.shared_state().await.assert_eq(indoc! {" oneˇ two three four five six "}); cx.simulate_shared_keystrokes("3 shift-j").await; cx.shared_state().await.assert_eq(indoc! {" one two threeˇ four five six "});