vim: Implement <count>%
motion (#25839)
Closes https://github.com/zed-industries/zed/discussions/25665 > Currently Zed is missing quite an useful Vim motion: <count>% (go to {count} percentage in the file). Description: {count}% - Go to {count} percentage in the file, on the first non-blank in the line linewise. To compute the new line number this formula is used: ({count} * number-of-lines + 99) / 100 . > [Link](https://neovim.io/doc/user/motion.html#N%25). Release Notes: - vim: Added `<count>%` motion --------- Co-authored-by: Conrad Irwin <conrad@zed.dev>
This commit is contained in:
parent
314ad5dd5f
commit
22b1a02e23
3 changed files with 152 additions and 1 deletions
|
@ -247,7 +247,8 @@
|
||||||
"context": "VimControl && VimCount",
|
"context": "VimControl && VimCount",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"0": ["vim::Number", 0],
|
"0": ["vim::Number", 0],
|
||||||
":": "vim::CountCommand"
|
":": "vim::CountCommand",
|
||||||
|
"%": "vim::GoToPercentage"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -74,6 +74,7 @@ pub enum Motion {
|
||||||
StartOfDocument,
|
StartOfDocument,
|
||||||
EndOfDocument,
|
EndOfDocument,
|
||||||
Matching,
|
Matching,
|
||||||
|
GoToPercentage,
|
||||||
UnmatchedForward {
|
UnmatchedForward {
|
||||||
char: char,
|
char: char,
|
||||||
},
|
},
|
||||||
|
@ -281,6 +282,7 @@ actions!(
|
||||||
StartOfDocument,
|
StartOfDocument,
|
||||||
EndOfDocument,
|
EndOfDocument,
|
||||||
Matching,
|
Matching,
|
||||||
|
GoToPercentage,
|
||||||
NextLineStart,
|
NextLineStart,
|
||||||
PreviousLineStart,
|
PreviousLineStart,
|
||||||
StartOfLineDownward,
|
StartOfLineDownward,
|
||||||
|
@ -402,6 +404,9 @@ pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
|
||||||
Vim::action(editor, cx, |vim, _: &Matching, window, cx| {
|
Vim::action(editor, cx, |vim, _: &Matching, window, cx| {
|
||||||
vim.motion(Motion::Matching, window, cx)
|
vim.motion(Motion::Matching, window, cx)
|
||||||
});
|
});
|
||||||
|
Vim::action(editor, cx, |vim, _: &GoToPercentage, window, cx| {
|
||||||
|
vim.motion(Motion::GoToPercentage, window, cx)
|
||||||
|
});
|
||||||
Vim::action(
|
Vim::action(
|
||||||
editor,
|
editor,
|
||||||
cx,
|
cx,
|
||||||
|
@ -643,6 +648,7 @@ impl Motion {
|
||||||
| PreviousMethodEnd
|
| PreviousMethodEnd
|
||||||
| NextComment
|
| NextComment
|
||||||
| PreviousComment
|
| PreviousComment
|
||||||
|
| GoToPercentage
|
||||||
| Jump { line: true, .. } => true,
|
| Jump { line: true, .. } => true,
|
||||||
EndOfLine { .. }
|
EndOfLine { .. }
|
||||||
| Matching
|
| Matching
|
||||||
|
@ -701,6 +707,7 @@ impl Motion {
|
||||||
| StartOfLineDownward
|
| StartOfLineDownward
|
||||||
| EndOfLineDownward
|
| EndOfLineDownward
|
||||||
| GoToColumn
|
| GoToColumn
|
||||||
|
| GoToPercentage
|
||||||
| NextWordStart { .. }
|
| NextWordStart { .. }
|
||||||
| NextWordEnd { .. }
|
| NextWordEnd { .. }
|
||||||
| PreviousWordStart { .. }
|
| PreviousWordStart { .. }
|
||||||
|
@ -745,6 +752,7 @@ impl Motion {
|
||||||
| EndOfLine { .. }
|
| EndOfLine { .. }
|
||||||
| EndOfLineDownward
|
| EndOfLineDownward
|
||||||
| Matching
|
| Matching
|
||||||
|
| GoToPercentage
|
||||||
| UnmatchedForward { .. }
|
| UnmatchedForward { .. }
|
||||||
| UnmatchedBackward { .. }
|
| UnmatchedBackward { .. }
|
||||||
| FindForward { .. }
|
| FindForward { .. }
|
||||||
|
@ -886,6 +894,7 @@ impl Motion {
|
||||||
SelectionGoal::None,
|
SelectionGoal::None,
|
||||||
),
|
),
|
||||||
Matching => (matching(map, point), SelectionGoal::None),
|
Matching => (matching(map, point), SelectionGoal::None),
|
||||||
|
GoToPercentage => (go_to_percentage(map, point, times), SelectionGoal::None),
|
||||||
UnmatchedForward { char } => (
|
UnmatchedForward { char } => (
|
||||||
unmatched_forward(map, point, *char, times),
|
unmatched_forward(map, point, *char, times),
|
||||||
SelectionGoal::None,
|
SelectionGoal::None,
|
||||||
|
@ -2194,6 +2203,22 @@ fn matching(map: &DisplaySnapshot, display_point: DisplayPoint) -> DisplayPoint
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Go to {count} percentage in the file, on the first
|
||||||
|
// non-blank in the line linewise. To compute the new
|
||||||
|
// line number this formula is used:
|
||||||
|
// ({count} * number-of-lines + 99) / 100
|
||||||
|
//
|
||||||
|
// https://neovim.io/doc/user/motion.html#N%25
|
||||||
|
fn go_to_percentage(map: &DisplaySnapshot, point: DisplayPoint, count: usize) -> DisplayPoint {
|
||||||
|
let total_lines = map.buffer_snapshot.max_point().row + 1;
|
||||||
|
let target_line = (count * total_lines as usize + 99) / 100;
|
||||||
|
let target_point = DisplayPoint::new(
|
||||||
|
DisplayRow(target_line.saturating_sub(1) as u32),
|
||||||
|
point.column(),
|
||||||
|
);
|
||||||
|
map.clip_point(target_point, Bias::Left)
|
||||||
|
}
|
||||||
|
|
||||||
fn unmatched_forward(
|
fn unmatched_forward(
|
||||||
map: &DisplaySnapshot,
|
map: &DisplaySnapshot,
|
||||||
mut display_point: DisplayPoint,
|
mut display_point: DisplayPoint,
|
||||||
|
@ -3470,4 +3495,103 @@ mod test {
|
||||||
Mode::Normal,
|
Mode::Normal,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_go_to_percentage(cx: &mut gpui::TestAppContext) {
|
||||||
|
let mut cx = NeovimBackedTestContext::new(cx).await;
|
||||||
|
// Normal mode
|
||||||
|
cx.set_shared_state(indoc! {"
|
||||||
|
The ˇquick brown
|
||||||
|
fox jumps over
|
||||||
|
the lazy dog
|
||||||
|
The quick brown
|
||||||
|
fox jumps over
|
||||||
|
the lazy dog
|
||||||
|
The quick brown
|
||||||
|
fox jumps over
|
||||||
|
the lazy dog"})
|
||||||
|
.await;
|
||||||
|
cx.simulate_shared_keystrokes("2 0 %").await;
|
||||||
|
cx.shared_state().await.assert_eq(indoc! {"
|
||||||
|
The quick brown
|
||||||
|
fox ˇjumps over
|
||||||
|
the lazy dog
|
||||||
|
The quick brown
|
||||||
|
fox jumps over
|
||||||
|
the lazy dog
|
||||||
|
The quick brown
|
||||||
|
fox jumps over
|
||||||
|
the lazy dog"});
|
||||||
|
|
||||||
|
cx.simulate_shared_keystrokes("2 5 %").await;
|
||||||
|
cx.shared_state().await.assert_eq(indoc! {"
|
||||||
|
The quick brown
|
||||||
|
fox jumps over
|
||||||
|
the ˇlazy dog
|
||||||
|
The quick brown
|
||||||
|
fox jumps over
|
||||||
|
the lazy dog
|
||||||
|
The quick brown
|
||||||
|
fox jumps over
|
||||||
|
the lazy dog"});
|
||||||
|
|
||||||
|
cx.simulate_shared_keystrokes("7 5 %").await;
|
||||||
|
cx.shared_state().await.assert_eq(indoc! {"
|
||||||
|
The quick brown
|
||||||
|
fox jumps over
|
||||||
|
the lazy dog
|
||||||
|
The quick brown
|
||||||
|
fox jumps over
|
||||||
|
the lazy dog
|
||||||
|
The ˇquick brown
|
||||||
|
fox jumps over
|
||||||
|
the lazy dog"});
|
||||||
|
|
||||||
|
// Visual mode
|
||||||
|
cx.set_shared_state(indoc! {"
|
||||||
|
The ˇquick brown
|
||||||
|
fox jumps over
|
||||||
|
the lazy dog
|
||||||
|
The quick brown
|
||||||
|
fox jumps over
|
||||||
|
the lazy dog
|
||||||
|
The quick brown
|
||||||
|
fox jumps over
|
||||||
|
the lazy dog"})
|
||||||
|
.await;
|
||||||
|
cx.simulate_shared_keystrokes("v 5 0 %").await;
|
||||||
|
cx.shared_state().await.assert_eq(indoc! {"
|
||||||
|
The «quick brown
|
||||||
|
fox jumps over
|
||||||
|
the lazy dog
|
||||||
|
The quick brown
|
||||||
|
fox jˇ»umps over
|
||||||
|
the lazy dog
|
||||||
|
The quick brown
|
||||||
|
fox jumps over
|
||||||
|
the lazy dog"});
|
||||||
|
|
||||||
|
cx.set_shared_state(indoc! {"
|
||||||
|
The ˇquick brown
|
||||||
|
fox jumps over
|
||||||
|
the lazy dog
|
||||||
|
The quick brown
|
||||||
|
fox jumps over
|
||||||
|
the lazy dog
|
||||||
|
The quick brown
|
||||||
|
fox jumps over
|
||||||
|
the lazy dog"})
|
||||||
|
.await;
|
||||||
|
cx.simulate_shared_keystrokes("v 1 0 0 %").await;
|
||||||
|
cx.shared_state().await.assert_eq(indoc! {"
|
||||||
|
The «quick brown
|
||||||
|
fox jumps over
|
||||||
|
the lazy dog
|
||||||
|
The quick brown
|
||||||
|
fox jumps over
|
||||||
|
the lazy dog
|
||||||
|
The quick brown
|
||||||
|
fox jumps over
|
||||||
|
the lˇ»azy dog"});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
26
crates/vim/test_data/test_go_to_percentage.json
Normal file
26
crates/vim/test_data/test_go_to_percentage.json
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{"Put":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog\nThe quick brown\nfox jumps over\nthe lazy dog\nThe quick brown\nfox jumps over\nthe lazy dog"}}
|
||||||
|
{"Key":"2"}
|
||||||
|
{"Key":"0"}
|
||||||
|
{"Key":"%"}
|
||||||
|
{"Get":{"state":"The quick brown\nfox ˇjumps over\nthe lazy dog\nThe quick brown\nfox jumps over\nthe lazy dog\nThe quick brown\nfox jumps over\nthe lazy dog","mode":"Normal"}}
|
||||||
|
{"Key":"2"}
|
||||||
|
{"Key":"5"}
|
||||||
|
{"Key":"%"}
|
||||||
|
{"Get":{"state":"The quick brown\nfox jumps over\nthe ˇlazy dog\nThe quick brown\nfox jumps over\nthe lazy dog\nThe quick brown\nfox jumps over\nthe lazy dog","mode":"Normal"}}
|
||||||
|
{"Key":"7"}
|
||||||
|
{"Key":"5"}
|
||||||
|
{"Key":"%"}
|
||||||
|
{"Get":{"state":"The quick brown\nfox jumps over\nthe lazy dog\nThe quick brown\nfox jumps over\nthe lazy dog\nThe ˇquick brown\nfox jumps over\nthe lazy dog","mode":"Normal"}}
|
||||||
|
{"Put":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog\nThe quick brown\nfox jumps over\nthe lazy dog\nThe quick brown\nfox jumps over\nthe lazy dog"}}
|
||||||
|
{"Key":"v"}
|
||||||
|
{"Key":"5"}
|
||||||
|
{"Key":"0"}
|
||||||
|
{"Key":"%"}
|
||||||
|
{"Get":{"state":"The «quick brown\nfox jumps over\nthe lazy dog\nThe quick brown\nfox jˇ»umps over\nthe lazy dog\nThe quick brown\nfox jumps over\nthe lazy dog","mode":"Visual"}}
|
||||||
|
{"Put":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog\nThe quick brown\nfox jumps over\nthe lazy dog\nThe quick brown\nfox jumps over\nthe lazy dog"}}
|
||||||
|
{"Key":"v"}
|
||||||
|
{"Key":"1"}
|
||||||
|
{"Key":"0"}
|
||||||
|
{"Key":"0"}
|
||||||
|
{"Key":"%"}
|
||||||
|
{"Get":{"state":"The «quick brown\nfox jumps over\nthe lazy dog\nThe quick brown\nfox jumps over\nthe lazy dog\nThe quick brown\nfox jumps over\nthe lˇ»azy dog","mode":"Visual"}}
|
Loading…
Add table
Add a link
Reference in a new issue