From f5debdf8fe94ebf55ab95824c678938ce439926e Mon Sep 17 00:00:00 2001 From: matthias314 <56549971+matthias314@users.noreply.github.com> Date: Mon, 20 Jan 2025 14:28:38 -0500 Subject: [PATCH] ignore quoted and escaped characters when splitting keybindings into actions (#3612) --- internal/action/bufpane.go | 4 +--- internal/util/util.go | 22 ++++++++++++++++++++++ runtime/help/keybindings.md | 4 +++- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/internal/action/bufpane.go b/internal/action/bufpane.go index d0c369b1..da9c8868 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 c2349f6c..bcfeca07 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 ecac2957..e0c5c869 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