You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
usage g completion-init zsh breaks default completion with _files #692
After installing generic completion for usage scripts, zsh completion complains:
$ emacs <TAB>
(eval):1: no matches found: *:globbed-files
Below is Codex's analysis of the issue. It might be overcomplicating things quite a bit, but the crux is valid: if I remove emulate -L zsh from the completion-init script, the problem goes away.
emulate -L zsh resets shell options for the function. That is dangerous inside zsh completion functions because the completion system relies on a particular option environment when helpers like _files / _path_files run.
The fallback:
_files
is then running under the wrong local option state, so zsh eventually evaluates an internal file-pattern tag such as:
*:globbed-files
as a real glob. With nomatch active, this becomes:
_usage_default_complete() {
emulate -L zsh -o extendedglob
setopt localoptions nonomatch
local cmd cmdpath
...
# Not a usage shebang script — fall back to file completion
_files
}
Even better design
Do not replace the global default completer with a wrapper that calls _files directly:
compdef _usage_default_complete -default-
That is broad and fragile. It intercepts completion for every command without a specific completion.
A safer fallback would be:
_default
instead of:
_files
So the end becomes:
# Not a usage shebang script — fall back to normal default completion
_default
But then you must avoid infinite recursion, because this function itself is installed as -default-. So the robust fix is more involved: it should save the previous default completion function before replacing it, then call that previous handler as fallback.
Practical local workaround
Edit the generated script locally and patch the top:
The core issue is not globbed-files; that is a normal zsh completion tag. The problem is the generated completion wrapper changes the option environment and then delegates to _files.
Something still isn't quite right. With the fix, I don't get the error message anymore, for sure. But:
$ source <(usage g completion-init zsh)
$ emacs --<TAB>[completion inserts "\*" here]
$ source <(usage g completion-init zsh | sed -e 's/emulate -L zsh//')
$ emacs --<TAB>[bell, nothing is inserted]
I asked the AI again:
That confirms the diagnosis: emulate -L zsh is clobbering the completion system’s expected option state.
The * insertion is a second symptom of the same thing. Adding nonomatch only suppresses the error; it lets the bad fallback proceed, so _files treats the internal * pattern as a literal candidate and inserts it.
Better fix
Do not call _files after emulate -L zsh.
Instead, preserve the caller’s completion options around the generated code, or only use emulate in the branch that calls usage.
Try this structure:
_usage_default_complete() {
local cmd cmdpath
cmd="${words[1]}"if [[ "$cmd"==*/* ]];then
cmdpath="$cmd"elif((${+commands[$cmd]}));then
cmdpath="${commands[$cmd]}"fiif [[ -n"$cmdpath"&&-f"$cmdpath" ]];thenlocal first
if IFS= read -r first <"$cmdpath"2>/dev/null && [[ "$first"=="#!"*"usage"* ]];thenif((${+commands[usage]}));then
emulate -L zsh
local -a values=() descs=() inserts=()
local needs_menu=0 line
while IFS= read -r line;dolocal -a parts=("${(@ps:\t:)line}")
values+=("${parts[1]}")
descs+=("${parts[2]}")
inserts+=("${parts[3]}")
[[ "${parts[3]}"=="'"* ]] && needs_menu=1
done<<(command usage complete-word --shell zsh -f "$cmdpath" --cword=$((CURRENT -1)) -- "${(Q)words[@]}")(( needs_menu ))&& compstate[insert]=menu
if((${#inserts[@]}));thenlocal -a _usage_display=()
local _usage_i _usage_max=0 _usage_v _usage_pad
for_usage_vin"${values[@]}";do((${#_usage_v}> _usage_max ))&& _usage_max=${#_usage_v}donefor((_usage_i=1; _usage_i<=${#values[@]}; _usage_i++));doif [[ -n"${descs[_usage_i]}" ]];then
_usage_pad=$(( _usage_max -${#values[_usage_i]}))
_usage_display+=("${values[_usage_i]}${(l:_usage_pad:::)} -- ${descs[_usage_i]}")
else
_usage_display+=("${values[_usage_i]}")
fidone
compadd -l -d _usage_display -U -Q -S '' -a inserts
fireturn$?fififi
_files
}
Smaller patch
Move:
emulate -L zsh
from the top of the function to inside this branch:
if((${+commands[usage]}));then
emulate -L zsh
...
fi
That way _files runs in the original completion environment.
Why deleting it works
Completion functions are already called in a special context. _files expects that context. Top-level emulate -L zsh resets options too broadly, so fallback completion breaks.
setopt nonomatch masks the exception but leaves the completion semantics wrong, hence literal * insertion.
Activity
jdx commentedon Jun 23, 2026
Opened #693 to address this.
The PR keeps
emulate -L zshbut restores a completion-friendly option state before falling back to_files:It also extends the zsh init integration test to exercise the non-usage fallback path and assert that
_filesseesnomatch=offandextendedglob=on.Validation run:
cargo test -p usage-lib test_complete_zsh_initcargo test -p usage-cli test_zsh_completion_init_integration --test shell_completions_integrationThis comment was generated by Claude Code.
dhanak commentedon Jun 24, 2026
Something still isn't quite right. With the fix, I don't get the error message anymore, for sure. But:
I asked the AI again:
!12287 merge bump-usage-3.5.3 into main
fix(zsh): stop the completion-init handler from breaking file complet…