mirror of
https://github.com/spf13/cobra
synced 2025-04-27 17:17:20 +00:00
Merge 5aadb0d2bd
into 01ffff4eca
This commit is contained in:
commit
6c7506b303
4 changed files with 389 additions and 181 deletions
|
@ -534,9 +534,10 @@ func writeLocalNonPersistentFlag(buf io.StringWriter, flag *pflag.Flag) {
|
||||||
|
|
||||||
// prepareCustomAnnotationsForFlags setup annotations for go completions for registered flags
|
// prepareCustomAnnotationsForFlags setup annotations for go completions for registered flags
|
||||||
func prepareCustomAnnotationsForFlags(cmd *Command) {
|
func prepareCustomAnnotationsForFlags(cmd *Command) {
|
||||||
flagCompletionMutex.RLock()
|
cmd.initializeCompletionStorage()
|
||||||
defer flagCompletionMutex.RUnlock()
|
cmd.flagCompletionMutex.RLock()
|
||||||
for flag := range flagCompletionFunctions {
|
defer cmd.flagCompletionMutex.RUnlock()
|
||||||
|
for flag := range cmd.flagCompletionFunctions {
|
||||||
// Make sure the completion script calls the __*_go_custom_completion function for
|
// Make sure the completion script calls the __*_go_custom_completion function for
|
||||||
// every registered flag. We need to do this here (and not when the flag was registered
|
// every registered flag. We need to do this here (and not when the flag was registered
|
||||||
// for completion) so that we can know the root command name for the prefix
|
// for completion) so that we can know the root command name for the prefix
|
||||||
|
@ -641,6 +642,7 @@ func writeCmdAliases(buf io.StringWriter, cmd *Command) {
|
||||||
WriteStringAndCheck(buf, ` fi`)
|
WriteStringAndCheck(buf, ` fi`)
|
||||||
WriteStringAndCheck(buf, "\n")
|
WriteStringAndCheck(buf, "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeArgAliases(buf io.StringWriter, cmd *Command) {
|
func writeArgAliases(buf io.StringWriter, cmd *Command) {
|
||||||
WriteStringAndCheck(buf, " noun_aliases=()\n")
|
WriteStringAndCheck(buf, " noun_aliases=()\n")
|
||||||
sort.Strings(cmd.ArgAliases)
|
sort.Strings(cmd.ArgAliases)
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
flag "github.com/spf13/pflag"
|
flag "github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
@ -164,6 +165,13 @@ type Command struct {
|
||||||
// that we can use on every pflag set and children commands
|
// that we can use on every pflag set and children commands
|
||||||
globNormFunc func(f *flag.FlagSet, name string) flag.NormalizedName
|
globNormFunc func(f *flag.FlagSet, name string) flag.NormalizedName
|
||||||
|
|
||||||
|
// flagsCompletions contrains completions for arbitrary lists of flags.
|
||||||
|
// Those flags may or may not actually strictly belong to the command in the function,
|
||||||
|
// but registering completions for them through the command allows for garbage-collecting.
|
||||||
|
flagCompletionFunctions map[*flag.Flag]func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective)
|
||||||
|
// lock for reading and writing from flagCompletionFunctions
|
||||||
|
flagCompletionMutex *sync.RWMutex
|
||||||
|
|
||||||
// usageFunc is usage func defined by user.
|
// usageFunc is usage func defined by user.
|
||||||
usageFunc func(*Command) error
|
usageFunc func(*Command) error
|
||||||
// usageTemplate is usage template defined by user.
|
// usageTemplate is usage template defined by user.
|
||||||
|
|
|
@ -34,12 +34,6 @@ const (
|
||||||
ShellCompNoDescRequestCmd = "__completeNoDesc"
|
ShellCompNoDescRequestCmd = "__completeNoDesc"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Global map of flag completion functions. Make sure to use flagCompletionMutex before you try to read and write from it.
|
|
||||||
var flagCompletionFunctions = map[*pflag.Flag]func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective){}
|
|
||||||
|
|
||||||
// lock for reading and writing from flagCompletionFunctions
|
|
||||||
var flagCompletionMutex = &sync.RWMutex{}
|
|
||||||
|
|
||||||
// ShellCompDirective is a bit map representing the different behaviors the shell
|
// ShellCompDirective is a bit map representing the different behaviors the shell
|
||||||
// can be instructed to have once completions have been provided.
|
// can be instructed to have once completions have been provided.
|
||||||
type ShellCompDirective int
|
type ShellCompDirective int
|
||||||
|
@ -137,28 +131,77 @@ func (c *Command) RegisterFlagCompletionFunc(flagName string, f func(cmd *Comman
|
||||||
if flag == nil {
|
if flag == nil {
|
||||||
return fmt.Errorf("RegisterFlagCompletionFunc: flag '%s' does not exist", flagName)
|
return fmt.Errorf("RegisterFlagCompletionFunc: flag '%s' does not exist", flagName)
|
||||||
}
|
}
|
||||||
flagCompletionMutex.Lock()
|
// Ensure none of our relevant fields are nil.
|
||||||
defer flagCompletionMutex.Unlock()
|
c.initializeCompletionStorage()
|
||||||
|
|
||||||
if _, exists := flagCompletionFunctions[flag]; exists {
|
c.flagCompletionMutex.Lock()
|
||||||
|
defer c.flagCompletionMutex.Unlock()
|
||||||
|
|
||||||
|
// And attempt to bind the completion.
|
||||||
|
if _, exists := c.flagCompletionFunctions[flag]; exists {
|
||||||
return fmt.Errorf("RegisterFlagCompletionFunc: flag '%s' already registered", flagName)
|
return fmt.Errorf("RegisterFlagCompletionFunc: flag '%s' already registered", flagName)
|
||||||
}
|
}
|
||||||
flagCompletionFunctions[flag] = f
|
c.flagCompletionFunctions[flag] = f
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFlagCompletionFunc returns the completion function for the given flag of the command, if available.
|
// GetFlagCompletion returns the completion function for the given flag, if available.
|
||||||
func (c *Command) GetFlagCompletionFunc(flagName string) (func(*Command, []string, string) ([]string, ShellCompDirective), bool) {
|
func (c *Command) GetFlagCompletionFunc(flag *pflag.Flag) (func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective), bool) {
|
||||||
flag := c.Flag(flagName)
|
c.initializeCompletionStorage()
|
||||||
if flag == nil {
|
|
||||||
|
c.flagCompletionMutex.RLock()
|
||||||
|
defer c.flagCompletionMutex.RUnlock()
|
||||||
|
|
||||||
|
completionFunc, exists := c.flagCompletionFunctions[flag]
|
||||||
|
|
||||||
|
// If found it here, return now
|
||||||
|
if completionFunc != nil && exists {
|
||||||
|
return completionFunc, exists
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are already at the root command level, return anyway
|
||||||
|
if !c.HasParent() {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
flagCompletionMutex.RLock()
|
// Or walk up the command tree.
|
||||||
defer flagCompletionMutex.RUnlock()
|
return c.Parent().GetFlagCompletionFunc(flag)
|
||||||
|
}
|
||||||
|
|
||||||
completionFunc, exists := flagCompletionFunctions[flag]
|
// GetFlagCompletionByName returns the completion function for the given flag in the command by name, if available.
|
||||||
return completionFunc, exists
|
// If the flag is not found in the command's local flags, it looks into the persistent flags, which might belong to one of the command's parents.
|
||||||
|
func (c *Command) GetFlagCompletionFuncByName(flagName string) (func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective), bool) {
|
||||||
|
// Attempt to find it in the local flags.
|
||||||
|
if flag := c.Flags().Lookup(flagName); flag != nil {
|
||||||
|
return c.GetFlagCompletionFunc(flag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Or try to find it in the "command-specific" persistent flags.
|
||||||
|
if flag := c.PersistentFlags().Lookup(flagName); flag != nil {
|
||||||
|
return c.GetFlagCompletionFunc(flag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Else, check all persistent flags belonging to one of the parents.
|
||||||
|
// This ensures that we won't return the completion function of a
|
||||||
|
// parent's LOCAL flag.
|
||||||
|
if flag := c.InheritedFlags().Lookup(flagName); flag != nil {
|
||||||
|
return c.GetFlagCompletionFunc(flag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// No flag exists either locally, or as one of the parent persistent flags.
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// initializeCompletionStorage is (and should be) called in all
|
||||||
|
// functions that make use of the command's flag completion functions.
|
||||||
|
func (c *Command) initializeCompletionStorage() {
|
||||||
|
if c.flagCompletionMutex == nil {
|
||||||
|
c.flagCompletionMutex = new(sync.RWMutex)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.flagCompletionFunctions == nil {
|
||||||
|
c.flagCompletionFunctions = make(map[*pflag.Flag]func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective), 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a string listing the different directive enabled in the specified parameter
|
// Returns a string listing the different directive enabled in the specified parameter
|
||||||
|
@ -521,9 +564,7 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi
|
||||||
// Find the completion function for the flag or command
|
// Find the completion function for the flag or command
|
||||||
var completionFn func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective)
|
var completionFn func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective)
|
||||||
if flag != nil && flagCompletion {
|
if flag != nil && flagCompletion {
|
||||||
flagCompletionMutex.RLock()
|
completionFn, _ = finalCmd.GetFlagCompletionFunc(flag)
|
||||||
completionFn = flagCompletionFunctions[flag]
|
|
||||||
flagCompletionMutex.RUnlock()
|
|
||||||
} else {
|
} else {
|
||||||
completionFn = finalCmd.ValidArgsFunction
|
completionFn = finalCmd.ValidArgsFunction
|
||||||
}
|
}
|
||||||
|
@ -847,7 +888,6 @@ to your powershell profile.
|
||||||
return cmd.Root().GenPowerShellCompletion(out)
|
return cmd.Root().GenPowerShellCompletion(out)
|
||||||
}
|
}
|
||||||
return cmd.Root().GenPowerShellCompletionWithDesc(out)
|
return cmd.Root().GenPowerShellCompletionWithDesc(out)
|
||||||
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if haveNoDescFlag {
|
if haveNoDescFlag {
|
||||||
|
@ -887,7 +927,7 @@ func CompDebug(msg string, printToStdErr bool) {
|
||||||
// variable BASH_COMP_DEBUG_FILE to the path of some file to be used.
|
// variable BASH_COMP_DEBUG_FILE to the path of some file to be used.
|
||||||
if path := os.Getenv("BASH_COMP_DEBUG_FILE"); path != "" {
|
if path := os.Getenv("BASH_COMP_DEBUG_FILE"); path != "" {
|
||||||
f, err := os.OpenFile(path,
|
f, err := os.OpenFile(path,
|
||||||
os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
WriteStringAndCheck(f, msg)
|
WriteStringAndCheck(f, msg)
|
||||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue