input: binding set can track unconsumed triggers

This commit is contained in:
Mitchell Hashimoto
2023-09-29 20:18:41 -07:00
parent f9962a04ef
commit 9d0e7ab138

View File

@ -372,6 +372,13 @@ pub const Set = struct {
std.hash_map.default_max_load_percentage, std.hash_map.default_max_load_percentage,
); );
const UnconsumedMap = std.HashMapUnmanaged(
Trigger,
void,
Context(Trigger),
std.hash_map.default_max_load_percentage,
);
/// The set of bindings. /// The set of bindings.
bindings: HashMap = .{}, bindings: HashMap = .{},
@ -380,9 +387,23 @@ pub const Set = struct {
/// the most recently added binding for an action. /// the most recently added binding for an action.
reverse: ReverseMap = .{}, reverse: ReverseMap = .{},
/// The map of triggers that explicitly do not want to be consumed
/// when matched. A trigger is "consumed" when it is not further
/// processed and potentially sent to the terminal. An "unconsumed"
/// trigger will perform both its action and also continue normal
/// encoding processing (if any).
///
/// This is stored as a separate map since unconsumed triggers are
/// rare and we don't want to bloat our map with a byte per entry
/// (for boolean state) when most entries will be consumed.
///
/// Assert: trigger in this map is also in bindings.
unconsumed: UnconsumedMap = .{},
pub fn deinit(self: *Set, alloc: Allocator) void { pub fn deinit(self: *Set, alloc: Allocator) void {
self.bindings.deinit(alloc); self.bindings.deinit(alloc);
self.reverse.deinit(alloc); self.reverse.deinit(alloc);
self.unconsumed.deinit(alloc);
self.* = undefined; self.* = undefined;
} }
@ -393,11 +414,36 @@ pub const Set = struct {
alloc: Allocator, alloc: Allocator,
t: Trigger, t: Trigger,
action: Action, action: Action,
) Allocator.Error!void {
try self.put_(alloc, t, action, true);
}
/// Same as put but marks the trigger as unconsumed. An unconsumed
/// trigger will evaluate the action and continue to encode for the
/// terminal.
///
/// This is a separate function because this case is rare.
pub fn putUnconsumed(
self: *Set,
alloc: Allocator,
t: Trigger,
action: Action,
) Allocator.Error!void {
try self.put_(alloc, t, action, false);
}
fn put_(
self: *Set,
alloc: Allocator,
t: Trigger,
action: Action,
consumed: bool,
) Allocator.Error!void { ) Allocator.Error!void {
// unbind should never go into the set, it should be handled prior // unbind should never go into the set, it should be handled prior
assert(action != .unbind); assert(action != .unbind);
const gop = try self.bindings.getOrPut(alloc, t); const gop = try self.bindings.getOrPut(alloc, t);
if (!consumed) try self.unconsumed.put(alloc, t, {});
// If we have an existing binding for this trigger, we have to // If we have an existing binding for this trigger, we have to
// update the reverse mapping to remove the old action. // update the reverse mapping to remove the old action.
@ -410,6 +456,9 @@ pub const Set = struct {
break :it; break :it;
} }
} }
// We also have to remove the unconsumed state if it exists.
if (consumed) _ = self.unconsumed.remove(t);
} }
gop.value_ptr.* = action; gop.value_ptr.* = action;
@ -429,10 +478,18 @@ pub const Set = struct {
return self.reverse.get(a); return self.reverse.get(a);
} }
/// Returns true if the given trigger should be consumed. Requires
/// that trigger is in the set to be valid so this should only follow
/// a non-null get.
pub fn getConsumed(self: Set, t: Trigger) bool {
return self.unconsumed.get(t) == null;
}
/// Remove a binding for a given trigger. /// Remove a binding for a given trigger.
pub fn remove(self: *Set, t: Trigger) void { pub fn remove(self: *Set, t: Trigger) void {
const action = self.bindings.get(t) orelse return; const action = self.bindings.get(t) orelse return;
_ = self.bindings.remove(t); _ = self.bindings.remove(t);
_ = self.unconsumed.remove(t);
// Look for a matching action in bindings and use that. // Look for a matching action in bindings and use that.
// Note: we'd LIKE to replace this with the most recent binding but // Note: we'd LIKE to replace this with the most recent binding but
@ -654,3 +711,20 @@ test "set: overriding a mapping updates reverse" {
try testing.expect(trigger == null); try testing.expect(trigger == null);
} }
} }
test "set: consumed state" {
const testing = std.testing;
const alloc = testing.allocator;
var s: Set = .{};
defer s.deinit(alloc);
try s.put(alloc, .{ .key = .a }, .{ .new_window = {} });
try testing.expect(s.getConsumed(.{ .key = .a }));
try s.putUnconsumed(alloc, .{ .key = .a }, .{ .new_window = {} });
try testing.expect(!s.getConsumed(.{ .key = .a }));
try s.put(alloc, .{ .key = .a }, .{ .new_window = {} });
try testing.expect(s.getConsumed(.{ .key = .a }));
}