Complete ! shell escapes like a shell

A line starting with '!' was not in the completer tree at all, so Tab did
nothing there -- just where the paths are longest. It now completes the
way a shell does: the command word against the executables on PATH, the
arguments against the filesystem, resolved relative to the active project
because that is where forwardShell runs the line. Directories complete
with their trailing slash, `~/` and absolute paths work, and dot entries
stay hidden until the prefix asks for one.

readline's completer is a tree of fixed words, which cannot express "a
prefix that is not a word", so this is a small AutoCompleter that
dispatches on the '!' and hands everything else to the existing tree. Its
contract is easy to get subtly wrong -- candidates are the suffixes still
missing, and the length is counted in runes, not bytes -- so the
conversion has its own test, as does completing only the basename inside
a directory, which is what keeps the candidate list readable.

Only the basename is offered inside a directory, PATH is scanned once per
session, and a bare '!' offers nothing rather than every executable on
the machine.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-26 18:18:01 +02:00
co-authored by Claude Opus 5
parent 2a622046f2
commit 2acca170e6
5 changed files with 399 additions and 7 deletions
+19 -2
View File
@@ -53,8 +53,9 @@ project, its git branch and a `*` dirty marker:
Features: command history (`~/.mgsh_history`), Tab completion (commands, local
projects for `cd`/`open`, server repos for `clone`/`show`, branches/tags for
`checkout`/`tag`, mirror targets for `pushremote`, filesystem paths for `dist`),
and colored `list`/`log`/error output.
`checkout`/`tag`, mirror targets for `pushremote`/`release`, filesystem paths for
`dist`, and shell-style completion after `!`), and colored `list`/`log`/error
output.
The server repository list is fetched once per session on the first Tab that
needs it; `rescan` refreshes it (and reloads the configuration).
@@ -70,6 +71,22 @@ prefix it with `!`:
< src/myproject > !ls -la
```
It runs in the active project's directory. Tab completion works there the way it
does in a shell: the word after the `!` completes against the executables on
`PATH`, everything after it against the filesystem — relative to the project,
with `~/` and absolute paths understood, and directories completing with their
trailing slash so the next Tab walks into them. Dot entries stay out of the way
until the prefix asks for one.
```
< src/myproject > !vi ma<Tab> -> !vi main
< src/myproject > !vi <Tab> -> Makefile main.go main_test.go src/
< src/myproject > !gre<Tab> -> grep gresource
```
Word splitting for completion is by whitespace only; quotes and backslash
escapes are left to the shell that runs the line.
### Commands
Run `help` for the full list. Highlights: