From f1f260eb598745ebc59df542980b0a0c8c1b8d06 Mon Sep 17 00:00:00 2001 From: maxlandon Date: Tue, 8 Aug 2023 04:14:47 +0200 Subject: [PATCH 1/9] Don't use global map for flag completions. --- bash_completions.go | 7 ++++--- command.go | 8 ++++++++ completions.go | 24 +++++++++++------------- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/bash_completions.go b/bash_completions.go index 8a5315184..091aaff13 100644 --- a/bash_completions.go +++ b/bash_completions.go @@ -534,9 +534,9 @@ func writeLocalNonPersistentFlag(buf io.StringWriter, flag *pflag.Flag) { // prepareCustomAnnotationsForFlags setup annotations for go completions for registered flags func prepareCustomAnnotationsForFlags(cmd *Command) { - flagCompletionMutex.RLock() - defer flagCompletionMutex.RUnlock() - for flag := range flagCompletionFunctions { + cmd.flagCompletionMutex.RLock() + defer cmd.flagCompletionMutex.RUnlock() + for flag := range cmd.flagCompletionFunctions { // Make sure the completion script calls the __*_go_custom_completion function for // every registered flag. We need to do this here (and not when the flag was registered // for completion) so that we can know the root command name for the prefix @@ -644,6 +644,7 @@ func writeCmdAliases(buf io.StringWriter, cmd *Command) { WriteStringAndCheck(buf, ` fi`) WriteStringAndCheck(buf, "\n") } + func writeArgAliases(buf io.StringWriter, cmd *Command) { WriteStringAndCheck(buf, " noun_aliases=()\n") sort.Strings(cmd.ArgAliases) diff --git a/command.go b/command.go index 01f7c6f1c..93a470c84 100644 --- a/command.go +++ b/command.go @@ -26,6 +26,7 @@ import ( "path/filepath" "sort" "strings" + "sync" flag "github.com/spf13/pflag" ) @@ -158,6 +159,13 @@ type Command struct { // that we can use on every pflag set and children commands globNormFunc func(f *flag.FlagSet, name string) flag.NormalizedName + // flagsCompletions contrains completions for arbitrary lists of flags. + // Those flags may or may not actually strictly belong to the command in the function, + // but registering completions for them through the command allows for garbage-collecting. + flagCompletionFunctions map[*flag.Flag]func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) + // lock for reading and writing from flagCompletionFunctions + flagCompletionMutex *sync.RWMutex + // usageFunc is usage func defined by user. usageFunc func(*Command) error // usageTemplate is usage template defined by user. diff --git a/completions.go b/completions.go index ee38c4d0b..27110e073 100644 --- a/completions.go +++ b/completions.go @@ -18,7 +18,6 @@ import ( "fmt" "os" "strings" - "sync" "github.com/spf13/pflag" ) @@ -33,10 +32,10 @@ const ( ) // Global map of flag completion functions. Make sure to use flagCompletionMutex before you try to read and write from it. -var flagCompletionFunctions = map[*pflag.Flag]func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective){} - -// lock for reading and writing from flagCompletionFunctions -var flagCompletionMutex = &sync.RWMutex{} +// var flagCompletionFunctions = map[*pflag.Flag]func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective){} +// +// // lock for reading and writing from flagCompletionFunctions +// var flagCompletionMutex = &sync.RWMutex{} // ShellCompDirective is a bit map representing the different behaviors the shell // can be instructed to have once completions have been provided. @@ -135,13 +134,13 @@ func (c *Command) RegisterFlagCompletionFunc(flagName string, f func(cmd *Comman if flag == nil { return fmt.Errorf("RegisterFlagCompletionFunc: flag '%s' does not exist", flagName) } - flagCompletionMutex.Lock() - defer flagCompletionMutex.Unlock() + c.flagCompletionMutex.Lock() + defer c.flagCompletionMutex.Unlock() - if _, exists := flagCompletionFunctions[flag]; exists { + if _, exists := c.flagCompletionFunctions[flag]; exists { return fmt.Errorf("RegisterFlagCompletionFunc: flag '%s' already registered", flagName) } - flagCompletionFunctions[flag] = f + c.flagCompletionFunctions[flag] = f return nil } @@ -479,9 +478,9 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi // Find the completion function for the flag or command var completionFn func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) if flag != nil && flagCompletion { - flagCompletionMutex.RLock() - completionFn = flagCompletionFunctions[flag] - flagCompletionMutex.RUnlock() + finalCmd.flagCompletionMutex.RLock() + completionFn = finalCmd.flagCompletionFunctions[flag] + finalCmd.flagCompletionMutex.RUnlock() } else { completionFn = finalCmd.ValidArgsFunction } @@ -805,7 +804,6 @@ to your powershell profile. return cmd.Root().GenPowerShellCompletion(out) } return cmd.Root().GenPowerShellCompletionWithDesc(out) - }, } if haveNoDescFlag { From 53fb4ebbd13c3f3c6bf5c9e1c2c2e22065e3454a Mon Sep 17 00:00:00 2001 From: maxlandon Date: Sat, 30 Sep 2023 18:30:43 +0200 Subject: [PATCH 2/9] Ensure all completion maps are initialized before use --- bash_completions.go | 1 + completions.go | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/bash_completions.go b/bash_completions.go index 091aaff13..db3509120 100644 --- a/bash_completions.go +++ b/bash_completions.go @@ -534,6 +534,7 @@ func writeLocalNonPersistentFlag(buf io.StringWriter, flag *pflag.Flag) { // prepareCustomAnnotationsForFlags setup annotations for go completions for registered flags func prepareCustomAnnotationsForFlags(cmd *Command) { + cmd.initializeCompletionStorage() cmd.flagCompletionMutex.RLock() defer cmd.flagCompletionMutex.RUnlock() for flag := range cmd.flagCompletionFunctions { diff --git a/completions.go b/completions.go index 27110e073..c8b6094da 100644 --- a/completions.go +++ b/completions.go @@ -18,8 +18,10 @@ import ( "fmt" "os" "strings" + "sync" "github.com/spf13/pflag" + flag "github.com/spf13/pflag" ) const ( @@ -134,9 +136,13 @@ func (c *Command) RegisterFlagCompletionFunc(flagName string, f func(cmd *Comman if flag == nil { return fmt.Errorf("RegisterFlagCompletionFunc: flag '%s' does not exist", flagName) } + // Ensure none of our relevant fields are nil. + c.initializeCompletionStorage() + c.flagCompletionMutex.Lock() defer c.flagCompletionMutex.Unlock() + // And attempt to bind the completion. if _, exists := c.flagCompletionFunctions[flag]; exists { return fmt.Errorf("RegisterFlagCompletionFunc: flag '%s' already registered", flagName) } @@ -144,6 +150,20 @@ func (c *Command) RegisterFlagCompletionFunc(flagName string, f func(cmd *Comman return nil } +// initializeCompletionStorage is (and should be) called in all +// functions that make use of the command's flag completion functions. +func (c *Command) initializeCompletionStorage() { + if c.flagCompletionMutex == nil { + c.flagCompletionMutex = new(sync.RWMutex) + } + + var completionFn func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) + + if c.flagCompletionFunctions == nil { + c.flagCompletionFunctions = make(map[*flag.Flag]completionFn, 0) + } +} + // Returns a string listing the different directive enabled in the specified parameter func (d ShellCompDirective) string() string { var directives []string @@ -478,6 +498,8 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi // Find the completion function for the flag or command var completionFn func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) if flag != nil && flagCompletion { + c.initializeCompletionStorage() + finalCmd.flagCompletionMutex.RLock() completionFn = finalCmd.flagCompletionFunctions[flag] finalCmd.flagCompletionMutex.RUnlock() From 4d3a6e1d5639d1cd481801963c6a61d7a46267cb Mon Sep 17 00:00:00 2001 From: maxlandon Date: Sat, 30 Sep 2023 20:43:04 +0200 Subject: [PATCH 3/9] Merge master, and adapt flag completion getters --- .github/workflows/test.yml | 5 +++-- command.go | 28 +++++++++++++++++++++++++--- command_test.go | 33 +++++++++++++++++++++++++++++++++ completions.go | 27 +++++++++++++++++++++++++++ site/content/user_guide.md | 6 ++++++ 5 files changed, 94 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 498e3becd..6b4c165db 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -43,13 +43,13 @@ jobs: - uses: actions/setup-go@v3 with: - go-version: '^1.20' + go-version: '^1.21' check-latest: true cache: true - uses: actions/checkout@v3 - - uses: golangci/golangci-lint-action@v3.6.0 + - uses: golangci/golangci-lint-action@v3.7.0 with: version: latest args: --verbose @@ -67,6 +67,7 @@ jobs: - 18 - 19 - 20 + - 21 name: '${{ matrix.platform }} | 1.${{ matrix.go }}.x' runs-on: ${{ matrix.platform }}-latest steps: diff --git a/command.go b/command.go index 93a470c84..61861b999 100644 --- a/command.go +++ b/command.go @@ -189,6 +189,9 @@ type Command struct { // versionTemplate is the version template defined by user. versionTemplate string + // errPrefix is the error message prefix defined by user. + errPrefix string + // inReader is a reader defined by the user that replaces stdin inReader io.Reader // outWriter is a writer defined by the user that replaces stdout @@ -354,6 +357,11 @@ func (c *Command) SetVersionTemplate(s string) { c.versionTemplate = s } +// SetErrPrefix sets error message prefix to be used. Application can use it to set custom prefix. +func (c *Command) SetErrPrefix(s string) { + c.errPrefix = s +} + // SetGlobalNormalizationFunc sets a normalization function to all flag sets and also to child commands. // The user should not have a cyclic dependency on commands. func (c *Command) SetGlobalNormalizationFunc(n func(f *flag.FlagSet, name string) flag.NormalizedName) { @@ -603,6 +611,18 @@ func (c *Command) VersionTemplate() string { ` } +// ErrPrefix return error message prefix for the command +func (c *Command) ErrPrefix() string { + if c.errPrefix != "" { + return c.errPrefix + } + + if c.HasParent() { + return c.parent.ErrPrefix() + } + return "Error:" +} + func hasNoOptDefVal(name string, fs *flag.FlagSet) bool { flag := fs.Lookup(name) if flag == nil { @@ -760,7 +780,9 @@ func (c *Command) findNext(next string) *Command { } if len(matches) == 1 { - return matches[0] + // Temporarily disable gosec G602, which produces a false positive. + // See https://github.com/securego/gosec/issues/1005. + return matches[0] // #nosec G602 } return nil @@ -1056,7 +1078,7 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { c = cmd } if !c.SilenceErrors { - c.PrintErrln("Error:", err.Error()) + c.PrintErrln(c.ErrPrefix(), err.Error()) c.PrintErrf("Run '%v --help' for usage.\n", c.CommandPath()) } return c, err @@ -1085,7 +1107,7 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { // If root command has SilenceErrors flagged, // all subcommands should respect it if !cmd.SilenceErrors && !c.SilenceErrors { - c.PrintErrln("Error:", err.Error()) + c.PrintErrln(cmd.ErrPrefix(), err.Error()) } // If root command has SilenceUsage flagged, diff --git a/command_test.go b/command_test.go index b0f5e860e..4afb7f7b8 100644 --- a/command_test.go +++ b/command_test.go @@ -1099,6 +1099,39 @@ func TestShorthandVersionTemplate(t *testing.T) { checkStringContains(t, output, "customized version: 1.0.0") } +func TestRootErrPrefixExecutedOnSubcommand(t *testing.T) { + rootCmd := &Command{Use: "root", Run: emptyRun} + rootCmd.SetErrPrefix("root error prefix:") + rootCmd.AddCommand(&Command{Use: "sub", Run: emptyRun}) + + output, err := executeCommand(rootCmd, "sub", "--unknown-flag") + if err == nil { + t.Errorf("Expected error") + } + + checkStringContains(t, output, "root error prefix: unknown flag: --unknown-flag") +} + +func TestRootAndSubErrPrefix(t *testing.T) { + rootCmd := &Command{Use: "root", Run: emptyRun} + subCmd := &Command{Use: "sub", Run: emptyRun} + rootCmd.AddCommand(subCmd) + rootCmd.SetErrPrefix("root error prefix:") + subCmd.SetErrPrefix("sub error prefix:") + + if output, err := executeCommand(rootCmd, "--unknown-root-flag"); err == nil { + t.Errorf("Expected error") + } else { + checkStringContains(t, output, "root error prefix: unknown flag: --unknown-root-flag") + } + + if output, err := executeCommand(rootCmd, "sub", "--unknown-sub-flag"); err == nil { + t.Errorf("Expected error") + } else { + checkStringContains(t, output, "sub error prefix: unknown flag: --unknown-sub-flag") + } +} + func TestVersionFlagExecutedOnSubcommand(t *testing.T) { rootCmd := &Command{Use: "root", Version: "1.0.0"} rootCmd.AddCommand(&Command{Use: "sub", Run: emptyRun}) diff --git a/completions.go b/completions.go index c8b6094da..68eb445ba 100644 --- a/completions.go +++ b/completions.go @@ -150,6 +150,33 @@ func (c *Command) RegisterFlagCompletionFunc(flagName string, f func(cmd *Comman return nil } +// GetFlagCompletion returns the completion function for the given flag, if available. +func (c *Command) GetFlagCompletion(flag *pflag.Flag) (func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective), bool) { + c.initializeCompletionStorage() + + c.flagCompletionMutex.RLock() + defer c.flagCompletionMutex.RUnlock() + + completionFunc, exists := c.flagCompletionFunctions[flag] + + if completionFunc != nil && exists { + return completionFunc, exists + } + + // If not found on the current, walk up the command tree. + return c.Parent().GetFlagCompletion(flag) +} + +// GetFlagCompletionByName returns the completion function for the given flag in the command by name, if available. +func (c *Command) GetFlagCompletionByName(flagName string) (func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective), bool) { + flag := c.Flags().Lookup(flagName) + if flag == nil { + return nil, false + } + + return c.GetFlagCompletion(flag) +} + // initializeCompletionStorage is (and should be) called in all // functions that make use of the command's flag completion functions. func (c *Command) initializeCompletionStorage() { diff --git a/site/content/user_guide.md b/site/content/user_guide.md index 56e8e44a3..93daadf56 100644 --- a/site/content/user_guide.md +++ b/site/content/user_guide.md @@ -596,6 +596,12 @@ Running an application with the '--version' flag will print the version to stdou the version template. The template can be customized using the `cmd.SetVersionTemplate(s string)` function. +## Error Message Prefix + +Cobra prints an error message when receiving a non-nil error value. +The default error message is `Error: `. +The Prefix, `Error:` can be customized using the `cmd.SetErrPrefix(s string)` function. + ## PreRun and PostRun Hooks It is possible to run functions before or after the main `Run` function of your command. The `PersistentPreRun` and `PreRun` functions will be executed before `Run`. `PersistentPostRun` and `PostRun` will be executed after `Run`. The `Persistent*Run` functions will be inherited by children if they do not declare their own. These functions are run in the following order: From 5dad42575c39e1cca2d28fbc6f8d69f66add2bec Mon Sep 17 00:00:00 2001 From: maxlandon Date: Sat, 30 Sep 2023 20:48:28 +0200 Subject: [PATCH 4/9] Avoid infinitely walking up the command tree --- completions.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/completions.go b/completions.go index 68eb445ba..0ee4f28a6 100644 --- a/completions.go +++ b/completions.go @@ -159,11 +159,17 @@ func (c *Command) GetFlagCompletion(flag *pflag.Flag) (func(cmd *Command, args [ completionFunc, exists := c.flagCompletionFunctions[flag] + // If found it here, return now if completionFunc != nil && exists { return completionFunc, exists } - // If not found on the current, walk up the command tree. + // If we are already at the root command level, return anyway + if !c.HasParent() { + return nil, false + } + + // Or walk up the command tree. return c.Parent().GetFlagCompletion(flag) } From 340cb4c7a3cc5c30a7e21d0acaac52bbd1f8b5ad Mon Sep 17 00:00:00 2001 From: maxlandon Date: Thu, 2 Nov 2023 14:39:11 +0100 Subject: [PATCH 5/9] Fix compilation issues and remove commented code --- completions.go | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/completions.go b/completions.go index ac7219c6c..5e3521a00 100644 --- a/completions.go +++ b/completions.go @@ -33,12 +33,6 @@ const ( ShellCompNoDescRequestCmd = "__completeNoDesc" ) -// Global map of flag completion functions. Make sure to use flagCompletionMutex before you try to read and write from it. -// var flagCompletionFunctions = map[*pflag.Flag]func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective){} -// -// // lock for reading and writing from flagCompletionFunctions -// var flagCompletionMutex = &sync.RWMutex{} - // ShellCompDirective is a bit map representing the different behaviors the shell // can be instructed to have once completions have been provided. type ShellCompDirective int @@ -190,10 +184,8 @@ func (c *Command) initializeCompletionStorage() { c.flagCompletionMutex = new(sync.RWMutex) } - var completionFn func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) - if c.flagCompletionFunctions == nil { - c.flagCompletionFunctions = make(map[*flag.Flag]completionFn, 0) + c.flagCompletionFunctions = make(map[*flag.Flag]func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective), 0) } } @@ -907,7 +899,7 @@ func CompDebug(msg string, printToStdErr bool) { // variable BASH_COMP_DEBUG_FILE to the path of some file to be used. if path := os.Getenv("BASH_COMP_DEBUG_FILE"); path != "" { f, err := os.OpenFile(path, - os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644) if err == nil { defer f.Close() WriteStringAndCheck(f, msg) From 69578f24adfe3bd0a8e1c822724ccb44f20f1c8b Mon Sep 17 00:00:00 2001 From: maxlandon Date: Thu, 2 Nov 2023 14:54:55 +0100 Subject: [PATCH 6/9] Pass all tests by using correct flagComp lookups in completion command code. --- completions.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/completions.go b/completions.go index 5e3521a00..73b6e1f9b 100644 --- a/completions.go +++ b/completions.go @@ -532,11 +532,7 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi // Find the completion function for the flag or command var completionFn func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) if flag != nil && flagCompletion { - c.initializeCompletionStorage() - - finalCmd.flagCompletionMutex.RLock() - completionFn = finalCmd.flagCompletionFunctions[flag] - finalCmd.flagCompletionMutex.RUnlock() + completionFn, _ = finalCmd.GetFlagCompletion(flag) } else { completionFn = finalCmd.ValidArgsFunction } From 1548134ccbbe047b2dbe8d404640fe23671c8e08 Mon Sep 17 00:00:00 2001 From: maxlandon Date: Thu, 2 Nov 2023 15:34:36 +0100 Subject: [PATCH 7/9] Remove useless duplicate import --- completions.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/completions.go b/completions.go index 73b6e1f9b..c00daec9a 100644 --- a/completions.go +++ b/completions.go @@ -21,7 +21,6 @@ import ( "sync" "github.com/spf13/pflag" - flag "github.com/spf13/pflag" ) const ( @@ -185,7 +184,7 @@ func (c *Command) initializeCompletionStorage() { } if c.flagCompletionFunctions == nil { - c.flagCompletionFunctions = make(map[*flag.Flag]func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective), 0) + c.flagCompletionFunctions = make(map[*pflag.Flag]func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective), 0) } } From f5848b646babc06f51f93b7dd09ef90737a71504 Mon Sep 17 00:00:00 2001 From: maxlandon Date: Thu, 2 Nov 2023 17:22:44 +0100 Subject: [PATCH 8/9] Rename GetFlagCompletionFuncByName() for flag names lookup --- completions_test.go | 453 +++++++++++++++++++++++++++++--------------- 1 file changed, 301 insertions(+), 152 deletions(-) diff --git a/completions_test.go b/completions_test.go index d5aee2501..69836d461 100644 --- a/completions_test.go +++ b/completions_test.go @@ -97,7 +97,8 @@ func TestCmdNameCompletionInGo(t *testing.T) { "help", "secondChild", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -112,7 +113,8 @@ func TestCmdNameCompletionInGo(t *testing.T) { expected = strings.Join([]string{ "secondChild", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -127,7 +129,8 @@ func TestCmdNameCompletionInGo(t *testing.T) { expected = strings.Join([]string{ ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -146,7 +149,8 @@ func TestCmdNameCompletionInGo(t *testing.T) { "help\tHelp about any command", "secondChild", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -186,7 +190,8 @@ func TestNoCmdNameCompletionInGo(t *testing.T) { expected := strings.Join([]string{ ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -202,7 +207,8 @@ func TestNoCmdNameCompletionInGo(t *testing.T) { expected = strings.Join([]string{ ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -224,7 +230,8 @@ func TestNoCmdNameCompletionInGo(t *testing.T) { "completion", "help", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -246,7 +253,8 @@ func TestNoCmdNameCompletionInGo(t *testing.T) { expected = strings.Join([]string{ "childCmd2", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -261,7 +269,8 @@ func TestNoCmdNameCompletionInGo(t *testing.T) { expected = strings.Join([]string{ ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -277,7 +286,8 @@ func TestNoCmdNameCompletionInGo(t *testing.T) { expected = strings.Join([]string{ ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -294,7 +304,8 @@ func TestNoCmdNameCompletionInGo(t *testing.T) { expected = strings.Join([]string{ "childCmd2", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -311,7 +322,8 @@ func TestNoCmdNameCompletionInGo(t *testing.T) { expected = strings.Join([]string{ "childCmd2", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -336,7 +348,8 @@ func TestValidArgsCompletionInGo(t *testing.T) { "two", "three", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -351,7 +364,8 @@ func TestValidArgsCompletionInGo(t *testing.T) { expected = strings.Join([]string{ "one", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -365,7 +379,8 @@ func TestValidArgsCompletionInGo(t *testing.T) { expected = strings.Join([]string{ ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -399,7 +414,8 @@ func TestValidArgsAndCmdCompletionInGo(t *testing.T) { "one", "two", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -415,7 +431,8 @@ func TestValidArgsAndCmdCompletionInGo(t *testing.T) { "thechild", "two", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -450,7 +467,8 @@ func TestValidArgsFuncAndCmdCompletionInGo(t *testing.T) { "one", "two", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -466,7 +484,8 @@ func TestValidArgsFuncAndCmdCompletionInGo(t *testing.T) { "thechild", "two", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -482,7 +501,8 @@ func TestValidArgsFuncAndCmdCompletionInGo(t *testing.T) { "thechild\tThe child command", "two\tThe second", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -516,7 +536,8 @@ func TestFlagNameCompletionInGo(t *testing.T) { "completion", "help", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -536,7 +557,8 @@ func TestFlagNameCompletionInGo(t *testing.T) { "--second", "-s", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -551,7 +573,8 @@ func TestFlagNameCompletionInGo(t *testing.T) { expected = strings.Join([]string{ "--first", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -572,7 +595,8 @@ func TestFlagNameCompletionInGo(t *testing.T) { "--version", "-v", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -607,7 +631,8 @@ func TestFlagNameCompletionInGoWithDesc(t *testing.T) { "completion\tGenerate the autocompletion script for the specified shell", "help\tHelp about any command", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -627,7 +652,8 @@ func TestFlagNameCompletionInGoWithDesc(t *testing.T) { "--second\tsecond flag", "-s\tsecond flag", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -642,7 +668,8 @@ func TestFlagNameCompletionInGoWithDesc(t *testing.T) { expected = strings.Join([]string{ "--first\tfirst flag", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -663,7 +690,8 @@ func TestFlagNameCompletionInGoWithDesc(t *testing.T) { "--version\tversion for childCmd", "-v\tversion for childCmd", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -708,7 +736,8 @@ func TestFlagNameCompletionRepeat(t *testing.T) { "--second", "--slice", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -729,7 +758,8 @@ func TestFlagNameCompletionRepeat(t *testing.T) { "--help", "--slice", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -753,7 +783,8 @@ func TestFlagNameCompletionRepeat(t *testing.T) { "--second", "--slice", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -782,7 +813,8 @@ func TestFlagNameCompletionRepeat(t *testing.T) { "--slice", "-l", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -800,7 +832,8 @@ func TestFlagNameCompletionRepeat(t *testing.T) { expected = strings.Join([]string{ "-a", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -852,7 +885,8 @@ func TestRequiredFlagNameCompletionInGo(t *testing.T) { "-p", "realArg", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -870,7 +904,8 @@ func TestRequiredFlagNameCompletionInGo(t *testing.T) { "--requiredPersistent", "-p", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -885,7 +920,8 @@ func TestRequiredFlagNameCompletionInGo(t *testing.T) { expected = strings.Join([]string{ "--release", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -904,7 +940,8 @@ func TestRequiredFlagNameCompletionInGo(t *testing.T) { "-s", "subArg", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -921,7 +958,8 @@ func TestRequiredFlagNameCompletionInGo(t *testing.T) { "--subRequired", "-s", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -935,7 +973,8 @@ func TestRequiredFlagNameCompletionInGo(t *testing.T) { expected = strings.Join([]string{ "--subNotRequired", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -954,7 +993,8 @@ func TestRequiredFlagNameCompletionInGo(t *testing.T) { "-p", "realArg", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -976,7 +1016,8 @@ func TestRequiredFlagNameCompletionInGo(t *testing.T) { "-r", "realArg", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -994,7 +1035,8 @@ func TestRequiredFlagNameCompletionInGo(t *testing.T) { expected = strings.Join([]string{ "realArg", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1032,7 +1074,8 @@ func TestFlagFileExtFilterCompletionInGo(t *testing.T) { expected := strings.Join([]string{ ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1046,7 +1089,8 @@ func TestFlagFileExtFilterCompletionInGo(t *testing.T) { expected = strings.Join([]string{ "log", ":8", - "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveFilterFileExt", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1060,7 +1104,8 @@ func TestFlagFileExtFilterCompletionInGo(t *testing.T) { expected = strings.Join([]string{ "yaml", "yml", ":8", - "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveFilterFileExt", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1074,7 +1119,8 @@ func TestFlagFileExtFilterCompletionInGo(t *testing.T) { expected = strings.Join([]string{ "yaml", "yml", ":8", - "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveFilterFileExt", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1088,7 +1134,8 @@ func TestFlagFileExtFilterCompletionInGo(t *testing.T) { expected = strings.Join([]string{ "yaml", "yml", ":8", - "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveFilterFileExt", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1102,7 +1149,8 @@ func TestFlagFileExtFilterCompletionInGo(t *testing.T) { expected = strings.Join([]string{ "yaml", "yml", ":8", - "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveFilterFileExt", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1116,7 +1164,8 @@ func TestFlagFileExtFilterCompletionInGo(t *testing.T) { expected = strings.Join([]string{ "txt", ":8", - "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveFilterFileExt", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1150,7 +1199,8 @@ func TestFlagDirFilterCompletionInGo(t *testing.T) { expected := strings.Join([]string{ ":16", - "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveFilterDirs", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1163,7 +1213,8 @@ func TestFlagDirFilterCompletionInGo(t *testing.T) { expected = strings.Join([]string{ ":16", - "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveFilterDirs", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1177,7 +1228,8 @@ func TestFlagDirFilterCompletionInGo(t *testing.T) { expected = strings.Join([]string{ "themes", ":16", - "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveFilterDirs", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1191,7 +1243,8 @@ func TestFlagDirFilterCompletionInGo(t *testing.T) { expected = strings.Join([]string{ "themes", ":16", - "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveFilterDirs", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1205,7 +1258,8 @@ func TestFlagDirFilterCompletionInGo(t *testing.T) { expected = strings.Join([]string{ "themes", ":16", - "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveFilterDirs", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1219,7 +1273,8 @@ func TestFlagDirFilterCompletionInGo(t *testing.T) { expected = strings.Join([]string{ "themes", ":16", - "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveFilterDirs", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1232,7 +1287,8 @@ func TestFlagDirFilterCompletionInGo(t *testing.T) { expected = strings.Join([]string{ ":16", - "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveFilterDirs", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1274,7 +1330,8 @@ func TestValidArgsFuncCmdContext(t *testing.T) { expected := strings.Join([]string{ ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1298,7 +1355,8 @@ func TestValidArgsFuncSingleCmd(t *testing.T) { "one", "two", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1313,7 +1371,8 @@ func TestValidArgsFuncSingleCmd(t *testing.T) { expected = strings.Join([]string{ "two", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1340,7 +1399,8 @@ func TestValidArgsFuncSingleCmdInvalidArg(t *testing.T) { expected := strings.Join([]string{ ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1371,7 +1431,8 @@ func TestValidArgsFuncChildCmds(t *testing.T) { "one", "two", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1386,7 +1447,8 @@ func TestValidArgsFuncChildCmds(t *testing.T) { expected = strings.Join([]string{ "two", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1400,7 +1462,8 @@ func TestValidArgsFuncChildCmds(t *testing.T) { expected = strings.Join([]string{ ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1416,7 +1479,8 @@ func TestValidArgsFuncChildCmds(t *testing.T) { "three", "four", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1430,7 +1494,8 @@ func TestValidArgsFuncChildCmds(t *testing.T) { expected = strings.Join([]string{ "three", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1444,7 +1509,8 @@ func TestValidArgsFuncChildCmds(t *testing.T) { expected = strings.Join([]string{ ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1471,7 +1537,8 @@ func TestValidArgsFuncAliases(t *testing.T) { "one", "two", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1486,7 +1553,8 @@ func TestValidArgsFuncAliases(t *testing.T) { expected = strings.Join([]string{ "two", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1500,7 +1568,8 @@ func TestValidArgsFuncAliases(t *testing.T) { expected = strings.Join([]string{ ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1624,7 +1693,8 @@ func TestFlagCompletionInGo(t *testing.T) { "2", "10", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1640,7 +1710,8 @@ func TestFlagCompletionInGo(t *testing.T) { "1", "10", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1657,7 +1728,8 @@ func TestFlagCompletionInGo(t *testing.T) { "myfile.json", "file.xml", ":6", - "Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1673,7 +1745,8 @@ func TestFlagCompletionInGo(t *testing.T) { "file.yaml", "file.xml", ":6", - "Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1704,7 +1777,8 @@ func TestValidArgsFuncChildCmdsWithDesc(t *testing.T) { "one\tThe first", "two\tThe second", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1719,7 +1793,8 @@ func TestValidArgsFuncChildCmdsWithDesc(t *testing.T) { expected = strings.Join([]string{ "two\tThe second", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1733,7 +1808,8 @@ func TestValidArgsFuncChildCmdsWithDesc(t *testing.T) { expected = strings.Join([]string{ ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1749,7 +1825,8 @@ func TestValidArgsFuncChildCmdsWithDesc(t *testing.T) { "three\tThe third", "four\tThe fourth", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1763,7 +1840,8 @@ func TestValidArgsFuncChildCmdsWithDesc(t *testing.T) { expected = strings.Join([]string{ "three\tThe third", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1777,7 +1855,8 @@ func TestValidArgsFuncChildCmdsWithDesc(t *testing.T) { expected = strings.Join([]string{ ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1816,7 +1895,8 @@ func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) { "--help\thelp for child", "--string\ttest string flag", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1832,7 +1912,8 @@ func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) { "--validarg", "test", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1848,7 +1929,8 @@ func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) { "--validarg", "test", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1867,7 +1949,8 @@ func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) { "--validarg", "test", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1883,7 +1966,8 @@ func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) { "--validarg", "test", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1899,7 +1983,8 @@ func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) { "--validarg", "test", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1915,7 +2000,8 @@ func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) { "--validarg", "test", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1931,7 +2017,8 @@ func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) { "arg1", "arg2", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1947,7 +2034,8 @@ func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) { "--validarg", "test", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1963,7 +2051,8 @@ func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) { "--validarg", "test", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -1981,7 +2070,8 @@ func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) { expected = strings.Join([]string{ "--validarg", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -2000,7 +2090,8 @@ func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) { "--validarg", "--toComp=ab", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -2035,7 +2126,8 @@ func TestFlagCompletionWorksRootCommandAddedAfterFlags(t *testing.T) { expected := strings.Join([]string{ "myval", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -2068,7 +2160,8 @@ func TestFlagCompletionForPersistentFlagsCalledFromSubCmd(t *testing.T) { expected := strings.Join([]string{ "myval", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -2142,7 +2235,8 @@ func TestFlagCompletionConcurrentRegistration(t *testing.T) { expected := strings.Join([]string{ flagName, ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -2187,7 +2281,8 @@ func TestFlagCompletionInGoWithDesc(t *testing.T) { "2\tThe second", "10\tThe tenth", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -2203,7 +2298,8 @@ func TestFlagCompletionInGoWithDesc(t *testing.T) { "1\tThe first", "10\tThe tenth", ":0", - "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveDefault", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -2220,7 +2316,8 @@ func TestFlagCompletionInGoWithDesc(t *testing.T) { "myfile.json\tJSON format", "file.xml\tXML format", ":6", - "Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -2236,7 +2333,8 @@ func TestFlagCompletionInGoWithDesc(t *testing.T) { "file.yaml\tYAML format", "file.xml\tXML format", ":6", - "Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -2264,7 +2362,8 @@ func TestValidArgsNotValidArgsFunc(t *testing.T) { "one", "two", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -2279,7 +2378,8 @@ func TestValidArgsNotValidArgsFunc(t *testing.T) { expected = strings.Join([]string{ "two", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -2306,7 +2406,8 @@ func TestArgAliasesCompletionInGo(t *testing.T) { "two", "three", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -2322,7 +2423,8 @@ func TestArgAliasesCompletionInGo(t *testing.T) { "two", "three", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -2337,7 +2439,8 @@ func TestArgAliasesCompletionInGo(t *testing.T) { expected = strings.Join([]string{ "trois", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -2374,7 +2477,8 @@ func TestCompleteHelp(t *testing.T) { "completion", "help", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -2392,7 +2496,8 @@ func TestCompleteHelp(t *testing.T) { "completion", "help", // " help help" is a valid command, so should be completed ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -2407,7 +2512,8 @@ func TestCompleteHelp(t *testing.T) { expected = strings.Join([]string{ "child3", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -2579,7 +2685,8 @@ func TestCompleteCompletion(t *testing.T) { "powershell", "zsh", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -2602,7 +2709,8 @@ func TestCompleteCompletion(t *testing.T) { expected = strings.Join([]string{ ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -2634,7 +2742,8 @@ func TestMultipleShorthandFlagCompletion(t *testing.T) { "foo", "bar", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -2650,7 +2759,8 @@ func TestMultipleShorthandFlagCompletion(t *testing.T) { "foo", "bar", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -2665,7 +2775,8 @@ func TestMultipleShorthandFlagCompletion(t *testing.T) { expected = strings.Join([]string{ "works", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -2680,7 +2791,8 @@ func TestMultipleShorthandFlagCompletion(t *testing.T) { expected = strings.Join([]string{ "works", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -2696,7 +2808,8 @@ func TestMultipleShorthandFlagCompletion(t *testing.T) { "foo", "bar", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -2704,7 +2817,6 @@ func TestMultipleShorthandFlagCompletion(t *testing.T) { } func TestCompleteWithDisableFlagParsing(t *testing.T) { - flagValidArgs := func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { return []string{"--flag", "-f"}, ShellCompDirectiveNoFileComp } @@ -2737,7 +2849,8 @@ func TestCompleteWithDisableFlagParsing(t *testing.T) { "--flag", "-f", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -2759,7 +2872,8 @@ func TestCompleteWithDisableFlagParsing(t *testing.T) { "--nonPersistent", "-n", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -2788,7 +2902,8 @@ func TestCompleteWithRootAndLegacyArgs(t *testing.T) { "arg1", "arg2", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -2804,7 +2919,8 @@ func TestCompleteWithRootAndLegacyArgs(t *testing.T) { "arg1", "arg2", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -2831,7 +2947,8 @@ func TestFixedCompletions(t *testing.T) { "banana", "orange", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n") if output != expected { t.Errorf("expected: %q, got: %q", expected, output) @@ -2877,7 +2994,8 @@ func TestCompletionForGroupedFlags(t *testing.T) { expectedOutput: strings.Join([]string{ "subArg", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, { desc: "flags in group suggested with - prefix", @@ -2890,7 +3008,8 @@ func TestCompletionForGroupedFlags(t *testing.T) { "--ingroup3", "--nogroup", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, { desc: "when flag in group present, other flags in group suggested even without - prefix", @@ -2900,7 +3019,8 @@ func TestCompletionForGroupedFlags(t *testing.T) { "--ingroup3", "subArg", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, { desc: "when all flags in group present, flags not suggested without - prefix", @@ -2908,7 +3028,8 @@ func TestCompletionForGroupedFlags(t *testing.T) { expectedOutput: strings.Join([]string{ "subArg", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, { desc: "group ignored if some flags not applicable", @@ -2918,7 +3039,8 @@ func TestCompletionForGroupedFlags(t *testing.T) { "completion", "help", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, } @@ -2980,7 +3102,8 @@ func TestCompletionForOneRequiredGroupFlags(t *testing.T) { "--ingroup3", "subArg", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, { desc: "flags in group suggested with - prefix", @@ -2990,7 +3113,8 @@ func TestCompletionForOneRequiredGroupFlags(t *testing.T) { "--ingroup2", "--ingroup3", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, { desc: "when any flag in group present, other flags in group not suggested without - prefix", @@ -2998,7 +3122,8 @@ func TestCompletionForOneRequiredGroupFlags(t *testing.T) { expectedOutput: strings.Join([]string{ "subArg", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, { desc: "when all flags in group present, flags not suggested without - prefix", @@ -3006,7 +3131,8 @@ func TestCompletionForOneRequiredGroupFlags(t *testing.T) { expectedOutput: strings.Join([]string{ "subArg", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, { desc: "group ignored if some flags not applicable", @@ -3016,7 +3142,8 @@ func TestCompletionForOneRequiredGroupFlags(t *testing.T) { "completion", "help", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, } @@ -3075,7 +3202,8 @@ func TestCompletionForMutuallyExclusiveFlags(t *testing.T) { expectedOutput: strings.Join([]string{ "subArg", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, { desc: "flags in mutually exclusive group suggested with the - prefix", @@ -3088,7 +3216,8 @@ func TestCompletionForMutuallyExclusiveFlags(t *testing.T) { "--ingroup3", "--nogroup", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, { desc: "when flag in mutually exclusive group present, other flags in group not suggested even with the - prefix", @@ -3099,7 +3228,8 @@ func TestCompletionForMutuallyExclusiveFlags(t *testing.T) { "-h", "--nogroup", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, { desc: "group ignored if some flags not applicable", @@ -3110,7 +3240,8 @@ func TestCompletionForMutuallyExclusiveFlags(t *testing.T) { "--ingroup1", "--ingroup2", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, } @@ -3214,70 +3345,80 @@ func TestCompletionCobraFlags(t *testing.T) { "--version", "-v", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, { desc: "no completion after --help flag", args: []string{"--help", ""}, expectedOutput: strings.Join([]string{ ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, { desc: "no completion after -h flag", args: []string{"-h", ""}, expectedOutput: strings.Join([]string{ ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, { desc: "no completion after --version flag", args: []string{"--version", ""}, expectedOutput: strings.Join([]string{ ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, { desc: "no completion after -v flag", args: []string{"-v", ""}, expectedOutput: strings.Join([]string{ ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, { desc: "no completion after --help flag even with other completions", args: []string{"child", "--help", ""}, expectedOutput: strings.Join([]string{ ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, { desc: "no completion after -h flag even with other completions", args: []string{"child", "-h", ""}, expectedOutput: strings.Join([]string{ ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, { desc: "no completion after --version flag even with other completions", args: []string{"child", "--version", ""}, expectedOutput: strings.Join([]string{ ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, { desc: "no completion after -v flag even with other completions", args: []string{"child", "-v", ""}, expectedOutput: strings.Join([]string{ ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, { desc: "no completion after -v flag even with other flag completions", args: []string{"child", "-v", "-"}, expectedOutput: strings.Join([]string{ ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, { desc: "completion after --help flag when created by program", @@ -3285,7 +3426,8 @@ func TestCompletionCobraFlags(t *testing.T) { expectedOutput: strings.Join([]string{ "extra2", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, { desc: "completion after -h flag when created by program", @@ -3293,7 +3435,8 @@ func TestCompletionCobraFlags(t *testing.T) { expectedOutput: strings.Join([]string{ "extra2", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, { desc: "completion after --version flag when created by program", @@ -3301,7 +3444,8 @@ func TestCompletionCobraFlags(t *testing.T) { expectedOutput: strings.Join([]string{ "extra2", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, { desc: "completion after -v flag when created by program", @@ -3309,14 +3453,16 @@ func TestCompletionCobraFlags(t *testing.T) { expectedOutput: strings.Join([]string{ "extra2", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, { desc: "completion after --version when only -v flag was created by program", args: []string{"child3", "--version", ""}, expectedOutput: strings.Join([]string{ ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, { desc: "completion after -v flag when only -v flag was created by program", @@ -3324,7 +3470,8 @@ func TestCompletionCobraFlags(t *testing.T) { expectedOutput: strings.Join([]string{ "extra3", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, { desc: "no completion for --help/-h and --version/-v flags when DisableFlagParsing=true", @@ -3332,7 +3479,8 @@ func TestCompletionCobraFlags(t *testing.T) { expectedOutput: strings.Join([]string{ "extra4", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, { desc: "completions for program-defined --help/-h and --version/-v flags even when DisableFlagParsing=true", @@ -3344,7 +3492,8 @@ func TestCompletionCobraFlags(t *testing.T) { "-v", "extra5", ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + "Completion ended with directive: ShellCompDirectiveNoFileComp", "", + }, "\n"), }, } @@ -3500,7 +3649,7 @@ func TestGetFlagCompletion(t *testing.T) { for _, tc := range testcases { t.Run(tc.desc, func(t *testing.T) { - compFunc, exists := tc.cmd.GetFlagCompletionFunc(tc.flagName) + compFunc, exists := tc.cmd.GetFlagCompletionFuncByName(tc.flagName) if tc.exists != exists { t.Errorf("Unexpected result looking for flag completion function") } From 5aadb0d2bdef099b034637bdb9e2685327fc2416 Mon Sep 17 00:00:00 2001 From: maxlandon Date: Thu, 2 Nov 2023 17:43:35 +0100 Subject: [PATCH 9/9] Fix a logic lookup bug that was kind enough to surface at the good moment. --- completions.go | 22 ++++++++++++++++++---- completions_test.go | 15 ++++++++++++--- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/completions.go b/completions.go index 52e42662c..ba00ad6c2 100644 --- a/completions.go +++ b/completions.go @@ -167,13 +167,27 @@ func (c *Command) GetFlagCompletionFunc(flag *pflag.Flag) (func(cmd *Command, ar } // GetFlagCompletionByName returns the completion function for the given flag in the command by name, if available. +// If the flag is not found in the command's local flags, it looks into the persistent flags, which might belong to one of the command's parents. func (c *Command) GetFlagCompletionFuncByName(flagName string) (func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective), bool) { - flag := c.Flags().Lookup(flagName) - if flag == nil { - return nil, false + // Attempt to find it in the local flags. + if flag := c.Flags().Lookup(flagName); flag != nil { + return c.GetFlagCompletionFunc(flag) + } + + // Or try to find it in the "command-specific" persistent flags. + if flag := c.PersistentFlags().Lookup(flagName); flag != nil { + return c.GetFlagCompletionFunc(flag) + } + + // Else, check all persistent flags belonging to one of the parents. + // This ensures that we won't return the completion function of a + // parent's LOCAL flag. + if flag := c.InheritedFlags().Lookup(flagName); flag != nil { + return c.GetFlagCompletionFunc(flag) } - return c.GetFlagCompletionFunc(flag) + // No flag exists either locally, or as one of the parent persistent flags. + return nil, false } // initializeCompletionStorage is (and should be) called in all diff --git a/completions_test.go b/completions_test.go index 69836d461..451a831de 100644 --- a/completions_test.go +++ b/completions_test.go @@ -3581,21 +3581,30 @@ func TestGetFlagCompletion(t *testing.T) { rootCmd := &Command{Use: "root", Run: emptyRun} rootCmd.Flags().String("rootflag", "", "root flag") - _ = rootCmd.RegisterFlagCompletionFunc("rootflag", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { + err := rootCmd.RegisterFlagCompletionFunc("rootflag", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { return []string{"rootvalue"}, ShellCompDirectiveKeepOrder }) + if err != nil { + t.Error(err) + } rootCmd.PersistentFlags().String("persistentflag", "", "persistent flag") - _ = rootCmd.RegisterFlagCompletionFunc("persistentflag", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { + err = rootCmd.RegisterFlagCompletionFunc("persistentflag", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { return []string{"persistentvalue"}, ShellCompDirectiveDefault }) + if err != nil { + t.Error(err) + } childCmd := &Command{Use: "child", Run: emptyRun} childCmd.Flags().String("childflag", "", "child flag") - _ = childCmd.RegisterFlagCompletionFunc("childflag", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { + err = childCmd.RegisterFlagCompletionFunc("childflag", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { return []string{"childvalue"}, ShellCompDirectiveNoFileComp | ShellCompDirectiveNoSpace }) + if err != nil { + t.Error(err) + } rootCmd.AddCommand(childCmd)