Read git diff.context option
[tig.git] / src / keys.c
blob065ea6062a012793607a1f9c3a299696d166ed51
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_table {
149 const char *name;
150 int value;
153 static const struct key_table key_table[] = {
154 { "Enter", KEY_RETURN },
155 { "Space", ' ' },
156 { "Backspace", KEY_BACKSPACE },
157 { "Tab", KEY_TAB },
158 { "Escape", KEY_ESC },
159 { "Left", KEY_LEFT },
160 { "Right", KEY_RIGHT },
161 { "Up", KEY_UP },
162 { "Down", KEY_DOWN },
163 { "Insert", KEY_IC },
164 { "Delete", KEY_DC },
165 { "Hash", '#' },
166 { "Home", KEY_HOME },
167 { "End", KEY_END },
168 { "PageUp", KEY_PPAGE },
169 { "PgUp", KEY_PPAGE },
170 { "PageDown", KEY_NPAGE },
171 { "PgDown", KEY_NPAGE },
172 { "F1", KEY_F(1) },
173 { "F2", KEY_F(2) },
174 { "F3", KEY_F(3) },
175 { "F4", KEY_F(4) },
176 { "F5", KEY_F(5) },
177 { "F6", KEY_F(6) },
178 { "F7", KEY_F(7) },
179 { "F8", KEY_F(8) },
180 { "F9", KEY_F(9) },
181 { "F10", KEY_F(10) },
182 { "F11", KEY_F(11) },
183 { "F12", KEY_F(12) },
187 get_key_value(const char **name_ptr, struct key *key)
189 const char *name = *name_ptr;
190 size_t namelen = 0;
191 int i;
193 memset(key, 0, sizeof(*key));
195 for (i = 0; i < ARRAY_SIZE(key_table); i++)
196 if (!strcasecmp(key_table[i].name, name)) {
197 namelen = strlen(key_table[i].name);
198 if (key_table[i].value == ' ') {
199 name = " ";
200 break;
202 if (key_table[i].value == '#') {
203 name = "#";
204 break;
206 *name_ptr = name + namelen;
207 key->data.value = key_table[i].value;
208 return OK;
211 if (name[0] == '^' && name[1] == '[') {
212 key->modifiers.escape = 1;
213 name += 2;
214 } else if (name[0] == '^') {
215 key->modifiers.control = 1;
216 name += 1;
219 i = utf8_char_length(name);
220 if (strlen(name) >= i && utf8_to_unicode(name, i) != 0) {
221 strncpy(key->data.bytes, name, i);
222 key->modifiers.multibytes = 1;
223 if (!namelen)
224 namelen += i + (name - *name_ptr);
225 *name_ptr += namelen;
226 return OK;
229 return ERR;
232 const char *
233 get_key_name(const struct key key[], size_t keys)
235 static char buf[SIZEOF_STR];
236 const char *modifier = "";
237 size_t pos = 0;
238 int i;
240 if (keys == 1 && !key->modifiers.multibytes) {
241 for (i = 0; i < ARRAY_SIZE(key_table); i++)
242 if (key_table[i].value == key->data.value)
243 return key_table[i].name;
246 for (i = 0; i < keys; i++) {
247 const char *start = pos ? "" : "'";
248 const char *end = i + 1 == keys ? "'" : "";
250 if (key[i].modifiers.escape)
251 modifier = "^[";
252 else if (key[i].modifiers.control)
253 modifier = "^";
255 if (!string_format_from(buf, &pos, "%s%s%s%s",
256 start, modifier, key[i].data.bytes, end))
257 return "(no key)";
260 return buf;
263 static bool
264 append_key(char *buf, size_t *pos, const struct keybinding *keybinding)
266 const char *sep = *pos > 0 ? ", " : "";
267 const char *keyname = get_key_name(keybinding->key, keybinding->keys);
269 return string_nformat(buf, BUFSIZ, pos, "%s%s", sep, keyname);
272 static bool
273 append_keymap_request_keys(char *buf, size_t *pos, enum request request,
274 struct keymap *keymap, bool all)
276 int i;
278 for (i = 0; i < keymap->size; i++) {
279 if (keymap->data[i]->request == request) {
280 if (!append_key(buf, pos, keymap->data[i]))
281 return FALSE;
282 if (!all)
283 break;
287 return TRUE;
290 const char *
291 get_keys(struct keymap *keymap, enum request request, bool all)
293 static char buf[BUFSIZ];
294 size_t pos = 0;
296 buf[pos] = 0;
298 if (!append_keymap_request_keys(buf, &pos, request, keymap, all))
299 return "Too many keybindings!";
300 if (pos > 0 && !all)
301 return buf;
303 if (!is_generic_keymap(keymap)) {
304 /* Only the generic keymap includes the default keybindings when
305 * listing all keys. */
306 if (all)
307 return buf;
309 if (!append_keymap_request_keys(buf, &pos, request, generic_keymap, all))
310 return "Too many keybindings!";
311 if (pos)
312 return buf;
315 return buf;
318 static struct run_request *run_request;
319 static size_t run_requests;
321 DEFINE_ALLOCATOR(realloc_run_requests, struct run_request, 8)
323 enum status_code
324 add_run_request(struct keymap *keymap, struct key key[],
325 size_t keys, const char **argv)
327 struct run_request *req;
328 struct run_request_flags flags = {};
330 if (!strchr(":!?@<", *argv[0]))
331 return error("Unknown request name: %s", argv[0]);
333 while (*argv[0]) {
334 if (*argv[0] == ':') {
335 flags.internal = 1;
336 argv[0]++;
337 break;
338 } else if (*argv[0] == '@') {
339 flags.silent = 1;
340 } else if (*argv[0] == '?') {
341 flags.confirm = 1;
342 } else if (*argv[0] == '<') {
343 flags.exit = 1;
344 } else if (*argv[0] != '!') {
345 break;
347 argv[0]++;
350 if (!realloc_run_requests(&run_request, run_requests, 1))
351 return ERROR_OUT_OF_MEMORY;
353 if (!argv_copy(&run_request[run_requests].argv, argv))
354 return ERROR_OUT_OF_MEMORY;
356 req = &run_request[run_requests++];
357 req->flags = flags;
358 req->keymap = keymap;
360 return add_keybinding(keymap, REQ_RUN_REQUESTS + run_requests, key, keys);
363 struct run_request *
364 get_run_request(enum request request)
366 if (request <= REQ_RUN_REQUESTS || request > REQ_RUN_REQUESTS + run_requests)
367 return NULL;
368 return &run_request[request - REQ_RUN_REQUESTS - 1];
371 /* vim: set ts=8 sw=8 noexpandtab: */