feat: add support for command sequence bindings

This commit is contained in:
CJ van den Berg 2024-12-02 19:38:46 +01:00
parent 98062f669d
commit b693b111b2
Signed by: neurocyte
GPG key ID: 8EB1E1BB660E3FB9

View file

@ -272,7 +272,21 @@ const Command = struct {
command_ = try allocator.dupe(u8, token.string); command_ = try allocator.dupe(u8, token.string);
state = .args; state = .args;
}, },
.args => try args.append(allocator, token), .args => {
switch (token) {
.string, .integer, .float, .bool => {},
else => {
var json = std.ArrayList(u8).init(allocator);
defer json.deinit();
std.json.stringify(token, .{}, json.writer()) catch {};
const logger = log.logger("keybind");
logger.print_err("keybind.load", "ERROR: invalid command argument '{s}'", .{json.items});
logger.deinit();
return error.InvalidFormat;
},
}
try args.append(allocator, token);
},
} }
} }
@ -291,7 +305,7 @@ const Command = struct {
//An association of an command with a triggering key chord //An association of an command with a triggering key chord
const Binding = struct { const Binding = struct {
key_events: []KeyEvent, key_events: []KeyEvent,
command: Command, commands: []Command,
fn len(self: Binding) usize { fn len(self: Binding) usize {
return self.key_events.items.len; return self.key_events.items.len;
@ -365,22 +379,28 @@ const BindingSet = struct {
fn load_event(self: *BindingSet, allocator: std.mem.Allocator, dest: *std.ArrayListUnmanaged(Binding), event: input.Event, bindings: []const []const std.json.Value) (parse_flow.ParseError || parse_vim.ParseError)!void { fn load_event(self: *BindingSet, allocator: std.mem.Allocator, dest: *std.ArrayListUnmanaged(Binding), event: input.Event, bindings: []const []const std.json.Value) (parse_flow.ParseError || parse_vim.ParseError)!void {
bindings: for (bindings) |entry| { bindings: for (bindings) |entry| {
const token = entry[0]; if (entry.len < 2) {
if (token != .string) {
const logger = log.logger("keybind"); const logger = log.logger("keybind");
logger.print_err("keybind.load", "ERROR: invalid binding key token {any}", .{token}); logger.print_err("keybind.load", "ERROR: invalid binding definition {any}", .{entry});
logger.deinit();
continue :bindings;
}
const keys = entry[0];
if (keys != .string) {
const logger = log.logger("keybind");
logger.print_err("keybind.load", "ERROR: invalid binding key definition {any}", .{keys});
logger.deinit(); logger.deinit();
continue :bindings; continue :bindings;
} }
const key_events = switch (self.syntax) { const key_events = switch (self.syntax) {
.flow => parse_flow.parse_key_events(allocator, event, token.string) catch |e| { .flow => parse_flow.parse_key_events(allocator, event, keys.string) catch |e| {
const logger = log.logger("keybind"); const logger = log.logger("keybind");
logger.print_err("keybind.load", "ERROR: {s} {s}", .{ @errorName(e), parse_flow.parse_error_message }); logger.print_err("keybind.load", "ERROR: {s} {s}", .{ @errorName(e), parse_flow.parse_error_message });
logger.deinit(); logger.deinit();
break; break;
}, },
.vim => parse_vim.parse_key_events(allocator, event, token.string) catch |e| { .vim => parse_vim.parse_key_events(allocator, event, keys.string) catch |e| {
const logger = log.logger("keybind"); const logger = log.logger("keybind");
logger.print_err("keybind.load.vim", "ERROR: {s} {s}", .{ @errorName(e), parse_vim.parse_error_message }); logger.print_err("keybind.load.vim", "ERROR: {s} {s}", .{ @errorName(e), parse_vim.parse_error_message });
logger.deinit(); logger.deinit();
@ -389,9 +409,28 @@ const BindingSet = struct {
}; };
errdefer allocator.free(key_events); errdefer allocator.free(key_events);
const cmd = entry[1];
var cmds = std.ArrayList(Command).init(allocator);
defer cmds.deinit();
if (cmd == .string) {
try cmds.append(try Command.load(allocator, entry[1..]));
} else {
for (entry[1..]) |cmd_entry| {
if (cmd_entry != .array) {
var json = std.ArrayList(u8).init(allocator);
defer json.deinit();
std.json.stringify(cmd_entry, .{}, json.writer()) catch {};
const logger = log.logger("keybind");
logger.print_err("keybind.load", "ERROR: invalid command definition {s}", .{json.items});
logger.deinit();
continue :bindings;
}
try cmds.append(try Command.load(allocator, cmd_entry.array.items));
}
}
try dest.append(allocator, .{ try dest.append(allocator, .{
.key_events = key_events, .key_events = key_events,
.command = try Command.load(allocator, entry[1..]), .commands = try cmds.toOwnedSlice(),
}); });
} }
} }
@ -413,7 +452,8 @@ const BindingSet = struct {
const hints_map = &self.hints_map; const hints_map = &self.hints_map;
for (self.press.items) |binding| { for (self.press.items) |binding| {
var hint = if (hints_map.get(binding.command.command)) |previous| const cmd = binding.commands[0].command;
var hint = if (hints_map.get(cmd)) |previous|
std.ArrayList(u8).fromOwnedSlice(allocator, previous) std.ArrayList(u8).fromOwnedSlice(allocator, previous)
else else
std.ArrayList(u8).init(allocator); std.ArrayList(u8).init(allocator);
@ -433,7 +473,7 @@ const BindingSet = struct {
}, },
} }
} }
try hints_map.put(allocator, binding.command.command, try hint.toOwnedSlice()); try hints_map.put(allocator, cmd, try hint.toOwnedSlice());
} }
} }
@ -483,7 +523,7 @@ const BindingSet = struct {
.key = keypress, .key = keypress,
.modifiers = modifiers, .modifiers = modifiers,
}) catch |e| return tp.exit_error(e, @errorReturnTrace())) |binding| { }) catch |e| return tp.exit_error(e, @errorReturnTrace())) |binding| {
try binding.command.execute(); for (binding.commands) |*cmd| try cmd.execute();
} }
} else if (try m.match(.{"F"})) { } else if (try m.match(.{"F"})) {
self.flush() catch |e| return tp.exit_error(e, @errorReturnTrace()); self.flush() catch |e| return tp.exit_error(e, @errorReturnTrace());