Helix Select Mode (#37748)

Please credit @eliaperantoni, for the original PR (#34136).
Merge after (#34060) to avoid conflicts.

Closes https://github.com/zed-industries/zed/issues/33838
Closes https://github.com/zed-industries/zed/issues/33906

Release Notes:
- Helix will no longer sometimes fall out into "normal" mode, will
remain in "helix normal" (example: vv)
- Added dedicated "helix select" mode that can be targeted by
keybindings

Known issues:
- [ ] Helix motion, especially surround-add will not properly work in
visual mode, as it won't call `helix_move_cursor`. It is possible
however to respect self.mode in change_selection now.
- [ ] Some operations, such as `Ctrl+A` (increment) or `>` (indent) will
collapse selection also. I haven't found a way to avoid it.

---------

Co-authored-by: fantacell <ghub@giggo.de>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
This commit is contained in:
Romans Malinovskis
2025-09-12 17:47:07 +02:00
committed by GitHub
co-authored by fantacell Conrad Irwin
parent a577128163
commit cba9ff55c7
9 changed files with 73 additions and 41 deletions
+21 -10
View File
@@ -516,11 +516,7 @@ impl Vim {
vim.update(cx, |_, cx| {
Vim::action(editor, cx, |vim, _: &SwitchToNormalMode, window, cx| {
if HelixModeSetting::get_global(cx).0 {
vim.switch_mode(Mode::HelixNormal, false, window, cx)
} else {
vim.switch_mode(Mode::Normal, false, window, cx)
}
vim.switch_mode(Mode::Normal, false, window, cx)
});
Vim::action(editor, cx, |vim, _: &SwitchToInsertMode, window, cx| {
@@ -1030,6 +1026,13 @@ impl Vim {
editor.set_relative_line_number(Some(is_relative), cx)
});
}
if HelixModeSetting::get_global(cx).0 {
if self.mode == Mode::Normal {
self.mode = Mode::HelixNormal
} else if self.mode == Mode::Visual {
self.mode = Mode::HelixSelect
}
}
if leave_selections {
return;
@@ -1151,7 +1154,7 @@ impl Vim {
}
Mode::HelixNormal => cursor_shape.normal.unwrap_or(CursorShape::Block),
Mode::Replace => cursor_shape.replace.unwrap_or(CursorShape::Underline),
Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
Mode::Visual | Mode::VisualLine | Mode::VisualBlock | Mode::HelixSelect => {
cursor_shape.visual.unwrap_or(CursorShape::Block)
}
Mode::Insert => cursor_shape.insert.unwrap_or({
@@ -1175,7 +1178,8 @@ impl Vim {
| Mode::Replace
| Mode::Visual
| Mode::VisualLine
| Mode::VisualBlock => false,
| Mode::VisualBlock
| Mode::HelixSelect => false,
}
}
@@ -1190,7 +1194,8 @@ impl Vim {
| Mode::VisualLine
| Mode::VisualBlock
| Mode::Replace
| Mode::HelixNormal => false,
| Mode::HelixNormal
| Mode::HelixSelect => false,
Mode::Normal => true,
}
}
@@ -1202,6 +1207,7 @@ impl Vim {
Mode::Insert => "insert",
Mode::Replace => "replace",
Mode::HelixNormal => "helix_normal",
Mode::HelixSelect => "helix_select",
}
.to_string();
@@ -1227,7 +1233,12 @@ impl Vim {
}
}
if mode == "normal" || mode == "visual" || mode == "operator" || mode == "helix_normal" {
if mode == "normal"
|| mode == "visual"
|| mode == "operator"
|| mode == "helix_normal"
|| mode == "helix_select"
{
context.add("VimControl");
}
context.set("vim_mode", mode);
@@ -1522,7 +1533,7 @@ impl Vim {
cx: &mut Context<Self>,
) {
match self.mode {
Mode::VisualLine | Mode::VisualBlock | Mode::Visual => {
Mode::VisualLine | Mode::VisualBlock | Mode::Visual | Mode::HelixSelect => {
self.update_editor(cx, |vim, editor, cx| {
let original_mode = vim.undo_modes.get(transaction_id);
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {