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
|
@ -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| {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue