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>
100 lines
2.8 KiB
Go
100 lines
2.8 KiB
Go
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")
|
|
}
|