From cee0d5b7b93f41e8c2f0f508d42e93e45565b4f8 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 26 Nov 2025 23:09:17 +0100 Subject: [PATCH 1/3] refactor: don't special case cursor rendering in inclusive mode This is too confusing. Rendering cursors differently, only in inclusive mode and only if there is an active selection is too confusing and pushes a lot of edge cases into otherwise simple commands. This will likely break a lot of the existing helix commands, but is better in the long run to fix them anyway. --- src/tui/editor.zig | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/src/tui/editor.zig b/src/tui/editor.zig index ddcda8f..83d2832 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -146,13 +146,6 @@ pub const CurSel = struct { }; } - fn to_cursor_inclusive(self: *const Self, root: Buffer.Root, metrics: Buffer.Metrics) Cursor { - var cursor = self.cursor; - if (self.selection) |sel| if (!sel.is_reversed()) - cursor.move_left(root, metrics) catch {}; - return cursor; - } - pub fn disable_selection(self: *Self, root: Buffer.Root, metrics: Buffer.Metrics) void { switch (tui.get_selection_style()) { .normal => self.disable_selection_normal(), @@ -1181,26 +1174,18 @@ pub const Editor = struct { } fn render_cursors(self: *Self, theme: *const Widget.Theme, cell_map: CellMap) !void { - const style = tui.get_selection_style(); const frame = tracy.initZone(@src(), .{ .name = "editor render cursors" }); defer frame.deinit(); if (tui.config().enable_terminal_cursor and tui.rdr().vx.caps.multi_cursor) tui.rdr().clear_all_multi_cursors() catch {}; for (self.cursels.items[0 .. self.cursels.items.len - 1]) |*cursel_| if (cursel_.*) |*cursel| { - const cursor = self.get_rendered_cursor(style, cursel); + const cursor = cursel.cursor; try self.render_cursor_secondary(&cursor, theme, cell_map); }; - const cursor = self.get_rendered_cursor(style, self.get_primary()); + const cursor = self.get_primary().cursor; try self.render_cursor_primary(&cursor, theme, cell_map); } - fn get_rendered_cursor(self: *Self, style: anytype, cursel: anytype) Cursor { - return switch (style) { - .normal => cursel.cursor, - .inclusive => cursel.to_cursor_inclusive(self.buf_root() catch return cursel.cursor, self.metrics), - }; - } - fn render_cursor_primary(self: *Self, cursor: *const Cursor, theme: *const Widget.Theme, cell_map: CellMap) !void { if (!tui.is_mainview_focused() or !self.enable_terminal_cursor) { if (self.screen_cursor(cursor)) |pos| { From b7613542e77bbb910e47b51a8c471c5ea7b80c3d Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 26 Nov 2025 23:16:50 +0100 Subject: [PATCH 2/3] refactor: CurSel.to_selection is internal --- src/tui/editor.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tui/editor.zig b/src/tui/editor.zig index 83d2832..fd417c0 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -125,7 +125,7 @@ pub const CurSel = struct { return if (self.selection) |*sel| sel else unreachable; } - pub fn to_selection(self: *const Self, root: Buffer.Root, metrics: Buffer.Metrics) Selection { + fn to_selection(self: *const Self, root: Buffer.Root, metrics: Buffer.Metrics) Selection { return switch (tui.get_selection_style()) { .normal => self.to_selection_normal(), .inclusive => self.to_selection_inclusive(root, metrics), From c46e830e046aa13d70026e9cedeb0aa2127914a4 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Thu, 27 Nov 2025 11:28:00 +0100 Subject: [PATCH 3/3] refactor: add Editor.with_cursels_const_repeat --- src/tui/editor.zig | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/tui/editor.zig b/src/tui/editor.zig index fd417c0..5e34ca3 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -2117,6 +2117,21 @@ pub const Editor = struct { return if (someone_stopped) error.Stop else {}; } + pub fn with_cursels_const_repeat(self: *Self, root: Buffer.Root, move: cursel_operator_const, ctx: Context) error{Stop}!void { + var someone_stopped = false; + var repeat: usize = 1; + _ = ctx.args.match(.{tp.extract(&repeat)}) catch false; + while (repeat > 0) : (repeat -= 1) { + for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| + with_cursel_const(root, move, cursel, self.metrics) catch { + someone_stopped = true; + }; + self.collapse_cursors(); + if (someone_stopped) break; + } + return if (someone_stopped) error.Stop else {}; + } + fn with_cursel_mut_arg(self: *Self, root: Buffer.Root, op: cursel_operator_mut_arg, cursel: *CurSel, allocator: Allocator, ctx: Context) error{Stop}!Buffer.Root { return op(self, root, cursel, allocator, ctx); }