summaryrefslogtreecommitdiff
path: root/src/key.zig
blob: 3a007fb6f5167ff444d795d16825f6a9a31b767e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
const std = @import("std");

pub const Key = enum(u16) {
    return_ = 0x0d,
    escape = 0x1b,
    space = 0x20,
    backspace = 0x7f,
    max_char = 0xff,

    meta_nil = 0x100,
    meta_max_char = 0x1ff,

    left,
    right,
    up,
    down,
    delete,
    home,
    end,
    page_up,
    page_down,

    _,

    pub fn char(ch: u8) Key {
        return @intToEnum(Key, ch);
    }

    pub fn ctrl(ch: u8) Key {
        return @intToEnum(Key, ch & 0x1f);
    }

    pub fn format(
        key: Key,
        comptime fmt: []const u8,
        options: std.fmt.FormatOptions,
        writer: anytype,
    ) @TypeOf(writer).Error!void {
        comptime if (fmt.len != 0) {
            @compileError("Key doesn't support {" ++ fmt ++ "} format");
        };

        return switch (key) {
            .return_ => std.fmt.formatBuf("<return>", options, writer),
            .escape => std.fmt.formatBuf("<escape>", options, writer),
            .space => std.fmt.formatBuf("<space>", options, writer),
            .backspace => std.fmt.formatBuf("<backspace>", options, writer),
            .max_char => key.formatGeneric(options, writer),

            .meta_nil, .meta_max_char => key.formatGeneric(options, writer),

            .left => std.fmt.formatBuf("<left>", options, writer),
            .right => std.fmt.formatBuf("<right>", options, writer),
            .up => std.fmt.formatBuf("<up>", options, writer),
            .down => std.fmt.formatBuf("<down>", options, writer),
            .delete => std.fmt.formatBuf("<delete>", options, writer),
            .home => std.fmt.formatBuf("<home>", options, writer),
            .end => std.fmt.formatBuf("<end>", options, writer),
            .page_up => std.fmt.formatBuf("<page-up>", options, writer),
            .page_down => std.fmt.formatBuf("<page-down>", options, writer),

            _ => key.formatGeneric(options, writer),
        };
    }

    pub fn meta(ch: u8) Key {
        return @intToEnum(Key, ch + @enumToInt(Key.meta_nil));
    }

    pub fn metaCtrl(ch: u8) Key {
        return @intToEnum(Key, (ch & 0x1f) + @enumToInt(Key.meta_nil));
    }

    fn formatGeneric(
        key: Key,
        options: std.fmt.FormatOptions,
        writer: anytype,
    ) @TypeOf(writer).Error!void {
        const key_int = @enumToInt(key);
        if (key_int < @enumToInt(Key.space)) {
            const ch = std.ascii.toLower(@intCast(u8, key_int + 0x40));
            const buf = [_]u8{ 'C', '-', ch };
            return std.fmt.formatBuf(&buf, options, writer);
        } else if (key_int < @enumToInt(Key.meta_nil)) {
            const buf = [_]u8{@intCast(u8, key_int)};
            // This should be printed as C-? or <backspace>, it's dealt with in
            // format()
            std.debug.assert(buf[0] != '\x7F');
            return std.fmt.formatBuf(&buf, options, writer);
        } else if (key_int <= @enumToInt(Key.meta_max_char)) {
            try std.fmt.formatBuf("M-", options, writer);
            return Key.format(
                @intToEnum(Key, key_int - @enumToInt(Key.meta_nil)),
                "",
                options,
                writer,
            );
        } else {
            unreachable;
        }
    }
};