diff --git a/README.md b/README.md index 528233d3..64c29ef1 100644 --- a/README.md +++ b/README.md @@ -216,11 +216,11 @@ For a more complete example of a larger application, please checkout [Hugo](http ## The Help Command -Cobra automatically adds a help command to your application. +Cobra automatically adds a help command to your application when you have subcommands. This will be called when a user runs 'app help'. Additionally help will also support all other commands as input. Say for instance you have a command called 'create' without any additional configuration cobra will work when 'app help -create' is called. +create' is called. Every command will automatically have the '--help' flag added. ### Example diff --git a/cobra_test.go b/cobra_test.go index dd44812c..5c98a603 100644 --- a/cobra_test.go +++ b/cobra_test.go @@ -461,7 +461,7 @@ func TestRootHelp(t *testing.T) { x := fullSetupTest("--help") checkResultContains(t, x, "Available Commands:") - checkResultContains(t, x, "for more information about that command") + checkResultContains(t, x, "for more information about a command") if strings.Contains(x.Output, "unknown flag: --help") { t.Errorf("--help shouldn't trigger an error, Got: \n %s", x.Output) @@ -470,7 +470,7 @@ func TestRootHelp(t *testing.T) { x = fullSetupTest("echo --help") checkResultContains(t, x, "Available Commands:") - checkResultContains(t, x, "for more information about that command") + checkResultContains(t, x, "for more information about a command") if strings.Contains(x.Output, "unknown flag: --help") { t.Errorf("--help shouldn't trigger an error, Got: \n %s", x.Output) @@ -482,7 +482,7 @@ func TestRootNoCommandHelp(t *testing.T) { x := rootOnlySetupTest("--help") checkResultOmits(t, x, "Available Commands:") - checkResultOmits(t, x, "for more information about that command") + checkResultOmits(t, x, "for more information about a command") if strings.Contains(x.Output, "unknown flag: --help") { t.Errorf("--help shouldn't trigger an error, Got: \n %s", x.Output) @@ -491,7 +491,7 @@ func TestRootNoCommandHelp(t *testing.T) { x = rootOnlySetupTest("echo --help") checkResultOmits(t, x, "Available Commands:") - checkResultOmits(t, x, "for more information about that command") + checkResultOmits(t, x, "for more information about a command") if strings.Contains(x.Output, "unknown flag: --help") { t.Errorf("--help shouldn't trigger an error, Got: \n %s", x.Output) diff --git a/command.go b/command.go index e976fe5a..b4f62632 100644 --- a/command.go +++ b/command.go @@ -40,9 +40,9 @@ type Command struct { Short string // The long message shown in the 'help ' output. Long string - // Set of flags specific to this command. + // Full set of flags flags *flag.FlagSet - // Set of flags children commands will inherit + // Set of flags childrens of this command will inherit pflags *flag.FlagSet // Run runs the command. // The args are the arguments after the command name. @@ -206,12 +206,14 @@ Aliases: Available Commands: {{range .Commands}}{{if .Runnable}} {{rpad .Use .UsagePadding }} {{.Short}}{{end}}{{end}} {{end}} -{{ if .HasFlags}} Available Flags: -{{.Flags.FlagUsages}}{{end}}{{if .HasParent}}{{if and (gt .Commands 0) (gt .Parent.Commands 1) }} +{{ if .HasLocalFlags}}Flags: +{{.LocalFlags.FlagUsages}}{{end}} +{{ if .HasAnyPersistentFlags}}Global Flags: +{{.AllPersistentFlags.FlagUsages}}{{end}}{{if .HasParent}}{{if and (gt .Commands 0) (gt .Parent.Commands 1) }} Additional help topics: {{if gt .Commands 0 }}{{range .Commands}}{{if not .Runnable}} {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if gt .Parent.Commands 1 }}{{range .Parent.Commands}}{{if .Runnable}}{{if not (eq .Name $cmd.Name) }}{{end}} {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{end}} {{end}}{{ if .HasSubCommands }} -Use "{{.Root.Name}} help [command]" for more information about that command. +Use "{{.Root.Name}} help [command]" for more information about a command. {{end}}` } } @@ -707,7 +709,7 @@ func (c *Command) HasParent() bool { return c.parent != nil } -// Get the Commands FlagSet +// Get the complete FlagSet that applies to this command (local and persistent declared here and by all parents) func (c *Command) Flags() *flag.FlagSet { if c.flags == nil { c.flags = flag.NewFlagSet(c.Name(), flag.ContinueOnError) @@ -720,7 +722,23 @@ func (c *Command) Flags() *flag.FlagSet { return c.flags } -// Get the Commands Persistent FlagSet +// Get the local FlagSet specifically set in the current command +func (c *Command) LocalFlags() *flag.FlagSet { + c.mergePersistentFlags() + + local := flag.NewFlagSet(c.Name(), flag.ContinueOnError) + allPersistent := c.AllPersistentFlags() + + c.Flags().VisitAll(func(f *flag.Flag) { + if allPersistent.Lookup(f.Name) == nil { + local.AddFlag(f) + } + }) + + return local +} + +// Get the Persistent FlagSet specifically set in the current command func (c *Command) PersistentFlags() *flag.FlagSet { if c.pflags == nil { c.pflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError) @@ -732,6 +750,29 @@ func (c *Command) PersistentFlags() *flag.FlagSet { return c.pflags } +// Get the Persistent FlagSet traversing the Command hierarchy +func (c *Command) AllPersistentFlags() *flag.FlagSet { + allPersistent := flag.NewFlagSet(c.Name(), flag.ContinueOnError) + + var visit func(x *Command) + visit = func(x *Command) { + if x.HasPersistentFlags() { + x.PersistentFlags().VisitAll(func(f *flag.Flag) { + if allPersistent.Lookup(f.Name) == nil { + allPersistent.AddFlag(f) + } + }) + } + if x.HasParent() { + visit(x.parent) + } + } + + visit(c) + + return allPersistent +} + // For use in testing func (c *Command) ResetFlags() { c.flagErrorBuf = new(bytes.Buffer) @@ -742,7 +783,7 @@ func (c *Command) ResetFlags() { c.pflags.SetOutput(c.flagErrorBuf) } -// Does the command contain flags (local not persistent) +// Does the command contain any flags (local plus persistent from the entire structure) func (c *Command) HasFlags() bool { return c.Flags().HasFlags() } @@ -752,6 +793,16 @@ func (c *Command) HasPersistentFlags() bool { return c.PersistentFlags().HasFlags() } +// Does the command hierarchy contain persistent flags +func (c *Command) HasAnyPersistentFlags() bool { + return c.AllPersistentFlags().HasFlags() +} + +// Does the command has flags specifically declared locally +func (c *Command) HasLocalFlags() bool { + return c.LocalFlags().HasFlags() +} + // Climbs up the command tree looking for matching flag func (c *Command) Flag(name string) (flag *flag.Flag) { flag = c.Flags().Lookup(name) @@ -791,6 +842,10 @@ func (c *Command) ParseFlags(args []string) (err error) { return nil } +func (c *Command) Parent() *Command { + return c.parent +} + func (c *Command) mergePersistentFlags() { var rmerge func(x *Command) @@ -809,7 +864,3 @@ func (c *Command) mergePersistentFlags() { rmerge(c) } - -func (c *Command) Parent() *Command { - return c.parent -}