cobra
cobra copied to clipboard
ValidArgsFunction not always called for completion of positional arguments
Hi,
I want to define a custom completion for the first argument of a sub command.
That's my ValidArgsFunction (I defined it for the sub command update
, my cli application is called crema
):
func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) == 0 {
return repoNames(toComplete), cobra.ShellCompDirectiveNoFileComp
}
return []string{}, cobra.ShellCompDirectiveNoFileComp`
}
The function repoNames
would deliver the valid values for the first argument.
The custom completion function is called as expected when I do:
crema update <tab>
It is not called when I do
crema update r<tab>
(I.e., when I type the first letter of a valid value for the first argument). Instead a file completion is done, but I expect that my custom completion function is called).
Also when I do
crema __complete update <tab>
to debug the whole thing, a file completion is done (instead, I expect that my custom completion function is called).
How can I get the desired behavior? BTW: I am using cobra v1.4.0 and zsh.
Thanks, mipi
Hi @mipimipi.
When calling the __complete
function you must not press <TAB>
but actually execute the command (pressing <ENTER>
). You should also explicitly specify the empty argument in that case only. So try comparing:
crema __complete update '' <enter>
crema __complete update r <enter>
I hope this helps.
Hi @marckhouzam,
thanks for the quick response. Now I was able to debug with __complete
(btw: I had to do crema __complete update r <enter>
instead of crema __complete update r <tab>
), but strange things happen (just as background info: repoNames()
returns values if it's called with toComplete="t"
, it returns an empty list if it is called with toComplete="r"
, which is both correct):
-
crema update r<tab>
: My custom completion function is not called, instead file completion is done. -
crema update r<space><tab>
: My custom completion function is called, but nowargs=["r"]
and thus, it assumes that the completion is called for the 2nd argument. But from the program logic, it is only possible to determine valid values for the 1st arg. -
crema update t<tab>
: My custom completion function is called (!), the correct completion proposals are returned. No file completion is executed. -
crema update t<space><tab>
: Same as 2. -
crema __complete update r<enter>
: Output::4 Completion ended with directive: ShellCompDirectiveNoFileComp
-
crema __complete update t<enter>
: Output (test
andtest-m1
are the correct values):test test-m1 :4 Completion ended with directive: ShellCompDirectiveNoFileComp
First, I do not understand why in 1. a file completion is done though I set ShellCompDirectiveNoFileComp
. Second, I do not understand why in 1. a file completion is done, but neither in 3. nor in 5. it is not done.
How do I check whether my custom completion function is called? - I have a print statement at the very beginning.
Could you shed some light on that?
Thanks, mipi
Hi @marckhouzam, thanks for the quick response. Now I was able to debug with
__complete
(btw: I had to docrema __complete update r <enter>
instead ofcrema __complete update r <tab>
),
That was my mistake. I updated my comment to fix it. Sorry.
but strange things happen (just as background info:
repoNames()
returns values if it's called withtoComplete="t"
, it returns an empty list if it is called withtoComplete="r"
, which is both correct):
crema update r<tab>
: My custom completion function is not called, instead file completion is done.
This is strange. See below.
crema update r<space><tab>
: My custom completion function is called, but nowargs=["r"]
and thus, it assumes that the completion is called for the 2nd argument. But from the program logic, it is only possible to determine valid values for the 1st arg.
This is normal as you have specified an argument of r
and then asked for completions of the next argument.
crema update t<tab>
: My custom completion function is called (!), the correct completion proposals are returned. No file completion is executed.
🎉
crema update t<space><tab>
: Same as 2.
Normal as for point 2.
crema __complete update r<enter>
: Output::4 Completion ended with directive: ShellCompDirectiveNoFileComp
This is the expected output and I would have expected things to work properly when doing shell completion. Where is the print statement output you say you have at the beginning?
crema __complete update t<enter>
: Output (test
andtest-m1
are the correct values): test test-m1 :4 Completion ended with directive: ShellCompDirectiveNoFileComp
This is correct.
First, I do not understand why in 1. a file completion is done though I set
ShellCompDirectiveNoFileComp
. Second, I do not understand why in 1. a file completion is done, but neither in 3. nor in 5. it is not done.
Me neither 😄 The file completion for 1 is wrong. The other two cases are correct, as per your ValidArgsFunction
.
How do I check whether my custom completion function is called? - I have a print statement at the very beginning.
Could the print statement be the problem? If you are printing to stdout it will affect shell completion. You should instead use cobra.CompError()
or one of those methods described here: https://github.com/spf13/cobra/blob/master/shell_completions.md#debugging
OK, I now used cobra.CompError
instead of a print statement, but the output is still strange:
-
crema update r<tab>
: Custom completion function not called (i.e., no message to stderr), but file completion done. -
crema __complete update r<enter>
: Output:[Debug] [Error] TOCOMPLETE: r :4 Completion ended with directive: ShellCompDirectiveNoFileComp
Looks OK to me, but it's different from 1 which is strange.
Here's the code:
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
cobra.CompError(fmt.Sprintf("TOCOMPLETE: %s", toComplete))
if len(args) == 0 {
return repoNames(toComplete), cobra.ShellCompDirectiveNoFileComp
}
return []string{}, cobra.ShellCompDirectiveNoFileComp
}
crema update r<tab>
: Custom completion function not called (i.e., no message to stderr), but file completion done.
You won't see the output when doing shell completion, it is normal; the shell completion script redirects this output to /dev/null
.
The fact that there is file completion is not normal.
How do you generate the completion script for crema
?
How to do you load it in the shell?
Which version of zsh
are you using?
You say that crema update t<tab>
does shell completion properly but not crema update r<tab>
, is that right?
How to I generate and load the completion scripts?
- I defined the sub command
gencomp
which executescmd.Root().GenZshCompletion(os.Stdout)
- I execute that sub command and pipe the result into
/usr/share/zsh/site-functions/_crema
- I source
/usr/share/zsh/site-functions/_crema
I am using zsh version 5.9
You say that crema update t
does shell completion properly but not crema update r , is that right?
Yes, that's right. The reason is - as I assume - that there are valid values that start with "t", but there are no valid values that start with "r". Thus, the function repoNames()
returns for "t" a string array that is not empty, but returns an empty string array for "r".
BTW: I created #1714 by accident. Sorry.
We'll have to debug the behavior of the script.
In the shell where you will perform shell completion please set this variable:
export BASH_COMP_DEBUG_FILE=/tmp/debug.txt
Then perform the shell completion test:
crema update r<tab>
And look at the printouts that appeared in /tmp/debug.txt
I did crema update r<tab>
, and that's the content of /tmp/debug.txt
:
========= starting completion logic ==========
CURRENT: 3, words[*]: crema update r
Truncated words[*]: crema update r,
lastParam: r, lastChar: r
About to call: eval crema __complete update r
[Debug] [Error] TOCOMPLETE: rcompletion output: :4
last line: :4
directive: 4
completions:
flagPrefix:
Calling _describe
_describe did not find completions.
Checking if we should do file completion.
deactivating file completion
As before, file completion was done.
deactivating file completion
Hmm... What do you mean by "file completion was done". What do you see happening?
crema update r<tab>
leads to crema update README.md
. README.md
is the only file in the directory that starts with 'r'.
BTW, that's the content of /tmp/debug.txt
for crema update a<tab>
(there are also valid values starting with 'a' - the only such valid value is 'archi3linux'):
========= starting completion logic ==========
CURRENT: 3, words[*]: crema update a
Truncated words[*]: crema update a,
lastParam: a, lastChar: a
About to call: eval crema __complete update a
[Debug] [Error] TOCOMPLETE: acompletion output: archi3linux
:4
last line: :4
directive: 4
completions: archi3linux
flagPrefix:
Adding completion: archi3linux
Calling _describe
_describe found some completions
Up to now nothing explains why file completion is done. I see that zsh 5.9, which you are running, was released very recently. I wonder if some behavior changed?
I will find time to install this new version and try to reproduce the problem.
OK, thanks a lot.
@mipimipi I ran zsh 5.9 in docker and tested with helm's shell completion but was not able to reproduce the problem.
Below is what I did:
docker run --rm -it zshusers/zsh:5.9
# Install curl
apt update
apt install curl -y
# Install helm
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
chmod 700 get_helm.sh
./get_helm.sh
# Enable shell completion
autoload -Uz compinit
compinit
source <(helm completion zsh)
# Test
helm list <TAB> # No file completion as expected
helm package <TAB> # File completion as expected
So, I'm thinking it is either some issue with crema
, or it is related to your particular zsh settings.
What you could do is install helm in your own environment and try to see if file completion behaves as expected.
@marckhouzam: Thanks for checking. I will investigate further.
The Cobra project currently lacks enough contributors to adequately respond to all issues. This bot triages issues and PRs according to the following rules:
- After 60d of inactivity, lifecycle/stale is applied. - After 30d of inactivity since lifecycle/stale was applied, lifecycle/rotten is applied and the issue is closed. You can:
- Make a comment to remove the stale label and show your support. The 60 days reset. - If an issue has lifecycle/rotten and is closed, comment and ask maintainers if they'd be interseted in reopening