| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include <limits.h> | ||
| 2 | #include <stdlib.h> | ||
| 3 | #include "bind.h" | ||
| 4 | #include "change.h" | ||
| 5 | #include "command/macro.h" | ||
| 6 | #include "command/run.h" | ||
| 7 | #include "command/serialize.h" | ||
| 8 | #include "editor.h" | ||
| 9 | #include "util/debug.h" | ||
| 10 | #include "util/xmalloc.h" | ||
| 11 | |||
| 12 | 1955 | void add_binding(IntMap *bindings, KeyCode key, CachedCommand *cc) | |
| 13 | { | ||
| 14 | 1955 | cached_command_free(intmap_insert_or_replace(bindings, key, cc)); | |
| 15 | 1955 | } | |
| 16 | |||
| 17 | 1 | void remove_binding(IntMap *bindings, KeyCode key) | |
| 18 | { | ||
| 19 | 1 | cached_command_free(intmap_remove(bindings, key)); | |
| 20 | 1 | } | |
| 21 | |||
| 22 | 11 | const CachedCommand *lookup_binding(const IntMap *bindings, KeyCode key) | |
| 23 | { | ||
| 24 | 11 | return intmap_get(bindings, key); | |
| 25 | } | ||
| 26 | |||
| 27 | 34 | void free_bindings(IntMap *bindings) | |
| 28 | { | ||
| 29 | 34 | intmap_free(bindings, FREE_FUNC(cached_command_free)); | |
| 30 | 34 | } | |
| 31 | |||
| 32 | 3 | bool handle_binding(EditorState *e, const ModeHandler *handler, KeyCode key) | |
| 33 | { | ||
| 34 | 3 | const CachedCommand *binding = lookup_binding(&handler->key_bindings, key); | |
| 35 |
1/2✓ Branch 0 (3→4) taken 3 times.
✗ Branch 1 (3→15) not taken.
|
3 | if (!binding) { |
| 36 | return false; | ||
| 37 | } | ||
| 38 | |||
| 39 | 3 | const CommandSet *cmds = handler->cmds; | |
| 40 | 3 | BUG_ON(!cmds); | |
| 41 | |||
| 42 | // If the command isn't cached or a macro is being recorded | ||
| 43 |
3/6✓ Branch 0 (6→7) taken 3 times.
✗ Branch 1 (6→9) not taken.
✓ Branch 2 (7→8) taken 3 times.
✗ Branch 3 (7→11) not taken.
✗ Branch 4 (8→9) not taken.
✓ Branch 5 (8→11) taken 3 times.
|
3 | if (!binding->cmd || (cmds->macro_record && macro_is_recording(&e->macro))) { |
| 44 | // Parse and run command string | ||
| 45 | ✗ | CommandRunner runner = cmdrunner(e, cmds); | |
| 46 | ✗ | runner.flags |= CMDRUNNER_ALLOW_RECORDING; | |
| 47 | ✗ | return handle_command(&runner, binding->cmd_str); | |
| 48 | } | ||
| 49 | |||
| 50 | // Command is cached; call it directly | ||
| 51 | 3 | begin_change(CHANGE_MERGE_NONE); | |
| 52 | 3 | command_func_call(e, &e->err, binding->cmd, &binding->a); | |
| 53 | 3 | end_change(); | |
| 54 | 3 | return true; | |
| 55 | } | ||
| 56 | |||
| 57 | typedef struct { | ||
| 58 | KeyCode key; | ||
| 59 | const char *cmd; | ||
| 60 | } KeyBinding; | ||
| 61 | |||
| 62 | 1207 | static int binding_cmp(const void *ap, const void *bp) | |
| 63 | { | ||
| 64 | 1207 | static_assert((MOD_MASK | KEY_SPECIAL_MAX) <= INT_MAX); | |
| 65 | 1207 | const KeyBinding *a = ap; | |
| 66 | 1207 | const KeyBinding *b = bp; | |
| 67 | 1207 | return (int)a->key - (int)b->key; | |
| 68 | } | ||
| 69 | |||
| 70 | 24 | UNITTEST { | |
| 71 | // NOLINTBEGIN(bugprone-assert-side-effect) | ||
| 72 | 24 | KeyBinding a = {.key = KEY_F5}; | |
| 73 | 24 | KeyBinding b = {.key = KEY_F5}; | |
| 74 | 24 | BUG_ON(binding_cmp(&a, &b) != 0); | |
| 75 | 24 | b.key = KEY_F3; | |
| 76 | 24 | BUG_ON(binding_cmp(&a, &b) <= 0); | |
| 77 | 24 | b.key = KEY_F12; | |
| 78 | 24 | BUG_ON(binding_cmp(&a, &b) >= 0); | |
| 79 | // NOLINTEND(bugprone-assert-side-effect) | ||
| 80 | 24 | } | |
| 81 | |||
| 82 | 3 | bool dump_bindings(const IntMap *bindings, const char *flag, String *buf) | |
| 83 | { | ||
| 84 | 3 | const size_t count = bindings->count; | |
| 85 |
1/2✓ Branch 0 (2→3) taken 3 times.
✗ Branch 1 (2→26) not taken.
|
3 | if (unlikely(count == 0)) { |
| 86 | return false; | ||
| 87 | } | ||
| 88 | |||
| 89 | // Clone the contents of the map as an array of key/command pairs | ||
| 90 | 3 | KeyBinding *array = xmallocarray(count, sizeof(*array)); | |
| 91 | 3 | size_t n = 0; | |
| 92 |
2/2✓ Branch 0 (7→5) taken 218 times.
✓ Branch 1 (7→8) taken 3 times.
|
221 | for (IntMapIter it = intmap_iter(bindings); intmap_next(&it); ) { |
| 93 | 218 | const CachedCommand *cc = it.entry->value; | |
| 94 | 218 | array[n++] = (KeyBinding) { | |
| 95 | 218 | .key = it.entry->key, | |
| 96 | 218 | .cmd = cc->cmd_str, | |
| 97 | }; | ||
| 98 | } | ||
| 99 | |||
| 100 | // Sort the array | ||
| 101 | 3 | BUG_ON(n != count); | |
| 102 | 3 | qsort(array, count, sizeof(array[0]), binding_cmp); | |
| 103 | |||
| 104 | // Serialize the bindings in sorted order | ||
| 105 | 3 | char keystr[KEYCODE_STR_BUFSIZE]; | |
| 106 |
2/2✓ Branch 0 (24→12) taken 218 times.
✓ Branch 1 (24→25) taken 3 times.
|
221 | for (size_t i = 0; i < count; i++) { |
| 107 | 218 | string_append_literal(buf, "bind "); | |
| 108 |
1/2✗ Branch 0 (13→14) not taken.
✓ Branch 1 (13→17) taken 218 times.
|
218 | if (flag[0] != '\0' && flag[0] != '-') { |
| 109 | ✗ | string_append_cstring(buf, "-T "); | |
| 110 | ✗ | string_append_escaped_arg(buf, flag, true); | |
| 111 | ✗ | string_append_byte(buf, ' '); | |
| 112 | } else { | ||
| 113 | 218 | string_append_cstring(buf, flag); | |
| 114 | } | ||
| 115 | 218 | size_t keylen = keycode_to_str(array[i].key, keystr); | |
| 116 | 218 | string_append_escaped_arg_sv(buf, string_view(keystr, keylen), true); | |
| 117 | 218 | string_append_byte(buf, ' '); | |
| 118 | 218 | string_append_escaped_arg(buf, array[i].cmd, true); | |
| 119 | 218 | string_append_byte(buf, '\n'); | |
| 120 | } | ||
| 121 | |||
| 122 | 3 | free(array); | |
| 123 | 3 | return true; | |
| 124 | } | ||
| 125 |