86 lines
		
	
	
	
		
			3.3 KiB
		
	
	
	
		
			Zig
		
	
	
	
	
	
			
		
		
	
	
			86 lines
		
	
	
	
		
			3.3 KiB
		
	
	
	
		
			Zig
		
	
	
	
	
	
const std = @import("std");
 | 
						|
const input = @import("input");
 | 
						|
 | 
						|
pub 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![]input.KeyEvent {
 | 
						|
    parse_error_reset();
 | 
						|
    if (str.len == 0) return parse_error("empty", .{});
 | 
						|
    var result_events = std.ArrayList(input.KeyEvent).init(allocator);
 | 
						|
    var iter_sequence = std.mem.tokenizeScalar(u8, str, ' ');
 | 
						|
    while (iter_sequence.next()) |item| {
 | 
						|
        var key: ?input.Key = null;
 | 
						|
        var mods = input.ModSet{};
 | 
						|
        var iter = std.mem.tokenizeScalar(u8, item, '+');
 | 
						|
        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(input.KeyEvent.from_key_modset(k, 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 },
 | 
						|
        .{ "lt", '<' },
 | 
						|
        .{ "gt", '>' },
 | 
						|
    });
 | 
						|
};
 |