summaryrefslogtreecommitdiff
path: root/src/key.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/key.zig')
-rw-r--r--src/key.zig158
1 files changed, 106 insertions, 52 deletions
diff --git a/src/key.zig b/src/key.zig
index 5376017..d508ddf 100644
--- a/src/key.zig
+++ b/src/key.zig
@@ -1,34 +1,27 @@
1const std = @import("std"); 1const std = @import("std");
2 2
3pub const Key = enum(u16) { 3pub const Key = enum(u16) {
4 tab = 0x09,
4 return_ = 0x0d, 5 return_ = 0x0d,
5 escape = 0x1b, 6 escape = 0x1b,
6 space = 0x20, 7 space = 0x20,
7 backspace = 0x7f, 8 backspace = 0x7f,
8 max_char = 0xff, 9 max_char = 0xff,
9 10
10 meta_nil = 0x100,
11 meta_max_char = 0x1ff,
12
13 left,
14 right,
15 up, 11 up,
16 down, 12 down,
17 home, 13 right,
14 left,
18 end, 15 end,
16 home,
17 insert,
19 delete, 18 delete,
20 page_up, 19 page_up,
21 page_down, 20 page_down,
22 21
23 ctrl_left, 22 mod_shft = 0x1000,
24 ctrl_right, 23 mod_meta = 0x2000,
25 ctrl_up, 24 mod_ctrl = 0x4000,
26 ctrl_down,
27 ctrl_home,
28 ctrl_end,
29 // ctrl_delete,
30 // ctrl_page_up,
31 // ctrl_page_down,
32 25
33 _, 26 _,
34 27
@@ -36,8 +29,59 @@ pub const Key = enum(u16) {
36 return @intToEnum(Key, ch); 29 return @intToEnum(Key, ch);
37 } 30 }
38 31
39 pub fn ctrl(ch: u8) Key { 32 pub fn shift(k: anytype) Key {
40 return @intToEnum(Key, ch & 0x1f); 33 return Key.mod(.mod_shft, Key.ify(k));
34 }
35
36 pub fn meta(k: anytype) Key {
37 return Key.mod(.mod_meta, Key.ify(k));
38 }
39
40 pub fn ctrl(k: anytype) Key {
41 return Key.mod(.mod_ctrl, Key.ify(k));
42 }
43
44 /// Key.ify == Keyify :)
45 fn ify(k: anytype) Key {
46 return switch (@TypeOf(k)) {
47 comptime_int, u8 => Key.char(k),
48 Key => k,
49 else => unreachable,
50 };
51 }
52
53 fn mod(comptime modifier: Key, k: Key) Key {
54 comptime std.debug.assert(
55 modifier == .mod_shft
56 or modifier == .mod_meta
57 or modifier == .mod_ctrl
58 );
59
60 const shft_int = @enumToInt(Key.mod_shft);
61 const meta_int = @enumToInt(Key.mod_meta);
62 const ctrl_int = @enumToInt(Key.mod_ctrl);
63
64 const max_char_int = @enumToInt(Key.max_char);
65
66 const mod_int = @enumToInt(modifier);
67 const k_int = @enumToInt(k);
68 if (k_int & mod_int == mod_int) {
69 return k;
70 }
71
72 const k_origmod = k_int & (shft_int | meta_int | ctrl_int);
73 const k_nomod = k_int & ~k_origmod;
74 if (k_nomod <= max_char_int) {
75 // Appending S- to a character is not smart
76 std.debug.assert(modifier != .mod_shft);
77 return switch (modifier) {
78 .mod_meta => @intToEnum(Key, k_int | meta_int),
79 .mod_ctrl => @intToEnum(Key, k_origmod | (k_nomod & 0x1f)),
80 else => unreachable,
81 };
82 } else {
83 return @intToEnum(Key, k_int | mod_int);
84 }
41 } 85 }
42 86
43 pub fn format( 87 pub fn format(
@@ -51,70 +95,80 @@ pub const Key = enum(u16) {
51 }; 95 };
52 96
53 return switch (key) { 97 return switch (key) {
98 .tab => std.fmt.formatBuf("<tab>", options, writer),
54 .return_ => std.fmt.formatBuf("<return>", options, writer), 99 .return_ => std.fmt.formatBuf("<return>", options, writer),
55 .escape => std.fmt.formatBuf("<escape>", options, writer), 100 .escape => std.fmt.formatBuf("<escape>", options, writer),
56 .space => std.fmt.formatBuf("<space>", options, writer), 101 .space => std.fmt.formatBuf("<space>", options, writer),
57 .backspace => std.fmt.formatBuf("<backspace>", options, writer), 102 .backspace => std.fmt.formatBuf("<backspace>", options, writer),
58 .max_char => key.formatGeneric(options, writer), 103 .max_char => key.formatGeneric(options, writer),
59 104
60 .meta_nil, .meta_max_char => key.formatGeneric(options, writer),
61
62 .left => std.fmt.formatBuf("<left>", options, writer),
63 .right => std.fmt.formatBuf("<right>", options, writer),
64 .up => std.fmt.formatBuf("<up>", options, writer), 105 .up => std.fmt.formatBuf("<up>", options, writer),
65 .down => std.fmt.formatBuf("<down>", options, writer), 106 .down => std.fmt.formatBuf("<down>", options, writer),
66 .home => std.fmt.formatBuf("<home>", options, writer), 107 .right => std.fmt.formatBuf("<right>", options, writer),
108 .left => std.fmt.formatBuf("<left>", options, writer),
67 .end => std.fmt.formatBuf("<end>", options, writer), 109 .end => std.fmt.formatBuf("<end>", options, writer),
110 .home => std.fmt.formatBuf("<home>", options, writer),
111 .insert => std.fmt.formatBuf("<insert>", options, writer),
68 .delete => std.fmt.formatBuf("<delete>", options, writer), 112 .delete => std.fmt.formatBuf("<delete>", options, writer),
69 .page_up => std.fmt.formatBuf("<page-up>", options, writer), 113 .page_up => std.fmt.formatBuf("<page-up>", options, writer),
70 .page_down => std.fmt.formatBuf("<page-down>", options, writer), 114 .page_down => std.fmt.formatBuf("<page-down>", options, writer),
71 115
72 .ctrl_left => std.fmt.formatBuf("C-<left>", options, writer), 116 .mod_shft, .mod_meta, .mod_ctrl => key.formatGeneric(options, writer),
73 .ctrl_right => std.fmt.formatBuf("C-<right>", options, writer),
74 .ctrl_up => std.fmt.formatBuf("C-<up>", options, writer),
75 .ctrl_down => std.fmt.formatBuf("C-<down>", options, writer),
76 .ctrl_home => std.fmt.formatBuf("C-<home>", options, writer),
77 .ctrl_end => std.fmt.formatBuf("C-<end>", options, writer),
78 // ctrl_delete
79 // ctrl_page_up
80 // ctrl_page_down
81
82 _ => key.formatGeneric(options, writer), 117 _ => key.formatGeneric(options, writer),
83 }; 118 };
84 } 119 }
85 120
86 pub fn meta(ch: u8) Key {
87 return @intToEnum(Key, ch + @enumToInt(Key.meta_nil));
88 }
89
90 pub fn metaCtrl(ch: u8) Key {
91 return @intToEnum(Key, (ch & 0x1f) + @enumToInt(Key.meta_nil));
92 }
93
94 fn formatGeneric( 121 fn formatGeneric(
95 key: Key, 122 key: Key,
96 options: std.fmt.FormatOptions, 123 options: std.fmt.FormatOptions,
97 writer: anytype, 124 writer: anytype,
98 ) @TypeOf(writer).Error!void { 125 ) @TypeOf(writer).Error!void {
126 const shft_int = @enumToInt(Key.mod_shft);
127 const meta_int = @enumToInt(Key.mod_meta);
128 const ctrl_int = @enumToInt(Key.mod_ctrl);
129
99 const key_int = @enumToInt(key); 130 const key_int = @enumToInt(key);
100 if (key_int < @enumToInt(Key.space)) { 131 if (key_int & shft_int == shft_int) {
101 const ch = std.ascii.toLower(@intCast(u8, key_int + 0x40)); 132 try std.fmt.formatBuf("S-", options, writer);
102 const buf = [_]u8{ 'C', '-', ch }; 133 return Key.format(
103 return std.fmt.formatBuf(&buf, options, writer); 134 @intToEnum(Key, key_int & ~shft_int),
104 } else if (key_int < @enumToInt(Key.meta_nil)) { 135 "",
105 const buf = [_]u8{@intCast(u8, key_int)}; 136 options,
106 // This should be printed as C-? or <backspace>, it's dealt with in 137 writer,
107 // format() 138 );
108 std.debug.assert(buf[0] != '\x7F'); 139 } else if (key_int & meta_int == meta_int) {
109 return std.fmt.formatBuf(&buf, options, writer);
110 } else if (key_int <= @enumToInt(Key.meta_max_char)) {
111 try std.fmt.formatBuf("M-", options, writer); 140 try std.fmt.formatBuf("M-", options, writer);
112 return Key.format( 141 return Key.format(
113 @intToEnum(Key, key_int - @enumToInt(Key.meta_nil)), 142 @intToEnum(Key, key_int & ~meta_int),
143 "",
144 options,
145 writer,
146 );
147 } else if (key_int & ctrl_int == ctrl_int) {
148 try std.fmt.formatBuf("C-", options, writer);
149 return Key.format(
150 @intToEnum(Key, key_int & ~ctrl_int),
114 "", 151 "",
115 options, 152 options,
116 writer, 153 writer,
117 ); 154 );
155 } else if (key_int < 0x20) {
156 try std.fmt.formatBuf("C-", options, writer);
157 return Key.format(
158 Key.char(@intCast(u8, key_int + 0x40)),
159 "",
160 options,
161 writer
162 );
163 } else if (key_int < 0x100) {
164 const ch = @intCast(u8, key_int);
165 if (std.ascii.isGraph(ch)) {
166 return writer.writeByte(ch);
167 } else {
168 try writer.writeAll("<\\x");
169 try std.fmt.formatIntValue(ch, "X", options, writer);
170 return writer.writeAll(">");
171 }
118 } else { 172 } else {
119 unreachable; 173 unreachable;
120 } 174 }