Repository navigation
fix: complete subcommands after a flag that needs no value on the root command - #2501
Conversation
|
The The change adds about 1.2KB (704 bytes of text and 527 of pclntab, measured with Go 1.27.3 for linux/amd64), but main is already close to the limit: it reports 2.2MB, and the check rounds to one decimal, so anything that crosses the next 4KB page reports 2.3MB. A version without the buffer lands on the same page, so I don't think trimming the code gets it back under. I can raise |
bc04167 to
bc36bb0
Compare
|
I've added a second commit that raises both limits by 0.1MB, as the check suggests, so CI can go green. The smallest size in CI ( |
bc36bb0 to
2127145
Compare
| // The root command completes os.Args, which still hold the flags, so a | ||
| // flag that needs nothing more must lead to the subcommands rather than | ||
| // to an empty list of flag suggestions. | ||
| origArgv := os.Args | ||
| t.Cleanup(func() { os.Args = origArgv }) |
2127145 to
245af54
Compare
|
@Juneezee @coilysiren friendly ping on #2501, #2502 and #2506. All three are rebased on main with the review points addressed, and CI is green on each. Let me know if anything else is needed. |
Juneezee
left a comment
There was a problem hiding this comment.
Thanks, the bug is real: on main, app --verbose <TAB> falls back to file names in bash and zsh, and fish shows nothing. The PR fixes it in all three.
A short name still hits it. Add this flag to the example app in the description:
&cli.BoolFlag{Name: "force", Aliases: []string{"f"}},app -f <TAB> completes to app -f --force in bash and zsh, fish offers only --force, and app -f d<TAB> completes nothing. printFlagSuggestions matches f against the flag's own name, force:
Lines 246 to 256 in 7dad5b6
so the suggestions are never empty, and the new check never treats -f as complete:
Lines 310 to 317 in 245af54
The program cannot tell app --debug<TAB> from app --debug <TAB>, but the shell can. How about printing both and letting the shell pick? It also drops the buffer and the bytes import:
printFlagSuggestions(lastArg, flags, cmd.Root().Writer)
// The last argument may be the flag being typed, as in "app --debug<TAB>",
// or one before the cursor, as in "app --debug <TAB>". The bash, zsh and
// fish scripts keep only the suggestions that match the word being typed,
// so after a complete flag, suggest the subcommands too.
if !isCompleteFlag(lastArg, flags) {
return
}In the test, add Aliases: []string{"v"} to the verbose flag and change the cases to:
{name: "start of a longer flag", arg: "--debug", want: []string{"--debug-level", "sub"}},
{name: "short alias", arg: "-v", want: []string{"--verbose", "sub"}},With these, --debug<TAB>, --verb<TAB> and -f<TAB> complete as they do now. --debug <TAB> and -f <TAB> list the subcommands next to --debug-level and --force, and -f d<TAB> completes deploy. I checked this in bash 5.3, zsh and fish 4.9.3. go test ./... (also with -race and the three build tags), golangci-lint and check-binary-size pass.
Please also update the description (the "no longer flag starts with it" rule and the --debug example) and the last sentence of the commit message.
One correction to the description: bash splits words at =, so on main app --config=x <TAB> already lists the subcommands in bash. Only zsh and fish showed nothing.
|
@Juneezee Thank you. These are done.
The description drops the "no longer flag starts with it" rule, covers the |
6ace056 to
726b346
Compare
The root command completes the arguments passed to Run, which still hold its flags, so after a flag that takes no value or has its value after "=", such as "app --verbose <TAB>", it printed flag suggestions for that flag and found none. A subcommand completes its parsed arguments and does not have this problem. Print the flag suggestions either way, and when the flag needs nothing more print the subcommands too, so the shell can keep the ones that match the word being typed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com> Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
726b346 to
40f2e2a
Compare
What type of PR is this?
What this PR does / why we need it:
The shell scripts send the word under the cursor only when it starts with
-, so forapp --verbose <TAB>the program getsapp --verbose --generate-shell-completion. A subcommand completes its parsed arguments, where--verbosehas already been consumed, soapp deploy --verbose <TAB>listsdeploy's subcommands. The root command completes its raw arguments instead, so its last argument is--verbose,DefaultCompleteWithFlagsprints flag suggestions for it, and sinceprintFlagSuggestionsskips the flag that was typed in full, it prints nothing.A short name hits the same bug. Since #2502,
printFlagSuggestionsprints nothing for a short flag written out in full, so withapp -f <TAB>falls back to file names in bash and zsh, fish shows nothing, andapp -f d<TAB>completes nothing.Bash splits words at
=, so on mainapp --config=x <TAB>already lists the subcommands in bash. Only zsh and fish showed nothing.The program cannot tell
app --debug<TAB>fromapp --debug <TAB>. It prints the flag suggestions, and when the last argument is a flag that takes no value (including a short alias and aBoolWithInverseFlag's--no-names) or has its value after=, it prints the subcommands too. The bash, zsh and fish scripts keep only the suggestions that match the word being typed:--debug<TAB>,--verb<TAB>and-f<TAB>complete as they do now--debug <TAB>lists the subcommands next to--debug-level, and-f <TAB>lists the subcommands-f d<TAB>completesdeploy--formatwith no value still prints nothing, and a partial flag such as--verbstill suggests only--verboseSubcommands do not reach the new branch for a known flag, since their parsed arguments no longer hold it.
This is separate from #2489, which changes where the root command's arguments come from and not how the last one is read. It is rebased on #2489, so the new test passes its arguments to
Runinstead of settingos.Args.Which issue(s) this PR fixes:
None, found while testing the generated scripts in bash and fish.
Testing
TestCompletionAfterCompleteRootFlag: a bool flag, an inverse bool flag and--format=jsonlist the subcommands (these three fail onmain);--debuglists--debug-leveland the subcommand; the short alias-vlists the subcommand;--formatwith no value and a partial--verbkeep their current output.--debug<TAB>,--verb<TAB>and-f<TAB>complete as before.--debug <TAB>lists the subcommands next to--debug-level,-f <TAB>lists the subcommands, and-f d<TAB>completesdeploy.go test ./...passes.The change and the test were written with Claude Code (AI).
Release Notes