Show set-cmd-text command bindings in completion.

Fixes #5942.

For bindings like `o -> :set-cmd-text -s :open`, the user conceptually
expects that `o` maps to `open`, yet they do not show in the command
completion UI.

With this patch, a binding to a command of form
`set-cmd-text [flags...] :<cmd> [cmdargs]` will be treated as if it were
bound to `<cmd> [cmdargs]` instead for the purpose of completion.
Bindings to `set-cmd-text --append` are ignored.
This commit is contained in:
Ryan Roden-Corrent 2020-12-17 17:25:00 -05:00
parent 8e7f24bc0c
commit 2c4bb064e6
No known key found for this signature in database
GPG Key ID: 435D8B10692555C9
2 changed files with 49 additions and 2 deletions

View File

@ -162,13 +162,40 @@ class KeyConfig:
bindings[key] = binding
return bindings
def _strip_leading_flags(self, cmdline: str) -> List[str]:
"""Split cmdline at whitespace until the first non-flag."""
first, _, rest = cmdline.partition(" ")
if first.startswith("-"):
return [first] + self._strip_leading_flags(rest)
return [cmdline]
def _implied_cmd(self, cmdline: str) -> str:
"""Return cmdline, or the implied cmd if cmdline is a set-cmd-text."""
if not cmdline.startswith("set-cmd-text "):
return cmdline
cmdline = cmdline[len("set-cmd-text "):]
*flags, cmd = self._strip_leading_flags(cmdline)
if "-a" in flags or "--append" in flags or not cmd.startswith(":"):
return "" # doesn't look like this sets a command
return cmd.lstrip(":")
def get_reverse_bindings_for(self, mode: str) -> '_ReverseBindings':
"""Get a dict of commands to a list of bindings for the mode."""
"""Get a dict of commands to a list of bindings for the mode.
This is intented for user-facing display of keybindings.
As such, bindings for 'set-cmd-text [flags] :<cmd> ...' are translated
to '<cmd> ...', as from the user's perspective these keys behave like
bindings for '<cmd>' (that allow for further input before running).
See #5942.
"""
cmd_to_keys: KeyConfig._ReverseBindings = {}
bindings = self.get_bindings_for(mode)
for seq, full_cmd in sorted(bindings.items()):
for cmd in full_cmd.split(';;'):
cmd = cmd.strip()
cmd = self._implied_cmd(cmd.strip())
if not cmd:
continue
cmd_to_keys.setdefault(cmd, [])
# Put bindings involving modifiers last
if any(info.modifiers for info in seq):

View File

@ -198,6 +198,26 @@ class TestKeyConfig:
# Chained command
({'a': 'message-info foo ;; message-info bar'},
{'message-info foo': ['a'], 'message-info bar': ['a']}),
# Command using set-cmd-text (#5942)
(
{
"o": "set-cmd-text -s :open",
"O": "set-cmd-text -s :open -t",
"go": "set-cmd-text :open {url:pretty}",
# all of these should be ignored
"/": "set-cmd-text /",
"?": "set-cmd-text ?",
":": "set-cmd-text :",
"a": "set-cmd-text no_leading_colon",
"b": "set-cmd-text -s -a :skip_cuz_append",
"c": "set-cmd-text --append :skip_cuz_append",
},
{
"open": ["o"],
"open -t": ["O"],
"open {url:pretty}": ["go"],
}
),
])
def test_get_reverse_bindings_for(self, key_config_stub, config_stub,
no_bindings, bindings, expected):