vis

a vi-like editor based on Plan 9's structural regular expressions

git clone https://9o.is/git/vis.git

commit 46f00c2dc9aa99a2c7537e457a162978670eeaf5
parent 2d5b199f691007def544c3eeb0a9d8044d7aa1d8
Author: Marc André Tanner <mat@brain-dump.org>
Date:   Wed, 14 Oct 2015 23:00:47 +0200

vis: introduce special keys which allow mappings to editor actions

Key bindings in vis are always recursive, hence mapping ~
to ~l will cause an infinite loop.

Instead vis supports special editor "keys" which map to
internal editor functions.

As an example one can thus map ~ to <vis-operator-case-swap>l
or even <vis-operator-case-swap><cursor-char-next>

Furthermore this makes it possible to completely unmap core
editor features such as operators, the corresponding funtionality
is still available via its corresponding special key.

Diffstat:
Meditor.c | 9+++++++++
Meditor.h | 3+++
Mvis.c | 33++++++++++++++++++++++++++++++++-
3 files changed, 44 insertions(+), 1 deletion(-)

diff --git a/editor.c b/editor.c @@ -252,6 +252,14 @@ bool editor_mode_unmap(Mode *mode, const char *name) { return map_delete(mode->bindings, name); } +bool editor_action_register(Editor *ed, KeyAction *action) { + if (!ed->actions) + ed->actions = map_new(); + if (!ed->actions) + return false; + return map_put(ed->actions, action->name, action); +} + static void window_free(Win *win) { if (!win) return; @@ -430,6 +438,7 @@ void editor_free(Editor *ed) { ed->ui->free(ed->ui); map_free(ed->cmds); map_free(ed->options); + map_free(ed->actions); buffer_release(&ed->buffer_repeat); free(ed); } diff --git a/editor.h b/editor.h @@ -253,6 +253,7 @@ struct Editor { volatile sig_atomic_t cancel_filter; /* abort external command */ volatile sig_atomic_t sigbus; sigjmp_buf sigbus_jmpbuf; + Map *actions; /* built in special editor keys / commands */ }; Editor *editor_new(Ui*); @@ -266,6 +267,8 @@ bool editor_mode_bindings(Mode*, KeyBinding**); bool editor_mode_map(Mode*, const char *name, KeyBinding*); bool editor_mode_unmap(Mode*, const char *name); +bool editor_action_register(Editor*, KeyAction*); + /* these function operate on the currently focused window but make sure * that all windows which show the affected region are redrawn too. */ void editor_insert_key(Editor*, const char *data, size_t len); diff --git a/vis.c b/vis.c @@ -2600,6 +2600,18 @@ static const char *keynext(const char *keys) { /* first try to parse a special key of the form <Key> */ if (*keys == '<' && (next = termkey_strpkey(termkey, keys+1, &key, TERMKEY_FORMAT_VIM)) && *next == '>') return next+1; + if (*keys == '<') { + const char *start = keys + 1, *end = start; + while (*end && *end != '>') + end++; + if (end > start && end - start - 1 < 64 && *end == '>') { + char key[64]; + memcpy(key, start, end - start); + key[end - start] = '\0'; + if (map_get(vis->actions, key)) + return end + 1; + } + } while (!ISUTF8(*keys)) keys++; return termkey_strpkey(termkey, keys, &key, TERMKEY_FORMAT_VIM); @@ -2655,7 +2667,20 @@ static const char *keypress(const char *input) { } else if (prefix) { /* incomplete key binding? */ cur = end; } else { /* no keybinding */ - if (vis->mode->input) + KeyAction *action = NULL; + if (start[0] == '<' && end[-1] == '>') { + /* test for special editor key command */ + char tmp = end[-1]; + end[-1] = '\0'; + action = map_get(vis->actions, start+1); + end[-1] = tmp; + if (action) { + end = (char*)action->func(end, &action->arg); + if (!end) + break; + } + } + if (!action && vis->mode->input) vis->mode->input(start, end - start); start = cur = end; } @@ -2780,6 +2805,12 @@ int main(int argc, char *argv[]) { if (!editor_syntax_load(vis, syntaxes)) die("Could not load syntax highlighting definitions\n"); + for (int i = 0; i < LENGTH(vis_action); i++) { + KeyAction *action = &vis_action[i]; + if (!editor_action_register(vis, action)) + die("Could not register action: %s\n", action->name); + } + char *cmd = NULL; bool end_of_options = false; for (int i = 1; i < argc; i++) {