Add doc example for disabling automatic use of topo-order for the graph
[tig.git] / src / keys.c
blob8180abf0fcefb06b1944293ac652e91a41a949f3
1 /* Copyright (c) 2006-2014 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 #define VIEW_KEYMAP(id, name) { #name }
29 VIEW_INFO(VIEW_KEYMAP)
32 static struct keymap *generic_keymap = keymaps;
33 #define is_generic_keymap(keymap) ((keymap) == generic_keymap)
35 struct keymap *
36 get_keymap_by_index(int i)
38 return 0 <= i && i < ARRAY_SIZE(keymaps) ? &keymaps[i] : NULL;
41 struct keymap *
42 get_keymap(const char *name, size_t namelen)
44 int i;
46 for (i = 0; i < ARRAY_SIZE(keymaps); i++)
47 if (!strncasecmp(keymaps[i].name, name, namelen))
48 return &keymaps[i];
50 return NULL;
53 static bool
54 keybinding_equals(struct keybinding *keybinding, struct key key[],
55 size_t keys, bool *conflict_ptr)
57 bool conflict = FALSE;
58 int i;
60 if (keybinding->keys != keys)
61 return FALSE;
63 for (i = 0; i < keys; i++) {
64 struct key *key1 = &keybinding->key[i];
65 struct key *key2 = &key[i];
67 if (key1->modifiers.control &&
68 key1->modifiers.multibytes &&
69 !memcmp(&key1->modifiers, &key2->modifiers, sizeof(key1->modifiers)) &&
70 strlen(key1->data.bytes) == 1 &&
71 strlen(key2->data.bytes) == 1) {
72 int c1 = key1->data.bytes[0];
73 int c2 = key2->data.bytes[0];
75 if (ascii_toupper(c1) != ascii_toupper(c2))
76 return FALSE;
77 if (c1 != c2)
78 conflict = TRUE;
79 } else {
80 if (memcmp(key1, key2, sizeof(*key1)))
81 return FALSE;
85 if (conflict_ptr)
86 *conflict_ptr = conflict;
87 return TRUE;
90 enum status_code
91 add_keybinding(struct keymap *table, enum request request,
92 struct key key[], size_t keys)
94 struct keybinding *keybinding;
95 char buf[SIZEOF_STR];
96 bool conflict = FALSE;
97 size_t i;
99 for (i = 0; i < table->size; i++) {
100 if (keybinding_equals(table->data[i], key, keys, &conflict)) {
101 enum request old_request = table->data[i]->request;
102 const char *old_name;
104 table->data[i]->request = request;
105 if (!conflict)
106 return SUCCESS;
108 old_name = get_request_name(old_request);
109 string_ncopy(buf, old_name, strlen(old_name));
110 return error("Key binding for %s and %s conflict; "
111 "keys using Ctrl are case insensitive",
112 buf, get_request_name(request));
116 table->data = realloc(table->data, (table->size + 1) * sizeof(*table->data));
117 keybinding = calloc(1, sizeof(*keybinding) + (sizeof(*key) * (keys - 1)));
118 if (!table->data || !keybinding)
119 die("Failed to allocate keybinding");
121 memcpy(keybinding->key, key, sizeof(*key) * keys);
122 keybinding->keys = keys;
123 keybinding->request = request;
124 table->data[table->size++] = keybinding;
125 return SUCCESS;
128 /* Looks for a key binding first in the given map, then in the generic map, and
129 * lastly in the default keybindings. */
130 enum request
131 get_keybinding(struct keymap *keymap, struct key key[], size_t keys)
133 size_t i;
135 for (i = 0; i < keymap->size; i++)
136 if (keybinding_equals(keymap->data[i], key, keys, NULL))
137 return keymap->data[i]->request;
139 for (i = 0; i < generic_keymap->size; i++)
140 if (keybinding_equals(generic_keymap->data[i], key, keys, NULL))
141 return generic_keymap->data[i]->request;
143 return REQ_NONE;
147 struct key_mapping {
148 const char *name;
149 int value;
152 static const struct key_mapping key_mappings[] = {
153 { "Enter", KEY_RETURN },
154 { "Space", ' ' },
155 { "Backspace", KEY_BACKSPACE },
156 { "Tab", KEY_TAB },
157 { "Escape", KEY_ESC },
158 { "Esc", KEY_ESC },
159 { "Left", KEY_LEFT },
160 { "Right", KEY_RIGHT },
161 { "Up", KEY_UP },
162 { "Down", KEY_DOWN },
163 { "Insert", KEY_IC },
164 { "Ins", KEY_IC },
165 { "Delete", KEY_DC },
166 { "Del", KEY_DC },
167 { "Hash", '#' },
168 { "Home", KEY_HOME },
169 { "End", KEY_END },
170 { "PageUp", KEY_PPAGE },
171 { "PgUp", KEY_PPAGE },
172 { "PageDown", KEY_NPAGE },
173 { "PgDown", KEY_NPAGE },
174 { "LessThan", '<' },
175 { "LT", '<' },
176 { "F1", KEY_F(1) },
177 { "F2", KEY_F(2) },
178 { "F3", KEY_F(3) },
179 { "F4", KEY_F(4) },
180 { "F5", KEY_F(5) },
181 { "F6", KEY_F(6) },
182 { "F7", KEY_F(7) },
183 { "F8", KEY_F(8) },
184 { "F9", KEY_F(9) },
185 { "F10", KEY_F(10) },
186 { "F11", KEY_F(11) },
187 { "F12", KEY_F(12) },
188 { "ScrollBack", KEY_SR },
189 { "SBack", KEY_SR },
190 { "ScrollFwd", KEY_SF },
191 { "SFwd", KEY_SF },
194 static const struct key_mapping *
195 get_key_mapping(const char *name, size_t namelen)
197 int i;
199 for (i = 0; i < ARRAY_SIZE(key_mappings); i++) {
200 if (namelen == strlen(key_mappings[i].name) &&
201 !strncasecmp(key_mappings[i].name, name, namelen))
202 return &key_mappings[i];
205 return NULL;
208 static enum status_code
209 parse_key_value(struct key *key, const char **name_ptr, size_t offset,
210 const char *replacement, const char *end)
212 const char *name = replacement ? replacement : *name_ptr + offset;
213 size_t namelen = utf8_char_length(name);
214 const char *nameend = name + namelen;
216 if (strlen(name) < namelen || utf8_to_unicode(name, namelen) == 0)
217 return error("Error parsing UTF-8 bytes: %s", name);
219 strncpy(key->data.bytes, name, namelen);
220 key->modifiers.multibytes = 1;
221 if (end) {
222 *name_ptr = end + 1;
223 if (!replacement && nameend + 1 < end)
224 return success("Ignoring text after key mapping: %.*s",
225 (int) (end - nameend), nameend);
226 } else {
227 *name_ptr = nameend;
230 return SUCCESS;
233 enum status_code
234 get_key_value(const char **name_ptr, struct key *key)
236 const char *name = *name_ptr;
237 const char *end = NULL;
239 memset(key, 0, sizeof(*key));
241 if (*name == '<') {
242 end = strchr(name + 1, '>');
243 if (!end)
244 return error("Missing '>' from key mapping: %s", name);
246 if (!prefixcmp(name, "<Ctrl-")) {
247 key->modifiers.control = 1;
248 return parse_key_value(key, name_ptr, 6, NULL, end);
250 } else if (!prefixcmp(name, "<C-")) {
251 key->modifiers.control = 1;
252 return parse_key_value(key, name_ptr, 3, NULL, end);
254 } else {
255 const struct key_mapping *mapping;
256 const char *start = name + 1;
257 int len = end - start;
259 mapping = get_key_mapping(start, len);
260 if (!mapping)
261 return error("Unknown key mapping: %.*s", len, start);
263 if (mapping->value == ' ')
264 return parse_key_value(key, name_ptr, 0, " ", end);
266 if (mapping->value == '#')
267 return parse_key_value(key, name_ptr, 0, "#", end);
269 if (mapping->value == '<')
270 return parse_key_value(key, name_ptr, 0, "<", end);
272 if (mapping->value == KEY_ESC) {
273 size_t offset = (end - name) + 1;
275 key->modifiers.escape = 1;
276 return parse_key_value(key, name_ptr, offset, NULL, NULL);
279 *name_ptr = end + 1;
280 key->data.value = mapping->value;
281 return SUCCESS;
285 if (name[0] == '^' && name[1] == '[') {
286 return error("Escape key combo must now use '<Esc>%s' "
287 "instead of '%s'", name + 2, name);
288 } else if (name[0] == '^' && name[1] != '\0') {
289 return error("Control key mapping must now use '<Ctrl-%s>' "
290 "instead of '%s'", name + 1, name);
293 return parse_key_value(key, name_ptr, 0, NULL, end);
296 const char *
297 get_key_name(const struct key key[], size_t keys)
299 static char buf[SIZEOF_STR];
300 size_t pos = 0;
301 int i;
303 for (i = 0; i < keys; i++) {
304 bool multibytes = key[i].modifiers.multibytes;
305 const char *name = multibytes ? key[i].data.bytes : "";
306 const char *start = "";
307 const char *end = "";
309 if (key[i].modifiers.escape) {
310 start = "<Esc>";
311 } else if (key[i].modifiers.control) {
312 start = "<Ctrl-";
313 end = ">";
314 } else if (*name == ',') {
315 /* Quote commas so they stand out in the help view. */
316 start = "'";
317 end = "'";
320 /* Use symbolic name for spaces so they are readable. */
321 if (!*name || *name == ' ') {
322 int value = *name ? *name : key[i].data.value;
323 int j;
325 name = "<?>";
326 for (j = 0; j < ARRAY_SIZE(key_mappings); j++)
327 if (key_mappings[j].value == value) {
328 start = key[i].modifiers.escape ? "<Esc><" : "<";
329 end = ">";
330 name = key_mappings[j].name;
331 break;
335 if (!string_format_from(buf, &pos, "%s%s%s", start, name, end))
336 return "(no key)";
339 return buf;
342 static bool
343 append_key(char *buf, size_t *pos, const struct keybinding *keybinding)
345 const char *sep = *pos > 0 ? ", " : "";
346 const char *keyname = get_key_name(keybinding->key, keybinding->keys);
348 return string_nformat(buf, BUFSIZ, pos, "%s%s", sep, keyname);
351 static bool
352 append_keymap_request_keys(char *buf, size_t *pos, enum request request,
353 struct keymap *keymap, bool all)
355 int i;
357 for (i = 0; i < keymap->size; i++) {
358 if (keymap->data[i]->request == request) {
359 if (!append_key(buf, pos, keymap->data[i]))
360 return FALSE;
361 if (!all)
362 break;
366 return TRUE;
369 const char *
370 get_keys(struct keymap *keymap, enum request request, bool all)
372 static char buf[BUFSIZ];
373 size_t pos = 0;
375 buf[pos] = 0;
377 if (!append_keymap_request_keys(buf, &pos, request, keymap, all))
378 return "Too many keybindings!";
379 if (pos > 0 && !all)
380 return buf;
382 if (!is_generic_keymap(keymap)) {
383 /* Only the generic keymap includes the default keybindings when
384 * listing all keys. */
385 if (all)
386 return buf;
388 if (!append_keymap_request_keys(buf, &pos, request, generic_keymap, all))
389 return "Too many keybindings!";
390 if (pos)
391 return buf;
394 return buf;
397 static struct run_request *run_request;
398 static size_t run_requests;
400 DEFINE_ALLOCATOR(realloc_run_requests, struct run_request, 8)
402 #define COMMAND_FLAGS ":!?@<"
404 enum status_code
405 parse_run_request_flags(struct run_request_flags *flags, const char **argv)
407 if (!strchr(COMMAND_FLAGS, *argv[0]))
408 return error("Unknown command flag '%c'; expected one of %s", argv[0][0], COMMAND_FLAGS);
410 while (*argv[0]) {
411 if (*argv[0] == ':') {
412 flags->internal = 1;
413 argv[0]++;
414 break;
415 } else if (*argv[0] == '@') {
416 flags->silent = 1;
417 } else if (*argv[0] == '?') {
418 flags->confirm = 1;
419 } else if (*argv[0] == '<') {
420 flags->exit = 1;
421 } else if (*argv[0] != '!') {
422 break;
424 argv[0]++;
427 return SUCCESS;
430 enum status_code
431 add_run_request(struct keymap *keymap, struct key key[],
432 size_t keys, const char **argv)
434 struct run_request *req;
435 struct run_request_flags flags = {};
436 enum status_code code = parse_run_request_flags(&flags, argv);
438 if (code != SUCCESS)
439 return code;
441 if (!realloc_run_requests(&run_request, run_requests, 1))
442 return ERROR_OUT_OF_MEMORY;
444 if (!argv_copy(&run_request[run_requests].argv, argv))
445 return ERROR_OUT_OF_MEMORY;
447 req = &run_request[run_requests++];
448 req->flags = flags;
449 req->keymap = keymap;
451 return add_keybinding(keymap, REQ_RUN_REQUESTS + run_requests, key, keys);
454 struct run_request *
455 get_run_request(enum request request)
457 if (request <= REQ_RUN_REQUESTS || request > REQ_RUN_REQUESTS + run_requests)
458 return NULL;
459 return &run_request[request - REQ_RUN_REQUESTS - 1];
462 /* vim: set ts=8 sw=8 noexpandtab: */