Skip to content

Commit 6dec1ae

Browse files
authored
The default ShellCompDirective can be customized for a command and its subcommands (#2238)
1 parent c8289c1 commit 6dec1ae

File tree

3 files changed

+102
-0
lines changed

3 files changed

+102
-0
lines changed

completions.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,13 @@ type CompletionOptions struct {
115115
DisableDescriptions bool
116116
// HiddenDefaultCmd makes the default 'completion' command hidden
117117
HiddenDefaultCmd bool
118+
// DefaultShellCompDirective sets the ShellCompDirective that is returned
119+
// if no special directive can be determined
120+
DefaultShellCompDirective *ShellCompDirective
121+
}
122+
123+
func (receiver *CompletionOptions) SetDefaultShellCompDirective(directive ShellCompDirective) {
124+
receiver.DefaultShellCompDirective = &directive
118125
}
119126

120127
// Completion is a string that can be used for completions
@@ -480,6 +487,14 @@ func (c *Command) getCompletions(args []string) (*Command, []Completion, ShellCo
480487
}
481488
} else {
482489
directive = ShellCompDirectiveDefault
490+
// check current and parent commands for a custom DefaultShellCompDirective
491+
for cmd := finalCmd; cmd != nil; cmd = cmd.parent {
492+
if cmd.CompletionOptions.DefaultShellCompDirective != nil {
493+
directive = *cmd.CompletionOptions.DefaultShellCompDirective
494+
break
495+
}
496+
}
497+
483498
if flag == nil {
484499
foundLocalNonPersistentFlag := false
485500
// If TraverseChildren is true on the root command we don't check for

completions_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4015,3 +4015,60 @@ func TestInitDefaultCompletionCmd(t *testing.T) {
40154015
})
40164016
}
40174017
}
4018+
4019+
func TestCustomDefaultShellCompDirective(t *testing.T) {
4020+
rootCmd := &Command{Use: "root", Run: emptyRun}
4021+
rootCmd.PersistentFlags().String("string", "", "test string flag")
4022+
// use ShellCompDirectiveNoFileComp instead of the default, which is ShellCompDirectiveDefault.
4023+
rootCmd.CompletionOptions.SetDefaultShellCompDirective(ShellCompDirectiveNoFileComp)
4024+
4025+
// child1 inherits the custom DefaultShellCompDirective.
4026+
childCmd1 := &Command{Use: "child1", Run: emptyRun}
4027+
// child2 resets the custom DefaultShellCompDirective to the default value.
4028+
childCmd2 := &Command{Use: "child2", Run: emptyRun}
4029+
childCmd2.CompletionOptions.SetDefaultShellCompDirective(ShellCompDirectiveDefault)
4030+
4031+
rootCmd.AddCommand(childCmd1, childCmd2)
4032+
4033+
testCases := []struct {
4034+
desc string
4035+
args []string
4036+
expectedDirective string
4037+
}{
4038+
{
4039+
"flag completion on root command with custom DefaultShellCompDirective",
4040+
[]string{"--string", ""},
4041+
"ShellCompDirectiveNoFileComp",
4042+
},
4043+
{
4044+
"flag completion on subcommand with inherited custom DefaultShellCompDirective",
4045+
[]string{"child1", "--string", ""},
4046+
"ShellCompDirectiveNoFileComp",
4047+
},
4048+
{
4049+
"flag completion on subcommand with reset DefaultShellCompDirective",
4050+
[]string{"child2", "--string", ""},
4051+
"ShellCompDirectiveDefault",
4052+
},
4053+
}
4054+
4055+
for _, tc := range testCases {
4056+
t.Run(tc.desc, func(t *testing.T) {
4057+
args := []string{ShellCompNoDescRequestCmd}
4058+
args = append(args, tc.args...)
4059+
4060+
output, err := executeCommand(rootCmd, args...)
4061+
4062+
if err != nil {
4063+
t.Errorf("Unexpected error: %v", err)
4064+
}
4065+
4066+
outputWords := strings.Split(strings.TrimSpace(output), " ")
4067+
directive := outputWords[len(outputWords)-1]
4068+
4069+
if directive != tc.expectedDirective {
4070+
t.Errorf("expected: %q, got: %q", tc.expectedDirective, directive)
4071+
}
4072+
})
4073+
}
4074+
}

site/content/completions/_index.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,36 @@ $ helm status --output [tab][tab]
305305
json table yaml
306306
```
307307

308+
#### Change the default ShellCompDirective
309+
310+
When no completion function is registered for a leaf command or for a flag, Cobra will
311+
automatically use `ShellCompDirectiveDefault`, which will invoke the shell's filename completion.
312+
This implies that when file completion does not apply to a leaf command or to a flag (the command
313+
or flag does not operate on a filename), turning off file completion requires you to register a
314+
completion function for that command/flag.
315+
For example:
316+
317+
```go
318+
cmd.RegisterFlagCompletionFunc("flag-name", cobra.NoFileCompletions)
319+
```
320+
321+
If you find that there are more situations where file completion should be turned off than
322+
when it is applicable, you can recursively change the default `ShellCompDirective` for a command
323+
and its subcommands to `ShellCompDirectiveNoFileComp`:
324+
325+
```go
326+
cmd.CompletionOptions.SetDefaultShellCompDirective(ShellCompDirectiveNoFileComp)
327+
```
328+
329+
If doing so, keep in mind that you should instead register a completion function for leaf commands or
330+
flags where file completion is applicable. For example:
331+
332+
```go
333+
cmd.RegisterFlagCompletionFunc("flag-name", cobra.FixedCompletions(nil, ShellCompDirectiveDefault))
334+
```
335+
336+
To change the default directive for the entire program, set the DefaultShellCompDirective on the root command.
337+
308338
#### Debugging
309339

310340
You can also easily debug your Go completion code for flags:

0 commit comments

Comments
 (0)