vendor/github.com/spf13/cobra/completions.go
changeset 256 6d9efbef00a9
child 260 445e01aede7e
equal deleted inserted replaced
255:4f153a23adab 256:6d9efbef00a9
       
     1 package cobra
       
     2 
       
     3 import (
       
     4 	"fmt"
       
     5 	"os"
       
     6 	"strings"
       
     7 	"sync"
       
     8 
       
     9 	"github.com/spf13/pflag"
       
    10 )
       
    11 
       
    12 const (
       
    13 	// ShellCompRequestCmd is the name of the hidden command that is used to request
       
    14 	// completion results from the program.  It is used by the shell completion scripts.
       
    15 	ShellCompRequestCmd = "__complete"
       
    16 	// ShellCompNoDescRequestCmd is the name of the hidden command that is used to request
       
    17 	// completion results without their description.  It is used by the shell completion scripts.
       
    18 	ShellCompNoDescRequestCmd = "__completeNoDesc"
       
    19 )
       
    20 
       
    21 // Global map of flag completion functions. Make sure to use flagCompletionMutex before you try to read and write from it.
       
    22 var flagCompletionFunctions = map[*pflag.Flag]func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective){}
       
    23 
       
    24 // lock for reading and writing from flagCompletionFunctions
       
    25 var flagCompletionMutex = &sync.RWMutex{}
       
    26 
       
    27 // ShellCompDirective is a bit map representing the different behaviors the shell
       
    28 // can be instructed to have once completions have been provided.
       
    29 type ShellCompDirective int
       
    30 
       
    31 type flagCompError struct {
       
    32 	subCommand string
       
    33 	flagName   string
       
    34 }
       
    35 
       
    36 func (e *flagCompError) Error() string {
       
    37 	return "Subcommand '" + e.subCommand + "' does not support flag '" + e.flagName + "'"
       
    38 }
       
    39 
       
    40 const (
       
    41 	// ShellCompDirectiveError indicates an error occurred and completions should be ignored.
       
    42 	ShellCompDirectiveError ShellCompDirective = 1 << iota
       
    43 
       
    44 	// ShellCompDirectiveNoSpace indicates that the shell should not add a space
       
    45 	// after the completion even if there is a single completion provided.
       
    46 	ShellCompDirectiveNoSpace
       
    47 
       
    48 	// ShellCompDirectiveNoFileComp indicates that the shell should not provide
       
    49 	// file completion even when no completion is provided.
       
    50 	ShellCompDirectiveNoFileComp
       
    51 
       
    52 	// ShellCompDirectiveFilterFileExt indicates that the provided completions
       
    53 	// should be used as file extension filters.
       
    54 	// For flags, using Command.MarkFlagFilename() and Command.MarkPersistentFlagFilename()
       
    55 	// is a shortcut to using this directive explicitly.  The BashCompFilenameExt
       
    56 	// annotation can also be used to obtain the same behavior for flags.
       
    57 	ShellCompDirectiveFilterFileExt
       
    58 
       
    59 	// ShellCompDirectiveFilterDirs indicates that only directory names should
       
    60 	// be provided in file completion.  To request directory names within another
       
    61 	// directory, the returned completions should specify the directory within
       
    62 	// which to search.  The BashCompSubdirsInDir annotation can be used to
       
    63 	// obtain the same behavior but only for flags.
       
    64 	ShellCompDirectiveFilterDirs
       
    65 
       
    66 	// ===========================================================================
       
    67 
       
    68 	// All directives using iota should be above this one.
       
    69 	// For internal use.
       
    70 	shellCompDirectiveMaxValue
       
    71 
       
    72 	// ShellCompDirectiveDefault indicates to let the shell perform its default
       
    73 	// behavior after completions have been provided.
       
    74 	// This one must be last to avoid messing up the iota count.
       
    75 	ShellCompDirectiveDefault ShellCompDirective = 0
       
    76 )
       
    77 
       
    78 const (
       
    79 	// Constants for the completion command
       
    80 	compCmdName              = "completion"
       
    81 	compCmdNoDescFlagName    = "no-descriptions"
       
    82 	compCmdNoDescFlagDesc    = "disable completion descriptions"
       
    83 	compCmdNoDescFlagDefault = false
       
    84 )
       
    85 
       
    86 // CompletionOptions are the options to control shell completion
       
    87 type CompletionOptions struct {
       
    88 	// DisableDefaultCmd prevents Cobra from creating a default 'completion' command
       
    89 	DisableDefaultCmd bool
       
    90 	// DisableNoDescFlag prevents Cobra from creating the '--no-descriptions' flag
       
    91 	// for shells that support completion descriptions
       
    92 	DisableNoDescFlag bool
       
    93 	// DisableDescriptions turns off all completion descriptions for shells
       
    94 	// that support them
       
    95 	DisableDescriptions bool
       
    96 }
       
    97 
       
    98 // NoFileCompletions can be used to disable file completion for commands that should
       
    99 // not trigger file completions.
       
   100 func NoFileCompletions(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
       
   101 	return nil, ShellCompDirectiveNoFileComp
       
   102 }
       
   103 
       
   104 // RegisterFlagCompletionFunc should be called to register a function to provide completion for a flag.
       
   105 func (c *Command) RegisterFlagCompletionFunc(flagName string, f func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective)) error {
       
   106 	flag := c.Flag(flagName)
       
   107 	if flag == nil {
       
   108 		return fmt.Errorf("RegisterFlagCompletionFunc: flag '%s' does not exist", flagName)
       
   109 	}
       
   110 	flagCompletionMutex.Lock()
       
   111 	defer flagCompletionMutex.Unlock()
       
   112 
       
   113 	if _, exists := flagCompletionFunctions[flag]; exists {
       
   114 		return fmt.Errorf("RegisterFlagCompletionFunc: flag '%s' already registered", flagName)
       
   115 	}
       
   116 	flagCompletionFunctions[flag] = f
       
   117 	return nil
       
   118 }
       
   119 
       
   120 // Returns a string listing the different directive enabled in the specified parameter
       
   121 func (d ShellCompDirective) string() string {
       
   122 	var directives []string
       
   123 	if d&ShellCompDirectiveError != 0 {
       
   124 		directives = append(directives, "ShellCompDirectiveError")
       
   125 	}
       
   126 	if d&ShellCompDirectiveNoSpace != 0 {
       
   127 		directives = append(directives, "ShellCompDirectiveNoSpace")
       
   128 	}
       
   129 	if d&ShellCompDirectiveNoFileComp != 0 {
       
   130 		directives = append(directives, "ShellCompDirectiveNoFileComp")
       
   131 	}
       
   132 	if d&ShellCompDirectiveFilterFileExt != 0 {
       
   133 		directives = append(directives, "ShellCompDirectiveFilterFileExt")
       
   134 	}
       
   135 	if d&ShellCompDirectiveFilterDirs != 0 {
       
   136 		directives = append(directives, "ShellCompDirectiveFilterDirs")
       
   137 	}
       
   138 	if len(directives) == 0 {
       
   139 		directives = append(directives, "ShellCompDirectiveDefault")
       
   140 	}
       
   141 
       
   142 	if d >= shellCompDirectiveMaxValue {
       
   143 		return fmt.Sprintf("ERROR: unexpected ShellCompDirective value: %d", d)
       
   144 	}
       
   145 	return strings.Join(directives, ", ")
       
   146 }
       
   147 
       
   148 // Adds a special hidden command that can be used to request custom completions.
       
   149 func (c *Command) initCompleteCmd(args []string) {
       
   150 	completeCmd := &Command{
       
   151 		Use:                   fmt.Sprintf("%s [command-line]", ShellCompRequestCmd),
       
   152 		Aliases:               []string{ShellCompNoDescRequestCmd},
       
   153 		DisableFlagsInUseLine: true,
       
   154 		Hidden:                true,
       
   155 		DisableFlagParsing:    true,
       
   156 		Args:                  MinimumNArgs(1),
       
   157 		Short:                 "Request shell completion choices for the specified command-line",
       
   158 		Long: fmt.Sprintf("%[2]s is a special command that is used by the shell completion logic\n%[1]s",
       
   159 			"to request completion choices for the specified command-line.", ShellCompRequestCmd),
       
   160 		Run: func(cmd *Command, args []string) {
       
   161 			finalCmd, completions, directive, err := cmd.getCompletions(args)
       
   162 			if err != nil {
       
   163 				CompErrorln(err.Error())
       
   164 				// Keep going for multiple reasons:
       
   165 				// 1- There could be some valid completions even though there was an error
       
   166 				// 2- Even without completions, we need to print the directive
       
   167 			}
       
   168 
       
   169 			noDescriptions := (cmd.CalledAs() == ShellCompNoDescRequestCmd)
       
   170 			for _, comp := range completions {
       
   171 				if noDescriptions {
       
   172 					// Remove any description that may be included following a tab character.
       
   173 					comp = strings.Split(comp, "\t")[0]
       
   174 				}
       
   175 
       
   176 				// Make sure we only write the first line to the output.
       
   177 				// This is needed if a description contains a linebreak.
       
   178 				// Otherwise the shell scripts will interpret the other lines as new flags
       
   179 				// and could therefore provide a wrong completion.
       
   180 				comp = strings.Split(comp, "\n")[0]
       
   181 
       
   182 				// Finally trim the completion.  This is especially important to get rid
       
   183 				// of a trailing tab when there are no description following it.
       
   184 				// For example, a sub-command without a description should not be completed
       
   185 				// with a tab at the end (or else zsh will show a -- following it
       
   186 				// although there is no description).
       
   187 				comp = strings.TrimSpace(comp)
       
   188 
       
   189 				// Print each possible completion to stdout for the completion script to consume.
       
   190 				fmt.Fprintln(finalCmd.OutOrStdout(), comp)
       
   191 			}
       
   192 
       
   193 			// As the last printout, print the completion directive for the completion script to parse.
       
   194 			// The directive integer must be that last character following a single colon (:).
       
   195 			// The completion script expects :<directive>
       
   196 			fmt.Fprintf(finalCmd.OutOrStdout(), ":%d\n", directive)
       
   197 
       
   198 			// Print some helpful info to stderr for the user to understand.
       
   199 			// Output from stderr must be ignored by the completion script.
       
   200 			fmt.Fprintf(finalCmd.ErrOrStderr(), "Completion ended with directive: %s\n", directive.string())
       
   201 		},
       
   202 	}
       
   203 	c.AddCommand(completeCmd)
       
   204 	subCmd, _, err := c.Find(args)
       
   205 	if err != nil || subCmd.Name() != ShellCompRequestCmd {
       
   206 		// Only create this special command if it is actually being called.
       
   207 		// This reduces possible side-effects of creating such a command;
       
   208 		// for example, having this command would cause problems to a
       
   209 		// cobra program that only consists of the root command, since this
       
   210 		// command would cause the root command to suddenly have a subcommand.
       
   211 		c.RemoveCommand(completeCmd)
       
   212 	}
       
   213 }
       
   214 
       
   215 func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDirective, error) {
       
   216 	// The last argument, which is not completely typed by the user,
       
   217 	// should not be part of the list of arguments
       
   218 	toComplete := args[len(args)-1]
       
   219 	trimmedArgs := args[:len(args)-1]
       
   220 
       
   221 	var finalCmd *Command
       
   222 	var finalArgs []string
       
   223 	var err error
       
   224 	// Find the real command for which completion must be performed
       
   225 	// check if we need to traverse here to parse local flags on parent commands
       
   226 	if c.Root().TraverseChildren {
       
   227 		finalCmd, finalArgs, err = c.Root().Traverse(trimmedArgs)
       
   228 	} else {
       
   229 		finalCmd, finalArgs, err = c.Root().Find(trimmedArgs)
       
   230 	}
       
   231 	if err != nil {
       
   232 		// Unable to find the real command. E.g., <program> someInvalidCmd <TAB>
       
   233 		return c, []string{}, ShellCompDirectiveDefault, fmt.Errorf("Unable to find a command for arguments: %v", trimmedArgs)
       
   234 	}
       
   235 	finalCmd.ctx = c.ctx
       
   236 
       
   237 	// Check if we are doing flag value completion before parsing the flags.
       
   238 	// This is important because if we are completing a flag value, we need to also
       
   239 	// remove the flag name argument from the list of finalArgs or else the parsing
       
   240 	// could fail due to an invalid value (incomplete) for the flag.
       
   241 	flag, finalArgs, toComplete, flagErr := checkIfFlagCompletion(finalCmd, finalArgs, toComplete)
       
   242 
       
   243 	// Check if interspersed is false or -- was set on a previous arg.
       
   244 	// This works by counting the arguments. Normally -- is not counted as arg but
       
   245 	// if -- was already set or interspersed is false and there is already one arg then
       
   246 	// the extra added -- is counted as arg.
       
   247 	flagCompletion := true
       
   248 	_ = finalCmd.ParseFlags(append(finalArgs, "--"))
       
   249 	newArgCount := finalCmd.Flags().NArg()
       
   250 
       
   251 	// Parse the flags early so we can check if required flags are set
       
   252 	if err = finalCmd.ParseFlags(finalArgs); err != nil {
       
   253 		return finalCmd, []string{}, ShellCompDirectiveDefault, fmt.Errorf("Error while parsing flags from args %v: %s", finalArgs, err.Error())
       
   254 	}
       
   255 
       
   256 	realArgCount := finalCmd.Flags().NArg()
       
   257 	if newArgCount > realArgCount {
       
   258 		// don't do flag completion (see above)
       
   259 		flagCompletion = false
       
   260 	}
       
   261 	// Error while attempting to parse flags
       
   262 	if flagErr != nil {
       
   263 		// If error type is flagCompError and we don't want flagCompletion we should ignore the error
       
   264 		if _, ok := flagErr.(*flagCompError); !(ok && !flagCompletion) {
       
   265 			return finalCmd, []string{}, ShellCompDirectiveDefault, flagErr
       
   266 		}
       
   267 	}
       
   268 
       
   269 	if flag != nil && flagCompletion {
       
   270 		// Check if we are completing a flag value subject to annotations
       
   271 		if validExts, present := flag.Annotations[BashCompFilenameExt]; present {
       
   272 			if len(validExts) != 0 {
       
   273 				// File completion filtered by extensions
       
   274 				return finalCmd, validExts, ShellCompDirectiveFilterFileExt, nil
       
   275 			}
       
   276 
       
   277 			// The annotation requests simple file completion.  There is no reason to do
       
   278 			// that since it is the default behavior anyway.  Let's ignore this annotation
       
   279 			// in case the program also registered a completion function for this flag.
       
   280 			// Even though it is a mistake on the program's side, let's be nice when we can.
       
   281 		}
       
   282 
       
   283 		if subDir, present := flag.Annotations[BashCompSubdirsInDir]; present {
       
   284 			if len(subDir) == 1 {
       
   285 				// Directory completion from within a directory
       
   286 				return finalCmd, subDir, ShellCompDirectiveFilterDirs, nil
       
   287 			}
       
   288 			// Directory completion
       
   289 			return finalCmd, []string{}, ShellCompDirectiveFilterDirs, nil
       
   290 		}
       
   291 	}
       
   292 
       
   293 	// When doing completion of a flag name, as soon as an argument starts with
       
   294 	// a '-' we know it is a flag.  We cannot use isFlagArg() here as it requires
       
   295 	// the flag name to be complete
       
   296 	if flag == nil && len(toComplete) > 0 && toComplete[0] == '-' && !strings.Contains(toComplete, "=") && flagCompletion {
       
   297 		var completions []string
       
   298 
       
   299 		// First check for required flags
       
   300 		completions = completeRequireFlags(finalCmd, toComplete)
       
   301 
       
   302 		// If we have not found any required flags, only then can we show regular flags
       
   303 		if len(completions) == 0 {
       
   304 			doCompleteFlags := func(flag *pflag.Flag) {
       
   305 				if !flag.Changed ||
       
   306 					strings.Contains(flag.Value.Type(), "Slice") ||
       
   307 					strings.Contains(flag.Value.Type(), "Array") {
       
   308 					// If the flag is not already present, or if it can be specified multiple times (Array or Slice)
       
   309 					// we suggest it as a completion
       
   310 					completions = append(completions, getFlagNameCompletions(flag, toComplete)...)
       
   311 				}
       
   312 			}
       
   313 
       
   314 			// We cannot use finalCmd.Flags() because we may not have called ParsedFlags() for commands
       
   315 			// that have set DisableFlagParsing; it is ParseFlags() that merges the inherited and
       
   316 			// non-inherited flags.
       
   317 			finalCmd.InheritedFlags().VisitAll(func(flag *pflag.Flag) {
       
   318 				doCompleteFlags(flag)
       
   319 			})
       
   320 			finalCmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) {
       
   321 				doCompleteFlags(flag)
       
   322 			})
       
   323 		}
       
   324 
       
   325 		directive := ShellCompDirectiveNoFileComp
       
   326 		if len(completions) == 1 && strings.HasSuffix(completions[0], "=") {
       
   327 			// If there is a single completion, the shell usually adds a space
       
   328 			// after the completion.  We don't want that if the flag ends with an =
       
   329 			directive = ShellCompDirectiveNoSpace
       
   330 		}
       
   331 		return finalCmd, completions, directive, nil
       
   332 	}
       
   333 
       
   334 	// We only remove the flags from the arguments if DisableFlagParsing is not set.
       
   335 	// This is important for commands which have requested to do their own flag completion.
       
   336 	if !finalCmd.DisableFlagParsing {
       
   337 		finalArgs = finalCmd.Flags().Args()
       
   338 	}
       
   339 
       
   340 	var completions []string
       
   341 	directive := ShellCompDirectiveDefault
       
   342 	if flag == nil {
       
   343 		foundLocalNonPersistentFlag := false
       
   344 		// If TraverseChildren is true on the root command we don't check for
       
   345 		// local flags because we can use a local flag on a parent command
       
   346 		if !finalCmd.Root().TraverseChildren {
       
   347 			// Check if there are any local, non-persistent flags on the command-line
       
   348 			localNonPersistentFlags := finalCmd.LocalNonPersistentFlags()
       
   349 			finalCmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) {
       
   350 				if localNonPersistentFlags.Lookup(flag.Name) != nil && flag.Changed {
       
   351 					foundLocalNonPersistentFlag = true
       
   352 				}
       
   353 			})
       
   354 		}
       
   355 
       
   356 		// Complete subcommand names, including the help command
       
   357 		if len(finalArgs) == 0 && !foundLocalNonPersistentFlag {
       
   358 			// We only complete sub-commands if:
       
   359 			// - there are no arguments on the command-line and
       
   360 			// - there are no local, non-persistent flags on the command-line or TraverseChildren is true
       
   361 			for _, subCmd := range finalCmd.Commands() {
       
   362 				if subCmd.IsAvailableCommand() || subCmd == finalCmd.helpCommand {
       
   363 					if strings.HasPrefix(subCmd.Name(), toComplete) {
       
   364 						completions = append(completions, fmt.Sprintf("%s\t%s", subCmd.Name(), subCmd.Short))
       
   365 					}
       
   366 					directive = ShellCompDirectiveNoFileComp
       
   367 				}
       
   368 			}
       
   369 		}
       
   370 
       
   371 		// Complete required flags even without the '-' prefix
       
   372 		completions = append(completions, completeRequireFlags(finalCmd, toComplete)...)
       
   373 
       
   374 		// Always complete ValidArgs, even if we are completing a subcommand name.
       
   375 		// This is for commands that have both subcommands and ValidArgs.
       
   376 		if len(finalCmd.ValidArgs) > 0 {
       
   377 			if len(finalArgs) == 0 {
       
   378 				// ValidArgs are only for the first argument
       
   379 				for _, validArg := range finalCmd.ValidArgs {
       
   380 					if strings.HasPrefix(validArg, toComplete) {
       
   381 						completions = append(completions, validArg)
       
   382 					}
       
   383 				}
       
   384 				directive = ShellCompDirectiveNoFileComp
       
   385 
       
   386 				// If no completions were found within commands or ValidArgs,
       
   387 				// see if there are any ArgAliases that should be completed.
       
   388 				if len(completions) == 0 {
       
   389 					for _, argAlias := range finalCmd.ArgAliases {
       
   390 						if strings.HasPrefix(argAlias, toComplete) {
       
   391 							completions = append(completions, argAlias)
       
   392 						}
       
   393 					}
       
   394 				}
       
   395 			}
       
   396 
       
   397 			// If there are ValidArgs specified (even if they don't match), we stop completion.
       
   398 			// Only one of ValidArgs or ValidArgsFunction can be used for a single command.
       
   399 			return finalCmd, completions, directive, nil
       
   400 		}
       
   401 
       
   402 		// Let the logic continue so as to add any ValidArgsFunction completions,
       
   403 		// even if we already found sub-commands.
       
   404 		// This is for commands that have subcommands but also specify a ValidArgsFunction.
       
   405 	}
       
   406 
       
   407 	// Find the completion function for the flag or command
       
   408 	var completionFn func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective)
       
   409 	if flag != nil && flagCompletion {
       
   410 		flagCompletionMutex.RLock()
       
   411 		completionFn = flagCompletionFunctions[flag]
       
   412 		flagCompletionMutex.RUnlock()
       
   413 	} else {
       
   414 		completionFn = finalCmd.ValidArgsFunction
       
   415 	}
       
   416 	if completionFn != nil {
       
   417 		// Go custom completion defined for this flag or command.
       
   418 		// Call the registered completion function to get the completions.
       
   419 		var comps []string
       
   420 		comps, directive = completionFn(finalCmd, finalArgs, toComplete)
       
   421 		completions = append(completions, comps...)
       
   422 	}
       
   423 
       
   424 	return finalCmd, completions, directive, nil
       
   425 }
       
   426 
       
   427 func getFlagNameCompletions(flag *pflag.Flag, toComplete string) []string {
       
   428 	if nonCompletableFlag(flag) {
       
   429 		return []string{}
       
   430 	}
       
   431 
       
   432 	var completions []string
       
   433 	flagName := "--" + flag.Name
       
   434 	if strings.HasPrefix(flagName, toComplete) {
       
   435 		// Flag without the =
       
   436 		completions = append(completions, fmt.Sprintf("%s\t%s", flagName, flag.Usage))
       
   437 
       
   438 		// Why suggest both long forms: --flag and --flag= ?
       
   439 		// This forces the user to *always* have to type either an = or a space after the flag name.
       
   440 		// Let's be nice and avoid making users have to do that.
       
   441 		// Since boolean flags and shortname flags don't show the = form, let's go that route and never show it.
       
   442 		// The = form will still work, we just won't suggest it.
       
   443 		// This also makes the list of suggested flags shorter as we avoid all the = forms.
       
   444 		//
       
   445 		// if len(flag.NoOptDefVal) == 0 {
       
   446 		// 	// Flag requires a value, so it can be suffixed with =
       
   447 		// 	flagName += "="
       
   448 		// 	completions = append(completions, fmt.Sprintf("%s\t%s", flagName, flag.Usage))
       
   449 		// }
       
   450 	}
       
   451 
       
   452 	flagName = "-" + flag.Shorthand
       
   453 	if len(flag.Shorthand) > 0 && strings.HasPrefix(flagName, toComplete) {
       
   454 		completions = append(completions, fmt.Sprintf("%s\t%s", flagName, flag.Usage))
       
   455 	}
       
   456 
       
   457 	return completions
       
   458 }
       
   459 
       
   460 func completeRequireFlags(finalCmd *Command, toComplete string) []string {
       
   461 	var completions []string
       
   462 
       
   463 	doCompleteRequiredFlags := func(flag *pflag.Flag) {
       
   464 		if _, present := flag.Annotations[BashCompOneRequiredFlag]; present {
       
   465 			if !flag.Changed {
       
   466 				// If the flag is not already present, we suggest it as a completion
       
   467 				completions = append(completions, getFlagNameCompletions(flag, toComplete)...)
       
   468 			}
       
   469 		}
       
   470 	}
       
   471 
       
   472 	// We cannot use finalCmd.Flags() because we may not have called ParsedFlags() for commands
       
   473 	// that have set DisableFlagParsing; it is ParseFlags() that merges the inherited and
       
   474 	// non-inherited flags.
       
   475 	finalCmd.InheritedFlags().VisitAll(func(flag *pflag.Flag) {
       
   476 		doCompleteRequiredFlags(flag)
       
   477 	})
       
   478 	finalCmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) {
       
   479 		doCompleteRequiredFlags(flag)
       
   480 	})
       
   481 
       
   482 	return completions
       
   483 }
       
   484 
       
   485 func checkIfFlagCompletion(finalCmd *Command, args []string, lastArg string) (*pflag.Flag, []string, string, error) {
       
   486 	if finalCmd.DisableFlagParsing {
       
   487 		// We only do flag completion if we are allowed to parse flags
       
   488 		// This is important for commands which have requested to do their own flag completion.
       
   489 		return nil, args, lastArg, nil
       
   490 	}
       
   491 
       
   492 	var flagName string
       
   493 	trimmedArgs := args
       
   494 	flagWithEqual := false
       
   495 	orgLastArg := lastArg
       
   496 
       
   497 	// When doing completion of a flag name, as soon as an argument starts with
       
   498 	// a '-' we know it is a flag.  We cannot use isFlagArg() here as that function
       
   499 	// requires the flag name to be complete
       
   500 	if len(lastArg) > 0 && lastArg[0] == '-' {
       
   501 		if index := strings.Index(lastArg, "="); index >= 0 {
       
   502 			// Flag with an =
       
   503 			if strings.HasPrefix(lastArg[:index], "--") {
       
   504 				// Flag has full name
       
   505 				flagName = lastArg[2:index]
       
   506 			} else {
       
   507 				// Flag is shorthand
       
   508 				// We have to get the last shorthand flag name
       
   509 				// e.g. `-asd` => d to provide the correct completion
       
   510 				// https://github.com/spf13/cobra/issues/1257
       
   511 				flagName = lastArg[index-1 : index]
       
   512 			}
       
   513 			lastArg = lastArg[index+1:]
       
   514 			flagWithEqual = true
       
   515 		} else {
       
   516 			// Normal flag completion
       
   517 			return nil, args, lastArg, nil
       
   518 		}
       
   519 	}
       
   520 
       
   521 	if len(flagName) == 0 {
       
   522 		if len(args) > 0 {
       
   523 			prevArg := args[len(args)-1]
       
   524 			if isFlagArg(prevArg) {
       
   525 				// Only consider the case where the flag does not contain an =.
       
   526 				// If the flag contains an = it means it has already been fully processed,
       
   527 				// so we don't need to deal with it here.
       
   528 				if index := strings.Index(prevArg, "="); index < 0 {
       
   529 					if strings.HasPrefix(prevArg, "--") {
       
   530 						// Flag has full name
       
   531 						flagName = prevArg[2:]
       
   532 					} else {
       
   533 						// Flag is shorthand
       
   534 						// We have to get the last shorthand flag name
       
   535 						// e.g. `-asd` => d to provide the correct completion
       
   536 						// https://github.com/spf13/cobra/issues/1257
       
   537 						flagName = prevArg[len(prevArg)-1:]
       
   538 					}
       
   539 					// Remove the uncompleted flag or else there could be an error created
       
   540 					// for an invalid value for that flag
       
   541 					trimmedArgs = args[:len(args)-1]
       
   542 				}
       
   543 			}
       
   544 		}
       
   545 	}
       
   546 
       
   547 	if len(flagName) == 0 {
       
   548 		// Not doing flag completion
       
   549 		return nil, trimmedArgs, lastArg, nil
       
   550 	}
       
   551 
       
   552 	flag := findFlag(finalCmd, flagName)
       
   553 	if flag == nil {
       
   554 		// Flag not supported by this command, the interspersed option might be set so return the original args
       
   555 		return nil, args, orgLastArg, &flagCompError{subCommand: finalCmd.Name(), flagName: flagName}
       
   556 	}
       
   557 
       
   558 	if !flagWithEqual {
       
   559 		if len(flag.NoOptDefVal) != 0 {
       
   560 			// We had assumed dealing with a two-word flag but the flag is a boolean flag.
       
   561 			// In that case, there is no value following it, so we are not really doing flag completion.
       
   562 			// Reset everything to do noun completion.
       
   563 			trimmedArgs = args
       
   564 			flag = nil
       
   565 		}
       
   566 	}
       
   567 
       
   568 	return flag, trimmedArgs, lastArg, nil
       
   569 }
       
   570 
       
   571 // initDefaultCompletionCmd adds a default 'completion' command to c.
       
   572 // This function will do nothing if any of the following is true:
       
   573 // 1- the feature has been explicitly disabled by the program,
       
   574 // 2- c has no subcommands (to avoid creating one),
       
   575 // 3- c already has a 'completion' command provided by the program.
       
   576 func (c *Command) initDefaultCompletionCmd() {
       
   577 	if c.CompletionOptions.DisableDefaultCmd || !c.HasSubCommands() {
       
   578 		return
       
   579 	}
       
   580 
       
   581 	for _, cmd := range c.commands {
       
   582 		if cmd.Name() == compCmdName || cmd.HasAlias(compCmdName) {
       
   583 			// A completion command is already available
       
   584 			return
       
   585 		}
       
   586 	}
       
   587 
       
   588 	haveNoDescFlag := !c.CompletionOptions.DisableNoDescFlag && !c.CompletionOptions.DisableDescriptions
       
   589 
       
   590 	completionCmd := &Command{
       
   591 		Use:   compCmdName,
       
   592 		Short: "generate the autocompletion script for the specified shell",
       
   593 		Long: fmt.Sprintf(`
       
   594 Generate the autocompletion script for %[1]s for the specified shell.
       
   595 See each sub-command's help for details on how to use the generated script.
       
   596 `, c.Root().Name()),
       
   597 		Args:              NoArgs,
       
   598 		ValidArgsFunction: NoFileCompletions,
       
   599 	}
       
   600 	c.AddCommand(completionCmd)
       
   601 
       
   602 	out := c.OutOrStdout()
       
   603 	noDesc := c.CompletionOptions.DisableDescriptions
       
   604 	shortDesc := "generate the autocompletion script for %s"
       
   605 	bash := &Command{
       
   606 		Use:   "bash",
       
   607 		Short: fmt.Sprintf(shortDesc, "bash"),
       
   608 		Long: fmt.Sprintf(`
       
   609 Generate the autocompletion script for the bash shell.
       
   610 
       
   611 This script depends on the 'bash-completion' package.
       
   612 If it is not installed already, you can install it via your OS's package manager.
       
   613 
       
   614 To load completions in your current shell session:
       
   615 $ source <(%[1]s completion bash)
       
   616 
       
   617 To load completions for every new session, execute once:
       
   618 Linux:
       
   619   $ %[1]s completion bash > /etc/bash_completion.d/%[1]s
       
   620 MacOS:
       
   621   $ %[1]s completion bash > /usr/local/etc/bash_completion.d/%[1]s
       
   622 
       
   623 You will need to start a new shell for this setup to take effect.
       
   624   `, c.Root().Name()),
       
   625 		Args:                  NoArgs,
       
   626 		DisableFlagsInUseLine: true,
       
   627 		ValidArgsFunction:     NoFileCompletions,
       
   628 		RunE: func(cmd *Command, args []string) error {
       
   629 			return cmd.Root().GenBashCompletionV2(out, !noDesc)
       
   630 		},
       
   631 	}
       
   632 	if haveNoDescFlag {
       
   633 		bash.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc)
       
   634 	}
       
   635 
       
   636 	zsh := &Command{
       
   637 		Use:   "zsh",
       
   638 		Short: fmt.Sprintf(shortDesc, "zsh"),
       
   639 		Long: fmt.Sprintf(`
       
   640 Generate the autocompletion script for the zsh shell.
       
   641 
       
   642 If shell completion is not already enabled in your environment you will need
       
   643 to enable it.  You can execute the following once:
       
   644 
       
   645 $ echo "autoload -U compinit; compinit" >> ~/.zshrc
       
   646 
       
   647 To load completions for every new session, execute once:
       
   648 # Linux:
       
   649 $ %[1]s completion zsh > "${fpath[1]}/_%[1]s"
       
   650 # macOS:
       
   651 $ %[1]s completion zsh > /usr/local/share/zsh/site-functions/_%[1]s
       
   652 
       
   653 You will need to start a new shell for this setup to take effect.
       
   654 `, c.Root().Name()),
       
   655 		Args:              NoArgs,
       
   656 		ValidArgsFunction: NoFileCompletions,
       
   657 		RunE: func(cmd *Command, args []string) error {
       
   658 			if noDesc {
       
   659 				return cmd.Root().GenZshCompletionNoDesc(out)
       
   660 			}
       
   661 			return cmd.Root().GenZshCompletion(out)
       
   662 		},
       
   663 	}
       
   664 	if haveNoDescFlag {
       
   665 		zsh.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc)
       
   666 	}
       
   667 
       
   668 	fish := &Command{
       
   669 		Use:   "fish",
       
   670 		Short: fmt.Sprintf(shortDesc, "fish"),
       
   671 		Long: fmt.Sprintf(`
       
   672 Generate the autocompletion script for the fish shell.
       
   673 
       
   674 To load completions in your current shell session:
       
   675 $ %[1]s completion fish | source
       
   676 
       
   677 To load completions for every new session, execute once:
       
   678 $ %[1]s completion fish > ~/.config/fish/completions/%[1]s.fish
       
   679 
       
   680 You will need to start a new shell for this setup to take effect.
       
   681 `, c.Root().Name()),
       
   682 		Args:              NoArgs,
       
   683 		ValidArgsFunction: NoFileCompletions,
       
   684 		RunE: func(cmd *Command, args []string) error {
       
   685 			return cmd.Root().GenFishCompletion(out, !noDesc)
       
   686 		},
       
   687 	}
       
   688 	if haveNoDescFlag {
       
   689 		fish.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc)
       
   690 	}
       
   691 
       
   692 	powershell := &Command{
       
   693 		Use:   "powershell",
       
   694 		Short: fmt.Sprintf(shortDesc, "powershell"),
       
   695 		Long: fmt.Sprintf(`
       
   696 Generate the autocompletion script for powershell.
       
   697 
       
   698 To load completions in your current shell session:
       
   699 PS C:\> %[1]s completion powershell | Out-String | Invoke-Expression
       
   700 
       
   701 To load completions for every new session, add the output of the above command
       
   702 to your powershell profile.
       
   703 `, c.Root().Name()),
       
   704 		Args:              NoArgs,
       
   705 		ValidArgsFunction: NoFileCompletions,
       
   706 		RunE: func(cmd *Command, args []string) error {
       
   707 			if noDesc {
       
   708 				return cmd.Root().GenPowerShellCompletion(out)
       
   709 			}
       
   710 			return cmd.Root().GenPowerShellCompletionWithDesc(out)
       
   711 
       
   712 		},
       
   713 	}
       
   714 	if haveNoDescFlag {
       
   715 		powershell.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc)
       
   716 	}
       
   717 
       
   718 	completionCmd.AddCommand(bash, zsh, fish, powershell)
       
   719 }
       
   720 
       
   721 func findFlag(cmd *Command, name string) *pflag.Flag {
       
   722 	flagSet := cmd.Flags()
       
   723 	if len(name) == 1 {
       
   724 		// First convert the short flag into a long flag
       
   725 		// as the cmd.Flag() search only accepts long flags
       
   726 		if short := flagSet.ShorthandLookup(name); short != nil {
       
   727 			name = short.Name
       
   728 		} else {
       
   729 			set := cmd.InheritedFlags()
       
   730 			if short = set.ShorthandLookup(name); short != nil {
       
   731 				name = short.Name
       
   732 			} else {
       
   733 				return nil
       
   734 			}
       
   735 		}
       
   736 	}
       
   737 	return cmd.Flag(name)
       
   738 }
       
   739 
       
   740 // CompDebug prints the specified string to the same file as where the
       
   741 // completion script prints its logs.
       
   742 // Note that completion printouts should never be on stdout as they would
       
   743 // be wrongly interpreted as actual completion choices by the completion script.
       
   744 func CompDebug(msg string, printToStdErr bool) {
       
   745 	msg = fmt.Sprintf("[Debug] %s", msg)
       
   746 
       
   747 	// Such logs are only printed when the user has set the environment
       
   748 	// variable BASH_COMP_DEBUG_FILE to the path of some file to be used.
       
   749 	if path := os.Getenv("BASH_COMP_DEBUG_FILE"); path != "" {
       
   750 		f, err := os.OpenFile(path,
       
   751 			os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
       
   752 		if err == nil {
       
   753 			defer f.Close()
       
   754 			WriteStringAndCheck(f, msg)
       
   755 		}
       
   756 	}
       
   757 
       
   758 	if printToStdErr {
       
   759 		// Must print to stderr for this not to be read by the completion script.
       
   760 		fmt.Fprint(os.Stderr, msg)
       
   761 	}
       
   762 }
       
   763 
       
   764 // CompDebugln prints the specified string with a newline at the end
       
   765 // to the same file as where the completion script prints its logs.
       
   766 // Such logs are only printed when the user has set the environment
       
   767 // variable BASH_COMP_DEBUG_FILE to the path of some file to be used.
       
   768 func CompDebugln(msg string, printToStdErr bool) {
       
   769 	CompDebug(fmt.Sprintf("%s\n", msg), printToStdErr)
       
   770 }
       
   771 
       
   772 // CompError prints the specified completion message to stderr.
       
   773 func CompError(msg string) {
       
   774 	msg = fmt.Sprintf("[Error] %s", msg)
       
   775 	CompDebug(msg, true)
       
   776 }
       
   777 
       
   778 // CompErrorln prints the specified completion message to stderr with a newline at the end.
       
   779 func CompErrorln(msg string) {
       
   780 	CompError(fmt.Sprintf("%s\n", msg))
       
   781 }