diff --git a/src/config/Config.zig b/src/config/Config.zig index 6d45ab4ec..44a61caae 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -3311,15 +3311,8 @@ pub const Keybinds = struct { return; } - const binding = try inputpkg.Binding.parse(value); - switch (binding.action) { - .unbind => self.set.remove(binding.trigger), - else => if (binding.consumed) { - try self.set.put(alloc, binding.trigger, binding.action); - } else { - try self.set.putUnconsumed(alloc, binding.trigger, binding.action); - }, - } + // Let our much better tested binding package handle parsing and storage. + try self.set.parseAndPut(alloc, value); } /// Deep copy of the struct. Required by Config. diff --git a/src/input/Binding.zig b/src/input/Binding.zig index 8d554a36e..bb8b65d11 100644 --- a/src/input/Binding.zig +++ b/src/input/Binding.zig @@ -72,6 +72,10 @@ pub const Parser = struct { .consumed = !self.unconsumed, } }; } + + pub fn reset(self: *Parser) void { + self.trigger_it.i = 0; + } }; /// An iterator that yields each trigger in a sequence of triggers. For @@ -828,6 +832,44 @@ pub const Set = struct { self.* = undefined; } + /// Parse a user input binding and add it to the set. This will handle + /// the "unbind" case, ensure consumed/unconsumed fields are set correctly, + /// handle sequences, etc. + /// + /// If an error is returned, the set is unmodified and safe to reuse. + pub fn parseAndPut( + self: *Set, + alloc: Allocator, + input: []const u8, + ) (Allocator.Error || Error)!void { + // To make cleanup easier, we ensure that the full sequence is + // valid before making any set modifications. This is more expensive + // computationally but it makes cleanup way, way easier. + var it = try Parser.init(input); + while (try it.next()) |_| {} + it.reset(); + + // Now we know the input is valid, we can add it to the set. + var set: *Set = self; + while (it.next() catch unreachable) |elem| switch (elem) { + .leader => |t| { + _ = t; + @panic("TODO"); + }, + + .binding => |b| switch (b.action) { + // TODO: unbinding sequences doesn't remove their leaders + .unbind => set.remove(b.trigger), + + else => if (b.consumed) { + try set.put(alloc, b.trigger, b.action); + } else { + try set.putUnconsumed(alloc, b.trigger, b.action); + }, + }, + }; + } + /// Add a binding to the set. If the binding already exists then /// this will overwrite it. pub fn put( @@ -1254,6 +1296,70 @@ test "parse: sequences" { } } +test "set: parseAndPut typical binding" { + const testing = std.testing; + const alloc = testing.allocator; + + var s: Set = .{}; + defer s.deinit(alloc); + + try s.parseAndPut(alloc, "a=new_window"); + + // Creates forward mapping + { + const action = s.get(.{ .key = .{ .translated = .a } }).?; + try testing.expect(action == .new_window); + } + + // Creates reverse mapping + { + const trigger = s.getTrigger(.{ .new_window = {} }).?; + try testing.expect(trigger.key.translated == .a); + } +} + +test "set: parseAndPut unconsumed binding" { + const testing = std.testing; + const alloc = testing.allocator; + + var s: Set = .{}; + defer s.deinit(alloc); + + try s.parseAndPut(alloc, "unconsumed:a=new_window"); + + // Creates forward mapping + { + const trigger: Trigger = .{ .key = .{ .translated = .a } }; + const action = s.get(trigger).?; + try testing.expect(action == .new_window); + try testing.expect(!s.getConsumed(trigger)); + } + + // Creates reverse mapping + { + const trigger = s.getTrigger(.{ .new_window = {} }).?; + try testing.expect(trigger.key.translated == .a); + } +} + +test "set: parseAndPut removed binding" { + const testing = std.testing; + const alloc = testing.allocator; + + var s: Set = .{}; + defer s.deinit(alloc); + + try s.parseAndPut(alloc, "a=new_window"); + try s.parseAndPut(alloc, "a=unbind"); + + // Creates forward mapping + { + const trigger: Trigger = .{ .key = .{ .translated = .a } }; + try testing.expect(s.get(trigger) == null); + } + try testing.expect(s.getTrigger(.{ .new_window = {} }) == null); +} + test "set: maintains reverse mapping" { const testing = std.testing; const alloc = testing.allocator;