helix: Improve "x" behavior (#35611)
Closes #32020 Release Notes: - Helix: Improve `x` behaviour. Will respect modifiers (`5 x`). Pressing `x` on a empty line, will select current+next line, because helix considers current line to be already selected without the need of pressing `x`.
This commit is contained in:
@@ -435,7 +435,7 @@
|
||||
"g b": "vim::WindowBottom",
|
||||
|
||||
"shift-r": "editor::Paste",
|
||||
"x": "editor::SelectLine",
|
||||
"x": "vim::HelixSelectLine",
|
||||
"shift-x": "editor::SelectLine",
|
||||
"%": "editor::SelectAll",
|
||||
// Window mode
|
||||
|
||||
+186
-2
@@ -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;
|
||||
@@ -25,11 +27,14 @@ actions!(
|
||||
HelixAppend,
|
||||
/// Goes to the location of the last modification.
|
||||
HelixGotoLastModification,
|
||||
/// 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);
|
||||
@@ -442,6 +447,47 @@ impl Vim {
|
||||
) {
|
||||
self.jump(".".into(), false, false, 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)]
|
||||
@@ -850,4 +896,142 @@ mod test {
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user