dte test coverage


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 50.0% high: ≥ 85.0%
Coverage Exec / Excl / Total
Lines: 100.0% 87 / 0 / 87
Functions: 100.0% 7 / 0 / 7
Branches: 96.3% 52 / 6 / 60

src/terminal/key.c
Line Branch Exec Source
1 #include <string.h>
2 #include "key.h"
3 #include "util/array.h"
4 #include "util/ascii.h"
5 #include "util/debug.h"
6 #include "util/numtostr.h"
7 #include "util/str-util.h"
8 #include "util/unicode.h"
9 #include "util/utf8.h"
10
11 #define K(key) [key - KEY_SPECIAL_MIN]
12
13 static const char special_names[][10] = {
14 K(KEY_ESCAPE) = "escape",
15 K(KEY_BACKSPACE) = "backspace",
16 K(KEY_INSERT) = "insert",
17 K(KEY_DELETE) = "delete",
18 K(KEY_HOME) = "home",
19 K(KEY_END) = "end",
20 K(KEY_PAGE_UP) = "pgup",
21 K(KEY_PAGE_DOWN) = "pgdown",
22 K(KEY_UP) = "up",
23 K(KEY_DOWN) = "down",
24 K(KEY_RIGHT) = "right",
25 K(KEY_LEFT) = "left",
26 K(KEY_BEGIN) = "begin",
27 K(KEY_SCROLL_LOCK) = "scrlock",
28 K(KEY_PRINT_SCREEN) = "print",
29 K(KEY_PAUSE) = "pause",
30 K(KEY_MENU) = "menu",
31 K(KEY_F1) = "F1",
32 K(KEY_F2) = "F2",
33 K(KEY_F3) = "F3",
34 K(KEY_F4) = "F4",
35 K(KEY_F5) = "F5",
36 K(KEY_F6) = "F6",
37 K(KEY_F7) = "F7",
38 K(KEY_F8) = "F8",
39 K(KEY_F9) = "F9",
40 K(KEY_F10) = "F10",
41 K(KEY_F11) = "F11",
42 K(KEY_F12) = "F12",
43 K(KEY_F13) = "F13",
44 K(KEY_F14) = "F14",
45 K(KEY_F15) = "F15",
46 K(KEY_F16) = "F16",
47 K(KEY_F17) = "F17",
48 K(KEY_F18) = "F18",
49 K(KEY_F19) = "F19",
50 K(KEY_F20) = "F20",
51 K(KEY_F21) = "F21",
52 K(KEY_F22) = "F22",
53 K(KEY_F23) = "F23",
54 K(KEY_F24) = "F24",
55 K(KEY_F25) = "F25",
56 K(KEY_F26) = "F26",
57 K(KEY_F27) = "F27",
58 K(KEY_F28) = "F28",
59 K(KEY_F29) = "F29",
60 K(KEY_F30) = "F30",
61 K(KEY_F31) = "F31",
62 K(KEY_F32) = "F32",
63 K(KEY_F33) = "F33",
64 K(KEY_F34) = "F34",
65 K(KEY_F35) = "F35",
66 };
67
68 static const struct {
69 char name[8];
70 KeyCode key;
71 } other_keys[] = {
72 {"tab", KEY_TAB},
73 {"enter", KEY_ENTER},
74 {"space", KEY_SPACE},
75 };
76
77 24 UNITTEST {
78 24 static_assert(ARRAYLEN(special_names) == NR_SPECIAL_KEYS);
79 24 CHECK_STRING_ARRAY(special_names);
80 24 CHECK_STRUCT_ARRAY(other_keys, name);
81
82 24 static_assert(KEY_UNICODE_MAX == UNICODE_MAX_VALID_CODEPOINT);
83 24 static_assert(KEY_MASK + 1 == 1u << 21);
84 24 static_assert((KEY_MASK & MOD_MASK) == 0);
85 24 static_assert(((KEY_MASK | MOD_MASK) & KEYCODE_QUERY_REPLY_BIT) == 0);
86 24 static_assert((KEY_MASK & KEY_SPECIAL_MIN) == KEY_SPECIAL_MIN);
87 24 static_assert((KEY_MASK & KEY_SPECIAL_MAX) == KEY_SPECIAL_MAX);
88 24 static_assert((KEY_MASK & KEY_IGNORE) == KEY_IGNORE);
89 24 static_assert((KEY_MASK & KEYCODE_DETECTED_PASTE) == KEYCODE_DETECTED_PASTE);
90 24 static_assert((KEY_MASK & KEYCODE_BRACKETED_PASTE) == KEYCODE_BRACKETED_PASTE);
91 24 }
92
93 // https://www.gnu.org/software/emacs/manual/html_node/efaq/Binding-combinations-of-modifiers-and-function-keys.html
94 3687 static KeyCode modifier_from_char(char c)
95 {
96
6/6
✓ Branch 2 → 3 taken 776 times.
✓ Branch 2 → 4 taken 513 times.
✓ Branch 2 → 5 taken 20 times.
✓ Branch 2 → 6 taken 6 times.
✓ Branch 2 → 7 taken 1750 times.
✓ Branch 2 → 8 taken 622 times.
3687 switch (c) {
97 case 'C': return MOD_CTRL;
98 776 case 'M': return MOD_META;
99 513 case 'S': return MOD_SHIFT;
100 20 case 's': return MOD_SUPER;
101 6 case 'H': return MOD_HYPER;
102 }
103 1750 return 0;
104 }
105
106 1774 static size_t parse_modifiers(const char *str, KeyCode *modifiersp)
107 {
108
3/4
✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 5 taken 1772 times.
✓ Branch 3 → 4 taken 2 times.
✗ Branch 3 → 5 not taken.
1774 if (str[0] == '^' && str[1] != '\0') {
109 2 *modifiersp = MOD_CTRL;
110 2 return 1;
111 }
112
113 KeyCode modifiers = 0;
114 size_t i = 0;
115
116 5602 while (1) {
117 3687 KeyCode tmp = modifier_from_char(str[i]);
118
6/6
✓ Branch 6 → 7 taken 1937 times.
✓ Branch 6 → 10 taken 1750 times.
✓ Branch 7 → 8 taken 1917 times.
✓ Branch 7 → 10 taken 20 times.
✓ Branch 8 → 9 taken 1915 times.
✓ Branch 8 → 10 taken 2 times.
3687 if (tmp == 0 || str[i + 1] != '-' || modifiers & tmp) {
119 break;
120 }
121 1915 modifiers |= tmp;
122 1915 i += 2;
123 }
124
125 1772 *modifiersp = modifiers;
126 1772 return i;
127 }
128
129 1774 KeyCode keycode_from_str(const char *str)
130 {
131 1774 KeyCode modifiers;
132 1774 str += parse_modifiers(str, &modifiers);
133 1774 size_t len = strlen(str);
134 1774 size_t pos = 0;
135 1774 KeyCode ch = u_get_char(str, len, &pos);
136
137
3/4
✓ Branch 4 → 5 taken 1774 times.
✗ Branch 4 → 17 not taken.
✓ Branch 5 → 6 taken 814 times.
✓ Branch 5 → 17 taken 960 times.
1774 if (u_is_unicode(ch) && pos == len) {
138
2/2
✓ Branch 6 → 7 taken 806 times.
✓ Branch 6 → 24 taken 8 times.
814 if (unlikely(u_is_cntrl(ch))) {
139 return KEY_NONE;
140 }
141
2/2
✓ Branch 7 → 8 taken 10 times.
✓ Branch 7 → 12 taken 796 times.
806 if (u_is_ascii_upper(ch)) {
142
2/2
✓ Branch 8 → 9 taken 8 times.
✓ Branch 8 → 10 taken 2 times.
10 if (modifiers & MOD_CTRL) {
143 // Convert C-A to C-a
144 8 ch = ascii_tolower(ch);
145
2/2
✓ Branch 10 → 11 taken 1 time.
✓ Branch 10 → 12 taken 1 time.
2 } else if (modifiers & MOD_META) {
146 // Convert M-A to M-S-a
147 1 modifiers |= MOD_SHIFT;
148 1 ch = ascii_tolower(ch);
149 }
150 }
151 806 return modifiers | ch;
152 }
153
154
2/2
✓ Branch 18 → 13 taken 2779 times.
✓ Branch 18 → 23 taken 895 times.
3674 for (size_t i = 0; i < ARRAYLEN(other_keys); i++) {
155
2/2
✓ Branch 14 → 15 taken 65 times.
✓ Branch 14 → 16 taken 2714 times.
2779 if (ascii_streq_icase(str, other_keys[i].name)) {
156 65 return modifiers | other_keys[i].key;
157 }
158 }
159
160
2/2
✓ Branch 23 → 19 taken 9174 times.
✓ Branch 23 → 24 taken 9 times.
9183 for (size_t i = 0; i < ARRAYLEN(special_names); i++) {
161
2/2
✓ Branch 20 → 21 taken 886 times.
✓ Branch 20 → 22 taken 8288 times.
9174 if (ascii_streq_icase(str, special_names[i])) {
162 886 return modifiers | (KEY_SPECIAL_MIN + i);
163 }
164 }
165
166 return KEY_NONE;
167 }
168
169 292 static const char *lookup_other_key(KeyCode key)
170 {
171
2/2
✓ Branch 6 → 3 taken 849 times.
✓ Branch 6 → 7 taken 272 times.
1121 for (size_t i = 0; i < ARRAYLEN(other_keys); i++) {
172
2/2
✓ Branch 3 → 4 taken 20 times.
✓ Branch 3 → 5 taken 829 times.
849 if (key == other_keys[i].key) {
173 20 return other_keys[i].name;
174 }
175 }
176 return NULL;
177 }
178
179 593 static size_t modifiers_to_str(KeyCode k, char *buf)
180 {
181 593 static const struct {
182 char str[2];
183 KeyCode code;
184 } mods[] = {
185 {"C-", MOD_CTRL},
186 {"M-", MOD_META},
187 {"S-", MOD_SHIFT},
188 {"s-", MOD_SUPER},
189 {"H-", MOD_HYPER},
190 };
191
192 593 size_t pos = 0;
193
2/2
✓ Branch 7 → 3 taken 2965 times.
✓ Branch 7 → 8 taken 593 times.
3558 for (size_t i = 0; i < ARRAYLEN(mods); i++) {
194
2/2
✓ Branch 3 → 4 taken 651 times.
✓ Branch 3 → 6 taken 2314 times.
2965 if (k & mods[i].code) {
195 651 pos += copystrn(buf + pos, mods[i].str, sizeof(mods[0].str));
196 }
197 }
198
199 593 return pos;
200 }
201
202 // Writes the string representation of `k` into `buf` (which must
203 // have at least `KEYCODE_STR_BUFSIZE` bytes available) and returns
204 // the length of the written string.
205 597 size_t keycode_to_str(KeyCode k, char buf[static KEYCODE_STR_BUFSIZE])
206 {
207 597 KeyCode key = keycode_get_key(k);
208 597 KeyCode mask = MOD_MASK | KEY_MASK;
209
4/4
✓ Branch 2 → 3 taken 596 times.
✓ Branch 2 → 6 taken 1 time.
✓ Branch 3 → 4 taken 593 times.
✓ Branch 3 → 6 taken 3 times.
597 bool is_key_combo = (k & mask) == k && key <= KEY_SPECIAL_MAX;
210
211 597 if (unlikely(!is_key_combo)) {
212 4 size_t n = (k & KEYCODE_QUERY_REPLY_BIT)
213 1 ? copyliteral(buf, "QUERY REPLY; 0x")
214
2/2
✓ Branch 6 → 7 taken 1 time.
✓ Branch 6 → 8 taken 3 times.
4 : copyliteral(buf, "INVALID; 0x")
215 ;
216 4 return n + buf_umax_to_hex_str(k, buf + n, 8);
217 }
218
219 593 const char *name;
220 593 size_t pos = modifiers_to_str(k, buf);
221
222
2/2
✓ Branch 5 → 11 taken 301 times.
✓ Branch 5 → 12 taken 292 times.
593 if (key >= KEY_SPECIAL_MIN) {
223 301 name = special_names[key - KEY_SPECIAL_MIN];
224 } else {
225 292 name = lookup_other_key(key);
226
2/2
✓ Branch 12 → 13 taken 272 times.
✓ Branch 12 → 15 taken 20 times.
292 if (!name) {
227 272 pos += u_set_char(buf + pos, key);
228 272 buf[pos] = '\0';
229 272 return pos;
230 }
231 }
232
233 321 BUG_ON(name[0] == '\0');
234 321 char *end = memccpy(buf + pos, name, '\0', KEYCODE_STR_BUFSIZE - pos);
235 321 BUG_ON(!end);
236 321 BUG_ON(end <= buf);
237 321 return (size_t)(end - buf) - 1;
238 }
239