@@ -35,6 +35,12 @@ const FlagSetByCobraAnnotation = "cobra_annotation_flag_set_by_cobra"
3535// FParseErrWhitelist configures Flag parse errors to be ignored
3636type FParseErrWhitelist flag.ParseErrorsWhitelist
3737
38+ // Structure to manage groups for commands
39+ type Group struct {
40+ ID string
41+ Title string
42+ }
43+
3844// Command is just that, a command for your application.
3945// E.g. 'go run ...' - 'run' is the command. Cobra requires
4046// you to define the usage and description as part of your command
@@ -61,6 +67,9 @@ type Command struct {
6167// Short is the short description shown in the 'help' output.
6268Short string
6369
70+ // The group id under which this subcommand is grouped in the 'help' output of its parent.
71+ GroupID string
72+
6473// Long is the long message shown in the 'help <this-command>' output.
6574Long string
6675
@@ -128,6 +137,9 @@ type Command struct {
128137// PersistentPostRunE: PersistentPostRun but returns an error.
129138PersistentPostRunE func (cmd * Command , args []string ) error
130139
140+ // groups for subcommands
141+ commandgroups []* Group
142+
131143// args is actual args parsed from flags.
132144args []string
133145// flagErrorBuf contains all error messages from pflag.
@@ -160,6 +172,12 @@ type Command struct {
160172// helpCommand is command with usage 'help'. If it's not defined by user,
161173// cobra uses default help command.
162174helpCommand * Command
175+ // helpCommandGroupID is the group id for the helpCommand
176+ helpCommandGroupID string
177+
178+ // completionCommandGroupID is the group id for the completion command
179+ completionCommandGroupID string
180+
163181// versionTemplate is the version template defined by user.
164182versionTemplate string
165183
@@ -303,6 +321,21 @@ func (c *Command) SetHelpCommand(cmd *Command) {
303321c .helpCommand = cmd
304322}
305323
324+ // SetHelpCommandGroup sets the group id of the help command.
325+ func (c * Command ) SetHelpCommandGroupID (groupID string ) {
326+ if c .helpCommand != nil {
327+ c .helpCommand .GroupID = groupID
328+ }
329+ // helpCommandGroupID is used if no helpCommand is defined by the user
330+ c .helpCommandGroupID = groupID
331+ }
332+
333+ // SetCompletionCommandGroup sets the group id of the completion command.
334+ func (c * Command ) SetCompletionCommandGroupID (groupID string ) {
335+ // completionCommandGroupID is used if no completion command is defined by the user
336+ c .Root ().completionCommandGroupID = groupID
337+ }
338+
306339// SetHelpTemplate sets help template to be used. Application can use it to set custom template.
307340func (c * Command ) SetHelpTemplate (s string ) {
308341c .helpTemplate = s
@@ -511,10 +544,16 @@ Aliases:
511544 {{.NameAndAliases}}{{end}}{{if .HasExample}}
512545
513546Examples:
514- {{.Example}}{{end}}{{if .HasAvailableSubCommands}}
547+ {{.Example}}{{end}}{{if .HasAvailableSubCommands}}{{$cmds := .Commands}}{{if eq (len .Groups) 0}}
548+
549+ Available Commands:{{range $cmds}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
550+ {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{else}}{{range $group := .Groups}}
515551
516- Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
517- {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
552+ {{.Title}}{{range $cmds}}{{if (and (eq .GroupID $group.ID) (or .IsAvailableCommand (eq .Name "help")))}}
553+ {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if not .AllChildCommandsHaveGroup}}
554+
555+ Additional Commands:{{range $cmds}}{{if (and (eq .GroupID "") (or .IsAvailableCommand (eq .Name "help")))}}
556+ {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
518557
519558Flags:
520559{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
@@ -1140,6 +1179,7 @@ Simply type ` + c.Name() + ` help [path to command] for full details.`,
11401179CheckErr (cmd .Help ())
11411180}
11421181},
1182+ GroupID : c .helpCommandGroupID ,
11431183}
11441184}
11451185c .RemoveCommand (c .helpCommand )
@@ -1178,6 +1218,10 @@ func (c *Command) AddCommand(cmds ...*Command) {
11781218panic ("Command can't be a child of itself" )
11791219}
11801220cmds [i ].parent = c
1221+ // if Group is not defined let the developer know right away
1222+ if x .GroupID != "" && ! c .ContainsGroup (x .GroupID ) {
1223+ panic (fmt .Sprintf ("Group id '%s' is not defined for subcommand '%s'" , x .GroupID , cmds [i ].CommandPath ()))
1224+ }
11811225// update max lengths
11821226usageLen := len (x .Use )
11831227if usageLen > c .commandsMaxUseLen {
@@ -1200,6 +1244,36 @@ func (c *Command) AddCommand(cmds ...*Command) {
12001244}
12011245}
12021246
1247+ // Groups returns a slice of child command groups.
1248+ func (c * Command ) Groups () []* Group {
1249+ return c .commandgroups
1250+ }
1251+
1252+ // AllChildCommandsHaveGroup returns if all subcommands are assigned to a group
1253+ func (c * Command ) AllChildCommandsHaveGroup () bool {
1254+ for _ , sub := range c .commands {
1255+ if (sub .IsAvailableCommand () || sub == c .helpCommand ) && sub .GroupID == "" {
1256+ return false
1257+ }
1258+ }
1259+ return true
1260+ }
1261+
1262+ // ContainGroups return if groupID exists in the list of command groups.
1263+ func (c * Command ) ContainsGroup (groupID string ) bool {
1264+ for _ , x := range c .commandgroups {
1265+ if x .ID == groupID {
1266+ return true
1267+ }
1268+ }
1269+ return false
1270+ }
1271+
1272+ // AddGroup adds one or more command groups to this parent command.
1273+ func (c * Command ) AddGroup (groups ... * Group ) {
1274+ c .commandgroups = append (c .commandgroups , groups ... )
1275+ }
1276+
12031277// RemoveCommand removes one or more commands from a parent command.
12041278func (c * Command ) RemoveCommand (cmds ... * Command ) {
12051279commands := []* Command {}
0 commit comments