diff --git a/crates/gpui2/src/key_dispatch.rs b/crates/gpui2/src/key_dispatch.rs index 6c8c19aa65a75f7de99ff64e98175685fd47a9b0..a1571c09f289b8edb7e29e571dd72d69a20dc994 100644 --- a/crates/gpui2/src/key_dispatch.rs +++ b/crates/gpui2/src/key_dispatch.rs @@ -230,6 +230,12 @@ impl DispatchTree { } } + pub fn has_pending_keystrokes(&self) -> bool { + self.keystroke_matchers + .iter() + .any(|(_, matcher)| matcher.has_pending_keystrokes()) + } + pub fn dispatch_path(&self, target: DispatchNodeId) -> SmallVec<[DispatchNodeId; 32]> { let mut dispatch_path: SmallVec<[DispatchNodeId; 32]> = SmallVec::new(); let mut current_node_id = Some(target); diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 9811578184b1134e492dba66841a882f182f7fa7..b84137fa967be7201f7c0b33b95e18b248e258a5 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -1468,6 +1468,13 @@ impl<'a> WindowContext<'a> { self.dispatch_keystroke_observers(event, None); } + pub fn has_pending_keystrokes(&self) -> bool { + self.window + .rendered_frame + .dispatch_tree + .has_pending_keystrokes() + } + fn dispatch_action_on_node(&mut self, node_id: DispatchNodeId, action: Box) { let dispatch_path = self .window diff --git a/crates/vim2/src/insert.rs b/crates/vim2/src/insert.rs index 81aac70d02107a850c02a2c4a00f929616a2dbb5..7c272318287bf323e693121d0e0400cedaa83e75 100644 --- a/crates/vim2/src/insert.rs +++ b/crates/vim2/src/insert.rs @@ -36,100 +36,90 @@ fn normal_before(_: &mut Workspace, action: &NormalBefore, cx: &mut ViewContext< } } -// #[cfg(test)] -// mod test { -// use std::sync::Arc; - -// use gpui::executor::Deterministic; - -// use crate::{ -// state::Mode, -// test::{NeovimBackedTestContext, VimTestContext}, -// }; - -// #[gpui::test] -// async fn test_enter_and_exit_insert_mode(cx: &mut gpui::TestAppContext) { -// let mut cx = VimTestContext::new(cx, true).await; -// cx.simulate_keystroke("i"); -// assert_eq!(cx.mode(), Mode::Insert); -// cx.simulate_keystrokes(["T", "e", "s", "t"]); -// cx.assert_editor_state("Testˇ"); -// cx.simulate_keystroke("escape"); -// assert_eq!(cx.mode(), Mode::Normal); -// cx.assert_editor_state("Tesˇt"); -// } - -// #[gpui::test] -// async fn test_insert_with_counts( -// deterministic: Arc, -// cx: &mut gpui::TestAppContext, -// ) { -// let mut cx = NeovimBackedTestContext::new(cx).await; - -// cx.set_shared_state("ˇhello\n").await; -// cx.simulate_shared_keystrokes(["5", "i", "-", "escape"]) -// .await; -// deterministic.run_until_parked(); -// cx.assert_shared_state("----ˇ-hello\n").await; - -// cx.set_shared_state("ˇhello\n").await; -// cx.simulate_shared_keystrokes(["5", "a", "-", "escape"]) -// .await; -// deterministic.run_until_parked(); -// cx.assert_shared_state("h----ˇ-ello\n").await; - -// cx.simulate_shared_keystrokes(["4", "shift-i", "-", "escape"]) -// .await; -// deterministic.run_until_parked(); -// cx.assert_shared_state("---ˇ-h-----ello\n").await; - -// cx.simulate_shared_keystrokes(["3", "shift-a", "-", "escape"]) -// .await; -// deterministic.run_until_parked(); -// cx.assert_shared_state("----h-----ello--ˇ-\n").await; - -// cx.set_shared_state("ˇhello\n").await; -// cx.simulate_shared_keystrokes(["3", "o", "o", "i", "escape"]) -// .await; -// deterministic.run_until_parked(); -// cx.assert_shared_state("hello\noi\noi\noˇi\n").await; - -// cx.set_shared_state("ˇhello\n").await; -// cx.simulate_shared_keystrokes(["3", "shift-o", "o", "i", "escape"]) -// .await; -// deterministic.run_until_parked(); -// cx.assert_shared_state("oi\noi\noˇi\nhello\n").await; -// } - -// #[gpui::test] -// async fn test_insert_with_repeat( -// deterministic: Arc, -// cx: &mut gpui::TestAppContext, -// ) { -// let mut cx = NeovimBackedTestContext::new(cx).await; - -// cx.set_shared_state("ˇhello\n").await; -// cx.simulate_shared_keystrokes(["3", "i", "-", "escape"]) -// .await; -// deterministic.run_until_parked(); -// cx.assert_shared_state("--ˇ-hello\n").await; -// cx.simulate_shared_keystrokes(["."]).await; -// deterministic.run_until_parked(); -// cx.assert_shared_state("----ˇ--hello\n").await; -// cx.simulate_shared_keystrokes(["2", "."]).await; -// deterministic.run_until_parked(); -// cx.assert_shared_state("-----ˇ---hello\n").await; - -// cx.set_shared_state("ˇhello\n").await; -// cx.simulate_shared_keystrokes(["2", "o", "k", "k", "escape"]) -// .await; -// deterministic.run_until_parked(); -// cx.assert_shared_state("hello\nkk\nkˇk\n").await; -// cx.simulate_shared_keystrokes(["."]).await; -// deterministic.run_until_parked(); -// cx.assert_shared_state("hello\nkk\nkk\nkk\nkˇk\n").await; -// cx.simulate_shared_keystrokes(["1", "."]).await; -// deterministic.run_until_parked(); -// cx.assert_shared_state("hello\nkk\nkk\nkk\nkk\nkˇk\n").await; -// } -// } +#[cfg(test)] +mod test { + use crate::{ + state::Mode, + test::{NeovimBackedTestContext, VimTestContext}, + }; + + #[gpui::test] + async fn test_enter_and_exit_insert_mode(cx: &mut gpui::TestAppContext) { + let mut cx = VimTestContext::new(cx, true).await; + cx.simulate_keystroke("i"); + assert_eq!(cx.mode(), Mode::Insert); + cx.simulate_keystrokes(["T", "e", "s", "t"]); + cx.assert_editor_state("Testˇ"); + cx.simulate_keystroke("escape"); + assert_eq!(cx.mode(), Mode::Normal); + cx.assert_editor_state("Tesˇt"); + } + + #[gpui::test] + async fn test_insert_with_counts(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.set_shared_state("ˇhello\n").await; + cx.simulate_shared_keystrokes(["5", "i", "-", "escape"]) + .await; + cx.run_until_parked(); + cx.assert_shared_state("----ˇ-hello\n").await; + + cx.set_shared_state("ˇhello\n").await; + cx.simulate_shared_keystrokes(["5", "a", "-", "escape"]) + .await; + cx.run_until_parked(); + cx.assert_shared_state("h----ˇ-ello\n").await; + + cx.simulate_shared_keystrokes(["4", "shift-i", "-", "escape"]) + .await; + cx.run_until_parked(); + cx.assert_shared_state("---ˇ-h-----ello\n").await; + + cx.simulate_shared_keystrokes(["3", "shift-a", "-", "escape"]) + .await; + cx.run_until_parked(); + cx.assert_shared_state("----h-----ello--ˇ-\n").await; + + cx.set_shared_state("ˇhello\n").await; + cx.simulate_shared_keystrokes(["3", "o", "o", "i", "escape"]) + .await; + cx.run_until_parked(); + cx.assert_shared_state("hello\noi\noi\noˇi\n").await; + + cx.set_shared_state("ˇhello\n").await; + cx.simulate_shared_keystrokes(["3", "shift-o", "o", "i", "escape"]) + .await; + cx.run_until_parked(); + cx.assert_shared_state("oi\noi\noˇi\nhello\n").await; + } + + #[gpui::test] + async fn test_insert_with_repeat(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.set_shared_state("ˇhello\n").await; + cx.simulate_shared_keystrokes(["3", "i", "-", "escape"]) + .await; + cx.run_until_parked(); + cx.assert_shared_state("--ˇ-hello\n").await; + cx.simulate_shared_keystrokes(["."]).await; + cx.run_until_parked(); + cx.assert_shared_state("----ˇ--hello\n").await; + cx.simulate_shared_keystrokes(["2", "."]).await; + cx.run_until_parked(); + cx.assert_shared_state("-----ˇ---hello\n").await; + + cx.set_shared_state("ˇhello\n").await; + cx.simulate_shared_keystrokes(["2", "o", "k", "k", "escape"]) + .await; + cx.run_until_parked(); + cx.assert_shared_state("hello\nkk\nkˇk\n").await; + cx.simulate_shared_keystrokes(["."]).await; + cx.run_until_parked(); + cx.assert_shared_state("hello\nkk\nkk\nkk\nkˇk\n").await; + cx.simulate_shared_keystrokes(["1", "."]).await; + cx.run_until_parked(); + cx.assert_shared_state("hello\nkk\nkk\nkk\nkk\nkˇk\n").await; + } +} diff --git a/crates/vim2/src/motion.rs b/crates/vim2/src/motion.rs index 4e258bc283d25b74da30daebb6ccfaaa88b27dd4..73ba70d5add11f33d55e001e2637c05bcca0afe5 100644 --- a/crates/vim2/src/motion.rs +++ b/crates/vim2/src/motion.rs @@ -952,147 +952,156 @@ pub(crate) fn next_line_end( end_of_line(map, false, point) } -// #[cfg(test)] -// mod test { - -// use crate::test::NeovimBackedTestContext; -// use indoc::indoc; - -// #[gpui::test] -// async fn test_start_end_of_paragraph(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx).await; - -// let initial_state = indoc! {r"ˇabc -// def - -// paragraph -// the second - -// third and -// final"}; - -// // goes down once -// cx.set_shared_state(initial_state).await; -// cx.simulate_shared_keystrokes(["}"]).await; -// cx.assert_shared_state(indoc! {r"abc -// def -// ˇ -// paragraph -// the second - -// third and -// final"}) -// .await; - -// // goes up once -// cx.simulate_shared_keystrokes(["{"]).await; -// cx.assert_shared_state(initial_state).await; - -// // goes down twice -// cx.simulate_shared_keystrokes(["2", "}"]).await; -// cx.assert_shared_state(indoc! {r"abc -// def - -// paragraph -// the second -// ˇ - -// third and -// final"}) -// .await; - -// // goes down over multiple blanks -// cx.simulate_shared_keystrokes(["}"]).await; -// cx.assert_shared_state(indoc! {r"abc -// def - -// paragraph -// the second - -// third and -// finaˇl"}) -// .await; - -// // goes up twice -// cx.simulate_shared_keystrokes(["2", "{"]).await; -// cx.assert_shared_state(indoc! {r"abc -// def -// ˇ -// paragraph -// the second - -// third and -// final"}) -// .await -// } - -// #[gpui::test] -// async fn test_matching(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx).await; - -// cx.set_shared_state(indoc! {r"func ˇ(a string) { -// do(something(with.and_arrays[0, 2])) -// }"}) -// .await; -// cx.simulate_shared_keystrokes(["%"]).await; -// cx.assert_shared_state(indoc! {r"func (a stringˇ) { -// do(something(with.and_arrays[0, 2])) -// }"}) -// .await; - -// // test it works on the last character of the line -// cx.set_shared_state(indoc! {r"func (a string) ˇ{ -// do(something(with.and_arrays[0, 2])) -// }"}) -// .await; -// cx.simulate_shared_keystrokes(["%"]).await; -// cx.assert_shared_state(indoc! {r"func (a string) { -// do(something(with.and_arrays[0, 2])) -// ˇ}"}) -// .await; - -// // test it works on immediate nesting -// cx.set_shared_state("ˇ{()}").await; -// cx.simulate_shared_keystrokes(["%"]).await; -// cx.assert_shared_state("{()ˇ}").await; -// cx.simulate_shared_keystrokes(["%"]).await; -// cx.assert_shared_state("ˇ{()}").await; - -// // test it works on immediate nesting inside braces -// cx.set_shared_state("{\n ˇ{()}\n}").await; -// cx.simulate_shared_keystrokes(["%"]).await; -// cx.assert_shared_state("{\n {()ˇ}\n}").await; - -// // test it jumps to the next paren on a line -// cx.set_shared_state("func ˇboop() {\n}").await; -// cx.simulate_shared_keystrokes(["%"]).await; -// cx.assert_shared_state("func boop(ˇ) {\n}").await; -// } - -// #[gpui::test] -// async fn test_comma_semicolon(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx).await; - -// cx.set_shared_state("ˇone two three four").await; -// cx.simulate_shared_keystrokes(["f", "o"]).await; -// cx.assert_shared_state("one twˇo three four").await; -// cx.simulate_shared_keystrokes([","]).await; -// cx.assert_shared_state("ˇone two three four").await; -// cx.simulate_shared_keystrokes(["2", ";"]).await; -// cx.assert_shared_state("one two three fˇour").await; -// cx.simulate_shared_keystrokes(["shift-t", "e"]).await; -// cx.assert_shared_state("one two threeˇ four").await; -// cx.simulate_shared_keystrokes(["3", ";"]).await; -// cx.assert_shared_state("oneˇ two three four").await; -// cx.simulate_shared_keystrokes([","]).await; -// cx.assert_shared_state("one two thˇree four").await; -// } - -// #[gpui::test] -// async fn test_next_line_start(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx).await; -// cx.set_shared_state("ˇone\n two\nthree").await; -// cx.simulate_shared_keystrokes(["enter"]).await; -// cx.assert_shared_state("one\n ˇtwo\nthree").await; -// } -// } +#[cfg(test)] +mod test { + + use crate::test::NeovimBackedTestContext; + use indoc::indoc; + + #[gpui::test] + async fn test_start_end_of_paragraph(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + let initial_state = indoc! {r"ˇabc + def + + paragraph + the second + + + + third and + final"}; + + // goes down once + cx.set_shared_state(initial_state).await; + cx.simulate_shared_keystrokes(["}"]).await; + cx.assert_shared_state(indoc! {r"abc + def + ˇ + paragraph + the second + + + + third and + final"}) + .await; + + // goes up once + cx.simulate_shared_keystrokes(["{"]).await; + cx.assert_shared_state(initial_state).await; + + // goes down twice + cx.simulate_shared_keystrokes(["2", "}"]).await; + cx.assert_shared_state(indoc! {r"abc + def + + paragraph + the second + ˇ + + + third and + final"}) + .await; + + // goes down over multiple blanks + cx.simulate_shared_keystrokes(["}"]).await; + cx.assert_shared_state(indoc! {r"abc + def + + paragraph + the second + + + + third and + finaˇl"}) + .await; + + // goes up twice + cx.simulate_shared_keystrokes(["2", "{"]).await; + cx.assert_shared_state(indoc! {r"abc + def + ˇ + paragraph + the second + + + + third and + final"}) + .await + } + + #[gpui::test] + async fn test_matching(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.set_shared_state(indoc! {r"func ˇ(a string) { + do(something(with.and_arrays[0, 2])) + }"}) + .await; + cx.simulate_shared_keystrokes(["%"]).await; + cx.assert_shared_state(indoc! {r"func (a stringˇ) { + do(something(with.and_arrays[0, 2])) + }"}) + .await; + + // test it works on the last character of the line + cx.set_shared_state(indoc! {r"func (a string) ˇ{ + do(something(with.and_arrays[0, 2])) + }"}) + .await; + cx.simulate_shared_keystrokes(["%"]).await; + cx.assert_shared_state(indoc! {r"func (a string) { + do(something(with.and_arrays[0, 2])) + ˇ}"}) + .await; + + // test it works on immediate nesting + cx.set_shared_state("ˇ{()}").await; + cx.simulate_shared_keystrokes(["%"]).await; + cx.assert_shared_state("{()ˇ}").await; + cx.simulate_shared_keystrokes(["%"]).await; + cx.assert_shared_state("ˇ{()}").await; + + // test it works on immediate nesting inside braces + cx.set_shared_state("{\n ˇ{()}\n}").await; + cx.simulate_shared_keystrokes(["%"]).await; + cx.assert_shared_state("{\n {()ˇ}\n}").await; + + // test it jumps to the next paren on a line + cx.set_shared_state("func ˇboop() {\n}").await; + cx.simulate_shared_keystrokes(["%"]).await; + cx.assert_shared_state("func boop(ˇ) {\n}").await; + } + + #[gpui::test] + async fn test_comma_semicolon(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.set_shared_state("ˇone two three four").await; + cx.simulate_shared_keystrokes(["f", "o"]).await; + cx.assert_shared_state("one twˇo three four").await; + cx.simulate_shared_keystrokes([","]).await; + cx.assert_shared_state("ˇone two three four").await; + cx.simulate_shared_keystrokes(["2", ";"]).await; + cx.assert_shared_state("one two three fˇour").await; + cx.simulate_shared_keystrokes(["shift-t", "e"]).await; + cx.assert_shared_state("one two threeˇ four").await; + cx.simulate_shared_keystrokes(["3", ";"]).await; + cx.assert_shared_state("oneˇ two three four").await; + cx.simulate_shared_keystrokes([","]).await; + cx.assert_shared_state("one two thˇree four").await; + } + + #[gpui::test] + async fn test_next_line_start(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + cx.set_shared_state("ˇone\n two\nthree").await; + cx.simulate_shared_keystrokes(["enter"]).await; + cx.assert_shared_state("one\n ˇtwo\nthree").await; + } +} diff --git a/crates/vim2/src/normal/case.rs b/crates/vim2/src/normal/case.rs index 6f321faa7fdfd8296db406d84d6dae58b15e26d4..22d09f8359f52060a7435cef321ca0c7f71bd37c 100644 --- a/crates/vim2/src/normal/case.rs +++ b/crates/vim2/src/normal/case.rs @@ -74,43 +74,43 @@ pub fn change_case(_: &mut Workspace, _: &ChangeCase, cx: &mut ViewContext) { }); } -// #[cfg(test)] -// mod test { -// use crate::{ -// state::Mode, -// test::{NeovimBackedTestContext, VimTestContext}, -// }; -// use indoc::indoc; - -// #[gpui::test] -// async fn test_paste(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx).await; - -// // single line -// cx.set_shared_state(indoc! {" -// The quick brown -// fox ˇjumps over -// the lazy dog"}) -// .await; -// cx.simulate_shared_keystrokes(["v", "w", "y"]).await; -// cx.assert_shared_clipboard("jumps o").await; -// cx.set_shared_state(indoc! {" -// The quick brown -// fox jumps oveˇr -// the lazy dog"}) -// .await; -// cx.simulate_shared_keystroke("p").await; -// cx.assert_shared_state(indoc! {" -// The quick brown -// fox jumps overjumps ˇo -// the lazy dog"}) -// .await; - -// cx.set_shared_state(indoc! {" -// The quick brown -// fox jumps oveˇr -// the lazy dog"}) -// .await; -// cx.simulate_shared_keystroke("shift-p").await; -// cx.assert_shared_state(indoc! {" -// The quick brown -// fox jumps ovejumps ˇor -// the lazy dog"}) -// .await; - -// // line mode -// cx.set_shared_state(indoc! {" -// The quick brown -// fox juˇmps over -// the lazy dog"}) -// .await; -// cx.simulate_shared_keystrokes(["d", "d"]).await; -// cx.assert_shared_clipboard("fox jumps over\n").await; -// cx.assert_shared_state(indoc! {" -// The quick brown -// the laˇzy dog"}) -// .await; -// cx.simulate_shared_keystroke("p").await; -// cx.assert_shared_state(indoc! {" -// The quick brown -// the lazy dog -// ˇfox jumps over"}) -// .await; -// cx.simulate_shared_keystrokes(["k", "shift-p"]).await; -// cx.assert_shared_state(indoc! {" -// The quick brown -// ˇfox jumps over -// the lazy dog -// fox jumps over"}) -// .await; - -// // multiline, cursor to first character of pasted text. -// cx.set_shared_state(indoc! {" -// The quick brown -// fox jumps ˇover -// the lazy dog"}) -// .await; -// cx.simulate_shared_keystrokes(["v", "j", "y"]).await; -// cx.assert_shared_clipboard("over\nthe lazy do").await; - -// cx.simulate_shared_keystroke("p").await; -// cx.assert_shared_state(indoc! {" -// The quick brown -// fox jumps oˇover -// the lazy dover -// the lazy dog"}) -// .await; -// cx.simulate_shared_keystrokes(["u", "shift-p"]).await; -// cx.assert_shared_state(indoc! {" -// The quick brown -// fox jumps ˇover -// the lazy doover -// the lazy dog"}) -// .await; -// } - -// #[gpui::test] -// async fn test_paste_visual(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx).await; - -// // copy in visual mode -// cx.set_shared_state(indoc! {" -// The quick brown -// fox jˇumps over -// the lazy dog"}) -// .await; -// cx.simulate_shared_keystrokes(["v", "i", "w", "y"]).await; -// cx.assert_shared_state(indoc! {" -// The quick brown -// fox ˇjumps over -// the lazy dog"}) -// .await; -// // paste in visual mode -// cx.simulate_shared_keystrokes(["w", "v", "i", "w", "p"]) -// .await; -// cx.assert_shared_state(indoc! {" -// The quick brown -// fox jumps jumpˇs -// the lazy dog"}) -// .await; -// cx.assert_shared_clipboard("over").await; -// // paste in visual line mode -// cx.simulate_shared_keystrokes(["up", "shift-v", "shift-p"]) -// .await; -// cx.assert_shared_state(indoc! {" -// ˇover -// fox jumps jumps -// the lazy dog"}) -// .await; -// cx.assert_shared_clipboard("over").await; -// // paste in visual block mode -// cx.simulate_shared_keystrokes(["ctrl-v", "down", "down", "p"]) -// .await; -// cx.assert_shared_state(indoc! {" -// oveˇrver -// overox jumps jumps -// overhe lazy dog"}) -// .await; - -// // copy in visual line mode -// cx.set_shared_state(indoc! {" -// The quick brown -// fox juˇmps over -// the lazy dog"}) -// .await; -// cx.simulate_shared_keystrokes(["shift-v", "d"]).await; -// cx.assert_shared_state(indoc! {" -// The quick brown -// the laˇzy dog"}) -// .await; -// // paste in visual mode -// cx.simulate_shared_keystrokes(["v", "i", "w", "p"]).await; -// cx.assert_shared_state( -// &indoc! {" -// The quick brown -// the_ -// ˇfox jumps over -// _dog"} -// .replace("_", " "), // Hack for trailing whitespace -// ) -// .await; -// cx.assert_shared_clipboard("lazy").await; -// cx.set_shared_state(indoc! {" -// The quick brown -// fox juˇmps over -// the lazy dog"}) -// .await; -// cx.simulate_shared_keystrokes(["shift-v", "d"]).await; -// cx.assert_shared_state(indoc! {" -// The quick brown -// the laˇzy dog"}) -// .await; -// // paste in visual line mode -// cx.simulate_shared_keystrokes(["k", "shift-v", "p"]).await; -// cx.assert_shared_state(indoc! {" -// ˇfox jumps over -// the lazy dog"}) -// .await; -// cx.assert_shared_clipboard("The quick brown\n").await; -// } - -// #[gpui::test] -// async fn test_paste_visual_block(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx).await; -// // copy in visual block mode -// cx.set_shared_state(indoc! {" -// The ˇquick brown -// fox jumps over -// the lazy dog"}) -// .await; -// cx.simulate_shared_keystrokes(["ctrl-v", "2", "j", "y"]) -// .await; -// cx.assert_shared_clipboard("q\nj\nl").await; -// cx.simulate_shared_keystrokes(["p"]).await; -// cx.assert_shared_state(indoc! {" -// The qˇquick brown -// fox jjumps over -// the llazy dog"}) -// .await; -// cx.simulate_shared_keystrokes(["v", "i", "w", "shift-p"]) -// .await; -// cx.assert_shared_state(indoc! {" -// The ˇq brown -// fox jjjumps over -// the lllazy dog"}) -// .await; -// cx.simulate_shared_keystrokes(["v", "i", "w", "shift-p"]) -// .await; - -// cx.set_shared_state(indoc! {" -// The ˇquick brown -// fox jumps over -// the lazy dog"}) -// .await; -// cx.simulate_shared_keystrokes(["ctrl-v", "j", "y"]).await; -// cx.assert_shared_clipboard("q\nj").await; -// cx.simulate_shared_keystrokes(["l", "ctrl-v", "2", "j", "shift-p"]) -// .await; -// cx.assert_shared_state(indoc! {" -// The qˇqick brown -// fox jjmps over -// the lzy dog"}) -// .await; - -// cx.simulate_shared_keystrokes(["shift-v", "p"]).await; -// cx.assert_shared_state(indoc! {" -// ˇq -// j -// fox jjmps over -// the lzy dog"}) -// .await; -// } - -// #[gpui::test] -// async fn test_paste_indent(cx: &mut gpui::TestAppContext) { -// let mut cx = VimTestContext::new_typescript(cx).await; - -// cx.set_state( -// indoc! {" -// class A {ˇ -// } -// "}, -// Mode::Normal, -// ); -// cx.simulate_keystrokes(["o", "a", "(", ")", "{", "escape"]); -// cx.assert_state( -// indoc! {" -// class A { -// a()ˇ{} -// } -// "}, -// Mode::Normal, -// ); -// // cursor goes to the first non-blank character in the line; -// cx.simulate_keystrokes(["y", "y", "p"]); -// cx.assert_state( -// indoc! {" -// class A { -// a(){} -// ˇa(){} -// } -// "}, -// Mode::Normal, -// ); -// // indentation is preserved when pasting -// cx.simulate_keystrokes(["u", "shift-v", "up", "y", "shift-p"]); -// cx.assert_state( -// indoc! {" -// ˇclass A { -// a(){} -// class A { -// a(){} -// } -// "}, -// Mode::Normal, -// ); -// } -// } +#[cfg(test)] +mod test { + use crate::{ + state::Mode, + test::{NeovimBackedTestContext, VimTestContext}, + }; + use indoc::indoc; + + #[gpui::test] + async fn test_paste(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + // single line + cx.set_shared_state(indoc! {" + The quick brown + fox ˇjumps over + the lazy dog"}) + .await; + cx.simulate_shared_keystrokes(["v", "w", "y"]).await; + cx.assert_shared_clipboard("jumps o").await; + cx.set_shared_state(indoc! {" + The quick brown + fox jumps oveˇr + the lazy dog"}) + .await; + cx.simulate_shared_keystroke("p").await; + cx.assert_shared_state(indoc! {" + The quick brown + fox jumps overjumps ˇo + the lazy dog"}) + .await; + + cx.set_shared_state(indoc! {" + The quick brown + fox jumps oveˇr + the lazy dog"}) + .await; + cx.simulate_shared_keystroke("shift-p").await; + cx.assert_shared_state(indoc! {" + The quick brown + fox jumps ovejumps ˇor + the lazy dog"}) + .await; + + // line mode + cx.set_shared_state(indoc! {" + The quick brown + fox juˇmps over + the lazy dog"}) + .await; + cx.simulate_shared_keystrokes(["d", "d"]).await; + cx.assert_shared_clipboard("fox jumps over\n").await; + cx.assert_shared_state(indoc! {" + The quick brown + the laˇzy dog"}) + .await; + cx.simulate_shared_keystroke("p").await; + cx.assert_shared_state(indoc! {" + The quick brown + the lazy dog + ˇfox jumps over"}) + .await; + cx.simulate_shared_keystrokes(["k", "shift-p"]).await; + cx.assert_shared_state(indoc! {" + The quick brown + ˇfox jumps over + the lazy dog + fox jumps over"}) + .await; + + // multiline, cursor to first character of pasted text. + cx.set_shared_state(indoc! {" + The quick brown + fox jumps ˇover + the lazy dog"}) + .await; + cx.simulate_shared_keystrokes(["v", "j", "y"]).await; + cx.assert_shared_clipboard("over\nthe lazy do").await; + + cx.simulate_shared_keystroke("p").await; + cx.assert_shared_state(indoc! {" + The quick brown + fox jumps oˇover + the lazy dover + the lazy dog"}) + .await; + cx.simulate_shared_keystrokes(["u", "shift-p"]).await; + cx.assert_shared_state(indoc! {" + The quick brown + fox jumps ˇover + the lazy doover + the lazy dog"}) + .await; + } + + #[gpui::test] + async fn test_paste_visual(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + // copy in visual mode + cx.set_shared_state(indoc! {" + The quick brown + fox jˇumps over + the lazy dog"}) + .await; + cx.simulate_shared_keystrokes(["v", "i", "w", "y"]).await; + cx.assert_shared_state(indoc! {" + The quick brown + fox ˇjumps over + the lazy dog"}) + .await; + // paste in visual mode + cx.simulate_shared_keystrokes(["w", "v", "i", "w", "p"]) + .await; + cx.assert_shared_state(indoc! {" + The quick brown + fox jumps jumpˇs + the lazy dog"}) + .await; + cx.assert_shared_clipboard("over").await; + // paste in visual line mode + cx.simulate_shared_keystrokes(["up", "shift-v", "shift-p"]) + .await; + cx.assert_shared_state(indoc! {" + ˇover + fox jumps jumps + the lazy dog"}) + .await; + cx.assert_shared_clipboard("over").await; + // paste in visual block mode + cx.simulate_shared_keystrokes(["ctrl-v", "down", "down", "p"]) + .await; + cx.assert_shared_state(indoc! {" + oveˇrver + overox jumps jumps + overhe lazy dog"}) + .await; + + // copy in visual line mode + cx.set_shared_state(indoc! {" + The quick brown + fox juˇmps over + the lazy dog"}) + .await; + cx.simulate_shared_keystrokes(["shift-v", "d"]).await; + cx.assert_shared_state(indoc! {" + The quick brown + the laˇzy dog"}) + .await; + // paste in visual mode + cx.simulate_shared_keystrokes(["v", "i", "w", "p"]).await; + cx.assert_shared_state( + &indoc! {" + The quick brown + the_ + ˇfox jumps over + _dog"} + .replace("_", " "), // Hack for trailing whitespace + ) + .await; + cx.assert_shared_clipboard("lazy").await; + cx.set_shared_state(indoc! {" + The quick brown + fox juˇmps over + the lazy dog"}) + .await; + cx.simulate_shared_keystrokes(["shift-v", "d"]).await; + cx.assert_shared_state(indoc! {" + The quick brown + the laˇzy dog"}) + .await; + // paste in visual line mode + cx.simulate_shared_keystrokes(["k", "shift-v", "p"]).await; + cx.assert_shared_state(indoc! {" + ˇfox jumps over + the lazy dog"}) + .await; + cx.assert_shared_clipboard("The quick brown\n").await; + } + + #[gpui::test] + async fn test_paste_visual_block(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + // copy in visual block mode + cx.set_shared_state(indoc! {" + The ˇquick brown + fox jumps over + the lazy dog"}) + .await; + cx.simulate_shared_keystrokes(["ctrl-v", "2", "j", "y"]) + .await; + cx.assert_shared_clipboard("q\nj\nl").await; + cx.simulate_shared_keystrokes(["p"]).await; + cx.assert_shared_state(indoc! {" + The qˇquick brown + fox jjumps over + the llazy dog"}) + .await; + cx.simulate_shared_keystrokes(["v", "i", "w", "shift-p"]) + .await; + cx.assert_shared_state(indoc! {" + The ˇq brown + fox jjjumps over + the lllazy dog"}) + .await; + cx.simulate_shared_keystrokes(["v", "i", "w", "shift-p"]) + .await; + + cx.set_shared_state(indoc! {" + The ˇquick brown + fox jumps over + the lazy dog"}) + .await; + cx.simulate_shared_keystrokes(["ctrl-v", "j", "y"]).await; + cx.assert_shared_clipboard("q\nj").await; + cx.simulate_shared_keystrokes(["l", "ctrl-v", "2", "j", "shift-p"]) + .await; + cx.assert_shared_state(indoc! {" + The qˇqick brown + fox jjmps over + the lzy dog"}) + .await; + + cx.simulate_shared_keystrokes(["shift-v", "p"]).await; + cx.assert_shared_state(indoc! {" + ˇq + j + fox jjmps over + the lzy dog"}) + .await; + } + + #[gpui::test] + async fn test_paste_indent(cx: &mut gpui::TestAppContext) { + let mut cx = VimTestContext::new_typescript(cx).await; + + cx.set_state( + indoc! {" + class A {ˇ + } + "}, + Mode::Normal, + ); + cx.simulate_keystrokes(["o", "a", "(", ")", "{", "escape"]); + cx.assert_state( + indoc! {" + class A { + a()ˇ{} + } + "}, + Mode::Normal, + ); + // cursor goes to the first non-blank character in the line; + cx.simulate_keystrokes(["y", "y", "p"]); + cx.assert_state( + indoc! {" + class A { + a(){} + ˇa(){} + } + "}, + Mode::Normal, + ); + // indentation is preserved when pasting + cx.simulate_keystrokes(["u", "shift-v", "up", "y", "shift-p"]); + cx.assert_state( + indoc! {" + ˇclass A { + a(){} + class A { + a(){} + } + "}, + Mode::Normal, + ); + } +} diff --git a/crates/vim2/src/normal/repeat.rs b/crates/vim2/src/normal/repeat.rs index a5a43b8389f3e4793087fa7d15a1fb8c6ecf8870..a643c126ef4edbd00442578059b9db55cb3e5774 100644 --- a/crates/vim2/src/normal/repeat.rs +++ b/crates/vim2/src/normal/repeat.rs @@ -196,328 +196,301 @@ pub(crate) fn repeat(cx: &mut WindowContext, from_insert_mode: bool) { .detach_and_log_err(cx); } -// #[cfg(test)] -// mod test { -// use std::sync::Arc; - -// use editor::test::editor_lsp_test_context::EditorLspTestContext; -// use futures::StreamExt; -// use indoc::indoc; - -// use gpui::{executor::Deterministic, View}; - -// use crate::{ -// state::Mode, -// test::{NeovimBackedTestContext, VimTestContext}, -// }; - -// #[gpui::test] -// async fn test_dot_repeat(deterministic: Arc, cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx).await; - -// // "o" -// cx.set_shared_state("ˇhello").await; -// cx.simulate_shared_keystrokes(["o", "w", "o", "r", "l", "d", "escape"]) -// .await; -// cx.assert_shared_state("hello\nworlˇd").await; -// cx.simulate_shared_keystrokes(["."]).await; -// deterministic.run_until_parked(); -// cx.assert_shared_state("hello\nworld\nworlˇd").await; - -// // "d" -// cx.simulate_shared_keystrokes(["^", "d", "f", "o"]).await; -// cx.simulate_shared_keystrokes(["g", "g", "."]).await; -// deterministic.run_until_parked(); -// cx.assert_shared_state("ˇ\nworld\nrld").await; - -// // "p" (note that it pastes the current clipboard) -// cx.simulate_shared_keystrokes(["j", "y", "y", "p"]).await; -// cx.simulate_shared_keystrokes(["shift-g", "y", "y", "."]) -// .await; -// deterministic.run_until_parked(); -// cx.assert_shared_state("\nworld\nworld\nrld\nˇrld").await; - -// // "~" (note that counts apply to the action taken, not . itself) -// cx.set_shared_state("ˇthe quick brown fox").await; -// cx.simulate_shared_keystrokes(["2", "~", "."]).await; -// deterministic.run_until_parked(); -// cx.set_shared_state("THE ˇquick brown fox").await; -// cx.simulate_shared_keystrokes(["3", "."]).await; -// deterministic.run_until_parked(); -// cx.set_shared_state("THE QUIˇck brown fox").await; -// deterministic.run_until_parked(); -// cx.simulate_shared_keystrokes(["."]).await; -// deterministic.run_until_parked(); -// cx.assert_shared_state("THE QUICK ˇbrown fox").await; -// } - -// #[gpui::test] -// async fn test_repeat_ime(deterministic: Arc, cx: &mut gpui::TestAppContext) { -// let mut cx = VimTestContext::new(cx, true).await; - -// cx.set_state("hˇllo", Mode::Normal); -// cx.simulate_keystrokes(["i"]); - -// // simulate brazilian input for ä. -// cx.update_editor(|editor, cx| { -// editor.replace_and_mark_text_in_range(None, "\"", Some(1..1), cx); -// editor.replace_text_in_range(None, "ä", cx); -// }); -// cx.simulate_keystrokes(["escape"]); -// cx.assert_state("hˇällo", Mode::Normal); -// cx.simulate_keystrokes(["."]); -// deterministic.run_until_parked(); -// cx.assert_state("hˇäällo", Mode::Normal); -// } - -// #[gpui::test] -// async fn test_repeat_completion( -// deterministic: Arc, -// cx: &mut gpui::TestAppContext, -// ) { -// let cx = EditorLspTestContext::new_rust( -// lsp::ServerCapabilities { -// completion_provider: Some(lsp::CompletionOptions { -// trigger_characters: Some(vec![".".to_string(), ":".to_string()]), -// resolve_provider: Some(true), -// ..Default::default() -// }), -// ..Default::default() -// }, -// cx, -// ) -// .await; -// let mut cx = VimTestContext::new_with_lsp(cx, true); - -// cx.set_state( -// indoc! {" -// onˇe -// two -// three -// "}, -// Mode::Normal, -// ); - -// let mut request = -// cx.handle_request::(move |_, params, _| async move { -// let position = params.text_document_position.position; -// Ok(Some(lsp::CompletionResponse::Array(vec![ -// lsp::CompletionItem { -// label: "first".to_string(), -// text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit { -// range: lsp::Range::new(position.clone(), position.clone()), -// new_text: "first".to_string(), -// })), -// ..Default::default() -// }, -// lsp::CompletionItem { -// label: "second".to_string(), -// text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit { -// range: lsp::Range::new(position.clone(), position.clone()), -// new_text: "second".to_string(), -// })), -// ..Default::default() -// }, -// ]))) -// }); -// cx.simulate_keystrokes(["a", "."]); -// request.next().await; -// cx.condition(|editor, _| editor.context_menu_visible()) -// .await; -// cx.simulate_keystrokes(["down", "enter", "!", "escape"]); - -// cx.assert_state( -// indoc! {" -// one.secondˇ! -// two -// three -// "}, -// Mode::Normal, -// ); -// cx.simulate_keystrokes(["j", "."]); -// deterministic.run_until_parked(); -// cx.assert_state( -// indoc! {" -// one.second! -// two.secondˇ! -// three -// "}, -// Mode::Normal, -// ); -// } - -// #[gpui::test] -// async fn test_repeat_visual(deterministic: Arc, cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx).await; - -// // single-line (3 columns) -// cx.set_shared_state(indoc! { -// "ˇthe quick brown -// fox jumps over -// the lazy dog" -// }) -// .await; -// cx.simulate_shared_keystrokes(["v", "i", "w", "s", "o", "escape"]) -// .await; -// cx.assert_shared_state(indoc! { -// "ˇo quick brown -// fox jumps over -// the lazy dog" -// }) -// .await; -// cx.simulate_shared_keystrokes(["j", "w", "."]).await; -// deterministic.run_until_parked(); -// cx.assert_shared_state(indoc! { -// "o quick brown -// fox ˇops over -// the lazy dog" -// }) -// .await; -// cx.simulate_shared_keystrokes(["f", "r", "."]).await; -// deterministic.run_until_parked(); -// cx.assert_shared_state(indoc! { -// "o quick brown -// fox ops oveˇothe lazy dog" -// }) -// .await; - -// // visual -// cx.set_shared_state(indoc! { -// "the ˇquick brown -// fox jumps over -// fox jumps over -// fox jumps over -// the lazy dog" -// }) -// .await; -// cx.simulate_shared_keystrokes(["v", "j", "x"]).await; -// cx.assert_shared_state(indoc! { -// "the ˇumps over -// fox jumps over -// fox jumps over -// the lazy dog" -// }) -// .await; -// cx.simulate_shared_keystrokes(["."]).await; -// deterministic.run_until_parked(); -// cx.assert_shared_state(indoc! { -// "the ˇumps over -// fox jumps over -// the lazy dog" -// }) -// .await; -// cx.simulate_shared_keystrokes(["w", "."]).await; -// deterministic.run_until_parked(); -// cx.assert_shared_state(indoc! { -// "the umps ˇumps over -// the lazy dog" -// }) -// .await; -// cx.simulate_shared_keystrokes(["j", "."]).await; -// deterministic.run_until_parked(); -// cx.assert_shared_state(indoc! { -// "the umps umps over -// the ˇog" -// }) -// .await; - -// // block mode (3 rows) -// cx.set_shared_state(indoc! { -// "ˇthe quick brown -// fox jumps over -// the lazy dog" -// }) -// .await; -// cx.simulate_shared_keystrokes(["ctrl-v", "j", "j", "shift-i", "o", "escape"]) -// .await; -// cx.assert_shared_state(indoc! { -// "ˇothe quick brown -// ofox jumps over -// othe lazy dog" -// }) -// .await; -// cx.simulate_shared_keystrokes(["j", "4", "l", "."]).await; -// deterministic.run_until_parked(); -// cx.assert_shared_state(indoc! { -// "othe quick brown -// ofoxˇo jumps over -// otheo lazy dog" -// }) -// .await; - -// // line mode -// cx.set_shared_state(indoc! { -// "ˇthe quick brown -// fox jumps over -// the lazy dog" -// }) -// .await; -// cx.simulate_shared_keystrokes(["shift-v", "shift-r", "o", "escape"]) -// .await; -// cx.assert_shared_state(indoc! { -// "ˇo -// fox jumps over -// the lazy dog" -// }) -// .await; -// cx.simulate_shared_keystrokes(["j", "."]).await; -// deterministic.run_until_parked(); -// cx.assert_shared_state(indoc! { -// "o -// ˇo -// the lazy dog" -// }) -// .await; -// } - -// #[gpui::test] -// async fn test_repeat_motion_counts( -// deterministic: Arc, -// cx: &mut gpui::TestAppContext, -// ) { -// let mut cx = NeovimBackedTestContext::new(cx).await; - -// cx.set_shared_state(indoc! { -// "ˇthe quick brown -// fox jumps over -// the lazy dog" -// }) -// .await; -// cx.simulate_shared_keystrokes(["3", "d", "3", "l"]).await; -// cx.assert_shared_state(indoc! { -// "ˇ brown -// fox jumps over -// the lazy dog" -// }) -// .await; -// cx.simulate_shared_keystrokes(["j", "."]).await; -// deterministic.run_until_parked(); -// cx.assert_shared_state(indoc! { -// " brown -// ˇ over -// the lazy dog" -// }) -// .await; -// cx.simulate_shared_keystrokes(["j", "2", "."]).await; -// deterministic.run_until_parked(); -// cx.assert_shared_state(indoc! { -// " brown -// over -// ˇe lazy dog" -// }) -// .await; -// } - -// #[gpui::test] -// async fn test_record_interrupted( -// deterministic: Arc, -// cx: &mut gpui::TestAppContext, -// ) { -// let mut cx = VimTestContext::new(cx, true).await; - -// cx.set_state("ˇhello\n", Mode::Normal); -// cx.simulate_keystrokes(["4", "i", "j", "cmd-shift-p", "escape", "escape"]); -// deterministic.run_until_parked(); -// cx.assert_state("ˇjhello\n", Mode::Normal); -// } -// } +#[cfg(test)] +mod test { + use editor::test::editor_lsp_test_context::EditorLspTestContext; + use futures::StreamExt; + use indoc::indoc; + + use gpui::InputHandler; + + use crate::{ + state::Mode, + test::{NeovimBackedTestContext, VimTestContext}, + }; + + #[gpui::test] + async fn test_dot_repeat(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + // "o" + cx.set_shared_state("ˇhello").await; + cx.simulate_shared_keystrokes(["o", "w", "o", "r", "l", "d", "escape"]) + .await; + cx.assert_shared_state("hello\nworlˇd").await; + cx.simulate_shared_keystrokes(["."]).await; + cx.assert_shared_state("hello\nworld\nworlˇd").await; + + // "d" + cx.simulate_shared_keystrokes(["^", "d", "f", "o"]).await; + cx.simulate_shared_keystrokes(["g", "g", "."]).await; + cx.assert_shared_state("ˇ\nworld\nrld").await; + + // "p" (note that it pastes the current clipboard) + cx.simulate_shared_keystrokes(["j", "y", "y", "p"]).await; + cx.simulate_shared_keystrokes(["shift-g", "y", "y", "."]) + .await; + cx.assert_shared_state("\nworld\nworld\nrld\nˇrld").await; + + // "~" (note that counts apply to the action taken, not . itself) + cx.set_shared_state("ˇthe quick brown fox").await; + cx.simulate_shared_keystrokes(["2", "~", "."]).await; + cx.set_shared_state("THE ˇquick brown fox").await; + cx.simulate_shared_keystrokes(["3", "."]).await; + cx.set_shared_state("THE QUIˇck brown fox").await; + cx.run_until_parked(); + cx.simulate_shared_keystrokes(["."]).await; + cx.assert_shared_state("THE QUICK ˇbrown fox").await; + } + + #[gpui::test] + async fn test_repeat_ime(cx: &mut gpui::TestAppContext) { + let mut cx = VimTestContext::new(cx, true).await; + + cx.set_state("hˇllo", Mode::Normal); + cx.simulate_keystrokes(["i"]); + + // simulate brazilian input for ä. + cx.update_editor(|editor, cx| { + editor.replace_and_mark_text_in_range(None, "\"", Some(1..1), cx); + editor.replace_text_in_range(None, "ä", cx); + }); + cx.simulate_keystrokes(["escape"]); + cx.assert_state("hˇällo", Mode::Normal); + cx.simulate_keystrokes(["."]); + cx.assert_state("hˇäällo", Mode::Normal); + } + + #[gpui::test] + async fn test_repeat_completion(cx: &mut gpui::TestAppContext) { + VimTestContext::init(cx); + let cx = EditorLspTestContext::new_rust( + lsp::ServerCapabilities { + completion_provider: Some(lsp::CompletionOptions { + trigger_characters: Some(vec![".".to_string(), ":".to_string()]), + resolve_provider: Some(true), + ..Default::default() + }), + ..Default::default() + }, + cx, + ) + .await; + let mut cx = VimTestContext::new_with_lsp(cx, true); + + cx.set_state( + indoc! {" + onˇe + two + three + "}, + Mode::Normal, + ); + + let mut request = + cx.handle_request::(move |_, params, _| async move { + let position = params.text_document_position.position; + Ok(Some(lsp::CompletionResponse::Array(vec![ + lsp::CompletionItem { + label: "first".to_string(), + text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit { + range: lsp::Range::new(position.clone(), position.clone()), + new_text: "first".to_string(), + })), + ..Default::default() + }, + lsp::CompletionItem { + label: "second".to_string(), + text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit { + range: lsp::Range::new(position.clone(), position.clone()), + new_text: "second".to_string(), + })), + ..Default::default() + }, + ]))) + }); + cx.simulate_keystrokes(["a", "."]); + request.next().await; + cx.condition(|editor, _| editor.context_menu_visible()) + .await; + cx.simulate_keystrokes(["down", "enter", "!", "escape"]); + + cx.assert_state( + indoc! {" + one.secondˇ! + two + three + "}, + Mode::Normal, + ); + cx.simulate_keystrokes(["j", "."]); + cx.assert_state( + indoc! {" + one.second! + two.secondˇ! + three + "}, + Mode::Normal, + ); + } + + #[gpui::test] + async fn test_repeat_visual(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + // single-line (3 columns) + cx.set_shared_state(indoc! { + "ˇthe quick brown + fox jumps over + the lazy dog" + }) + .await; + cx.simulate_shared_keystrokes(["v", "i", "w", "s", "o", "escape"]) + .await; + cx.assert_shared_state(indoc! { + "ˇo quick brown + fox jumps over + the lazy dog" + }) + .await; + cx.simulate_shared_keystrokes(["j", "w", "."]).await; + cx.assert_shared_state(indoc! { + "o quick brown + fox ˇops over + the lazy dog" + }) + .await; + cx.simulate_shared_keystrokes(["f", "r", "."]).await; + cx.assert_shared_state(indoc! { + "o quick brown + fox ops oveˇothe lazy dog" + }) + .await; + + // visual + cx.set_shared_state(indoc! { + "the ˇquick brown + fox jumps over + fox jumps over + fox jumps over + the lazy dog" + }) + .await; + cx.simulate_shared_keystrokes(["v", "j", "x"]).await; + cx.assert_shared_state(indoc! { + "the ˇumps over + fox jumps over + fox jumps over + the lazy dog" + }) + .await; + cx.simulate_shared_keystrokes(["."]).await; + cx.assert_shared_state(indoc! { + "the ˇumps over + fox jumps over + the lazy dog" + }) + .await; + cx.simulate_shared_keystrokes(["w", "."]).await; + cx.assert_shared_state(indoc! { + "the umps ˇumps over + the lazy dog" + }) + .await; + cx.simulate_shared_keystrokes(["j", "."]).await; + cx.assert_shared_state(indoc! { + "the umps umps over + the ˇog" + }) + .await; + + // block mode (3 rows) + cx.set_shared_state(indoc! { + "ˇthe quick brown + fox jumps over + the lazy dog" + }) + .await; + cx.simulate_shared_keystrokes(["ctrl-v", "j", "j", "shift-i", "o", "escape"]) + .await; + cx.assert_shared_state(indoc! { + "ˇothe quick brown + ofox jumps over + othe lazy dog" + }) + .await; + cx.simulate_shared_keystrokes(["j", "4", "l", "."]).await; + cx.assert_shared_state(indoc! { + "othe quick brown + ofoxˇo jumps over + otheo lazy dog" + }) + .await; + + // line mode + cx.set_shared_state(indoc! { + "ˇthe quick brown + fox jumps over + the lazy dog" + }) + .await; + cx.simulate_shared_keystrokes(["shift-v", "shift-r", "o", "escape"]) + .await; + cx.assert_shared_state(indoc! { + "ˇo + fox jumps over + the lazy dog" + }) + .await; + cx.simulate_shared_keystrokes(["j", "."]).await; + cx.assert_shared_state(indoc! { + "o + ˇo + the lazy dog" + }) + .await; + } + + #[gpui::test] + async fn test_repeat_motion_counts(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.set_shared_state(indoc! { + "ˇthe quick brown + fox jumps over + the lazy dog" + }) + .await; + cx.simulate_shared_keystrokes(["3", "d", "3", "l"]).await; + cx.assert_shared_state(indoc! { + "ˇ brown + fox jumps over + the lazy dog" + }) + .await; + cx.simulate_shared_keystrokes(["j", "."]).await; + cx.assert_shared_state(indoc! { + " brown + ˇ over + the lazy dog" + }) + .await; + cx.simulate_shared_keystrokes(["j", "2", "."]).await; + cx.assert_shared_state(indoc! { + " brown + over + ˇe lazy dog" + }) + .await; + } + + #[gpui::test] + async fn test_record_interrupted(cx: &mut gpui::TestAppContext) { + let mut cx = VimTestContext::new(cx, true).await; + + cx.set_state("ˇhello\n", Mode::Normal); + cx.simulate_keystrokes(["4", "i", "j", "cmd-shift-p", "escape"]); + cx.simulate_keystrokes(["escape"]); + cx.assert_state("ˇjhello\n", Mode::Normal); + } +} diff --git a/crates/vim2/src/normal/scroll.rs b/crates/vim2/src/normal/scroll.rs index fe5f3a9017f5ff81639330f63ed6866425625182..84a27e20cee6984ea04f16935f3e936a7333acf3 100644 --- a/crates/vim2/src/normal/scroll.rs +++ b/crates/vim2/src/normal/scroll.rs @@ -108,122 +108,140 @@ fn scroll_editor( } } -// #[cfg(test)] -// mod test { -// use crate::{ -// state::Mode, -// test::{NeovimBackedTestContext, VimTestContext}, -// }; -// use gpui::geometry::vector::vec2f; -// use indoc::indoc; -// use language::Point; - -// #[gpui::test] -// async fn test_scroll(cx: &mut gpui::TestAppContext) { -// let mut cx = VimTestContext::new(cx, true).await; - -// let window = cx.window; -// let line_height = cx.editor(|editor, cx| editor.style().text.line_height(cx.font_cache())); -// window.simulate_resize(vec2f(1000., 8.0 * line_height - 1.0), &mut cx); - -// cx.set_state( -// indoc!( -// "ˇone -// two -// three -// four -// five -// six -// seven -// eight -// nine -// ten -// eleven -// twelve -// " -// ), -// Mode::Normal, -// ); - -// cx.update_editor(|editor, cx| { -// assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 0.)) -// }); -// cx.simulate_keystrokes(["ctrl-e"]); -// cx.update_editor(|editor, cx| { -// assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 1.)) -// }); -// cx.simulate_keystrokes(["2", "ctrl-e"]); -// cx.update_editor(|editor, cx| { -// assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.)) -// }); -// cx.simulate_keystrokes(["ctrl-y"]); -// cx.update_editor(|editor, cx| { -// assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 2.)) -// }); - -// // does not select in normal mode -// cx.simulate_keystrokes(["g", "g"]); -// cx.update_editor(|editor, cx| { -// assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 0.)) -// }); -// cx.simulate_keystrokes(["ctrl-d"]); -// cx.update_editor(|editor, cx| { -// assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.0)); -// assert_eq!( -// editor.selections.newest(cx).range(), -// Point::new(6, 0)..Point::new(6, 0) -// ) -// }); - -// // does select in visual mode -// cx.simulate_keystrokes(["g", "g"]); -// cx.update_editor(|editor, cx| { -// assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 0.)) -// }); -// cx.simulate_keystrokes(["v", "ctrl-d"]); -// cx.update_editor(|editor, cx| { -// assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.0)); -// assert_eq!( -// editor.selections.newest(cx).range(), -// Point::new(0, 0)..Point::new(6, 1) -// ) -// }); -// } -// #[gpui::test] -// async fn test_ctrl_d_u(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx).await; - -// cx.set_scroll_height(10).await; - -// pub fn sample_text(rows: usize, cols: usize, start_char: char) -> String { -// let mut text = String::new(); -// for row in 0..rows { -// let c: char = (start_char as u32 + row as u32) as u8 as char; -// let mut line = c.to_string().repeat(cols); -// if row < rows - 1 { -// line.push('\n'); -// } -// text += &line; -// } -// text -// } -// let content = "ˇ".to_owned() + &sample_text(26, 2, 'a'); -// cx.set_shared_state(&content).await; - -// // skip over the scrolloff at the top -// // test ctrl-d -// cx.simulate_shared_keystrokes(["4", "j", "ctrl-d"]).await; -// cx.assert_state_matches().await; -// cx.simulate_shared_keystrokes(["ctrl-d"]).await; -// cx.assert_state_matches().await; -// cx.simulate_shared_keystrokes(["g", "g", "ctrl-d"]).await; -// cx.assert_state_matches().await; - -// // test ctrl-u -// cx.simulate_shared_keystrokes(["ctrl-u"]).await; -// cx.assert_state_matches().await; -// cx.simulate_shared_keystrokes(["ctrl-d", "ctrl-d", "4", "j", "ctrl-u", "ctrl-u"]) -// .await; -// cx.assert_state_matches().await; -// } -// } +#[cfg(test)] +mod test { + use crate::{ + state::Mode, + test::{NeovimBackedTestContext, VimTestContext}, + }; + use gpui::{point, px, size, Context}; + use indoc::indoc; + use language::Point; + + #[gpui::test] + async fn test_scroll(cx: &mut gpui::TestAppContext) { + let mut cx = VimTestContext::new(cx, true).await; + + let (line_height, visible_line_count) = cx.editor(|editor, cx| { + ( + editor + .style() + .unwrap() + .text + .line_height_in_pixels(cx.rem_size()), + editor.visible_line_count().unwrap(), + ) + }); + + let window = cx.window; + let margin = cx + .update_window(window, |_, cx| { + cx.viewport_size().height - line_height * visible_line_count + }) + .unwrap(); + cx.simulate_window_resize( + cx.window, + size(px(1000.), margin + 8. * line_height - px(1.0)), + ); + + cx.set_state( + indoc!( + "ˇone + two + three + four + five + six + seven + eight + nine + ten + eleven + twelve + " + ), + Mode::Normal, + ); + + cx.update_editor(|editor, cx| { + assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 0.)) + }); + cx.simulate_keystrokes(["ctrl-e"]); + cx.update_editor(|editor, cx| { + assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 1.)) + }); + cx.simulate_keystrokes(["2", "ctrl-e"]); + cx.update_editor(|editor, cx| { + assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 3.)) + }); + cx.simulate_keystrokes(["ctrl-y"]); + cx.update_editor(|editor, cx| { + assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 2.)) + }); + + // does not select in normal mode + cx.simulate_keystrokes(["g", "g"]); + cx.update_editor(|editor, cx| { + assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 0.)) + }); + cx.simulate_keystrokes(["ctrl-d"]); + cx.update_editor(|editor, cx| { + assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 3.0)); + assert_eq!( + editor.selections.newest(cx).range(), + Point::new(6, 0)..Point::new(6, 0) + ) + }); + + // does select in visual mode + cx.simulate_keystrokes(["g", "g"]); + cx.update_editor(|editor, cx| { + assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 0.)) + }); + cx.simulate_keystrokes(["v", "ctrl-d"]); + cx.update_editor(|editor, cx| { + assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 3.0)); + assert_eq!( + editor.selections.newest(cx).range(), + Point::new(0, 0)..Point::new(6, 1) + ) + }); + } + #[gpui::test] + async fn test_ctrl_d_u(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.set_scroll_height(10).await; + + pub fn sample_text(rows: usize, cols: usize, start_char: char) -> String { + let mut text = String::new(); + for row in 0..rows { + let c: char = (start_char as u32 + row as u32) as u8 as char; + let mut line = c.to_string().repeat(cols); + if row < rows - 1 { + line.push('\n'); + } + text += &line; + } + text + } + let content = "ˇ".to_owned() + &sample_text(26, 2, 'a'); + cx.set_shared_state(&content).await; + + // skip over the scrolloff at the top + // test ctrl-d + cx.simulate_shared_keystrokes(["4", "j", "ctrl-d"]).await; + cx.assert_state_matches().await; + cx.simulate_shared_keystrokes(["ctrl-d"]).await; + cx.assert_state_matches().await; + cx.simulate_shared_keystrokes(["g", "g", "ctrl-d"]).await; + cx.assert_state_matches().await; + + // test ctrl-u + cx.simulate_shared_keystrokes(["ctrl-u"]).await; + cx.assert_state_matches().await; + cx.simulate_shared_keystrokes(["ctrl-d", "ctrl-d", "4", "j", "ctrl-u", "ctrl-u"]) + .await; + cx.assert_state_matches().await; + } +} diff --git a/crates/vim2/src/normal/search.rs b/crates/vim2/src/normal/search.rs index ef26d029a534c1fe3046b6534aa699465a49198c..3873c5b78b646aca83dfd825a9fec760d5f2ea7f 100644 --- a/crates/vim2/src/normal/search.rs +++ b/crates/vim2/src/normal/search.rs @@ -347,58 +347,50 @@ fn parse_replace_all(query: &str) -> Replacement { // #[cfg(test)] // mod test { -// use std::sync::Arc; - // use editor::DisplayPoint; // use search::BufferSearchBar; // use crate::{state::Mode, test::VimTestContext}; // #[gpui::test] -// async fn test_move_to_next( -// cx: &mut gpui::TestAppContext, -// deterministic: Arc, -// ) { +// async fn test_move_to_next(cx: &mut gpui::TestAppContext) { // let mut cx = VimTestContext::new(cx, true).await; // cx.set_state("ˇhi\nhigh\nhi\n", Mode::Normal); // cx.simulate_keystrokes(["*"]); -// deterministic.run_until_parked(); +// cx.run_until_parked(); // cx.assert_state("hi\nhigh\nˇhi\n", Mode::Normal); // cx.simulate_keystrokes(["*"]); -// deterministic.run_until_parked(); +// cx.run_until_parked(); // cx.assert_state("ˇhi\nhigh\nhi\n", Mode::Normal); // cx.simulate_keystrokes(["#"]); -// deterministic.run_until_parked(); +// cx.run_until_parked(); // cx.assert_state("hi\nhigh\nˇhi\n", Mode::Normal); // cx.simulate_keystrokes(["#"]); -// deterministic.run_until_parked(); +// cx.run_until_parked(); // cx.assert_state("ˇhi\nhigh\nhi\n", Mode::Normal); // cx.simulate_keystrokes(["2", "*"]); -// deterministic.run_until_parked(); +// cx.run_until_parked(); // cx.assert_state("ˇhi\nhigh\nhi\n", Mode::Normal); // cx.simulate_keystrokes(["g", "*"]); -// deterministic.run_until_parked(); +// cx.run_until_parked(); // cx.assert_state("hi\nˇhigh\nhi\n", Mode::Normal); // cx.simulate_keystrokes(["n"]); // cx.assert_state("hi\nhigh\nˇhi\n", Mode::Normal); // cx.simulate_keystrokes(["g", "#"]); -// deterministic.run_until_parked(); +// cx.run_until_parked(); // cx.assert_state("hi\nˇhigh\nhi\n", Mode::Normal); // } // #[gpui::test] -// async fn test_search( -// cx: &mut gpui::TestAppContext, -// deterministic: Arc, -// ) { +// async fn test_search(cx: &mut gpui::TestAppContext) { // let mut cx = VimTestContext::new(cx, true).await; // cx.set_state("aa\nbˇb\ncc\ncc\ncc\n", Mode::Normal); @@ -414,11 +406,11 @@ fn parse_replace_all(query: &str) -> Replacement { // .expect("Buffer search bar should be deployed") // }); -// search_bar.read_with(cx.cx, |bar, cx| { +// cx.update_view(search_bar, |bar, cx| { // assert_eq!(bar.query(cx), "cc"); // }); -// deterministic.run_until_parked(); +// cx.run_until_parked(); // cx.update_editor(|editor, cx| { // let highlights = editor.all_text_background_highlights(cx); @@ -440,51 +432,41 @@ fn parse_replace_all(query: &str) -> Replacement { // // ? to go to previous // cx.simulate_keystrokes(["?", "enter"]); -// deterministic.run_until_parked(); // cx.assert_state("aa\nbb\ncc\ncc\nˇcc\n", Mode::Normal); // cx.simulate_keystrokes(["?", "enter"]); -// deterministic.run_until_parked(); // cx.assert_state("aa\nbb\ncc\nˇcc\ncc\n", Mode::Normal); // // / to go to next // cx.simulate_keystrokes(["/", "enter"]); -// deterministic.run_until_parked(); // cx.assert_state("aa\nbb\ncc\ncc\nˇcc\n", Mode::Normal); // // ?{search} to search backwards // cx.simulate_keystrokes(["?", "b", "enter"]); -// deterministic.run_until_parked(); // cx.assert_state("aa\nbˇb\ncc\ncc\ncc\n", Mode::Normal); // // works with counts // cx.simulate_keystrokes(["4", "/", "c"]); -// deterministic.run_until_parked(); // cx.simulate_keystrokes(["enter"]); // cx.assert_state("aa\nbb\ncc\ncˇc\ncc\n", Mode::Normal); // // check that searching resumes from cursor, not previous match // cx.set_state("ˇaa\nbb\ndd\ncc\nbb\n", Mode::Normal); // cx.simulate_keystrokes(["/", "d"]); -// deterministic.run_until_parked(); // cx.simulate_keystrokes(["enter"]); // cx.assert_state("aa\nbb\nˇdd\ncc\nbb\n", Mode::Normal); // cx.update_editor(|editor, cx| editor.move_to_beginning(&Default::default(), cx)); // cx.assert_state("ˇaa\nbb\ndd\ncc\nbb\n", Mode::Normal); // cx.simulate_keystrokes(["/", "b"]); -// deterministic.run_until_parked(); // cx.simulate_keystrokes(["enter"]); // cx.assert_state("aa\nˇbb\ndd\ncc\nbb\n", Mode::Normal); // } // #[gpui::test] -// async fn test_non_vim_search( -// cx: &mut gpui::TestAppContext, -// deterministic: Arc, -// ) { +// async fn test_non_vim_search(cx: &mut gpui::TestAppContext) { // let mut cx = VimTestContext::new(cx, false).await; // cx.set_state("ˇone one one one", Mode::Normal); // cx.simulate_keystrokes(["cmd-f"]); -// deterministic.run_until_parked(); +// cx.run_until_parked(); // cx.assert_editor_state("«oneˇ» one one one"); // cx.simulate_keystrokes(["enter"]); diff --git a/crates/vim2/src/normal/substitute.rs b/crates/vim2/src/normal/substitute.rs index f623b499e0eb541532319ce13a73b52b8c0835fc..63fa58dbed4d1a3f056e48c6f29a4b84088910d7 100644 --- a/crates/vim2/src/normal/substitute.rs +++ b/crates/vim2/src/normal/substitute.rs @@ -81,196 +81,196 @@ pub fn substitute(vim: &mut Vim, count: Option, line_mode: bool, cx: &mut vim.switch_mode(Mode::Insert, true, cx); } -// #[cfg(test)] -// mod test { -// use crate::{ -// state::Mode, -// test::{NeovimBackedTestContext, VimTestContext}, -// }; -// use indoc::indoc; +#[cfg(test)] +mod test { + use crate::{ + state::Mode, + test::{NeovimBackedTestContext, VimTestContext}, + }; + use indoc::indoc; -// #[gpui::test] -// async fn test_substitute(cx: &mut gpui::TestAppContext) { -// let mut cx = VimTestContext::new(cx, true).await; + #[gpui::test] + async fn test_substitute(cx: &mut gpui::TestAppContext) { + let mut cx = VimTestContext::new(cx, true).await; -// // supports a single cursor -// cx.set_state(indoc! {"ˇabc\n"}, Mode::Normal); -// cx.simulate_keystrokes(["s", "x"]); -// cx.assert_editor_state("xˇbc\n"); + // supports a single cursor + cx.set_state(indoc! {"ˇabc\n"}, Mode::Normal); + cx.simulate_keystrokes(["s", "x"]); + cx.assert_editor_state("xˇbc\n"); -// // supports a selection -// cx.set_state(indoc! {"a«bcˇ»\n"}, Mode::Visual); -// cx.assert_editor_state("a«bcˇ»\n"); -// cx.simulate_keystrokes(["s", "x"]); -// cx.assert_editor_state("axˇ\n"); + // supports a selection + cx.set_state(indoc! {"a«bcˇ»\n"}, Mode::Visual); + cx.assert_editor_state("a«bcˇ»\n"); + cx.simulate_keystrokes(["s", "x"]); + cx.assert_editor_state("axˇ\n"); -// // supports counts -// cx.set_state(indoc! {"ˇabc\n"}, Mode::Normal); -// cx.simulate_keystrokes(["2", "s", "x"]); -// cx.assert_editor_state("xˇc\n"); + // supports counts + cx.set_state(indoc! {"ˇabc\n"}, Mode::Normal); + cx.simulate_keystrokes(["2", "s", "x"]); + cx.assert_editor_state("xˇc\n"); -// // supports multiple cursors -// cx.set_state(indoc! {"a«bcˇ»deˇffg\n"}, Mode::Normal); -// cx.simulate_keystrokes(["2", "s", "x"]); -// cx.assert_editor_state("axˇdexˇg\n"); + // supports multiple cursors + cx.set_state(indoc! {"a«bcˇ»deˇffg\n"}, Mode::Normal); + cx.simulate_keystrokes(["2", "s", "x"]); + cx.assert_editor_state("axˇdexˇg\n"); -// // does not read beyond end of line -// cx.set_state(indoc! {"ˇabc\n"}, Mode::Normal); -// cx.simulate_keystrokes(["5", "s", "x"]); -// cx.assert_editor_state("xˇ\n"); + // does not read beyond end of line + cx.set_state(indoc! {"ˇabc\n"}, Mode::Normal); + cx.simulate_keystrokes(["5", "s", "x"]); + cx.assert_editor_state("xˇ\n"); -// // it handles multibyte characters -// cx.set_state(indoc! {"ˇcàfé\n"}, Mode::Normal); -// cx.simulate_keystrokes(["4", "s"]); -// cx.assert_editor_state("ˇ\n"); + // it handles multibyte characters + cx.set_state(indoc! {"ˇcàfé\n"}, Mode::Normal); + cx.simulate_keystrokes(["4", "s"]); + cx.assert_editor_state("ˇ\n"); -// // should transactionally undo selection changes -// cx.simulate_keystrokes(["escape", "u"]); -// cx.assert_editor_state("ˇcàfé\n"); + // should transactionally undo selection changes + cx.simulate_keystrokes(["escape", "u"]); + cx.assert_editor_state("ˇcàfé\n"); -// // it handles visual line mode -// cx.set_state( -// indoc! {" -// alpha -// beˇta -// gamma"}, -// Mode::Normal, -// ); -// cx.simulate_keystrokes(["shift-v", "s"]); -// cx.assert_editor_state(indoc! {" -// alpha -// ˇ -// gamma"}); -// } + // it handles visual line mode + cx.set_state( + indoc! {" + alpha + beˇta + gamma"}, + Mode::Normal, + ); + cx.simulate_keystrokes(["shift-v", "s"]); + cx.assert_editor_state(indoc! {" + alpha + ˇ + gamma"}); + } -// #[gpui::test] -// async fn test_visual_change(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx).await; + #[gpui::test] + async fn test_visual_change(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; -// cx.set_shared_state("The quick ˇbrown").await; -// cx.simulate_shared_keystrokes(["v", "w", "c"]).await; -// cx.assert_shared_state("The quick ˇ").await; + cx.set_shared_state("The quick ˇbrown").await; + cx.simulate_shared_keystrokes(["v", "w", "c"]).await; + cx.assert_shared_state("The quick ˇ").await; -// cx.set_shared_state(indoc! {" -// The ˇquick brown -// fox jumps over -// the lazy dog"}) -// .await; -// cx.simulate_shared_keystrokes(["v", "w", "j", "c"]).await; -// cx.assert_shared_state(indoc! {" -// The ˇver -// the lazy dog"}) -// .await; + cx.set_shared_state(indoc! {" + The ˇquick brown + fox jumps over + the lazy dog"}) + .await; + cx.simulate_shared_keystrokes(["v", "w", "j", "c"]).await; + cx.assert_shared_state(indoc! {" + The ˇver + the lazy dog"}) + .await; -// let cases = cx.each_marked_position(indoc! {" -// The ˇquick brown -// fox jumps ˇover -// the ˇlazy dog"}); -// for initial_state in cases { -// cx.assert_neovim_compatible(&initial_state, ["v", "w", "j", "c"]) -// .await; -// cx.assert_neovim_compatible(&initial_state, ["v", "w", "k", "c"]) -// .await; -// } -// } + let cases = cx.each_marked_position(indoc! {" + The ˇquick brown + fox jumps ˇover + the ˇlazy dog"}); + for initial_state in cases { + cx.assert_neovim_compatible(&initial_state, ["v", "w", "j", "c"]) + .await; + cx.assert_neovim_compatible(&initial_state, ["v", "w", "k", "c"]) + .await; + } + } -// #[gpui::test] -// async fn test_visual_line_change(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx) -// .await -// .binding(["shift-v", "c"]); -// cx.assert(indoc! {" -// The quˇick brown -// fox jumps over -// the lazy dog"}) -// .await; -// // Test pasting code copied on change -// cx.simulate_shared_keystrokes(["escape", "j", "p"]).await; -// cx.assert_state_matches().await; + #[gpui::test] + async fn test_visual_line_change(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx) + .await + .binding(["shift-v", "c"]); + cx.assert(indoc! {" + The quˇick brown + fox jumps over + the lazy dog"}) + .await; + // Test pasting code copied on change + cx.simulate_shared_keystrokes(["escape", "j", "p"]).await; + cx.assert_state_matches().await; -// cx.assert_all(indoc! {" -// The quick brown -// fox juˇmps over -// the laˇzy dog"}) -// .await; -// let mut cx = cx.binding(["shift-v", "j", "c"]); -// cx.assert(indoc! {" -// The quˇick brown -// fox jumps over -// the lazy dog"}) -// .await; -// // Test pasting code copied on delete -// cx.simulate_shared_keystrokes(["escape", "j", "p"]).await; -// cx.assert_state_matches().await; + cx.assert_all(indoc! {" + The quick brown + fox juˇmps over + the laˇzy dog"}) + .await; + let mut cx = cx.binding(["shift-v", "j", "c"]); + cx.assert(indoc! {" + The quˇick brown + fox jumps over + the lazy dog"}) + .await; + // Test pasting code copied on delete + cx.simulate_shared_keystrokes(["escape", "j", "p"]).await; + cx.assert_state_matches().await; -// cx.assert_all(indoc! {" -// The quick brown -// fox juˇmps over -// the laˇzy dog"}) -// .await; -// } + cx.assert_all(indoc! {" + The quick brown + fox juˇmps over + the laˇzy dog"}) + .await; + } -// #[gpui::test] -// async fn test_substitute_line(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx).await; + #[gpui::test] + async fn test_substitute_line(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; -// let initial_state = indoc! {" -// The quick brown -// fox juˇmps over -// the lazy dog -// "}; + let initial_state = indoc! {" + The quick brown + fox juˇmps over + the lazy dog + "}; -// // normal mode -// cx.set_shared_state(initial_state).await; -// cx.simulate_shared_keystrokes(["shift-s", "o"]).await; -// cx.assert_shared_state(indoc! {" -// The quick brown -// oˇ -// the lazy dog -// "}) -// .await; + // normal mode + cx.set_shared_state(initial_state).await; + cx.simulate_shared_keystrokes(["shift-s", "o"]).await; + cx.assert_shared_state(indoc! {" + The quick brown + oˇ + the lazy dog + "}) + .await; -// // visual mode -// cx.set_shared_state(initial_state).await; -// cx.simulate_shared_keystrokes(["v", "k", "shift-s", "o"]) -// .await; -// cx.assert_shared_state(indoc! {" -// oˇ -// the lazy dog -// "}) -// .await; + // visual mode + cx.set_shared_state(initial_state).await; + cx.simulate_shared_keystrokes(["v", "k", "shift-s", "o"]) + .await; + cx.assert_shared_state(indoc! {" + oˇ + the lazy dog + "}) + .await; -// // visual block mode -// cx.set_shared_state(initial_state).await; -// cx.simulate_shared_keystrokes(["ctrl-v", "j", "shift-s", "o"]) -// .await; -// cx.assert_shared_state(indoc! {" -// The quick brown -// oˇ -// "}) -// .await; + // visual block mode + cx.set_shared_state(initial_state).await; + cx.simulate_shared_keystrokes(["ctrl-v", "j", "shift-s", "o"]) + .await; + cx.assert_shared_state(indoc! {" + The quick brown + oˇ + "}) + .await; -// // visual mode including newline -// cx.set_shared_state(initial_state).await; -// cx.simulate_shared_keystrokes(["v", "$", "shift-s", "o"]) -// .await; -// cx.assert_shared_state(indoc! {" -// The quick brown -// oˇ -// the lazy dog -// "}) -// .await; + // visual mode including newline + cx.set_shared_state(initial_state).await; + cx.simulate_shared_keystrokes(["v", "$", "shift-s", "o"]) + .await; + cx.assert_shared_state(indoc! {" + The quick brown + oˇ + the lazy dog + "}) + .await; -// // indentation -// cx.set_neovim_option("shiftwidth=4").await; -// cx.set_shared_state(initial_state).await; -// cx.simulate_shared_keystrokes([">", ">", "shift-s", "o"]) -// .await; -// cx.assert_shared_state(indoc! {" -// The quick brown -// oˇ -// the lazy dog -// "}) -// .await; -// } -// } + // indentation + cx.set_neovim_option("shiftwidth=4").await; + cx.set_shared_state(initial_state).await; + cx.simulate_shared_keystrokes([">", ">", "shift-s", "o"]) + .await; + cx.assert_shared_state(indoc! {" + The quick brown + oˇ + the lazy dog + "}) + .await; + } +} diff --git a/crates/vim2/src/object.rs b/crates/vim2/src/object.rs index d25dfb083140aa6bdf03ed1052b2a1b0d69073d7..47d1647dc765b6c76896cb1bdfc3af0fff5a7f07 100644 --- a/crates/vim2/src/object.rs +++ b/crates/vim2/src/object.rs @@ -587,439 +587,439 @@ fn surrounding_markers( ) } -// #[cfg(test)] -// mod test { -// use indoc::indoc; - -// use crate::{ -// state::Mode, -// test::{ExemptionFeatures, NeovimBackedTestContext, VimTestContext}, -// }; - -// const WORD_LOCATIONS: &'static str = indoc! {" -// The quick ˇbrowˇnˇ••• -// fox ˇjuˇmpsˇ over -// the lazy dogˇ•• -// ˇ -// ˇ -// ˇ -// Thˇeˇ-ˇquˇickˇ ˇbrownˇ• -// ˇ•• -// ˇ•• -// ˇ fox-jumpˇs over -// the lazy dogˇ• -// ˇ -// " -// }; - -// #[gpui::test] -// async fn test_change_word_object(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx).await; - -// cx.assert_binding_matches_all(["c", "i", "w"], WORD_LOCATIONS) -// .await; -// cx.assert_binding_matches_all(["c", "i", "shift-w"], WORD_LOCATIONS) -// .await; -// cx.assert_binding_matches_all(["c", "a", "w"], WORD_LOCATIONS) -// .await; -// cx.assert_binding_matches_all(["c", "a", "shift-w"], WORD_LOCATIONS) -// .await; -// } - -// #[gpui::test] -// async fn test_delete_word_object(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx).await; - -// cx.assert_binding_matches_all(["d", "i", "w"], WORD_LOCATIONS) -// .await; -// cx.assert_binding_matches_all(["d", "i", "shift-w"], WORD_LOCATIONS) -// .await; -// cx.assert_binding_matches_all(["d", "a", "w"], WORD_LOCATIONS) -// .await; -// cx.assert_binding_matches_all(["d", "a", "shift-w"], WORD_LOCATIONS) -// .await; -// } - -// #[gpui::test] -// async fn test_visual_word_object(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx).await; - -// /* -// cx.set_shared_state("The quick ˇbrown\nfox").await; -// cx.simulate_shared_keystrokes(["v"]).await; -// cx.assert_shared_state("The quick «bˇ»rown\nfox").await; -// cx.simulate_shared_keystrokes(["i", "w"]).await; -// cx.assert_shared_state("The quick «brownˇ»\nfox").await; -// */ -// cx.set_shared_state("The quick brown\nˇ\nfox").await; -// cx.simulate_shared_keystrokes(["v"]).await; -// cx.assert_shared_state("The quick brown\n«\nˇ»fox").await; -// cx.simulate_shared_keystrokes(["i", "w"]).await; -// cx.assert_shared_state("The quick brown\n«\nˇ»fox").await; - -// cx.assert_binding_matches_all(["v", "i", "w"], WORD_LOCATIONS) -// .await; -// cx.assert_binding_matches_all_exempted( -// ["v", "h", "i", "w"], -// WORD_LOCATIONS, -// ExemptionFeatures::NonEmptyVisualTextObjects, -// ) -// .await; -// cx.assert_binding_matches_all_exempted( -// ["v", "l", "i", "w"], -// WORD_LOCATIONS, -// ExemptionFeatures::NonEmptyVisualTextObjects, -// ) -// .await; -// cx.assert_binding_matches_all(["v", "i", "shift-w"], WORD_LOCATIONS) -// .await; - -// cx.assert_binding_matches_all_exempted( -// ["v", "i", "h", "shift-w"], -// WORD_LOCATIONS, -// ExemptionFeatures::NonEmptyVisualTextObjects, -// ) -// .await; -// cx.assert_binding_matches_all_exempted( -// ["v", "i", "l", "shift-w"], -// WORD_LOCATIONS, -// ExemptionFeatures::NonEmptyVisualTextObjects, -// ) -// .await; - -// cx.assert_binding_matches_all_exempted( -// ["v", "a", "w"], -// WORD_LOCATIONS, -// ExemptionFeatures::AroundObjectLeavesWhitespaceAtEndOfLine, -// ) -// .await; -// cx.assert_binding_matches_all_exempted( -// ["v", "a", "shift-w"], -// WORD_LOCATIONS, -// ExemptionFeatures::AroundObjectLeavesWhitespaceAtEndOfLine, -// ) -// .await; -// } - -// const SENTENCE_EXAMPLES: &[&'static str] = &[ -// "ˇThe quick ˇbrownˇ?ˇ ˇFox Jˇumpsˇ!ˇ Ovˇer theˇ lazyˇ.", -// indoc! {" -// ˇThe quick ˇbrownˇ -// fox jumps over -// the lazy doˇgˇ.ˇ ˇThe quick ˇ -// brown fox jumps over -// "}, -// indoc! {" -// The quick brown fox jumps. -// Over the lazy dog -// ˇ -// ˇ -// ˇ fox-jumpˇs over -// the lazy dog.ˇ -// ˇ -// "}, -// r#"ˇThe ˇquick brownˇ.)ˇ]ˇ'ˇ" Brown ˇfox jumpsˇ.ˇ "#, -// ]; - -// #[gpui::test] -// async fn test_change_sentence_object(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx) -// .await -// .binding(["c", "i", "s"]); -// cx.add_initial_state_exemptions( -// "The quick brown fox jumps.\nOver the lazy dog\nˇ\nˇ\n fox-jumps over\nthe lazy dog.\n\n", -// ExemptionFeatures::SentenceOnEmptyLines); -// cx.add_initial_state_exemptions( -// "The quick brown fox jumps.\nOver the lazy dog\n\n\nˇ foxˇ-ˇjumpˇs over\nthe lazy dog.\n\n", -// ExemptionFeatures::SentenceAtStartOfLineWithWhitespace); -// cx.add_initial_state_exemptions( -// "The quick brown fox jumps.\nOver the lazy dog\n\n\n fox-jumps over\nthe lazy dog.ˇ\nˇ\n", -// ExemptionFeatures::SentenceAfterPunctuationAtEndOfFile); -// for sentence_example in SENTENCE_EXAMPLES { -// cx.assert_all(sentence_example).await; -// } - -// let mut cx = cx.binding(["c", "a", "s"]); -// cx.add_initial_state_exemptions( -// "The quick brown?ˇ Fox Jumps! Over the lazy.", -// ExemptionFeatures::IncorrectLandingPosition, -// ); -// cx.add_initial_state_exemptions( -// "The quick brown.)]\'\" Brown fox jumps.ˇ ", -// ExemptionFeatures::AroundObjectLeavesWhitespaceAtEndOfLine, -// ); - -// for sentence_example in SENTENCE_EXAMPLES { -// cx.assert_all(sentence_example).await; -// } -// } - -// #[gpui::test] -// async fn test_delete_sentence_object(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx) -// .await -// .binding(["d", "i", "s"]); -// cx.add_initial_state_exemptions( -// "The quick brown fox jumps.\nOver the lazy dog\nˇ\nˇ\n fox-jumps over\nthe lazy dog.\n\n", -// ExemptionFeatures::SentenceOnEmptyLines); -// cx.add_initial_state_exemptions( -// "The quick brown fox jumps.\nOver the lazy dog\n\n\nˇ foxˇ-ˇjumpˇs over\nthe lazy dog.\n\n", -// ExemptionFeatures::SentenceAtStartOfLineWithWhitespace); -// cx.add_initial_state_exemptions( -// "The quick brown fox jumps.\nOver the lazy dog\n\n\n fox-jumps over\nthe lazy dog.ˇ\nˇ\n", -// ExemptionFeatures::SentenceAfterPunctuationAtEndOfFile); - -// for sentence_example in SENTENCE_EXAMPLES { -// cx.assert_all(sentence_example).await; -// } - -// let mut cx = cx.binding(["d", "a", "s"]); -// cx.add_initial_state_exemptions( -// "The quick brown?ˇ Fox Jumps! Over the lazy.", -// ExemptionFeatures::IncorrectLandingPosition, -// ); -// cx.add_initial_state_exemptions( -// "The quick brown.)]\'\" Brown fox jumps.ˇ ", -// ExemptionFeatures::AroundObjectLeavesWhitespaceAtEndOfLine, -// ); - -// for sentence_example in SENTENCE_EXAMPLES { -// cx.assert_all(sentence_example).await; -// } -// } - -// #[gpui::test] -// async fn test_visual_sentence_object(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx) -// .await -// .binding(["v", "i", "s"]); -// for sentence_example in SENTENCE_EXAMPLES { -// cx.assert_all_exempted(sentence_example, ExemptionFeatures::SentenceOnEmptyLines) -// .await; -// } - -// let mut cx = cx.binding(["v", "a", "s"]); -// for sentence_example in SENTENCE_EXAMPLES { -// cx.assert_all_exempted( -// sentence_example, -// ExemptionFeatures::AroundSentenceStartingBetweenIncludesWrongWhitespace, -// ) -// .await; -// } -// } - -// // Test string with "`" for opening surrounders and "'" for closing surrounders -// const SURROUNDING_MARKER_STRING: &str = indoc! {" -// ˇTh'ˇe ˇ`ˇ'ˇquˇi`ˇck broˇ'wn` -// 'ˇfox juˇmps ovˇ`ˇer -// the ˇlazy dˇ'ˇoˇ`ˇg"}; - -// const SURROUNDING_OBJECTS: &[(char, char)] = &[ -// ('\'', '\''), // Quote -// ('`', '`'), // Back Quote -// ('"', '"'), // Double Quote -// ('(', ')'), // Parentheses -// ('[', ']'), // SquareBrackets -// ('{', '}'), // CurlyBrackets -// ('<', '>'), // AngleBrackets -// ]; - -// #[gpui::test] -// async fn test_change_surrounding_character_objects(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx).await; - -// for (start, end) in SURROUNDING_OBJECTS { -// let marked_string = SURROUNDING_MARKER_STRING -// .replace('`', &start.to_string()) -// .replace('\'', &end.to_string()); - -// cx.assert_binding_matches_all(["c", "i", &start.to_string()], &marked_string) -// .await; -// cx.assert_binding_matches_all(["c", "i", &end.to_string()], &marked_string) -// .await; -// cx.assert_binding_matches_all(["c", "a", &start.to_string()], &marked_string) -// .await; -// cx.assert_binding_matches_all(["c", "a", &end.to_string()], &marked_string) -// .await; -// } -// } -// #[gpui::test] -// async fn test_singleline_surrounding_character_objects(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx).await; -// cx.set_shared_wrap(12).await; - -// cx.set_shared_state(indoc! { -// "helˇlo \"world\"!" -// }) -// .await; -// cx.simulate_shared_keystrokes(["v", "i", "\""]).await; -// cx.assert_shared_state(indoc! { -// "hello \"«worldˇ»\"!" -// }) -// .await; - -// cx.set_shared_state(indoc! { -// "hello \"wˇorld\"!" -// }) -// .await; -// cx.simulate_shared_keystrokes(["v", "i", "\""]).await; -// cx.assert_shared_state(indoc! { -// "hello \"«worldˇ»\"!" -// }) -// .await; - -// cx.set_shared_state(indoc! { -// "hello \"wˇorld\"!" -// }) -// .await; -// cx.simulate_shared_keystrokes(["v", "a", "\""]).await; -// cx.assert_shared_state(indoc! { -// "hello« \"world\"ˇ»!" -// }) -// .await; - -// cx.set_shared_state(indoc! { -// "hello \"wˇorld\" !" -// }) -// .await; -// cx.simulate_shared_keystrokes(["v", "a", "\""]).await; -// cx.assert_shared_state(indoc! { -// "hello «\"world\" ˇ»!" -// }) -// .await; - -// cx.set_shared_state(indoc! { -// "hello \"wˇorld\"• -// goodbye" -// }) -// .await; -// cx.simulate_shared_keystrokes(["v", "a", "\""]).await; -// cx.assert_shared_state(indoc! { -// "hello «\"world\" ˇ» -// goodbye" -// }) -// .await; -// } - -// #[gpui::test] -// async fn test_multiline_surrounding_character_objects(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx).await; - -// cx.set_shared_state(indoc! { -// "func empty(a string) bool { -// if a == \"\" { -// return true -// } -// ˇreturn false -// }" -// }) -// .await; -// cx.simulate_shared_keystrokes(["v", "i", "{"]).await; -// cx.assert_shared_state(indoc! {" -// func empty(a string) bool { -// « if a == \"\" { -// return true -// } -// return false -// ˇ»}"}) -// .await; -// cx.set_shared_state(indoc! { -// "func empty(a string) bool { -// if a == \"\" { -// ˇreturn true -// } -// return false -// }" -// }) -// .await; -// cx.simulate_shared_keystrokes(["v", "i", "{"]).await; -// cx.assert_shared_state(indoc! {" -// func empty(a string) bool { -// if a == \"\" { -// « return true -// ˇ» } -// return false -// }"}) -// .await; - -// cx.set_shared_state(indoc! { -// "func empty(a string) bool { -// if a == \"\" ˇ{ -// return true -// } -// return false -// }" -// }) -// .await; -// cx.simulate_shared_keystrokes(["v", "i", "{"]).await; -// cx.assert_shared_state(indoc! {" -// func empty(a string) bool { -// if a == \"\" { -// « return true -// ˇ» } -// return false -// }"}) -// .await; -// } - -// #[gpui::test] -// async fn test_vertical_bars(cx: &mut gpui::TestAppContext) { -// let mut cx = VimTestContext::new(cx, true).await; -// cx.set_state( -// indoc! {" -// fn boop() { -// baz(ˇ|a, b| { bar(|j, k| { })}) -// }" -// }, -// Mode::Normal, -// ); -// cx.simulate_keystrokes(["c", "i", "|"]); -// cx.assert_state( -// indoc! {" -// fn boop() { -// baz(|ˇ| { bar(|j, k| { })}) -// }" -// }, -// Mode::Insert, -// ); -// cx.simulate_keystrokes(["escape", "1", "8", "|"]); -// cx.assert_state( -// indoc! {" -// fn boop() { -// baz(|| { bar(ˇ|j, k| { })}) -// }" -// }, -// Mode::Normal, -// ); - -// cx.simulate_keystrokes(["v", "a", "|"]); -// cx.assert_state( -// indoc! {" -// fn boop() { -// baz(|| { bar(«|j, k| ˇ»{ })}) -// }" -// }, -// Mode::Visual, -// ); -// } - -// #[gpui::test] -// async fn test_delete_surrounding_character_objects(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx).await; - -// for (start, end) in SURROUNDING_OBJECTS { -// let marked_string = SURROUNDING_MARKER_STRING -// .replace('`', &start.to_string()) -// .replace('\'', &end.to_string()); - -// cx.assert_binding_matches_all(["d", "i", &start.to_string()], &marked_string) -// .await; -// cx.assert_binding_matches_all(["d", "i", &end.to_string()], &marked_string) -// .await; -// cx.assert_binding_matches_all(["d", "a", &start.to_string()], &marked_string) -// .await; -// cx.assert_binding_matches_all(["d", "a", &end.to_string()], &marked_string) -// .await; -// } -// } -// } +#[cfg(test)] +mod test { + use indoc::indoc; + + use crate::{ + state::Mode, + test::{ExemptionFeatures, NeovimBackedTestContext, VimTestContext}, + }; + + const WORD_LOCATIONS: &'static str = indoc! {" + The quick ˇbrowˇnˇ••• + fox ˇjuˇmpsˇ over + the lazy dogˇ•• + ˇ + ˇ + ˇ + Thˇeˇ-ˇquˇickˇ ˇbrownˇ• + ˇ•• + ˇ•• + ˇ fox-jumpˇs over + the lazy dogˇ• + ˇ + " + }; + + #[gpui::test] + async fn test_change_word_object(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.assert_binding_matches_all(["c", "i", "w"], WORD_LOCATIONS) + .await; + cx.assert_binding_matches_all(["c", "i", "shift-w"], WORD_LOCATIONS) + .await; + cx.assert_binding_matches_all(["c", "a", "w"], WORD_LOCATIONS) + .await; + cx.assert_binding_matches_all(["c", "a", "shift-w"], WORD_LOCATIONS) + .await; + } + + #[gpui::test] + async fn test_delete_word_object(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.assert_binding_matches_all(["d", "i", "w"], WORD_LOCATIONS) + .await; + cx.assert_binding_matches_all(["d", "i", "shift-w"], WORD_LOCATIONS) + .await; + cx.assert_binding_matches_all(["d", "a", "w"], WORD_LOCATIONS) + .await; + cx.assert_binding_matches_all(["d", "a", "shift-w"], WORD_LOCATIONS) + .await; + } + + #[gpui::test] + async fn test_visual_word_object(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + /* + cx.set_shared_state("The quick ˇbrown\nfox").await; + cx.simulate_shared_keystrokes(["v"]).await; + cx.assert_shared_state("The quick «bˇ»rown\nfox").await; + cx.simulate_shared_keystrokes(["i", "w"]).await; + cx.assert_shared_state("The quick «brownˇ»\nfox").await; + */ + cx.set_shared_state("The quick brown\nˇ\nfox").await; + cx.simulate_shared_keystrokes(["v"]).await; + cx.assert_shared_state("The quick brown\n«\nˇ»fox").await; + cx.simulate_shared_keystrokes(["i", "w"]).await; + cx.assert_shared_state("The quick brown\n«\nˇ»fox").await; + + cx.assert_binding_matches_all(["v", "i", "w"], WORD_LOCATIONS) + .await; + cx.assert_binding_matches_all_exempted( + ["v", "h", "i", "w"], + WORD_LOCATIONS, + ExemptionFeatures::NonEmptyVisualTextObjects, + ) + .await; + cx.assert_binding_matches_all_exempted( + ["v", "l", "i", "w"], + WORD_LOCATIONS, + ExemptionFeatures::NonEmptyVisualTextObjects, + ) + .await; + cx.assert_binding_matches_all(["v", "i", "shift-w"], WORD_LOCATIONS) + .await; + + cx.assert_binding_matches_all_exempted( + ["v", "i", "h", "shift-w"], + WORD_LOCATIONS, + ExemptionFeatures::NonEmptyVisualTextObjects, + ) + .await; + cx.assert_binding_matches_all_exempted( + ["v", "i", "l", "shift-w"], + WORD_LOCATIONS, + ExemptionFeatures::NonEmptyVisualTextObjects, + ) + .await; + + cx.assert_binding_matches_all_exempted( + ["v", "a", "w"], + WORD_LOCATIONS, + ExemptionFeatures::AroundObjectLeavesWhitespaceAtEndOfLine, + ) + .await; + cx.assert_binding_matches_all_exempted( + ["v", "a", "shift-w"], + WORD_LOCATIONS, + ExemptionFeatures::AroundObjectLeavesWhitespaceAtEndOfLine, + ) + .await; + } + + const SENTENCE_EXAMPLES: &[&'static str] = &[ + "ˇThe quick ˇbrownˇ?ˇ ˇFox Jˇumpsˇ!ˇ Ovˇer theˇ lazyˇ.", + indoc! {" + ˇThe quick ˇbrownˇ + fox jumps over + the lazy doˇgˇ.ˇ ˇThe quick ˇ + brown fox jumps over + "}, + indoc! {" + The quick brown fox jumps. + Over the lazy dog + ˇ + ˇ + ˇ fox-jumpˇs over + the lazy dog.ˇ + ˇ + "}, + r#"ˇThe ˇquick brownˇ.)ˇ]ˇ'ˇ" Brown ˇfox jumpsˇ.ˇ "#, + ]; + + #[gpui::test] + async fn test_change_sentence_object(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx) + .await + .binding(["c", "i", "s"]); + cx.add_initial_state_exemptions( + "The quick brown fox jumps.\nOver the lazy dog\nˇ\nˇ\n fox-jumps over\nthe lazy dog.\n\n", + ExemptionFeatures::SentenceOnEmptyLines); + cx.add_initial_state_exemptions( + "The quick brown fox jumps.\nOver the lazy dog\n\n\nˇ foxˇ-ˇjumpˇs over\nthe lazy dog.\n\n", + ExemptionFeatures::SentenceAtStartOfLineWithWhitespace); + cx.add_initial_state_exemptions( + "The quick brown fox jumps.\nOver the lazy dog\n\n\n fox-jumps over\nthe lazy dog.ˇ\nˇ\n", + ExemptionFeatures::SentenceAfterPunctuationAtEndOfFile); + for sentence_example in SENTENCE_EXAMPLES { + cx.assert_all(sentence_example).await; + } + + let mut cx = cx.binding(["c", "a", "s"]); + cx.add_initial_state_exemptions( + "The quick brown?ˇ Fox Jumps! Over the lazy.", + ExemptionFeatures::IncorrectLandingPosition, + ); + cx.add_initial_state_exemptions( + "The quick brown.)]\'\" Brown fox jumps.ˇ ", + ExemptionFeatures::AroundObjectLeavesWhitespaceAtEndOfLine, + ); + + for sentence_example in SENTENCE_EXAMPLES { + cx.assert_all(sentence_example).await; + } + } + + #[gpui::test] + async fn test_delete_sentence_object(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx) + .await + .binding(["d", "i", "s"]); + cx.add_initial_state_exemptions( + "The quick brown fox jumps.\nOver the lazy dog\nˇ\nˇ\n fox-jumps over\nthe lazy dog.\n\n", + ExemptionFeatures::SentenceOnEmptyLines); + cx.add_initial_state_exemptions( + "The quick brown fox jumps.\nOver the lazy dog\n\n\nˇ foxˇ-ˇjumpˇs over\nthe lazy dog.\n\n", + ExemptionFeatures::SentenceAtStartOfLineWithWhitespace); + cx.add_initial_state_exemptions( + "The quick brown fox jumps.\nOver the lazy dog\n\n\n fox-jumps over\nthe lazy dog.ˇ\nˇ\n", + ExemptionFeatures::SentenceAfterPunctuationAtEndOfFile); + + for sentence_example in SENTENCE_EXAMPLES { + cx.assert_all(sentence_example).await; + } + + let mut cx = cx.binding(["d", "a", "s"]); + cx.add_initial_state_exemptions( + "The quick brown?ˇ Fox Jumps! Over the lazy.", + ExemptionFeatures::IncorrectLandingPosition, + ); + cx.add_initial_state_exemptions( + "The quick brown.)]\'\" Brown fox jumps.ˇ ", + ExemptionFeatures::AroundObjectLeavesWhitespaceAtEndOfLine, + ); + + for sentence_example in SENTENCE_EXAMPLES { + cx.assert_all(sentence_example).await; + } + } + + #[gpui::test] + async fn test_visual_sentence_object(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx) + .await + .binding(["v", "i", "s"]); + for sentence_example in SENTENCE_EXAMPLES { + cx.assert_all_exempted(sentence_example, ExemptionFeatures::SentenceOnEmptyLines) + .await; + } + + let mut cx = cx.binding(["v", "a", "s"]); + for sentence_example in SENTENCE_EXAMPLES { + cx.assert_all_exempted( + sentence_example, + ExemptionFeatures::AroundSentenceStartingBetweenIncludesWrongWhitespace, + ) + .await; + } + } + + // Test string with "`" for opening surrounders and "'" for closing surrounders + const SURROUNDING_MARKER_STRING: &str = indoc! {" + ˇTh'ˇe ˇ`ˇ'ˇquˇi`ˇck broˇ'wn` + 'ˇfox juˇmps ovˇ`ˇer + the ˇlazy dˇ'ˇoˇ`ˇg"}; + + const SURROUNDING_OBJECTS: &[(char, char)] = &[ + ('\'', '\''), // Quote + ('`', '`'), // Back Quote + ('"', '"'), // Double Quote + ('(', ')'), // Parentheses + ('[', ']'), // SquareBrackets + ('{', '}'), // CurlyBrackets + ('<', '>'), // AngleBrackets + ]; + + #[gpui::test] + async fn test_change_surrounding_character_objects(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + for (start, end) in SURROUNDING_OBJECTS { + let marked_string = SURROUNDING_MARKER_STRING + .replace('`', &start.to_string()) + .replace('\'', &end.to_string()); + + cx.assert_binding_matches_all(["c", "i", &start.to_string()], &marked_string) + .await; + cx.assert_binding_matches_all(["c", "i", &end.to_string()], &marked_string) + .await; + cx.assert_binding_matches_all(["c", "a", &start.to_string()], &marked_string) + .await; + cx.assert_binding_matches_all(["c", "a", &end.to_string()], &marked_string) + .await; + } + } + #[gpui::test] + async fn test_singleline_surrounding_character_objects(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + cx.set_shared_wrap(12).await; + + cx.set_shared_state(indoc! { + "helˇlo \"world\"!" + }) + .await; + cx.simulate_shared_keystrokes(["v", "i", "\""]).await; + cx.assert_shared_state(indoc! { + "hello \"«worldˇ»\"!" + }) + .await; + + cx.set_shared_state(indoc! { + "hello \"wˇorld\"!" + }) + .await; + cx.simulate_shared_keystrokes(["v", "i", "\""]).await; + cx.assert_shared_state(indoc! { + "hello \"«worldˇ»\"!" + }) + .await; + + cx.set_shared_state(indoc! { + "hello \"wˇorld\"!" + }) + .await; + cx.simulate_shared_keystrokes(["v", "a", "\""]).await; + cx.assert_shared_state(indoc! { + "hello« \"world\"ˇ»!" + }) + .await; + + cx.set_shared_state(indoc! { + "hello \"wˇorld\" !" + }) + .await; + cx.simulate_shared_keystrokes(["v", "a", "\""]).await; + cx.assert_shared_state(indoc! { + "hello «\"world\" ˇ»!" + }) + .await; + + cx.set_shared_state(indoc! { + "hello \"wˇorld\"• + goodbye" + }) + .await; + cx.simulate_shared_keystrokes(["v", "a", "\""]).await; + cx.assert_shared_state(indoc! { + "hello «\"world\" ˇ» + goodbye" + }) + .await; + } + + #[gpui::test] + async fn test_multiline_surrounding_character_objects(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.set_shared_state(indoc! { + "func empty(a string) bool { + if a == \"\" { + return true + } + ˇreturn false + }" + }) + .await; + cx.simulate_shared_keystrokes(["v", "i", "{"]).await; + cx.assert_shared_state(indoc! {" + func empty(a string) bool { + « if a == \"\" { + return true + } + return false + ˇ»}"}) + .await; + cx.set_shared_state(indoc! { + "func empty(a string) bool { + if a == \"\" { + ˇreturn true + } + return false + }" + }) + .await; + cx.simulate_shared_keystrokes(["v", "i", "{"]).await; + cx.assert_shared_state(indoc! {" + func empty(a string) bool { + if a == \"\" { + « return true + ˇ» } + return false + }"}) + .await; + + cx.set_shared_state(indoc! { + "func empty(a string) bool { + if a == \"\" ˇ{ + return true + } + return false + }" + }) + .await; + cx.simulate_shared_keystrokes(["v", "i", "{"]).await; + cx.assert_shared_state(indoc! {" + func empty(a string) bool { + if a == \"\" { + « return true + ˇ» } + return false + }"}) + .await; + } + + #[gpui::test] + async fn test_vertical_bars(cx: &mut gpui::TestAppContext) { + let mut cx = VimTestContext::new(cx, true).await; + cx.set_state( + indoc! {" + fn boop() { + baz(ˇ|a, b| { bar(|j, k| { })}) + }" + }, + Mode::Normal, + ); + cx.simulate_keystrokes(["c", "i", "|"]); + cx.assert_state( + indoc! {" + fn boop() { + baz(|ˇ| { bar(|j, k| { })}) + }" + }, + Mode::Insert, + ); + cx.simulate_keystrokes(["escape", "1", "8", "|"]); + cx.assert_state( + indoc! {" + fn boop() { + baz(|| { bar(ˇ|j, k| { })}) + }" + }, + Mode::Normal, + ); + + cx.simulate_keystrokes(["v", "a", "|"]); + cx.assert_state( + indoc! {" + fn boop() { + baz(|| { bar(«|j, k| ˇ»{ })}) + }" + }, + Mode::Visual, + ); + } + + #[gpui::test] + async fn test_delete_surrounding_character_objects(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + for (start, end) in SURROUNDING_OBJECTS { + let marked_string = SURROUNDING_MARKER_STRING + .replace('`', &start.to_string()) + .replace('\'', &end.to_string()); + + cx.assert_binding_matches_all(["d", "i", &start.to_string()], &marked_string) + .await; + cx.assert_binding_matches_all(["d", "i", &end.to_string()], &marked_string) + .await; + cx.assert_binding_matches_all(["d", "a", &start.to_string()], &marked_string) + .await; + cx.assert_binding_matches_all(["d", "a", &end.to_string()], &marked_string) + .await; + } + } +} diff --git a/crates/vim2/src/test/neovim_backed_test_context.rs b/crates/vim2/src/test/neovim_backed_test_context.rs index 0648b9169e6c6f0650cc293f15f465be37f5ad19..21455de55293faea2e39812a9df35f506f04dde6 100644 --- a/crates/vim2/src/test/neovim_backed_test_context.rs +++ b/crates/vim2/src/test/neovim_backed_test_context.rs @@ -2,6 +2,7 @@ // todo!() use editor::{scroll::VERTICAL_SCROLL_MARGIN, test::editor_test_context::ContextHandle}; +use gpui::{point, px, rems, size, Context}; use indoc::indoc; use settings::SettingsStore; use std::{ @@ -153,20 +154,36 @@ impl<'a> NeovimBackedTestContext<'a> { }) } - // todo!() - // pub async fn set_scroll_height(&mut self, rows: u32) { - // // match Zed's scrolling behavior - // self.neovim - // .set_option(&format!("scrolloff={}", VERTICAL_SCROLL_MARGIN)) - // .await; - // // +2 to account for the vim command UI at the bottom. - // self.neovim.set_option(&format!("lines={}", rows + 2)).await; - // let window = self.window; - // let line_height = - // self.editor(|editor, cx| editor.style().text.line_height(cx.font_cache())); - - // window.simulate_resize(vec2f(1000., (rows as f32) * line_height), &mut self.cx); - // } + pub async fn set_scroll_height(&mut self, rows: u32) { + // match Zed's scrolling behavior + self.neovim + .set_option(&format!("scrolloff={}", VERTICAL_SCROLL_MARGIN)) + .await; + // +2 to account for the vim command UI at the bottom. + self.neovim.set_option(&format!("lines={}", rows + 2)).await; + let (line_height, visible_line_count) = self.editor(|editor, cx| { + ( + editor + .style() + .unwrap() + .text + .line_height_in_pixels(cx.rem_size()), + editor.visible_line_count().unwrap(), + ) + }); + + let window = self.window; + let margin = self + .update_window(window, |_, cx| { + cx.viewport_size().height - line_height * visible_line_count + }) + .unwrap(); + + self.simulate_window_resize( + self.window, + size(px(1000.), margin + (rows as f32) * line_height), + ); + } pub async fn set_neovim_option(&mut self, option: &str) { self.neovim.set_option(option).await; diff --git a/crates/vim2/src/test/vim_test_context.rs b/crates/vim2/src/test/vim_test_context.rs index 7b9d71ffca1d8f2b1100b86ed0eb734266059b7a..395696448037c8baf8c807ac06cab07c94f1597c 100644 --- a/crates/vim2/src/test/vim_test_context.rs +++ b/crates/vim2/src/test/vim_test_context.rs @@ -18,7 +18,11 @@ pub struct VimTestContext<'a> { } impl<'a> VimTestContext<'a> { - pub async fn new(cx: &'a mut gpui::TestAppContext, enabled: bool) -> VimTestContext<'a> { + pub fn init(cx: &mut gpui::TestAppContext) { + if cx.has_global::() { + dbg!("OOPS"); + return; + } cx.update(|cx| { search::init(cx); let settings = SettingsStore::test(cx); @@ -26,18 +30,16 @@ impl<'a> VimTestContext<'a> { command_palette::init(cx); crate::init(cx); }); + } + + pub async fn new(cx: &'a mut gpui::TestAppContext, enabled: bool) -> VimTestContext<'a> { + Self::init(cx); let lsp = EditorLspTestContext::new_rust(Default::default(), cx).await; Self::new_with_lsp(lsp, enabled) } pub async fn new_typescript(cx: &'a mut gpui::TestAppContext) -> VimTestContext<'a> { - cx.update(|cx| { - search::init(cx); - let settings = SettingsStore::test(cx); - cx.set_global(settings); - command_palette::init(cx); - crate::init(cx); - }); + Self::init(cx); Self::new_with_lsp( EditorLspTestContext::new_typescript(Default::default(), cx).await, true, diff --git a/crates/vim2/src/vim.rs b/crates/vim2/src/vim.rs index 9513dbb342f413fca76a05e03f28fb1b096af413..62205630a153e886af9021f1bb45671dd496b7b7 100644 --- a/crates/vim2/src/vim.rs +++ b/crates/vim2/src/vim.rs @@ -140,6 +140,8 @@ pub fn observe_keystrokes(cx: &mut WindowContext) { if action.name().starts_with("vim::") { return; } + } else if cx.has_pending_keystrokes() { + return; } Vim::update(cx, |vim, cx| match vim.active_operator() { diff --git a/crates/vim2/src/visual.rs b/crates/vim2/src/visual.rs index 791b26f78ce8e498da660debd16e2c058fdf14e2..1fd11167c6843206ddcef2b4ccee96a0f9886dae 100644 --- a/crates/vim2/src/visual.rs +++ b/crates/vim2/src/visual.rs @@ -463,567 +463,572 @@ pub fn select_previous( .unwrap_or(Ok(())) } -// #[cfg(test)] -// mod test { -// use indoc::indoc; -// use workspace::item::Item; - -// use crate::{ -// state::Mode, -// test::{NeovimBackedTestContext, VimTestContext}, -// }; - -// #[gpui::test] -// async fn test_enter_visual_mode(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx).await; - -// cx.set_shared_state(indoc! { -// "The ˇquick brown -// fox jumps over -// the lazy dog" -// }) -// .await; -// let cursor = cx.update_editor(|editor, cx| editor.pixel_position_of_cursor(cx)); - -// // entering visual mode should select the character -// // under cursor -// cx.simulate_shared_keystrokes(["v"]).await; -// cx.assert_shared_state(indoc! { "The «qˇ»uick brown -// fox jumps over -// the lazy dog"}) -// .await; -// cx.update_editor(|editor, cx| assert_eq!(cursor, editor.pixel_position_of_cursor(cx))); - -// // forwards motions should extend the selection -// cx.simulate_shared_keystrokes(["w", "j"]).await; -// cx.assert_shared_state(indoc! { "The «quick brown -// fox jumps oˇ»ver -// the lazy dog"}) -// .await; - -// cx.simulate_shared_keystrokes(["escape"]).await; -// assert_eq!(Mode::Normal, cx.neovim_mode().await); -// cx.assert_shared_state(indoc! { "The quick brown -// fox jumps ˇover -// the lazy dog"}) -// .await; - -// // motions work backwards -// cx.simulate_shared_keystrokes(["v", "k", "b"]).await; -// cx.assert_shared_state(indoc! { "The «ˇquick brown -// fox jumps o»ver -// the lazy dog"}) -// .await; - -// // works on empty lines -// cx.set_shared_state(indoc! {" -// a -// ˇ -// b -// "}) -// .await; -// let cursor = cx.update_editor(|editor, cx| editor.pixel_position_of_cursor(cx)); -// cx.simulate_shared_keystrokes(["v"]).await; -// cx.assert_shared_state(indoc! {" -// a -// « -// ˇ»b -// "}) -// .await; -// cx.update_editor(|editor, cx| assert_eq!(cursor, editor.pixel_position_of_cursor(cx))); - -// // toggles off again -// cx.simulate_shared_keystrokes(["v"]).await; -// cx.assert_shared_state(indoc! {" -// a -// ˇ -// b -// "}) -// .await; - -// // works at the end of a document -// cx.set_shared_state(indoc! {" -// a -// b -// ˇ"}) -// .await; - -// cx.simulate_shared_keystrokes(["v"]).await; -// cx.assert_shared_state(indoc! {" -// a -// b -// ˇ"}) -// .await; -// assert_eq!(cx.mode(), cx.neovim_mode().await); -// } - -// #[gpui::test] -// async fn test_enter_visual_line_mode(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx).await; - -// cx.set_shared_state(indoc! { -// "The ˇquick brown -// fox jumps over -// the lazy dog" -// }) -// .await; -// cx.simulate_shared_keystrokes(["shift-v"]).await; -// cx.assert_shared_state(indoc! { "The «qˇ»uick brown -// fox jumps over -// the lazy dog"}) -// .await; -// assert_eq!(cx.mode(), cx.neovim_mode().await); -// cx.simulate_shared_keystrokes(["x"]).await; -// cx.assert_shared_state(indoc! { "fox ˇjumps over -// the lazy dog"}) -// .await; - -// // it should work on empty lines -// cx.set_shared_state(indoc! {" -// a -// ˇ -// b"}) -// .await; -// cx.simulate_shared_keystrokes(["shift-v"]).await; -// cx.assert_shared_state(indoc! { " -// a -// « -// ˇ»b"}) -// .await; -// cx.simulate_shared_keystrokes(["x"]).await; -// cx.assert_shared_state(indoc! { " -// a -// ˇb"}) -// .await; - -// // it should work at the end of the document -// cx.set_shared_state(indoc! {" -// a -// b -// ˇ"}) -// .await; -// let cursor = cx.update_editor(|editor, cx| editor.pixel_position_of_cursor(cx)); -// cx.simulate_shared_keystrokes(["shift-v"]).await; -// cx.assert_shared_state(indoc! {" -// a -// b -// ˇ"}) -// .await; -// assert_eq!(cx.mode(), cx.neovim_mode().await); -// cx.update_editor(|editor, cx| assert_eq!(cursor, editor.pixel_position_of_cursor(cx))); -// cx.simulate_shared_keystrokes(["x"]).await; -// cx.assert_shared_state(indoc! {" -// a -// ˇb"}) -// .await; -// } - -// #[gpui::test] -// async fn test_visual_delete(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx).await; - -// cx.assert_binding_matches(["v", "w"], "The quick ˇbrown") -// .await; - -// cx.assert_binding_matches(["v", "w", "x"], "The quick ˇbrown") -// .await; -// cx.assert_binding_matches( -// ["v", "w", "j", "x"], -// indoc! {" -// The ˇquick brown -// fox jumps over -// the lazy dog"}, -// ) -// .await; -// // Test pasting code copied on delete -// cx.simulate_shared_keystrokes(["j", "p"]).await; -// cx.assert_state_matches().await; - -// let mut cx = cx.binding(["v", "w", "j", "x"]); -// cx.assert_all(indoc! {" -// The ˇquick brown -// fox jumps over -// the ˇlazy dog"}) -// .await; -// let mut cx = cx.binding(["v", "b", "k", "x"]); -// cx.assert_all(indoc! {" -// The ˇquick brown -// fox jumps ˇover -// the ˇlazy dog"}) -// .await; -// } - -// #[gpui::test] -// async fn test_visual_line_delete(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx).await; - -// cx.set_shared_state(indoc! {" -// The quˇick brown -// fox jumps over -// the lazy dog"}) -// .await; -// cx.simulate_shared_keystrokes(["shift-v", "x"]).await; -// cx.assert_state_matches().await; - -// // Test pasting code copied on delete -// cx.simulate_shared_keystroke("p").await; -// cx.assert_state_matches().await; - -// cx.set_shared_state(indoc! {" -// The quick brown -// fox jumps over -// the laˇzy dog"}) -// .await; -// cx.simulate_shared_keystrokes(["shift-v", "x"]).await; -// cx.assert_state_matches().await; -// cx.assert_shared_clipboard("the lazy dog\n").await; - -// for marked_text in cx.each_marked_position(indoc! {" -// The quˇick brown -// fox jumps over -// the lazy dog"}) -// { -// cx.set_shared_state(&marked_text).await; -// cx.simulate_shared_keystrokes(["shift-v", "j", "x"]).await; -// cx.assert_state_matches().await; -// // Test pasting code copied on delete -// cx.simulate_shared_keystroke("p").await; -// cx.assert_state_matches().await; -// } - -// cx.set_shared_state(indoc! {" -// The ˇlong line -// should not -// crash -// "}) -// .await; -// cx.simulate_shared_keystrokes(["shift-v", "$", "x"]).await; -// cx.assert_state_matches().await; -// } - -// #[gpui::test] -// async fn test_visual_yank(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx).await; - -// cx.set_shared_state("The quick ˇbrown").await; -// cx.simulate_shared_keystrokes(["v", "w", "y"]).await; -// cx.assert_shared_state("The quick ˇbrown").await; -// cx.assert_shared_clipboard("brown").await; - -// cx.set_shared_state(indoc! {" -// The ˇquick brown -// fox jumps over -// the lazy dog"}) -// .await; -// cx.simulate_shared_keystrokes(["v", "w", "j", "y"]).await; -// cx.assert_shared_state(indoc! {" -// The ˇquick brown -// fox jumps over -// the lazy dog"}) -// .await; -// cx.assert_shared_clipboard(indoc! {" -// quick brown -// fox jumps o"}) -// .await; - -// cx.set_shared_state(indoc! {" -// The quick brown -// fox jumps over -// the ˇlazy dog"}) -// .await; -// cx.simulate_shared_keystrokes(["v", "w", "j", "y"]).await; -// cx.assert_shared_state(indoc! {" -// The quick brown -// fox jumps over -// the ˇlazy dog"}) -// .await; -// cx.assert_shared_clipboard("lazy d").await; -// cx.simulate_shared_keystrokes(["shift-v", "y"]).await; -// cx.assert_shared_clipboard("the lazy dog\n").await; - -// let mut cx = cx.binding(["v", "b", "k", "y"]); -// cx.set_shared_state(indoc! {" -// The ˇquick brown -// fox jumps over -// the lazy dog"}) -// .await; -// cx.simulate_shared_keystrokes(["v", "b", "k", "y"]).await; -// cx.assert_shared_state(indoc! {" -// ˇThe quick brown -// fox jumps over -// the lazy dog"}) -// .await; -// cx.assert_clipboard_content(Some("The q")); - -// cx.set_shared_state(indoc! {" -// The quick brown -// fox ˇjumps over -// the lazy dog"}) -// .await; -// cx.simulate_shared_keystrokes(["shift-v", "shift-g", "shift-y"]) -// .await; -// cx.assert_shared_state(indoc! {" -// The quick brown -// ˇfox jumps over -// the lazy dog"}) -// .await; -// cx.assert_shared_clipboard("fox jumps over\nthe lazy dog\n") -// .await; -// } - -// #[gpui::test] -// async fn test_visual_block_mode(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx).await; - -// cx.set_shared_state(indoc! { -// "The ˇquick brown -// fox jumps over -// the lazy dog" -// }) -// .await; -// cx.simulate_shared_keystrokes(["ctrl-v"]).await; -// cx.assert_shared_state(indoc! { -// "The «qˇ»uick brown -// fox jumps over -// the lazy dog" -// }) -// .await; -// cx.simulate_shared_keystrokes(["2", "down"]).await; -// cx.assert_shared_state(indoc! { -// "The «qˇ»uick brown -// fox «jˇ»umps over -// the «lˇ»azy dog" -// }) -// .await; -// cx.simulate_shared_keystrokes(["e"]).await; -// cx.assert_shared_state(indoc! { -// "The «quicˇ»k brown -// fox «jumpˇ»s over -// the «lazyˇ» dog" -// }) -// .await; -// cx.simulate_shared_keystrokes(["^"]).await; -// cx.assert_shared_state(indoc! { -// "«ˇThe q»uick brown -// «ˇfox j»umps over -// «ˇthe l»azy dog" -// }) -// .await; -// cx.simulate_shared_keystrokes(["$"]).await; -// cx.assert_shared_state(indoc! { -// "The «quick brownˇ» -// fox «jumps overˇ» -// the «lazy dogˇ»" -// }) -// .await; -// cx.simulate_shared_keystrokes(["shift-f", " "]).await; -// cx.assert_shared_state(indoc! { -// "The «quickˇ» brown -// fox «jumpsˇ» over -// the «lazy ˇ»dog" -// }) -// .await; - -// // toggling through visual mode works as expected -// cx.simulate_shared_keystrokes(["v"]).await; -// cx.assert_shared_state(indoc! { -// "The «quick brown -// fox jumps over -// the lazy ˇ»dog" -// }) -// .await; -// cx.simulate_shared_keystrokes(["ctrl-v"]).await; -// cx.assert_shared_state(indoc! { -// "The «quickˇ» brown -// fox «jumpsˇ» over -// the «lazy ˇ»dog" -// }) -// .await; - -// cx.set_shared_state(indoc! { -// "The ˇquick -// brown -// fox -// jumps over the - -// lazy dog -// " -// }) -// .await; -// cx.simulate_shared_keystrokes(["ctrl-v", "down", "down"]) -// .await; -// cx.assert_shared_state(indoc! { -// "The«ˇ q»uick -// bro«ˇwn» -// foxˇ -// jumps over the - -// lazy dog -// " -// }) -// .await; -// cx.simulate_shared_keystrokes(["down"]).await; -// cx.assert_shared_state(indoc! { -// "The «qˇ»uick -// brow«nˇ» -// fox -// jump«sˇ» over the - -// lazy dog -// " -// }) -// .await; -// cx.simulate_shared_keystroke("left").await; -// cx.assert_shared_state(indoc! { -// "The«ˇ q»uick -// bro«ˇwn» -// foxˇ -// jum«ˇps» over the - -// lazy dog -// " -// }) -// .await; -// cx.simulate_shared_keystrokes(["s", "o", "escape"]).await; -// cx.assert_shared_state(indoc! { -// "Theˇouick -// broo -// foxo -// jumo over the - -// lazy dog -// " -// }) -// .await; - -// //https://github.com/zed-industries/community/issues/1950 -// cx.set_shared_state(indoc! { -// "Theˇ quick brown - -// fox jumps over -// the lazy dog -// " -// }) -// .await; -// cx.simulate_shared_keystrokes(["l", "ctrl-v", "j", "j"]) -// .await; -// cx.assert_shared_state(indoc! { -// "The «qˇ»uick brown - -// fox «jˇ»umps over -// the lazy dog -// " -// }) -// .await; -// } - -// #[gpui::test] -// async fn test_visual_block_issue_2123(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx).await; - -// cx.set_shared_state(indoc! { -// "The ˇquick brown -// fox jumps over -// the lazy dog -// " -// }) -// .await; -// cx.simulate_shared_keystrokes(["ctrl-v", "right", "down"]) -// .await; -// cx.assert_shared_state(indoc! { -// "The «quˇ»ick brown -// fox «juˇ»mps over -// the lazy dog -// " -// }) -// .await; -// } - -// #[gpui::test] -// async fn test_visual_block_insert(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx).await; - -// cx.set_shared_state(indoc! { -// "ˇThe quick brown -// fox jumps over -// the lazy dog -// " -// }) -// .await; -// cx.simulate_shared_keystrokes(["ctrl-v", "9", "down"]).await; -// cx.assert_shared_state(indoc! { -// "«Tˇ»he quick brown -// «fˇ»ox jumps over -// «tˇ»he lazy dog -// ˇ" -// }) -// .await; - -// cx.simulate_shared_keystrokes(["shift-i", "k", "escape"]) -// .await; -// cx.assert_shared_state(indoc! { -// "ˇkThe quick brown -// kfox jumps over -// kthe lazy dog -// k" -// }) -// .await; - -// cx.set_shared_state(indoc! { -// "ˇThe quick brown -// fox jumps over -// the lazy dog -// " -// }) -// .await; -// cx.simulate_shared_keystrokes(["ctrl-v", "9", "down"]).await; -// cx.assert_shared_state(indoc! { -// "«Tˇ»he quick brown -// «fˇ»ox jumps over -// «tˇ»he lazy dog -// ˇ" -// }) -// .await; -// cx.simulate_shared_keystrokes(["c", "k", "escape"]).await; -// cx.assert_shared_state(indoc! { -// "ˇkhe quick brown -// kox jumps over -// khe lazy dog -// k" -// }) -// .await; -// } - -// #[gpui::test] -// async fn test_visual_object(cx: &mut gpui::TestAppContext) { -// let mut cx = NeovimBackedTestContext::new(cx).await; - -// cx.set_shared_state("hello (in [parˇens] o)").await; -// cx.simulate_shared_keystrokes(["ctrl-v", "l"]).await; -// cx.simulate_shared_keystrokes(["a", "]"]).await; -// cx.assert_shared_state("hello (in «[parens]ˇ» o)").await; -// assert_eq!(cx.mode(), Mode::Visual); -// cx.simulate_shared_keystrokes(["i", "("]).await; -// cx.assert_shared_state("hello («in [parens] oˇ»)").await; - -// cx.set_shared_state("hello in a wˇord again.").await; -// cx.simulate_shared_keystrokes(["ctrl-v", "l", "i", "w"]) -// .await; -// cx.assert_shared_state("hello in a w«ordˇ» again.").await; -// assert_eq!(cx.mode(), Mode::VisualBlock); -// cx.simulate_shared_keystrokes(["o", "a", "s"]).await; -// cx.assert_shared_state("«ˇhello in a word» again.").await; -// assert_eq!(cx.mode(), Mode::Visual); -// } - -// #[gpui::test] -// async fn test_mode_across_command(cx: &mut gpui::TestAppContext) { -// let mut cx = VimTestContext::new(cx, true).await; - -// cx.set_state("aˇbc", Mode::Normal); -// cx.simulate_keystrokes(["ctrl-v"]); -// assert_eq!(cx.mode(), Mode::VisualBlock); -// cx.simulate_keystrokes(["cmd-shift-p", "escape"]); -// assert_eq!(cx.mode(), Mode::VisualBlock); -// } -// } +#[cfg(test)] +mod test { + use indoc::indoc; + use workspace::item::Item; + + use crate::{ + state::Mode, + test::{NeovimBackedTestContext, VimTestContext}, + }; + + #[gpui::test] + async fn test_enter_visual_mode(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.set_shared_state(indoc! { + "The ˇquick brown + fox jumps over + the lazy dog" + }) + .await; + let cursor = cx.update_editor(|editor, cx| editor.pixel_position_of_cursor(cx)); + + // entering visual mode should select the character + // under cursor + cx.simulate_shared_keystrokes(["v"]).await; + cx.assert_shared_state(indoc! { "The «qˇ»uick brown + fox jumps over + the lazy dog"}) + .await; + cx.update_editor(|editor, cx| assert_eq!(cursor, editor.pixel_position_of_cursor(cx))); + + // forwards motions should extend the selection + cx.simulate_shared_keystrokes(["w", "j"]).await; + cx.assert_shared_state(indoc! { "The «quick brown + fox jumps oˇ»ver + the lazy dog"}) + .await; + + cx.simulate_shared_keystrokes(["escape"]).await; + assert_eq!(Mode::Normal, cx.neovim_mode().await); + cx.assert_shared_state(indoc! { "The quick brown + fox jumps ˇover + the lazy dog"}) + .await; + + // motions work backwards + cx.simulate_shared_keystrokes(["v", "k", "b"]).await; + cx.assert_shared_state(indoc! { "The «ˇquick brown + fox jumps o»ver + the lazy dog"}) + .await; + + // works on empty lines + cx.set_shared_state(indoc! {" + a + ˇ + b + "}) + .await; + let cursor = cx.update_editor(|editor, cx| editor.pixel_position_of_cursor(cx)); + cx.simulate_shared_keystrokes(["v"]).await; + cx.assert_shared_state(indoc! {" + a + « + ˇ»b + "}) + .await; + cx.update_editor(|editor, cx| assert_eq!(cursor, editor.pixel_position_of_cursor(cx))); + + // toggles off again + cx.simulate_shared_keystrokes(["v"]).await; + cx.assert_shared_state(indoc! {" + a + ˇ + b + "}) + .await; + + // works at the end of a document + cx.set_shared_state(indoc! {" + a + b + ˇ"}) + .await; + + cx.simulate_shared_keystrokes(["v"]).await; + cx.assert_shared_state(indoc! {" + a + b + ˇ"}) + .await; + assert_eq!(cx.mode(), cx.neovim_mode().await); + } + + #[gpui::test] + async fn test_enter_visual_line_mode(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.set_shared_state(indoc! { + "The ˇquick brown + fox jumps over + the lazy dog" + }) + .await; + cx.simulate_shared_keystrokes(["shift-v"]).await; + cx.assert_shared_state(indoc! { "The «qˇ»uick brown + fox jumps over + the lazy dog"}) + .await; + assert_eq!(cx.mode(), cx.neovim_mode().await); + cx.simulate_shared_keystrokes(["x"]).await; + cx.assert_shared_state(indoc! { "fox ˇjumps over + the lazy dog"}) + .await; + + // it should work on empty lines + cx.set_shared_state(indoc! {" + a + ˇ + b"}) + .await; + cx.simulate_shared_keystrokes(["shift-v"]).await; + cx.assert_shared_state(indoc! { " + a + « + ˇ»b"}) + .await; + cx.simulate_shared_keystrokes(["x"]).await; + cx.assert_shared_state(indoc! { " + a + ˇb"}) + .await; + + // it should work at the end of the document + cx.set_shared_state(indoc! {" + a + b + ˇ"}) + .await; + let cursor = cx.update_editor(|editor, cx| editor.pixel_position_of_cursor(cx)); + cx.simulate_shared_keystrokes(["shift-v"]).await; + cx.assert_shared_state(indoc! {" + a + b + ˇ"}) + .await; + assert_eq!(cx.mode(), cx.neovim_mode().await); + cx.update_editor(|editor, cx| assert_eq!(cursor, editor.pixel_position_of_cursor(cx))); + cx.simulate_shared_keystrokes(["x"]).await; + cx.assert_shared_state(indoc! {" + a + ˇb"}) + .await; + } + + #[gpui::test] + async fn test_visual_delete(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.assert_binding_matches(["v", "w"], "The quick ˇbrown") + .await; + + cx.assert_binding_matches(["v", "w", "x"], "The quick ˇbrown") + .await; + cx.assert_binding_matches( + ["v", "w", "j", "x"], + indoc! {" + The ˇquick brown + fox jumps over + the lazy dog"}, + ) + .await; + // Test pasting code copied on delete + cx.simulate_shared_keystrokes(["j", "p"]).await; + cx.assert_state_matches().await; + + let mut cx = cx.binding(["v", "w", "j", "x"]); + cx.assert_all(indoc! {" + The ˇquick brown + fox jumps over + the ˇlazy dog"}) + .await; + let mut cx = cx.binding(["v", "b", "k", "x"]); + cx.assert_all(indoc! {" + The ˇquick brown + fox jumps ˇover + the ˇlazy dog"}) + .await; + } + + #[gpui::test] + async fn test_visual_line_delete(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.set_shared_state(indoc! {" + The quˇick brown + fox jumps over + the lazy dog"}) + .await; + cx.simulate_shared_keystrokes(["shift-v", "x"]).await; + cx.assert_state_matches().await; + + // Test pasting code copied on delete + cx.simulate_shared_keystroke("p").await; + cx.assert_state_matches().await; + + cx.set_shared_state(indoc! {" + The quick brown + fox jumps over + the laˇzy dog"}) + .await; + cx.simulate_shared_keystrokes(["shift-v", "x"]).await; + cx.assert_state_matches().await; + cx.assert_shared_clipboard("the lazy dog\n").await; + + for marked_text in cx.each_marked_position(indoc! {" + The quˇick brown + fox jumps over + the lazy dog"}) + { + cx.set_shared_state(&marked_text).await; + cx.simulate_shared_keystrokes(["shift-v", "j", "x"]).await; + cx.assert_state_matches().await; + // Test pasting code copied on delete + cx.simulate_shared_keystroke("p").await; + cx.assert_state_matches().await; + } + + cx.set_shared_state(indoc! {" + The ˇlong line + should not + crash + "}) + .await; + cx.simulate_shared_keystrokes(["shift-v", "$", "x"]).await; + cx.assert_state_matches().await; + } + + #[gpui::test] + async fn test_visual_yank(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.set_shared_state("The quick ˇbrown").await; + cx.simulate_shared_keystrokes(["v", "w", "y"]).await; + cx.assert_shared_state("The quick ˇbrown").await; + cx.assert_shared_clipboard("brown").await; + + cx.set_shared_state(indoc! {" + The ˇquick brown + fox jumps over + the lazy dog"}) + .await; + cx.simulate_shared_keystrokes(["v", "w", "j", "y"]).await; + cx.assert_shared_state(indoc! {" + The ˇquick brown + fox jumps over + the lazy dog"}) + .await; + cx.assert_shared_clipboard(indoc! {" + quick brown + fox jumps o"}) + .await; + + cx.set_shared_state(indoc! {" + The quick brown + fox jumps over + the ˇlazy dog"}) + .await; + cx.simulate_shared_keystrokes(["v", "w", "j", "y"]).await; + cx.assert_shared_state(indoc! {" + The quick brown + fox jumps over + the ˇlazy dog"}) + .await; + cx.assert_shared_clipboard("lazy d").await; + cx.simulate_shared_keystrokes(["shift-v", "y"]).await; + cx.assert_shared_clipboard("the lazy dog\n").await; + + let mut cx = cx.binding(["v", "b", "k", "y"]); + cx.set_shared_state(indoc! {" + The ˇquick brown + fox jumps over + the lazy dog"}) + .await; + cx.simulate_shared_keystrokes(["v", "b", "k", "y"]).await; + cx.assert_shared_state(indoc! {" + ˇThe quick brown + fox jumps over + the lazy dog"}) + .await; + assert_eq!( + cx.read_from_clipboard() + .map(|item| item.text().clone()) + .unwrap(), + "The q" + ); + + cx.set_shared_state(indoc! {" + The quick brown + fox ˇjumps over + the lazy dog"}) + .await; + cx.simulate_shared_keystrokes(["shift-v", "shift-g", "shift-y"]) + .await; + cx.assert_shared_state(indoc! {" + The quick brown + ˇfox jumps over + the lazy dog"}) + .await; + cx.assert_shared_clipboard("fox jumps over\nthe lazy dog\n") + .await; + } + + #[gpui::test] + async fn test_visual_block_mode(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.set_shared_state(indoc! { + "The ˇquick brown + fox jumps over + the lazy dog" + }) + .await; + cx.simulate_shared_keystrokes(["ctrl-v"]).await; + cx.assert_shared_state(indoc! { + "The «qˇ»uick brown + fox jumps over + the lazy dog" + }) + .await; + cx.simulate_shared_keystrokes(["2", "down"]).await; + cx.assert_shared_state(indoc! { + "The «qˇ»uick brown + fox «jˇ»umps over + the «lˇ»azy dog" + }) + .await; + cx.simulate_shared_keystrokes(["e"]).await; + cx.assert_shared_state(indoc! { + "The «quicˇ»k brown + fox «jumpˇ»s over + the «lazyˇ» dog" + }) + .await; + cx.simulate_shared_keystrokes(["^"]).await; + cx.assert_shared_state(indoc! { + "«ˇThe q»uick brown + «ˇfox j»umps over + «ˇthe l»azy dog" + }) + .await; + cx.simulate_shared_keystrokes(["$"]).await; + cx.assert_shared_state(indoc! { + "The «quick brownˇ» + fox «jumps overˇ» + the «lazy dogˇ»" + }) + .await; + cx.simulate_shared_keystrokes(["shift-f", " "]).await; + cx.assert_shared_state(indoc! { + "The «quickˇ» brown + fox «jumpsˇ» over + the «lazy ˇ»dog" + }) + .await; + + // toggling through visual mode works as expected + cx.simulate_shared_keystrokes(["v"]).await; + cx.assert_shared_state(indoc! { + "The «quick brown + fox jumps over + the lazy ˇ»dog" + }) + .await; + cx.simulate_shared_keystrokes(["ctrl-v"]).await; + cx.assert_shared_state(indoc! { + "The «quickˇ» brown + fox «jumpsˇ» over + the «lazy ˇ»dog" + }) + .await; + + cx.set_shared_state(indoc! { + "The ˇquick + brown + fox + jumps over the + + lazy dog + " + }) + .await; + cx.simulate_shared_keystrokes(["ctrl-v", "down", "down"]) + .await; + cx.assert_shared_state(indoc! { + "The«ˇ q»uick + bro«ˇwn» + foxˇ + jumps over the + + lazy dog + " + }) + .await; + cx.simulate_shared_keystrokes(["down"]).await; + cx.assert_shared_state(indoc! { + "The «qˇ»uick + brow«nˇ» + fox + jump«sˇ» over the + + lazy dog + " + }) + .await; + cx.simulate_shared_keystroke("left").await; + cx.assert_shared_state(indoc! { + "The«ˇ q»uick + bro«ˇwn» + foxˇ + jum«ˇps» over the + + lazy dog + " + }) + .await; + cx.simulate_shared_keystrokes(["s", "o", "escape"]).await; + cx.assert_shared_state(indoc! { + "Theˇouick + broo + foxo + jumo over the + + lazy dog + " + }) + .await; + + //https://github.com/zed-industries/community/issues/1950 + cx.set_shared_state(indoc! { + "Theˇ quick brown + + fox jumps over + the lazy dog + " + }) + .await; + cx.simulate_shared_keystrokes(["l", "ctrl-v", "j", "j"]) + .await; + cx.assert_shared_state(indoc! { + "The «qˇ»uick brown + + fox «jˇ»umps over + the lazy dog + " + }) + .await; + } + + #[gpui::test] + async fn test_visual_block_issue_2123(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.set_shared_state(indoc! { + "The ˇquick brown + fox jumps over + the lazy dog + " + }) + .await; + cx.simulate_shared_keystrokes(["ctrl-v", "right", "down"]) + .await; + cx.assert_shared_state(indoc! { + "The «quˇ»ick brown + fox «juˇ»mps over + the lazy dog + " + }) + .await; + } + + #[gpui::test] + async fn test_visual_block_insert(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.set_shared_state(indoc! { + "ˇThe quick brown + fox jumps over + the lazy dog + " + }) + .await; + cx.simulate_shared_keystrokes(["ctrl-v", "9", "down"]).await; + cx.assert_shared_state(indoc! { + "«Tˇ»he quick brown + «fˇ»ox jumps over + «tˇ»he lazy dog + ˇ" + }) + .await; + + cx.simulate_shared_keystrokes(["shift-i", "k", "escape"]) + .await; + cx.assert_shared_state(indoc! { + "ˇkThe quick brown + kfox jumps over + kthe lazy dog + k" + }) + .await; + + cx.set_shared_state(indoc! { + "ˇThe quick brown + fox jumps over + the lazy dog + " + }) + .await; + cx.simulate_shared_keystrokes(["ctrl-v", "9", "down"]).await; + cx.assert_shared_state(indoc! { + "«Tˇ»he quick brown + «fˇ»ox jumps over + «tˇ»he lazy dog + ˇ" + }) + .await; + cx.simulate_shared_keystrokes(["c", "k", "escape"]).await; + cx.assert_shared_state(indoc! { + "ˇkhe quick brown + kox jumps over + khe lazy dog + k" + }) + .await; + } + + #[gpui::test] + async fn test_visual_object(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.set_shared_state("hello (in [parˇens] o)").await; + cx.simulate_shared_keystrokes(["ctrl-v", "l"]).await; + cx.simulate_shared_keystrokes(["a", "]"]).await; + cx.assert_shared_state("hello (in «[parens]ˇ» o)").await; + assert_eq!(cx.mode(), Mode::Visual); + cx.simulate_shared_keystrokes(["i", "("]).await; + cx.assert_shared_state("hello («in [parens] oˇ»)").await; + + cx.set_shared_state("hello in a wˇord again.").await; + cx.simulate_shared_keystrokes(["ctrl-v", "l", "i", "w"]) + .await; + cx.assert_shared_state("hello in a w«ordˇ» again.").await; + assert_eq!(cx.mode(), Mode::VisualBlock); + cx.simulate_shared_keystrokes(["o", "a", "s"]).await; + cx.assert_shared_state("«ˇhello in a word» again.").await; + assert_eq!(cx.mode(), Mode::Visual); + } + + #[gpui::test] + async fn test_mode_across_command(cx: &mut gpui::TestAppContext) { + let mut cx = VimTestContext::new(cx, true).await; + + cx.set_state("aˇbc", Mode::Normal); + cx.simulate_keystrokes(["ctrl-v"]); + assert_eq!(cx.mode(), Mode::VisualBlock); + cx.simulate_keystrokes(["cmd-shift-p", "escape"]); + assert_eq!(cx.mode(), Mode::VisualBlock); + } +}