refactor: move dynamic keybindings to keybind module

This commit is contained in:
CJ van den Berg 2024-11-13 18:15:00 +01:00
parent d33bb955f9
commit 7fff8fc529
3 changed files with 64 additions and 36 deletions

View file

@ -12,7 +12,23 @@ const mod = @import("renderer").input.modifier;
const event_type = @import("renderer").input.event_type;
const command = @import("command");
const EventHandler = @import("EventHandler");
const tui = @import("tui.zig");
pub const Mode = struct {
input_handler: EventHandler,
event_handler: ?EventHandler = null,
name: []const u8 = "",
line_numbers: enum { absolute, relative } = .absolute,
keybind_hints: ?*const KeybindHints = null,
cursor_shape: renderer.CursorShape = .block,
pub fn deinit(self: *Mode) void {
self.input_handler.deinit();
if (self.event_handler) |eh| eh.deinit();
}
};
pub const KeybindHints = std.static_string_map.StaticStringMap([]const u8);
//A single key event, such as Ctrl-E
pub const KeyEvent = struct {
@ -361,7 +377,7 @@ pub const Hint = struct {
};
//A Collection of keybindings
pub const Mode = struct {
const BindingSet = struct {
allocator: std.mem.Allocator,
bindings: std.ArrayList(Binding),
on_match_failure: OnMatchFailure = .ignore,
@ -369,7 +385,7 @@ pub const Mode = struct {
current_sequence_egc: std.ArrayList(u8),
last_key_event_timestamp_ms: i64 = 0,
input_buffer: std.ArrayList(u8),
tui_mode: tui.Mode,
tui_mode: Mode,
const OnMatchFailure = enum { insert, ignore };
@ -377,8 +393,8 @@ pub const Mode = struct {
bindings: []const []const []const u8,
on_match_failure: OnMatchFailure,
pub fn toMode(self: *const @This(), allocator: std.mem.Allocator) !*Mode {
var result = try Mode.init(allocator);
pub fn toMode(self: *const @This(), allocator: std.mem.Allocator) !*BindingSet {
var result = try init(allocator);
result.on_match_failure = self.on_match_failure;
var state: enum { key_event, command, args } = .key_event;
for (self.bindings) |entry| {
@ -437,8 +453,8 @@ pub const Mode = struct {
.last_key_event_timestamp_ms = std.time.milliTimestamp(),
.input_buffer = try std.ArrayList(u8).initCapacity(allocator, 16),
.bindings = std.ArrayList(Binding).init(allocator),
.tui_mode = tui.Mode{
.handler = EventHandler.to_owned(self),
.tui_mode = Mode{
.input_handler = EventHandler.to_owned(self),
.name = "INSERT",
//.description = "vim",
.line_numbers = .relative,
@ -448,7 +464,7 @@ pub const Mode = struct {
return self;
}
pub fn deinit(self: *const Mode) void {
pub fn deinit(self: *const BindingSet) void {
for (self.bindings.items) |binding| {
binding.deinit();
}
@ -530,7 +546,7 @@ pub const Mode = struct {
}
//register a key press and try to match it with a binding
pub fn registerKeyEvent(self: *Mode, egc: u8, event: KeyEvent) !void {
pub fn registerKeyEvent(self: *BindingSet, egc: u8, event: KeyEvent) !void {
//clear key history if enough time has passed since last key press
const timestamp = std.time.milliTimestamp();
@ -595,7 +611,7 @@ pub const Mode = struct {
};
//A collection of various modes under a single namespace, such as "vim" or "emacs"
pub const Namespace = HashMap(*Mode);
const Namespace = HashMap(*BindingSet);
const HashMap = std.StringArrayHashMap;
//Data structure for mapping key events to keybindings
@ -614,7 +630,7 @@ pub const Bindings = struct {
return self.namespaces.values()[self.active_namespace];
}
pub fn activeMode(self: *Bindings) *Mode {
pub fn activeMode(self: *Bindings) *BindingSet {
return self.activeNamespace().values()[self.active_mode];
}
@ -629,7 +645,7 @@ pub const Bindings = struct {
return self;
}
pub fn addMode(self: *@This(), namespace_name: []const u8, mode_name: []const u8, mode: *Mode) !void {
pub fn addMode(self: *@This(), namespace_name: []const u8, mode_name: []const u8, mode: *BindingSet) !void {
const namespace = self.namespaces.getPtr(namespace_name) orelse blk: {
try self.namespaces.putNoClobber(namespace_name, Namespace.init(self.allocator));
break :blk self.namespaces.getPtr(namespace_name).?;
@ -648,7 +664,7 @@ pub const Bindings = struct {
self.allocator.destroy(self);
}
pub fn addNamespace(self: *Bindings, name: []const u8, modes: []const Mode) !void {
pub fn addNamespace(self: *Bindings, name: []const u8, modes: []const BindingSet) !void {
try self.namespaces.put(name, .{ .name = name, .modes = modes });
}
@ -659,7 +675,7 @@ pub const Bindings = struct {
for (parsed.value.object.values(), 0..) |namespace, i| {
if (namespace != .object) return error.namespaceNotObject;
for (namespace.object.values(), 0..) |mode, j| {
const mode_config = try std.json.parseFromValue(Mode.JsonConfig, self.allocator, mode, .{});
const mode_config = try std.json.parseFromValue(BindingSet.JsonConfig, self.allocator, mode, .{});
defer mode_config.deinit();
const parsed_mode = try mode_config.value.toMode(self.allocator);
try self.addMode(parsed.value.object.keys()[i], namespace.object.keys()[j], parsed_mode);
@ -668,7 +684,6 @@ pub const Bindings = struct {
}
};
const alloc = std.testing.allocator;
const expectEqual = std.testing.expectEqual;
const parse_test_cases = .{
@ -682,6 +697,7 @@ const parse_test_cases = .{
};
test "parse" {
const alloc = std.testing.allocator;
inline for (parse_test_cases) |case| {
var parsed = Sequence.init(alloc);
defer parsed.deinit();
@ -708,6 +724,7 @@ const match_test_cases = .{
};
test "match" {
const alloc = std.testing.allocator;
inline for (match_test_cases) |case| {
var input = Sequence.init(alloc);
defer input.deinit();
@ -721,6 +738,7 @@ test "match" {
}
test "json" {
const alloc = std.testing.allocator;
var bindings = try Bindings.init(alloc);
defer bindings.deinit();
try bindings.loadJson(@embedFile("keybindings.json"));