Add group flags

This commit is contained in:
knqyf263 2022-08-18 12:15:39 +03:00
parent dbf85f6104
commit ec6fe6b61c
2 changed files with 148 additions and 1 deletions

View file

@ -139,6 +139,8 @@ type Command struct {
iflags *flag.FlagSet iflags *flag.FlagSet
// parentsPflags is all persistent flags of cmd's parents. // parentsPflags is all persistent flags of cmd's parents.
parentsPflags *flag.FlagSet parentsPflags *flag.FlagSet
// lnamedFlagSets contains local named flags.
lnamedFlagSets *NamedFlagSets
// globNormFunc is the global normalization function // globNormFunc is the global normalization function
// 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
@ -514,7 +516,8 @@ Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "he
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
Flags: Flags:
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} {{.LocalNonNamedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableNamedFlags}}
{{.NamedFlagSets.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
Global Flags: Global Flags:
{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}} {{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}
@ -578,6 +581,7 @@ func stripFlags(args []string, c *Command) []string {
return args return args
} }
c.mergePersistentFlags() c.mergePersistentFlags()
c.mergeNamedFlags()
commands := []string{} commands := []string{}
flags := c.Flags() flags := c.Flags()
@ -1046,6 +1050,7 @@ func (c *Command) validateRequiredFlags() error {
// If c already has help flag, it will do nothing. // If c already has help flag, it will do nothing.
func (c *Command) InitDefaultHelpFlag() { func (c *Command) InitDefaultHelpFlag() {
c.mergePersistentFlags() c.mergePersistentFlags()
c.mergeNamedFlags()
if c.Flags().Lookup("help") == nil { if c.Flags().Lookup("help") == nil {
usage := "help for " usage := "help for "
if c.Name() == "" { if c.Name() == "" {
@ -1067,6 +1072,7 @@ func (c *Command) InitDefaultVersionFlag() {
} }
c.mergePersistentFlags() c.mergePersistentFlags()
c.mergeNamedFlags()
if c.Flags().Lookup("version") == nil { if c.Flags().Lookup("version") == nil {
usage := "version for " usage := "version for "
if c.Name() == "" { if c.Name() == "" {
@ -1475,6 +1481,31 @@ func (c *Command) Flags() *flag.FlagSet {
return c.flags return c.flags
} }
// NamedFlagSets returns all the named FlagSet that applies to this command.
func (c *Command) NamedFlagSets() *NamedFlagSets {
if c.lnamedFlagSets == nil {
c.lnamedFlagSets = NewNamedFlagSets(c.Name(), flag.ContinueOnError)
}
return c.lnamedFlagSets
}
// NamedFlags returns the specific named FlagSet that applies to this command.
func (c *Command) NamedFlags(name string) *flag.FlagSet {
nfs := c.NamedFlagSets()
flags, ok := nfs.FlagSet(name)
if !ok {
if c.flagErrorBuf == nil {
c.flagErrorBuf = new(bytes.Buffer)
}
flags.SetOutput(c.flagErrorBuf)
if c.globNormFunc != nil {
flags.SetNormalizeFunc(c.globNormFunc)
}
}
return flags
}
// LocalNonPersistentFlags are flags specific to this command which will NOT persist to subcommands. // LocalNonPersistentFlags are flags specific to this command which will NOT persist to subcommands.
func (c *Command) LocalNonPersistentFlags() *flag.FlagSet { func (c *Command) LocalNonPersistentFlags() *flag.FlagSet {
persistentFlags := c.PersistentFlags() persistentFlags := c.PersistentFlags()
@ -1488,9 +1519,23 @@ func (c *Command) LocalNonPersistentFlags() *flag.FlagSet {
return out return out
} }
// LocalNonNamedFlags are flags specific to this command which are NOT named.
func (c *Command) LocalNonNamedFlags() *flag.FlagSet {
namedFlags := c.NamedFlagSets().Flatten()
out := flag.NewFlagSet(c.Name(), flag.ContinueOnError)
c.LocalFlags().VisitAll(func(f *flag.Flag) {
if namedFlags.Lookup(f.Name) == nil {
out.AddFlag(f)
}
})
return out
}
// LocalFlags returns the local FlagSet specifically set in the current command. // LocalFlags returns the local FlagSet specifically set in the current command.
func (c *Command) LocalFlags() *flag.FlagSet { func (c *Command) LocalFlags() *flag.FlagSet {
c.mergePersistentFlags() c.mergePersistentFlags()
c.mergeNamedFlags()
if c.lflags == nil { if c.lflags == nil {
c.lflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError) c.lflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError)
@ -1517,6 +1562,7 @@ func (c *Command) LocalFlags() *flag.FlagSet {
// InheritedFlags returns all flags which were inherited from parent commands. // InheritedFlags returns all flags which were inherited from parent commands.
func (c *Command) InheritedFlags() *flag.FlagSet { func (c *Command) InheritedFlags() *flag.FlagSet {
c.mergePersistentFlags() c.mergePersistentFlags()
c.mergeNamedFlags()
if c.iflags == nil { if c.iflags == nil {
c.iflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError) c.iflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError)
@ -1568,6 +1614,7 @@ func (c *Command) ResetFlags() {
c.lflags = nil c.lflags = nil
c.iflags = nil c.iflags = nil
c.parentsPflags = nil c.parentsPflags = nil
c.lnamedFlagSets = nil
} }
// HasFlags checks if the command contains any flags (local plus persistent from the entire structure). // HasFlags checks if the command contains any flags (local plus persistent from the entire structure).
@ -1596,6 +1643,11 @@ func (c *Command) HasAvailableFlags() bool {
return c.Flags().HasAvailableFlags() return c.Flags().HasAvailableFlags()
} }
// HasAvailableNamedFlags checks if the command contains any named flags which are not hidden or deprecated.
func (c *Command) HasAvailableNamedFlags() bool {
return c.NamedFlagSets().Flatten().HasAvailableFlags()
}
// HasAvailablePersistentFlags checks if the command contains persistent flags which are not hidden or deprecated. // HasAvailablePersistentFlags checks if the command contains persistent flags which are not hidden or deprecated.
func (c *Command) HasAvailablePersistentFlags() bool { func (c *Command) HasAvailablePersistentFlags() bool {
return c.PersistentFlags().HasAvailableFlags() return c.PersistentFlags().HasAvailableFlags()
@ -1648,6 +1700,7 @@ func (c *Command) ParseFlags(args []string) error {
} }
beforeErrorBufLen := c.flagErrorBuf.Len() beforeErrorBufLen := c.flagErrorBuf.Len()
c.mergePersistentFlags() c.mergePersistentFlags()
c.mergeNamedFlags()
// do it here after merging all flags and just before parse // do it here after merging all flags and just before parse
c.Flags().ParseErrorsWhitelist = flag.ParseErrorsWhitelist(c.FParseErrWhitelist) c.Flags().ParseErrorsWhitelist = flag.ParseErrorsWhitelist(c.FParseErrWhitelist)
@ -1674,6 +1727,11 @@ func (c *Command) mergePersistentFlags() {
c.Flags().AddFlagSet(c.parentsPflags) c.Flags().AddFlagSet(c.parentsPflags)
} }
// mergeNamedFlags merges c.NamedFlagSets() to c.Flags()
func (c *Command) mergeNamedFlags() {
c.Flags().AddFlagSet(c.NamedFlagSets().Flatten())
}
// updateParentsPflags updates c.parentsPflags by adding // updateParentsPflags updates c.parentsPflags by adding
// new persistent flags of all parents. // new persistent flags of all parents.
// If c.parentsPflags == nil, it makes new. // If c.parentsPflags == nil, it makes new.

89
named_flag_sets.go Normal file
View file

@ -0,0 +1,89 @@
package cobra
import (
"bytes"
"fmt"
"strings"
"github.com/spf13/pflag"
)
// NamedFlagSets stores named flag sets in the order of calling FlagSet.
type NamedFlagSets struct {
name string
errorHandling pflag.ErrorHandling
// order is an ordered list of flag set names.
order []string
// flagSets stores the flag sets by name.
flagSets map[string]*pflag.FlagSet
}
func NewNamedFlagSets(name string, errorHandling pflag.ErrorHandling) *NamedFlagSets {
return &NamedFlagSets{
name: name,
errorHandling: errorHandling,
}
}
// FlagSet returns the flag set with the given name and adds it to the
// ordered name list if it is not in there yet.
func (nfs *NamedFlagSets) FlagSet(name string) (*pflag.FlagSet, bool) {
if nfs.flagSets == nil {
nfs.flagSets = map[string]*pflag.FlagSet{}
}
var ok bool
if _, ok = nfs.flagSets[name]; !ok {
flagSet := pflag.NewFlagSet(name, nfs.errorHandling)
nfs.flagSets[name] = flagSet
nfs.order = append(nfs.order, name)
}
return nfs.flagSets[name], ok
}
// Flatten returns a single flag set containing all the flag sets
// in the NamedFlagSet
func (nfs *NamedFlagSets) Flatten() *pflag.FlagSet {
out := pflag.NewFlagSet(nfs.name, nfs.errorHandling)
for _, fs := range nfs.flagSets {
out.AddFlagSet(fs)
}
return out
}
// FlagUsages returns a string containing the usage information for all flags in
// the FlagSet
func (nfs *NamedFlagSets) FlagUsages() string {
return nfs.FlagUsagesWrapped(0)
}
func (nfs *NamedFlagSets) FlagUsagesWrapped(cols int) string {
var buf bytes.Buffer
for _, name := range nfs.order {
fs := nfs.flagSets[name]
if !fs.HasFlags() {
continue
}
wideFS := pflag.NewFlagSet("", pflag.ExitOnError)
wideFS.AddFlagSet(fs)
var zzz string
if cols > 24 {
zzz = strings.Repeat("z", cols-24)
wideFS.Int(zzz, 0, strings.Repeat("z", cols-24))
}
s := fmt.Sprintf("\n%s Flags:\n%s", strings.ToUpper(name[:1])+name[1:], wideFS.FlagUsagesWrapped(cols))
if cols > 24 {
i := strings.Index(s, zzz)
lines := strings.Split(s[:i], "\n")
fmt.Fprint(&buf, strings.Join(lines[:len(lines)-1], "\n"))
fmt.Fprintln(&buf)
} else {
fmt.Fprint(&buf, s)
}
}
return buf.String()
}