1 /* Copyright (c) 2006-2015 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
[] = {
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 static struct keymap
*search_keymap
= keymaps
+ 1;
37 #define is_search_keymap(keymap) ((keymap) == search_keymap)
40 get_keymap(const char *name
, size_t namelen
)
44 for (i
= 0; i
< ARRAY_SIZE(keymaps
); i
++)
45 if (!strncasecmp(keymaps
[i
].name
, name
, namelen
))
52 keybinding_matches(const struct keybinding
*keybinding
, const struct key key
[],
53 size_t keys
, bool *conflict_ptr
)
55 bool conflict
= false;
58 if (keybinding
->keys
< keys
)
61 for (i
= 0; i
< keys
; i
++) {
62 const struct key
*key1
= &keybinding
->key
[i
];
63 const struct key
*key2
= &key
[i
];
65 if (key1
->modifiers
.control
&&
66 key1
->modifiers
.multibytes
&&
67 !memcmp(&key1
->modifiers
, &key2
->modifiers
, sizeof(key1
->modifiers
)) &&
68 strlen(key1
->data
.bytes
) == 1 &&
69 strlen(key2
->data
.bytes
) == 1) {
70 int c1
= key1
->data
.bytes
[0];
71 int c2
= key2
->data
.bytes
[0];
73 if (ascii_toupper(c1
) != ascii_toupper(c2
))
78 if (memcmp(key1
, key2
, sizeof(*key1
)))
83 if (conflict_ptr
&& keybinding
->request
!= REQ_NONE
)
84 *conflict_ptr
= conflict
;
89 keybinding_equals(const struct keybinding
*keybinding
, const struct key key
[],
90 size_t keys
, bool *conflict_ptr
)
92 if (keybinding
->keys
!= keys
)
94 return keybinding_matches(keybinding
, key
, keys
, conflict_ptr
);
98 add_keybinding(struct keymap
*table
, enum request request
,
99 const struct key key
[], size_t keys
)
101 struct keybinding
*keybinding
;
102 char buf
[SIZEOF_STR
];
103 bool conflict
= false;
106 for (i
= 0; i
< table
->size
; i
++) {
107 if (keybinding_equals(table
->data
[i
], key
, keys
, &conflict
)) {
108 enum request old_request
= table
->data
[i
]->request
;
109 const char *old_name
;
111 table
->data
[i
]->request
= request
;
115 old_name
= get_request_name(old_request
);
116 string_ncopy(buf
, old_name
, strlen(old_name
));
117 return error("Key binding for %s and %s conflict; "
118 "keys using Ctrl are case insensitive",
119 buf
, get_request_name(request
));
123 table
->data
= realloc(table
->data
, (table
->size
+ 1) * sizeof(*table
->data
));
124 keybinding
= calloc(1, sizeof(*keybinding
) + (sizeof(*key
) * (keys
- 1)));
125 if (!table
->data
|| !keybinding
)
126 die("Failed to allocate keybinding");
128 memcpy(keybinding
->key
, key
, sizeof(*key
) * keys
);
129 keybinding
->keys
= keys
;
130 keybinding
->request
= request
;
131 table
->data
[table
->size
++] = keybinding
;
136 get_keybinding_in_keymap(const struct keymap
*keymap
, const struct key key
[], size_t keys
, int *matches
)
138 enum request request
= REQ_UNKNOWN
;
141 for (i
= 0; i
< keymap
->size
; i
++)
142 if (keybinding_matches(keymap
->data
[i
], key
, keys
, NULL
)) {
143 if (matches
&& keymap
->data
[i
]->request
!= REQ_NONE
)
145 /* Overriding keybindings, might have been added
146 * at the end of the keymap so we need to
147 * iterate all keybindings. */
148 if (keymap
->data
[i
]->keys
== keys
)
149 request
= keymap
->data
[i
]->request
;
155 /* Looks for a key binding first in the given keymap, then in the generic keymap. */
157 get_keybinding(const struct keymap
*keymap
, const struct key key
[], size_t keys
, int *matches
)
159 enum request request
= get_keybinding_in_keymap(keymap
, key
, keys
, matches
);
161 if (!is_search_keymap(keymap
)) {
162 int generic_matches
= 0;
163 enum request generic_request
= get_keybinding_in_keymap(generic_keymap
, key
, keys
, &generic_matches
);
165 /* Include generic matches iff there are more than one
166 * so unbound keys in the current keymap still override
167 * generic keys while still ensuring that the key combo
168 * handler continues to wait for more keys if there is
169 * another possible match. E.g. while in `main` view:
171 * bind generic q quit # 'q' will quit
172 * bind main q none # 'q' will do nothing
173 * bind generic qa quit # 'qa' will quit
174 * bind main qn next # 'qn' will move to next entry
176 if (matches
&& (request
== REQ_UNKNOWN
|| generic_matches
> 1))
177 (*matches
) += generic_matches
;
178 if (request
== REQ_UNKNOWN
)
179 request
= generic_request
;
182 return request
== REQ_NONE
? REQ_UNKNOWN
: request
;
191 static const struct key_mapping key_mappings
[] = {
192 { "Enter", KEY_RETURN
},
194 { "Backspace", KEY_BACKSPACE
},
196 { "Escape", KEY_ESC
},
198 { "Left", KEY_LEFT
},
199 { "Right", KEY_RIGHT
},
201 { "Down", KEY_DOWN
},
202 { "Insert", KEY_IC
},
204 { "Delete", KEY_DC
},
207 { "Home", KEY_HOME
},
209 { "PageUp", KEY_PPAGE
},
210 { "PgUp", KEY_PPAGE
},
211 { "PageDown", KEY_NPAGE
},
212 { "PgDown", KEY_NPAGE
},
224 { "F10", KEY_F(10) },
225 { "F11", KEY_F(11) },
226 { "F12", KEY_F(12) },
227 { "ScrollBack", KEY_SR
},
229 { "ScrollFwd", KEY_SF
},
233 static const struct key_mapping
*
234 get_key_mapping(const char *name
, size_t namelen
)
238 for (i
= 0; i
< ARRAY_SIZE(key_mappings
); i
++) {
239 if (namelen
== strlen(key_mappings
[i
].name
) &&
240 !strncasecmp(key_mappings
[i
].name
, name
, namelen
))
241 return &key_mappings
[i
];
247 static enum status_code
248 parse_key_value(struct key
*key
, const char **name_ptr
, size_t offset
,
249 const char *replacement
, const char *end
)
251 const char *name
= replacement
? replacement
: *name_ptr
+ offset
;
252 size_t namelen
= utf8_char_length(name
);
253 const char *nameend
= name
+ namelen
;
255 if (strlen(name
) < namelen
|| utf8_to_unicode(name
, namelen
) == 0)
256 return error("Error parsing UTF-8 bytes: %s", name
);
258 strncpy(key
->data
.bytes
, name
, namelen
);
259 key
->modifiers
.multibytes
= 1;
262 if (!replacement
&& nameend
+ 1 < end
)
263 return success("Ignoring text after key mapping: %.*s",
264 (int) (end
- nameend
), nameend
);
273 get_key_value(const char **name_ptr
, struct key
*key
)
275 const char *name
= *name_ptr
;
276 const char *end
= NULL
;
278 memset(key
, 0, sizeof(*key
));
281 end
= strchr(name
+ 1, '>');
283 return error("Missing '>' from key mapping: %s", name
);
285 if (!prefixcmp(name
, "<Ctrl-")) {
286 key
->modifiers
.control
= 1;
287 return parse_key_value(key
, name_ptr
, 6, NULL
, end
);
289 } else if (!prefixcmp(name
, "<C-")) {
290 key
->modifiers
.control
= 1;
291 return parse_key_value(key
, name_ptr
, 3, NULL
, end
);
294 const struct key_mapping
*mapping
;
295 const char *start
= name
+ 1;
296 int len
= end
- start
;
298 mapping
= get_key_mapping(start
, len
);
300 return error("Unknown key mapping: %.*s", len
, start
);
302 if (mapping
->value
== ' ')
303 return parse_key_value(key
, name_ptr
, 0, " ", end
);
305 if (mapping
->value
== '#')
306 return parse_key_value(key
, name_ptr
, 0, "#", end
);
308 if (mapping
->value
== '<')
309 return parse_key_value(key
, name_ptr
, 0, "<", end
);
312 key
->data
.value
= mapping
->value
;
317 if (name
[0] == '^' && name
[1] == '[') {
318 return error("Escape key combo must now use '<Esc>%s' "
319 "instead of '%s'", name
+ 2, name
);
320 } else if (name
[0] == '^' && name
[1] != '\0') {
321 return error("Control key mapping must now use '<Ctrl-%s>' "
322 "instead of '%s'", name
+ 1, name
);
325 return parse_key_value(key
, name_ptr
, 0, NULL
, end
);
329 get_key_name(const struct key key
[], size_t keys
, bool quote_comma
)
331 static char buf
[SIZEOF_STR
];
335 for (i
= 0; i
< keys
; i
++) {
336 bool multibytes
= key
[i
].modifiers
.multibytes
;
337 const char *name
= multibytes
? key
[i
].data
.bytes
: "";
338 const char *start
= "";
339 const char *end
= "";
342 if (key
[i
].modifiers
.control
) {
345 } else if (*name
== ',' && quote_comma
) {
346 /* Quote commas so they stand out in the help view. */
351 /* Use symbolic name for spaces so they are readable. */
352 use_symbolic
= !*name
|| *name
== ' ';
353 /* When listing keys for :save-options quote illegal characters. */
354 if (!quote_comma
&& (*name
== '<' || *name
== '#'))
358 int value
= *name
? *name
: key
[i
].data
.value
;
362 for (j
= 0; j
< ARRAY_SIZE(key_mappings
); j
++)
363 if (key_mappings
[j
].value
== value
) {
366 name
= key_mappings
[j
].name
;
371 if (!string_format_from(buf
, &pos
, "%s%s%s", start
, name
, end
))
379 append_key(char *buf
, size_t *pos
, const struct keybinding
*keybinding
, bool all
)
381 const char *sep
= *pos
> 0 ? ", " : "";
382 const char *keyname
= get_key_name(keybinding
->key
, keybinding
->keys
, all
);
384 return string_nformat(buf
, BUFSIZ
, pos
, "%s%s", sep
, keyname
);
388 append_keymap_request_keys(char *buf
, size_t *pos
, enum request request
,
389 const struct keymap
*keymap
, bool all
)
393 for (i
= 0; i
< keymap
->size
; i
++) {
394 if (keymap
->data
[i
]->request
== request
) {
395 if (!append_key(buf
, pos
, keymap
->data
[i
], all
))
406 get_keys(const struct keymap
*keymap
, enum request request
, bool all
)
408 static char buf
[BUFSIZ
];
413 if (!append_keymap_request_keys(buf
, &pos
, request
, keymap
, all
))
414 return "Too many keybindings!";
418 if (!is_generic_keymap(keymap
)) {
419 /* Only the generic keymap includes the default keybindings when
420 * listing all keys. */
424 if (!append_keymap_request_keys(buf
, &pos
, request
, generic_keymap
, all
))
425 return "Too many keybindings!";
433 static struct run_request
*run_request
;
434 static size_t run_requests
;
436 DEFINE_ALLOCATOR(realloc_run_requests
, struct run_request
, 8)
438 #define COMMAND_FLAGS ":!?@<"
441 parse_run_request_flags(struct run_request_flags
*flags
, const char **argv
)
443 if (!strchr(COMMAND_FLAGS
, *argv
[0]))
444 return error("Unknown command flag '%c'; expected one of %s", argv
[0][0], COMMAND_FLAGS
);
447 if (*argv
[0] == ':') {
451 } else if (*argv
[0] == '@') {
453 } else if (*argv
[0] == '?') {
455 } else if (*argv
[0] == '<') {
457 } else if (*argv
[0] != '!') {
467 add_run_request(struct keymap
*keymap
, const struct key key
[],
468 size_t keys
, const char **argv
)
470 struct run_request
*req
;
471 struct run_request_flags flags
= {0};
472 enum status_code code
= parse_run_request_flags(&flags
, argv
);
477 if (!realloc_run_requests(&run_request
, run_requests
, 1))
478 return ERROR_OUT_OF_MEMORY
;
480 if (!argv_copy(&run_request
[run_requests
].argv
, argv
))
481 return ERROR_OUT_OF_MEMORY
;
483 req
= &run_request
[run_requests
++];
485 req
->keymap
= keymap
;
487 return add_keybinding(keymap
, REQ_RUN_REQUESTS
+ run_requests
, key
, keys
);
491 get_run_request(enum request request
)
493 if (request
<= REQ_RUN_REQUESTS
|| request
> REQ_RUN_REQUESTS
+ run_requests
)
495 return &run_request
[request
- REQ_RUN_REQUESTS
- 1];
499 format_run_request_flags(const struct run_request
*req
)
501 static char flags
[8];
504 memset(flags
, 0, sizeof(flags
));
506 if (req
->flags
.internal
)
507 flags
[flagspos
++] = ':';
509 flags
[flagspos
] = '!'; /* Optional, if other flags are defined */
511 if (req
->flags
.silent
)
512 flags
[flagspos
++] = '@';
513 if (req
->flags
.confirm
)
514 flags
[flagspos
++] = '?';
516 flags
[flagspos
++] = '<';
518 flags
[flagspos
++] = 0;
523 struct key_visitor_state
{
524 key_visitor_fn visitor
;
526 struct keymap
*keymap
;
532 foreach_key_visit(struct key_visitor_state
*state
, const char *group
,
533 enum request request
,
534 const struct request_info
*req_info
, const struct run_request
*run_req
)
536 struct keymap
*keymap
= state
->keymap
;
539 if (state
->group
== group
)
542 if (state
->combine_keys
) {
543 const char *key
= get_keys(keymap
, request
, true);
549 state
->group
= group
;
550 return state
->visitor(state
->data
, group
, keymap
, request
,
551 key
, req_info
, run_req
);
554 for (i
= 0; i
< keymap
->size
; i
++) {
555 if (keymap
->data
[i
]->request
== request
) {
556 struct keybinding
*keybinding
= keymap
->data
[i
];
557 const char *key
= get_key_name(keybinding
->key
, keybinding
->keys
, false);
562 if (!state
->visitor(state
->data
, group
, keymap
, request
,
563 key
, req_info
, run_req
))
567 state
->group
= group
;
576 foreach_key_request(void *data
, const struct request_info
*req_info
, const char *group
)
578 struct key_visitor_state
*state
= data
;
580 if (req_info
->request
== REQ_NONE
)
583 return foreach_key_visit(state
, group
, req_info
->request
, req_info
, NULL
);
587 foreach_key_run_request(struct key_visitor_state
*state
, bool internal
, bool toggles
)
589 struct keymap
*keymap
= state
->keymap
;
590 const char *group
= !internal
? "External commands:" :
591 toggles
? "Option toggling:" :
592 "Internal commands:";
593 enum request request
= REQ_RUN_REQUESTS
+ 1;
595 for (; true; request
++) {
596 struct run_request
*req
= get_run_request(request
);
602 if (req
->flags
.internal
!= !!internal
||
603 req
->keymap
!= keymap
||
604 !*(key
= get_keys(keymap
, request
, true)))
607 if (toggles
!= !strcmp(req
->argv
[0], "toggle"))
610 if (!foreach_key_visit(state
, group
, request
, NULL
, req
))
618 foreach_key(key_visitor_fn visitor
, void *data
, bool combine_keys
)
622 for (i
= 0; i
< ARRAY_SIZE(keymaps
); i
++) {
623 struct key_visitor_state state
= { visitor
, data
, &keymaps
[i
], combine_keys
};
625 if (!foreach_request(foreach_key_request
, &state
)
626 || !foreach_key_run_request(&state
, true, true)
627 || !foreach_key_run_request(&state
, true, false)
628 || !foreach_key_run_request(&state
, false, false))
635 /* vim: set ts=8 sw=8 noexpandtab: */