Complete aliases that expand to a shell escape

`alias ll '!ls -la'` makes everything after `ll` a shell argument just as
surely as typing the `!` does, but Tab there still went to the builtin
command tree and found nothing. The dispatch now asks what a line will
turn into rather than how it starts: a '!' escape, or a name that is not a
builtin and resolves to an alias whose body starts with '!'.

Only the arguments complete — the command word is fixed by the alias
body, so `ll vi` offers the file, never the editor. An alias to a builtin
stays with the builtin tree.

Only the alias itself is inspected, not what its expansion might expand
to in turn: an alias chain can rewrite its own arguments, and guessing at
that would offer candidates for a command line other than the one being
built.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-26 18:28:35 +02:00
co-authored by Claude Opus 5
parent 2acca170e6
commit 0d0d28560e
5 changed files with 142 additions and 27 deletions
+5 -5
View File
@@ -9,9 +9,10 @@ import (
"github.com/chzyer/readline"
)
// completer wires up Tab completion. A line starting with '!' is completed the
// way a shell would — executables for the command, paths for its arguments —
// and everything else against the builtin command tree.
// completer wires up Tab completion. A line headed for a shell — a '!' escape,
// or an alias that expands to one — is completed the way a shell would:
// executables for the command, paths for its arguments. Everything else goes to
// the builtin command tree.
func completer() readline.AutoCompleter {
return &mgshCompleter{builtin: builtinCompleter()}
}
@@ -23,8 +24,7 @@ func (c *mgshCompleter) Do(line []rune, pos int) ([][]rune, int) {
if pos > len(line) {
pos = len(line)
}
if strings.HasPrefix(strings.TrimLeft(string(line[:pos]), " \t"), "!") {
cands, prefix := shellCandidates(string(line[:pos]))
if cands, prefix, ok := completeShellLine(string(line[:pos])); ok {
return runeSuffixes(cands, prefix)
}
return c.builtin.Do(line, pos)