From 9984d5e2b588382a7d73aacafb1b59a327f3461a Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Tue, 18 Nov 2025 16:43:42 +0100 Subject: [PATCH] feat: add switch_input_mode command to change keybind mode in a mini mode --- src/keybind/keybind.zig | 46 ++++++++++++++++++++++++++++++++++------- src/tui/tui.zig | 15 ++++++++++++++ 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/src/keybind/keybind.zig b/src/keybind/keybind.zig index 16485d0..846c36e 100644 --- a/src/keybind/keybind.zig +++ b/src/keybind/keybind.zig @@ -68,15 +68,13 @@ const Handler = struct { fn create(mode_name: []const u8, allocator: std.mem.Allocator, opts: anytype) !Mode { const self = try allocator.create(@This()); errdefer allocator.destroy(self); + const insert_command = if (@hasField(@TypeOf(opts), "insert_command")) + opts.insert_command + else + "insert_chars"; self.* = .{ .allocator = allocator, - .bindings = try get_mode_binding_set( - mode_name, - if (@hasField(@TypeOf(opts), "insert_command")) - opts.insert_command - else - "insert_chars", - ), + .bindings = try get_mode_binding_set(mode_name, insert_command), }; return .{ .allocator = allocator, @@ -89,8 +87,33 @@ const Handler = struct { .selection_style = self.bindings.selection_style, .init_command = self.bindings.init_command, .deinit_command = self.bindings.deinit_command, + .insert_command = try allocator.dupe(u8, insert_command), }; } + fn replace(mode_: *Mode, mode_name: []const u8, allocator: std.mem.Allocator) !void { + const self = try allocator.create(@This()); + errdefer allocator.destroy(self); + self.* = .{ + .allocator = allocator, + .bindings = try get_mode_binding_set(mode_name, mode_.insert_command), + }; + + if (mode_.deinit_command) |deinit_command| deinit_command.execute_const(); + if (mode_.input_handler) |ih| ih.deinit(); + mode_.allocator.free(mode_.mode); + mode_.mode = try allocator.dupe(u8, mode_name); + mode_.allocator = allocator; + + mode_.input_handler = EventHandler.to_owned(self); + mode_.keybind_hints = self.bindings.hints(); + mode_.name = self.bindings.name; + mode_.line_numbers = self.bindings.line_numbers; + mode_.cursor_shape = self.bindings.cursor_shape; + mode_.selection_style = self.bindings.selection_style; + mode_.init_command = self.bindings.init_command; + mode_.deinit_command = self.bindings.deinit_command; + if (mode_.init_command) |init_command| init_command.execute_const(); + } pub fn deinit(self: *@This()) void { self.allocator.destroy(self); } @@ -113,6 +136,7 @@ pub const Mode = struct { init_command: ?Command = null, deinit_command: ?Command = null, initialized: bool = false, + insert_command: []const u8, pub fn run_init(self: *Mode) void { if (self.initialized) return; @@ -121,10 +145,18 @@ pub const Mode = struct { if (self.init_command) |init_command| init_command.execute_const(); } + pub fn replace(self: *Mode, mode_name: []const u8, allocator: std.mem.Allocator) !void { + Handler.replace(self, mode_name, allocator) catch |e| switch (e) { + error.NotFound => return error.Stop, + else => return e, + }; + } + pub fn deinit(self: *Mode) void { if (self.deinit_command) |deinit_command| deinit_command.execute_const(); if (self.event_handler) |eh| eh.deinit(); if (self.input_handler) |ih| ih.deinit(); + self.allocator.free(self.insert_command); self.allocator.free(self.mode); self.deinit_command = null; diff --git a/src/tui/tui.zig b/src/tui/tui.zig index 9a58b2d..ae18821 100644 --- a/src/tui/tui.zig +++ b/src/tui/tui.zig @@ -835,6 +835,10 @@ fn enter_input_mode(self: *Self, new_mode: Mode) command.Result { if (self.input_mode_) |*m| m.run_init(); } +fn switch_input_mode(self: *Self, mode_name: []const u8) !void { + if (self.input_mode_) |*m| try m.replace(mode_name, self.allocator); +} + fn refresh_input_mode(self: *Self) command.Result { const mode = (self.input_mode_ orelse return).mode; var new_mode = self.get_input_mode(mode) catch ret: { @@ -1090,6 +1094,17 @@ const cmds = struct { } pub const enter_mode_meta: Meta = .{ .arguments = &.{.string} }; + pub fn switch_mode(self: *Self, ctx: Ctx) Result { + if (!self.delayed_init_done) return; + + var mode: []const u8 = undefined; + if (!try ctx.args.match(.{tp.extract(&mode)})) + return tp.exit_error(error.InvalidSwitchModeArgument, null); + + return self.switch_input_mode(mode); + } + pub const switch_mode_meta: Meta = .{ .arguments = &.{.string} }; + pub fn enter_mode_default(self: *Self, _: Ctx) Result { return enter_mode(self, Ctx.fmt(.{keybind.default_mode})); }