This commit is contained in:
Romans Malinovskis 2025-08-26 08:57:33 +08:00 committed by GitHub
commit 8f6cc2c1f9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 187 additions and 3 deletions

View file

@ -433,7 +433,7 @@
"g c": "vim::WindowMiddle",
"g b": "vim::WindowBottom",
"x": "editor::SelectLine",
"x": "vim::HelixSelectLine",
"shift-x": "editor::SelectLine",
"%": "editor::SelectAll",
// Window mode

View file

@ -1,8 +1,10 @@
use editor::display_map::DisplaySnapshot;
use editor::{DisplayPoint, Editor, SelectionEffects, ToOffset, ToPoint, movement};
use editor::{
DisplayPoint, Editor, HideMouseCursorOrigin, SelectionEffects, ToOffset, ToPoint, movement,
};
use gpui::{Action, actions};
use gpui::{Context, Window};
use language::{CharClassifier, CharKind};
use language::{CharClassifier, CharKind, Point};
use text::{Bias, SelectionGoal};
use crate::motion;
@ -23,11 +25,14 @@ actions!(
HelixInsert,
/// Appends at the end of the selection.
HelixAppend,
/// Select entire line or multiple lines, extending downwards.
HelixSelectLine,
]
);
pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
Vim::action(editor, cx, Vim::helix_normal_after);
Vim::action(editor, cx, Vim::helix_select_lines);
Vim::action(editor, cx, Vim::helix_insert);
Vim::action(editor, cx, Vim::helix_append);
Vim::action(editor, cx, Vim::helix_yank);
@ -430,6 +435,47 @@ impl Vim {
});
self.switch_mode(Mode::HelixNormal, true, window, cx);
}
pub fn helix_select_lines(
&mut self,
_: &HelixSelectLine,
window: &mut Window,
cx: &mut Context<Self>,
) {
let count = Vim::take_count(cx).unwrap_or(1);
self.update_editor(cx, |_, editor, cx| {
editor.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
let display_map = editor.display_map.update(cx, |map, cx| map.snapshot(cx));
let mut selections = editor.selections.all::<Point>(cx);
let max_point = display_map.buffer_snapshot.max_point();
let buffer_snapshot = &display_map.buffer_snapshot;
for selection in &mut selections {
// Start always goes to column 0 of the first selected line
let start_row = selection.start.row;
let current_end_row = selection.end.row;
// Check if cursor is on empty line by checking first character
let line_start_offset = buffer_snapshot.point_to_offset(Point::new(start_row, 0));
let first_char = buffer_snapshot.chars_at(line_start_offset).next();
let extra_line = if first_char == Some('\n') { 1 } else { 0 };
let end_row = current_end_row + count as u32 + extra_line;
selection.start = Point::new(start_row, 0);
selection.end = if end_row > max_point.row {
max_point
} else {
Point::new(end_row, 0)
};
selection.reversed = false;
}
editor.change_selections(Default::default(), window, cx, |s| {
s.select(selections);
});
});
}
}
#[cfg(test)]
@ -763,4 +809,142 @@ mod test {
cx.shared_clipboard().assert_eq("worl");
cx.assert_state("hello «worlˇ»d", Mode::HelixNormal);
}
#[gpui::test]
async fn test_helix_select_lines(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
cx.set_state(
"line one\nline ˇtwo\nline three\nline four",
Mode::HelixNormal,
);
cx.simulate_keystrokes("2 x");
cx.assert_state(
"line one\n«line two\nline three\nˇ»line four",
Mode::HelixNormal,
);
// Test extending existing line selection
cx.set_state(
indoc! {"
li«ˇne one
li»ne two
line three
line four"},
Mode::HelixNormal,
);
cx.simulate_keystrokes("x");
cx.assert_state(
indoc! {"
«line one
line two
ˇ»line three
line four"},
Mode::HelixNormal,
);
// Pressing x in empty line, select next line (because helix considers cursor a selection)
cx.set_state(
indoc! {"
line one
ˇ
line three
line four"},
Mode::HelixNormal,
);
cx.simulate_keystrokes("x");
cx.assert_state(
indoc! {"
line one
«
line three
ˇ»line four"},
Mode::HelixNormal,
);
// Empty line with count selects extra + count lines
cx.set_state(
indoc! {"
line one
ˇ
line three
line four
line five"},
Mode::HelixNormal,
);
cx.simulate_keystrokes("2 x");
cx.assert_state(
indoc! {"
line one
«
line three
line four
ˇ»line five"},
Mode::HelixNormal,
);
// Compare empty vs non-empty line behavior
cx.set_state(
indoc! {"
ˇnon-empty line
line two
line three"},
Mode::HelixNormal,
);
cx.simulate_keystrokes("x");
cx.assert_state(
indoc! {"
«non-empty line
ˇ»line two
line three"},
Mode::HelixNormal,
);
// Same test but with empty line - should select one extra
cx.set_state(
indoc! {"
ˇ
line two
line three"},
Mode::HelixNormal,
);
cx.simulate_keystrokes("x");
cx.assert_state(
indoc! {"
«
line two
ˇ»line three"},
Mode::HelixNormal,
);
// Test selecting multiple lines with count
cx.set_state(
indoc! {"
ˇline one
line two
line threeˇ
line four
line five"},
Mode::HelixNormal,
);
cx.simulate_keystrokes("x");
cx.assert_state(
indoc! {"
«line one
ˇ»line two
«line three
ˇ»line four
line five"},
Mode::HelixNormal,
);
cx.simulate_keystrokes("x");
cx.assert_state(
indoc! {"
«line one
line two
line three
line four
ˇ»line five"},
Mode::HelixNormal,
);
}
}