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