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.
15 #include "tig/types.h"
26 static struct keymap keymaps
[] = {
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)
36 get_keymap_by_index(int i
)
38 return 0 <= i
&& i
< ARRAY_SIZE(keymaps
) ? &keymaps
[i
] : NULL
;
42 get_keymap(const char *name
, size_t namelen
)
46 for (i
= 0; i
< ARRAY_SIZE(keymaps
); i
++)
47 if (!strncasecmp(keymaps
[i
].name
, name
, namelen
))
54 keybinding_equals(struct keybinding
*keybinding
, struct key key
[],
55 size_t keys
, bool *conflict_ptr
)
57 bool conflict
= FALSE
;
60 if (keybinding
->keys
!= keys
)
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
))
80 if (memcmp(key1
, key2
, sizeof(*key1
)))
86 *conflict_ptr
= conflict
;
91 add_keybinding(struct keymap
*table
, enum request request
,
92 struct key key
[], size_t keys
)
94 struct keybinding
*keybinding
;
96 bool conflict
= FALSE
;
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
;
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
;
128 /* Looks for a key binding first in the given map, then in the generic map, and
129 * lastly in the default keybindings. */
131 get_keybinding(struct keymap
*keymap
, struct key key
[], size_t keys
)
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
;
152 static const struct key_mapping key_mappings
[] = {
153 { "Enter", KEY_RETURN
},
155 { "Backspace", KEY_BACKSPACE
},
157 { "Escape", KEY_ESC
},
159 { "Left", KEY_LEFT
},
160 { "Right", KEY_RIGHT
},
162 { "Down", KEY_DOWN
},
163 { "Insert", KEY_IC
},
165 { "Delete", KEY_DC
},
168 { "Home", KEY_HOME
},
170 { "PageUp", KEY_PPAGE
},
171 { "PgUp", KEY_PPAGE
},
172 { "PageDown", KEY_NPAGE
},
173 { "PgDown", KEY_NPAGE
},
185 { "F10", KEY_F(10) },
186 { "F11", KEY_F(11) },
187 { "F12", KEY_F(12) },
188 { "ScrollBack", KEY_SR
},
190 { "ScrollFwd", KEY_SF
},
194 static const struct key_mapping
*
195 get_key_mapping(const char *name
, size_t namelen
)
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
];
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;
223 if (!replacement
&& nameend
+ 1 < end
)
224 return success("Ignoring text after key mapping: %.*s",
225 (int) (end
- nameend
), nameend
);
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
));
242 end
= strchr(name
+ 1, '>');
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
);
255 const struct key_mapping
*mapping
;
256 const char *start
= name
+ 1;
257 int len
= end
- start
;
259 mapping
= get_key_mapping(start
, len
);
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
);
280 key
->data
.value
= mapping
->value
;
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
);
297 get_key_name(const struct key key
[], size_t keys
)
299 static char buf
[SIZEOF_STR
];
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
) {
311 } else if (key
[i
].modifiers
.control
) {
314 } else if (*name
== ',') {
315 /* Quote commas so they stand out in the help view. */
320 /* Use symbolic name for spaces so they are readable. */
321 if (!*name
|| *name
== ' ') {
322 int value
= *name
? *name
: key
[i
].data
.value
;
326 for (j
= 0; j
< ARRAY_SIZE(key_mappings
); j
++)
327 if (key_mappings
[j
].value
== value
) {
328 start
= key
[i
].modifiers
.escape
? "<Esc><" : "<";
330 name
= key_mappings
[j
].name
;
335 if (!string_format_from(buf
, &pos
, "%s%s%s", start
, name
, end
))
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
);
352 append_keymap_request_keys(char *buf
, size_t *pos
, enum request request
,
353 struct keymap
*keymap
, bool all
)
357 for (i
= 0; i
< keymap
->size
; i
++) {
358 if (keymap
->data
[i
]->request
== request
) {
359 if (!append_key(buf
, pos
, keymap
->data
[i
]))
370 get_keys(struct keymap
*keymap
, enum request request
, bool all
)
372 static char buf
[BUFSIZ
];
377 if (!append_keymap_request_keys(buf
, &pos
, request
, keymap
, all
))
378 return "Too many keybindings!";
382 if (!is_generic_keymap(keymap
)) {
383 /* Only the generic keymap includes the default keybindings when
384 * listing all keys. */
388 if (!append_keymap_request_keys(buf
, &pos
, request
, generic_keymap
, all
))
389 return "Too many keybindings!";
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 ":!?@<"
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
);
411 if (*argv
[0] == ':') {
415 } else if (*argv
[0] == '@') {
417 } else if (*argv
[0] == '?') {
419 } else if (*argv
[0] == '<') {
421 } else if (*argv
[0] != '!') {
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
);
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
++];
449 req
->keymap
= keymap
;
451 return add_keybinding(keymap
, REQ_RUN_REQUESTS
+ run_requests
, key
, keys
);
455 get_run_request(enum request request
)
457 if (request
<= REQ_RUN_REQUESTS
|| request
> REQ_RUN_REQUESTS
+ run_requests
)
459 return &run_request
[request
- REQ_RUN_REQUESTS
- 1];
462 /* vim: set ts=8 sw=8 noexpandtab: */