Shell completion requires double TAB on first usage
bartekpacia opened this issue · 5 comments
My urfave/cli version is
v2.27.1
Checklist
- Are you running the latest v2 release? The list of releases is here.
- Did you check the manual for your release? The v2 manual is here
- Did you perform a search about this problem? Here's the GitHub guide about searching.
Dependency Management
My project is using Go modules.
Describe the bug
TL;DR After shell startup, I have to click tab twice for completions to show up.
I noticed that the provided zsh completion script doesn't work as expected when it is placed in the conventional directory for shell completion function /opt/homebrew/share/zsh/site-functions
(My zsh
is installed through Homebrew, to keep it up-to-date).
To reproduce expected behavior
All code is available in a public repository of mine.
-
Clone it
-
Build executable:
go build -o emu cmd/emu/main.go
-
Source shell completion:
source <(cat autocomplete/zsh_autocomplete)
-
Run
./emu
followed by<SPACE>
and<TAB>
, and notice that shell completions show up just fine:Video demo:
completion.mp4
To reproduce wrong behavior
Do step 1 and 2 from above, but instead of sourcing the completion script directly, put it into the conventional location for shell completion scripts:
cp ./autocomplete/zsh_autocomplete ./autocomplete/_emu
cp ./autocomplete/_emu /opt/homebrew/share/zsh/site-functions
This is for macOS with Homebrew-installed
zsh
. For Linux it's probably gonna be sth like/usr/local/share/zsh/site-functions
.
Now reload shell so it picks up new completions from that directory:
exec zsh
Note
At this point, you can run which _emu
and see it output something like this:
$ which _emu
_emu () {
# undefined
builtin autoload -XUz /opt/homebrew/share/zsh/site-functions
}
which is strange and I don't understand it. It's as if it was not initialized?
Now, try to trigger shell completion by typing this in the cloned project:
./emu <TAB>
and notice no shell completions show up. But if you click <TAB>
second time, they will show up.
This video demonstrates this behavior:
completion_broken.mp4
Notice that at the end of the video I clicked <TAB>
but it didn't do anything.
But interesting thing is that after the first <TAB>
, but before the second <TAB>
, the output of which _emu
changes:
New which _emu output
$ which _emu
_emu () {
_cli_zsh_autocomplete () {
local -a opts
local cur
cur=${words[-1]}
if [[ "$cur" == "-"* ]]
then
opts=("${(@f)$(${words[@]:0:#words[@]-1} ${cur} --generate-bash-completion)}")
else
opts=("${(@f)$(${words[@]:0:#words[@]-1} --generate-bash-completion)}")
fi
if [[ "${opts[1]}" != "" ]]
then
_describe 'values' opts
else
_files
fi
}
compdef _cli_zsh_autocomplete emu
}
Expected behavior
I expect shell completion suggestions to be shown immediately on first TAB.
Additional context
I noticed this only occurs with urfave/cli.
It does not occur with zsh completions generated by Cobra.
I did some googling and found
- Zsh completion need to hit
<TAB>
twice - this question explains my exact issue. - https://unix.stackexchange.com/q/335656/417321 - very similar as well
Want to fix this yourself?
I don't think I have enough CLI completion expertise and experience. But I have time, if someone could guide me into some resources, I'd be happy to try!
Also, thanks a ton for this wonderful library, I truly love it as it's much more lightweight than Cobra IMHO. If this small issue was fixed, I'd be on cloud 9.
Run go version
and paste its output here
go version go1.22.1 darwin/arm64
Run go env
and paste its output here
GO111MODULE=''
GOARCH='arm64'
GOBIN=''
GOCACHE='/Users/bartek/Library/Caches/go-build'
GOENV='/Users/bartek/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMODCACHE='/Users/bartek/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/bartek/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/opt/homebrew/Cellar/go/1.22.1/libexec'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/opt/homebrew/Cellar/go/1.22.1/libexec/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.22.1'
GCCGO='gccgo'
AR='ar'
CC='cc'
CXX='c++'
CGO_ENABLED='1'
GOMOD='/Users/bartek/projects/emu/go.mod'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/r6/5z0bypqn4cb8p369zlrfhw3c0000gn/T/go-build1695057898=/tmp/go-build -gno-record-gcc-switches -fno-common'
I did some digging and noticed commit e66017d
This bug does not occur with commits before e66017d
Completion script not exhibiting the bug (before e66017d)
#compdef emu
local -a opts
local cur
cur=${words[-1]}
if [[ "$cur" == "-"* ]]; then
opts=("${(@f)$(${words[@]:0:#words[@]-1} ${cur} --generate-bash-completion)}")
else
opts=("${(@f)$(${words[@]:0:#words[@]-1} --generate-bash-completion)}")
fi
if [[ "${opts[1]}" != "" ]]; then
_describe 'values' opts
else
_files
fi
Completion script exhibiting the bug
#compdef emu
_cli_zsh_autocomplete() {
local -a opts
local cur
cur=${words[-1]}
if [[ "$cur" == "-"* ]]; then
opts=("${(@f)$(${words[@]:0:#words[@]-1} ${cur} --generate-bash-completion)}")
else
opts=("${(@f)$(${words[@]:0:#words[@]-1} --generate-bash-completion)}")
fi
if [[ "${opts[1]}" != "" ]]; then
_describe 'values' opts
else
_files
fi
}
compdef _cli_zsh_autocomplete emu
@bartekpacia I'm not a zsh expert. Is there a way you can fix it ?
Yes, I have fixed it (see this comment for a version of completion script that works fine).
My suggestion is to eliminate the $PROG
variable from completion scripts. Instead, the docs should tell users to copy and modify their completion scripts.
For example, I have a program called emu
. Therefore I have to change $PROG
to emu
, resulting in:
#compdef emu
local -a opts
local cur
cur=${words[-1]}
if [[ "$cur" == "-"* ]]; then
opts=("${(@f)$(${words[@]:0:#words[@]-1} ${cur} --generate-bash-completion)}")
else
opts=("${(@f)$(${words[@]:0:#words[@]-1} --generate-bash-completion)}")
fi
if [[ "${opts[1]}" != "" ]]; then
_describe 'values' opts
else
_files
fi
What do you think? I would be happy to update the docs accordingly.
Note that these completion scripts are for v2. For v3,
--generate-bash-completion
must be replaced with--generate-shell-completion
.
Yeah go ahead.
The even better version of the script above. The advantage is that it can be source
ed, which makes testing easier (no need to copy to /opt/homebrew/share/zsh/site-functions
):
. ./autocomplete/zsh_autocomplete
#compdef emu
compdef _emu emu
_emu() {
local -a opts
local cur
cur=${words[-1]}
if [[ "$cur" == "-"* ]]; then
opts=("${(@f)$(${words[@]:0:#words[@]-1} ${cur} --generate-shell-completion)}")
else
opts=("${(@f)$(${words[@]:0:#words[@]-1} --generate-shell-completion)}")
fi
if [[ "${opts[1]}" != "" ]]; then
_describe 'values' opts
else
_files
fi
}
# don't run the completion function when being source-ed or eval-ed
if [ "$funcstack[1]" = "_emu" ]; then
_emu
fi