summaryrefslogtreecommitdiff
path: root/vendor/github.com/spf13/cobra/completions.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/spf13/cobra/completions.go')
-rw-r--r--vendor/github.com/spf13/cobra/completions.go1020
1 files changed, 0 insertions, 1020 deletions
diff --git a/vendor/github.com/spf13/cobra/completions.go b/vendor/github.com/spf13/cobra/completions.go
deleted file mode 100644
index d3607c2d2..000000000
--- a/vendor/github.com/spf13/cobra/completions.go
+++ /dev/null
@@ -1,1020 +0,0 @@
-// Copyright 2013-2023 The Cobra Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package cobra
-
-import (
- "fmt"
- "os"
- "regexp"
- "strconv"
- "strings"
- "sync"
-
- "github.com/spf13/pflag"
-)
-
-const (
- // ShellCompRequestCmd is the name of the hidden command that is used to request
- // completion results from the program. It is used by the shell completion scripts.
- ShellCompRequestCmd = "__complete"
- // ShellCompNoDescRequestCmd is the name of the hidden command that is used to request
- // completion results without their description. It is used by the shell completion scripts.
- 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]CompletionFunc{}
-
-// 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
-
-type flagCompError struct {
- subCommand string
- flagName string
-}
-
-func (e *flagCompError) Error() string {
- return "Subcommand '" + e.subCommand + "' does not support flag '" + e.flagName + "'"
-}
-
-const (
- // ShellCompDirectiveError indicates an error occurred and completions should be ignored.
- ShellCompDirectiveError ShellCompDirective = 1 << iota
-
- // ShellCompDirectiveNoSpace indicates that the shell should not add a space
- // after the completion even if there is a single completion provided.
- ShellCompDirectiveNoSpace
-
- // ShellCompDirectiveNoFileComp indicates that the shell should not provide
- // file completion even when no completion is provided.
- ShellCompDirectiveNoFileComp
-
- // ShellCompDirectiveFilterFileExt indicates that the provided completions
- // should be used as file extension filters.
- // For flags, using Command.MarkFlagFilename() and Command.MarkPersistentFlagFilename()
- // is a shortcut to using this directive explicitly. The BashCompFilenameExt
- // annotation can also be used to obtain the same behavior for flags.
- ShellCompDirectiveFilterFileExt
-
- // ShellCompDirectiveFilterDirs indicates that only directory names should
- // be provided in file completion. To request directory names within another
- // directory, the returned completions should specify the directory within
- // which to search. The BashCompSubdirsInDir annotation can be used to
- // obtain the same behavior but only for flags.
- ShellCompDirectiveFilterDirs
-
- // ShellCompDirectiveKeepOrder indicates that the shell should preserve the order
- // in which the completions are provided
- ShellCompDirectiveKeepOrder
-
- // ===========================================================================
-
- // All directives using iota should be above this one.
- // For internal use.
- shellCompDirectiveMaxValue
-
- // ShellCompDirectiveDefault indicates to let the shell perform its default
- // behavior after completions have been provided.
- // This one must be last to avoid messing up the iota count.
- ShellCompDirectiveDefault ShellCompDirective = 0
-)
-
-const (
- // Constants for the completion command
- compCmdName = "completion"
- compCmdNoDescFlagName = "no-descriptions"
- compCmdNoDescFlagDesc = "disable completion descriptions"
- compCmdNoDescFlagDefault = false
-)
-
-// CompletionOptions are the options to control shell completion
-type CompletionOptions struct {
- // DisableDefaultCmd prevents Cobra from creating a default 'completion' command
- DisableDefaultCmd bool
- // DisableNoDescFlag prevents Cobra from creating the '--no-descriptions' flag
- // for shells that support completion descriptions
- DisableNoDescFlag bool
- // DisableDescriptions turns off all completion descriptions for shells
- // that support them
- DisableDescriptions bool
- // HiddenDefaultCmd makes the default 'completion' command hidden
- HiddenDefaultCmd bool
- // DefaultShellCompDirective sets the ShellCompDirective that is returned
- // if no special directive can be determined
- DefaultShellCompDirective *ShellCompDirective
-}
-
-func (receiver *CompletionOptions) SetDefaultShellCompDirective(directive ShellCompDirective) {
- receiver.DefaultShellCompDirective = &directive
-}
-
-// Completion is a string that can be used for completions
-//
-// two formats are supported:
-// - the completion choice
-// - the completion choice with a textual description (separated by a TAB).
-//
-// [CompletionWithDesc] can be used to create a completion string with a textual description.
-//
-// Note: Go type alias is used to provide a more descriptive name in the documentation, but any string can be used.
-type Completion = string
-
-// CompletionFunc is a function that provides completion results.
-type CompletionFunc = func(cmd *Command, args []string, toComplete string) ([]Completion, ShellCompDirective)
-
-// CompletionWithDesc returns a [Completion] with a description by using the TAB delimited format.
-func CompletionWithDesc(choice string, description string) Completion {
- return choice + "\t" + description
-}
-
-// NoFileCompletions can be used to disable file completion for commands that should
-// not trigger file completions.
-//
-// This method satisfies [CompletionFunc].
-// It can be used with [Command.RegisterFlagCompletionFunc] and for [Command.ValidArgsFunction].
-func NoFileCompletions(cmd *Command, args []string, toComplete string) ([]Completion, ShellCompDirective) {
- return nil, ShellCompDirectiveNoFileComp
-}
-
-// FixedCompletions can be used to create a completion function which always
-// returns the same results.
-//
-// This method returns a function that satisfies [CompletionFunc]
-// It can be used with [Command.RegisterFlagCompletionFunc] and for [Command.ValidArgsFunction].
-func FixedCompletions(choices []Completion, directive ShellCompDirective) CompletionFunc {
- return func(cmd *Command, args []string, toComplete string) ([]Completion, ShellCompDirective) {
- return choices, directive
- }
-}
-
-// RegisterFlagCompletionFunc should be called to register a function to provide completion for a flag.
-//
-// You can use pre-defined completion functions such as [FixedCompletions] or [NoFileCompletions],
-// or you can define your own.
-func (c *Command) RegisterFlagCompletionFunc(flagName string, f CompletionFunc) error {
- flag := c.Flag(flagName)
- if flag == nil {
- return fmt.Errorf("RegisterFlagCompletionFunc: flag '%s' does not exist", flagName)
- }
- flagCompletionMutex.Lock()
- defer flagCompletionMutex.Unlock()
-
- if _, exists := flagCompletionFunctions[flag]; exists {
- return fmt.Errorf("RegisterFlagCompletionFunc: flag '%s' already registered", flagName)
- }
- flagCompletionFunctions[flag] = f
- return nil
-}
-
-// GetFlagCompletionFunc returns the completion function for the given flag of the command, if available.
-func (c *Command) GetFlagCompletionFunc(flagName string) (CompletionFunc, bool) {
- flag := c.Flag(flagName)
- if flag == nil {
- return nil, false
- }
-
- flagCompletionMutex.RLock()
- defer flagCompletionMutex.RUnlock()
-
- completionFunc, exists := flagCompletionFunctions[flag]
- return completionFunc, exists
-}
-
-// Returns a string listing the different directive enabled in the specified parameter
-func (d ShellCompDirective) string() string {
- var directives []string
- if d&ShellCompDirectiveError != 0 {
- directives = append(directives, "ShellCompDirectiveError")
- }
- if d&ShellCompDirectiveNoSpace != 0 {
- directives = append(directives, "ShellCompDirectiveNoSpace")
- }
- if d&ShellCompDirectiveNoFileComp != 0 {
- directives = append(directives, "ShellCompDirectiveNoFileComp")
- }
- if d&ShellCompDirectiveFilterFileExt != 0 {
- directives = append(directives, "ShellCompDirectiveFilterFileExt")
- }
- if d&ShellCompDirectiveFilterDirs != 0 {
- directives = append(directives, "ShellCompDirectiveFilterDirs")
- }
- if d&ShellCompDirectiveKeepOrder != 0 {
- directives = append(directives, "ShellCompDirectiveKeepOrder")
- }
- if len(directives) == 0 {
- directives = append(directives, "ShellCompDirectiveDefault")
- }
-
- if d >= shellCompDirectiveMaxValue {
- return fmt.Sprintf("ERROR: unexpected ShellCompDirective value: %d", d)
- }
- return strings.Join(directives, ", ")
-}
-
-// initCompleteCmd adds a special hidden command that can be used to request custom completions.
-func (c *Command) initCompleteCmd(args []string) {
- completeCmd := &Command{
- Use: fmt.Sprintf("%s [command-line]", ShellCompRequestCmd),
- Aliases: []string{ShellCompNoDescRequestCmd},
- DisableFlagsInUseLine: true,
- Hidden: true,
- DisableFlagParsing: true,
- Args: MinimumNArgs(1),
- Short: "Request shell completion choices for the specified command-line",
- Long: fmt.Sprintf("%[2]s is a special command that is used by the shell completion logic\n%[1]s",
- "to request completion choices for the specified command-line.", ShellCompRequestCmd),
- Run: func(cmd *Command, args []string) {
- finalCmd, completions, directive, err := cmd.getCompletions(args)
- if err != nil {
- CompErrorln(err.Error())
- // Keep going for multiple reasons:
- // 1- There could be some valid completions even though there was an error
- // 2- Even without completions, we need to print the directive
- }
-
- noDescriptions := cmd.CalledAs() == ShellCompNoDescRequestCmd
- if !noDescriptions {
- if doDescriptions, err := strconv.ParseBool(getEnvConfig(cmd, configEnvVarSuffixDescriptions)); err == nil {
- noDescriptions = !doDescriptions
- }
- }
- noActiveHelp := GetActiveHelpConfig(finalCmd) == activeHelpGlobalDisable
- out := finalCmd.OutOrStdout()
- for _, comp := range completions {
- if noActiveHelp && strings.HasPrefix(comp, activeHelpMarker) {
- // Remove all activeHelp entries if it's disabled.
- continue
- }
- if noDescriptions {
- // Remove any description that may be included following a tab character.
- comp = strings.SplitN(comp, "\t", 2)[0]
- }
-
- // Make sure we only write the first line to the output.
- // This is needed if a description contains a linebreak.
- // Otherwise the shell scripts will interpret the other lines as new flags
- // and could therefore provide a wrong completion.
- comp = strings.SplitN(comp, "\n", 2)[0]
-
- // Finally trim the completion. This is especially important to get rid
- // of a trailing tab when there are no description following it.
- // For example, a sub-command without a description should not be completed
- // with a tab at the end (or else zsh will show a -- following it
- // although there is no description).
- comp = strings.TrimSpace(comp)
-
- // Print each possible completion to the output for the completion script to consume.
- fmt.Fprintln(out, comp)
- }
-
- // As the last printout, print the completion directive for the completion script to parse.
- // The directive integer must be that last character following a single colon (:).
- // The completion script expects :<directive>
- fmt.Fprintf(out, ":%d\n", directive)
-
- // Print some helpful info to stderr for the user to understand.
- // Output from stderr must be ignored by the completion script.
- fmt.Fprintf(finalCmd.ErrOrStderr(), "Completion ended with directive: %s\n", directive.string())
- },
- }
- c.AddCommand(completeCmd)
- subCmd, _, err := c.Find(args)
- if err != nil || subCmd.Name() != ShellCompRequestCmd {
- // Only create this special command if it is actually being called.
- // This reduces possible side-effects of creating such a command;
- // for example, having this command would cause problems to a
- // cobra program that only consists of the root command, since this
- // command would cause the root command to suddenly have a subcommand.
- c.RemoveCommand(completeCmd)
- }
-}
-
-// SliceValue is a reduced version of [pflag.SliceValue]. It is used to detect
-// flags that accept multiple values and therefore can provide completion
-// multiple times.
-type SliceValue interface {
- // GetSlice returns the flag value list as an array of strings.
- GetSlice() []string
-}
-
-func (c *Command) getCompletions(args []string) (*Command, []Completion, ShellCompDirective, error) {
- // The last argument, which is not completely typed by the user,
- // should not be part of the list of arguments
- toComplete := args[len(args)-1]
- trimmedArgs := args[:len(args)-1]
-
- var finalCmd *Command
- var finalArgs []string
- var err error
- // Find the real command for which completion must be performed
- // check if we need to traverse here to parse local flags on parent commands
- if c.Root().TraverseChildren {
- finalCmd, finalArgs, err = c.Root().Traverse(trimmedArgs)
- } else {
- // For Root commands that don't specify any value for their Args fields, when we call
- // Find(), if those Root commands don't have any sub-commands, they will accept arguments.
- // However, because we have added the __complete sub-command in the current code path, the
- // call to Find() -> legacyArgs() will return an error if there are any arguments.
- // To avoid this, we first remove the __complete command to get back to having no sub-commands.
- rootCmd := c.Root()
- if len(rootCmd.Commands()) == 1 {
- rootCmd.RemoveCommand(c)
- }
-
- finalCmd, finalArgs, err = rootCmd.Find(trimmedArgs)
- }
- if err != nil {
- // Unable to find the real command. E.g., <program> someInvalidCmd <TAB>
- return c, []Completion{}, ShellCompDirectiveDefault, fmt.Errorf("unable to find a command for arguments: %v", trimmedArgs)
- }
- finalCmd.ctx = c.ctx
-
- // These flags are normally added when `execute()` is called on `finalCmd`,
- // however, when doing completion, we don't call `finalCmd.execute()`.
- // Let's add the --help and --version flag ourselves but only if the finalCmd
- // has not disabled flag parsing; if flag parsing is disabled, it is up to the
- // finalCmd itself to handle the completion of *all* flags.
- if !finalCmd.DisableFlagParsing {
- finalCmd.InitDefaultHelpFlag()
- finalCmd.InitDefaultVersionFlag()
- }
-
- // Check if we are doing flag value completion before parsing the flags.
- // This is important because if we are completing a flag value, we need to also
- // remove the flag name argument from the list of finalArgs or else the parsing
- // could fail due to an invalid value (incomplete) for the flag.
- flag, finalArgs, toComplete, flagErr := checkIfFlagCompletion(finalCmd, finalArgs, toComplete)
-
- // Check if interspersed is false or -- was set on a previous arg.
- // This works by counting the arguments. Normally -- is not counted as arg but
- // if -- was already set or interspersed is false and there is already one arg then
- // the extra added -- is counted as arg.
- flagCompletion := true
- _ = finalCmd.ParseFlags(append(finalArgs, "--"))
- newArgCount := finalCmd.Flags().NArg()
-
- // Parse the flags early so we can check if required flags are set
- if err = finalCmd.ParseFlags(finalArgs); err != nil {
- return finalCmd, []Completion{}, ShellCompDirectiveDefault, fmt.Errorf("Error while parsing flags from args %v: %s", finalArgs, err.Error())
- }
-
- realArgCount := finalCmd.Flags().NArg()
- if newArgCount > realArgCount {
- // don't do flag completion (see above)
- flagCompletion = false
- }
- // Error while attempting to parse flags
- if flagErr != nil {
- // If error type is flagCompError and we don't want flagCompletion we should ignore the error
- if _, ok := flagErr.(*flagCompError); !ok || flagCompletion {
- return finalCmd, []Completion{}, ShellCompDirectiveDefault, flagErr
- }
- }
-
- // Look for the --help or --version flags. If they are present,
- // there should be no further completions.
- if helpOrVersionFlagPresent(finalCmd) {
- return finalCmd, []Completion{}, ShellCompDirectiveNoFileComp, nil
- }
-
- // We only remove the flags from the arguments if DisableFlagParsing is not set.
- // This is important for commands which have requested to do their own flag completion.
- if !finalCmd.DisableFlagParsing {
- finalArgs = finalCmd.Flags().Args()
- }
-
- if flag != nil && flagCompletion {
- // Check if we are completing a flag value subject to annotations
- if validExts, present := flag.Annotations[BashCompFilenameExt]; present {
- if len(validExts) != 0 {
- // File completion filtered by extensions
- return finalCmd, validExts, ShellCompDirectiveFilterFileExt, nil
- }
-
- // The annotation requests simple file completion. There is no reason to do
- // that since it is the default behavior anyway. Let's ignore this annotation
- // in case the program also registered a completion function for this flag.
- // Even though it is a mistake on the program's side, let's be nice when we can.
- }
-
- if subDir, present := flag.Annotations[BashCompSubdirsInDir]; present {
- if len(subDir) == 1 {
- // Directory completion from within a directory
- return finalCmd, subDir, ShellCompDirectiveFilterDirs, nil
- }
- // Directory completion
- return finalCmd, []Completion{}, ShellCompDirectiveFilterDirs, nil
- }
- }
-
- var completions []Completion
- var directive ShellCompDirective
-
- // Enforce flag groups before doing flag completions
- finalCmd.enforceFlagGroupsForCompletion()
-
- // Note that we want to perform flagname completion even if finalCmd.DisableFlagParsing==true;
- // doing this allows for completion of persistent flag names even for commands that disable flag parsing.
- //
- // When doing completion of a flag name, as soon as an argument starts with
- // a '-' we know it is a flag. We cannot use isFlagArg() here as it requires
- // the flag name to be complete
- if flag == nil && len(toComplete) > 0 && toComplete[0] == '-' && !strings.Contains(toComplete, "=") && flagCompletion {
- // First check for required flags
- completions = completeRequireFlags(finalCmd, toComplete)
-
- // If we have not found any required flags, only then can we show regular flags
- if len(completions) == 0 {
- doCompleteFlags := func(flag *pflag.Flag) {
- _, acceptsMultiple := flag.Value.(SliceValue)
- acceptsMultiple = acceptsMultiple ||
- strings.Contains(flag.Value.Type(), "Slice") ||
- strings.Contains(flag.Value.Type(), "Array") ||
- strings.HasPrefix(flag.Value.Type(), "stringTo")
-
- if !flag.Changed || acceptsMultiple {
- // If the flag is not already present, or if it can be specified multiple times (Array, Slice, or stringTo)
- // we suggest it as a completion
- completions = append(completions, getFlagNameCompletions(flag, toComplete)...)
- }
- }
-
- // We cannot use finalCmd.Flags() because we may not have called ParsedFlags() for commands
- // that have set DisableFlagParsing; it is ParseFlags() that merges the inherited and
- // non-inherited flags.
- finalCmd.InheritedFlags().VisitAll(func(flag *pflag.Flag) {
- doCompleteFlags(flag)
- })
- // Try to complete non-inherited flags even if DisableFlagParsing==true.
- // This allows programs to tell Cobra about flags for completion even
- // if the actual parsing of flags is not done by Cobra.
- // For instance, Helm uses this to provide flag name completion for
- // some of its plugins.
- finalCmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) {
- doCompleteFlags(flag)
- })
- }
-
- directive = ShellCompDirectiveNoFileComp
- if len(completions) == 1 && strings.HasSuffix(completions[0], "=") {
- // If there is a single completion, the shell usually adds a space
- // after the completion. We don't want that if the flag ends with an =
- directive = ShellCompDirectiveNoSpace
- }
-
- if !finalCmd.DisableFlagParsing {
- // If DisableFlagParsing==false, we have completed the flags as known by Cobra;
- // we can return what we found.
- // If DisableFlagParsing==true, Cobra may not be aware of all flags, so we
- // let the logic continue to see if ValidArgsFunction needs to be called.
- return finalCmd, completions, directive, nil
- }
- } else {
- directive = ShellCompDirectiveDefault
- // check current and parent commands for a custom DefaultShellCompDirective
- for cmd := finalCmd; cmd != nil; cmd = cmd.parent {
- if cmd.CompletionOptions.DefaultShellCompDirective != nil {
- directive = *cmd.CompletionOptions.DefaultShellCompDirective
- break
- }
- }
-
- if flag == nil {
- foundLocalNonPersistentFlag := false
- // If TraverseChildren is true on the root command we don't check for
- // local flags because we can use a local flag on a parent command
- if !finalCmd.Root().TraverseChildren {
- // Check if there are any local, non-persistent flags on the command-line
- localNonPersistentFlags := finalCmd.LocalNonPersistentFlags()
- finalCmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) {
- if localNonPersistentFlags.Lookup(flag.Name) != nil && flag.Changed {
- foundLocalNonPersistentFlag = true
- }
- })
- }
-
- // Complete subcommand names, including the help command
- if len(finalArgs) == 0 && !foundLocalNonPersistentFlag {
- // We only complete sub-commands if:
- // - there are no arguments on the command-line and
- // - there are no local, non-persistent flags on the command-line or TraverseChildren is true
- for _, subCmd := range finalCmd.Commands() {
- if subCmd.IsAvailableCommand() || subCmd == finalCmd.helpCommand {
- if strings.HasPrefix(subCmd.Name(), toComplete) {
- completions = append(completions, CompletionWithDesc(subCmd.Name(), subCmd.Short))
- }
- directive = ShellCompDirectiveNoFileComp
- }
- }
- }
-
- // Complete required flags even without the '-' prefix
- completions = append(completions, completeRequireFlags(finalCmd, toComplete)...)
-
- // Always complete ValidArgs, even if we are completing a subcommand name.
- // This is for commands that have both subcommands and ValidArgs.
- if len(finalCmd.ValidArgs) > 0 {
- if len(finalArgs) == 0 {
- // ValidArgs are only for the first argument
- for _, validArg := range finalCmd.ValidArgs {
- if strings.HasPrefix(validArg, toComplete) {
- completions = append(completions, validArg)
- }
- }
- directive = ShellCompDirectiveNoFileComp
-
- // If no completions were found within commands or ValidArgs,
- // see if there are any ArgAliases that should be completed.
- if len(completions) == 0 {
- for _, argAlias := range finalCmd.ArgAliases {
- if strings.HasPrefix(argAlias, toComplete) {
- completions = append(completions, argAlias)
- }
- }
- }
- }
-
- // If there are ValidArgs specified (even if they don't match), we stop completion.
- // Only one of ValidArgs or ValidArgsFunction can be used for a single command.
- return finalCmd, completions, directive, nil
- }
-
- // Let the logic continue so as to add any ValidArgsFunction completions,
- // even if we already found sub-commands.
- // This is for commands that have subcommands but also specify a ValidArgsFunction.
- }
- }
-
- // Find the completion function for the flag or command
- var completionFn CompletionFunc
- if flag != nil && flagCompletion {
- flagCompletionMutex.RLock()
- completionFn = flagCompletionFunctions[flag]
- flagCompletionMutex.RUnlock()
- } else {
- completionFn = finalCmd.ValidArgsFunction
- }
- if completionFn != nil {
- // Go custom completion defined for this flag or command.
- // Call the registered completion function to get the completions.
- var comps []Completion
- comps, directive = completionFn(finalCmd, finalArgs, toComplete)
- completions = append(completions, comps...)
- }
-
- return finalCmd, completions, directive, nil
-}
-
-func helpOrVersionFlagPresent(cmd *Command) bool {
- if versionFlag := cmd.Flags().Lookup("version"); versionFlag != nil &&
- len(versionFlag.Annotations[FlagSetByCobraAnnotation]) > 0 && versionFlag.Changed {
- return true
- }
- if helpFlag := cmd.Flags().Lookup(helpFlagName); helpFlag != nil &&
- len(helpFlag.Annotations[FlagSetByCobraAnnotation]) > 0 && helpFlag.Changed {
- return true
- }
- return false
-}
-
-func getFlagNameCompletions(flag *pflag.Flag, toComplete string) []Completion {
- if nonCompletableFlag(flag) {
- return []Completion{}
- }
-
- var completions []Completion
- flagName := "--" + flag.Name
- if strings.HasPrefix(flagName, toComplete) {
- // Flag without the =
- completions = append(completions, CompletionWithDesc(flagName, flag.Usage))
-
- // Why suggest both long forms: --flag and --flag= ?
- // This forces the user to *always* have to type either an = or a space after the flag name.
- // Let's be nice and avoid making users have to do that.
- // Since boolean flags and shortname flags don't show the = form, let's go that route and never show it.
- // The = form will still work, we just won't suggest it.
- // This also makes the list of suggested flags shorter as we avoid all the = forms.
- //
- // if len(flag.NoOptDefVal) == 0 {
- // // Flag requires a value, so it can be suffixed with =
- // flagName += "="
- // completions = append(completions, CompletionWithDesc(flagName, flag.Usage))
- // }
- }
-
- flagName = "-" + flag.Shorthand
- if len(flag.Shorthand) > 0 && strings.HasPrefix(flagName, toComplete) {
- completions = append(completions, CompletionWithDesc(flagName, flag.Usage))
- }
-
- return completions
-}
-
-func completeRequireFlags(finalCmd *Command, toComplete string) []Completion {
- var completions []Completion
-
- doCompleteRequiredFlags := func(flag *pflag.Flag) {
- if _, present := flag.Annotations[BashCompOneRequiredFlag]; present {
- if !flag.Changed {
- // If the flag is not already present, we suggest it as a completion
- completions = append(completions, getFlagNameCompletions(flag, toComplete)...)
- }
- }
- }
-
- // We cannot use finalCmd.Flags() because we may not have called ParsedFlags() for commands
- // that have set DisableFlagParsing; it is ParseFlags() that merges the inherited and
- // non-inherited flags.
- finalCmd.InheritedFlags().VisitAll(func(flag *pflag.Flag) {
- doCompleteRequiredFlags(flag)
- })
- finalCmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) {
- doCompleteRequiredFlags(flag)
- })
-
- return completions
-}
-
-func checkIfFlagCompletion(finalCmd *Command, args []string, lastArg string) (*pflag.Flag, []string, string, error) {
- if finalCmd.DisableFlagParsing {
- // We only do flag completion if we are allowed to parse flags
- // This is important for commands which have requested to do their own flag completion.
- return nil, args, lastArg, nil
- }
-
- var flagName string
- trimmedArgs := args
- flagWithEqual := false
- orgLastArg := lastArg
-
- // When doing completion of a flag name, as soon as an argument starts with
- // a '-' we know it is a flag. We cannot use isFlagArg() here as that function
- // requires the flag name to be complete
- if len(lastArg) > 0 && lastArg[0] == '-' {
- if index := strings.Index(lastArg, "="); index >= 0 {
- // Flag with an =
- if strings.HasPrefix(lastArg[:index], "--") {
- // Flag has full name
- flagName = lastArg[2:index]
- } else {
- // Flag is shorthand
- // We have to get the last shorthand flag name
- // e.g. `-asd` => d to provide the correct completion
- // https://github.com/spf13/cobra/issues/1257
- flagName = lastArg[index-1 : index]
- }
- lastArg = lastArg[index+1:]
- flagWithEqual = true
- } else {
- // Normal flag completion
- return nil, args, lastArg, nil
- }
- }
-
- if len(flagName) == 0 {
- if len(args) > 0 {
- prevArg := args[len(args)-1]
- if isFlagArg(prevArg) {
- // Only consider the case where the flag does not contain an =.
- // If the flag contains an = it means it has already been fully processed,
- // so we don't need to deal with it here.
- if index := strings.Index(prevArg, "="); index < 0 {
- if strings.HasPrefix(prevArg, "--") {
- // Flag has full name
- flagName = prevArg[2:]
- } else {
- // Flag is shorthand
- // We have to get the last shorthand flag name
- // e.g. `-asd` => d to provide the correct completion
- // https://github.com/spf13/cobra/issues/1257
- flagName = prevArg[len(prevArg)-1:]
- }
- // Remove the uncompleted flag or else there could be an error created
- // for an invalid value for that flag
- trimmedArgs = args[:len(args)-1]
- }
- }
- }
- }
-
- if len(flagName) == 0 {
- // Not doing flag completion
- return nil, trimmedArgs, lastArg, nil
- }
-
- flag := findFlag(finalCmd, flagName)
- if flag == nil {
- // Flag not supported by this command, the interspersed option might be set so return the original args
- return nil, args, orgLastArg, &flagCompError{subCommand: finalCmd.Name(), flagName: flagName}
- }
-
- if !flagWithEqual {
- if len(flag.NoOptDefVal) != 0 {
- // We had assumed dealing with a two-word flag but the flag is a boolean flag.
- // In that case, there is no value following it, so we are not really doing flag completion.
- // Reset everything to do noun completion.
- trimmedArgs = args
- flag = nil
- }
- }
-
- return flag, trimmedArgs, lastArg, nil
-}
-
-// InitDefaultCompletionCmd adds a default 'completion' command to c.
-// This function will do nothing if any of the following is true:
-// 1- the feature has been explicitly disabled by the program,
-// 2- c has no subcommands (to avoid creating one),
-// 3- c already has a 'completion' command provided by the program.
-func (c *Command) InitDefaultCompletionCmd(args ...string) {
- if c.CompletionOptions.DisableDefaultCmd {
- return
- }
-
- for _, cmd := range c.commands {
- if cmd.Name() == compCmdName || cmd.HasAlias(compCmdName) {
- // A completion command is already available
- return
- }
- }
-
- haveNoDescFlag := !c.CompletionOptions.DisableNoDescFlag && !c.CompletionOptions.DisableDescriptions
-
- // Special case to know if there are sub-commands or not.
- hasSubCommands := false
- for _, cmd := range c.commands {
- if cmd.Name() != ShellCompRequestCmd && cmd.Name() != helpCommandName {
- // We found a real sub-command (not 'help' or '__complete')
- hasSubCommands = true
- break
- }
- }
-
- completionCmd := &Command{
- Use: compCmdName,
- Short: "Generate the autocompletion script for the specified shell",
- Long: fmt.Sprintf(`Generate the autocompletion script for %[1]s for the specified shell.
-See each sub-command's help for details on how to use the generated script.
-`, c.Root().Name()),
- Args: NoArgs,
- ValidArgsFunction: NoFileCompletions,
- Hidden: c.CompletionOptions.HiddenDefaultCmd,
- GroupID: c.completionCommandGroupID,
- }
- c.AddCommand(completionCmd)
-
- if !hasSubCommands {
- // If the 'completion' command will be the only sub-command,
- // we only create it if it is actually being called.
- // This avoids breaking programs that would suddenly find themselves with
- // a subcommand, which would prevent them from accepting arguments.
- // We also create the 'completion' command if the user is triggering
- // shell completion for it (prog __complete completion '')
- subCmd, cmdArgs, err := c.Find(args)
- if err != nil || subCmd.Name() != compCmdName &&
- (subCmd.Name() != ShellCompRequestCmd || len(cmdArgs) <= 1 || cmdArgs[0] != compCmdName) {
- // The completion command is not being called or being completed so we remove it.
- c.RemoveCommand(completionCmd)
- return
- }
- }
-
- out := c.OutOrStdout()
- noDesc := c.CompletionOptions.DisableDescriptions
- shortDesc := "Generate the autocompletion script for %s"
- bash := &Command{
- Use: "bash",
- Short: fmt.Sprintf(shortDesc, "bash"),
- Long: fmt.Sprintf(`Generate the autocompletion script for the bash shell.
-
-This script depends on the 'bash-completion' package.
-If it is not installed already, you can install it via your OS's package manager.
-
-To load completions in your current shell session:
-
- source <(%[1]s completion bash)
-
-To load completions for every new session, execute once:
-
-#### Linux:
-
- %[1]s completion bash > /etc/bash_completion.d/%[1]s
-
-#### macOS:
-
- %[1]s completion bash > $(brew --prefix)/etc/bash_completion.d/%[1]s
-
-You will need to start a new shell for this setup to take effect.
-`, c.Root().Name()),
- Args: NoArgs,
- DisableFlagsInUseLine: true,
- ValidArgsFunction: NoFileCompletions,
- RunE: func(cmd *Command, args []string) error {
- return cmd.Root().GenBashCompletionV2(out, !noDesc)
- },
- }
- if haveNoDescFlag {
- bash.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc)
- }
-
- zsh := &Command{
- Use: "zsh",
- Short: fmt.Sprintf(shortDesc, "zsh"),
- Long: fmt.Sprintf(`Generate the autocompletion script for the zsh shell.
-
-If shell completion is not already enabled in your environment you will need
-to enable it. You can execute the following once:
-
- echo "autoload -U compinit; compinit" >> ~/.zshrc
-
-To load completions in your current shell session:
-
- source <(%[1]s completion zsh)
-
-To load completions for every new session, execute once:
-
-#### Linux:
-
- %[1]s completion zsh > "${fpath[1]}/_%[1]s"
-
-#### macOS:
-
- %[1]s completion zsh > $(brew --prefix)/share/zsh/site-functions/_%[1]s
-
-You will need to start a new shell for this setup to take effect.
-`, c.Root().Name()),
- Args: NoArgs,
- ValidArgsFunction: NoFileCompletions,
- RunE: func(cmd *Command, args []string) error {
- if noDesc {
- return cmd.Root().GenZshCompletionNoDesc(out)
- }
- return cmd.Root().GenZshCompletion(out)
- },
- }
- if haveNoDescFlag {
- zsh.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc)
- }
-
- fish := &Command{
- Use: "fish",
- Short: fmt.Sprintf(shortDesc, "fish"),
- Long: fmt.Sprintf(`Generate the autocompletion script for the fish shell.
-
-To load completions in your current shell session:
-
- %[1]s completion fish | source
-
-To load completions for every new session, execute once:
-
- %[1]s completion fish > ~/.config/fish/completions/%[1]s.fish
-
-You will need to start a new shell for this setup to take effect.
-`, c.Root().Name()),
- Args: NoArgs,
- ValidArgsFunction: NoFileCompletions,
- RunE: func(cmd *Command, args []string) error {
- return cmd.Root().GenFishCompletion(out, !noDesc)
- },
- }
- if haveNoDescFlag {
- fish.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc)
- }
-
- powershell := &Command{
- Use: "powershell",
- Short: fmt.Sprintf(shortDesc, "powershell"),
- Long: fmt.Sprintf(`Generate the autocompletion script for powershell.
-
-To load completions in your current shell session:
-
- %[1]s completion powershell | Out-String | Invoke-Expression
-
-To load completions for every new session, add the output of the above command
-to your powershell profile.
-`, c.Root().Name()),
- Args: NoArgs,
- ValidArgsFunction: NoFileCompletions,
- RunE: func(cmd *Command, args []string) error {
- if noDesc {
- return cmd.Root().GenPowerShellCompletion(out)
- }
- return cmd.Root().GenPowerShellCompletionWithDesc(out)
-
- },
- }
- if haveNoDescFlag {
- powershell.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc)
- }
-
- completionCmd.AddCommand(bash, zsh, fish, powershell)
-}
-
-func findFlag(cmd *Command, name string) *pflag.Flag {
- flagSet := cmd.Flags()
- if len(name) == 1 {
- // First convert the short flag into a long flag
- // as the cmd.Flag() search only accepts long flags
- if short := flagSet.ShorthandLookup(name); short != nil {
- name = short.Name
- } else {
- set := cmd.InheritedFlags()
- if short = set.ShorthandLookup(name); short != nil {
- name = short.Name
- } else {
- return nil
- }
- }
- }
- return cmd.Flag(name)
-}
-
-// CompDebug prints the specified string to the same file as where the
-// completion script prints its logs.
-// Note that completion printouts should never be on stdout as they would
-// be wrongly interpreted as actual completion choices by the completion script.
-func CompDebug(msg string, printToStdErr bool) {
- msg = fmt.Sprintf("[Debug] %s", msg)
-
- // Such logs are only printed when the user has set the environment
- // 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)
- if err == nil {
- defer f.Close()
- WriteStringAndCheck(f, msg)
- }
- }
-
- if printToStdErr {
- // Must print to stderr for this not to be read by the completion script.
- fmt.Fprint(os.Stderr, msg)
- }
-}
-
-// CompDebugln prints the specified string with a newline at the end
-// to the same file as where the completion script prints its logs.
-// Such logs are only printed when the user has set the environment
-// variable BASH_COMP_DEBUG_FILE to the path of some file to be used.
-func CompDebugln(msg string, printToStdErr bool) {
- CompDebug(fmt.Sprintf("%s\n", msg), printToStdErr)
-}
-
-// CompError prints the specified completion message to stderr.
-func CompError(msg string) {
- msg = fmt.Sprintf("[Error] %s", msg)
- CompDebug(msg, true)
-}
-
-// CompErrorln prints the specified completion message to stderr with a newline at the end.
-func CompErrorln(msg string) {
- CompError(fmt.Sprintf("%s\n", msg))
-}
-
-// These values should not be changed: users will be using them explicitly.
-const (
- configEnvVarGlobalPrefix = "COBRA"
- configEnvVarSuffixDescriptions = "COMPLETION_DESCRIPTIONS"
-)
-
-var configEnvVarPrefixSubstRegexp = regexp.MustCompile(`[^A-Z0-9_]`)
-
-// configEnvVar returns the name of the program-specific configuration environment
-// variable. It has the format <PROGRAM>_<SUFFIX> where <PROGRAM> is the name of the
-// root command in upper case, with all non-ASCII-alphanumeric characters replaced by `_`.
-func configEnvVar(name, suffix string) string {
- // This format should not be changed: users will be using it explicitly.
- v := strings.ToUpper(fmt.Sprintf("%s_%s", name, suffix))
- v = configEnvVarPrefixSubstRegexp.ReplaceAllString(v, "_")
- return v
-}
-
-// getEnvConfig returns the value of the configuration environment variable
-// <PROGRAM>_<SUFFIX> where <PROGRAM> is the name of the root command in upper
-// case, with all non-ASCII-alphanumeric characters replaced by `_`.
-// If the value is empty or not set, the value of the environment variable
-// COBRA_<SUFFIX> is returned instead.
-func getEnvConfig(cmd *Command, suffix string) string {
- v := os.Getenv(configEnvVar(cmd.Root().Name(), suffix))
- if v == "" {
- v = os.Getenv(configEnvVar(configEnvVarGlobalPrefix, suffix))
- }
- return v
-}