diff options
author | Tim Culverhouse <tim@timculverhouse.com> | 2024-02-19 18:19:54 -0600 |
---|---|---|
committer | Robin Jarry <robin@jarry.cc> | 2024-02-22 21:46:15 +0100 |
commit | 7917372e94e13a478003c3c4663c3e2239e7b032 (patch) | |
tree | de1e5c4010f38cea4627ecda92529fbd7d752727 | |
parent | 782a17dfb056b526bb41858978302a11a9934337 (diff) | |
download | aerc-7917372e94e13a478003c3c4663c3e2239e7b032.tar.gz |
binds: refactor parser to be more tolerant
Refactor the bind parser to construct arbitrary modified keys instead of
only relying on built in maps. Add additional tests to cover edge cases.
Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
Acked-by: Robin Jarry <robin@jarry.cc>
-rw-r--r-- | config/binds.go | 141 | ||||
-rw-r--r-- | config/binds_test.go | 13 | ||||
-rw-r--r-- | doc/aerc-binds.5.scd | 182 |
3 files changed, 64 insertions, 272 deletions
diff --git a/config/binds.go b/config/binds.go index 95fa8c7d..3ddc2578 100644 --- a/config/binds.go +++ b/config/binds.go @@ -10,6 +10,7 @@ import ( "regexp" "strings" "unicode" + "unicode/utf8" "git.sr.ht/~rjarry/aerc/lib/log" "git.sr.ht/~rockorager/vaxis" @@ -474,10 +475,8 @@ func FormatKeyStrokes(keystrokes []KeyStroke) string { for name, ks := range keyNames { if ks.Modifiers == stroke.Modifiers && ks.Key == stroke.Key { switch name { - case "cr", "c-m": + case "cr": s = "<enter>" - case "c-i": - s = "<tab>" case "space": s = " " case "semicolon": @@ -485,9 +484,21 @@ func FormatKeyStrokes(keystrokes []KeyStroke) string { default: s = fmt.Sprintf("<%s>", name) } + // remove any modifiers this named key comes + // with so we format properly + stroke.Modifiers &^= ks.Modifiers break } } + if stroke.Modifiers&vaxis.ModCtrl > 0 { + sb.WriteString("c-") + } + if stroke.Modifiers&vaxis.ModAlt > 0 { + sb.WriteString("a-") + } + if stroke.Modifiers&vaxis.ModShift > 0 { + sb.WriteString("s-") + } if s == "" && stroke.Key < unicode.MaxRune { s = string(stroke.Key) } @@ -512,37 +523,21 @@ var keyNames = map[string]KeyStroke{ "space": {vaxis.ModifierMask(0), ' '}, "semicolon": {vaxis.ModifierMask(0), ';'}, "enter": {vaxis.ModifierMask(0), vaxis.KeyEnter}, - "c-enter": {vaxis.ModCtrl, vaxis.KeyEnter}, - "a-enter": {vaxis.ModAlt, vaxis.KeyEnter}, "up": {vaxis.ModifierMask(0), vaxis.KeyUp}, - "c-up": {vaxis.ModCtrl, vaxis.KeyUp}, - "a-up": {vaxis.ModAlt, vaxis.KeyUp}, "down": {vaxis.ModifierMask(0), vaxis.KeyDown}, - "c-down": {vaxis.ModCtrl, vaxis.KeyDown}, - "a-down": {vaxis.ModAlt, vaxis.KeyDown}, "right": {vaxis.ModifierMask(0), vaxis.KeyRight}, - "c-right": {vaxis.ModCtrl, vaxis.KeyRight}, - "a-right": {vaxis.ModAlt, vaxis.KeyRight}, "left": {vaxis.ModifierMask(0), vaxis.KeyLeft}, - "c-left": {vaxis.ModCtrl, vaxis.KeyLeft}, - "a-left": {vaxis.ModAlt, vaxis.KeyLeft}, "upleft": {vaxis.ModifierMask(0), vaxis.KeyUpLeft}, "upright": {vaxis.ModifierMask(0), vaxis.KeyUpRight}, "downleft": {vaxis.ModifierMask(0), vaxis.KeyDownLeft}, "downright": {vaxis.ModifierMask(0), vaxis.KeyDownRight}, "center": {vaxis.ModifierMask(0), vaxis.KeyCenter}, "pgup": {vaxis.ModifierMask(0), vaxis.KeyPgUp}, - "c-pgup": {vaxis.ModCtrl, vaxis.KeyPgUp}, - "a-pgup": {vaxis.ModAlt, vaxis.KeyPgUp}, "pgdn": {vaxis.ModifierMask(0), vaxis.KeyPgDown}, - "c-pgdn": {vaxis.ModCtrl, vaxis.KeyPgDown}, - "a-pgdn": {vaxis.ModAlt, vaxis.KeyPgDown}, "home": {vaxis.ModifierMask(0), vaxis.KeyHome}, "end": {vaxis.ModifierMask(0), vaxis.KeyEnd}, "insert": {vaxis.ModifierMask(0), vaxis.KeyInsert}, "delete": {vaxis.ModifierMask(0), vaxis.KeyDelete}, - "c-delete": {vaxis.ModCtrl, vaxis.KeyDelete}, - "a-delete": {vaxis.ModAlt, vaxis.KeyDelete}, "backspace": {vaxis.ModifierMask(0), vaxis.KeyBackspace}, // "help": {vaxis.ModifierMask(0), vaxis.KeyHelp}, "exit": {vaxis.ModifierMask(0), vaxis.KeyExit}, @@ -614,80 +609,6 @@ var keyNames = map[string]KeyStroke{ "f61": {vaxis.ModifierMask(0), vaxis.KeyF61}, "f62": {vaxis.ModifierMask(0), vaxis.KeyF62}, "f63": {vaxis.ModifierMask(0), vaxis.KeyF63}, - "c-space": {vaxis.ModCtrl, ' '}, - "c-a": {vaxis.ModCtrl, 'a'}, - "c-b": {vaxis.ModCtrl, 'b'}, - "c-c": {vaxis.ModCtrl, 'c'}, - "c-d": {vaxis.ModCtrl, 'd'}, - "c-e": {vaxis.ModCtrl, 'e'}, - "c-f": {vaxis.ModCtrl, 'f'}, - "c-g": {vaxis.ModCtrl, 'g'}, - "c-h": {vaxis.ModCtrl, 'h'}, - "c-i": {vaxis.ModCtrl, 'i'}, - "c-j": {vaxis.ModCtrl, 'j'}, - "c-k": {vaxis.ModCtrl, 'k'}, - "c-l": {vaxis.ModCtrl, 'l'}, - "c-m": {vaxis.ModCtrl, 'm'}, - "c-n": {vaxis.ModCtrl, 'n'}, - "c-o": {vaxis.ModCtrl, 'o'}, - "c-p": {vaxis.ModCtrl, 'p'}, - "c-q": {vaxis.ModCtrl, 'q'}, - "c-r": {vaxis.ModCtrl, 'r'}, - "c-s": {vaxis.ModCtrl, 's'}, - "c-t": {vaxis.ModCtrl, 't'}, - "c-u": {vaxis.ModCtrl, 'u'}, - "c-v": {vaxis.ModCtrl, 'v'}, - "c-w": {vaxis.ModCtrl, 'w'}, - "c-x": {vaxis.ModCtrl, 'x'}, - "c-y": {vaxis.ModCtrl, 'y'}, - "c-z": {vaxis.ModCtrl, 'z'}, - "c-]": {vaxis.ModCtrl, ']'}, - "c-\\": {vaxis.ModCtrl, '\\'}, - "c-[": {vaxis.ModCtrl, '['}, - "c-^": {vaxis.ModCtrl, '^'}, - "c-_": {vaxis.ModCtrl, '_'}, - "a-space": {vaxis.ModAlt, ' '}, - "a-0": {vaxis.ModAlt, '0'}, - "a-1": {vaxis.ModAlt, '1'}, - "a-2": {vaxis.ModAlt, '2'}, - "a-3": {vaxis.ModAlt, '3'}, - "a-4": {vaxis.ModAlt, '4'}, - "a-5": {vaxis.ModAlt, '5'}, - "a-6": {vaxis.ModAlt, '6'}, - "a-7": {vaxis.ModAlt, '7'}, - "a-8": {vaxis.ModAlt, '8'}, - "a-9": {vaxis.ModAlt, '9'}, - "a-a": {vaxis.ModAlt, 'a'}, - "a-b": {vaxis.ModAlt, 'b'}, - "a-c": {vaxis.ModAlt, 'c'}, - "a-d": {vaxis.ModAlt, 'd'}, - "a-e": {vaxis.ModAlt, 'e'}, - "a-f": {vaxis.ModAlt, 'f'}, - "a-g": {vaxis.ModAlt, 'g'}, - "a-h": {vaxis.ModAlt, 'h'}, - "a-i": {vaxis.ModAlt, 'i'}, - "a-j": {vaxis.ModAlt, 'j'}, - "a-k": {vaxis.ModAlt, 'k'}, - "a-l": {vaxis.ModAlt, 'l'}, - "a-m": {vaxis.ModAlt, 'm'}, - "a-n": {vaxis.ModAlt, 'n'}, - "a-o": {vaxis.ModAlt, 'o'}, - "a-p": {vaxis.ModAlt, 'p'}, - "a-q": {vaxis.ModAlt, 'q'}, - "a-r": {vaxis.ModAlt, 'r'}, - "a-s": {vaxis.ModAlt, 's'}, - "a-t": {vaxis.ModAlt, 't'}, - "a-u": {vaxis.ModAlt, 'u'}, - "a-v": {vaxis.ModAlt, 'v'}, - "a-w": {vaxis.ModAlt, 'w'}, - "a-x": {vaxis.ModAlt, 'x'}, - "a-y": {vaxis.ModAlt, 'y'}, - "a-z": {vaxis.ModAlt, 'z'}, - "a-]": {vaxis.ModAlt, ']'}, - "a-\\": {vaxis.ModAlt, '\\'}, - "a-[": {vaxis.ModAlt, '['}, - "a-^": {vaxis.ModAlt, '^'}, - "a-_": {vaxis.ModAlt, '_'}, "nul": {vaxis.ModCtrl, ' '}, "soh": {vaxis.ModCtrl, 'a'}, "stx": {vaxis.ModCtrl, 'b'}, @@ -747,10 +668,36 @@ func ParseKeyStrokes(keystrokes string) ([]KeyStroke, error) { return nil, errors.New("Expected a key name") } name = name[:len(name)-1] - if key, ok := keyNames[strings.ToLower(name)]; ok { - strokes = append(strokes, key) - } else { - return nil, fmt.Errorf("Unknown key '%s'", name) + args := strings.Split(name, "-") + // check if the last char was a '-' and we'll add it + // back. We check for "--" in case it was an invalid + // keystroke (ie <C->) + if strings.HasSuffix(name, "--") { + args = append(args, "-") + } + ks := KeyStroke{} + for i, arg := range args { + if i == len(args)-1 { + key, ok := keyNames[strings.ToLower(arg)] + if !ok { + r, n := utf8.DecodeRuneInString(arg) + if n != len(arg) { + return nil, fmt.Errorf("Unknown key '%s'", name) + } + key = KeyStroke{Key: r} + } + ks.Key = key.Key + ks.Modifiers |= key.Modifiers + strokes = append(strokes, ks) + } + switch strings.ToLower(arg) { + case "s", "S": + ks.Modifiers |= vaxis.ModShift + case "a", "A": + ks.Modifiers |= vaxis.ModAlt + case "c", "C": + ks.Modifiers |= vaxis.ModCtrl + } } case '>': return nil, errors.New("Found '>' without '<'") diff --git a/config/binds_test.go b/config/binds_test.go index a92cf22f..7d4cd779 100644 --- a/config/binds_test.go +++ b/config/binds_test.go @@ -13,7 +13,10 @@ func TestGetBinding(t *testing.T) { bindings := NewKeyBindings() add := func(binding, cmd string) { - b, _ := ParseBinding(binding, cmd, "") + b, err := ParseBinding(binding, cmd, "") + if err != nil { + t.Fatal(err) + } bindings.Add(b) } @@ -58,6 +61,8 @@ func TestGetBinding(t *testing.T) { add("<C-Down>", ":next") add("<C-PgUp>", ":prev") add("<C-Enter>", ":open") + add("<C-->", ":open") + add("<S-up>", ":open") test([]KeyStroke{ {vaxis.ModCtrl, 'a'}, }, BINDING_FOUND, "c-a") @@ -73,4 +78,10 @@ func TestGetBinding(t *testing.T) { test([]KeyStroke{ {vaxis.ModCtrl, vaxis.KeyEnter}, }, BINDING_FOUND, ":open") + test([]KeyStroke{ + {vaxis.ModCtrl, '-'}, + }, BINDING_FOUND, ":open") + test([]KeyStroke{ + {vaxis.ModShift, vaxis.KeyUp}, + }, BINDING_FOUND, ":open") } diff --git a/doc/aerc-binds.5.scd b/doc/aerc-binds.5.scd index a46b6427..de8f436c 100644 --- a/doc/aerc-binds.5.scd +++ b/doc/aerc-binds.5.scd @@ -147,8 +147,14 @@ available in each binding context: # SUPPORTED KEYS In addition to letters and some characters (e.g. *a*, *RR*, *gu*, *?*, *!*, -etc.), special keys may be specified in *<angle brackets>*. The following -special keys are supported: +etc.), special keys may be specified in *<angle brackets>*. The syntax for +modified or special keys is: + + <C-A-S-key> + +Where C is control, A is alt, S is shift, and key is the named key or character. + +Valid key names are: [[ *Name* :- *Description* @@ -162,40 +168,16 @@ special keys are supported: : Enter | *<up>* : Up arrow -| *<c-up>* -: Ctrl+Up -| *<a-up>* -: Alt+Up | *<down>* : Down arrow -| *<c-down>* -: Ctrl+Down -| *<a-down>* -: Alt+Down | *<right>* : Right arrow -| *<c-right>* -: Ctrl+Right -| *<a-right>* -: Alt+Right | *<left>* : Left arrow -| *<c-left>* -: Ctrl+Left -| *<a-left>* -: Alt+Left | *<pgup>* : Page Up -| *<c-pgup>* -: Ctrl+PageUp -| *<a-pgup>* -: Alt+PageUp | *<pgdn>* : Page Down -| *<c-pgdn>* -: Ctrl+PageDn -| *<a-pgdn>* -: Alt+PageDn | *<home>* : Home | *<end>* @@ -204,10 +186,6 @@ special keys are supported: : Insert | *<delete>* : Delete -| *<c-delete>* -: Ctrl+Delete -| *<a-delete>* -: Alt+Delete | *<backspace>* : Backspace | *<exit>* @@ -222,150 +200,6 @@ special keys are supported: : Shift+Tab | *<esc>* : Escape -| *<c-space>* -: Ctrl+Space -| *<a-space>* -: Alt+Space -| *<a-0>* -: Alt+0 -| *<a-1>* -: Alt+1 -| *<a-2>* -: Alt+2 -| *<a-3>* -: Alt+3 -| *<a-4>* -: Alt+4 -| *<a-5>* -: Alt+5 -| *<a-6>* -: Alt+6 -| *<a-7>* -: Alt+7 -| *<a-8>* -: Alt+8 -| *<a-9>* -: Alt+9 -| *<c-a>* -: Ctrl+a -| *<a-a>* -: Alt+a -| *<c-b>* -: Ctrl+b -| *<a-b>* -: Alt+b -| *<c-c>* -: Ctrl+c -| *<a-c>* -: Alt+c -| *<c-d>* -: Ctrl+d -| *<a-d>* -: Alt+d -| *<c-e>* -: Ctrl+e -| *<a-e>* -: Alt+e -| *<c-f>* -: Ctrl+f -| *<a-f>* -: Alt+f -| *<c-g>* -: Ctrl+g -| *<a-g>* -: Alt+g -| *<c-h>* -: Ctrl+h -| *<a-h>* -: Alt+h -| *<c-i>* -: Ctrl+i -| *<a-i>* -: Alt+i -| *<c-j>* -: Ctrl+j -| *<a-j>* -: Alt+j -| *<c-k>* -: Ctrl+k -| *<a-k>* -: Alt+k -| *<c-l>* -: Ctrl+l -| *<a-l>* -: Alt+l -| *<c-m>* -: Ctrl+m -| *<a-m>* -: Alt+m -| *<c-n>* -: Ctrl+n -| *<a-n>* -: Alt+n -| *<c-o>* -: Ctrl+o -| *<a-o>* -: Alt+o -| *<c-p>* -: Ctrl+p -| *<a-p>* -: Alt+p -| *<c-q>* -: Ctrl+q -| *<a-q>* -: Alt+q -| *<c-r>* -: Ctrl+r -| *<a-r>* -: Alt+r -| *<c-s>* -: Ctrl+s -| *<a-s>* -: Alt+s -| *<c-t>* -: Ctrl+t -| *<a-t>* -: Alt+t -| *<c-u>* -: Ctrl+u -| *<a-u>* -: Alt+u -| *<c-v>* -: Ctrl+v -| *<a-v>* -: Alt+v -| *<c-w>* -: Ctrl+w -| *<a-w>* -: Alt+w -| *<c-x>* -: Ctrl+x -| *<a-x>* -: Alt+x -| *<c-y>* -: Ctrl+y -| *<a-y>* -: Alt+y -| *<c-z>* -: Ctrl+z -| *<a-z>* -: Alt+z -| *<c-]>* -: Ctrl+] -| *<a-]>* -: Alt+] -| *<c-[>* -: Ctrl+[ -| *<a-[>* -: Alt+[ -| *<c-^>* -: Ctrl+^ -| *<a-^>* -: Alt+^ -| *<c-\_>* -: Ctrl+\_ -| *<a-\_>* -: Alt+\_ # SEE ALSO |