vim: Add some forced motion support (#27991)
Closes https://github.com/zed-industries/zed/issues/20971 Added `v` input to yank and delete to override default motion. The global vim state tracking if the forced motion flag was passed handled the same way that the count is. [The main chunk of code maps the motion kind from the default to the overridden kind](https://github.com/zed-industries/zed/pull/27991/files#diff-2dca6b7d1673c912d14e4edc74e415abbe3a4e6d6b37e0e2006d30828bf4bb9cR1249-R1254). To handle the case of deleting a single character (dv0) at the start of a row I had to modify the control flow [here](https://github.com/zed-industries/zed/pull/27991/files#diff-2dca6b7d1673c912d14e4edc74e415abbe3a4e6d6b37e0e2006d30828bf4bb9cR1240-R1244). Then to handle an exclusive delete till the end of the row (dv$) I [saturated the endpoint with a left bias](https://github.com/zed-industries/zed/pull/27991/files#diff-2dca6b7d1673c912d14e4edc74e415abbe3a4e6d6b37e0e2006d30828bf4bb9cR1281-R1286). Test case: dv0 https://github.com/user-attachments/assets/613cf9fb-9732-425c-9179-025f3e107584 Test case: yvjp https://github.com/user-attachments/assets/550b7c77-1eb8-41c3-894b-117eb50b7a5d Release Notes: - Added some forced motion support for delete and yank
This commit is contained in:
parent
1df01eabfe
commit
08ce230bae
30 changed files with 485 additions and 58 deletions
|
@ -539,6 +539,7 @@
|
|||
"bindings": {
|
||||
"d": "vim::CurrentLine",
|
||||
"s": "vim::PushDeleteSurrounds",
|
||||
"v": "vim::PushForcedMotion", // "d v"
|
||||
"o": "editor::ToggleSelectedDiffHunks", // "d o"
|
||||
"shift-o": "git::ToggleStaged",
|
||||
"p": "git::Restore", // "d p"
|
||||
|
@ -587,6 +588,7 @@
|
|||
"context": "vim_operator == y",
|
||||
"bindings": {
|
||||
"y": "vim::CurrentLine",
|
||||
"v": "vim::PushForcedMotion",
|
||||
"s": ["vim::PushAddSurrounds", {}]
|
||||
}
|
||||
},
|
||||
|
|
|
@ -24,6 +24,7 @@ impl Vim {
|
|||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let count = Vim::take_count(cx).unwrap_or(1);
|
||||
Vim::take_forced_motion(cx);
|
||||
if self.change_list.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -234,6 +234,7 @@ pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
|
|||
return;
|
||||
};
|
||||
let count = Vim::take_count(cx).unwrap_or(1);
|
||||
Vim::take_forced_motion(cx);
|
||||
let n = if count > 1 {
|
||||
format!(".,.+{}", count.saturating_sub(1))
|
||||
} else {
|
||||
|
@ -1323,6 +1324,7 @@ impl Vim {
|
|||
&mut self,
|
||||
motion: Motion,
|
||||
times: Option<usize>,
|
||||
forced_motion: bool,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Vim>,
|
||||
) {
|
||||
|
@ -1335,7 +1337,13 @@ impl Vim {
|
|||
let start = editor.selections.newest_display(cx);
|
||||
let text_layout_details = editor.text_layout_details(window);
|
||||
let (mut range, _) = motion
|
||||
.range(&snapshot, start.clone(), times, &text_layout_details)
|
||||
.range(
|
||||
&snapshot,
|
||||
start.clone(),
|
||||
times,
|
||||
&text_layout_details,
|
||||
forced_motion,
|
||||
)
|
||||
.unwrap_or((start.range(), MotionKind::Exclusive));
|
||||
if range.start != start.start {
|
||||
editor.change_selections(None, window, cx, |s| {
|
||||
|
|
|
@ -18,6 +18,7 @@ pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
|
|||
Vim::action(editor, cx, |vim, _: &Indent, window, cx| {
|
||||
vim.record_current_action(cx);
|
||||
let count = Vim::take_count(cx).unwrap_or(1);
|
||||
Vim::take_forced_motion(cx);
|
||||
vim.store_visual_marks(window, cx);
|
||||
vim.update_editor(window, cx, |vim, editor, window, cx| {
|
||||
editor.transact(window, cx, |editor, window, cx| {
|
||||
|
@ -36,6 +37,7 @@ pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
|
|||
Vim::action(editor, cx, |vim, _: &Outdent, window, cx| {
|
||||
vim.record_current_action(cx);
|
||||
let count = Vim::take_count(cx).unwrap_or(1);
|
||||
Vim::take_forced_motion(cx);
|
||||
vim.store_visual_marks(window, cx);
|
||||
vim.update_editor(window, cx, |vim, editor, window, cx| {
|
||||
editor.transact(window, cx, |editor, window, cx| {
|
||||
|
@ -54,6 +56,7 @@ pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
|
|||
Vim::action(editor, cx, |vim, _: &AutoIndent, window, cx| {
|
||||
vim.record_current_action(cx);
|
||||
let count = Vim::take_count(cx).unwrap_or(1);
|
||||
Vim::take_forced_motion(cx);
|
||||
vim.store_visual_marks(window, cx);
|
||||
vim.update_editor(window, cx, |vim, editor, window, cx| {
|
||||
editor.transact(window, cx, |editor, window, cx| {
|
||||
|
@ -75,6 +78,7 @@ impl Vim {
|
|||
&mut self,
|
||||
motion: Motion,
|
||||
times: Option<usize>,
|
||||
forced_motion: bool,
|
||||
dir: IndentDirection,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
|
@ -88,7 +92,13 @@ impl Vim {
|
|||
s.move_with(|map, selection| {
|
||||
let anchor = map.display_point_to_anchor(selection.head(), Bias::Right);
|
||||
selection_starts.insert(selection.id, anchor);
|
||||
motion.expand_selection(map, selection, times, &text_layout_details);
|
||||
motion.expand_selection(
|
||||
map,
|
||||
selection,
|
||||
times,
|
||||
&text_layout_details,
|
||||
forced_motion,
|
||||
);
|
||||
});
|
||||
});
|
||||
match dir {
|
||||
|
|
|
@ -23,6 +23,7 @@ impl Vim {
|
|||
return;
|
||||
}
|
||||
let count = Vim::take_count(cx).unwrap_or(1);
|
||||
Vim::take_forced_motion(cx);
|
||||
self.stop_recording_immediately(action.boxed_clone(), cx);
|
||||
if count <= 1 || Vim::globals(cx).dot_replaying {
|
||||
self.create_mark("^".into(), window, cx);
|
||||
|
|
|
@ -650,6 +650,7 @@ impl Vim {
|
|||
}
|
||||
|
||||
let count = Vim::take_count(cx);
|
||||
let forced_motion = Vim::take_forced_motion(cx);
|
||||
let active_operator = self.active_operator();
|
||||
let mut waiting_operator: Option<Operator> = None;
|
||||
match self.mode {
|
||||
|
@ -659,7 +660,14 @@ impl Vim {
|
|||
target: Some(SurroundsType::Motion(motion)),
|
||||
});
|
||||
} else {
|
||||
self.normal_motion(motion.clone(), active_operator.clone(), count, window, cx)
|
||||
self.normal_motion(
|
||||
motion.clone(),
|
||||
active_operator.clone(),
|
||||
count,
|
||||
forced_motion,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
}
|
||||
Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
|
||||
|
@ -1183,7 +1191,6 @@ impl Motion {
|
|||
SelectionGoal::None,
|
||||
),
|
||||
};
|
||||
|
||||
(new_point != point || infallible).then_some((new_point, goal))
|
||||
}
|
||||
|
||||
|
@ -1194,6 +1201,7 @@ impl Motion {
|
|||
selection: Selection<DisplayPoint>,
|
||||
times: Option<usize>,
|
||||
text_layout_details: &TextLayoutDetails,
|
||||
forced_motion: bool,
|
||||
) -> Option<(Range<DisplayPoint>, MotionKind)> {
|
||||
if let Motion::ZedSearchResult {
|
||||
prior_selections,
|
||||
|
@ -1221,18 +1229,29 @@ impl Motion {
|
|||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
let (new_head, goal) = self.move_point(
|
||||
let maybe_new_point = self.move_point(
|
||||
map,
|
||||
selection.head(),
|
||||
selection.goal,
|
||||
times,
|
||||
text_layout_details,
|
||||
)?;
|
||||
);
|
||||
|
||||
let (new_head, goal) = match (maybe_new_point, forced_motion) {
|
||||
(Some((p, g)), _) => Some((p, g)),
|
||||
(None, false) => None,
|
||||
(None, true) => Some((selection.head(), selection.goal)),
|
||||
}?;
|
||||
|
||||
let mut selection = selection.clone();
|
||||
selection.set_head(new_head, goal);
|
||||
|
||||
let mut kind = self.default_kind();
|
||||
let mut kind = match (self.default_kind(), forced_motion) {
|
||||
(MotionKind::Linewise, true) => MotionKind::Exclusive,
|
||||
(MotionKind::Exclusive, true) => MotionKind::Inclusive,
|
||||
(MotionKind::Inclusive, true) => MotionKind::Exclusive,
|
||||
(kind, false) => kind,
|
||||
};
|
||||
|
||||
if let Motion::NextWordStart {
|
||||
ignore_punctuation: _,
|
||||
|
@ -1259,6 +1278,12 @@ impl Motion {
|
|||
} else if kind == MotionKind::Exclusive && !self.skip_exclusive_special_case() {
|
||||
let start_point = selection.start.to_point(map);
|
||||
let mut end_point = selection.end.to_point(map);
|
||||
let mut next_point = selection.end;
|
||||
*next_point.column_mut() += 1;
|
||||
next_point = map.clip_point(next_point, Bias::Right);
|
||||
if next_point.to_point(map) == end_point && forced_motion {
|
||||
selection.end = movement::saturating_left(map, selection.end);
|
||||
}
|
||||
|
||||
if end_point.row > start_point.row {
|
||||
let first_non_blank_of_start_row = map
|
||||
|
@ -1304,8 +1329,15 @@ impl Motion {
|
|||
selection: &mut Selection<DisplayPoint>,
|
||||
times: Option<usize>,
|
||||
text_layout_details: &TextLayoutDetails,
|
||||
forced_motion: bool,
|
||||
) -> Option<MotionKind> {
|
||||
let (range, kind) = self.range(map, selection.clone(), times, text_layout_details)?;
|
||||
let (range, kind) = self.range(
|
||||
map,
|
||||
selection.clone(),
|
||||
times,
|
||||
text_layout_details,
|
||||
forced_motion,
|
||||
)?;
|
||||
selection.start = range.start;
|
||||
selection.end = range.end;
|
||||
Some(kind)
|
||||
|
@ -3816,6 +3848,7 @@ mod test {
|
|||
Mode::Normal,
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_delete_key_can_remove_last_character(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = NeovimBackedTestContext::new(cx).await;
|
||||
|
@ -3823,4 +3856,147 @@ mod test {
|
|||
cx.simulate_shared_keystrokes("delete").await;
|
||||
cx.shared_state().await.assert_eq("aˇb");
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_forced_motion_delete_to_start_of_line(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = NeovimBackedTestContext::new(cx).await;
|
||||
|
||||
cx.set_shared_state(indoc! {"
|
||||
ˇthe quick brown fox
|
||||
jumped over the lazy dog"})
|
||||
.await;
|
||||
cx.simulate_shared_keystrokes("d v 0").await;
|
||||
cx.shared_state().await.assert_eq(indoc! {"
|
||||
ˇhe quick brown fox
|
||||
jumped over the lazy dog"});
|
||||
assert_eq!(cx.cx.forced_motion(), false);
|
||||
|
||||
cx.set_shared_state(indoc! {"
|
||||
the quick bˇrown fox
|
||||
jumped over the lazy dog"})
|
||||
.await;
|
||||
cx.simulate_shared_keystrokes("d v 0").await;
|
||||
cx.shared_state().await.assert_eq(indoc! {"
|
||||
ˇown fox
|
||||
jumped over the lazy dog"});
|
||||
assert_eq!(cx.cx.forced_motion(), false);
|
||||
|
||||
cx.set_shared_state(indoc! {"
|
||||
the quick brown foˇx
|
||||
jumped over the lazy dog"})
|
||||
.await;
|
||||
cx.simulate_shared_keystrokes("d v 0").await;
|
||||
cx.shared_state().await.assert_eq(indoc! {"
|
||||
ˇ
|
||||
jumped over the lazy dog"});
|
||||
assert_eq!(cx.cx.forced_motion(), false);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_forced_motion_delete_to_end_of_line(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = NeovimBackedTestContext::new(cx).await;
|
||||
|
||||
cx.set_shared_state(indoc! {"
|
||||
the quick brown foˇx
|
||||
jumped over the lazy dog"})
|
||||
.await;
|
||||
cx.simulate_shared_keystrokes("d v $").await;
|
||||
cx.shared_state().await.assert_eq(indoc! {"
|
||||
the quick brown foˇx
|
||||
jumped over the lazy dog"});
|
||||
assert_eq!(cx.cx.forced_motion(), false);
|
||||
|
||||
cx.set_shared_state(indoc! {"
|
||||
ˇthe quick brown fox
|
||||
jumped over the lazy dog"})
|
||||
.await;
|
||||
cx.simulate_shared_keystrokes("d v $").await;
|
||||
cx.shared_state().await.assert_eq(indoc! {"
|
||||
ˇx
|
||||
jumped over the lazy dog"});
|
||||
assert_eq!(cx.cx.forced_motion(), false);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_forced_motion_yank(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = NeovimBackedTestContext::new(cx).await;
|
||||
|
||||
cx.set_shared_state(indoc! {"
|
||||
ˇthe quick brown fox
|
||||
jumped over the lazy dog"})
|
||||
.await;
|
||||
cx.simulate_shared_keystrokes("y v j p").await;
|
||||
cx.shared_state().await.assert_eq(indoc! {"
|
||||
the quick brown fox
|
||||
ˇthe quick brown fox
|
||||
jumped over the lazy dog"});
|
||||
assert_eq!(cx.cx.forced_motion(), false);
|
||||
|
||||
cx.set_shared_state(indoc! {"
|
||||
the quick bˇrown fox
|
||||
jumped over the lazy dog"})
|
||||
.await;
|
||||
cx.simulate_shared_keystrokes("y v j p").await;
|
||||
cx.shared_state().await.assert_eq(indoc! {"
|
||||
the quick brˇrown fox
|
||||
jumped overown fox
|
||||
jumped over the lazy dog"});
|
||||
assert_eq!(cx.cx.forced_motion(), false);
|
||||
|
||||
cx.set_shared_state(indoc! {"
|
||||
the quick brown foˇx
|
||||
jumped over the lazy dog"})
|
||||
.await;
|
||||
cx.simulate_shared_keystrokes("y v j p").await;
|
||||
cx.shared_state().await.assert_eq(indoc! {"
|
||||
the quick brown foxˇx
|
||||
jumped over the la
|
||||
jumped over the lazy dog"});
|
||||
assert_eq!(cx.cx.forced_motion(), false);
|
||||
|
||||
cx.set_shared_state(indoc! {"
|
||||
the quick brown fox
|
||||
jˇumped over the lazy dog"})
|
||||
.await;
|
||||
cx.simulate_shared_keystrokes("y v k p").await;
|
||||
cx.shared_state().await.assert_eq(indoc! {"
|
||||
thˇhe quick brown fox
|
||||
je quick brown fox
|
||||
jumped over the lazy dog"});
|
||||
assert_eq!(cx.cx.forced_motion(), false);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_inclusive_to_exclusive_delete(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = NeovimBackedTestContext::new(cx).await;
|
||||
|
||||
cx.set_shared_state(indoc! {"
|
||||
ˇthe quick brown fox
|
||||
jumped over the lazy dog"})
|
||||
.await;
|
||||
cx.simulate_shared_keystrokes("d v e").await;
|
||||
cx.shared_state().await.assert_eq(indoc! {"
|
||||
ˇe quick brown fox
|
||||
jumped over the lazy dog"});
|
||||
assert_eq!(cx.cx.forced_motion(), false);
|
||||
|
||||
cx.set_shared_state(indoc! {"
|
||||
the quick bˇrown fox
|
||||
jumped over the lazy dog"})
|
||||
.await;
|
||||
cx.simulate_shared_keystrokes("d v e").await;
|
||||
cx.shared_state().await.assert_eq(indoc! {"
|
||||
the quick bˇn fox
|
||||
jumped over the lazy dog"});
|
||||
assert_eq!(cx.cx.forced_motion(), false);
|
||||
|
||||
cx.set_shared_state(indoc! {"
|
||||
the quick brown foˇx
|
||||
jumped over the lazy dog"})
|
||||
.await;
|
||||
cx.simulate_shared_keystrokes("d v e").await;
|
||||
cx.shared_state().await.assert_eq(indoc! {"
|
||||
the quick brown foˇd over the lazy dog"});
|
||||
assert_eq!(cx.cx.forced_motion(), false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,12 +86,14 @@ pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
|
|||
Vim::action(editor, cx, |vim, _: &DeleteLeft, window, cx| {
|
||||
vim.record_current_action(cx);
|
||||
let times = Vim::take_count(cx);
|
||||
vim.delete_motion(Motion::Left, times, window, cx);
|
||||
let forced_motion = Vim::take_forced_motion(cx);
|
||||
vim.delete_motion(Motion::Left, times, forced_motion, window, cx);
|
||||
});
|
||||
Vim::action(editor, cx, |vim, _: &DeleteRight, window, cx| {
|
||||
vim.record_current_action(cx);
|
||||
let times = Vim::take_count(cx);
|
||||
vim.delete_motion(Motion::Right, times, window, cx);
|
||||
let forced_motion = Vim::take_forced_motion(cx);
|
||||
vim.delete_motion(Motion::Right, times, forced_motion, window, cx);
|
||||
});
|
||||
|
||||
Vim::action(editor, cx, |vim, _: &HelixDelete, window, cx| {
|
||||
|
@ -111,11 +113,13 @@ pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
|
|||
Vim::action(editor, cx, |vim, _: &ChangeToEndOfLine, window, cx| {
|
||||
vim.start_recording(cx);
|
||||
let times = Vim::take_count(cx);
|
||||
let forced_motion = Vim::take_forced_motion(cx);
|
||||
vim.change_motion(
|
||||
Motion::EndOfLine {
|
||||
display_lines: false,
|
||||
},
|
||||
times,
|
||||
forced_motion,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
|
@ -123,11 +127,13 @@ pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
|
|||
Vim::action(editor, cx, |vim, _: &DeleteToEndOfLine, window, cx| {
|
||||
vim.record_current_action(cx);
|
||||
let times = Vim::take_count(cx);
|
||||
let forced_motion = Vim::take_forced_motion(cx);
|
||||
vim.delete_motion(
|
||||
Motion::EndOfLine {
|
||||
display_lines: false,
|
||||
},
|
||||
times,
|
||||
forced_motion,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
|
@ -142,6 +148,7 @@ pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
|
|||
|
||||
Vim::action(editor, cx, |vim, _: &Undo, window, cx| {
|
||||
let times = Vim::take_count(cx);
|
||||
Vim::take_forced_motion(cx);
|
||||
vim.update_editor(window, cx, |_, editor, window, cx| {
|
||||
for _ in 0..times.unwrap_or(1) {
|
||||
editor.undo(&editor::actions::Undo, window, cx);
|
||||
|
@ -150,6 +157,7 @@ pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
|
|||
});
|
||||
Vim::action(editor, cx, |vim, _: &Redo, window, cx| {
|
||||
let times = Vim::take_count(cx);
|
||||
Vim::take_forced_motion(cx);
|
||||
vim.update_editor(window, cx, |_, editor, window, cx| {
|
||||
for _ in 0..times.unwrap_or(1) {
|
||||
editor.redo(&editor::actions::Redo, window, cx);
|
||||
|
@ -170,48 +178,93 @@ impl Vim {
|
|||
motion: Motion,
|
||||
operator: Option<Operator>,
|
||||
times: Option<usize>,
|
||||
forced_motion: bool,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
match operator {
|
||||
None => self.move_cursor(motion, times, window, cx),
|
||||
Some(Operator::Change) => self.change_motion(motion, times, window, cx),
|
||||
Some(Operator::Delete) => self.delete_motion(motion, times, window, cx),
|
||||
Some(Operator::Yank) => self.yank_motion(motion, times, window, cx),
|
||||
Some(Operator::Change) => self.change_motion(motion, times, forced_motion, window, cx),
|
||||
Some(Operator::Delete) => self.delete_motion(motion, times, forced_motion, window, cx),
|
||||
Some(Operator::Yank) => self.yank_motion(motion, times, forced_motion, window, cx),
|
||||
Some(Operator::AddSurrounds { target: None }) => {}
|
||||
Some(Operator::Indent) => {
|
||||
self.indent_motion(motion, times, IndentDirection::In, window, cx)
|
||||
}
|
||||
Some(Operator::Rewrap) => self.rewrap_motion(motion, times, window, cx),
|
||||
Some(Operator::Outdent) => {
|
||||
self.indent_motion(motion, times, IndentDirection::Out, window, cx)
|
||||
}
|
||||
Some(Operator::AutoIndent) => {
|
||||
self.indent_motion(motion, times, IndentDirection::Auto, window, cx)
|
||||
}
|
||||
Some(Operator::ShellCommand) => self.shell_command_motion(motion, times, window, cx),
|
||||
Some(Operator::Lowercase) => {
|
||||
self.convert_motion(motion, times, ConvertTarget::LowerCase, window, cx)
|
||||
}
|
||||
Some(Operator::Uppercase) => {
|
||||
self.convert_motion(motion, times, ConvertTarget::UpperCase, window, cx)
|
||||
}
|
||||
Some(Operator::OppositeCase) => {
|
||||
self.convert_motion(motion, times, ConvertTarget::OppositeCase, window, cx)
|
||||
}
|
||||
Some(Operator::Rot13) => {
|
||||
self.convert_motion(motion, times, ConvertTarget::Rot13, window, cx)
|
||||
}
|
||||
Some(Operator::Rot47) => {
|
||||
self.convert_motion(motion, times, ConvertTarget::Rot47, window, cx)
|
||||
Some(Operator::Indent) => self.indent_motion(
|
||||
motion,
|
||||
times,
|
||||
forced_motion,
|
||||
IndentDirection::In,
|
||||
window,
|
||||
cx,
|
||||
),
|
||||
Some(Operator::Rewrap) => self.rewrap_motion(motion, times, forced_motion, window, cx),
|
||||
Some(Operator::Outdent) => self.indent_motion(
|
||||
motion,
|
||||
times,
|
||||
forced_motion,
|
||||
IndentDirection::Out,
|
||||
window,
|
||||
cx,
|
||||
),
|
||||
Some(Operator::AutoIndent) => self.indent_motion(
|
||||
motion,
|
||||
times,
|
||||
forced_motion,
|
||||
IndentDirection::Auto,
|
||||
window,
|
||||
cx,
|
||||
),
|
||||
Some(Operator::ShellCommand) => {
|
||||
self.shell_command_motion(motion, times, forced_motion, window, cx)
|
||||
}
|
||||
Some(Operator::Lowercase) => self.convert_motion(
|
||||
motion,
|
||||
times,
|
||||
forced_motion,
|
||||
ConvertTarget::LowerCase,
|
||||
window,
|
||||
cx,
|
||||
),
|
||||
Some(Operator::Uppercase) => self.convert_motion(
|
||||
motion,
|
||||
times,
|
||||
forced_motion,
|
||||
ConvertTarget::UpperCase,
|
||||
window,
|
||||
cx,
|
||||
),
|
||||
Some(Operator::OppositeCase) => self.convert_motion(
|
||||
motion,
|
||||
times,
|
||||
forced_motion,
|
||||
ConvertTarget::OppositeCase,
|
||||
window,
|
||||
cx,
|
||||
),
|
||||
Some(Operator::Rot13) => self.convert_motion(
|
||||
motion,
|
||||
times,
|
||||
forced_motion,
|
||||
ConvertTarget::Rot13,
|
||||
window,
|
||||
cx,
|
||||
),
|
||||
Some(Operator::Rot47) => self.convert_motion(
|
||||
motion,
|
||||
times,
|
||||
forced_motion,
|
||||
ConvertTarget::Rot47,
|
||||
window,
|
||||
cx,
|
||||
),
|
||||
Some(Operator::ToggleComments) => {
|
||||
self.toggle_comments_motion(motion, times, window, cx)
|
||||
self.toggle_comments_motion(motion, times, forced_motion, window, cx)
|
||||
}
|
||||
Some(Operator::ReplaceWithRegister) => {
|
||||
self.replace_with_register_motion(motion, times, window, cx)
|
||||
self.replace_with_register_motion(motion, times, forced_motion, window, cx)
|
||||
}
|
||||
Some(Operator::Exchange) => {
|
||||
self.exchange_motion(motion, times, forced_motion, window, cx)
|
||||
}
|
||||
Some(Operator::Exchange) => self.exchange_motion(motion, times, window, cx),
|
||||
Some(operator) => {
|
||||
// Can't do anything for text objects, Ignoring
|
||||
error!("Unexpected normal mode motion operator: {:?}", operator)
|
||||
|
@ -492,6 +545,7 @@ impl Vim {
|
|||
) {
|
||||
self.record_current_action(cx);
|
||||
let mut times = Vim::take_count(cx).unwrap_or(1);
|
||||
Vim::take_forced_motion(cx);
|
||||
if self.mode.is_visual() {
|
||||
times = 1;
|
||||
} else if times > 1 {
|
||||
|
@ -513,11 +567,19 @@ impl Vim {
|
|||
|
||||
fn yank_line(&mut self, _: &YankLine, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let count = Vim::take_count(cx);
|
||||
self.yank_motion(motion::Motion::CurrentLine, count, window, cx)
|
||||
let forced_motion = Vim::take_forced_motion(cx);
|
||||
self.yank_motion(
|
||||
motion::Motion::CurrentLine,
|
||||
count,
|
||||
forced_motion,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
|
||||
fn show_location(&mut self, _: &ShowLocation, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let count = Vim::take_count(cx);
|
||||
Vim::take_forced_motion(cx);
|
||||
self.update_editor(window, cx, |vim, editor, _window, cx| {
|
||||
let selection = editor.selections.newest_anchor();
|
||||
if let Some((_, buffer, _)) = editor.active_excerpt(cx) {
|
||||
|
@ -577,6 +639,7 @@ impl Vim {
|
|||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let count = Vim::take_count(cx).unwrap_or(1);
|
||||
Vim::take_forced_motion(cx);
|
||||
self.stop_recording(cx);
|
||||
self.update_editor(window, cx, |_, editor, window, cx| {
|
||||
editor.transact(window, cx, |editor, window, cx| {
|
||||
|
|
|
@ -18,6 +18,7 @@ impl Vim {
|
|||
&mut self,
|
||||
motion: Motion,
|
||||
times: Option<usize>,
|
||||
forced_motion: bool,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
|
@ -59,6 +60,7 @@ impl Vim {
|
|||
selection,
|
||||
times,
|
||||
&text_layout_details,
|
||||
forced_motion,
|
||||
);
|
||||
if let Motion::CurrentLine = motion {
|
||||
let mut start_offset =
|
||||
|
@ -181,7 +183,7 @@ fn expand_changed_word_selection(
|
|||
} else {
|
||||
Motion::NextWordStart { ignore_punctuation }
|
||||
};
|
||||
motion.expand_selection(map, selection, times, text_layout_details)
|
||||
motion.expand_selection(map, selection, times, text_layout_details, false)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ impl Vim {
|
|||
&mut self,
|
||||
motion: Motion,
|
||||
times: Option<usize>,
|
||||
forced_motion: bool,
|
||||
mode: ConvertTarget,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
|
@ -39,7 +40,13 @@ impl Vim {
|
|||
s.move_with(|map, selection| {
|
||||
let anchor = map.display_point_to_anchor(selection.head(), Bias::Left);
|
||||
selection_starts.insert(selection.id, anchor);
|
||||
motion.expand_selection(map, selection, times, &text_layout_details);
|
||||
motion.expand_selection(
|
||||
map,
|
||||
selection,
|
||||
times,
|
||||
&text_layout_details,
|
||||
forced_motion,
|
||||
);
|
||||
});
|
||||
});
|
||||
match mode {
|
||||
|
@ -185,6 +192,7 @@ impl Vim {
|
|||
self.record_current_action(cx);
|
||||
self.store_visual_marks(window, cx);
|
||||
let count = Vim::take_count(cx).unwrap_or(1) as u32;
|
||||
Vim::take_forced_motion(cx);
|
||||
|
||||
self.update_editor(window, cx, |vim, editor, window, cx| {
|
||||
let mut ranges = Vec::new();
|
||||
|
|
|
@ -18,6 +18,7 @@ impl Vim {
|
|||
&mut self,
|
||||
motion: Motion,
|
||||
times: Option<usize>,
|
||||
forced_motion: bool,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
|
@ -33,9 +34,13 @@ impl Vim {
|
|||
s.move_with(|map, selection| {
|
||||
let original_head = selection.head();
|
||||
original_columns.insert(selection.id, original_head.column());
|
||||
let kind =
|
||||
motion.expand_selection(map, selection, times, &text_layout_details);
|
||||
|
||||
let kind = motion.expand_selection(
|
||||
map,
|
||||
selection,
|
||||
times,
|
||||
&text_layout_details,
|
||||
forced_motion,
|
||||
);
|
||||
ranges_to_copy
|
||||
.push(selection.start.to_point(map)..selection.end.to_point(map));
|
||||
|
||||
|
|
|
@ -29,12 +29,14 @@ pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
|
|||
Vim::action(editor, cx, |vim, action: &Increment, window, cx| {
|
||||
vim.record_current_action(cx);
|
||||
let count = Vim::take_count(cx).unwrap_or(1);
|
||||
Vim::take_forced_motion(cx);
|
||||
let step = if action.step { count as i32 } else { 0 };
|
||||
vim.increment(count as i64, step, window, cx)
|
||||
});
|
||||
Vim::action(editor, cx, |vim, action: &Decrement, window, cx| {
|
||||
vim.record_current_action(cx);
|
||||
let count = Vim::take_count(cx).unwrap_or(1);
|
||||
Vim::take_forced_motion(cx);
|
||||
let step = if action.step { -1 * (count as i32) } else { 0 };
|
||||
vim.increment(-(count as i64), step, window, cx)
|
||||
});
|
||||
|
|
|
@ -28,6 +28,7 @@ impl Vim {
|
|||
self.record_current_action(cx);
|
||||
self.store_visual_marks(window, cx);
|
||||
let count = Vim::take_count(cx).unwrap_or(1);
|
||||
Vim::take_forced_motion(cx);
|
||||
|
||||
self.update_editor(window, cx, |vim, editor, window, cx| {
|
||||
let text_layout_details = editor.text_layout_details(window);
|
||||
|
@ -247,6 +248,7 @@ impl Vim {
|
|||
&mut self,
|
||||
motion: Motion,
|
||||
times: Option<usize>,
|
||||
forced_motion: bool,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
|
@ -258,7 +260,13 @@ impl Vim {
|
|||
editor.set_clip_at_line_ends(false, cx);
|
||||
editor.change_selections(None, window, cx, |s| {
|
||||
s.move_with(|map, selection| {
|
||||
motion.expand_selection(map, selection, times, &text_layout_details);
|
||||
motion.expand_selection(
|
||||
map,
|
||||
selection,
|
||||
times,
|
||||
&text_layout_details,
|
||||
forced_motion,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -170,6 +170,7 @@ impl Vim {
|
|||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let mut count = Vim::take_count(cx).unwrap_or(1);
|
||||
Vim::take_forced_motion(cx);
|
||||
self.clear_operator(window, cx);
|
||||
|
||||
let globals = Vim::globals(cx);
|
||||
|
@ -201,6 +202,7 @@ impl Vim {
|
|||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let count = Vim::take_count(cx);
|
||||
Vim::take_forced_motion(cx);
|
||||
|
||||
let Some((mut actions, selection, mode)) = Vim::update_globals(cx, |globals, _| {
|
||||
let actions = globals.recorded_actions.clone();
|
||||
|
|
|
@ -55,6 +55,7 @@ impl Vim {
|
|||
by: fn(c: Option<f32>) -> ScrollAmount,
|
||||
) {
|
||||
let amount = by(Vim::take_count(cx).map(|c| c as f32));
|
||||
Vim::take_forced_motion(cx);
|
||||
self.update_editor(window, cx, |_, editor, window, cx| {
|
||||
scroll_editor(editor, move_cursor, &amount, window, cx)
|
||||
});
|
||||
|
|
|
@ -138,6 +138,7 @@ impl Vim {
|
|||
Direction::Next
|
||||
};
|
||||
let count = Vim::take_count(cx).unwrap_or(1);
|
||||
Vim::take_forced_motion(cx);
|
||||
let prior_selections = self.editor_selections(window, cx);
|
||||
pane.update(cx, |pane, cx| {
|
||||
if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() {
|
||||
|
@ -261,6 +262,7 @@ impl Vim {
|
|||
return;
|
||||
};
|
||||
let count = Vim::take_count(cx).unwrap_or(1);
|
||||
Vim::take_forced_motion(cx);
|
||||
let prior_selections = self.editor_selections(window, cx);
|
||||
|
||||
let success = pane.update(cx, |pane, cx| {
|
||||
|
@ -303,6 +305,7 @@ impl Vim {
|
|||
return;
|
||||
};
|
||||
let count = Vim::take_count(cx).unwrap_or(1);
|
||||
Vim::take_forced_motion(cx);
|
||||
let prior_selections = self.editor_selections(window, cx);
|
||||
let cursor_word = self.editor_cursor_word(window, cx);
|
||||
let vim = cx.entity().clone();
|
||||
|
|
|
@ -13,6 +13,7 @@ pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
|
|||
Vim::action(editor, cx, |vim, _: &Substitute, window, cx| {
|
||||
vim.start_recording(cx);
|
||||
let count = Vim::take_count(cx);
|
||||
Vim::take_forced_motion(cx);
|
||||
vim.substitute(count, vim.mode == Mode::VisualLine, window, cx);
|
||||
});
|
||||
|
||||
|
@ -22,6 +23,7 @@ pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
|
|||
vim.switch_mode(Mode::VisualLine, false, window, cx)
|
||||
}
|
||||
let count = Vim::take_count(cx);
|
||||
Vim::take_forced_motion(cx);
|
||||
vim.substitute(count, true, window, cx)
|
||||
});
|
||||
}
|
||||
|
@ -47,6 +49,7 @@ impl Vim {
|
|||
selection,
|
||||
count,
|
||||
&text_layout_details,
|
||||
false,
|
||||
);
|
||||
}
|
||||
if line_mode {
|
||||
|
@ -60,6 +63,7 @@ impl Vim {
|
|||
selection,
|
||||
None,
|
||||
&text_layout_details,
|
||||
false,
|
||||
);
|
||||
if let Some((point, _)) = (Motion::FirstNonWhitespace {
|
||||
display_lines: false,
|
||||
|
|
|
@ -9,6 +9,7 @@ impl Vim {
|
|||
&mut self,
|
||||
motion: Motion,
|
||||
times: Option<usize>,
|
||||
forced_motion: bool,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
|
@ -21,7 +22,13 @@ impl Vim {
|
|||
s.move_with(|map, selection| {
|
||||
let anchor = map.display_point_to_anchor(selection.head(), Bias::Right);
|
||||
selection_starts.insert(selection.id, anchor);
|
||||
motion.expand_selection(map, selection, times, &text_layout_details);
|
||||
motion.expand_selection(
|
||||
map,
|
||||
selection,
|
||||
times,
|
||||
&text_layout_details,
|
||||
forced_motion,
|
||||
);
|
||||
});
|
||||
});
|
||||
editor.toggle_comments(&Default::default(), window, cx);
|
||||
|
|
|
@ -21,6 +21,7 @@ impl Vim {
|
|||
&mut self,
|
||||
motion: Motion,
|
||||
times: Option<usize>,
|
||||
forced_motion: bool,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
|
@ -33,8 +34,19 @@ impl Vim {
|
|||
editor.change_selections(None, window, cx, |s| {
|
||||
s.move_with(|map, selection| {
|
||||
let original_position = (selection.head(), selection.goal);
|
||||
original_positions.insert(selection.id, original_position);
|
||||
kind = motion.expand_selection(map, selection, times, &text_layout_details);
|
||||
kind = motion.expand_selection(
|
||||
map,
|
||||
selection,
|
||||
times,
|
||||
&text_layout_details,
|
||||
forced_motion,
|
||||
);
|
||||
if kind == Some(MotionKind::Exclusive) {
|
||||
original_positions
|
||||
.insert(selection.id, (selection.start, selection.goal));
|
||||
} else {
|
||||
original_positions.insert(selection.id, original_position);
|
||||
}
|
||||
})
|
||||
});
|
||||
let Some(kind) = kind else { return };
|
||||
|
|
|
@ -27,6 +27,7 @@ pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
|
|||
return;
|
||||
}
|
||||
let count = Vim::take_count(cx);
|
||||
Vim::take_forced_motion(cx);
|
||||
vim.undo_replace(count, window, cx)
|
||||
});
|
||||
}
|
||||
|
@ -179,6 +180,7 @@ impl Vim {
|
|||
&mut self,
|
||||
motion: Motion,
|
||||
times: Option<usize>,
|
||||
forced_motion: bool,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
|
@ -188,7 +190,13 @@ impl Vim {
|
|||
let text_layout_details = editor.text_layout_details(window);
|
||||
let mut selection = editor.selections.newest_display(cx);
|
||||
let snapshot = editor.snapshot(window, cx);
|
||||
motion.expand_selection(&snapshot, &mut selection, times, &text_layout_details);
|
||||
motion.expand_selection(
|
||||
&snapshot,
|
||||
&mut selection,
|
||||
times,
|
||||
&text_layout_details,
|
||||
forced_motion,
|
||||
);
|
||||
let start = snapshot
|
||||
.buffer_snapshot
|
||||
.anchor_before(selection.start.to_point(&snapshot));
|
||||
|
|
|
@ -10,6 +10,7 @@ pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
|
|||
Vim::action(editor, cx, |vim, _: &Rewrap, window, cx| {
|
||||
vim.record_current_action(cx);
|
||||
Vim::take_count(cx);
|
||||
Vim::take_forced_motion(cx);
|
||||
vim.store_visual_marks(window, cx);
|
||||
vim.update_editor(window, cx, |vim, editor, window, cx| {
|
||||
editor.transact(window, cx, |editor, window, cx| {
|
||||
|
@ -43,6 +44,7 @@ impl Vim {
|
|||
&mut self,
|
||||
motion: Motion,
|
||||
times: Option<usize>,
|
||||
forced_motion: bool,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
|
@ -55,7 +57,13 @@ impl Vim {
|
|||
s.move_with(|map, selection| {
|
||||
let anchor = map.display_point_to_anchor(selection.head(), Bias::Right);
|
||||
selection_starts.insert(selection.id, anchor);
|
||||
motion.expand_selection(map, selection, times, &text_layout_details);
|
||||
motion.expand_selection(
|
||||
map,
|
||||
selection,
|
||||
times,
|
||||
&text_layout_details,
|
||||
forced_motion,
|
||||
);
|
||||
});
|
||||
});
|
||||
editor.rewrap_impl(
|
||||
|
|
|
@ -202,7 +202,7 @@ pub struct VimGlobals {
|
|||
pub pre_count: Option<usize>,
|
||||
/// post_count is the number after an operator is specified (2 in 3d2d)
|
||||
pub post_count: Option<usize>,
|
||||
|
||||
pub forced_motion: bool,
|
||||
pub stop_recording_after_next_action: bool,
|
||||
pub ignore_current_insertion: bool,
|
||||
pub recorded_count: Option<usize>,
|
||||
|
|
|
@ -27,6 +27,7 @@ impl Vim {
|
|||
) {
|
||||
self.stop_recording(cx);
|
||||
let count = Vim::take_count(cx);
|
||||
let forced_motion = Vim::take_forced_motion(cx);
|
||||
let mode = self.mode;
|
||||
self.update_editor(window, cx, |_, editor, window, cx| {
|
||||
let text_layout_details = editor.text_layout_details(window);
|
||||
|
@ -55,7 +56,13 @@ impl Vim {
|
|||
}
|
||||
SurroundsType::Motion(motion) => {
|
||||
motion
|
||||
.range(&display_map, selection.clone(), count, &text_layout_details)
|
||||
.range(
|
||||
&display_map,
|
||||
selection.clone(),
|
||||
count,
|
||||
&text_layout_details,
|
||||
forced_motion,
|
||||
)
|
||||
.map(|(mut range, _)| {
|
||||
// The Motion::CurrentLine operation will contain the newline of the current line and leading/trailing whitespace
|
||||
if let Motion::CurrentLine = motion {
|
||||
|
|
|
@ -13,7 +13,7 @@ use super::{VimTestContext, neovim_connection::NeovimConnection};
|
|||
use crate::state::{Mode, VimGlobals};
|
||||
|
||||
pub struct NeovimBackedTestContext {
|
||||
cx: VimTestContext,
|
||||
pub(crate) cx: VimTestContext,
|
||||
pub(crate) neovim: NeovimConnection,
|
||||
|
||||
last_set_state: Option<String>,
|
||||
|
|
|
@ -142,6 +142,10 @@ impl VimTestContext {
|
|||
self.update_editor(|editor, _, cx| editor.addon::<VimAddon>().unwrap().entity.read(cx).mode)
|
||||
}
|
||||
|
||||
pub fn forced_motion(&mut self) -> bool {
|
||||
self.update_editor(|_, _, cx| cx.global::<VimGlobals>().forced_motion)
|
||||
}
|
||||
|
||||
pub fn active_operator(&mut self) -> Option<Operator> {
|
||||
self.update_editor(|editor, _, cx| {
|
||||
editor
|
||||
|
|
|
@ -145,6 +145,7 @@ actions!(
|
|||
PushDeleteSurrounds,
|
||||
PushMark,
|
||||
ToggleMarksView,
|
||||
PushForcedMotion,
|
||||
PushIndent,
|
||||
PushOutdent,
|
||||
PushAutoIndent,
|
||||
|
@ -233,6 +234,7 @@ pub fn init(cx: &mut App) {
|
|||
|
||||
workspace.register_action(|workspace, _: &ResizePaneRight, window, cx| {
|
||||
let count = Vim::take_count(cx).unwrap_or(1) as f32;
|
||||
Vim::take_forced_motion(cx);
|
||||
let theme = ThemeSettings::get_global(cx);
|
||||
let Ok(font_id) = window.text_system().font_id(&theme.buffer_font) else {
|
||||
return;
|
||||
|
@ -248,6 +250,7 @@ pub fn init(cx: &mut App) {
|
|||
|
||||
workspace.register_action(|workspace, _: &ResizePaneLeft, window, cx| {
|
||||
let count = Vim::take_count(cx).unwrap_or(1) as f32;
|
||||
Vim::take_forced_motion(cx);
|
||||
let theme = ThemeSettings::get_global(cx);
|
||||
let Ok(font_id) = window.text_system().font_id(&theme.buffer_font) else {
|
||||
return;
|
||||
|
@ -263,6 +266,7 @@ pub fn init(cx: &mut App) {
|
|||
|
||||
workspace.register_action(|workspace, _: &ResizePaneUp, window, cx| {
|
||||
let count = Vim::take_count(cx).unwrap_or(1) as f32;
|
||||
Vim::take_forced_motion(cx);
|
||||
let theme = ThemeSettings::get_global(cx);
|
||||
let height = theme.buffer_font_size(cx) * theme.buffer_line_height.value();
|
||||
workspace.resize_pane(Axis::Vertical, height * count, window, cx);
|
||||
|
@ -270,6 +274,7 @@ pub fn init(cx: &mut App) {
|
|||
|
||||
workspace.register_action(|workspace, _: &ResizePaneDown, window, cx| {
|
||||
let count = Vim::take_count(cx).unwrap_or(1) as f32;
|
||||
Vim::take_forced_motion(cx);
|
||||
let theme = ThemeSettings::get_global(cx);
|
||||
let height = theme.buffer_font_size(cx) * theme.buffer_line_height.value();
|
||||
workspace.resize_pane(Axis::Vertical, -height * count, window, cx);
|
||||
|
@ -472,7 +477,9 @@ impl Vim {
|
|||
vim.switch_mode(Mode::HelixNormal, false, window, cx)
|
||||
},
|
||||
);
|
||||
|
||||
Vim::action(editor, cx, |_, _: &PushForcedMotion, _, cx| {
|
||||
Vim::globals(cx).forced_motion = true;
|
||||
});
|
||||
Vim::action(editor, cx, |vim, action: &PushObject, window, cx| {
|
||||
vim.push_operator(
|
||||
Operator::Object {
|
||||
|
@ -907,6 +914,7 @@ impl Vim {
|
|||
self.current_tx.take();
|
||||
self.current_anchor.take();
|
||||
}
|
||||
Vim::take_forced_motion(cx);
|
||||
if mode != Mode::Insert && mode != Mode::Replace {
|
||||
Vim::take_count(cx);
|
||||
}
|
||||
|
@ -1011,6 +1019,13 @@ impl Vim {
|
|||
count
|
||||
}
|
||||
|
||||
pub fn take_forced_motion(cx: &mut App) -> bool {
|
||||
let global_state = cx.global_mut::<VimGlobals>();
|
||||
let forced_motion = global_state.forced_motion;
|
||||
global_state.forced_motion = false;
|
||||
forced_motion
|
||||
}
|
||||
|
||||
pub fn cursor_shape(&self, cx: &mut App) -> CursorShape {
|
||||
match self.mode {
|
||||
Mode::Normal => {
|
||||
|
@ -1372,6 +1387,7 @@ impl Vim {
|
|||
|
||||
fn clear_operator(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
Vim::take_count(cx);
|
||||
Vim::take_forced_motion(cx);
|
||||
self.selected_register.take();
|
||||
self.operator_stack.clear();
|
||||
self.sync_vim_settings(window, cx);
|
||||
|
|
|
@ -85,6 +85,7 @@ pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
|
|||
|
||||
Vim::action(editor, cx, |vim, _: &SelectLargerSyntaxNode, window, cx| {
|
||||
let count = Vim::take_count(cx).unwrap_or(1);
|
||||
Vim::take_forced_motion(cx);
|
||||
for _ in 0..count {
|
||||
vim.update_editor(window, cx, |_, editor, window, cx| {
|
||||
editor.select_larger_syntax_node(&Default::default(), window, cx);
|
||||
|
@ -97,6 +98,7 @@ pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
|
|||
cx,
|
||||
|vim, _: &SelectSmallerSyntaxNode, window, cx| {
|
||||
let count = Vim::take_count(cx).unwrap_or(1);
|
||||
Vim::take_forced_motion(cx);
|
||||
for _ in 0..count {
|
||||
vim.update_editor(window, cx, |_, editor, window, cx| {
|
||||
editor.select_smaller_syntax_node(&Default::default(), window, cx);
|
||||
|
@ -682,6 +684,7 @@ impl Vim {
|
|||
}
|
||||
|
||||
pub fn select_next(&mut self, _: &SelectNext, window: &mut Window, cx: &mut Context<Self>) {
|
||||
Vim::take_forced_motion(cx);
|
||||
let count =
|
||||
Vim::take_count(cx).unwrap_or_else(|| if self.mode.is_visual() { 1 } else { 2 });
|
||||
self.update_editor(window, cx, |_, editor, window, cx| {
|
||||
|
@ -704,6 +707,7 @@ impl Vim {
|
|||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
Vim::take_forced_motion(cx);
|
||||
let count =
|
||||
Vim::take_count(cx).unwrap_or_else(|| if self.mode.is_visual() { 1 } else { 2 });
|
||||
self.update_editor(window, cx, |_, editor, window, cx| {
|
||||
|
@ -725,6 +729,7 @@ impl Vim {
|
|||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
Vim::take_forced_motion(cx);
|
||||
let count = Vim::take_count(cx).unwrap_or(1);
|
||||
let Some(pane) = self.pane(window, cx) else {
|
||||
return;
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
{"Put":{"state":"the quick brown foˇx\njumped over the lazy dog"}}
|
||||
{"Key":"d"}
|
||||
{"Key":"v"}
|
||||
{"Key":"$"}
|
||||
{"Get":{"state":"the quick brown foˇx\njumped over the lazy dog","mode":"Normal"}}
|
||||
{"Put":{"state":"ˇthe quick brown fox\njumped over the lazy dog"}}
|
||||
{"Key":"d"}
|
||||
{"Key":"v"}
|
||||
{"Key":"$"}
|
||||
{"Get":{"state":"ˇx\njumped over the lazy dog","mode":"Normal"}}
|
|
@ -0,0 +1,15 @@
|
|||
{"Put":{"state":"ˇthe quick brown fox\njumped over the lazy dog"}}
|
||||
{"Key":"d"}
|
||||
{"Key":"v"}
|
||||
{"Key":"0"}
|
||||
{"Get":{"state":"ˇhe quick brown fox\njumped over the lazy dog","mode":"Normal"}}
|
||||
{"Put":{"state":"the quick bˇrown fox\njumped over the lazy dog"}}
|
||||
{"Key":"d"}
|
||||
{"Key":"v"}
|
||||
{"Key":"0"}
|
||||
{"Get":{"state":"ˇown fox\njumped over the lazy dog","mode":"Normal"}}
|
||||
{"Put":{"state":"the quick brown foˇx\njumped over the lazy dog"}}
|
||||
{"Key":"d"}
|
||||
{"Key":"v"}
|
||||
{"Key":"0"}
|
||||
{"Get":{"state":"ˇ\njumped over the lazy dog","mode":"Normal"}}
|
24
crates/vim/test_data/test_forced_motion_yank.json
Normal file
24
crates/vim/test_data/test_forced_motion_yank.json
Normal file
|
@ -0,0 +1,24 @@
|
|||
{"Put":{"state":"ˇthe quick brown fox\njumped over the lazy dog"}}
|
||||
{"Key":"y"}
|
||||
{"Key":"v"}
|
||||
{"Key":"j"}
|
||||
{"Key":"p"}
|
||||
{"Get":{"state":"the quick brown fox\nˇthe quick brown fox\njumped over the lazy dog","mode":"Normal"}}
|
||||
{"Put":{"state":"the quick bˇrown fox\njumped over the lazy dog"}}
|
||||
{"Key":"y"}
|
||||
{"Key":"v"}
|
||||
{"Key":"j"}
|
||||
{"Key":"p"}
|
||||
{"Get":{"state":"the quick brˇrown fox\njumped overown fox\njumped over the lazy dog","mode":"Normal"}}
|
||||
{"Put":{"state":"the quick brown foˇx\njumped over the lazy dog"}}
|
||||
{"Key":"y"}
|
||||
{"Key":"v"}
|
||||
{"Key":"j"}
|
||||
{"Key":"p"}
|
||||
{"Get":{"state":"the quick brown foxˇx\njumped over the la\njumped over the lazy dog","mode":"Normal"}}
|
||||
{"Put":{"state":"the quick brown fox\njˇumped over the lazy dog"}}
|
||||
{"Key":"y"}
|
||||
{"Key":"v"}
|
||||
{"Key":"k"}
|
||||
{"Key":"p"}
|
||||
{"Get":{"state":"thˇhe quick brown fox\nje quick brown fox\njumped over the lazy dog","mode":"Normal"}}
|
15
crates/vim/test_data/test_inclusive_to_exclusive_delete.json
Normal file
15
crates/vim/test_data/test_inclusive_to_exclusive_delete.json
Normal file
|
@ -0,0 +1,15 @@
|
|||
{"Put":{"state":"ˇthe quick brown fox\njumped over the lazy dog"}}
|
||||
{"Key":"d"}
|
||||
{"Key":"v"}
|
||||
{"Key":"e"}
|
||||
{"Get":{"state":"ˇe quick brown fox\njumped over the lazy dog","mode":"Normal"}}
|
||||
{"Put":{"state":"the quick bˇrown fox\njumped over the lazy dog"}}
|
||||
{"Key":"d"}
|
||||
{"Key":"v"}
|
||||
{"Key":"e"}
|
||||
{"Get":{"state":"the quick bˇn fox\njumped over the lazy dog","mode":"Normal"}}
|
||||
{"Put":{"state":"the quick brown foˇx\njumped over the lazy dog"}}
|
||||
{"Key":"d"}
|
||||
{"Key":"v"}
|
||||
{"Key":"e"}
|
||||
{"Get":{"state":"the quick brown foˇd over the lazy dog","mode":"Normal"}}
|
Loading…
Add table
Add a link
Reference in a new issue