aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Culverhouse <tim@timculverhouse.com>2024-02-19 18:19:54 -0600
committerRobin Jarry <robin@jarry.cc>2024-02-22 21:46:15 +0100
commit7917372e94e13a478003c3c4663c3e2239e7b032 (patch)
treede1e5c4010f38cea4627ecda92529fbd7d752727
parent782a17dfb056b526bb41858978302a11a9934337 (diff)
downloadaerc-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.go141
-rw-r--r--config/binds_test.go13
-rw-r--r--doc/aerc-binds.5.scd182
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