diff --git a/app.go b/app.go index 6abcc08a9b..dd7f026498 100644 --- a/app.go +++ b/app.go @@ -52,6 +52,8 @@ type App struct { HideVersion bool // categories contains the categorized commands and is populated on app startup categories CommandCategories + // Populate on app startup, only gettable through method Categories() + flagCategories FlagCategories // An action to execute when the shell completion flag is set BashComplete BashCompleteFunc // An action to execute before any subcommands are run, but after the context is ready @@ -181,6 +183,14 @@ func (a *App) Setup() { if c.HelpName == "" { c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name) } + + fc := FlagCategories{} + for _, flag := range c.Flags { + fc = fc.AddFlag(flag.GetCategory(), flag) + } + + sort.Sort(fc) + c.FlagCategories = fc newCommands = append(newCommands, c) } a.Commands = newCommands @@ -481,6 +491,11 @@ func (a *App) VisibleCommands() []*Command { return ret } +// Categories returns a slice containing all the categories with the commands they contain +func (a *App) VisibleFlagCategories() FlagCategories { + return a.flagCategories +} + // VisibleFlags returns a slice of the Flags with Hidden=false func (a *App) VisibleFlags() []Flag { return visibleFlags(a.Flags) diff --git a/category.go b/category.go index 867e3908ca..f9ba86a299 100644 --- a/category.go +++ b/category.go @@ -77,3 +77,46 @@ func (c *commandCategory) VisibleCommands() []*Command { } return ret } + +// FlagCategories is a slice of *FlagCategory. +type FlagCategories []*FlagCategory + +// FlagCategory is a category containing commands. +type FlagCategory struct { + Name string + Flags Flags +} + +func (f FlagCategories) Less(i, j int) bool { + return lexicographicLess(f[i].Name, f[j].Name) +} + +func (f FlagCategories) Len() int { + return len(f) +} + +func (f FlagCategories) Swap(i, j int) { + f[i], f[j] = f[j], f[i] +} + +// AddFlags adds a command to a category. +func (f FlagCategories) AddFlag(category string, flag Flag) FlagCategories { + for _, flagCategory := range f { + if flagCategory.Name == category { + flagCategory.Flags = append(flagCategory.Flags, flag) + return f + } + } + return append(f, &FlagCategory{Name: category, Flags: []Flag{flag}}) +} + +// VisibleFlags returns a slice of the Flags with Hidden=false +func (c *FlagCategory) VisibleFlags() []Flag { + ret := []Flag{} + for _, flag := range c.Flags { + if !flag.GetHidden() { + ret = append(ret, flag) + } + } + return ret +} diff --git a/command.go b/command.go index 3477686bb3..93fd483c39 100644 --- a/command.go +++ b/command.go @@ -39,6 +39,8 @@ type Command struct { Subcommands []*Command // List of flags to parse Flags []Flag + // List of all flag categories + FlagCategories FlagCategories // Treat all flags as normal arguments if true SkipFlagParsing bool // Boolean to hide built-in help command and help flag @@ -280,6 +282,11 @@ func (c *Command) startApp(ctx *Context) error { return app.RunAsSubcommand(ctx) } +// Categories returns a slice containing all the categories with the commands they contain +func (c Command) VisibleFlagCategories() FlagCategories { + return c.FlagCategories +} + // VisibleFlags returns a slice of the Flags with Hidden=false func (c *Command) VisibleFlags() []Flag { return visibleFlags(c.Flags) diff --git a/flag.go b/flag.go index 1907622e56..60d592e52c 100644 --- a/flag.go +++ b/flag.go @@ -94,6 +94,8 @@ type Flag interface { Apply(*flag.FlagSet) error Names() []string IsSet() bool + GetCategory() string + GetHidden() bool } // RequiredFlag is an interface that allows us to mark flags as required diff --git a/go.sum b/go.sum index bde502bd2e..977ae8bece 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=