@@ -152,6 +152,7 @@
"g end": ["vim::EndOfLine", { "display_lines": true }],
"g 0": ["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 v": "vim::RestoreVisualSelection",
"g ]": "editor::GoToDiagnostic",
@@ -84,6 +84,9 @@ pub enum Motion {
StartOfLine {
display_lines: bool,
},
+ MiddleOfLine {
+ display_lines: bool,
+ },
EndOfLine {
display_lines: bool,
},
@@ -265,6 +268,13 @@ pub struct StartOfLine {
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)]
#[serde(deny_unknown_fields)]
struct UnmatchedForward {
@@ -283,6 +293,7 @@ impl_actions!(
vim,
[
StartOfLine,
+ MiddleOfLine,
EndOfLine,
FirstNonWhitespace,
Down,
@@ -409,6 +420,15 @@ pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
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.motion(
Motion::EndOfLine {
@@ -737,6 +757,7 @@ impl Motion {
| SentenceBackward
| SentenceForward
| GoToColumn
+ | MiddleOfLine { .. }
| UnmatchedForward { .. }
| UnmatchedBackward { .. }
| NextWordStart { .. }
@@ -769,6 +790,7 @@ impl Motion {
Down { .. }
| Up { .. }
| EndOfLine { .. }
+ | MiddleOfLine { .. }
| Matching
| UnmatchedForward { .. }
| UnmatchedBackward { .. }
@@ -894,6 +916,10 @@ impl Motion {
start_of_line(map, *display_lines, point),
SelectionGoal::None,
),
+ MiddleOfLine { display_lines } => (
+ middle_of_line(map, *display_lines, point, maybe_times),
+ SelectionGoal::None,
+ ),
EndOfLine { display_lines } => (
end_of_line(map, *display_lines, point, times),
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(
map: &DisplaySnapshot,
display_lines: bool,
@@ -3906,6 +3962,61 @@ mod test {
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]
async fn test_forced_motion_delete_to_end_of_line(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx).await;