summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Uko Kokņevičs2022-01-05 04:44:30 +0200
committerGravatar Uko Kokņevičs2022-01-05 04:44:30 +0200
commitbd957acba04401468dc0a1d67430f1b4b00447ab (patch)
tree8076ed16519d1a05fc24ac2a640c07c2e3482d32 /src
parenta bit improved key map (diff)
downloades-bd957acba04401468dc0a1d67430f1b4b00447ab.tar.gz
es-bd957acba04401468dc0a1d67430f1b4b00447ab.tar.xz
es-bd957acba04401468dc0a1d67430f1b4b00447ab.zip
Much better key map
Diffstat (limited to 'src')
-rw-r--r--src/KeyMap.zig160
1 files changed, 105 insertions, 55 deletions
diff --git a/src/KeyMap.zig b/src/KeyMap.zig
index 5c866b8..e200faa 100644
--- a/src/KeyMap.zig
+++ b/src/KeyMap.zig
@@ -2,11 +2,14 @@ const es = @import("root");
2const std = @import("std"); 2const std = @import("std");
3 3
4const Allocator = std.mem.Allocator; 4const Allocator = std.mem.Allocator;
5const AutoHashMap = std.AutoHashMap; 5const ArrayList = std.ArrayList;
6const Buffer = es.Buffer; 6const Buffer = es.Buffer;
7const Editor = es.Editor; 7const Editor = es.Editor;
8const FormatOptions = std.fmt.FormatOptions;
9const HashMap = std.HashMap;
8const Key = es.Key; 10const Key = es.Key;
9const KeyMap = @This(); 11const KeyMap = @This();
12const Wyhash = std.hash.Wyhash;
10 13
11pub const Error = error{ 14pub const Error = error{
12 MalformedConfig, 15 MalformedConfig,
@@ -23,38 +26,60 @@ pub const Error = error{
23 26
24pub const BoundFn = fn (*Editor, *Buffer, Key) Error!void; 27pub const BoundFn = fn (*Editor, *Buffer, Key) Error!void;
25 28
26const Value = union(enum) { 29const Context = struct {
27 bound_fn: BoundFn, 30 pub fn hash(self: Context, key: []const Key) u64 {
28 submap: AutoHashMap(Key, Value), 31 _ = self;
29 32 var hasher = Wyhash.init(key.len);
30 pub fn deinit(self: *Value) void { 33 std.hash.autoHashStrat(&hasher, key, .Deep);
31 switch (self.*) { 34 return hasher.final();
32 .bound_fn => {}, 35 }
33 .submap => |*map| {
34 var it = map.valueIterator();
35 while (it.next()) |value| {
36 value.deinit();
37 }
38 36
39 map.deinit(); 37 pub fn eql(self: Context, a: []const Key, b: []const Key) bool {
40 }, 38 _ = self;
41 } 39 return std.mem.eql(Key, a, b);
40 }
41};
42 42
43 self.* = undefined; 43const Keys = struct {
44 keys: []const Key,
45
46 pub fn format(
47 self: Keys,
48 comptime fmt: []const u8,
49 options: FormatOptions,
50 writer: anytype,
51 ) @TypeOf(writer).Error!void {
52 var is_first = true;
53 for (self.keys) |key| {
54 if (is_first) {
55 is_first = false;
56 } else {
57 try std.fmt.formatBuf(" ", options, writer);
58 }
59
60 try Key.format(key, fmt, options, writer);
61 }
44 } 62 }
45}; 63};
46 64
65const RawMap = HashMap([]const Key, Value, Context, std.hash_map.default_max_load_percentage);
66
67const Value = union(enum) {
68 bound_fn: BoundFn,
69 chord: void,
70};
71
47allocator: Allocator, 72allocator: Allocator,
48current: ?*AutoHashMap(Key, Value), 73current_chord: ArrayList(Key),
49default: ?BoundFn, 74default: ?BoundFn,
50map: AutoHashMap(Key, Value), 75raw_map: RawMap,
51 76
52pub fn init(allocator: Allocator) KeyMap { 77pub fn init(allocator: Allocator) KeyMap {
53 return .{ 78 return .{
54 .allocator = allocator, 79 .allocator = allocator,
55 .current = null, 80 .current_chord = ArrayList(Key).init(allocator),
56 .default = null, 81 .default = null,
57 .map = AutoHashMap(Key, Value).init(allocator), 82 .raw_map = RawMap.init(allocator),
58 }; 83 };
59} 84}
60 85
@@ -126,65 +151,90 @@ pub fn defaultMap(allocator: Allocator) !KeyMap {
126} 151}
127 152
128pub fn deinit(self: *KeyMap) void { 153pub fn deinit(self: *KeyMap) void {
129 var it = self.map.valueIterator(); 154 self.current_chord.deinit();
130 while (it.next()) |value| { 155
131 value.deinit(); 156 var it = self.raw_map.keyIterator();
157 while (it.next()) |key| {
158 self.allocator.free(key.*);
132 } 159 }
133 self.map.deinit(); 160 self.raw_map.deinit();
134 161
135 self.* = undefined; 162 self.* = undefined;
136} 163}
137 164
138pub fn bind(self: *KeyMap, keys: []const Key, comptime f: anytype) !void { 165pub fn bind(self: *KeyMap, keys: []const Key, comptime f: anytype) !void {
139 std.debug.assert(keys.len > 0); 166 std.debug.assert(keys.len > 0);
140 var map: *AutoHashMap(Key, Value) = &self.map; 167 var idx: usize = 0;
141 for (keys[0..keys.len - 1]) |key| { 168 while (idx < keys.len - 1) : (idx += 1) {
142 const gop = try map.getOrPut(key); 169 const subseq = try self.allocator.dupe(Key, keys[0..idx + 1]);
170 errdefer self.allocator.free(subseq);
171
172 const gop = try self.raw_map.getOrPut(subseq);
143 if (!gop.found_existing) { 173 if (!gop.found_existing) {
144 gop.value_ptr.* = .{ .submap = AutoHashMap(Key, Value).init(self.allocator) }; 174 gop.value_ptr.* = .chord;
175 } else {
176 defer self.allocator.free(subseq);
177 switch (gop.value_ptr.*) {
178 .bound_fn => {
179 const longer_keys = Keys{ .keys = keys };
180 const shorter_keys = Keys{ .keys = keys };
181 std.log.err(
182 "Attempting to bind a longer chord ({}) over a shorter one ({})",
183 .{ longer_keys, shorter_keys },
184 );
185 return error.KeyBindError;
186 },
187 .chord => {},
188 }
145 } 189 }
190 }
191
192 const seq = try self.allocator.dupe(Key, keys);
193 errdefer self.allocator.free(seq);
146 194
195 const gop = try self.raw_map.getOrPut(seq);
196 if (!gop.found_existing) {
197 gop.value_ptr.* = .{ .bound_fn = wrapFn(f) };
198 } else {
199 defer self.allocator.free(seq);
200 // TODO: Maybe I wanna error on rebinding all the time? :thinking:
147 switch (gop.value_ptr.*) { 201 switch (gop.value_ptr.*) {
148 .bound_fn => { 202 .bound_fn => gop.value_ptr.* = .{ .bound_fn = wrapFn(f) },
149 std.log.err("Attempting to bind a longer chord over a shorter one ({any})", .{keys}); 203 .chord => {
204 const keys_wrap = Keys{ .keys = keys };
205 std.log.err(
206 "Attempting to bind a shorter chord ({}) over longer one(s)",
207 .{ keys_wrap },
208 );
150 return error.KeyBindError; 209 return error.KeyBindError;
151 }, 210 },
152 .submap => |*next_map| map = next_map,
153 } 211 }
154 } 212 }
155
156 const gop = try map.getOrPut(keys[keys.len - 1]);
157 if (!gop.found_existing) {
158 gop.value_ptr.* = .{ .bound_fn = wrapFn(f) };
159 } else switch (gop.value_ptr.*) {
160 .bound_fn => gop.value_ptr.* = .{ .bound_fn = wrapFn(f) },
161 .submap => {
162 std.log.err("Attempting to bind a shorter chord over a longer one ({any})", .{keys});
163 return error.KeyBindError;
164 },
165 }
166} 213}
167 214
168pub fn keypress(self: *KeyMap, editor: *Editor, buf: *Buffer, key: Key) !void { 215pub fn keypress(self: *KeyMap, editor: *Editor, buf: *Buffer, key: Key) !void {
169 const map = self.current orelse &self.map; 216 try self.current_chord.append(key);
170 if (map.getPtr(key)) |value| { 217 const keys = Keys{ .keys = self.current_chord.items };
171 switch (value.*) { 218 if (self.raw_map.get(self.current_chord.items)) |value| {
219 switch (value) {
172 .bound_fn => |f| { 220 .bound_fn => |f| {
173 self.current = null; 221 self.current_chord.clearRetainingCapacity();
174 return f(editor, buf, key); 222 editor.clearStatusMessage();
223 try f(editor, buf, key);
175 }, 224 },
176 .submap => |*submap| self.current = submap, 225 .chord => try editor.setStatusMessage("{}-", .{keys}),
177 } 226 }
178 } else if (self.current != null) { 227 } else if (self.current_chord.items.len > 1) {
179 // TODO: Output the full chord 228 std.log.debug("Unknown chord: {}", .{keys});
180 std.log.debug("Unknown chord: ... {}", .{key}); 229 try editor.setStatusMessage("Unknown chord: {}", .{keys});
181 try editor.setStatusMessage("Unknown chord: ... {}", .{key}); 230 self.current_chord.clearRetainingCapacity();
182 self.current = null;
183 } else if (self.default) |default| { 231 } else if (self.default) |default| {
184 return default(editor, buf, key); 232 try default(editor, buf, key);
233 self.current_chord.clearRetainingCapacity();
185 } else { 234 } else {
186 std.log.debug("Unknown key: {}", .{key}); 235 std.log.debug("Unknown key: {}", .{key});
187 try editor.setStatusMessage("Unknown key: {}", .{key}); 236 try editor.setStatusMessage("Unknown key: {}", .{key});
237 self.current_chord.clearRetainingCapacity();
188 } 238 }
189} 239}
190 240