From 71c5b400ced873cddd77497b7a9fd06184954813 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Mon, 28 Sep 2026 16:43:59 -0300 Subject: Z arms the view keys until Esc, and zm puts the cursor's column mid-view Z is helix's sticky view mode: the z keys stay armed after each until Esc, which leaves the mode and does nothing else (no pane hop). zm is align_view_middle: an unwrapped view scrolls sideways so the cursor's column is in its middle. Co-Authored-By: Claude Opus 5.5 --- src/modal.zig | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'src/modal.zig') diff --git a/src/modal.zig b/src/modal.zig index 0171da93..16196b4f 100644 --- a/src/modal.zig +++ b/src/modal.zig @@ -16,6 +16,7 @@ pub const Normal = struct { prefix_next, prefix_prev, prefix_register, + prefix_view_sticky, goto_file_start, goto_last_line, @@ -38,6 +39,7 @@ pub const Normal = struct { view_top, view_center, view_bottom, + view_middle, view_scroll_down, view_scroll_up, @@ -196,9 +198,12 @@ pub const Normal = struct { held_char: u21 = 0, /// `"`: the register the next command names, 0 for none register: u21 = 0, + /// `Z`: the view keys stay armed until Esc (helix's sticky view) + sticky: bool = false, + /// Drop what is pending; sticky view mode stays armed through it. pub fn clear(state: *State) void { - state.* = .{}; + state.* = if (state.sticky) .{ .sticky = true, .prefix = .view } else .{}; } }; @@ -254,7 +259,7 @@ pub const Normal = struct { view_bottom, last_modification, }; - pub const View = enum { top, center, bottom, scroll_down, scroll_up }; + pub const View = enum { top, center, bottom, middle, scroll_down, scroll_up }; pub const Find = enum { forward, backward, till_forward, till_backward }; pub const Line = enum { start, end, first_nonws }; pub const Page = enum { half_down, half_up, down, up }; @@ -407,7 +412,10 @@ pub const Normal = struct { pub fn parse(state: *State, key: Input) Result { if (key.has(.escape)) { - state.clear(); + // Esc out of sticky view mode leaves that mode and nothing else + const sticky = state.sticky; + state.* = .{}; + if (sticky) return .ignored; return resultAction(.escape); } @@ -448,8 +456,9 @@ pub const Normal = struct { return .ignored; }, .view => { - state.prefix = .none; + if (!state.sticky) state.prefix = .none; if (key.has(.view_top)) return resultAction(.{ .view = .top }); + if (key.has(.view_middle)) return resultAction(.{ .view = .middle }); if (key.has(.view_center)) return resultAction(.{ .view = .center }); if (key.has(.view_bottom)) return resultAction(.{ .view = .bottom }); if (key.has(.view_scroll_down)) return resultAction(.{ .view = .scroll_down }); @@ -543,6 +552,10 @@ pub const Normal = struct { // Prefix setters retain the count for their continuation. if (key.has(.prefix_goto)) return armPrefix(state, .goto, if (counted.explicit) count else 0); if (key.has(.prefix_view)) return armPrefix(state, .view, if (counted.explicit) count else 0); + if (key.has(.prefix_view_sticky)) { + state.sticky = true; + return armPrefix(state, .view, 0); + } if (key.has(.prefix_find_fwd)) return armPrefix(state, .find_fwd, if (counted.explicit) count else 0); if (key.has(.prefix_find_back)) return armPrefix(state, .find_back, if (counted.explicit) count else 0); if (key.has(.prefix_till_fwd)) return armPrefix(state, .till_fwd, if (counted.explicit) count else 0); -- cgit v1.3