diff --git a/README.md b/README.md index 1484240d94..3c544e3fc6 100644 --- a/README.md +++ b/README.md @@ -533,6 +533,11 @@ command and flag definitions are needed. Help is just a command like any other. There is no special logic or behavior around it. In fact, you can provide your own if you want. +### Grouping commands in help + +Cobra supports grouping of available commands. Groups can either be explicitly defined by `AddGroup` and set by +the `Group` element of a subcommand. If Groups are not explicitly defined they are implicitly defined. + ### Defining your own help You can provide your own Help command or your own template for the default command to use diff --git a/command.go b/command.go index 5f1cacccba..1dbfebdc2e 100644 --- a/command.go +++ b/command.go @@ -31,6 +31,12 @@ import ( // FParseErrWhitelist configures Flag parse errors to be ignored type FParseErrWhitelist flag.ParseErrorsWhitelist +// Structure to manage groups for commands +type Group struct { + Group string + Title string +} + // Command is just that, a command for your application. // E.g. 'go run ...' - 'run' is the command. Cobra requires // you to define the usage and description as part of your command @@ -49,6 +55,9 @@ type Command struct { // Short is the short description shown in the 'help' output. Short string + // The group under which the command is grouped in the 'help' output. + Group string + // Long is the long message shown in the 'help ' output. Long string @@ -167,6 +176,9 @@ type Command struct { called bool } + // groups for commands + commandgroups []*Group + // args is actual args parsed from flags. args []string // flagErrorBuf contains all error messages from pflag. @@ -199,6 +211,9 @@ type Command struct { // helpCommand is command with usage 'help'. If it's not defined by user, // cobra uses default help command. helpCommand *Command + // helpCommandGroup is the default group the helpCommand is in + helpCommandGroup string + // versionTemplate is the version template defined by user. versionTemplate string @@ -274,6 +289,15 @@ func (c *Command) SetHelpCommand(cmd *Command) { c.helpCommand = cmd } +// SetHelpCommandGroup sets the group of the help command. +func (c *Command) SetHelpCommandGroup(group string) { + if c.helpCommand != nil { + c.helpCommand.Group = group + } + // helpCommandGroup is used if no helpCommand is defined by the user + c.helpCommandGroup = group +} + // SetHelpTemplate sets help template to be used. Application can use it to set custom template. func (c *Command) SetHelpTemplate(s string) { c.helpTemplate = s @@ -482,10 +506,13 @@ Aliases: {{.NameAndAliases}}{{end}}{{if .HasExample}} Examples: -{{.Example}}{{end}}{{if .HasAvailableSubCommands}} +{{.Example}}{{end}}{{if .HasAvailableSubCommands}}{{$cmds := .Commands}} + +Available Commands:{{range $cmds}}{{if (and (eq .Group "") (or .IsAvailableCommand (eq .Name "help")))}} + {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{range $group := .Groups}} -Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}} - {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} +{{.Title}}{{range $cmds}}{{if (and (eq .Group $group.Group) (or .IsAvailableCommand (eq .Name "help")))}} + {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} Flags: {{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} @@ -1085,6 +1112,7 @@ Simply type ` + c.Name() + ` help [path to command] for full details.`, cmd.Help() } }, + Group: c.helpCommandGroup, } } c.RemoveCommand(c.helpCommand) @@ -1123,6 +1151,10 @@ func (c *Command) AddCommand(cmds ...*Command) { panic("Command can't be a child of itself") } cmds[i].parent = c + // if Group is not defined generate a new one with same title + if x.Group != "" && !c.ContainsGroup(x.Group) { + c.AddGroup(&Group{Group: x.Group, Title: x.Group}) + } // update max lengths usageLen := len(x.Use) if usageLen > c.commandsMaxUseLen { @@ -1145,6 +1177,28 @@ func (c *Command) AddCommand(cmds ...*Command) { } } +// Groups returns a slice of child command groups. +func (c *Command) Groups() []*Group { + return c.commandgroups +} + +// ContainGroups return if group is in command groups. +func (c *Command) ContainsGroup(group string) bool { + for _, x := range c.commandgroups { + if x.Group == group { + return true + } + } + return false +} + +// AddGroup adds one or more command groups to this parent command. +func (c *Command) AddGroup(groups ...*Group) { + for _, x := range groups { + c.commandgroups = append(c.commandgroups, x) + } +} + // RemoveCommand removes one or more commands from a parent command. func (c *Command) RemoveCommand(cmds ...*Command) { commands := []*Command{}