diff --git a/internal/action/bufpane.go b/internal/action/bufpane.go index d0c369b1c4..da9c8868b0 100644 --- a/internal/action/bufpane.go +++ b/internal/action/bufpane.go @@ -100,9 +100,7 @@ func BufMapEvent(k Event, action string) { break } - // TODO: fix problem when complex bindings have these - // characters (escape them?) - idx := strings.IndexAny(action, "&|,") + idx := util.IndexAnyUnquoted(action, "&|,") a := action if idx >= 0 { a = action[:idx] diff --git a/internal/util/util.go b/internal/util/util.go index c2349f6c09..bcfeca07f8 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -320,6 +320,28 @@ func RunePos(b []byte, i int) int { return CharacterCount(b[:i]) } +// IndexAnyUnquoted returns the first position in s of a character from chars. +// Escaped (with backslash) and quoted (with single or double quotes) characters +// are ignored. Returns -1 if not successful +func IndexAnyUnquoted(s, chars string) int { + var e bool + var q rune + for i, r := range s { + if e { + e = false + } else if (q == 0 || q == '"') && r == '\\' { + e = true + } else if r == q { + q = 0 + } else if q == 0 && (r == '\'' || r == '"') { + q = r + } else if q == 0 && strings.IndexRune(chars, r) >= 0 { + return i + } + } + return -1 +} + // MakeRelative will attempt to make a relative path between path and base func MakeRelative(path, base string) (string, error) { if len(path) > 0 { diff --git a/runtime/help/keybindings.md b/runtime/help/keybindings.md index ecac295786..e0c5c869b4 100644 --- a/runtime/help/keybindings.md +++ b/runtime/help/keybindings.md @@ -66,7 +66,9 @@ bindings, tab is bound as This means that if the `Autocomplete` action is successful, the chain will abort. Otherwise, it will try `IndentSelection`, and if that fails too, it -will execute `InsertTab`. +will execute `InsertTab`. To use `,`, `|` or `&` in an action (as an argument +to a command, for example), escape it with `\` or wrap it in single or double +quotes. ## Binding commands