From c8ed34701c15c3ada56fdc848d9dfe7524fc99f1 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Tue, 18 Nov 2025 19:02:42 +0100 Subject: [PATCH 01/11] refactor: extract whole symbol range in symbol_palette --- src/tui/mode/overlay/symbol_palette.zig | 33 ++++++++++++------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/tui/mode/overlay/symbol_palette.zig b/src/tui/mode/overlay/symbol_palette.zig index 21f3965..a32adda 100644 --- a/src/tui/mode/overlay/symbol_palette.zig +++ b/src/tui/mode/overlay/symbol_palette.zig @@ -40,7 +40,7 @@ const columns: [3]Column = .{ pub const Entry = struct { label: []const u8, - row: usize, + range: ed.Selection, cbor: []const u8, }; @@ -104,8 +104,8 @@ pub fn load_entries(palette: *Type) !usize { while (iter.len > 0) { var cbor_item: []const u8 = undefined; if (!try cbor.matchValue(&iter, cbor.extract_cbor(&cbor_item))) return error.BadCompletion; - const label_, const parent_, const kind, const row, _ = get_values(cbor_item); - (try palette.entries.addOne(palette.allocator)).* = .{ .cbor = cbor_item, .label = label_[0..@min(columns[0].max_width, label_.len)], .row = row }; + const label_, const parent_, const kind, const sel = get_values(cbor_item); + (try palette.entries.addOne(palette.allocator)).* = .{ .cbor = cbor_item, .label = label_[0..@min(columns[0].max_width, label_.len)], .range = sel }; const current_lengths: [3]usize = .{ label_.len, parent_.len, kind_name(@enumFromInt(kind)).len }; const label_len: u8 = @truncate(if (label_.len > columns[0].max_width) columns[0].max_width else label_.len); @@ -116,7 +116,7 @@ pub fn load_entries(palette: *Type) !usize { const less_fn = struct { fn less_fn(_: void, lhs: Entry, rhs: Entry) bool { - return lhs.row < rhs.row; + return lhs.range.begin.row < rhs.range.begin.row; } }.less_fn; std.mem.sort(Entry, palette.entries.items, {}, less_fn); @@ -147,7 +147,7 @@ pub fn on_render_menu(palette: *Type, button: *Type.ButtonType, theme: *const Wi if (!(cbor.matchValue(&iter, cbor.extract_cbor(&item_cbor)) catch false)) return false; if (!(cbor.matchValue(&iter, cbor.extract_cbor(&matches_cbor)) catch false)) return false; - const label_, const container, const kind, _, _ = get_values(item_cbor); + const label_, const container, const kind, _ = get_values(item_cbor); const icon_: []const u8 = kind_icon(@enumFromInt(kind)); const color: u24 = 0x0; var value: std.Io.Writer.Allocating = .init(palette.allocator); @@ -160,21 +160,20 @@ pub fn on_render_menu(palette: *Type, button: *Type.ButtonType, theme: *const Wi return tui.render_file_item(&button.plane, value.written(), icon_, color, indicator, matches_cbor, button.active, selected, button.hover, theme); } -fn get_values(item_cbor: []const u8) struct { []const u8, []const u8, u8, usize, usize } { +fn get_values(item_cbor: []const u8) struct { []const u8, []const u8, u8, ed.Selection } { var label_: []const u8 = ""; var container: []const u8 = ""; var kind: u8 = 0; - var row: usize = 0; - var col: usize = 0; + var range: ed.Selection = .{}; _ = cbor.match(item_cbor, .{ cbor.any, // file_path cbor.extract(&label_), // name cbor.extract(&container), // parent_name cbor.extract(&kind), // kind - cbor.extract(&row), // range.begin.row - cbor.extract(&col), // range.begin.col - cbor.any, // range.end.row - cbor.any, // range.end.col + cbor.extract(&range.begin.row), // range.begin.row + cbor.extract(&range.begin.col), // range.begin.col + cbor.extract(&range.end.row), // range.end.row + cbor.extract(&range.end.col), // range.end.col cbor.any, // tags cbor.any, // selectionRange.begin.row cbor.any, // selectionRange.begin.col @@ -183,19 +182,19 @@ fn get_values(item_cbor: []const u8) struct { []const u8, []const u8, u8, usize, cbor.any, // deprecated cbor.any, // detail }) catch false; - return .{ label_, container, kind, row, col }; + return .{ label_, container, kind, range }; } fn select(menu: **Type.MenuType, button: *Type.ButtonType, _: Type.Pos) void { - _, _, _, const row, const col = get_values(button.opts.label); + _, _, _, const sel = get_values(button.opts.label); tp.self_pid().send(.{ "cmd", "exit_overlay_mode" }) catch |e| menu.*.opts.ctx.logger.err(module_name, e); - tp.self_pid().send(.{ "cmd", "goto_line_and_column", .{ row + 1, col + 1 } }) catch |e| menu.*.opts.ctx.logger.err(module_name, e); + tp.self_pid().send(.{ "cmd", "goto_line_and_column", .{ sel.begin.row + 1, sel.begin.col + 1 } }) catch |e| menu.*.opts.ctx.logger.err(module_name, e); } pub fn updated(palette: *Type, button_: ?*Type.ButtonType) !void { const button = button_ orelse return cancel(palette); - _, _, _, const row, const col = get_values(button.opts.label); - tp.self_pid().send(.{ "cmd", "goto_line_and_column", .{ row + 1, col + 1 } }) catch {}; + _, _, _, const sel = get_values(button.opts.label); + tp.self_pid().send(.{ "cmd", "goto_line_and_column", .{ sel.begin.row + 1, sel.begin.col + 1 } }) catch {}; } pub fn cancel(palette: *Type) !void { From f26d0534de46374dd2ee29c160568ea2d7726caf Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Tue, 18 Nov 2025 19:32:58 +0100 Subject: [PATCH 02/11] fix: reversed logic of palette modal_dim --- src/tui/mode/overlay/palette.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tui/mode/overlay/palette.zig b/src/tui/mode/overlay/palette.zig index 9e1dcc5..cb02698 100644 --- a/src/tui/mode/overlay/palette.zig +++ b/src/tui/mode/overlay/palette.zig @@ -77,7 +77,7 @@ pub fn Create(options: type) type { else ModalBackground.Options(*Self).on_render_default else - ModalBackground.Options(*Self).on_render_default, + ModalBackground.Options(*Self).on_render_dim, }), .menu = try Menu.create(*Self, allocator, tui.plane(), .{ .ctx = self, From 9597dd7b6da260581930c30d3a36e0b2f3545c30 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Tue, 18 Nov 2025 19:33:40 +0100 Subject: [PATCH 03/11] fix: regression by bad merge in 1600bf6 --- src/tui/mode/overlay/palette.zig | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/tui/mode/overlay/palette.zig b/src/tui/mode/overlay/palette.zig index cb02698..e8ee77b 100644 --- a/src/tui/mode/overlay/palette.zig +++ b/src/tui/mode/overlay/palette.zig @@ -111,8 +111,6 @@ pub fn Create(options: type) type { try options.load_entries_with_args(self, ctx) else try options.load_entries(self); - if (self.entries.items.len > 0) - self.initial_selected = self.menu.selected; if (@hasDecl(options, "restore_state")) options.restore_state(self) catch {}; if (@hasDecl(options, "initial_query")) blk: { From 53391aa7b5af430bc5ba6c7c51c7d7eccd3b43c2 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Tue, 18 Nov 2025 19:35:36 +0100 Subject: [PATCH 04/11] refactor: limit palette quick activation to first palette_menu_activate_quick event --- src/tui/mode/overlay/palette.zig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tui/mode/overlay/palette.zig b/src/tui/mode/overlay/palette.zig index e8ee77b..a7083bd 100644 --- a/src/tui/mode/overlay/palette.zig +++ b/src/tui/mode/overlay/palette.zig @@ -42,6 +42,7 @@ pub fn Create(options: type) type { longest_hint: usize = 0, initial_selected: ?usize = null, placement: Placement, + quick_activate_enabled: bool = true, items: usize = 0, view_rows: usize, @@ -553,7 +554,9 @@ pub fn Create(options: type) type { pub const palette_menu_activate_meta: Meta = .{}; pub fn palette_menu_activate_quick(self: *Self, _: Ctx) Result { + if (!self.quick_activate_enabled) return; if (self.menu.selected orelse 0 > 0) self.menu.activate_selected(); + self.quick_activate_enabled = false; } pub const palette_menu_activate_quick_meta: Meta = .{}; From 99e32520ad223304644381f6e52e186af6a745da Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Tue, 18 Nov 2025 19:36:25 +0100 Subject: [PATCH 05/11] fix: disable quick activate in themes palette --- src/tui/mode/overlay/theme_palette.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tui/mode/overlay/theme_palette.zig b/src/tui/mode/overlay/theme_palette.zig index 4f454b2..1162cb1 100644 --- a/src/tui/mode/overlay/theme_palette.zig +++ b/src/tui/mode/overlay/theme_palette.zig @@ -39,6 +39,7 @@ pub fn load_entries(palette: *Type) !usize { }; longest_hint = @max(longest_hint, theme.name.len); } + palette.quick_activate_enabled = false; return longest_hint; } From 0e994e9f25adec26fd8b4ea46f846050e447a951 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Tue, 18 Nov 2025 19:38:14 +0100 Subject: [PATCH 06/11] refactor: disable dimming in theme palette To better evaluate the themes it is better not to dim. --- src/tui/mode/overlay/theme_palette.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tui/mode/overlay/theme_palette.zig b/src/tui/mode/overlay/theme_palette.zig index 1162cb1..51d7435 100644 --- a/src/tui/mode/overlay/theme_palette.zig +++ b/src/tui/mode/overlay/theme_palette.zig @@ -10,6 +10,7 @@ pub const Type = @import("palette.zig").Create(@This()); pub const label = "Search themes"; pub const name = " theme"; pub const description = "theme"; +pub const modal_dim = false; pub const Entry = struct { label: []const u8, From 1d06b71102752314ae46cec2d230c0cb076c070c Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Tue, 18 Nov 2025 19:39:28 +0100 Subject: [PATCH 07/11] refactor: move themes palette to top right To better see what the thmeme looks like for the current screen it is better to not cover it so much. --- src/tui/mode/overlay/theme_palette.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tui/mode/overlay/theme_palette.zig b/src/tui/mode/overlay/theme_palette.zig index 51d7435..d37f02b 100644 --- a/src/tui/mode/overlay/theme_palette.zig +++ b/src/tui/mode/overlay/theme_palette.zig @@ -11,6 +11,7 @@ pub const label = "Search themes"; pub const name = " theme"; pub const description = "theme"; pub const modal_dim = false; +pub const placement = .top_right; pub const Entry = struct { label: []const u8, From 70c2673cfe1f58ca994358671b3b4964856236ea Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Tue, 18 Nov 2025 19:55:52 +0100 Subject: [PATCH 08/11] refactor: add editor focus_on_range command --- src/tui/editor.zig | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/tui/editor.zig b/src/tui/editor.zig index 025e561..beda0ee 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -5711,6 +5711,37 @@ pub const Editor = struct { } pub const goto_line_and_column_meta: Meta = .{ .arguments = &.{ .integer, .integer } }; + pub fn focus_on_range(self: *Self, ctx: Context) Result { + var sel: Selection = .{}; + var pos_type: PosType = .column; + if (!try ctx.args.match(.{ + tp.extract(&sel.begin.row), + tp.extract(&sel.begin.col), + tp.extract(&sel.end.row), + tp.extract(&sel.end.col), + tp.extract(&pos_type), + })) return error.InvalidFocusOnRangeArgument; + + self.cancel_all_selections(); + const root = self.buf_root() catch return; + if (pos_type == .byte) + sel = sel.from_pos(root, self.metrics) catch return; + const primary = self.get_primary(); + try primary.cursor.move_to( + root, + sel.begin.row, + sel.begin.col, + self.metrics, + ); + primary.selection = sel; + if (self.view.is_visible(&primary.cursor)) + self.clamp() + else + try self.scroll_view_center(.{}); + self.need_render(); + } + pub const focus_on_range_meta: Meta = .{ .arguments = &.{ .integer, .integer, .integer, .integer } }; + pub fn goto_byte_offset(self: *Self, ctx: Context) Result { try self.send_editor_jump_source(); var offset: usize = 0; From fc244eabb6ca52a418eeee4c6d52d8d2c8e58109 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Tue, 18 Nov 2025 19:56:17 +0100 Subject: [PATCH 09/11] feat: focus currently selected symbol in symbol_palette instead of going to it --- src/tui/mode/overlay/symbol_palette.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tui/mode/overlay/symbol_palette.zig b/src/tui/mode/overlay/symbol_palette.zig index a32adda..56783b0 100644 --- a/src/tui/mode/overlay/symbol_palette.zig +++ b/src/tui/mode/overlay/symbol_palette.zig @@ -194,7 +194,7 @@ fn select(menu: **Type.MenuType, button: *Type.ButtonType, _: Type.Pos) void { pub fn updated(palette: *Type, button_: ?*Type.ButtonType) !void { const button = button_ orelse return cancel(palette); _, _, _, const sel = get_values(button.opts.label); - tp.self_pid().send(.{ "cmd", "goto_line_and_column", .{ sel.begin.row + 1, sel.begin.col + 1 } }) catch {}; + tp.self_pid().send(.{ "cmd", "focus_on_range", .{ sel.begin.row, sel.begin.col, sel.end.row, sel.end.col, ed.PosType.byte } }) catch {}; } pub fn cancel(palette: *Type) !void { From ae6df9dba8eca72bce6e642f1a3dda461f37f670 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Tue, 18 Nov 2025 19:59:08 +0100 Subject: [PATCH 10/11] feat: pre-select symbol at the primary cursor in symbol_palette --- src/tui/mode/overlay/symbol_palette.zig | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/tui/mode/overlay/symbol_palette.zig b/src/tui/mode/overlay/symbol_palette.zig index 56783b0..6f781b3 100644 --- a/src/tui/mode/overlay/symbol_palette.zig +++ b/src/tui/mode/overlay/symbol_palette.zig @@ -121,6 +121,9 @@ pub fn load_entries(palette: *Type) !usize { }.less_fn; std.mem.sort(Entry, palette.entries.items, {}, less_fn); + palette.initial_selected = find_closest(palette); + palette.quick_activate_enabled = false; + const total_width = total_row_width(); return 2 + if (max_cols_len > label.len + 3) total_width - max_label_len else label.len + 1 - max_cols_len; } @@ -185,6 +188,17 @@ fn get_values(item_cbor: []const u8) struct { []const u8, []const u8, u8, ed.Sel return .{ label_, container, kind, range }; } +fn find_closest(palette: *Type) ?usize { + const editor = tui.get_active_editor() orelse return null; + const cursor = editor.get_primary().cursor; + for (palette.entries.items, 0..) |entry, idx| { + _, _, _, const sel = get_values(entry.cbor); + if (cursor.within(sel)) + return idx + 1; + } + return null; +} + fn select(menu: **Type.MenuType, button: *Type.ButtonType, _: Type.Pos) void { _, _, _, const sel = get_values(button.opts.label); tp.self_pid().send(.{ "cmd", "exit_overlay_mode" }) catch |e| menu.*.opts.ctx.logger.err(module_name, e); From f9bbb558149dea7d437bb0579d246448313376ac Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Tue, 18 Nov 2025 20:03:36 +0100 Subject: [PATCH 11/11] fix: select previous symbol if there is no symbol at the cursor --- src/tui/mode/overlay/symbol_palette.zig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tui/mode/overlay/symbol_palette.zig b/src/tui/mode/overlay/symbol_palette.zig index 6f781b3..1acd873 100644 --- a/src/tui/mode/overlay/symbol_palette.zig +++ b/src/tui/mode/overlay/symbol_palette.zig @@ -191,10 +191,13 @@ fn get_values(item_cbor: []const u8) struct { []const u8, []const u8, u8, ed.Sel fn find_closest(palette: *Type) ?usize { const editor = tui.get_active_editor() orelse return null; const cursor = editor.get_primary().cursor; + var previous: usize = 0; for (palette.entries.items, 0..) |entry, idx| { _, _, _, const sel = get_values(entry.cbor); if (cursor.within(sel)) return idx + 1; + if (cursor.row < sel.begin.row) return previous + 1; + previous = idx; } return null; }