Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shell completion requires double TAB on first usage #1874

Closed
3 tasks done
bartekpacia opened this issue Mar 9, 2024 · 6 comments
Closed
3 tasks done

Shell completion requires double TAB on first usage #1874

bartekpacia opened this issue Mar 9, 2024 · 6 comments
Labels
area/v2 relates to / is being considered for v2 kind/bug describes or fixes a bug

Comments

@bartekpacia
Copy link
Member

bartekpacia commented Mar 9, 2024

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.

  1. Clone it

  2. Build executable:

    go build -o emu cmd/emu/main.go
    
  3. Source shell completion:

    source <(cat autocomplete/zsh_autocomplete)
    
  4. 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

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'
@bartekpacia bartekpacia added area/v2 relates to / is being considered for v2 kind/bug describes or fixes a bug status/triage maintainers still need to look into this labels Mar 9, 2024
@bartekpacia
Copy link
Member Author

bartekpacia commented Mar 10, 2024

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 added a commit to bartekpacia/emu that referenced this issue Mar 10, 2024
bartekpacia added a commit to bartekpacia/fhome that referenced this issue Mar 10, 2024
bartekpacia added a commit to bartekpacia/fhome that referenced this issue Mar 10, 2024
@dearchap
Copy link
Contributor

@bartekpacia I'm not a zsh expert. Is there a way you can fix it ?

@bartekpacia
Copy link
Member Author

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.

@dearchap
Copy link
Contributor

Yeah go ahead.

@meatballhat meatballhat removed the status/triage maintainers still need to look into this label Apr 27, 2024
@bartekpacia
Copy link
Member Author

bartekpacia commented Apr 30, 2024

Below is an even better version of the script above. The advantage is that it can be sourceed, which makes testing easier (no need to copy to /opt/homebrew/share/zsh/site-functions):

. ./autocomplete/zsh_autocomplete

Warning

The completion script must be sourced. If you don't source it, the following happens:

$ ./autocomplete/zsh_autocomplete_fhomed
./autocomplete/zsh_autocomplete_fhomed: line 2: compdef: command not found
#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

@bartekpacia
Copy link
Member Author

This issue is fixed in v3, especially after #1998

For examples of CLI programs built with this module and that have shell completion properly set up, see:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/v2 relates to / is being considered for v2 kind/bug describes or fixes a bug
Projects
None yet
Development

No branches or pull requests

3 participants