From e4794e3134b6449e36ed2771a8849046489cc252 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Wed, 23 Aug 2023 23:45:42 -0600 Subject: [PATCH] vim: Fix linewise copy of last line with no trailing newline Along the way, delete the VimBindingTestContext by updating the visual tests to no-longer need it. --- crates/vim/src/test.rs | 2 - .../vim/src/test/vim_binding_test_context.rs | 64 ------- crates/vim/src/test/vim_test_context.rs | 10 -- crates/vim/src/utils.rs | 19 +- crates/vim/src/visual.rs | 166 ++++++++---------- .../test_data/test_visual_line_delete.json | 15 +- crates/vim/test_data/test_visual_yank.json | 29 +++ 7 files changed, 118 insertions(+), 187 deletions(-) delete mode 100644 crates/vim/src/test/vim_binding_test_context.rs create mode 100644 crates/vim/test_data/test_visual_yank.json diff --git a/crates/vim/src/test.rs b/crates/vim/src/test.rs index 3b1b8c24f2..9cd927601f 100644 --- a/crates/vim/src/test.rs +++ b/crates/vim/src/test.rs @@ -1,7 +1,6 @@ mod neovim_backed_binding_test_context; mod neovim_backed_test_context; mod neovim_connection; -mod vim_binding_test_context; mod vim_test_context; use std::sync::Arc; @@ -10,7 +9,6 @@ use command_palette::CommandPalette; use editor::DisplayPoint; pub use neovim_backed_binding_test_context::*; pub use neovim_backed_test_context::*; -pub use vim_binding_test_context::*; pub use vim_test_context::*; use indoc::indoc; diff --git a/crates/vim/src/test/vim_binding_test_context.rs b/crates/vim/src/test/vim_binding_test_context.rs deleted file mode 100644 index 04afe0c058..0000000000 --- a/crates/vim/src/test/vim_binding_test_context.rs +++ /dev/null @@ -1,64 +0,0 @@ -use std::ops::{Deref, DerefMut}; - -use crate::*; - -use super::VimTestContext; - -pub struct VimBindingTestContext<'a, const COUNT: usize> { - cx: VimTestContext<'a>, - keystrokes_under_test: [&'static str; COUNT], - mode_before: Mode, - mode_after: Mode, -} - -impl<'a, const COUNT: usize> VimBindingTestContext<'a, COUNT> { - pub fn new( - keystrokes_under_test: [&'static str; COUNT], - mode_before: Mode, - mode_after: Mode, - cx: VimTestContext<'a>, - ) -> Self { - Self { - cx, - keystrokes_under_test, - mode_before, - mode_after, - } - } - - pub fn binding( - self, - keystrokes_under_test: [&'static str; NEW_COUNT], - ) -> VimBindingTestContext<'a, NEW_COUNT> { - VimBindingTestContext { - keystrokes_under_test, - cx: self.cx, - mode_before: self.mode_before, - mode_after: self.mode_after, - } - } - - pub fn assert(&mut self, initial_state: &str, state_after: &str) { - self.cx.assert_binding( - self.keystrokes_under_test, - initial_state, - self.mode_before, - state_after, - self.mode_after, - ) - } -} - -impl<'a, const COUNT: usize> Deref for VimBindingTestContext<'a, COUNT> { - type Target = VimTestContext<'a>; - - fn deref(&self) -> &Self::Target { - &self.cx - } -} - -impl<'a, const COUNT: usize> DerefMut for VimBindingTestContext<'a, COUNT> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.cx - } -} diff --git a/crates/vim/src/test/vim_test_context.rs b/crates/vim/src/test/vim_test_context.rs index 24fb16fd3d..9b03739570 100644 --- a/crates/vim/src/test/vim_test_context.rs +++ b/crates/vim/src/test/vim_test_context.rs @@ -8,8 +8,6 @@ use search::{BufferSearchBar, ProjectSearchBar}; use crate::{state::Operator, *}; -use super::VimBindingTestContext; - pub struct VimTestContext<'a> { cx: EditorLspTestContext<'a>, } @@ -126,14 +124,6 @@ impl<'a> VimTestContext<'a> { assert_eq!(self.mode(), mode_after, "{}", self.assertion_context()); assert_eq!(self.active_operator(), None, "{}", self.assertion_context()); } - - pub fn binding( - mut self, - keystrokes: [&'static str; COUNT], - ) -> VimBindingTestContext<'a, COUNT> { - let mode = self.mode(); - VimBindingTestContext::new(keystrokes, mode, mode, self) - } } impl<'a> Deref for VimTestContext<'a> { diff --git a/crates/vim/src/utils.rs b/crates/vim/src/utils.rs index c8ca4df72b..4a96b5bbea 100644 --- a/crates/vim/src/utils.rs +++ b/crates/vim/src/utils.rs @@ -1,5 +1,6 @@ use editor::{ClipboardSelection, Editor}; use gpui::{AppContext, ClipboardItem}; +use language::Point; pub fn copy_selections_content(editor: &mut Editor, linewise: bool, cx: &mut AppContext) { let selections = editor.selections.all_adjusted(cx); @@ -9,7 +10,7 @@ pub fn copy_selections_content(editor: &mut Editor, linewise: bool, cx: &mut App { let mut is_first = true; for selection in selections.iter() { - let start = selection.start; + let mut start = selection.start; let end = selection.end; if is_first { is_first = false; @@ -17,9 +18,25 @@ pub fn copy_selections_content(editor: &mut Editor, linewise: bool, cx: &mut App text.push_str("\n"); } let initial_len = text.len(); + + // if the file does not end with \n, and our line-mode selection ends on + // that line, we will have expanded the start of the selection to ensure it + // contains a newline (so that delete works as expected). We undo that change + // here. + let is_last_line = linewise + && end.row == buffer.max_buffer_row() + && buffer.max_point().column > 0 + && start == Point::new(start.row, buffer.line_len(start.row)); + + if is_last_line { + start = Point::new(buffer.max_buffer_row(), 0); + } for chunk in buffer.text_for_range(start..end) { text.push_str(chunk); } + if is_last_line { + text.push_str("\n"); + } clipboard_selections.push(ClipboardSelection { len: text.len() - initial_len, is_entire_line: linewise, diff --git a/crates/vim/src/visual.rs b/crates/vim/src/visual.rs index 1a11721a4e..ea4847bd78 100644 --- a/crates/vim/src/visual.rs +++ b/crates/vim/src/visual.rs @@ -563,38 +563,41 @@ mod test { #[gpui::test] async fn test_visual_line_delete(cx: &mut gpui::TestAppContext) { - let mut cx = NeovimBackedTestContext::new(cx) - .await - .binding(["shift-v", "x"]); - cx.assert(indoc! {" + 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.assert_all(indoc! {" + cx.set_shared_state(indoc! {" The quick brown - fox juˇmps over - the laˇzy dog"}) - .await; - let mut cx = cx.binding(["shift-v", "j", "x"]); - cx.assert(indoc! {" - The quˇick brown fox jumps over - the lazy dog"}) - .await; - // Test pasting code copied on delete - cx.simulate_shared_keystroke("p").await; - cx.assert_state_matches().await; - - cx.assert_all(indoc! {" - The quick brown - fox juˇmps 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 @@ -608,86 +611,57 @@ mod test { #[gpui::test] async fn test_visual_yank(cx: &mut gpui::TestAppContext) { - let cx = VimTestContext::new(cx, true).await; - let mut cx = cx.binding(["v", "w", "y"]); - cx.assert("The quick ˇbrown", "The quick ˇbrown"); - cx.assert_clipboard_content(Some("brown")); - let mut cx = cx.binding(["v", "w", "j", "y"]); - cx.assert( - indoc! {" + 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"}, - indoc! {" - The ˇquick brown - fox jumps over - the lazy dog"}, - ); - cx.assert_clipboard_content(Some(indoc! {" - quick brown - fox jumps o"})); - cx.assert( - indoc! {" - The quick brown - fox jumps over - the ˇlazy dog"}, - indoc! {" - The quick brown - fox jumps over - the ˇlazy dog"}, - ); - cx.assert_clipboard_content(Some("lazy d")); - cx.assert( - indoc! {" - The quick brown - fox jumps ˇover - the lazy dog"}, - indoc! {" - The quick brown - fox jumps ˇover - the lazy dog"}, - ); - cx.assert_clipboard_content(Some(indoc! {" - over - t"})); + 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.assert( - indoc! {" - The ˇquick brown - fox jumps over - the lazy dog"}, - indoc! {" - ˇThe quick brown - fox jumps over - the lazy dog"}, - ); + 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.assert( - indoc! {" - The quick brown - fox jumps over - the ˇlazy dog"}, - indoc! {" - The quick brown - ˇfox jumps over - the lazy dog"}, - ); - cx.assert_clipboard_content(Some(indoc! {" - fox jumps over - the l"})); - cx.assert( - indoc! {" - The quick brown - fox jumps ˇover - the lazy dog"}, - indoc! {" - The ˇquick brown - fox jumps over - the lazy dog"}, - ); - cx.assert_clipboard_content(Some(indoc! {" - quick brown - fox jumps o"})); } #[gpui::test] diff --git a/crates/vim/test_data/test_visual_line_delete.json b/crates/vim/test_data/test_visual_line_delete.json index 51406266f6..e221a4ad5f 100644 --- a/crates/vim/test_data/test_visual_line_delete.json +++ b/crates/vim/test_data/test_visual_line_delete.json @@ -4,14 +4,11 @@ {"Get":{"state":"fox juˇmps over\nthe lazy dog","mode":"Normal"}} {"Key":"p"} {"Get":{"state":"fox jumps over\nˇThe quick brown\nthe lazy dog","mode":"Normal"}} -{"Put":{"state":"The quick brown\nfox juˇmps over\nthe lazy dog"}} -{"Key":"shift-v"} -{"Key":"x"} -{"Get":{"state":"The quick brown\nthe laˇzy dog","mode":"Normal"}} {"Put":{"state":"The quick brown\nfox jumps over\nthe laˇzy dog"}} {"Key":"shift-v"} {"Key":"x"} {"Get":{"state":"The quick brown\nfox juˇmps over","mode":"Normal"}} +{"ReadRegister":{"name":"\"","value":"the lazy dog\n"}} {"Put":{"state":"The quˇick brown\nfox jumps over\nthe lazy dog"}} {"Key":"shift-v"} {"Key":"j"} @@ -19,16 +16,6 @@ {"Get":{"state":"the laˇzy dog","mode":"Normal"}} {"Key":"p"} {"Get":{"state":"the lazy dog\nˇThe quick brown\nfox jumps over","mode":"Normal"}} -{"Put":{"state":"The quick brown\nfox juˇmps over\nthe lazy dog"}} -{"Key":"shift-v"} -{"Key":"j"} -{"Key":"x"} -{"Get":{"state":"The quˇick brown","mode":"Normal"}} -{"Put":{"state":"The quick brown\nfox jumps over\nthe laˇzy dog"}} -{"Key":"shift-v"} -{"Key":"j"} -{"Key":"x"} -{"Get":{"state":"The quick brown\nfox juˇmps over","mode":"Normal"}} {"Put":{"state":"The ˇlong line\nshould not\ncrash\n"}} {"Key":"shift-v"} {"Key":"$"} diff --git a/crates/vim/test_data/test_visual_yank.json b/crates/vim/test_data/test_visual_yank.json new file mode 100644 index 0000000000..edc3b4f83d --- /dev/null +++ b/crates/vim/test_data/test_visual_yank.json @@ -0,0 +1,29 @@ +{"Put":{"state":"The quick ˇbrown"}} +{"Key":"v"} +{"Key":"w"} +{"Key":"y"} +{"Get":{"state":"The quick ˇbrown","mode":"Normal"}} +{"ReadRegister":{"name":"\"","value":"brown"}} +{"Put":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog"}} +{"Key":"v"} +{"Key":"w"} +{"Key":"j"} +{"Key":"y"} +{"Get":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog","mode":"Normal"}} +{"ReadRegister":{"name":"\"","value":"quick brown\nfox jumps o"}} +{"Put":{"state":"The quick brown\nfox jumps over\nthe ˇlazy dog"}} +{"Key":"v"} +{"Key":"w"} +{"Key":"j"} +{"Key":"y"} +{"Get":{"state":"The quick brown\nfox jumps over\nthe ˇlazy dog","mode":"Normal"}} +{"ReadRegister":{"name":"\"","value":"lazy d"}} +{"Key":"shift-v"} +{"Key":"y"} +{"ReadRegister":{"name":"\"","value":"the lazy dog\n"}} +{"Put":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog"}} +{"Key":"v"} +{"Key":"b"} +{"Key":"k"} +{"Key":"y"} +{"Get":{"state":"ˇThe quick brown\nfox jumps over\nthe lazy dog","mode":"Normal"}}