vim: Add g M
motion to go to the middle of a line (#30227)
Adds the "g M" vim motion to go to the middle of the line. --------- Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
This commit is contained in:
parent
c7725e31d9
commit
d791c6cdb1
3 changed files with 146 additions and 0 deletions
|
@ -152,6 +152,7 @@
|
||||||
"g end": ["vim::EndOfLine", { "display_lines": true }],
|
"g end": ["vim::EndOfLine", { "display_lines": true }],
|
||||||
"g 0": ["vim::StartOfLine", { "display_lines": true }],
|
"g 0": ["vim::StartOfLine", { "display_lines": true }],
|
||||||
"g home": ["vim::StartOfLine", { "display_lines": true }],
|
"g home": ["vim::StartOfLine", { "display_lines": true }],
|
||||||
|
"g shift-m": ["vim::MiddleOfLine", { "display_lines": true }],
|
||||||
"g ^": ["vim::FirstNonWhitespace", { "display_lines": true }],
|
"g ^": ["vim::FirstNonWhitespace", { "display_lines": true }],
|
||||||
"g v": "vim::RestoreVisualSelection",
|
"g v": "vim::RestoreVisualSelection",
|
||||||
"g ]": "editor::GoToDiagnostic",
|
"g ]": "editor::GoToDiagnostic",
|
||||||
|
|
|
@ -84,6 +84,9 @@ pub enum Motion {
|
||||||
StartOfLine {
|
StartOfLine {
|
||||||
display_lines: bool,
|
display_lines: bool,
|
||||||
},
|
},
|
||||||
|
MiddleOfLine {
|
||||||
|
display_lines: bool,
|
||||||
|
},
|
||||||
EndOfLine {
|
EndOfLine {
|
||||||
display_lines: bool,
|
display_lines: bool,
|
||||||
},
|
},
|
||||||
|
@ -265,6 +268,13 @@ pub struct StartOfLine {
|
||||||
pub(crate) display_lines: bool,
|
pub(crate) display_lines: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
struct MiddleOfLine {
|
||||||
|
#[serde(default)]
|
||||||
|
display_lines: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
|
#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
struct UnmatchedForward {
|
struct UnmatchedForward {
|
||||||
|
@ -283,6 +293,7 @@ impl_actions!(
|
||||||
vim,
|
vim,
|
||||||
[
|
[
|
||||||
StartOfLine,
|
StartOfLine,
|
||||||
|
MiddleOfLine,
|
||||||
EndOfLine,
|
EndOfLine,
|
||||||
FirstNonWhitespace,
|
FirstNonWhitespace,
|
||||||
Down,
|
Down,
|
||||||
|
@ -409,6 +420,15 @@ pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
Vim::action(editor, cx, |vim, action: &MiddleOfLine, window, cx| {
|
||||||
|
vim.motion(
|
||||||
|
Motion::MiddleOfLine {
|
||||||
|
display_lines: action.display_lines,
|
||||||
|
},
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
});
|
||||||
Vim::action(editor, cx, |vim, action: &EndOfLine, window, cx| {
|
Vim::action(editor, cx, |vim, action: &EndOfLine, window, cx| {
|
||||||
vim.motion(
|
vim.motion(
|
||||||
Motion::EndOfLine {
|
Motion::EndOfLine {
|
||||||
|
@ -737,6 +757,7 @@ impl Motion {
|
||||||
| SentenceBackward
|
| SentenceBackward
|
||||||
| SentenceForward
|
| SentenceForward
|
||||||
| GoToColumn
|
| GoToColumn
|
||||||
|
| MiddleOfLine { .. }
|
||||||
| UnmatchedForward { .. }
|
| UnmatchedForward { .. }
|
||||||
| UnmatchedBackward { .. }
|
| UnmatchedBackward { .. }
|
||||||
| NextWordStart { .. }
|
| NextWordStart { .. }
|
||||||
|
@ -769,6 +790,7 @@ impl Motion {
|
||||||
Down { .. }
|
Down { .. }
|
||||||
| Up { .. }
|
| Up { .. }
|
||||||
| EndOfLine { .. }
|
| EndOfLine { .. }
|
||||||
|
| MiddleOfLine { .. }
|
||||||
| Matching
|
| Matching
|
||||||
| UnmatchedForward { .. }
|
| UnmatchedForward { .. }
|
||||||
| UnmatchedBackward { .. }
|
| UnmatchedBackward { .. }
|
||||||
|
@ -894,6 +916,10 @@ impl Motion {
|
||||||
start_of_line(map, *display_lines, point),
|
start_of_line(map, *display_lines, point),
|
||||||
SelectionGoal::None,
|
SelectionGoal::None,
|
||||||
),
|
),
|
||||||
|
MiddleOfLine { display_lines } => (
|
||||||
|
middle_of_line(map, *display_lines, point, maybe_times),
|
||||||
|
SelectionGoal::None,
|
||||||
|
),
|
||||||
EndOfLine { display_lines } => (
|
EndOfLine { display_lines } => (
|
||||||
end_of_line(map, *display_lines, point, times),
|
end_of_line(map, *display_lines, point, times),
|
||||||
SelectionGoal::None,
|
SelectionGoal::None,
|
||||||
|
@ -1944,6 +1970,36 @@ pub(crate) fn start_of_line(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn middle_of_line(
|
||||||
|
map: &DisplaySnapshot,
|
||||||
|
display_lines: bool,
|
||||||
|
point: DisplayPoint,
|
||||||
|
times: Option<usize>,
|
||||||
|
) -> DisplayPoint {
|
||||||
|
let percent = if let Some(times) = times.filter(|&t| t <= 100) {
|
||||||
|
times as f64 / 100.
|
||||||
|
} else {
|
||||||
|
0.5
|
||||||
|
};
|
||||||
|
if display_lines {
|
||||||
|
map.clip_point(
|
||||||
|
DisplayPoint::new(
|
||||||
|
point.row(),
|
||||||
|
(map.line_len(point.row()) as f64 * percent) as u32,
|
||||||
|
),
|
||||||
|
Bias::Left,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let mut buffer_point = point.to_point(map);
|
||||||
|
buffer_point.column = (map
|
||||||
|
.buffer_snapshot
|
||||||
|
.line_len(MultiBufferRow(buffer_point.row)) as f64
|
||||||
|
* percent) as u32;
|
||||||
|
|
||||||
|
map.clip_point(buffer_point.to_display_point(map), Bias::Left)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn end_of_line(
|
pub(crate) fn end_of_line(
|
||||||
map: &DisplaySnapshot,
|
map: &DisplaySnapshot,
|
||||||
display_lines: bool,
|
display_lines: bool,
|
||||||
|
@ -3906,6 +3962,61 @@ mod test {
|
||||||
assert_eq!(cx.cx.forced_motion(), false);
|
assert_eq!(cx.cx.forced_motion(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_forced_motion_delete_to_middle_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 g shift-m").await;
|
||||||
|
cx.shared_state().await.assert_eq(indoc! {"
|
||||||
|
ˇ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 g shift-m").await;
|
||||||
|
cx.shared_state().await.assert_eq(indoc! {"
|
||||||
|
the quickˇ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 g shift-m").await;
|
||||||
|
cx.shared_state().await.assert_eq(indoc! {"
|
||||||
|
the quicˇk
|
||||||
|
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 7 5 g shift-m").await;
|
||||||
|
cx.shared_state().await.assert_eq(indoc! {"
|
||||||
|
ˇ fox
|
||||||
|
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 2 3 g shift-m").await;
|
||||||
|
cx.shared_state().await.assert_eq(indoc! {"
|
||||||
|
ˇuick brown fox
|
||||||
|
jumped over the lazy dog"});
|
||||||
|
assert_eq!(cx.cx.forced_motion(), false);
|
||||||
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_forced_motion_delete_to_end_of_line(cx: &mut gpui::TestAppContext) {
|
async fn test_forced_motion_delete_to_end_of_line(cx: &mut gpui::TestAppContext) {
|
||||||
let mut cx = NeovimBackedTestContext::new(cx).await;
|
let mut cx = NeovimBackedTestContext::new(cx).await;
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
{"Put":{"state":"ˇthe quick brown fox\njumped over the lazy dog"}}
|
||||||
|
{"Key":"d"}
|
||||||
|
{"Key":"v"}
|
||||||
|
{"Key":"g"}
|
||||||
|
{"Key":"shift-m"}
|
||||||
|
{"Get":{"state":"ˇ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":"g"}
|
||||||
|
{"Key":"shift-m"}
|
||||||
|
{"Get":{"state":"the quickˇ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":"g"}
|
||||||
|
{"Key":"shift-m"}
|
||||||
|
{"Get":{"state":"the quicˇk\njumped over the lazy dog","mode":"Normal"}}
|
||||||
|
{"Put":{"state":"ˇthe quick brown fox\njumped over the lazy dog"}}
|
||||||
|
{"Key":"d"}
|
||||||
|
{"Key":"v"}
|
||||||
|
{"Key":"7"}
|
||||||
|
{"Key":"5"}
|
||||||
|
{"Key":"g"}
|
||||||
|
{"Key":"shift-m"}
|
||||||
|
{"Get":{"state":"ˇ fox\njumped over the lazy dog","mode":"Normal"}}
|
||||||
|
{"Put":{"state":"ˇthe quick brown fox\njumped over the lazy dog"}}
|
||||||
|
{"Key":"d"}
|
||||||
|
{"Key":"v"}
|
||||||
|
{"Key":"2"}
|
||||||
|
{"Key":"3"}
|
||||||
|
{"Key":"g"}
|
||||||
|
{"Key":"shift-m"}
|
||||||
|
{"Get":{"state":"ˇuick brown fox\njumped over the lazy dog","mode":"Normal"}}
|
Loading…
Add table
Add a link
Reference in a new issue