Unify view refresh checking
[tig.git] / src / keys.c
blob77b1625be70e4a745ed6627ef329fac37a7e8c1f
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) },
191 static const struct key_mapping *
192 get_key_mapping(const char *name, size_t namelen)
194 int i;
196 for (i = 0; i < ARRAY_SIZE(key_mappings); i++) {
197 if (namelen == strlen(key_mappings[i].name) &&
198 !strncasecmp(key_mappings[i].name, name, namelen))
199 return &key_mappings[i];
202 return NULL;
205 static enum status_code
206 parse_key_value(struct key *key, const char **name_ptr, size_t offset,
207 const char *replacement, const char *end)
209 const char *name = replacement ? replacement : *name_ptr + offset;
210 size_t namelen = utf8_char_length(name);
211 const char *nameend = name + namelen;
213 if (strlen(name) < namelen || utf8_to_unicode(name, namelen) == 0)
214 return error("Error parsing UTF-8 bytes: %s", name);
216 strncpy(key->data.bytes, name, namelen);
217 key->modifiers.multibytes = 1;
218 if (end) {
219 *name_ptr = end + 1;
220 if (!replacement && nameend + 1 < end)
221 return success("Ignoring text after key mapping: %.*s",
222 (int) (end - nameend), nameend);
223 } else {
224 *name_ptr = nameend;
227 return SUCCESS;
230 enum status_code
231 get_key_value(const char **name_ptr, struct key *key)
233 const char *name = *name_ptr;
234 const char *end = NULL;
236 memset(key, 0, sizeof(*key));
238 if (*name == '<') {
239 end = strchr(name + 1, '>');
240 if (!end)
241 return error("Missing '>' from key mapping: %s", name);
243 if (!prefixcmp(name, "<Ctrl-")) {
244 key->modifiers.control = 1;
245 return parse_key_value(key, name_ptr, 6, NULL, end);
247 } else if (!prefixcmp(name, "<C-")) {
248 key->modifiers.control = 1;
249 return parse_key_value(key, name_ptr, 3, NULL, end);
251 } else {
252 const struct key_mapping *mapping;
253 const char *start = name + 1;
254 int len = end - start;
256 mapping = get_key_mapping(start, len);
257 if (!mapping)
258 return error("Unknown key mapping: %.*s", len, start);
260 if (mapping->value == ' ')
261 return parse_key_value(key, name_ptr, 0, " ", end);
263 if (mapping->value == '#')
264 return parse_key_value(key, name_ptr, 0, "#", end);
266 if (mapping->value == KEY_ESC) {
267 size_t offset = (end - name) + 1;
269 key->modifiers.escape = 1;
270 return parse_key_value(key, name_ptr, offset, NULL, NULL);
273 *name_ptr = end + 1;
274 key->data.value = mapping->value;
275 return SUCCESS;
279 if (name[0] == '^' && name[1] == '[') {
280 return error("Escape key combo must now use '<Esc>%s' "
281 "instead of '%s'", name + 2, name);
282 } else if (name[0] == '^') {
283 return error("Control key mapping must now use '<Ctrl-%s>' "
284 "instead of '%s'", name + 1, name);
287 return parse_key_value(key, name_ptr, 0, NULL, end);
290 const char *
291 get_key_name(const struct key key[], size_t keys)
293 static char buf[SIZEOF_STR];
294 const char *modifier = "";
295 size_t pos = 0;
296 int i;
298 if (keys == 1 && !key->modifiers.multibytes) {
299 for (i = 0; i < ARRAY_SIZE(key_mappings); i++)
300 if (key_mappings[i].value == key->data.value)
301 return key_mappings[i].name;
304 for (i = 0; i < keys; i++) {
305 const char *start = pos ? "" : "'";
306 const char *end = i + 1 == keys ? "'" : "";
308 if (key[i].modifiers.escape)
309 modifier = "^[";
310 else if (key[i].modifiers.control)
311 modifier = "^";
313 if (!string_format_from(buf, &pos, "%s%s%s%s",
314 start, modifier, key[i].data.bytes, end))
315 return "(no key)";
318 return buf;
321 static bool
322 append_key(char *buf, size_t *pos, const struct keybinding *keybinding)
324 const char *sep = *pos > 0 ? ", " : "";
325 const char *keyname = get_key_name(keybinding->key, keybinding->keys);
327 return string_nformat(buf, BUFSIZ, pos, "%s%s", sep, keyname);
330 static bool
331 append_keymap_request_keys(char *buf, size_t *pos, enum request request,
332 struct keymap *keymap, bool all)
334 int i;
336 for (i = 0; i < keymap->size; i++) {
337 if (keymap->data[i]->request == request) {
338 if (!append_key(buf, pos, keymap->data[i]))
339 return FALSE;
340 if (!all)
341 break;
345 return TRUE;
348 const char *
349 get_keys(struct keymap *keymap, enum request request, bool all)
351 static char buf[BUFSIZ];
352 size_t pos = 0;
354 buf[pos] = 0;
356 if (!append_keymap_request_keys(buf, &pos, request, keymap, all))
357 return "Too many keybindings!";
358 if (pos > 0 && !all)
359 return buf;
361 if (!is_generic_keymap(keymap)) {
362 /* Only the generic keymap includes the default keybindings when
363 * listing all keys. */
364 if (all)
365 return buf;
367 if (!append_keymap_request_keys(buf, &pos, request, generic_keymap, all))
368 return "Too many keybindings!";
369 if (pos)
370 return buf;
373 return buf;
376 static struct run_request *run_request;
377 static size_t run_requests;
379 DEFINE_ALLOCATOR(realloc_run_requests, struct run_request, 8)
381 enum status_code
382 add_run_request(struct keymap *keymap, struct key key[],
383 size_t keys, const char **argv)
385 struct run_request *req;
386 struct run_request_flags flags = {};
388 if (!strchr(":!?@<", *argv[0]))
389 return error("Unknown request name: %s", argv[0]);
391 while (*argv[0]) {
392 if (*argv[0] == ':') {
393 flags.internal = 1;
394 argv[0]++;
395 break;
396 } else if (*argv[0] == '@') {
397 flags.silent = 1;
398 } else if (*argv[0] == '?') {
399 flags.confirm = 1;
400 } else if (*argv[0] == '<') {
401 flags.exit = 1;
402 } else if (*argv[0] != '!') {
403 break;
405 argv[0]++;
408 if (!realloc_run_requests(&run_request, run_requests, 1))
409 return ERROR_OUT_OF_MEMORY;
411 if (!argv_copy(&run_request[run_requests].argv, argv))
412 return ERROR_OUT_OF_MEMORY;
414 req = &run_request[run_requests++];
415 req->flags = flags;
416 req->keymap = keymap;
418 return add_keybinding(keymap, REQ_RUN_REQUESTS + run_requests, key, keys);
421 struct run_request *
422 get_run_request(enum request request)
424 if (request <= REQ_RUN_REQUESTS || request > REQ_RUN_REQUESTS + run_requests)
425 return NULL;
426 return &run_request[request - REQ_RUN_REQUESTS - 1];
429 /* vim: set ts=8 sw=8 noexpandtab: */