feat(completion): fast zsh completion for large host caches
Cobra's generated zsh script spawns a subshell per candidate and sends every host through _describe, so <Tab> hangs for seconds with thousands of cached hosts. netssh now ships its own script: subcommands and flags go through _describe, plain hostnames through compadd. 5000 hosts complete in ~0.08 s instead of ~4 s. Add completion.max_results to cap the number of offered hostnames and sort host candidates for a stable order. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7f53b90266
commit
8fb7633783
5 files changed
+172
-18
No files matched your search
@@ -0,0 +1,99 @@
|
||||
package hook
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// ZshCompletionScript replaces Cobra's generated zsh completion. Cobra's script
|
||||
// spawns a subshell per candidate and passes every host through _describe,
|
||||
// which takes tens of seconds for thousands of hosts. This script splits the
|
||||
// candidates with parameter expansion only: entries with a description
|
||||
// (subcommands, flags) go through _describe, plain hostnames through compadd.
|
||||
//
|
||||
// The file works both from fpath (autoloaded as _netssh) and when sourced.
|
||||
const ZshCompletionScript = `#compdef netssh
|
||||
compdef _netssh netssh
|
||||
|
||||
# netssh zsh completion — generated by netssh, do not edit.
|
||||
_netssh() {
|
||||
local -a lines described plain
|
||||
local out directive=0 entry ret=1
|
||||
local -a nospace
|
||||
|
||||
out=$(netssh __complete "${(@)words[2,CURRENT]}" 2>/dev/null) || return 1
|
||||
lines=("${(@f)out}")
|
||||
if [[ ${lines[-1]} == :<-> ]]; then
|
||||
directive=${lines[-1]#:}
|
||||
lines[-1]=()
|
||||
fi
|
||||
# Directive 1: error.
|
||||
(( directive & 1 )) && return 1
|
||||
lines=(${lines:#_activeHelp_*})
|
||||
lines=(${lines:#})
|
||||
# Directive 2: no trailing space.
|
||||
(( directive & 2 )) && nospace=(-S '')
|
||||
|
||||
# Directive 8: the lines are file extensions to filter by.
|
||||
if (( directive & 8 )); then
|
||||
local -a globs
|
||||
for entry in $lines; do globs+=("*.${entry}"); done
|
||||
_files -g "(${(j:|:)globs})"
|
||||
return
|
||||
fi
|
||||
# Directive 16: complete directories only.
|
||||
if (( directive & 16 )); then
|
||||
_files -/
|
||||
return
|
||||
fi
|
||||
|
||||
described=(${(M)lines:#*$'\t'*})
|
||||
plain=(${lines:#*$'\t'*})
|
||||
if (( $#described )); then
|
||||
local -a items
|
||||
for entry in $described; do
|
||||
items+=("${${entry%%$'\t'*}//:/\\:}:${entry#*$'\t'}")
|
||||
done
|
||||
_describe -t commands 'netssh command' items $nospace && ret=0
|
||||
fi
|
||||
if (( $#plain )); then
|
||||
# Directive 32: keep the order supplied by netssh.
|
||||
if (( directive & 32 )); then
|
||||
compadd -V hosts -X '%Bhosts%b' $nospace -- $plain && ret=0
|
||||
else
|
||||
compadd -J hosts -X '%Bhosts%b' $nospace -- $plain && ret=0
|
||||
fi
|
||||
fi
|
||||
# Directive 4: no file completion as a fallback.
|
||||
if (( ! $#lines && ! (directive & 4) )); then
|
||||
_files && ret=0
|
||||
fi
|
||||
return ret
|
||||
}
|
||||
|
||||
if [ "$funcstack[1]" = "_netssh" ]; then
|
||||
_netssh "$@"
|
||||
fi
|
||||
`
|
||||
|
||||
// ZshCompletionNote tells the user how zsh picks up a completion file in dir.
|
||||
func ZshCompletionNote(dir string) string {
|
||||
dump := filepath.Join(zshDir(), ".zcompdump")
|
||||
return fmt.Sprintf(`zsh loads it from fpath. The netssh block in your .zshrc adds %s
|
||||
(netssh hook install). Otherwise add this before compinit:
|
||||
fpath=(%s $fpath)
|
||||
Then refresh the completion cache: rm -f %s* && exec zsh`, dir, dir, dump)
|
||||
}
|
||||
|
||||
func zshDir() string {
|
||||
if dir := os.Getenv("ZDOTDIR"); dir != "" {
|
||||
return dir
|
||||
}
|
||||
return os.Getenv("HOME")
|
||||
}
|
||||
|
||||
// ZshFuncDir is the user directory netssh installs zsh completion files into.
|
||||
func ZshFuncDir() string {
|
||||
return filepath.Join(os.Getenv("HOME"), ".zfunc")
|
||||
}
|
||||
Reference in new issue
Block a user