From 9997739b96a7fc142a18cb72cb7589bd4b4d61b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Andr=C3=A9=20Tanner?= Date: Fri, 16 Dec 2016 12:37:55 +0100 Subject: vis: change key input handling model Previously if you had a mapping for both `a` and `ab` the latter would in effect be unreachable because the greedy search would always match and then execute the former. With the new behavior we keep reading keys until we have a non ambigious sequence. That is after pressing `a` nothing will happen, if the next key is a `b` we will execute the `ab` mapping otherwise we will perform `a` and whatever the action is for the next key. Close #386 --- vis-modes.c | 4 ++-- vis.c | 45 ++++++++++++++++++++++++++++----------------- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/vis-modes.c b/vis-modes.c index f28856b..8df7b16 100644 --- a/vis-modes.c +++ b/vis-modes.c @@ -121,8 +121,8 @@ static bool mode_map(Vis *vis, Mode *mode, bool force, const char *key, const Ke if (!mode->bindings && !(mode->bindings = map_new())) return false; if (force) - map_prefix_delete(mode->bindings, key); - return (strcmp(key, "<") == 0 || !map_contains(mode->bindings, key)) && map_put(mode->bindings, key, binding); + map_delete(mode->bindings, key); + return map_put(mode->bindings, key, binding); } bool vis_mode_map(Vis *vis, enum VisMode id, bool force, const char *key, const KeyBinding *binding) { diff --git a/vis.c b/vis.c index f3ab4f6..a89cb8d 100644 --- a/vis.c +++ b/vis.c @@ -946,7 +946,7 @@ long vis_keys_codepoint(Vis *vis, const char *keys) { static void vis_keys_process(Vis *vis, size_t pos) { Buffer *buf = vis->keys; - char *keys = buf->data + pos, *start = keys, *cur = keys, *end = keys; + char *keys = buf->data + pos, *start = keys, *cur = keys, *end = keys, *binding_end = keys;; bool prefix = false; KeyBinding *binding = NULL; @@ -960,39 +960,50 @@ static void vis_keys_process(Vis *vis, size_t pos) { char tmp = *end; *end = '\0'; prefix = false; - binding = NULL; - for (Mode *mode = vis->mode; mode && !binding && !prefix; mode = mode->parent) { - for (int global = 0; global < 2 && !binding && !prefix; global++) { - Mode *mode_local = global || !vis->win ? mode : &vis->win->modes[mode->id]; - if (!mode_local->bindings) + for (Mode *global_mode = vis->mode; global_mode && !prefix; global_mode = global_mode->parent) { + for (int global = 0; global < 2 && !prefix; global++) { + Mode *mode = (global || !vis->win) ? + global_mode : + &vis->win->modes[global_mode->id]; + if (!mode->bindings) continue; - binding = map_get(mode_local->bindings, start); - /* "<" is never treated as a prefix because it is used to denote - * special key symbols */ - if (strcmp(cur, "<")) - prefix = !binding && map_contains(mode_local->bindings, start); + /* keep track of longest matching binding */ + KeyBinding *match = map_get(mode->bindings, start); + if (match && end > binding_end) { + binding = match; + binding_end = end; + } + /* "<" is never treated as a prefix because it + * is used to denote special key symbols */ + if (strcmp(start, "<")) { + prefix = (!match && map_contains(mode->bindings, start)) || + (match && !map_leaf(mode->bindings, start)); + } } } *end = tmp; - if (binding) { /* exact match */ + if (prefix) { + /* input sofar is ambigious, wait for more */ + cur = end; + end = start; + } else if (binding) { /* exact match */ if (binding->action) { - end = (char*)binding->action->func(vis, end, &binding->action->arg); + end = (char*)binding->action->func(vis, binding_end, &binding->action->arg); if (!end) { end = start; break; } start = cur = end; } else if (binding->alias) { - buffer_remove(buf, start - buf->data, end - start); + buffer_remove(buf, start - buf->data, binding_end - start); buffer_insert0(buf, start - buf->data, binding->alias); cur = end = start; } - } else if (prefix) { /* incomplete key binding? */ - cur = end; - end = start; + binding = NULL; + binding_end = start; } else { /* no keybinding */ KeyAction *action = NULL; if (start[0] == '<' && end[-1] == '>') { -- cgit v1.2.3