vim: Close popovers and expanded hunks on escape in Helix normal mode

In Helix normal mode escape runs `vim::SwitchToHelixNormalMode`, which
only switches the mode, so the diagnostic shown by `] d`, a hunk expanded
with `g o`, a hover popover and the like stayed open. Vim mode closes them
because escape there is `editor::Cancel`. Binding Helix to `Cancel` isn't
an option, since once nothing is open it collapses the selections.

Pull the part of `Editor::cancel` that runs before it touches the
selections into `Editor::dismiss_overlays`, and call it when escape is
pressed in Helix normal mode. Selections are kept as before.

Closes #55197
This commit is contained in:
dualfroz committed 2026-10-03 02:13:23 +02:00
1 parent 134c5e9f71
commit 5f70e7afe2
3 files changed
+68 -17

No files matched your search

+23 -16
View File
@@ -3519,22 +3519,7 @@ impl Editor {
self.selection_mark_mode = false; self.selection_mark_mode = false;
self.selection_drag_state = SelectionDragState::None; self.selection_drag_state = SelectionDragState::None;
if self.dismiss_menus_and_popups(true, window, cx) { if self.dismiss_overlays(window, cx) {
cx.notify();
return;
}
if self.clear_expanded_diff_hunks(cx) {
cx.notify();
return;
}
if self.show_git_blame_gutter
&& !self
.blame
.as_ref()
.is_some_and(|blame| blame.read(cx).is_static())
{
self.show_git_blame_gutter = false;
cx.notify();
return; return;
} }
@@ -3553,6 +3538,28 @@ impl Editor {
cx.propagate(); cx.propagate();
} }
/// Closes what [`Cancel`] closes before it gets to the selections: menus and popovers,
/// expanded diff hunks and the git blame gutter. Returns whether anything was closed.
pub fn dismiss_overlays(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
let mut dismissed =
self.dismiss_menus_and_popups(true, window, cx) || self.clear_expanded_diff_hunks(cx);
if !dismissed
&& self.show_git_blame_gutter
&& !self
.blame
.as_ref()
.is_some_and(|blame| blame.read(cx).is_static())
{
self.show_git_blame_gutter = false;
dismissed = true;
}
if dismissed {
cx.notify();
}
dismissed
}
pub fn dismiss_menus_and_popups( pub fn dismiss_menus_and_popups(
&mut self, &mut self,
is_user_requested: bool, is_user_requested: bool,
+40 -1
View File
@@ -1879,7 +1879,7 @@ mod test {
use futures::StreamExt; use futures::StreamExt;
use std::{fmt::Write, time::Duration}; use std::{fmt::Write, time::Duration};
use editor::{HighlightKey, MultiBufferOffset}; use editor::{Anchor, HighlightKey, MultiBufferOffset};
use gpui::{ use gpui::{
Bounds, DispatchEventResult, ElementInputHandler, KeyBinding, KeyDownEvent, Keystroke, Bounds, DispatchEventResult, ElementInputHandler, KeyBinding, KeyDownEvent, Keystroke,
PlatformInput, PlatformInputHandler, UpdateGlobal, VisualTestContext, PlatformInput, PlatformInputHandler, UpdateGlobal, VisualTestContext,
@@ -4687,6 +4687,45 @@ mod test {
assert_eq!(cx.active_operator(), None); assert_eq!(cx.active_operator(), None);
} }
#[gpui::test]
async fn test_helix_escape_collapses_expanded_hunk(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
cx.enable_helix();
let selected = indoc! {"
one
«twoˇ»
three"};
cx.set_state(selected, Mode::HelixNormal);
cx.set_head_text(indoc! {"
one
CHANGED
three"});
cx.run_until_parked();
let has_expanded_hunks = |cx: &mut VimTestContext| {
cx.update_editor(|editor, _, cx| {
editor
.buffer()
.read(cx)
.has_expanded_diff_hunks_in_ranges(&[Anchor::Min..Anchor::Max], cx)
})
};
cx.simulate_keystrokes("g o");
assert!(has_expanded_hunks(&mut cx));
cx.simulate_keystrokes("escape");
assert!(
!has_expanded_hunks(&mut cx),
"escape should collapse the expanded hunk"
);
cx.assert_state(selected, Mode::HelixNormal);
cx.simulate_keystrokes("escape");
cx.assert_state(selected, Mode::HelixNormal);
}
#[gpui::test] #[gpui::test]
async fn test_helix_rename_uses_visible_cursor_position(cx: &mut gpui::TestAppContext) { async fn test_helix_rename_uses_visible_cursor_position(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new_typescript(cx).await; let mut cx = VimTestContext::new_typescript(cx).await;
+5
View File
@@ -714,6 +714,11 @@ impl Vim {
editor, editor,
cx, cx,
|vim, _: &SwitchToHelixNormalMode, window, cx| { |vim, _: &SwitchToHelixNormalMode, window, cx| {
// Escape in helix normal mode lands here. Close popovers and overlays
// like `editor::Cancel` does in vim mode, but keep the selections.
if vim.mode == Mode::HelixNormal {
vim.update_editor(cx, |_, editor, cx| editor.dismiss_overlays(window, cx));
}
vim.switch_mode(Mode::HelixNormal, true, window, cx) vim.switch_mode(Mode::HelixNormal, true, window, cx)
}, },
); );