mirror of https://github.com/spf13/cobra.git
parent
4fb0a66a34
commit
e9498ae28a
91
command.go
91
command.go
|
@ -32,6 +32,7 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
FlagSetByCobraAnnotation = "cobra_annotation_flag_set_by_cobra"
|
FlagSetByCobraAnnotation = "cobra_annotation_flag_set_by_cobra"
|
||||||
|
FlagHelpGroupAnnotation = "cobra_annotation_flag_help_group"
|
||||||
CommandDisplayNameAnnotation = "cobra_annotation_command_display_name"
|
CommandDisplayNameAnnotation = "cobra_annotation_command_display_name"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -145,6 +146,9 @@ type Command struct {
|
||||||
// groups for subcommands
|
// groups for subcommands
|
||||||
commandgroups []*Group
|
commandgroups []*Group
|
||||||
|
|
||||||
|
// groups for flags in usage text.
|
||||||
|
flagHelpGroups []*Group
|
||||||
|
|
||||||
// args is actual args parsed from flags.
|
// args is actual args parsed from flags.
|
||||||
args []string
|
args []string
|
||||||
// flagErrorBuf contains all error messages from pflag.
|
// flagErrorBuf contains all error messages from pflag.
|
||||||
|
@ -568,13 +572,22 @@ Available Commands:{{range $cmds}}{{if (or .IsAvailableCommand (eq .Name "help")
|
||||||
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if not .AllChildCommandsHaveGroup}}
|
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if not .AllChildCommandsHaveGroup}}
|
||||||
|
|
||||||
Additional Commands:{{range $cmds}}{{if (and (eq .GroupID "") (or .IsAvailableCommand (eq .Name "help")))}}
|
Additional Commands:{{range $cmds}}{{if (and (eq .GroupID "") (or .IsAvailableCommand (eq .Name "help")))}}
|
||||||
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
|
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}}{{$cmd := .}}{{if eq (len .FlagHelpGroups) 0}}{{if .HasAvailableLocalFlags}}
|
||||||
|
|
||||||
Flags:
|
Flags:
|
||||||
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
|
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
|
||||||
|
|
||||||
Global Flags:
|
Global Flags:
|
||||||
{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}
|
{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{else}}{{$flags := .LocalFlags}}{{range $helpGroup := .FlagHelpGroups}}{{if not (eq (len ($cmd.UsageByFlagHelpGroupID "")) 0)}}
|
||||||
|
|
||||||
|
Flags:
|
||||||
|
{{$cmd.UsageByFlagHelpGroupID "" | trimTrailingWhitespaces}}{{end}}
|
||||||
|
|
||||||
|
{{.Title}} Flags:
|
||||||
|
{{$cmd.UsageByFlagHelpGroupID $helpGroup.ID | trimTrailingWhitespaces}}{{if not (eq (len ($cmd.UsageByFlagHelpGroupID "global")) 0)}}
|
||||||
|
|
||||||
|
Global Flags:
|
||||||
|
{{$cmd.UsageByFlagHelpGroupID "global" | trimTrailingWhitespaces}}{{end}}{{end}}{{end}}{{if .HasHelpSubCommands}}
|
||||||
|
|
||||||
Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
|
Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
|
||||||
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
|
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
|
||||||
|
@ -1336,6 +1349,80 @@ func (c *Command) Groups() []*Group {
|
||||||
return c.commandgroups
|
return c.commandgroups
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FlagHelpGroups returns a slice of the command's flag help groups
|
||||||
|
func (c *Command) FlagHelpGroups() []*Group {
|
||||||
|
return c.flagHelpGroups
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFlagHelpGroup adds one more flag help group do the command. Returns an error if the Group.ID is empty,
|
||||||
|
// or if the "global" reserved ID is used
|
||||||
|
func (c *Command) AddFlagHelpGroup(groups ...*Group) error {
|
||||||
|
for _, group := range groups {
|
||||||
|
if len(group.ID) == 0 {
|
||||||
|
return fmt.Errorf("flag help group ID must have at least one character")
|
||||||
|
}
|
||||||
|
|
||||||
|
if group.ID == "global" {
|
||||||
|
return fmt.Errorf(`"global" is a reserved flag help group ID`)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.flagHelpGroups = append(c.flagHelpGroups, group)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Command) hasFlagHelpGroup(groupID string) bool {
|
||||||
|
for _, g := range c.flagHelpGroups {
|
||||||
|
if g.ID == groupID {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFlagToHelpGroupID adds associates a flag to a groupID. Returns an error if the flag or group is non-existent
|
||||||
|
func (c *Command) AddFlagToHelpGroupID(flag, groupID string) error {
|
||||||
|
lf := c.Flags()
|
||||||
|
|
||||||
|
if !c.hasFlagHelpGroup(groupID) {
|
||||||
|
return fmt.Errorf("no such flag help group: %v", groupID)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := lf.SetAnnotation(flag, FlagHelpGroupAnnotation, []string{groupID})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UsageByFlagHelpGroupID returns the command flag's usage split by flag help groups. Flags without groups associated
|
||||||
|
// will appear under "Flags", and inherited flags will appear under "Global Flags"
|
||||||
|
func (c *Command) UsageByFlagHelpGroupID(groupID string) string {
|
||||||
|
if groupID == "global" {
|
||||||
|
return c.InheritedFlags().FlagUsages()
|
||||||
|
}
|
||||||
|
|
||||||
|
fs := &flag.FlagSet{}
|
||||||
|
|
||||||
|
c.LocalFlags().VisitAll(func(f *flag.Flag) {
|
||||||
|
if _, ok := f.Annotations[FlagHelpGroupAnnotation]; !ok {
|
||||||
|
if groupID == "" {
|
||||||
|
fs.AddFlag(f)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if id := f.Annotations[FlagHelpGroupAnnotation][0]; id == groupID {
|
||||||
|
fs.AddFlag(f)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return fs.FlagUsages()
|
||||||
|
}
|
||||||
|
|
||||||
// AllChildCommandsHaveGroup returns if all subcommands are assigned to a group
|
// AllChildCommandsHaveGroup returns if all subcommands are assigned to a group
|
||||||
func (c *Command) AllChildCommandsHaveGroup() bool {
|
func (c *Command) AllChildCommandsHaveGroup() bool {
|
||||||
for _, sub := range c.commands {
|
for _, sub := range c.commands {
|
||||||
|
|
|
@ -920,6 +920,82 @@ func TestPersistentRequiredFlagsWithDisableFlagParsing(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFlagHelpGroups(t *testing.T) {
|
||||||
|
|
||||||
|
t.Run("add flag to non-existing flag help group", func(t *testing.T) {
|
||||||
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
||||||
|
b := "b"
|
||||||
|
|
||||||
|
rootCmd.Flags().Bool(b, false, "bool flag")
|
||||||
|
|
||||||
|
err := rootCmd.AddFlagToHelpGroupID(b, "id")
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Expected error when adding a flag to non-existent flag help group")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("add non-existing flag to flag help group", func(t *testing.T) {
|
||||||
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
||||||
|
|
||||||
|
group := Group{ID: "id", Title: "GroupTitle"}
|
||||||
|
rootCmd.AddFlagHelpGroup(&group)
|
||||||
|
|
||||||
|
err := rootCmd.AddFlagToHelpGroupID("", "id")
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Expected error when adding a non-existent flag to flag help group")
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("add flag to flag help group", func(t *testing.T) {
|
||||||
|
child := &Command{Use: "child", Run: emptyRun}
|
||||||
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
||||||
|
|
||||||
|
rootCmd.AddCommand(child)
|
||||||
|
|
||||||
|
b := "b"
|
||||||
|
s := "s"
|
||||||
|
i := "i"
|
||||||
|
g := "g"
|
||||||
|
|
||||||
|
child.Flags().Bool(b, false, "bool flag")
|
||||||
|
child.Flags().String(s, "", "string flag")
|
||||||
|
child.Flags().Int(i, 0, "int flag")
|
||||||
|
rootCmd.PersistentFlags().String(g, "", "global flag")
|
||||||
|
|
||||||
|
group := Group{ID: "groupId", Title: "GroupTitle"}
|
||||||
|
|
||||||
|
child.AddFlagHelpGroup(&group)
|
||||||
|
|
||||||
|
_ = child.AddFlagToHelpGroupID(b, group.ID)
|
||||||
|
_ = child.AddFlagToHelpGroupID(s, group.ID)
|
||||||
|
x := `Usage:
|
||||||
|
root child [flags]
|
||||||
|
|
||||||
|
Flags:
|
||||||
|
-h, --help help for child
|
||||||
|
--i int int flag
|
||||||
|
|
||||||
|
GroupTitle Flags:
|
||||||
|
--b bool flag
|
||||||
|
--s string string flag
|
||||||
|
|
||||||
|
Global Flags:
|
||||||
|
--g string global flag
|
||||||
|
`
|
||||||
|
|
||||||
|
got, err := executeCommand(rootCmd, "help", "child")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got != x {
|
||||||
|
t.Errorf("Help text mismatch.\nExpected:\n%s\n\nGot:\n%s\n", x, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func TestInitHelpFlagMergesFlags(t *testing.T) {
|
func TestInitHelpFlagMergesFlags(t *testing.T) {
|
||||||
usage := "custom flag"
|
usage := "custom flag"
|
||||||
rootCmd := &Command{Use: "root"}
|
rootCmd := &Command{Use: "root"}
|
||||||
|
|
Loading…
Reference in New Issue