Fix the help tests
[tig.git] / src / keys.c
blobe405320b9a3c77b89e71c3ed00bb1ad4f238d27b
1 /* Copyright (c) 2006-2015 Jonas Fonseca <jonas.fonseca@gmail.com>
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU General Public License as
5 * published by the Free Software Foundation; either version 2 of
6 * the License, or (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
14 #include "tig/tig.h"
15 #include "tig/types.h"
16 #include "tig/argv.h"
17 #include "tig/io.h"
18 #include "tig/keys.h"
20 struct keybinding {
21 enum request request;
22 size_t keys;
23 struct key key[1];
26 static struct keymap keymaps[] = {
27 { "generic" },
28 { "search" },
29 #define VIEW_KEYMAP(id, name) { #name }
30 VIEW_INFO(VIEW_KEYMAP)
33 static struct keymap *generic_keymap = keymaps;
34 #define is_generic_keymap(keymap) ((keymap) == generic_keymap)
36 static struct keymap *search_keymap = keymaps + 1;
37 #define is_search_keymap(keymap) ((keymap) == search_keymap)
39 struct keymap *
40 get_keymap(const char *name, size_t namelen)
42 int i;
44 for (i = 0; i < ARRAY_SIZE(keymaps); i++)
45 if (!strncasecmp(keymaps[i].name, name, namelen))
46 return &keymaps[i];
48 return NULL;
51 static bool
52 keybinding_matches(const struct keybinding *keybinding, const struct key key[],
53 size_t keys, bool *conflict_ptr)
55 bool conflict = false;
56 int i;
58 if (keybinding->keys < keys)
59 return false;
61 for (i = 0; i < keys; i++) {
62 const struct key *key1 = &keybinding->key[i];
63 const struct key *key2 = &key[i];
65 if (key1->modifiers.control &&
66 key1->modifiers.multibytes &&
67 !memcmp(&key1->modifiers, &key2->modifiers, sizeof(key1->modifiers)) &&
68 strlen(key1->data.bytes) == 1 &&
69 strlen(key2->data.bytes) == 1) {
70 int c1 = key1->data.bytes[0];
71 int c2 = key2->data.bytes[0];
73 if (ascii_toupper(c1) != ascii_toupper(c2))
74 return false;
75 if (c1 != c2)
76 conflict = true;
77 } else {
78 if (memcmp(key1, key2, sizeof(*key1)))
79 return false;
83 if (conflict_ptr && keybinding->request != REQ_NONE)
84 *conflict_ptr = conflict;
85 return true;
88 static bool
89 keybinding_equals(const struct keybinding *keybinding, const struct key key[],
90 size_t keys, bool *conflict_ptr)
92 if (keybinding->keys != keys)
93 return false;
94 return keybinding_matches(keybinding, key, keys, conflict_ptr);
97 enum status_code
98 add_keybinding(struct keymap *table, enum request request,
99 const struct key key[], size_t keys)
101 struct keybinding *keybinding;
102 char buf[SIZEOF_STR];
103 bool conflict = false;
104 size_t i;
106 for (i = 0; i < table->size; i++) {
107 if (keybinding_equals(table->data[i], key, keys, &conflict)) {
108 enum request old_request = table->data[i]->request;
109 const char *old_name;
111 table->data[i]->request = request;
112 if (!conflict)
113 return SUCCESS;
115 old_name = get_request_name(old_request);
116 string_ncopy(buf, old_name, strlen(old_name));
117 return error("Key binding for %s and %s conflict; "
118 "keys using Ctrl are case insensitive",
119 buf, get_request_name(request));
123 table->data = realloc(table->data, (table->size + 1) * sizeof(*table->data));
124 keybinding = calloc(1, sizeof(*keybinding) + (sizeof(*key) * (keys - 1)));
125 if (!table->data || !keybinding)
126 die("Failed to allocate keybinding");
128 memcpy(keybinding->key, key, sizeof(*key) * keys);
129 keybinding->keys = keys;
130 keybinding->request = request;
131 table->data[table->size++] = keybinding;
132 return SUCCESS;
135 static enum request
136 get_keybinding_in_keymap(const struct keymap *keymap, const struct key key[], size_t keys, int *matches)
138 enum request request = REQ_UNKNOWN;
139 size_t i;
141 for (i = 0; i < keymap->size; i++)
142 if (keybinding_matches(keymap->data[i], key, keys, NULL)) {
143 if (matches && keymap->data[i]->request != REQ_NONE)
144 (*matches)++;
145 /* Overriding keybindings, might have been added
146 * at the end of the keymap so we need to
147 * iterate all keybindings. */
148 if (keymap->data[i]->keys == keys)
149 request = keymap->data[i]->request;
152 return request;
155 /* Looks for a key binding first in the given keymap, then in the generic keymap. */
156 enum request
157 get_keybinding(const struct keymap *keymap, const struct key key[], size_t keys, int *matches)
159 enum request request = get_keybinding_in_keymap(keymap, key, keys, matches);
161 if (!is_search_keymap(keymap)) {
162 int generic_matches = 0;
163 enum request generic_request = get_keybinding_in_keymap(generic_keymap, key, keys, &generic_matches);
165 /* Include generic matches iff there are more than one
166 * so unbound keys in the current keymap still override
167 * generic keys while still ensuring that the key combo
168 * handler continues to wait for more keys if there is
169 * another possible match. E.g. while in `main` view:
171 * bind generic q quit # 'q' will quit
172 * bind main q none # 'q' will do nothing
173 * bind generic qa quit # 'qa' will quit
174 * bind main qn next # 'qn' will move to next entry
176 if (matches && (request == REQ_UNKNOWN || generic_matches > 1))
177 (*matches) += generic_matches;
178 if (request == REQ_UNKNOWN)
179 request = generic_request;
182 return request == REQ_NONE ? REQ_UNKNOWN : request;
186 struct key_mapping {
187 const char *name;
188 int value;
191 static const struct key_mapping key_mappings[] = {
192 { "Enter", KEY_RETURN },
193 { "Space", ' ' },
194 { "Backspace", KEY_BACKSPACE },
195 { "Tab", KEY_TAB },
196 { "Escape", KEY_ESC },
197 { "Esc", KEY_ESC },
198 { "Left", KEY_LEFT },
199 { "Right", KEY_RIGHT },
200 { "Up", KEY_UP },
201 { "Down", KEY_DOWN },
202 { "Insert", KEY_IC },
203 { "Ins", KEY_IC },
204 { "Delete", KEY_DC },
205 { "Del", KEY_DC },
206 { "Hash", '#' },
207 { "Home", KEY_HOME },
208 { "End", KEY_END },
209 { "PageUp", KEY_PPAGE },
210 { "PgUp", KEY_PPAGE },
211 { "PageDown", KEY_NPAGE },
212 { "PgDown", KEY_NPAGE },
213 { "LessThan", '<' },
214 { "LT", '<' },
215 { "F1", KEY_F(1) },
216 { "F2", KEY_F(2) },
217 { "F3", KEY_F(3) },
218 { "F4", KEY_F(4) },
219 { "F5", KEY_F(5) },
220 { "F6", KEY_F(6) },
221 { "F7", KEY_F(7) },
222 { "F8", KEY_F(8) },
223 { "F9", KEY_F(9) },
224 { "F10", KEY_F(10) },
225 { "F11", KEY_F(11) },
226 { "F12", KEY_F(12) },
227 { "ScrollBack", KEY_SR },
228 { "SBack", KEY_SR },
229 { "ScrollFwd", KEY_SF },
230 { "SFwd", KEY_SF },
233 static const struct key_mapping *
234 get_key_mapping(const char *name, size_t namelen)
236 int i;
238 for (i = 0; i < ARRAY_SIZE(key_mappings); i++) {
239 if (namelen == strlen(key_mappings[i].name) &&
240 !strncasecmp(key_mappings[i].name, name, namelen))
241 return &key_mappings[i];
244 return NULL;
247 static enum status_code
248 parse_key_value(struct key *key, const char **name_ptr, size_t offset,
249 const char *replacement, const char *end)
251 const char *name = replacement ? replacement : *name_ptr + offset;
252 size_t namelen = utf8_char_length(name);
253 const char *nameend = name + namelen;
255 if (strlen(name) < namelen || utf8_to_unicode(name, namelen) == 0)
256 return error("Error parsing UTF-8 bytes: %s", name);
258 strncpy(key->data.bytes, name, namelen);
259 key->modifiers.multibytes = 1;
260 if (end) {
261 *name_ptr = end + 1;
262 if (!replacement && nameend + 1 < end)
263 return success("Ignoring text after key mapping: %.*s",
264 (int) (end - nameend), nameend);
265 } else {
266 *name_ptr = nameend;
269 return SUCCESS;
272 enum status_code
273 get_key_value(const char **name_ptr, struct key *key)
275 const char *name = *name_ptr;
276 const char *end = NULL;
278 memset(key, 0, sizeof(*key));
280 if (*name == '<') {
281 end = strchr(name + 1, '>');
282 if (!end)
283 return error("Missing '>' from key mapping: %s", name);
285 if (!prefixcmp(name, "<Ctrl-")) {
286 key->modifiers.control = 1;
287 return parse_key_value(key, name_ptr, 6, NULL, end);
289 } else if (!prefixcmp(name, "<C-")) {
290 key->modifiers.control = 1;
291 return parse_key_value(key, name_ptr, 3, NULL, end);
293 } else {
294 const struct key_mapping *mapping;
295 const char *start = name + 1;
296 int len = end - start;
298 mapping = get_key_mapping(start, len);
299 if (!mapping)
300 return error("Unknown key mapping: %.*s", len, start);
302 if (mapping->value == ' ')
303 return parse_key_value(key, name_ptr, 0, " ", end);
305 if (mapping->value == '#')
306 return parse_key_value(key, name_ptr, 0, "#", end);
308 if (mapping->value == '<')
309 return parse_key_value(key, name_ptr, 0, "<", end);
311 *name_ptr = end + 1;
312 key->data.value = mapping->value;
313 return SUCCESS;
317 if (name[0] == '^' && name[1] == '[') {
318 return error("Escape key combo must now use '<Esc>%s' "
319 "instead of '%s'", name + 2, name);
320 } else if (name[0] == '^' && name[1] != '\0') {
321 return error("Control key mapping must now use '<Ctrl-%s>' "
322 "instead of '%s'", name + 1, name);
325 return parse_key_value(key, name_ptr, 0, NULL, end);
328 const char *
329 get_key_name(const struct key key[], size_t keys, bool quote_comma)
331 static char buf[SIZEOF_STR];
332 size_t pos = 0;
333 int i;
335 for (i = 0; i < keys; i++) {
336 bool multibytes = key[i].modifiers.multibytes;
337 const char *name = multibytes ? key[i].data.bytes : "";
338 const char *start = "";
339 const char *end = "";
340 bool use_symbolic;
342 if (key[i].modifiers.control) {
343 start = "<Ctrl-";
344 end = ">";
345 } else if (*name == ',' && quote_comma) {
346 /* Quote commas so they stand out in the help view. */
347 start = "'";
348 end = "'";
351 /* Use symbolic name for spaces so they are readable. */
352 use_symbolic = !*name || *name == ' ';
353 /* When listing keys for :save-options quote illegal characters. */
354 if (!quote_comma && (*name == '<' || *name == '#'))
355 use_symbolic = true;
357 if (use_symbolic) {
358 int value = *name ? *name : key[i].data.value;
359 int j;
361 name = "<?>";
362 for (j = 0; j < ARRAY_SIZE(key_mappings); j++)
363 if (key_mappings[j].value == value) {
364 start = "<";
365 end = ">";
366 name = key_mappings[j].name;
367 break;
371 if (!string_format_from(buf, &pos, "%s%s%s", start, name, end))
372 return "(no key)";
375 return buf;
378 static bool
379 append_key(char *buf, size_t *pos, const struct keybinding *keybinding, bool all)
381 const char *sep = *pos > 0 ? ", " : "";
382 const char *keyname = get_key_name(keybinding->key, keybinding->keys, all);
384 return string_nformat(buf, BUFSIZ, pos, "%s%s", sep, keyname);
387 static bool
388 append_keymap_request_keys(char *buf, size_t *pos, enum request request,
389 const struct keymap *keymap, bool all)
391 int i;
393 for (i = 0; i < keymap->size; i++) {
394 if (keymap->data[i]->request == request) {
395 if (!append_key(buf, pos, keymap->data[i], all))
396 return false;
397 if (!all)
398 break;
402 return true;
405 const char *
406 get_keys(const struct keymap *keymap, enum request request, bool all)
408 static char buf[BUFSIZ];
409 size_t pos = 0;
411 buf[pos] = 0;
413 if (!append_keymap_request_keys(buf, &pos, request, keymap, all))
414 return "Too many keybindings!";
415 if (pos > 0 && !all)
416 return buf;
418 if (!is_generic_keymap(keymap)) {
419 /* Only the generic keymap includes the default keybindings when
420 * listing all keys. */
421 if (all)
422 return buf;
424 if (!append_keymap_request_keys(buf, &pos, request, generic_keymap, all))
425 return "Too many keybindings!";
426 if (pos)
427 return buf;
430 return buf;
433 static struct run_request *run_request;
434 static size_t run_requests;
436 DEFINE_ALLOCATOR(realloc_run_requests, struct run_request, 8)
438 #define COMMAND_FLAGS ":!?@<"
440 enum status_code
441 parse_run_request_flags(struct run_request_flags *flags, const char **argv)
443 if (!strchr(COMMAND_FLAGS, *argv[0]))
444 return error("Unknown command flag '%c'; expected one of %s", argv[0][0], COMMAND_FLAGS);
446 while (*argv[0]) {
447 if (*argv[0] == ':') {
448 flags->internal = 1;
449 argv[0]++;
450 break;
451 } else if (*argv[0] == '@') {
452 flags->silent = 1;
453 } else if (*argv[0] == '?') {
454 flags->confirm = 1;
455 } else if (*argv[0] == '<') {
456 flags->exit = 1;
457 } else if (*argv[0] != '!') {
458 break;
460 argv[0]++;
463 return SUCCESS;
466 enum status_code
467 add_run_request(struct keymap *keymap, const struct key key[],
468 size_t keys, const char **argv)
470 struct run_request *req;
471 struct run_request_flags flags = {0};
472 enum status_code code = parse_run_request_flags(&flags, argv);
474 if (code != SUCCESS)
475 return code;
477 if (!realloc_run_requests(&run_request, run_requests, 1))
478 return ERROR_OUT_OF_MEMORY;
480 if (!argv_copy(&run_request[run_requests].argv, argv))
481 return ERROR_OUT_OF_MEMORY;
483 req = &run_request[run_requests++];
484 req->flags = flags;
485 req->keymap = keymap;
487 return add_keybinding(keymap, REQ_RUN_REQUESTS + run_requests, key, keys);
490 struct run_request *
491 get_run_request(enum request request)
493 if (request <= REQ_RUN_REQUESTS || request > REQ_RUN_REQUESTS + run_requests)
494 return NULL;
495 return &run_request[request - REQ_RUN_REQUESTS - 1];
498 const char *
499 format_run_request_flags(const struct run_request *req)
501 static char flags[8];
502 int flagspos = 0;
504 memset(flags, 0, sizeof(flags));
506 if (req->flags.internal)
507 flags[flagspos++] = ':';
508 else
509 flags[flagspos] = '!'; /* Optional, if other flags are defined */
511 if (req->flags.silent)
512 flags[flagspos++] = '@';
513 if (req->flags.confirm)
514 flags[flagspos++] = '?';
515 if (req->flags.exit)
516 flags[flagspos++] = '<';
517 if (flagspos > 1)
518 flags[flagspos++] = 0;
520 return flags;
523 struct key_visitor_state {
524 key_visitor_fn visitor;
525 void *data;
526 struct keymap *keymap;
527 bool combine_keys;
528 const char *group;
531 static bool
532 foreach_key_visit(struct key_visitor_state *state, const char *group,
533 enum request request,
534 const struct request_info *req_info, const struct run_request *run_req)
536 struct keymap *keymap = state->keymap;
537 int i;
539 if (state->group == group)
540 group = NULL;
542 if (state->combine_keys) {
543 const char *key = get_keys(keymap, request, true);
545 if (!key || !*key)
546 return true;
548 if (group)
549 state->group = group;
550 return state->visitor(state->data, group, keymap, request,
551 key, req_info, run_req);
554 for (i = 0; i < keymap->size; i++) {
555 if (keymap->data[i]->request == request) {
556 struct keybinding *keybinding = keymap->data[i];
557 const char *key = get_key_name(keybinding->key, keybinding->keys, false);
559 if (!key || !*key)
560 continue;
562 if (!state->visitor(state->data, group, keymap, request,
563 key, req_info, run_req))
564 return false;
566 if (group)
567 state->group = group;
568 group = NULL;
572 return true;
575 static bool
576 foreach_key_request(void *data, const struct request_info *req_info, const char *group)
578 struct key_visitor_state *state = data;
580 if (req_info->request == REQ_NONE)
581 return true;
583 return foreach_key_visit(state, group, req_info->request, req_info, NULL);
586 static bool
587 foreach_key_run_request(struct key_visitor_state *state, bool internal, bool toggles)
589 struct keymap *keymap = state->keymap;
590 const char *group = !internal ? "External commands:" :
591 toggles ? "Option toggling:" :
592 "Internal commands:";
593 enum request request = REQ_RUN_REQUESTS + 1;
595 for (; true; request++) {
596 struct run_request *req = get_run_request(request);
597 const char *key;
599 if (!req)
600 break;
602 if (req->flags.internal != !!internal ||
603 req->keymap != keymap ||
604 !*(key = get_keys(keymap, request, true)))
605 continue;
607 if (toggles != !strcmp(req->argv[0], "toggle"))
608 continue;
610 if (!foreach_key_visit(state, group, request, NULL, req))
611 return false;
614 return true;
617 bool
618 foreach_key(key_visitor_fn visitor, void *data, bool combine_keys)
620 int i;
622 for (i = 0; i < ARRAY_SIZE(keymaps); i++) {
623 struct key_visitor_state state = { visitor, data, &keymaps[i], combine_keys };
625 if (!foreach_request(foreach_key_request, &state)
626 || !foreach_key_run_request(&state, true, true)
627 || !foreach_key_run_request(&state, true, false)
628 || !foreach_key_run_request(&state, false, false))
629 return false;
632 return true;
635 /* vim: set ts=8 sw=8 noexpandtab: */