From 4977acf6a58f7640ecca857610f1fa2a416b52ff Mon Sep 17 00:00:00 2001 From: Kay Simmons Date: Wed, 2 Nov 2022 01:20:11 -0700 Subject: [PATCH] fix some vim mode bugs around deletions and failed motions --- crates/editor/src/selections_collection.rs | 13 +++ crates/vim/src/motion.rs | 80 ++++++++++-------- crates/vim/src/normal.rs | 14 ++-- crates/vim/src/normal/change.rs | 82 ++++++++++++------- crates/vim/src/normal/delete.rs | 4 +- .../src/test/neovim_backed_test_context.rs | 9 +- crates/vim/src/visual.rs | 29 ++++--- .../test_change_end_of_document.json | 2 +- crates/vim/test_data/test_change_gg.json | 2 +- crates/vim/test_data/test_change_j.json | 2 +- crates/vim/test_data/test_change_k.json | 2 +- crates/vim/test_data/test_dd.json | 2 +- crates/vim/test_data/test_delete_e.json | 2 +- .../test_delete_end_of_document.json | 2 +- crates/vim/test_data/test_delete_gg.json | 2 +- crates/vim/test_data/test_delete_w.json | 2 +- crates/vim/test_data/test_repeated_cb.json | 2 +- 17 files changed, 154 insertions(+), 97 deletions(-) diff --git a/crates/editor/src/selections_collection.rs b/crates/editor/src/selections_collection.rs index fdb1fc44e4..256405f20e 100644 --- a/crates/editor/src/selections_collection.rs +++ b/crates/editor/src/selections_collection.rs @@ -677,6 +677,19 @@ impl<'a> MutableSelectionsCollection<'a> { }); } + pub fn maybe_move_cursors_with( + &mut self, + mut update_cursor_position: impl FnMut( + &DisplaySnapshot, + DisplayPoint, + SelectionGoal, + ) -> Option<(DisplayPoint, SelectionGoal)>, + ) { + self.move_cursors_with(|map, point, goal| { + update_cursor_position(map, point, goal).unwrap_or((point, goal)) + }) + } + pub fn replace_cursors_with( &mut self, mut find_replacement_cursors: impl FnMut(&DisplaySnapshot) -> Vec, diff --git a/crates/vim/src/motion.rs b/crates/vim/src/motion.rs index 20eb58e1e0..de621c5476 100644 --- a/crates/vim/src/motion.rs +++ b/crates/vim/src/motion.rs @@ -137,6 +137,11 @@ impl Motion { ) } + pub fn infallible(self) -> bool { + use Motion::*; + matches!(self, StartOfDocument | CurrentLine | EndOfDocument) + } + pub fn inclusive(self) -> bool { use Motion::*; match self { @@ -164,9 +169,9 @@ impl Motion { point: DisplayPoint, goal: SelectionGoal, times: usize, - ) -> (DisplayPoint, SelectionGoal) { + ) -> Option<(DisplayPoint, SelectionGoal)> { use Motion::*; - match self { + let (new_point, goal) = match self { Left => (left(map, point, times), SelectionGoal::None), Backspace => (backspace(map, point, times), SelectionGoal::None), Down => down(map, point, goal, times), @@ -191,7 +196,9 @@ impl Motion { StartOfDocument => (start_of_document(map, point, times), SelectionGoal::None), EndOfDocument => (end_of_document(map, point, times), SelectionGoal::None), Matching => (matching(map, point), SelectionGoal::None), - } + }; + + (new_point != point || self.infallible()).then_some((new_point, goal)) } // Expands a selection using self motion for an operator @@ -201,12 +208,13 @@ impl Motion { selection: &mut Selection, times: usize, expand_to_surrounding_newline: bool, - ) { - let (new_head, goal) = self.move_point(map, selection.head(), selection.goal, times); - selection.set_head(new_head, goal); + ) -> bool { + if let Some((new_head, goal)) = + self.move_point(map, selection.head(), selection.goal, times) + { + selection.set_head(new_head, goal); - if self.linewise() { - if selection.start != selection.end { + if self.linewise() { selection.start = map.prev_line_boundary(selection.start.to_point(map)).1; if expand_to_surrounding_newline { @@ -215,7 +223,7 @@ impl Motion { *selection.end.column_mut() = 0; selection.end = map.clip_point(selection.end, Bias::Right); // Don't reset the end here - return; + return true; } else if selection.start.row() > 0 { *selection.start.row_mut() -= 1; *selection.start.column_mut() = map.line_len(selection.start.row()); @@ -224,31 +232,33 @@ impl Motion { } (_, selection.end) = map.next_line_boundary(selection.end.to_point(map)); - } - } else { - // If the motion is exclusive and the end of the motion is in column 1, the - // end of the motion is moved to the end of the previous line and the motion - // becomes inclusive. Example: "}" moves to the first line after a paragraph, - // but "d}" will not include that line. - let mut inclusive = self.inclusive(); - if !inclusive - && self != Motion::Backspace - && selection.end.row() > selection.start.row() - && selection.end.column() == 0 - && selection.end.row() > 0 - { - inclusive = true; - *selection.end.row_mut() -= 1; - *selection.end.column_mut() = 0; - selection.end = map.clip_point( - map.next_line_boundary(selection.end.to_point(map)).1, - Bias::Left, - ); - } + } else { + // If the motion is exclusive and the end of the motion is in column 1, the + // end of the motion is moved to the end of the previous line and the motion + // becomes inclusive. Example: "}" moves to the first line after a paragraph, + // but "d}" will not include that line. + let mut inclusive = self.inclusive(); + if !inclusive + && self != Motion::Backspace + && selection.end.row() > selection.start.row() + && selection.end.column() == 0 + { + inclusive = true; + *selection.end.row_mut() -= 1; + *selection.end.column_mut() = 0; + selection.end = map.clip_point( + map.next_line_boundary(selection.end.to_point(map)).1, + Bias::Left, + ); + } - if inclusive && selection.end.column() < map.line_len(selection.end.row()) { - *selection.end.column_mut() += 1; + if inclusive && selection.end.column() < map.line_len(selection.end.row()) { + *selection.end.column_mut() += 1; + } } + true + } else { + false } } } @@ -325,9 +335,7 @@ pub(crate) fn next_word_start( || at_newline && crossed_newline || at_newline && left == '\n'; // Prevents skipping repeated empty lines - if at_newline { - crossed_newline = true; - } + crossed_newline |= at_newline; found }) } @@ -350,7 +358,7 @@ fn next_word_end( }); // find_boundary clips, so if the character after the next character is a newline or at the end of the document, we know - // we have backtraced already + // we have backtracked already if !map .chars_at(point) .nth(1) diff --git a/crates/vim/src/normal.rs b/crates/vim/src/normal.rs index 4720ed7373..531f52bf9a 100644 --- a/crates/vim/src/normal.rs +++ b/crates/vim/src/normal.rs @@ -115,7 +115,11 @@ pub fn normal_object(object: Object, cx: &mut MutableAppContext) { fn move_cursor(vim: &mut Vim, motion: Motion, times: usize, cx: &mut MutableAppContext) { vim.update_active_editor(cx, |editor, cx| { editor.change_selections(Some(Autoscroll::Fit), cx, |s| { - s.move_cursors_with(|map, cursor, goal| motion.move_point(map, cursor, goal, times)) + s.move_cursors_with(|map, cursor, goal| { + motion + .move_point(map, cursor, goal, times) + .unwrap_or((cursor, goal)) + }) }) }); } @@ -125,7 +129,7 @@ fn insert_after(_: &mut Workspace, _: &InsertAfter, cx: &mut ViewContext, times: usize, ignore_punctuation: bool, -) { - if times > 1 { - Motion::NextWordStart { ignore_punctuation }.expand_selection( - map, - selection, - times - 1, - false, - ); +) -> bool { + if times == 1 { + let in_word = map + .chars_at(selection.head()) + .next() + .map(|(c, _)| char_kind(c) != CharKind::Whitespace) + .unwrap_or_default(); + + if in_word { + selection.end = movement::find_boundary(map, selection.end, |left, right| { + let left_kind = char_kind(left).coerce_punctuation(ignore_punctuation); + let right_kind = char_kind(right).coerce_punctuation(ignore_punctuation); + + left_kind != right_kind && left_kind != CharKind::Whitespace + }); + true + } else { + Motion::NextWordStart { ignore_punctuation }.expand_selection(map, selection, 1, false) + } + } else { + Motion::NextWordStart { ignore_punctuation }.expand_selection(map, selection, times, false) } - - if times == 1 && selection.end.column() == map.line_len(selection.end.row()) { - return; - } - - selection.end = movement::find_boundary(map, selection.end, |left, right| { - let left_kind = char_kind(left).coerce_punctuation(ignore_punctuation); - let right_kind = char_kind(right).coerce_punctuation(ignore_punctuation); - - left_kind != right_kind || left == '\n' || right == '\n' - }); } #[cfg(test)] diff --git a/crates/vim/src/normal/delete.rs b/crates/vim/src/normal/delete.rs index b0ff7b5f0e..d9446e68a1 100644 --- a/crates/vim/src/normal/delete.rs +++ b/crates/vim/src/normal/delete.rs @@ -143,7 +143,7 @@ mod test { Test test ˇ test"}, - ExemptionFeatures::DeletionOnEmptyLine, + ExemptionFeatures::DeleteWordOnEmptyLine, ) .await; @@ -169,7 +169,7 @@ mod test { Test test ˇ test"}, - ExemptionFeatures::DeletionOnEmptyLine, + ExemptionFeatures::OperatorLastNewlineRemains, ) .await; diff --git a/crates/vim/src/test/neovim_backed_test_context.rs b/crates/vim/src/test/neovim_backed_test_context.rs index 1d5393fbb6..a6bf5bc6fa 100644 --- a/crates/vim/src/test/neovim_backed_test_context.rs +++ b/crates/vim/src/test/neovim_backed_test_context.rs @@ -8,7 +8,10 @@ use util::test::marked_text_offsets; use super::{neovim_connection::NeovimConnection, NeovimBackedBindingTestContext, VimTestContext}; use crate::state::Mode; -pub const SUPPORTED_FEATURES: &[ExemptionFeatures] = &[ExemptionFeatures::DeletionOnEmptyLine]; +pub const SUPPORTED_FEATURES: &[ExemptionFeatures] = &[ + ExemptionFeatures::DeletionOnEmptyLine, + ExemptionFeatures::OperatorAbortsOnFailedMotion, +]; /// Enum representing features we have tests for but which don't work, yet. Used /// to add exemptions and automatically @@ -19,6 +22,10 @@ pub enum ExemptionFeatures { DeletionOnEmptyLine, // When a motion fails, it should should not apply linewise operations OperatorAbortsOnFailedMotion, + // When an operator completes at the end of the file, an extra newline is left + OperatorLastNewlineRemains, + // Deleting a word on an empty line doesn't remove the newline + DeleteWordOnEmptyLine, // OBJECTS // Resulting position after the operation is slightly incorrect for unintuitive reasons. diff --git a/crates/vim/src/visual.rs b/crates/vim/src/visual.rs index 481d2570ae..7e0d48499c 100644 --- a/crates/vim/src/visual.rs +++ b/crates/vim/src/visual.rs @@ -30,20 +30,23 @@ pub fn visual_motion(motion: Motion, times: usize, cx: &mut MutableAppContext) { s.move_with(|map, selection| { let was_reversed = selection.reversed; - let (new_head, goal) = - motion.move_point(map, selection.head(), selection.goal, times); - selection.set_head(new_head, goal); + if let Some((new_head, goal)) = + motion.move_point(map, selection.head(), selection.goal, times) + { + selection.set_head(new_head, goal); - if was_reversed && !selection.reversed { - // Head was at the start of the selection, and now is at the end. We need to move the start - // back by one if possible in order to compensate for this change. - *selection.start.column_mut() = selection.start.column().saturating_sub(1); - selection.start = map.clip_point(selection.start, Bias::Left); - } else if !was_reversed && selection.reversed { - // Head was at the end of the selection, and now is at the start. We need to move the end - // forward by one if possible in order to compensate for this change. - *selection.end.column_mut() = selection.end.column() + 1; - selection.end = map.clip_point(selection.end, Bias::Right); + if was_reversed && !selection.reversed { + // Head was at the start of the selection, and now is at the end. We need to move the start + // back by one if possible in order to compensate for this change. + *selection.start.column_mut() = + selection.start.column().saturating_sub(1); + selection.start = map.clip_point(selection.start, Bias::Left); + } else if !was_reversed && selection.reversed { + // Head was at the end of the selection, and now is at the start. We need to move the end + // forward by one if possible in order to compensate for this change. + *selection.end.column_mut() = selection.end.column() + 1; + selection.end = map.clip_point(selection.end, Bias::Right); + } } }); }); diff --git a/crates/vim/test_data/test_change_end_of_document.json b/crates/vim/test_data/test_change_end_of_document.json index dcd810910c..8a0cc840be 100644 --- a/crates/vim/test_data/test_change_end_of_document.json +++ b/crates/vim/test_data/test_change_end_of_document.json @@ -1 +1 @@ -[{"Text":"The quick\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"}] \ No newline at end of file +[{"Text":"The quick\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick\nbrown fox\njumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"},{"Text":"The quick\nbrown fox\njumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_change_gg.json b/crates/vim/test_data/test_change_gg.json index dceb783e9c..b8e250788c 100644 --- a/crates/vim/test_data/test_change_gg.json +++ b/crates/vim/test_data/test_change_gg.json @@ -1 +1 @@ -[{"Text":"\njumps over\nthe lazy"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":""},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"}] \ No newline at end of file +[{"Text":"\njumps over\nthe lazy"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":""},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\nbrown fox\njumps over\nthe lazy"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\nbrown fox\njumps over\nthe lazy"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_change_j.json b/crates/vim/test_data/test_change_j.json index 3057656a99..a8ce6bbc85 100644 --- a/crates/vim/test_data/test_change_j.json +++ b/crates/vim/test_data/test_change_j.json @@ -1 +1 @@ -[{"Text":"The quick\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"\njumps over"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"}] \ No newline at end of file +[{"Text":"The quick\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick\nbrown fox\njumps over"},{"Mode":"Normal"},{"Selection":{"start":[2,6],"end":[2,6]}},{"Mode":"Normal"},{"Text":"\njumps over"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick\nbrown fox\n"},{"Mode":"Normal"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_change_k.json b/crates/vim/test_data/test_change_k.json index 7352c6052b..e37a013ec3 100644 --- a/crates/vim/test_data/test_change_k.json +++ b/crates/vim/test_data/test_change_k.json @@ -1 +1 @@ -[{"Text":"\njumps over"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"}] \ No newline at end of file +[{"Text":"\njumps over"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick\nbrown fox\njumps over"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"},{"Text":"\nbrown fox\njumps over"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_dd.json b/crates/vim/test_data/test_dd.json index a633028b9c..fa86b9d3b5 100644 --- a/crates/vim/test_data/test_dd.json +++ b/crates/vim/test_data/test_dd.json @@ -1 +1 @@ -[{"Text":""},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":""},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"brown fox\njumps over"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"},{"Text":"The quick\njumps over"},{"Mode":"Normal"},{"Selection":{"start":[1,6],"end":[1,6]}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox"},{"Mode":"Normal"},{"Selection":{"start":[1,6],"end":[1,6]}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox"}] \ No newline at end of file +[{"Text":""},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":""},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"brown fox\njumps over"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"},{"Text":"The quick\njumps over"},{"Mode":"Normal"},{"Selection":{"start":[1,6],"end":[1,6]}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox"},{"Mode":"Normal"},{"Selection":{"start":[1,6],"end":[1,6]}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox"},{"Mode":"Normal"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_e.json b/crates/vim/test_data/test_delete_e.json index 231fbe0ff9..e1bc415620 100644 --- a/crates/vim/test_data/test_delete_e.json +++ b/crates/vim/test_data/test_delete_e.json @@ -1 +1 @@ -[{"Text":"Te Test"},{"Mode":"Normal"},{"Selection":{"start":[0,2],"end":[0,2]}},{"Mode":"Normal"},{"Text":"T test"},{"Mode":"Normal"},{"Selection":{"start":[0,1],"end":[0,1]}},{"Mode":"Normal"},{"Text":"Test te\ntest"},{"Mode":"Normal"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Normal"},{"Text":"Test tes"},{"Mode":"Normal"},{"Selection":{"start":[0,7],"end":[0,7]}},{"Mode":"Normal"},{"Text":"Test test"}] \ No newline at end of file +[{"Text":"Te Test"},{"Mode":"Normal"},{"Selection":{"start":[0,2],"end":[0,2]}},{"Mode":"Normal"},{"Text":"T test"},{"Mode":"Normal"},{"Selection":{"start":[0,1],"end":[0,1]}},{"Mode":"Normal"},{"Text":"Test te\ntest"},{"Mode":"Normal"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Normal"},{"Text":"Test tes"},{"Mode":"Normal"},{"Selection":{"start":[0,7],"end":[0,7]}},{"Mode":"Normal"},{"Text":"Test te test"},{"Mode":"Normal"},{"Selection":{"start":[0,7],"end":[0,7]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_end_of_document.json b/crates/vim/test_data/test_delete_end_of_document.json index c0900e41b8..a43eb2d6fd 100644 --- a/crates/vim/test_data/test_delete_end_of_document.json +++ b/crates/vim/test_data/test_delete_end_of_document.json @@ -1 +1 @@ -[{"Text":"The quick"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"},{"Text":"The quick"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"}] \ No newline at end of file +[{"Text":"The quick"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"},{"Text":"The quick"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox\njumps over"},{"Mode":"Normal"},{"Selection":{"start":[2,5],"end":[2,5]}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox\njumps over"},{"Mode":"Normal"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_gg.json b/crates/vim/test_data/test_delete_gg.json index c33f03057d..ac6e54f355 100644 --- a/crates/vim/test_data/test_delete_gg.json +++ b/crates/vim/test_data/test_delete_gg.json @@ -1 +1 @@ -[{"Text":"jumps over\nthe lazy"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"},{"Text":""},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"}] \ No newline at end of file +[{"Text":"jumps over\nthe lazy"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"},{"Text":""},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"brown fox\njumps over\nthe lazy"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"},{"Text":"brown fox\njumps over\nthe lazy"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_w.json b/crates/vim/test_data/test_delete_w.json index 10c4782196..cb05a3b53a 100644 --- a/crates/vim/test_data/test_delete_w.json +++ b/crates/vim/test_data/test_delete_w.json @@ -1 +1 @@ -[{"Text":"Te"},{"Mode":"Normal"},{"Selection":{"start":[0,1],"end":[0,1]}},{"Mode":"Normal"},{"Text":"Ttest"},{"Mode":"Normal"},{"Selection":{"start":[0,1],"end":[0,1]}},{"Mode":"Normal"},{"Text":"Test te\ntest"},{"Mode":"Normal"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Normal"},{"Text":"Test tes\ntest"},{"Mode":"Normal"},{"Selection":{"start":[0,7],"end":[0,7]}},{"Mode":"Normal"},{"Text":"Test test\ntest"}] \ No newline at end of file +[{"Text":"Te"},{"Mode":"Normal"},{"Selection":{"start":[0,1],"end":[0,1]}},{"Mode":"Normal"},{"Text":"Ttest"},{"Mode":"Normal"},{"Selection":{"start":[0,1],"end":[0,1]}},{"Mode":"Normal"},{"Text":"Test te\ntest"},{"Mode":"Normal"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Normal"},{"Text":"Test tes\ntest"},{"Mode":"Normal"},{"Selection":{"start":[0,7],"end":[0,7]}},{"Mode":"Normal"},{"Text":"Test tetest"},{"Mode":"Normal"},{"Selection":{"start":[0,7],"end":[0,7]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_repeated_cb.json b/crates/vim/test_data/test_repeated_cb.json index b00195b2d9..ccb2312091 100644 --- a/crates/vim/test_data/test_repeated_cb.json +++ b/crates/vim/test_data/test_repeated_cb.json @@ -1 +1 @@ -[{"Text":"The ick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Insert"},{"Text":"The brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Insert"},{"Text":"The quick n\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick \n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\njumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox -over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumpsover\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,9],"end":[2,9]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-ver\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,10],"end":[2,10]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,10],"end":[2,10]}},{"Mode":"Insert"},{"Text":"ick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The n\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Insert"},{"Text":"The \n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick brown\njumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumpsver\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,9],"end":[2,9]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,9],"end":[2,9]}},{"Mode":"Insert"},{"Text":"ick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"n\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The \nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Insert"},{"Text":"The quick jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick brown\n-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nover\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox ver\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox \nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Insert"},{"Text":"ick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"n\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Insert"},{"Text":"The quick -over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick brown\nover\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nver\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"ick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"n\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The -over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Insert"},{"Text":"The quick over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick brown\nver\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"}] \ No newline at end of file +[{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The ick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Insert"},{"Text":"The brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Insert"},{"Text":"The quick n\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick \n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\njumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox -over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumpsover\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,9],"end":[2,9]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-ver\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,10],"end":[2,10]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,10],"end":[2,10]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"ick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The n\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Insert"},{"Text":"The \n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick brown\njumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumpsver\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,9],"end":[2,9]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,9],"end":[2,9]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"ick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"n\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The \nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Insert"},{"Text":"The quick jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick brown\n-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nover\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox ver\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox \nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"ick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"n\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Insert"},{"Text":"The quick -over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick brown\nover\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nver\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"ick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"n\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The -over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Insert"},{"Text":"The quick over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick brown\nver\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"}] \ No newline at end of file