gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

completions.go (33163B)


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