Partly restore old refresh behavior
[tig.git] / src / keys.c
blob1b77c5af78a1c47ca359d7cd69d3202b8ed09bd9
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"
19 #include "tig/util.h"
21 struct keybinding {
22 enum request request;
23 size_t keys;
24 struct key key[1];
27 static struct keymap keymaps[] = {
28 { "generic" },
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 struct keymap *
37 get_keymap_by_index(int i)
39 return 0 <= i && i < ARRAY_SIZE(keymaps) ? &keymaps[i] : NULL;
42 struct keymap *
43 get_keymap(const char *name, size_t namelen)
45 int i;
47 for (i = 0; i < ARRAY_SIZE(keymaps); i++)
48 if (!strncasecmp(keymaps[i].name, name, namelen))
49 return &keymaps[i];
51 return NULL;
54 static bool
55 keybinding_equals(struct keybinding *keybinding, struct key key[],
56 size_t keys, bool *conflict_ptr)
58 bool conflict = FALSE;
59 int i;
61 if (keybinding->keys != keys)
62 return FALSE;
64 for (i = 0; i < keys; i++) {
65 struct key *key1 = &keybinding->key[i];
66 struct key *key2 = &key[i];
68 if (key1->modifiers.control &&
69 key1->modifiers.multibytes &&
70 !memcmp(&key1->modifiers, &key2->modifiers, sizeof(key1->modifiers)) &&
71 strlen(key1->data.bytes) == 1 &&
72 strlen(key2->data.bytes) == 1) {
73 int c1 = key1->data.bytes[0];
74 int c2 = key2->data.bytes[0];
76 if (ascii_toupper(c1) != ascii_toupper(c2))
77 return FALSE;
78 if (c1 != c2)
79 conflict = TRUE;
80 } else {
81 if (memcmp(key1, key2, sizeof(*key1)))
82 return FALSE;
86 if (conflict_ptr)
87 *conflict_ptr = conflict;
88 return TRUE;
91 enum status_code
92 add_keybinding(struct keymap *table, enum request request,
93 struct key key[], size_t keys)
95 struct keybinding *keybinding;
96 char buf[SIZEOF_STR];
97 bool conflict = FALSE;
98 size_t i;
100 for (i = 0; i < table->size; i++) {
101 if (keybinding_equals(table->data[i], key, keys, &conflict)) {
102 enum request old_request = table->data[i]->request;
103 const char *old_name;
105 table->data[i]->request = request;
106 if (!conflict)
107 return SUCCESS;
109 old_name = get_request_name(old_request);
110 string_ncopy(buf, old_name, strlen(old_name));
111 return error("Key binding for %s and %s conflict; "
112 "keys using Ctrl are case insensitive",
113 buf, get_request_name(request));
117 table->data = realloc(table->data, (table->size + 1) * sizeof(*table->data));
118 keybinding = calloc(1, sizeof(*keybinding) + (sizeof(*key) * (keys - 1)));
119 if (!table->data || !keybinding)
120 die("Failed to allocate keybinding");
122 memcpy(keybinding->key, key, sizeof(*key) * keys);
123 keybinding->keys = keys;
124 keybinding->request = request;
125 table->data[table->size++] = keybinding;
126 return SUCCESS;
129 /* Looks for a key binding first in the given map, then in the generic map, and
130 * lastly in the default keybindings. */
131 enum request
132 get_keybinding(struct keymap *keymap, struct key key[], size_t keys)
134 size_t i;
136 for (i = 0; i < keymap->size; i++)
137 if (keybinding_equals(keymap->data[i], key, keys, NULL))
138 return keymap->data[i]->request;
140 for (i = 0; i < generic_keymap->size; i++)
141 if (keybinding_equals(generic_keymap->data[i], key, keys, NULL))
142 return generic_keymap->data[i]->request;
144 return REQ_NONE;
148 struct key_mapping {
149 const char *name;
150 int value;
153 static const struct key_mapping key_mappings[] = {
154 { "Enter", KEY_RETURN },
155 { "Space", ' ' },
156 { "Backspace", KEY_BACKSPACE },
157 { "Tab", KEY_TAB },
158 { "Escape", KEY_ESC },
159 { "Esc", KEY_ESC },
160 { "Left", KEY_LEFT },
161 { "Right", KEY_RIGHT },
162 { "Up", KEY_UP },
163 { "Down", KEY_DOWN },
164 { "Insert", KEY_IC },
165 { "Ins", KEY_IC },
166 { "Delete", KEY_DC },
167 { "Del", KEY_DC },
168 { "Hash", '#' },
169 { "Home", KEY_HOME },
170 { "End", KEY_END },
171 { "PageUp", KEY_PPAGE },
172 { "PgUp", KEY_PPAGE },
173 { "PageDown", KEY_NPAGE },
174 { "PgDown", KEY_NPAGE },
175 { "LessThan", '<' },
176 { "LT", '<' },
177 { "F1", KEY_F(1) },
178 { "F2", KEY_F(2) },
179 { "F3", KEY_F(3) },
180 { "F4", KEY_F(4) },
181 { "F5", KEY_F(5) },
182 { "F6", KEY_F(6) },
183 { "F7", KEY_F(7) },
184 { "F8", KEY_F(8) },
185 { "F9", KEY_F(9) },
186 { "F10", KEY_F(10) },
187 { "F11", KEY_F(11) },
188 { "F12", KEY_F(12) },
189 { "ScrollBack", KEY_SR },
190 { "SBack", KEY_SR },
191 { "ScrollFwd", KEY_SF },
192 { "SFwd", KEY_SF },
195 static const struct key_mapping *
196 get_key_mapping(const char *name, size_t namelen)
198 int i;
200 for (i = 0; i < ARRAY_SIZE(key_mappings); i++) {
201 if (namelen == strlen(key_mappings[i].name) &&
202 !strncasecmp(key_mappings[i].name, name, namelen))
203 return &key_mappings[i];
206 return NULL;
209 static enum status_code
210 parse_key_value(struct key *key, const char **name_ptr, size_t offset,
211 const char *replacement, const char *end)
213 const char *name = replacement ? replacement : *name_ptr + offset;
214 size_t namelen = utf8_char_length(name);
215 const char *nameend = name + namelen;
217 if (strlen(name) < namelen || utf8_to_unicode(name, namelen) == 0)
218 return error("Error parsing UTF-8 bytes: %s", name);
220 strncpy(key->data.bytes, name, namelen);
221 key->modifiers.multibytes = 1;
222 if (end) {
223 *name_ptr = end + 1;
224 if (!replacement && nameend + 1 < end)
225 return success("Ignoring text after key mapping: %.*s",
226 (int) (end - nameend), nameend);
227 } else {
228 *name_ptr = nameend;
231 return SUCCESS;
234 enum status_code
235 get_key_value(const char **name_ptr, struct key *key)
237 const char *name = *name_ptr;
238 const char *end = NULL;
240 memset(key, 0, sizeof(*key));
242 if (*name == '<') {
243 end = strchr(name + 1, '>');
244 if (!end)
245 return error("Missing '>' from key mapping: %s", name);
247 if (!prefixcmp(name, "<Ctrl-")) {
248 key->modifiers.control = 1;
249 return parse_key_value(key, name_ptr, 6, NULL, end);
251 } else if (!prefixcmp(name, "<C-")) {
252 key->modifiers.control = 1;
253 return parse_key_value(key, name_ptr, 3, NULL, end);
255 } else {
256 const struct key_mapping *mapping;
257 const char *start = name + 1;
258 int len = end - start;
260 mapping = get_key_mapping(start, len);
261 if (!mapping)
262 return error("Unknown key mapping: %.*s", len, start);
264 if (mapping->value == ' ')
265 return parse_key_value(key, name_ptr, 0, " ", end);
267 if (mapping->value == '#')
268 return parse_key_value(key, name_ptr, 0, "#", end);
270 if (mapping->value == '<')
271 return parse_key_value(key, name_ptr, 0, "<", end);
273 if (mapping->value == KEY_ESC) {
274 size_t offset = (end - name) + 1;
276 key->modifiers.escape = 1;
277 return parse_key_value(key, name_ptr, offset, NULL, NULL);
280 *name_ptr = end + 1;
281 key->data.value = mapping->value;
282 return SUCCESS;
286 if (name[0] == '^' && name[1] == '[') {
287 return error("Escape key combo must now use '<Esc>%s' "
288 "instead of '%s'", name + 2, name);
289 } else if (name[0] == '^' && name[1] != '\0') {
290 return error("Control key mapping must now use '<Ctrl-%s>' "
291 "instead of '%s'", name + 1, name);
294 return parse_key_value(key, name_ptr, 0, NULL, end);
297 const char *
298 get_key_name(const struct key key[], size_t keys)
300 static char buf[SIZEOF_STR];
301 size_t pos = 0;
302 int i;
304 for (i = 0; i < keys; i++) {
305 bool multibytes = key[i].modifiers.multibytes;
306 const char *name = multibytes ? key[i].data.bytes : "";
307 const char *start = "";
308 const char *end = "";
310 if (key[i].modifiers.escape) {
311 start = "<Esc>";
312 } else if (key[i].modifiers.control) {
313 start = "<Ctrl-";
314 end = ">";
315 } else if (*name == ',') {
316 /* Quote commas so they stand out in the help view. */
317 start = "'";
318 end = "'";
321 /* Use symbolic name for spaces so they are readable. */
322 if (!*name || *name == ' ') {
323 int value = *name ? *name : key[i].data.value;
324 int j;
326 name = "<?>";
327 for (j = 0; j < ARRAY_SIZE(key_mappings); j++)
328 if (key_mappings[j].value == value) {
329 start = key[i].modifiers.escape ? "<Esc><" : "<";
330 end = ">";
331 name = key_mappings[j].name;
332 break;
336 if (!string_format_from(buf, &pos, "%s%s%s", start, name, end))
337 return "(no key)";
340 return buf;
343 static bool
344 append_key(char *buf, size_t *pos, const struct keybinding *keybinding)
346 const char *sep = *pos > 0 ? ", " : "";
347 const char *keyname = get_key_name(keybinding->key, keybinding->keys);
349 return string_nformat(buf, BUFSIZ, pos, "%s%s", sep, keyname);
352 static bool
353 append_keymap_request_keys(char *buf, size_t *pos, enum request request,
354 struct keymap *keymap, bool all)
356 int i;
358 for (i = 0; i < keymap->size; i++) {
359 if (keymap->data[i]->request == request) {
360 if (!append_key(buf, pos, keymap->data[i]))
361 return FALSE;
362 if (!all)
363 break;
367 return TRUE;
370 const char *
371 get_keys(struct keymap *keymap, enum request request, bool all)
373 static char buf[BUFSIZ];
374 size_t pos = 0;
376 buf[pos] = 0;
378 if (!append_keymap_request_keys(buf, &pos, request, keymap, all))
379 return "Too many keybindings!";
380 if (pos > 0 && !all)
381 return buf;
383 if (!is_generic_keymap(keymap)) {
384 /* Only the generic keymap includes the default keybindings when
385 * listing all keys. */
386 if (all)
387 return buf;
389 if (!append_keymap_request_keys(buf, &pos, request, generic_keymap, all))
390 return "Too many keybindings!";
391 if (pos)
392 return buf;
395 return buf;
398 static struct run_request *run_request;
399 static size_t run_requests;
401 DEFINE_ALLOCATOR(realloc_run_requests, struct run_request, 8)
403 enum status_code
404 add_run_request(struct keymap *keymap, struct key key[],
405 size_t keys, const char **argv)
407 struct run_request *req;
408 struct run_request_flags flags = {};
410 if (!strchr(":!?@<", *argv[0]))
411 return error("Unknown request name: %s", argv[0]);
413 while (*argv[0]) {
414 if (*argv[0] == ':') {
415 flags.internal = 1;
416 argv[0]++;
417 break;
418 } else if (*argv[0] == '@') {
419 flags.silent = 1;
420 } else if (*argv[0] == '?') {
421 flags.confirm = 1;
422 } else if (*argv[0] == '<') {
423 flags.exit = 1;
424 } else if (*argv[0] != '!') {
425 break;
427 argv[0]++;
430 if (!realloc_run_requests(&run_request, run_requests, 1))
431 return ERROR_OUT_OF_MEMORY;
433 if (!argv_copy(&run_request[run_requests].argv, argv))
434 return ERROR_OUT_OF_MEMORY;
436 req = &run_request[run_requests++];
437 req->flags = flags;
438 req->keymap = keymap;
440 return add_keybinding(keymap, REQ_RUN_REQUESTS + run_requests, key, keys);
443 struct run_request *
444 get_run_request(enum request request)
446 if (request <= REQ_RUN_REQUESTS || request > REQ_RUN_REQUESTS + run_requests)
447 return NULL;
448 return &run_request[request - REQ_RUN_REQUESTS - 1];
451 /* vim: set ts=8 sw=8 noexpandtab: */