diff --git a/app.go b/app.go index 463437a58a..5b372101b2 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 + // flagCategories contains the categorized flags and is populated on app startup + 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 @@ -183,6 +185,8 @@ func (a *App) Setup() { if c.HelpName == "" { c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name) } + + c.flagCategories = newFlagCategoriesFromFlags(c.Flags) newCommands = append(newCommands, c) } a.Commands = newCommands @@ -207,6 +211,13 @@ func (a *App) Setup() { } sort.Sort(a.categories.(*commandCategories)) + a.flagCategories = newFlagCategories() + for _, fl := range a.Flags { + if cf, ok := fl.(CategorizableFlag); ok { + a.flagCategories.AddFlag(cf.GetCategory(), cf) + } + } + if a.Metadata == nil { a.Metadata = make(map[string]interface{}) } @@ -493,6 +504,14 @@ func (a *App) VisibleCommands() []*Command { return ret } +// VisibleFlagCategories returns a slice containing all the categories with the flags they contain +func (a *App) VisibleFlagCategories() []VisibleFlagCategory { + if a.flagCategories == nil { + return []VisibleFlagCategory{} + } + return a.flagCategories.VisibleCategories() +} + // VisibleFlags returns a slice of the Flags with Hidden=false func (a *App) VisibleFlags() []Flag { return visibleFlags(a.Flags) diff --git a/app_test.go b/app_test.go index 26ae28a95c..3dd73abc16 100644 --- a/app_test.go +++ b/app_test.go @@ -142,8 +142,8 @@ func ExampleApp_Run_appHelp() { // help, h Shows a list of commands or help for one command // // GLOBAL OPTIONS: - // --name value a name to say (default: "bob") // --help, -h show help (default: false) + // --name value a name to say (default: "bob") // --version, -v print the version (default: false) } @@ -1927,6 +1927,14 @@ func TestApp_VisibleCategories(t *testing.T) { expect(t, []CommandCategory{}, app.VisibleCategories()) } +func TestApp_VisibleFlagCategories(t *testing.T) { + app := &App{} + vfc := app.VisibleFlagCategories() + if len(vfc) != 0 { + t.Errorf("unexpected visible flag categories %+v", vfc) + } +} + func TestApp_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) { app := &App{ Action: func(c *Context) error { return nil }, diff --git a/category.go b/category.go index 867e3908ca..8bf325e203 100644 --- a/category.go +++ b/category.go @@ -1,10 +1,12 @@ package cli +import "sort" + // CommandCategories interface allows for category manipulation type CommandCategories interface { // AddCommand adds a command to a category, creating a new category if necessary. AddCommand(category string, command *Command) - // categories returns a copy of the category slice + // Categories returns a slice of categories sorted by name Categories() []CommandCategory } @@ -77,3 +79,93 @@ func (c *commandCategory) VisibleCommands() []*Command { } return ret } + +// FlagCategories interface allows for category manipulation +type FlagCategories interface { + // AddFlags adds a flag to a category, creating a new category if necessary. + AddFlag(category string, fl Flag) + // VisibleCategories returns a slice of visible flag categories sorted by name + VisibleCategories() []VisibleFlagCategory +} + +type defaultFlagCategories struct { + m map[string]*defaultVisibleFlagCategory +} + +func newFlagCategories() FlagCategories { + return &defaultFlagCategories{ + m: map[string]*defaultVisibleFlagCategory{}, + } +} + +func newFlagCategoriesFromFlags(fs []Flag) FlagCategories { + fc := newFlagCategories() + for _, fl := range fs { + if cf, ok := fl.(CategorizableFlag); ok { + fc.AddFlag(cf.GetCategory(), cf) + } + } + + return fc +} + +func (f *defaultFlagCategories) AddFlag(category string, fl Flag) { + if _, ok := f.m[category]; !ok { + f.m[category] = &defaultVisibleFlagCategory{name: category, m: map[string]Flag{}} + } + + f.m[category].m[fl.String()] = fl +} + +func (f *defaultFlagCategories) VisibleCategories() []VisibleFlagCategory { + catNames := []string{} + for name := range f.m { + catNames = append(catNames, name) + } + + sort.Strings(catNames) + + ret := make([]VisibleFlagCategory, len(catNames)) + for i, name := range catNames { + ret[i] = f.m[name] + } + + return ret +} + +// VisibleFlagCategory is a category containing flags. +type VisibleFlagCategory interface { + // Name returns the category name string + Name() string + // Flags returns a slice of VisibleFlag sorted by name + Flags() []VisibleFlag +} + +type defaultVisibleFlagCategory struct { + name string + m map[string]Flag +} + +func (fc *defaultVisibleFlagCategory) Name() string { + return fc.name +} + +func (fc *defaultVisibleFlagCategory) Flags() []VisibleFlag { + vfNames := []string{} + for flName, fl := range fc.m { + if vf, ok := fl.(VisibleFlag); ok { + if vf.IsVisible() { + vfNames = append(vfNames, flName) + } + } + } + + sort.Strings(vfNames) + + ret := make([]VisibleFlag, len(vfNames)) + for i, flName := range vfNames { + ret[i] = fc.m[flName].(VisibleFlag) + } + + return ret +} diff --git a/command.go b/command.go index 3b9b837034..2cafd8e0ec 100644 --- a/command.go +++ b/command.go @@ -38,7 +38,8 @@ type Command struct { // List of child commands Subcommands []*Command // List of flags to parse - Flags []Flag + Flags []Flag + flagCategories FlagCategories // Treat all flags as normal arguments if true SkipFlagParsing bool // Boolean to hide built-in help command and help flag @@ -286,6 +287,14 @@ func (c *Command) startApp(ctx *Context) error { return app.RunAsSubcommand(ctx) } +// VisibleFlagCategories returns a slice containing all the visible flag categories with the flags they contain +func (c *Command) VisibleFlagCategories() []VisibleFlagCategory { + if c.flagCategories == nil { + return []VisibleFlagCategory{} + } + return c.flagCategories.VisibleCategories() +} + // 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 b750a281bc..dbed577cdc 100644 --- a/flag.go +++ b/flag.go @@ -132,6 +132,14 @@ type VisibleFlag interface { IsVisible() bool } +// CategorizableFlag is an interface that allows us to potentially +// use a flag in a categorized representation. +type CategorizableFlag interface { + VisibleFlag + + GetCategory() string +} + func flagSet(name string, flags []Flag) (*flag.FlagSet, error) { set := flag.NewFlagSet(name, flag.ContinueOnError) diff --git a/flag_bool.go b/flag_bool.go index ff5eb7fbc2..b21d5163c9 100644 --- a/flag_bool.go +++ b/flag_bool.go @@ -16,6 +16,11 @@ func (f *BoolFlag) GetUsage() string { return f.Usage } +// GetCategory returns the category for the flag +func (f *BoolFlag) GetCategory() string { + return f.Category +} + // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *BoolFlag) GetValue() string { diff --git a/flag_duration.go b/flag_duration.go index bdc29b6d69..5178c6ae12 100644 --- a/flag_duration.go +++ b/flag_duration.go @@ -16,6 +16,11 @@ func (f *DurationFlag) GetUsage() string { return f.Usage } +// GetCategory returns the category for the flag +func (f *DurationFlag) GetCategory() string { + return f.Category +} + // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *DurationFlag) GetValue() string { diff --git a/flag_float64.go b/flag_float64.go index 9aa3f6911a..2d31739bc6 100644 --- a/flag_float64.go +++ b/flag_float64.go @@ -16,6 +16,11 @@ func (f *Float64Flag) GetUsage() string { return f.Usage } +// GetCategory returns the category for the flag +func (f *Float64Flag) GetCategory() string { + return f.Category +} + // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *Float64Flag) GetValue() string { diff --git a/flag_float64_slice.go b/flag_float64_slice.go index 377b779465..bc347ccdb2 100644 --- a/flag_float64_slice.go +++ b/flag_float64_slice.go @@ -91,6 +91,11 @@ func (f *Float64SliceFlag) GetUsage() string { return f.Usage } +// GetCategory returns the category for the flag +func (f *Float64SliceFlag) GetCategory() string { + return f.Category +} + // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *Float64SliceFlag) GetValue() string { diff --git a/flag_generic.go b/flag_generic.go index d7c0ed4cd1..680eeb9d71 100644 --- a/flag_generic.go +++ b/flag_generic.go @@ -21,6 +21,11 @@ func (f *GenericFlag) GetUsage() string { return f.Usage } +// GetCategory returns the category for the flag +func (f *GenericFlag) GetCategory() string { + return f.Category +} + // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *GenericFlag) GetValue() string { diff --git a/flag_int.go b/flag_int.go index e7e4077c56..c70b889858 100644 --- a/flag_int.go +++ b/flag_int.go @@ -16,6 +16,11 @@ func (f *IntFlag) GetUsage() string { return f.Usage } +// GetCategory returns the category for the flag +func (f *IntFlag) GetCategory() string { + return f.Category +} + // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *IntFlag) GetValue() string { diff --git a/flag_int64.go b/flag_int64.go index 96a8e9ace8..5e7038cfb1 100644 --- a/flag_int64.go +++ b/flag_int64.go @@ -16,6 +16,11 @@ func (f *Int64Flag) GetUsage() string { return f.Usage } +// GetCategory returns the category for the flag +func (f *Int64Flag) GetCategory() string { + return f.Category +} + // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *Int64Flag) GetValue() string { diff --git a/flag_int64_slice.go b/flag_int64_slice.go index 46e8e98210..5f3d5cd4ea 100644 --- a/flag_int64_slice.go +++ b/flag_int64_slice.go @@ -88,10 +88,15 @@ func (f *Int64SliceFlag) TakesValue() bool { } // GetUsage returns the usage string for the flag -func (f Int64SliceFlag) GetUsage() string { +func (f *Int64SliceFlag) GetUsage() string { return f.Usage } +// GetCategory returns the category for the flag +func (f *Int64SliceFlag) GetCategory() string { + return f.Category +} + // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *Int64SliceFlag) GetValue() string { diff --git a/flag_int_slice.go b/flag_int_slice.go index 2071cbf219..2ddf805967 100644 --- a/flag_int_slice.go +++ b/flag_int_slice.go @@ -99,10 +99,15 @@ func (f *IntSliceFlag) TakesValue() bool { } // GetUsage returns the usage string for the flag -func (f IntSliceFlag) GetUsage() string { +func (f *IntSliceFlag) GetUsage() string { return f.Usage } +// GetCategory returns the category for the flag +func (f *IntSliceFlag) GetCategory() string { + return f.Category +} + // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *IntSliceFlag) GetValue() string { diff --git a/flag_path.go b/flag_path.go index 2866f84c07..7c87a8900d 100644 --- a/flag_path.go +++ b/flag_path.go @@ -17,6 +17,11 @@ func (f *PathFlag) GetUsage() string { return f.Usage } +// GetCategory returns the category for the flag +func (f *PathFlag) GetCategory() string { + return f.Category +} + // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *PathFlag) GetValue() string { diff --git a/flag_string.go b/flag_string.go index 6333afe7ab..c8da38f92d 100644 --- a/flag_string.go +++ b/flag_string.go @@ -15,6 +15,11 @@ func (f *StringFlag) GetUsage() string { return f.Usage } +// GetCategory returns the category for the flag +func (f *StringFlag) GetCategory() string { + return f.Category +} + // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *StringFlag) GetValue() string { diff --git a/flag_string_slice.go b/flag_string_slice.go index 34c8af1fdc..599f42c7fb 100644 --- a/flag_string_slice.go +++ b/flag_string_slice.go @@ -86,6 +86,11 @@ func (f *StringSliceFlag) GetUsage() string { return f.Usage } +// GetCategory returns the category for the flag +func (f *StringSliceFlag) GetCategory() string { + return f.Category +} + // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *StringSliceFlag) GetValue() string { diff --git a/flag_timestamp.go b/flag_timestamp.go index e0dd9124cf..052247795c 100644 --- a/flag_timestamp.go +++ b/flag_timestamp.go @@ -68,6 +68,11 @@ func (f *TimestampFlag) GetUsage() string { return f.Usage } +// GetCategory returns the category for the flag +func (f *TimestampFlag) GetCategory() string { + return f.Category +} + // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *TimestampFlag) GetValue() string { diff --git a/flag_uint.go b/flag_uint.go index 1936a9f8c8..6092b1ad69 100644 --- a/flag_uint.go +++ b/flag_uint.go @@ -16,6 +16,11 @@ func (f *UintFlag) GetUsage() string { return f.Usage } +// GetCategory returns the category for the flag +func (f *UintFlag) GetCategory() string { + return f.Category +} + // Apply populates the flag given the flag set and environment func (f *UintFlag) Apply(set *flag.FlagSet) error { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { diff --git a/flag_uint64.go b/flag_uint64.go index 59e1bb5b1b..a37f30d9fc 100644 --- a/flag_uint64.go +++ b/flag_uint64.go @@ -16,6 +16,11 @@ func (f *Uint64Flag) GetUsage() string { return f.Usage } +// GetCategory returns the category for the flag +func (f *Uint64Flag) GetCategory() string { + return f.Category +} + // Apply populates the flag given the flag set and environment func (f *Uint64Flag) Apply(set *flag.FlagSet) error { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { diff --git a/godoc-current.txt b/godoc-current.txt index 90700ebde7..61fbfeb6f9 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -45,11 +45,16 @@ AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: COMMANDS:{{range .VisibleCategories}}{{if .Name}} {{.Name}}:{{range .VisibleCommands}} {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}} - {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} + {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{end}}{{if .VisibleFlagCategories}} + +GLOBAL OPTIONS:{{range .VisibleFlagCategories}} + {{if .Name}}{{.Name}} + {{end}}{{range .Flags}}{{.}} + {{end}}{{end}}{{else}}{{if .VisibleFlags}} GLOBAL OPTIONS: {{range $index, $option := .VisibleFlags}}{{if $index}} - {{end}}{{$option}}{{end}}{{end}}{{if .Copyright}} + {{end}}{{$option}}{{end}}{{end}}{{end}}{{if .Copyright}} COPYRIGHT: {{.Copyright}}{{end}} @@ -68,11 +73,16 @@ CATEGORY: {{.Category}}{{end}}{{if .Description}} DESCRIPTION: - {{.Description | nindent 3 | trim}}{{end}}{{if .VisibleFlags}} + {{.Description | nindent 3 | trim}}{{end}}{{if .VisibleFlagCategories}} + +OPTIONS:{{range .VisibleFlagCategories}} + {{if .Name}}{{.Name}} + {{end}}{{range .Flags}}{{.}} + {{end}}{{end}}{{else}}{{if .VisibleFlags}} OPTIONS: {{range .VisibleFlags}}{{.}} - {{end}}{{end}} + {{end}}{{end}}{{end}} ` CommandHelpTemplate is the text template for the command help topic. cli.go uses text/template to render templates. You can render custom help text by @@ -369,6 +379,10 @@ func (a *App) VisibleCategories() []CommandCategory func (a *App) VisibleCommands() []*Command VisibleCommands returns a slice of the Commands with Hidden=false +func (a *App) VisibleFlagCategories() []VisibleFlagCategory + VisibleFlagCategories returns a slice containing all the categories with the + flags they contain + func (a *App) VisibleFlags() []Flag VisibleFlags returns a slice of the Flags with Hidden=false @@ -409,6 +423,7 @@ type BeforeFunc func(*Context) error type BoolFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -431,6 +446,9 @@ func (f *BoolFlag) Apply(set *flag.FlagSet) error func (f *BoolFlag) Get(ctx *Context) bool Get returns the flag’s value in the given Context. +func (f *BoolFlag) GetCategory() string + GetCategory returns the category for the flag + func (f *BoolFlag) GetDefaultText() string GetDefaultText returns the default text for this flag @@ -462,6 +480,14 @@ func (f *BoolFlag) String() string func (f *BoolFlag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false +type CategorizableFlag interface { + VisibleFlag + + GetCategory() string +} + CategorizableFlag is an interface that allows us to potentially use a flag + in a categorized representation. + type Command struct { // The name of the command Name string @@ -493,6 +519,7 @@ type Command struct { Subcommands []*Command // List of flags to parse Flags []Flag + // Treat all flags as normal arguments if true SkipFlagParsing bool // Boolean to hide built-in help command and help flag @@ -532,13 +559,17 @@ func (c *Command) Run(ctx *Context) (err error) Run invokes the command given the context, parses ctx.Args() to generate command-specific flags +func (c *Command) VisibleFlagCategories() []VisibleFlagCategory + VisibleFlagCategories returns a slice containing all the visible flag + categories with the flags they contain + func (c *Command) VisibleFlags() []Flag VisibleFlags returns a slice of the Flags with Hidden=false type CommandCategories interface { // AddCommand adds a command to a category, creating a new category if necessary. AddCommand(category string, command *Command) - // categories returns a copy of the category slice + // Categories returns a slice of categories sorted by name Categories() []CommandCategory } CommandCategories interface allows for category manipulation @@ -682,6 +713,7 @@ type DocGenerationFlag interface { type DurationFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -704,6 +736,9 @@ func (f *DurationFlag) Apply(set *flag.FlagSet) error func (f *DurationFlag) Get(ctx *Context) time.Duration Get returns the flag’s value in the given Context. +func (f *DurationFlag) GetCategory() string + GetCategory returns the category for the flag + func (f *DurationFlag) GetDefaultText() string GetDefaultText returns the default text for this flag @@ -799,6 +834,14 @@ var VersionFlag Flag = &BoolFlag{ } VersionFlag prints the version for the application +type FlagCategories interface { + // AddFlags adds a flag to a category, creating a new category if necessary. + AddFlag(category string, fl Flag) + // VisibleCategories returns a slice of visible flag categories sorted by name + VisibleCategories() []VisibleFlagCategory +} + FlagCategories interface allows for category manipulation + type FlagEnvHintFunc func(envVars []string, str string) string FlagEnvHintFunc is used by the default FlagStringFunc to annotate flag help with the environment variable details. @@ -843,6 +886,7 @@ func (f FlagsByName) Swap(i, j int) type Float64Flag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -865,6 +909,9 @@ func (f *Float64Flag) Apply(set *flag.FlagSet) error func (f *Float64Flag) Get(ctx *Context) float64 Get returns the flag’s value in the given Context. +func (f *Float64Flag) GetCategory() string + GetCategory returns the category for the flag + func (f *Float64Flag) GetDefaultText() string GetDefaultText returns the default text for this flag @@ -922,6 +969,7 @@ func (f *Float64Slice) Value() []float64 type Float64SliceFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -944,6 +992,9 @@ func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error func (f *Float64SliceFlag) Get(ctx *Context) []float64 Get returns the flag’s value in the given Context. +func (f *Float64SliceFlag) GetCategory() string + GetCategory returns the category for the flag + func (f *Float64SliceFlag) GetDefaultText() string GetDefaultText returns the default text for this flag @@ -984,6 +1035,7 @@ type Generic interface { type GenericFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -1009,6 +1061,9 @@ func (f GenericFlag) Apply(set *flag.FlagSet) error func (f *GenericFlag) Get(ctx *Context) interface{} Get returns the flag’s value in the given Context. +func (f *GenericFlag) GetCategory() string + GetCategory returns the category for the flag + func (f *GenericFlag) GetDefaultText() string GetDefaultText returns the default text for this flag @@ -1043,6 +1098,7 @@ func (f *GenericFlag) TakesValue() bool type Int64Flag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -1065,6 +1121,9 @@ func (f *Int64Flag) Apply(set *flag.FlagSet) error func (f *Int64Flag) Get(ctx *Context) int64 Get returns the flag’s value in the given Context. +func (f *Int64Flag) GetCategory() string + GetCategory returns the category for the flag + func (f *Int64Flag) GetDefaultText() string GetDefaultText returns the default text for this flag @@ -1122,6 +1181,7 @@ func (i *Int64Slice) Value() []int64 type Int64SliceFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -1144,13 +1204,16 @@ func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error func (f *Int64SliceFlag) Get(ctx *Context) []int64 Get returns the flag’s value in the given Context. +func (f *Int64SliceFlag) GetCategory() string + GetCategory returns the category for the flag + func (f *Int64SliceFlag) GetDefaultText() string GetDefaultText returns the default text for this flag func (f *Int64SliceFlag) GetEnvVars() []string GetEnvVars returns the env vars for this flag -func (f Int64SliceFlag) GetUsage() string +func (f *Int64SliceFlag) GetUsage() string GetUsage returns the usage string for the flag func (f *Int64SliceFlag) GetValue() string @@ -1178,6 +1241,7 @@ func (f *Int64SliceFlag) TakesValue() bool type IntFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -1200,6 +1264,9 @@ func (f *IntFlag) Apply(set *flag.FlagSet) error func (f *IntFlag) Get(ctx *Context) int Get returns the flag’s value in the given Context. +func (f *IntFlag) GetCategory() string + GetCategory returns the category for the flag + func (f *IntFlag) GetDefaultText() string GetDefaultText returns the default text for this flag @@ -1261,6 +1328,7 @@ func (i *IntSlice) Value() []int type IntSliceFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -1283,13 +1351,16 @@ func (f *IntSliceFlag) Apply(set *flag.FlagSet) error func (f *IntSliceFlag) Get(ctx *Context) []int Get returns the flag’s value in the given Context. +func (f *IntSliceFlag) GetCategory() string + GetCategory returns the category for the flag + func (f *IntSliceFlag) GetDefaultText() string GetDefaultText returns the default text for this flag func (f *IntSliceFlag) GetEnvVars() []string GetEnvVars returns the env vars for this flag -func (f IntSliceFlag) GetUsage() string +func (f *IntSliceFlag) GetUsage() string GetUsage returns the usage string for the flag func (f *IntSliceFlag) GetValue() string @@ -1331,6 +1402,7 @@ type Path = string type PathFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -1355,6 +1427,9 @@ func (f *PathFlag) Apply(set *flag.FlagSet) error func (f *PathFlag) Get(ctx *Context) string Get returns the flag’s value in the given Context. +func (f *PathFlag) GetCategory() string + GetCategory returns the category for the flag + func (f *PathFlag) GetDefaultText() string GetDefaultText returns the default text for this flag @@ -1403,6 +1478,7 @@ type Serializer interface { type StringFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -1427,6 +1503,9 @@ func (f *StringFlag) Apply(set *flag.FlagSet) error func (f *StringFlag) Get(ctx *Context) string Get returns the flag’s value in the given Context. +func (f *StringFlag) GetCategory() string + GetCategory returns the category for the flag + func (f *StringFlag) GetDefaultText() string GetDefaultText returns the default text for this flag @@ -1484,6 +1563,7 @@ func (s *StringSlice) Value() []string type StringSliceFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -1508,6 +1588,9 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) error func (f *StringSliceFlag) Get(ctx *Context) []string Get returns the flag’s value in the given Context. +func (f *StringSliceFlag) GetCategory() string + GetCategory returns the category for the flag + func (f *StringSliceFlag) GetDefaultText() string GetDefaultText returns the default text for this flag @@ -1568,6 +1651,7 @@ func (t *Timestamp) Value() *time.Time type TimestampFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -1592,6 +1676,9 @@ func (f *TimestampFlag) Apply(set *flag.FlagSet) error func (f *TimestampFlag) Get(ctx *Context) *time.Time Get returns the flag’s value in the given Context. +func (f *TimestampFlag) GetCategory() string + GetCategory returns the category for the flag + func (f *TimestampFlag) GetDefaultText() string GetDefaultText returns the default text for this flag @@ -1626,6 +1713,7 @@ func (f *TimestampFlag) TakesValue() bool type Uint64Flag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -1648,6 +1736,9 @@ func (f *Uint64Flag) Apply(set *flag.FlagSet) error func (f *Uint64Flag) Get(ctx *Context) uint64 Get returns the flag’s value in the given Context. +func (f *Uint64Flag) GetCategory() string + GetCategory returns the category for the flag + func (f *Uint64Flag) GetDefaultText() string GetDefaultText returns the default text for this flag @@ -1682,6 +1773,7 @@ func (f *Uint64Flag) TakesValue() bool type UintFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -1704,6 +1796,9 @@ func (f *UintFlag) Apply(set *flag.FlagSet) error func (f *UintFlag) Get(ctx *Context) uint Get returns the flag’s value in the given Context. +func (f *UintFlag) GetCategory() string + GetCategory returns the category for the flag + func (f *UintFlag) GetDefaultText() string GetDefaultText returns the default text for this flag @@ -1743,6 +1838,14 @@ type VisibleFlag interface { } VisibleFlag is an interface that allows to check if a flag is visible +type VisibleFlagCategory interface { + // Name returns the category name string + Name() string + // Flags returns a slice of VisibleFlag sorted by name + Flags() []VisibleFlag +} + VisibleFlagCategory is a category containing flags. + package altsrc // import "github.com/urfave/cli/v2/altsrc" diff --git a/internal/genflags/generated.gotmpl b/internal/genflags/generated.gotmpl index 99c292aa7c..8b82ccfb1d 100644 --- a/internal/genflags/generated.gotmpl +++ b/internal/genflags/generated.gotmpl @@ -7,6 +7,7 @@ package {{.PackageName}} type {{.TypeName}} struct { Name string + Category string DefaultText string FilePath string Usage string diff --git a/template.go b/template.go index 39fa4db080..264eb856bb 100644 --- a/template.go +++ b/template.go @@ -22,11 +22,16 @@ AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: COMMANDS:{{range .VisibleCategories}}{{if .Name}} {{.Name}}:{{range .VisibleCommands}} {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}} - {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} + {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{end}}{{if .VisibleFlagCategories}} + +GLOBAL OPTIONS:{{range .VisibleFlagCategories}} + {{if .Name}}{{.Name}} + {{end}}{{range .Flags}}{{.}} + {{end}}{{end}}{{else}}{{if .VisibleFlags}} GLOBAL OPTIONS: {{range $index, $option := .VisibleFlags}}{{if $index}} - {{end}}{{$option}}{{end}}{{end}}{{if .Copyright}} + {{end}}{{$option}}{{end}}{{end}}{{end}}{{if .Copyright}} COPYRIGHT: {{.Copyright}}{{end}} @@ -45,11 +50,16 @@ CATEGORY: {{.Category}}{{end}}{{if .Description}} DESCRIPTION: - {{.Description | nindent 3 | trim}}{{end}}{{if .VisibleFlags}} + {{.Description | nindent 3 | trim}}{{end}}{{if .VisibleFlagCategories}} + +OPTIONS:{{range .VisibleFlagCategories}} + {{if .Name}}{{.Name}} + {{end}}{{range .Flags}}{{.}} + {{end}}{{end}}{{else}}{{if .VisibleFlags}} OPTIONS: {{range .VisibleFlags}}{{.}} - {{end}}{{end}} + {{end}}{{end}}{{end}} ` // SubcommandHelpTemplate is the text template for the subcommand help topic. diff --git a/testdata/godoc-v2.x.txt b/testdata/godoc-v2.x.txt index 90700ebde7..61fbfeb6f9 100644 --- a/testdata/godoc-v2.x.txt +++ b/testdata/godoc-v2.x.txt @@ -45,11 +45,16 @@ AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: COMMANDS:{{range .VisibleCategories}}{{if .Name}} {{.Name}}:{{range .VisibleCommands}} {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}} - {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} + {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{end}}{{if .VisibleFlagCategories}} + +GLOBAL OPTIONS:{{range .VisibleFlagCategories}} + {{if .Name}}{{.Name}} + {{end}}{{range .Flags}}{{.}} + {{end}}{{end}}{{else}}{{if .VisibleFlags}} GLOBAL OPTIONS: {{range $index, $option := .VisibleFlags}}{{if $index}} - {{end}}{{$option}}{{end}}{{end}}{{if .Copyright}} + {{end}}{{$option}}{{end}}{{end}}{{end}}{{if .Copyright}} COPYRIGHT: {{.Copyright}}{{end}} @@ -68,11 +73,16 @@ CATEGORY: {{.Category}}{{end}}{{if .Description}} DESCRIPTION: - {{.Description | nindent 3 | trim}}{{end}}{{if .VisibleFlags}} + {{.Description | nindent 3 | trim}}{{end}}{{if .VisibleFlagCategories}} + +OPTIONS:{{range .VisibleFlagCategories}} + {{if .Name}}{{.Name}} + {{end}}{{range .Flags}}{{.}} + {{end}}{{end}}{{else}}{{if .VisibleFlags}} OPTIONS: {{range .VisibleFlags}}{{.}} - {{end}}{{end}} + {{end}}{{end}}{{end}} ` CommandHelpTemplate is the text template for the command help topic. cli.go uses text/template to render templates. You can render custom help text by @@ -369,6 +379,10 @@ func (a *App) VisibleCategories() []CommandCategory func (a *App) VisibleCommands() []*Command VisibleCommands returns a slice of the Commands with Hidden=false +func (a *App) VisibleFlagCategories() []VisibleFlagCategory + VisibleFlagCategories returns a slice containing all the categories with the + flags they contain + func (a *App) VisibleFlags() []Flag VisibleFlags returns a slice of the Flags with Hidden=false @@ -409,6 +423,7 @@ type BeforeFunc func(*Context) error type BoolFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -431,6 +446,9 @@ func (f *BoolFlag) Apply(set *flag.FlagSet) error func (f *BoolFlag) Get(ctx *Context) bool Get returns the flag’s value in the given Context. +func (f *BoolFlag) GetCategory() string + GetCategory returns the category for the flag + func (f *BoolFlag) GetDefaultText() string GetDefaultText returns the default text for this flag @@ -462,6 +480,14 @@ func (f *BoolFlag) String() string func (f *BoolFlag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false +type CategorizableFlag interface { + VisibleFlag + + GetCategory() string +} + CategorizableFlag is an interface that allows us to potentially use a flag + in a categorized representation. + type Command struct { // The name of the command Name string @@ -493,6 +519,7 @@ type Command struct { Subcommands []*Command // List of flags to parse Flags []Flag + // Treat all flags as normal arguments if true SkipFlagParsing bool // Boolean to hide built-in help command and help flag @@ -532,13 +559,17 @@ func (c *Command) Run(ctx *Context) (err error) Run invokes the command given the context, parses ctx.Args() to generate command-specific flags +func (c *Command) VisibleFlagCategories() []VisibleFlagCategory + VisibleFlagCategories returns a slice containing all the visible flag + categories with the flags they contain + func (c *Command) VisibleFlags() []Flag VisibleFlags returns a slice of the Flags with Hidden=false type CommandCategories interface { // AddCommand adds a command to a category, creating a new category if necessary. AddCommand(category string, command *Command) - // categories returns a copy of the category slice + // Categories returns a slice of categories sorted by name Categories() []CommandCategory } CommandCategories interface allows for category manipulation @@ -682,6 +713,7 @@ type DocGenerationFlag interface { type DurationFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -704,6 +736,9 @@ func (f *DurationFlag) Apply(set *flag.FlagSet) error func (f *DurationFlag) Get(ctx *Context) time.Duration Get returns the flag’s value in the given Context. +func (f *DurationFlag) GetCategory() string + GetCategory returns the category for the flag + func (f *DurationFlag) GetDefaultText() string GetDefaultText returns the default text for this flag @@ -799,6 +834,14 @@ var VersionFlag Flag = &BoolFlag{ } VersionFlag prints the version for the application +type FlagCategories interface { + // AddFlags adds a flag to a category, creating a new category if necessary. + AddFlag(category string, fl Flag) + // VisibleCategories returns a slice of visible flag categories sorted by name + VisibleCategories() []VisibleFlagCategory +} + FlagCategories interface allows for category manipulation + type FlagEnvHintFunc func(envVars []string, str string) string FlagEnvHintFunc is used by the default FlagStringFunc to annotate flag help with the environment variable details. @@ -843,6 +886,7 @@ func (f FlagsByName) Swap(i, j int) type Float64Flag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -865,6 +909,9 @@ func (f *Float64Flag) Apply(set *flag.FlagSet) error func (f *Float64Flag) Get(ctx *Context) float64 Get returns the flag’s value in the given Context. +func (f *Float64Flag) GetCategory() string + GetCategory returns the category for the flag + func (f *Float64Flag) GetDefaultText() string GetDefaultText returns the default text for this flag @@ -922,6 +969,7 @@ func (f *Float64Slice) Value() []float64 type Float64SliceFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -944,6 +992,9 @@ func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error func (f *Float64SliceFlag) Get(ctx *Context) []float64 Get returns the flag’s value in the given Context. +func (f *Float64SliceFlag) GetCategory() string + GetCategory returns the category for the flag + func (f *Float64SliceFlag) GetDefaultText() string GetDefaultText returns the default text for this flag @@ -984,6 +1035,7 @@ type Generic interface { type GenericFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -1009,6 +1061,9 @@ func (f GenericFlag) Apply(set *flag.FlagSet) error func (f *GenericFlag) Get(ctx *Context) interface{} Get returns the flag’s value in the given Context. +func (f *GenericFlag) GetCategory() string + GetCategory returns the category for the flag + func (f *GenericFlag) GetDefaultText() string GetDefaultText returns the default text for this flag @@ -1043,6 +1098,7 @@ func (f *GenericFlag) TakesValue() bool type Int64Flag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -1065,6 +1121,9 @@ func (f *Int64Flag) Apply(set *flag.FlagSet) error func (f *Int64Flag) Get(ctx *Context) int64 Get returns the flag’s value in the given Context. +func (f *Int64Flag) GetCategory() string + GetCategory returns the category for the flag + func (f *Int64Flag) GetDefaultText() string GetDefaultText returns the default text for this flag @@ -1122,6 +1181,7 @@ func (i *Int64Slice) Value() []int64 type Int64SliceFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -1144,13 +1204,16 @@ func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error func (f *Int64SliceFlag) Get(ctx *Context) []int64 Get returns the flag’s value in the given Context. +func (f *Int64SliceFlag) GetCategory() string + GetCategory returns the category for the flag + func (f *Int64SliceFlag) GetDefaultText() string GetDefaultText returns the default text for this flag func (f *Int64SliceFlag) GetEnvVars() []string GetEnvVars returns the env vars for this flag -func (f Int64SliceFlag) GetUsage() string +func (f *Int64SliceFlag) GetUsage() string GetUsage returns the usage string for the flag func (f *Int64SliceFlag) GetValue() string @@ -1178,6 +1241,7 @@ func (f *Int64SliceFlag) TakesValue() bool type IntFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -1200,6 +1264,9 @@ func (f *IntFlag) Apply(set *flag.FlagSet) error func (f *IntFlag) Get(ctx *Context) int Get returns the flag’s value in the given Context. +func (f *IntFlag) GetCategory() string + GetCategory returns the category for the flag + func (f *IntFlag) GetDefaultText() string GetDefaultText returns the default text for this flag @@ -1261,6 +1328,7 @@ func (i *IntSlice) Value() []int type IntSliceFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -1283,13 +1351,16 @@ func (f *IntSliceFlag) Apply(set *flag.FlagSet) error func (f *IntSliceFlag) Get(ctx *Context) []int Get returns the flag’s value in the given Context. +func (f *IntSliceFlag) GetCategory() string + GetCategory returns the category for the flag + func (f *IntSliceFlag) GetDefaultText() string GetDefaultText returns the default text for this flag func (f *IntSliceFlag) GetEnvVars() []string GetEnvVars returns the env vars for this flag -func (f IntSliceFlag) GetUsage() string +func (f *IntSliceFlag) GetUsage() string GetUsage returns the usage string for the flag func (f *IntSliceFlag) GetValue() string @@ -1331,6 +1402,7 @@ type Path = string type PathFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -1355,6 +1427,9 @@ func (f *PathFlag) Apply(set *flag.FlagSet) error func (f *PathFlag) Get(ctx *Context) string Get returns the flag’s value in the given Context. +func (f *PathFlag) GetCategory() string + GetCategory returns the category for the flag + func (f *PathFlag) GetDefaultText() string GetDefaultText returns the default text for this flag @@ -1403,6 +1478,7 @@ type Serializer interface { type StringFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -1427,6 +1503,9 @@ func (f *StringFlag) Apply(set *flag.FlagSet) error func (f *StringFlag) Get(ctx *Context) string Get returns the flag’s value in the given Context. +func (f *StringFlag) GetCategory() string + GetCategory returns the category for the flag + func (f *StringFlag) GetDefaultText() string GetDefaultText returns the default text for this flag @@ -1484,6 +1563,7 @@ func (s *StringSlice) Value() []string type StringSliceFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -1508,6 +1588,9 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) error func (f *StringSliceFlag) Get(ctx *Context) []string Get returns the flag’s value in the given Context. +func (f *StringSliceFlag) GetCategory() string + GetCategory returns the category for the flag + func (f *StringSliceFlag) GetDefaultText() string GetDefaultText returns the default text for this flag @@ -1568,6 +1651,7 @@ func (t *Timestamp) Value() *time.Time type TimestampFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -1592,6 +1676,9 @@ func (f *TimestampFlag) Apply(set *flag.FlagSet) error func (f *TimestampFlag) Get(ctx *Context) *time.Time Get returns the flag’s value in the given Context. +func (f *TimestampFlag) GetCategory() string + GetCategory returns the category for the flag + func (f *TimestampFlag) GetDefaultText() string GetDefaultText returns the default text for this flag @@ -1626,6 +1713,7 @@ func (f *TimestampFlag) TakesValue() bool type Uint64Flag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -1648,6 +1736,9 @@ func (f *Uint64Flag) Apply(set *flag.FlagSet) error func (f *Uint64Flag) Get(ctx *Context) uint64 Get returns the flag’s value in the given Context. +func (f *Uint64Flag) GetCategory() string + GetCategory returns the category for the flag + func (f *Uint64Flag) GetDefaultText() string GetDefaultText returns the default text for this flag @@ -1682,6 +1773,7 @@ func (f *Uint64Flag) TakesValue() bool type UintFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -1704,6 +1796,9 @@ func (f *UintFlag) Apply(set *flag.FlagSet) error func (f *UintFlag) Get(ctx *Context) uint Get returns the flag’s value in the given Context. +func (f *UintFlag) GetCategory() string + GetCategory returns the category for the flag + func (f *UintFlag) GetDefaultText() string GetDefaultText returns the default text for this flag @@ -1743,6 +1838,14 @@ type VisibleFlag interface { } VisibleFlag is an interface that allows to check if a flag is visible +type VisibleFlagCategory interface { + // Name returns the category name string + Name() string + // Flags returns a slice of VisibleFlag sorted by name + Flags() []VisibleFlag +} + VisibleFlagCategory is a category containing flags. + package altsrc // import "github.com/urfave/cli/v2/altsrc" diff --git a/zz_generated.flags.go b/zz_generated.flags.go index 6c16c3e75a..3cae978c0c 100644 --- a/zz_generated.flags.go +++ b/zz_generated.flags.go @@ -8,6 +8,7 @@ import "time" type Float64SliceFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -47,6 +48,7 @@ func (f *Float64SliceFlag) IsVisible() bool { type GenericFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -93,6 +95,7 @@ func (f *GenericFlag) IsVisible() bool { type Int64SliceFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -132,6 +135,7 @@ func (f *Int64SliceFlag) IsVisible() bool { type IntSliceFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -171,6 +175,7 @@ func (f *IntSliceFlag) IsVisible() bool { type PathFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -217,6 +222,7 @@ func (f *PathFlag) IsVisible() bool { type StringSliceFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -258,6 +264,7 @@ func (f *StringSliceFlag) IsVisible() bool { type TimestampFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -304,6 +311,7 @@ func (f *TimestampFlag) IsVisible() bool { type BoolFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -348,6 +356,7 @@ func (f *BoolFlag) IsVisible() bool { type Float64Flag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -392,6 +401,7 @@ func (f *Float64Flag) IsVisible() bool { type IntFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -436,6 +446,7 @@ func (f *IntFlag) IsVisible() bool { type Int64Flag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -480,6 +491,7 @@ func (f *Int64Flag) IsVisible() bool { type StringFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -526,6 +538,7 @@ func (f *StringFlag) IsVisible() bool { type DurationFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -570,6 +583,7 @@ func (f *DurationFlag) IsVisible() bool { type UintFlag struct { Name string + Category string DefaultText string FilePath string Usage string @@ -614,6 +628,7 @@ func (f *UintFlag) IsVisible() bool { type Uint64Flag struct { Name string + Category string DefaultText string FilePath string Usage string