From fccab59a36c3b05e5a2c72ef8645c240c985f554 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 10 Dec 2025 14:34:17 +0100 Subject: [PATCH 01/11] refactor: optionally generate keybind_match events in keybind module --- src/keybind/keybind.zig | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/keybind/keybind.zig b/src/keybind/keybind.zig index 7aa573e..89a3507 100644 --- a/src/keybind/keybind.zig +++ b/src/keybind/keybind.zig @@ -24,6 +24,7 @@ const builtin_keybinds = std.StaticStringMap([]const u8).initComptime(.{ .{ "emacs", @embedFile("builtin/emacs.json") }, }); +pub var enable_match_events: bool = false; var integer_argument: ?usize = null; var mode_flag: KeybindMode = .normal; @@ -673,6 +674,8 @@ const BindingSet = struct { })) { const key_event = input.KeyEvent.from_message(event, keypress, keypress_shifted, text, modifiers); if (self.process_key_event(key_event) catch |e| return tp.exit_error(e, @errorReturnTrace())) |binding| { + if (enable_match_events) + self.send_match_event(binding); for (binding.commands) |*cmd| try cmd.execute(); } } else if (try m.match(.{"F"})) { @@ -771,6 +774,20 @@ const BindingSet = struct { } } + fn send_match_event(_: *const @This(), binding: *const Binding) void { + var buf: [tp.max_message_size]u8 = undefined; + var stream: std.Io.Writer = .fixed(&buf); + cbor.writeArrayHeader(&stream, 2) catch return; + cbor.writeValue(&stream, "keybind_match") catch return; + cbor.writeArrayHeader(&stream, binding.commands.len) catch return; + for (binding.commands) |cmd| { + cbor.writeArrayHeader(&stream, 2) catch return; + cbor.writeValue(&stream, cmd.command) catch return; + stream.writeAll(cmd.args) catch return; + } + _ = tp.self_pid().send_raw(.{ .buf = stream.buffered() }) catch {}; + } + fn log_keyhints_message() void { for (globals.current_sequence.items) |item| switch (item.key) { input.key.left_control, input.key.right_control => return, From 33f404b22dc771a161b2a179402418d1cd17b720 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 10 Dec 2025 14:35:12 +0100 Subject: [PATCH 02/11] refactor: add tui.enable_match_events and tui.disable_match_events --- src/tui/tui.zig | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/tui/tui.zig b/src/tui/tui.zig index d4d803f..8158cdb 100644 --- a/src/tui/tui.zig +++ b/src/tui/tui.zig @@ -2357,3 +2357,11 @@ pub fn set_last_palette(type_: PaletteType, ctx: command.Context) void { .ctx = .{ .args = ctx.args.clone(self.allocator) catch return }, }; } + +pub fn enable_match_events() void { + keybind.enable_match_events = true; +} + +pub fn disable_match_events() void { + keybind.enable_match_events = false; +} From b542707cf775d0c60a76e620e3c8d698094115c2 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 10 Dec 2025 14:37:51 +0100 Subject: [PATCH 03/11] feat: subscribe to keybind_match events in inputview and display them --- src/tui/inputview.zig | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/tui/inputview.zig b/src/tui/inputview.zig index 66c3d77..2ff2a1b 100644 --- a/src/tui/inputview.zig +++ b/src/tui/inputview.zig @@ -5,6 +5,7 @@ const ArrayList = @import("std").ArrayList; const Writer = @import("std").Io.Writer; const tp = @import("thespian"); +const cbor = @import("cbor"); const Plane = @import("renderer").Plane; const EventHandler = @import("EventHandler"); @@ -12,6 +13,7 @@ const input = @import("input"); const tui = @import("tui.zig"); const Widget = @import("Widget.zig"); +const MessageFilter = @import("MessageFilter.zig"); pub const name = "inputview"; @@ -42,10 +44,14 @@ pub fn create(allocator: Allocator, parent: Plane) !Widget { .buffer = .empty, }; try tui.input_listeners().add(EventHandler.bind(self, listen)); + try tui.message_filters().add(MessageFilter.bind(self, keybind_match)); + tui.enable_match_events(); return Widget.to(self); } pub fn deinit(self: *Self, allocator: Allocator) void { + tui.disable_match_events(); + tui.message_filters().remove_ptr(self); tui.input_listeners().remove_ptr(self); for (self.buffer.items) |item| self.allocator.free(item.json); @@ -101,7 +107,7 @@ fn append(self: *Self, json: []const u8) !void { }; } -pub fn listen(self: *Self, _: tp.pid_ref, m: tp.message) tp.result { +fn listen(self: *Self, _: tp.pid_ref, m: tp.message) tp.result { if (try m.match(.{ "M", tp.more })) return; var buf: [4096]u8 = undefined; const json = m.to_json(&buf) catch |e| return tp.exit_error(e, @errorReturnTrace()); @@ -129,6 +135,20 @@ pub fn listen(self: *Self, _: tp.pid_ref, m: tp.message) tp.result { self.append(result.written()) catch |e| return tp.exit_error(e, @errorReturnTrace()); } +fn keybind_match(self: *Self, _: tp.pid_ref, m: tp.message) MessageFilter.Error!bool { + var cmds: []const u8 = undefined; + if (!(m.match(.{ "keybind_match", tp.extract_cbor(&cmds) }) catch false)) return false; + + var result: Writer.Allocating = .init(self.allocator); + defer result.deinit(); + const writer = &result.writer; + writer.writeAll("keybind -> ") catch return true; + cbor.toJsonWriter(cmds, writer, .{}) catch return true; + + self.append(result.written()) catch return true; + return true; +} + pub fn receive(_: *Self, _: tp.pid_ref, _: tp.message) error{Exit}!bool { return false; } From ef30ac9de042b37f4c11c80966074527a681730d Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 10 Dec 2025 14:39:27 +0100 Subject: [PATCH 04/11] fix: drop spurious keybind_match events in tui.receive --- src/tui/tui.zig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tui/tui.zig b/src/tui/tui.zig index 8158cdb..1203973 100644 --- a/src/tui/tui.zig +++ b/src/tui/tui.zig @@ -500,6 +500,9 @@ fn receive_safe(self: *Self, from: tp.pid_ref, m: tp.message) !void { if (try m.match(.{"focus_out"})) return; + if (try m.match(.{ "keybind_match", tp.more })) + return; + if (try self.send_widgets(from, m)) return; From 7df83c7e2b01e49dfdf887151423204c77ad10f4 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 10 Dec 2025 15:12:01 +0100 Subject: [PATCH 05/11] refactor: add namespace and section to keybind match events --- src/keybind/keybind.zig | 8 +++++--- src/tui/inputview.zig | 7 ++++--- src/tui/tui.zig | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/keybind/keybind.zig b/src/keybind/keybind.zig index 89a3507..2ca6d50 100644 --- a/src/keybind/keybind.zig +++ b/src/keybind/keybind.zig @@ -774,11 +774,13 @@ const BindingSet = struct { } } - fn send_match_event(_: *const @This(), binding: *const Binding) void { + fn send_match_event(self: *const @This(), binding: *const Binding) void { var buf: [tp.max_message_size]u8 = undefined; var stream: std.Io.Writer = .fixed(&buf); - cbor.writeArrayHeader(&stream, 2) catch return; - cbor.writeValue(&stream, "keybind_match") catch return; + cbor.writeArrayHeader(&stream, 4) catch return; + cbor.writeValue(&stream, "K") catch return; + cbor.writeValue(&stream, self.name) catch return; + cbor.writeValue(&stream, self.config_section) catch return; cbor.writeArrayHeader(&stream, binding.commands.len) catch return; for (binding.commands) |cmd| { cbor.writeArrayHeader(&stream, 2) catch return; diff --git a/src/tui/inputview.zig b/src/tui/inputview.zig index 2ff2a1b..810126d 100644 --- a/src/tui/inputview.zig +++ b/src/tui/inputview.zig @@ -136,14 +136,15 @@ fn listen(self: *Self, _: tp.pid_ref, m: tp.message) tp.result { } fn keybind_match(self: *Self, _: tp.pid_ref, m: tp.message) MessageFilter.Error!bool { + var namespace: []const u8 = undefined; + var section: []const u8 = undefined; var cmds: []const u8 = undefined; - if (!(m.match(.{ "keybind_match", tp.extract_cbor(&cmds) }) catch false)) return false; + if (!(m.match(.{ "K", tp.extract(&namespace), tp.extract(§ion), tp.extract_cbor(&cmds) }) catch false)) return false; var result: Writer.Allocating = .init(self.allocator); defer result.deinit(); const writer = &result.writer; - writer.writeAll("keybind -> ") catch return true; - cbor.toJsonWriter(cmds, writer, .{}) catch return true; + cbor.toJsonWriter(m.buf, writer, .{}) catch return true; self.append(result.written()) catch return true; return true; diff --git a/src/tui/tui.zig b/src/tui/tui.zig index 1203973..b23d803 100644 --- a/src/tui/tui.zig +++ b/src/tui/tui.zig @@ -500,7 +500,7 @@ fn receive_safe(self: *Self, from: tp.pid_ref, m: tp.message) !void { if (try m.match(.{"focus_out"})) return; - if (try m.match(.{ "keybind_match", tp.more })) + if (try m.match(.{ "K", tp.more })) return; if (try self.send_widgets(from, m)) From c87884f924cf237edac733e355fc56cab0ebe675 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 10 Dec 2025 15:23:22 +0100 Subject: [PATCH 06/11] refactor: remove some obsolete overlay commands --- src/keybind/builtin/flow.json | 4 ---- src/tui/mode/overlay/open_recent.zig | 10 ---------- src/tui/mode/overlay/palette.zig | 10 ---------- src/tui/mode/overlay/vcs_status.zig | 10 ---------- 4 files changed, 34 deletions(-) diff --git a/src/keybind/builtin/flow.json b/src/keybind/builtin/flow.json index 6daedd7..32e7c87 100644 --- a/src/keybind/builtin/flow.json +++ b/src/keybind/builtin/flow.json @@ -43,7 +43,6 @@ ["f10", "theme_next"], ["f11", "toggle_panel"], ["alt+f11", "toggle_color_scheme"], - ["f12", "toggle_inputview"], ["shift+alt+f9", "hint_window_next_widget_style"], ["alt+!", "run_task"], ["ctrl+tab", "next_tab"], @@ -412,8 +411,6 @@ ["ctrl+shift+p", "palette_menu_down"], ["ctrl+shift+q", "quit_without_saving"], ["ctrl+shift+w", "close_file_without_saving"], - ["ctrl+shift+l", "overlay_toggle_panel"], - ["ctrl+shift+i", "overlay_toggle_inputview"], ["alt+shift+p", "palette_menu_down"], ["alt+p", "palette_menu_up"], ["alt+l", "toggle_panel"], @@ -421,7 +418,6 @@ ["f9", "theme_prev"], ["f10", "theme_next"], ["f11", "toggle_panel"], - ["f12", "toggle_inputview"], ["escape", "palette_menu_cancel"], ["up", "palette_menu_up"], ["down", "palette_menu_down"], diff --git a/src/tui/mode/overlay/open_recent.zig b/src/tui/mode/overlay/open_recent.zig index 73620b9..ad8ae61 100644 --- a/src/tui/mode/overlay/open_recent.zig +++ b/src/tui/mode/overlay/open_recent.zig @@ -489,16 +489,6 @@ const cmds = struct { } pub const overlay_insert_bytes_meta: Meta = .{ .arguments = &.{.string} }; - pub fn overlay_toggle_panel(self: *Self, _: Ctx) Result { - return self.cmd_async("toggle_panel"); - } - pub const overlay_toggle_panel_meta: Meta = .{}; - - pub fn overlay_toggle_inputview(self: *Self, _: Ctx) Result { - return self.cmd_async("toggle_inputview"); - } - pub const overlay_toggle_inputview_meta: Meta = .{}; - pub fn overlay_next_widget_style(self: *Self, _: Ctx) Result { tui.set_next_style(widget_type); self.do_resize(); diff --git a/src/tui/mode/overlay/palette.zig b/src/tui/mode/overlay/palette.zig index e19e907..9583002 100644 --- a/src/tui/mode/overlay/palette.zig +++ b/src/tui/mode/overlay/palette.zig @@ -604,16 +604,6 @@ pub fn Create(options: type) type { } pub const overlay_insert_bytes_meta: Meta = .{ .arguments = &.{.string} }; - pub fn overlay_toggle_panel(self: *Self, _: Ctx) Result { - return self.cmd_async("toggle_panel"); - } - pub const overlay_toggle_panel_meta: Meta = .{}; - - pub fn overlay_toggle_inputview(self: *Self, _: Ctx) Result { - return self.cmd_async("toggle_inputview"); - } - pub const overlay_toggle_inputview_meta: Meta = .{}; - pub fn overlay_next_widget_style(self: *Self, _: Ctx) Result { tui.set_next_style(widget_type); const padding = tui.get_widget_style(widget_type).padding; diff --git a/src/tui/mode/overlay/vcs_status.zig b/src/tui/mode/overlay/vcs_status.zig index 2ecfc41..b25b698 100644 --- a/src/tui/mode/overlay/vcs_status.zig +++ b/src/tui/mode/overlay/vcs_status.zig @@ -366,16 +366,6 @@ const cmds = struct { } pub const overlay_insert_bytes_meta: Meta = .{ .arguments = &.{.string} }; - pub fn overlay_toggle_panel(self: *Self, _: Ctx) Result { - return self.cmd_async("toggle_panel"); - } - pub const overlay_toggle_panel_meta: Meta = .{}; - - pub fn overlay_toggle_inputview(self: *Self, _: Ctx) Result { - return self.cmd_async("toggle_inputview"); - } - pub const overlay_toggle_inputview_meta: Meta = .{}; - pub fn overlay_next_widget_style(self: *Self, _: Ctx) Result { tui.set_next_style(widget_type); self.do_resize(); From 65c9b41784060dd8faa2110edd3bcdad9052e0ce Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 10 Dec 2025 15:56:42 +0100 Subject: [PATCH 07/11] refactor: split out keybind events from inputview to a new keybindview --- src/tui/inputview.zig | 20 ------- src/tui/keybindview.zig | 114 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 20 deletions(-) create mode 100644 src/tui/keybindview.zig diff --git a/src/tui/inputview.zig b/src/tui/inputview.zig index 810126d..80578c4 100644 --- a/src/tui/inputview.zig +++ b/src/tui/inputview.zig @@ -13,7 +13,6 @@ const input = @import("input"); const tui = @import("tui.zig"); const Widget = @import("Widget.zig"); -const MessageFilter = @import("MessageFilter.zig"); pub const name = "inputview"; @@ -44,14 +43,10 @@ pub fn create(allocator: Allocator, parent: Plane) !Widget { .buffer = .empty, }; try tui.input_listeners().add(EventHandler.bind(self, listen)); - try tui.message_filters().add(MessageFilter.bind(self, keybind_match)); - tui.enable_match_events(); return Widget.to(self); } pub fn deinit(self: *Self, allocator: Allocator) void { - tui.disable_match_events(); - tui.message_filters().remove_ptr(self); tui.input_listeners().remove_ptr(self); for (self.buffer.items) |item| self.allocator.free(item.json); @@ -135,21 +130,6 @@ fn listen(self: *Self, _: tp.pid_ref, m: tp.message) tp.result { self.append(result.written()) catch |e| return tp.exit_error(e, @errorReturnTrace()); } -fn keybind_match(self: *Self, _: tp.pid_ref, m: tp.message) MessageFilter.Error!bool { - var namespace: []const u8 = undefined; - var section: []const u8 = undefined; - var cmds: []const u8 = undefined; - if (!(m.match(.{ "K", tp.extract(&namespace), tp.extract(§ion), tp.extract_cbor(&cmds) }) catch false)) return false; - - var result: Writer.Allocating = .init(self.allocator); - defer result.deinit(); - const writer = &result.writer; - cbor.toJsonWriter(m.buf, writer, .{}) catch return true; - - self.append(result.written()) catch return true; - return true; -} - pub fn receive(_: *Self, _: tp.pid_ref, _: tp.message) error{Exit}!bool { return false; } diff --git a/src/tui/keybindview.zig b/src/tui/keybindview.zig new file mode 100644 index 0000000..3e5c3f3 --- /dev/null +++ b/src/tui/keybindview.zig @@ -0,0 +1,114 @@ +const eql = @import("std").mem.eql; +const time = @import("std").time; +const Allocator = @import("std").mem.Allocator; +const ArrayList = @import("std").ArrayList; +const Writer = @import("std").Io.Writer; + +const tp = @import("thespian"); +const cbor = @import("cbor"); + +const Plane = @import("renderer").Plane; +const input = @import("input"); + +const tui = @import("tui.zig"); +const Widget = @import("Widget.zig"); +const MessageFilter = @import("MessageFilter.zig"); + +pub const name = "keybindview"; + +allocator: Allocator, +parent: Plane, +plane: Plane, +buffer: Buffer, + +const Self = @This(); + +const Entry = struct { + time: i64, + tdiff: i64, + msg: []u8, +}; +const Buffer = ArrayList(Entry); + +pub fn create(allocator: Allocator, parent: Plane) !Widget { + var n = try Plane.init(&(Widget.Box{}).opts_vscroll(@typeName(Self)), parent); + errdefer n.deinit(); + const self = try allocator.create(Self); + errdefer allocator.destroy(self); + self.* = .{ + .allocator = allocator, + .parent = parent, + .plane = n, + .buffer = .empty, + }; + try tui.message_filters().add(MessageFilter.bind(self, keybind_match)); + tui.enable_match_events(); + return Widget.to(self); +} + +pub fn deinit(self: *Self, allocator: Allocator) void { + tui.disable_match_events(); + tui.message_filters().remove_ptr(self); + for (self.buffer.items) |item| + self.allocator.free(item.msg); + self.buffer.deinit(self.allocator); + self.plane.deinit(); + allocator.destroy(self); +} + +pub fn render(self: *Self, theme: *const Widget.Theme) bool { + self.plane.set_base_style(theme.panel); + self.plane.erase(); + self.plane.home(); + const height = self.plane.dim_y(); + var first = true; + const count = self.buffer.items.len; + const begin_at = if (height > count) 0 else count - height; + for (self.buffer.items[begin_at..]) |item| { + if (first) first = false else _ = self.plane.putstr("\n") catch return false; + self.output_tdiff(item.tdiff) catch return false; + _ = self.plane.putstr(item.msg) catch return false; + } + return false; +} + +fn output_tdiff(self: *Self, tdiff: i64) !void { + const msi = @divFloor(tdiff, time.us_per_ms); + if (msi == 0) { + const d: f64 = @floatFromInt(tdiff); + const ms = d / time.us_per_ms; + _ = try self.plane.print("{d:6.2}▎", .{ms}); + } else { + const ms: u64 = @intCast(msi); + _ = try self.plane.print("{d:6}▎", .{ms}); + } +} + +fn append(self: *Self, msg: []const u8) !void { + const ts = time.microTimestamp(); + const tdiff = if (self.buffer.items.len > 0) ts -| self.buffer.items[self.buffer.items.len - 1].time else 0; + (try self.buffer.addOne(self.allocator)).* = .{ + .time = ts, + .tdiff = tdiff, + .msg = try self.allocator.dupeZ(u8, msg), + }; +} + +fn keybind_match(self: *Self, _: tp.pid_ref, m: tp.message) MessageFilter.Error!bool { + var namespace: []const u8 = undefined; + var section: []const u8 = undefined; + var cmds: []const u8 = undefined; + if (!(m.match(.{ "K", tp.extract(&namespace), tp.extract(§ion), tp.extract_cbor(&cmds) }) catch false)) return false; + + var result: Writer.Allocating = .init(self.allocator); + defer result.deinit(); + const writer = &result.writer; + cbor.toJsonWriter(m.buf, writer, .{}) catch return true; + + self.append(result.written()) catch return true; + return true; +} + +pub fn receive(_: *Self, _: tp.pid_ref, _: tp.message) error{Exit}!bool { + return false; +} From 3071c10892480c1714b48202428f706dea42551b Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 10 Dec 2025 15:57:23 +0100 Subject: [PATCH 08/11] refactor: add toggle_keybindview command and flow mode keybinds --- src/keybind/builtin/flow.json | 2 ++ src/tui/mainview.zig | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/src/keybind/builtin/flow.json b/src/keybind/builtin/flow.json index 32e7c87..c15cdb3 100644 --- a/src/keybind/builtin/flow.json +++ b/src/keybind/builtin/flow.json @@ -34,6 +34,7 @@ ["alt+p", "goto_prev_file_or_diagnostic"], ["alt+l", "toggle_panel"], ["alt+i", "toggle_inputview"], + ["alt+k", "toggle_keybindview"], ["alt+x", "open_command_palette"], ["alt+f3", "toggle_auto_find"], ["f4", "toggle_input_mode"], @@ -415,6 +416,7 @@ ["alt+p", "palette_menu_up"], ["alt+l", "toggle_panel"], ["alt+i", "toggle_inputview"], + ["alt+k", "toggle_keybindview"], ["f9", "theme_prev"], ["f10", "theme_next"], ["f11", "toggle_panel"], diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index af31b5d..cbd8db0 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -31,6 +31,7 @@ const logview = @import("logview.zig"); const filelist_view = @import("filelist_view.zig"); const info_view = @import("info_view.zig"); const input_view = @import("inputview.zig"); +const keybind_view = @import("keybindview.zig"); const Self = @This(); const Commands = command.Collection(cmds); @@ -845,6 +846,11 @@ const cmds = struct { } pub const toggle_inputview_meta: Meta = .{ .description = "Toggle raw input log" }; + pub fn toggle_keybindview(self: *Self, _: Ctx) Result { + try self.toggle_panel_view(keybind_view, .toggle); + } + pub const toggle_keybindview_meta: Meta = .{ .description = "Toggle keybind log" }; + pub fn toggle_inspector_view(self: *Self, _: Ctx) Result { try self.toggle_panel_view(@import("inspector_view.zig"), .toggle); } From e9f51388f7760630eb9cec80c2b326495cd7d969 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 10 Dec 2025 15:59:09 +0100 Subject: [PATCH 09/11] refactor: show keybindview when started with --show-input --- src/tui/mainview.zig | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index cbd8db0..0b5fdb7 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -109,8 +109,10 @@ pub fn create(allocator: std.mem.Allocator) CreateError!Widget { if (tui.config().bottom_bar.len > 0) { self.bottom_bar = (try widgets.addP(try @import("status/bar.zig").create(allocator, self.plane, tui.config().bottom_bar, .grip, EventHandler.bind(self, handle_bottom_bar_event)))).*; } - if (tp.env.get().is("show-input")) + if (tp.env.get().is("show-input")) { self.toggle_inputview_async(); + self.toggle_keybindview_async(); + } if (tp.env.get().is("show-log")) self.toggle_logview_async(); return w; @@ -1650,6 +1652,10 @@ fn toggle_inputview_async(_: *Self) void { tp.self_pid().send(.{ "cmd", "toggle_inputview" }) catch return; } +fn toggle_keybindview_async(_: *Self) void { + tp.self_pid().send(.{ "cmd", "toggle_keybindview" }) catch return; +} + fn show_file_async(_: *Self, file_path: []const u8) void { tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_path } }) catch return; } From e9da2d5cbea6d0728e3920cfebdf50e01eb5970d Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 10 Dec 2025 15:59:32 +0100 Subject: [PATCH 10/11] refactor: add keybindview to toggle_panel --- src/tui/mainview.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index 0b5fdb7..190c92a 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -826,6 +826,8 @@ const cmds = struct { try self.toggle_panel_view(info_view, .toggle) else if (self.is_panel_view_showing(filelist_view)) try self.toggle_panel_view(filelist_view, .toggle) + else if (self.is_panel_view_showing(keybind_view)) + try self.toggle_panel_view(keybind_view, .toggle) else if (self.is_panel_view_showing(input_view)) try self.toggle_panel_view(input_view, .toggle) else From 5febf537a787e863ec2cce6e81019869dcf50183 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 10 Dec 2025 16:28:02 +0100 Subject: [PATCH 11/11] refactor: make keybindview show key event and make it pretty --- src/keybind/keybind.zig | 14 ++++++++++---- src/tui/keybindview.zig | 33 ++++++++++++++++----------------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/src/keybind/keybind.zig b/src/keybind/keybind.zig index 2ca6d50..d22c9fb 100644 --- a/src/keybind/keybind.zig +++ b/src/keybind/keybind.zig @@ -675,7 +675,7 @@ const BindingSet = struct { const key_event = input.KeyEvent.from_message(event, keypress, keypress_shifted, text, modifiers); if (self.process_key_event(key_event) catch |e| return tp.exit_error(e, @errorReturnTrace())) |binding| { if (enable_match_events) - self.send_match_event(binding); + self.send_match_event(key_event, binding); for (binding.commands) |*cmd| try cmd.execute(); } } else if (try m.match(.{"F"})) { @@ -774,13 +774,19 @@ const BindingSet = struct { } } - fn send_match_event(self: *const @This(), binding: *const Binding) void { + fn send_match_event(self: *const @This(), key_event: KeyEvent, binding: *const Binding) void { var buf: [tp.max_message_size]u8 = undefined; var stream: std.Io.Writer = .fixed(&buf); - cbor.writeArrayHeader(&stream, 4) catch return; + + var key_event_buf: [256]u8 = undefined; + var key_event_str: std.Io.Writer = .fixed(&key_event_buf); + key_event_str.print("{f}", .{key_event}) catch return; + + cbor.writeArrayHeader(&stream, 5) catch return; cbor.writeValue(&stream, "K") catch return; - cbor.writeValue(&stream, self.name) catch return; + cbor.writeValue(&stream, get_namespace()) catch return; cbor.writeValue(&stream, self.config_section) catch return; + cbor.writeValue(&stream, key_event_str.buffered()) catch return; cbor.writeArrayHeader(&stream, binding.commands.len) catch return; for (binding.commands) |cmd| { cbor.writeArrayHeader(&stream, 2) catch return; diff --git a/src/tui/keybindview.zig b/src/tui/keybindview.zig index 3e5c3f3..4e2f532 100644 --- a/src/tui/keybindview.zig +++ b/src/tui/keybindview.zig @@ -26,7 +26,7 @@ const Self = @This(); const Entry = struct { time: i64, tdiff: i64, - msg: []u8, + msg: []const u8, }; const Buffer = ArrayList(Entry); @@ -84,28 +84,27 @@ fn output_tdiff(self: *Self, tdiff: i64) !void { } } -fn append(self: *Self, msg: []const u8) !void { +fn keybind_match(self: *Self, _: tp.pid_ref, m: tp.message) MessageFilter.Error!bool { + var namespace: []const u8 = undefined; + var section: []const u8 = undefined; + var key_event: []const u8 = undefined; + var cmds: []const u8 = undefined; + if (!(m.match(.{ "K", tp.extract(&namespace), tp.extract(§ion), tp.extract(&key_event), tp.extract_cbor(&cmds) }) catch false)) return false; + + var result: Writer.Allocating = .init(self.allocator); + defer result.deinit(); + const writer = &result.writer; + + writer.print("{s}:{s} {s} -> ", .{ namespace, section, key_event }) catch return true; + cbor.toJsonWriter(cmds, writer, .{}) catch return true; + const ts = time.microTimestamp(); const tdiff = if (self.buffer.items.len > 0) ts -| self.buffer.items[self.buffer.items.len - 1].time else 0; (try self.buffer.addOne(self.allocator)).* = .{ .time = ts, .tdiff = tdiff, - .msg = try self.allocator.dupeZ(u8, msg), + .msg = result.toOwnedSlice() catch return true, }; -} - -fn keybind_match(self: *Self, _: tp.pid_ref, m: tp.message) MessageFilter.Error!bool { - var namespace: []const u8 = undefined; - var section: []const u8 = undefined; - var cmds: []const u8 = undefined; - if (!(m.match(.{ "K", tp.extract(&namespace), tp.extract(§ion), tp.extract_cbor(&cmds) }) catch false)) return false; - - var result: Writer.Allocating = .init(self.allocator); - defer result.deinit(); - const writer = &result.writer; - cbor.toJsonWriter(m.buf, writer, .{}) catch return true; - - self.append(result.written()) catch return true; return true; }