mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
input: add Set.parseAndPut
This commit is contained in:

committed by
Mitchell Hashimoto

parent
a798a26063
commit
bc4eab4af7
@ -3311,15 +3311,8 @@ pub const Keybinds = struct {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const binding = try inputpkg.Binding.parse(value);
|
// Let our much better tested binding package handle parsing and storage.
|
||||||
switch (binding.action) {
|
try self.set.parseAndPut(alloc, value);
|
||||||
.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);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deep copy of the struct. Required by Config.
|
/// Deep copy of the struct. Required by Config.
|
||||||
|
@ -72,6 +72,10 @@ pub const Parser = struct {
|
|||||||
.consumed = !self.unconsumed,
|
.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
|
/// An iterator that yields each trigger in a sequence of triggers. For
|
||||||
@ -828,6 +832,44 @@ pub const Set = struct {
|
|||||||
self.* = undefined;
|
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
|
/// Add a binding to the set. If the binding already exists then
|
||||||
/// this will overwrite it.
|
/// this will overwrite it.
|
||||||
pub fn put(
|
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" {
|
test "set: maintains reverse mapping" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
|
Reference in New Issue
Block a user