diff options
Diffstat (limited to 'examples/dte/bind.c')
| -rw-r--r-- | examples/dte/bind.c | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/examples/dte/bind.c b/examples/dte/bind.c new file mode 100644 index 0000000..222ee27 --- /dev/null +++ b/examples/dte/bind.c @@ -0,0 +1,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; +} |
