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