feat: add flow native keybinding parser
This commit is contained in:
parent
34f7c0a7bd
commit
524e5be47c
7 changed files with 742 additions and 535 deletions
85
src/keybind/dynamic/parse_flow.zig
Normal file
85
src/keybind/dynamic/parse_flow.zig
Normal file
|
@ -0,0 +1,85 @@
|
|||
const std = @import("std");
|
||||
const input = @import("input");
|
||||
const KeyEvent = @import("KeyEvent.zig");
|
||||
|
||||
const ParseError = error{
|
||||
OutOfMemory,
|
||||
InvalidFormat,
|
||||
};
|
||||
|
||||
var parse_error_buf: [256]u8 = undefined;
|
||||
pub var parse_error_message: []const u8 = "";
|
||||
|
||||
fn parse_error_reset() void {
|
||||
parse_error_message = "";
|
||||
}
|
||||
|
||||
fn parse_error(comptime format: anytype, args: anytype) ParseError {
|
||||
parse_error_message = std.fmt.bufPrint(&parse_error_buf, format, args) catch "error in parse_error";
|
||||
return error.InvalidFormat;
|
||||
}
|
||||
|
||||
pub fn parse_key_events(allocator: std.mem.Allocator, str: []const u8) ParseError![]KeyEvent {
|
||||
parse_error_reset();
|
||||
if (str.len == 0) return parse_error("empty", .{});
|
||||
var result_events = std.ArrayList(KeyEvent).init(allocator);
|
||||
var iter_events = std.mem.tokenizeScalar(u8, str, '>');
|
||||
while (iter_events.next()) |event| {
|
||||
var key: ?input.Key = null;
|
||||
var mods = input.ModSet{};
|
||||
var iter = std.mem.tokenizeScalar(u8, event, '+');
|
||||
loop: while (iter.next()) |part| {
|
||||
if (part.len == 0) return parse_error("empty part in '{s}'", .{str});
|
||||
const modsInfo = @typeInfo(input.ModSet).Struct;
|
||||
inline for (modsInfo.fields) |field| {
|
||||
if (std.mem.eql(u8, part, field.name)) {
|
||||
if (@field(mods, field.name)) return parse_error("duplicate modifier '{s}' in '{s}'", .{ part, str });
|
||||
@field(mods, field.name) = true;
|
||||
continue :loop;
|
||||
}
|
||||
}
|
||||
const alias_mods = .{
|
||||
.{ "cmd", "super" },
|
||||
.{ "command", "super" },
|
||||
.{ "opt", "alt" },
|
||||
.{ "option", "alt" },
|
||||
.{ "control", "ctrl" },
|
||||
};
|
||||
inline for (alias_mods) |pair| {
|
||||
if (std.mem.eql(u8, part, pair[0])) {
|
||||
if (@field(mods, pair[1])) return parse_error("duplicate modifier '{s}' in '{s}'", .{ part, str });
|
||||
@field(mods, pair[1]) = true;
|
||||
continue :loop;
|
||||
}
|
||||
}
|
||||
|
||||
if (key != null) return parse_error("multiple keys in '{s}'", .{str});
|
||||
key = input.key.name_map.get(part);
|
||||
if (key == null) key = name_map.get(part);
|
||||
if (key == null) unicode: {
|
||||
const view = std.unicode.Utf8View.init(part) catch break :unicode;
|
||||
var it = view.iterator();
|
||||
const cp = it.nextCodepoint() orelse break :unicode;
|
||||
if (it.nextCodepoint() != null) break :unicode;
|
||||
key = cp;
|
||||
}
|
||||
if (key == null) return parse_error("unknown key '{s}' in '{s}'", .{ part, str });
|
||||
}
|
||||
if (key) |k|
|
||||
try result_events.append(.{ .key = k, .modifiers = @bitCast(mods) })
|
||||
else
|
||||
return parse_error("no key defined in '{s}'", .{str});
|
||||
}
|
||||
return result_events.toOwnedSlice();
|
||||
}
|
||||
|
||||
pub const name_map = blk: {
|
||||
@setEvalBranchQuota(2000);
|
||||
break :blk std.StaticStringMap(u21).initComptime(.{
|
||||
.{ "tab", input.key.tab },
|
||||
.{ "enter", input.key.enter },
|
||||
.{ "escape", input.key.escape },
|
||||
.{ "space", input.key.space },
|
||||
.{ "backspace", input.key.backspace },
|
||||
});
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue