Skip to content

Commit 5a83f1e

Browse files
committed
fix: show flag completions when completing a double-dash prefix
1 parent a9261cf commit 5a83f1e

5 files changed

Lines changed: 60 additions & 21 deletions

File tree

command_parse.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ func (cmd *Command) parseFlags(args Args) (Args, error) {
8686

8787
// stop parsing once we see a "--"
8888
if firstArg == "--" {
89+
// In shell completion mode, preserve "--" so that completion can detect
90+
// when the user is completing "--" itself vs. completing after "--"
91+
if cmd.Root().shellCompletion {
92+
posArgs = append(posArgs, firstArg)
93+
}
8994
posArgs = append(posArgs, rargs[1:]...)
9095
return &stringSliceArgs{posArgs}, nil
9196
}
@@ -166,6 +171,12 @@ func (cmd *Command) parseFlags(args Args) (Args, error) {
166171
// not a bool flag so need to get the next arg
167172
if flagVal == "" && !valFromEqual {
168173
if len(rargs) == 1 {
174+
// In shell completion mode, preserve the flag so that DefaultCompleteWithFlags can use it
175+
// as lastArg and offer suggestions for it.
176+
if cmd.Root().shellCompletion {
177+
posArgs = append(posArgs, rargs...)
178+
return &stringSliceArgs{posArgs}, nil
179+
}
169180
return &stringSliceArgs{posArgs}, fmt.Errorf("%s%s", argumentNotProvidedErrMsg, firstArg)
170181
}
171182
flagVal = rargs[1]
@@ -182,6 +193,12 @@ func (cmd *Command) parseFlags(args Args) (Args, error) {
182193

183194
// no flag lookup found and short handling is disabled
184195
if !shortOptionHandling {
196+
// In shell completion mode, preserve the partial flag so that DefaultCompleteWithFlags can use it
197+
// as lastArg and offer suggestions that match the prefix.
198+
if cmd.Root().shellCompletion {
199+
posArgs = append(posArgs, rargs...)
200+
return &stringSliceArgs{posArgs}, nil
201+
}
185202
return &stringSliceArgs{posArgs}, fmt.Errorf("%s%s", providedButNotDefinedErrMsg, flagName)
186203
}
187204

command_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -599,8 +599,8 @@ func TestCommand_Run_CustomShellCompleteAcceptsMalformedFlags(t *testing.T) {
599599
testArgs *stringSliceArgs
600600
expectedOut string
601601
}{
602-
{testArgs: &stringSliceArgs{v: []string{"--undefined"}}, expectedOut: "found 0 args"},
603-
{testArgs: &stringSliceArgs{v: []string{"--number"}}, expectedOut: "found 0 args"},
602+
{testArgs: &stringSliceArgs{v: []string{"--undefined"}}, expectedOut: "found 1 args"},
603+
{testArgs: &stringSliceArgs{v: []string{"--number"}}, expectedOut: "found 1 args"},
604604
{testArgs: &stringSliceArgs{v: []string{"--number", "forty-two"}}, expectedOut: "found 0 args"},
605605
{testArgs: &stringSliceArgs{v: []string{"--number", "42"}}, expectedOut: "found 0 args"},
606606
{testArgs: &stringSliceArgs{v: []string{"--number", "42", "newArg"}}, expectedOut: "found 1 args"},

completion_test.go

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,13 @@ func TestCompletionSubcommand(t *testing.T) {
121121
},
122122
},
123123
{
124-
name: "subcommand flag no completion",
124+
name: "subcommand double dash shows long flags",
125125
args: []string{"foo", "bar", "--", completionFlag},
126-
contains: "l1",
127-
msg: "Expected output to contain shell name %[1]q",
126+
contains: "--l1",
127+
msg: "Expected output to contain flag %[1]q",
128128
msgArgs: []any{
129-
"l1",
129+
"--l1",
130130
},
131-
notContains: true,
132131
},
133132
{
134133
name: "sub sub command general completion",
@@ -150,14 +149,13 @@ func TestCompletionSubcommand(t *testing.T) {
150149
},
151150
},
152151
{
153-
name: "sub sub command no completion",
152+
name: "sub sub command double dash shows flags",
154153
args: []string{"foo", "bar", "xyz", "--", completionFlag},
155-
contains: "-g",
154+
contains: "--help",
156155
msg: "Expected output to contain flag %[1]q",
157156
msgArgs: []any{
158-
"-g",
157+
"--help",
159158
},
160-
notContains: true,
161159
},
162160
{
163161
name: "sub sub command no completion extra args",
@@ -169,6 +167,24 @@ func TestCompletionSubcommand(t *testing.T) {
169167
},
170168
notContains: true,
171169
},
170+
{
171+
name: "subcommand partial double dash flag completion",
172+
args: []string{"foo", "bar", "--l", completionFlag},
173+
contains: "--l1",
174+
msg: "Expected output to contain flag %[1]q",
175+
msgArgs: []any{
176+
"--l1",
177+
},
178+
},
179+
{
180+
name: "sub sub command partial double dash flag completion",
181+
args: []string{"foo", "bar", "xyz", "--he", completionFlag},
182+
contains: "--help",
183+
msg: "Expected output to contain flag %[1]q",
184+
msgArgs: []any{
185+
"--help",
186+
},
187+
},
172188
}
173189

174190
for _, test := range tests {

help.go

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -256,11 +256,6 @@ func DefaultCompleteWithFlags(ctx context.Context, cmd *Command) {
256256
lastArg = args[argsLen-1]
257257
}
258258

259-
if lastArg == "--" {
260-
tracef("No completions due to termination")
261-
return
262-
}
263-
264259
if lastArg == completionFlag {
265260
lastArg = ""
266261
}
@@ -485,10 +480,12 @@ func checkShellCompleteFlag(c *Command, arguments []string) (bool, []string) {
485480
return false, arguments
486481
}
487482

488-
// If arguments include "--", shell completion is disabled
489-
// because after "--" only positional arguments are accepted.
483+
// If arguments include "--" before the token being completed, shell completion
484+
// is disabled because after "--" only positional arguments are accepted.
490485
// https://unix.stackexchange.com/a/11382
491-
if slices.Contains(arguments, "--") {
486+
// Note: The token being completed is at position pos-1 (immediately before completionFlag).
487+
// We only check arguments before that position, so completing "--" itself still works.
488+
if pos >= 1 && slices.Contains(arguments[:pos-1], "--") {
492489
return false, arguments[:pos]
493490
}
494491

help_test.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1325,7 +1325,7 @@ func TestDefaultCompleteWithFlags(t *testing.T) {
13251325
expected: "",
13261326
},
13271327
{
1328-
name: "flag-suggestion-end-args",
1328+
name: "flag-suggestion-double-dash-shows-all-flags",
13291329
cmd: &Command{
13301330
Flags: []Flag{
13311331
&BoolFlag{Name: "excitement"},
@@ -1344,7 +1344,7 @@ func TestDefaultCompleteWithFlags(t *testing.T) {
13441344
},
13451345
argv: []string{"cmd", "--e", "--", completionFlag},
13461346
env: map[string]string{"SHELL": "bash"},
1347-
expected: "",
1347+
expected: "--excitement\n--hat-shape\n",
13481348
},
13491349
{
13501350
name: "typical-command-suggestion",
@@ -1907,6 +1907,15 @@ func Test_checkShellCompleteFlag(t *testing.T) {
19071907
wantShellCompletion: true,
19081908
wantArgs: []string{"foo"},
19091909
},
1910+
{
1911+
name: "double dash is the token being completed",
1912+
arguments: []string{"foo", "--", completionFlag},
1913+
cmd: &Command{
1914+
EnableShellCompletion: true,
1915+
},
1916+
wantShellCompletion: true,
1917+
wantArgs: []string{"foo", "--"},
1918+
},
19101919
}
19111920

19121921
for _, tt := range tests {

0 commit comments

Comments
 (0)