More consistent implementation of recursive subcommands

This commit is contained in:
Summer Mousa 2014-04-16 11:18:00 -05:00
parent 640826c88f
commit 13f0c8c0f6
3 changed files with 140 additions and 1 deletions

56
app.go
View File

@ -129,6 +129,62 @@ func (a *App) Run(arguments []string) error {
return nil return nil
} }
// Invokes the subcommand given the context, parses ctx.Args() to generate command-specific flags
func (a *App) RunAsSubcommand(c *Context) error {
// append help to commands
if a.Command(helpCommand.Name) == nil {
a.Commands = append(a.Commands, helpCommand)
}
// append help flags
if a.EnableBashCompletion {
a.appendFlag(BashCompletionFlag)
}
a.appendFlag(BoolFlag{"help, h", "show help"})
// parse flags
set := flagSet(a.Name, a.Flags)
set.SetOutput(ioutil.Discard)
err := set.Parse(c.Args().Tail())
nerr := normalizeFlags(a.Flags, set)
context := NewContext(a, set, set)
if nerr != nil {
fmt.Println(nerr)
ShowSubcommandHelp(context)
fmt.Println("")
return nerr
}
if err != nil {
fmt.Printf("Incorrect Usage.\n\n")
ShowSubcommandHelp(context)
fmt.Println("")
return err
}
if checkCompletions(context) {
return nil
}
if checkSubcommandHelp(context) {
return nil
}
args := context.Args()
if args.Present() {
name := args.First()
c := a.Command(name)
if c != nil {
return c.Run(context)
}
}
// Run default Action
a.Action(context)
return nil
}
// Returns the named command on App. Returns nil if the command does not exist // Returns the named command on App. Returns nil if the command does not exist
func (a *App) Command(name string) *Command { func (a *App) Command(name string) *Command {
for _, c := range a.Commands { for _, c := range a.Commands {

View File

@ -14,7 +14,7 @@ type Command struct {
ShortName string ShortName string
// A short description of the usage of this command // A short description of the usage of this command
Usage string Usage string
// A longer explaination of how the command works // A longer explanation of how the command works
Description string Description string
// The function to call when checking for bash command completions // The function to call when checking for bash command completions
BashComplete func(context *Context) BashComplete func(context *Context)
@ -22,10 +22,16 @@ type Command struct {
Action func(context *Context) Action func(context *Context)
// List of flags to parse // List of flags to parse
Flags []Flag Flags []Flag
// List of child commands
Subcommands []Command
} }
// Invokes the command given the context, parses ctx.Args() to generate command-specific flags // Invokes the command given the context, parses ctx.Args() to generate command-specific flags
func (c Command) Run(ctx *Context) error { func (c Command) Run(ctx *Context) error {
if len(c.Subcommands) > 0 {
return c.startApp(ctx)
}
// append help to flags // append help to flags
c.Flags = append( c.Flags = append(
c.Flags, c.Flags,
@ -89,3 +95,34 @@ func (c Command) Run(ctx *Context) error {
func (c Command) HasName(name string) bool { func (c Command) HasName(name string) bool {
return c.Name == name || c.ShortName == name return c.Name == name || c.ShortName == name
} }
func (c Command) startApp(ctx *Context) error {
app := NewApp()
// set the name and usage
app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name)
if c.Description != "" {
app.Usage = c.Description
} else {
app.Usage = c.Usage
}
// set the flags and commands
app.Commands = c.Subcommands
app.Flags = c.Flags
// bash completion
app.EnableBashCompletion = ctx.App.EnableBashCompletion
if c.BashComplete != nil {
app.BashComplete = c.BashComplete
}
// set the action
if c.Action != nil {
app.Action = c.Action
} else {
app.Action = helpSubcommand.Action
}
return app.RunAsSubcommand(ctx)
}

46
help.go
View File

@ -44,6 +44,24 @@ OPTIONS:
{{end}} {{end}}
` `
// The text template for the subcommand help topic.
// cli.go uses text/template to render templates. You can
// render custom help text by setting this variable.
var SubcommandHelpTemplate = `NAME:
{{.Name}} - {{.Usage}}
USAGE:
{{.Name}} [global options] command [command options] [arguments...]
COMMANDS:
{{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
{{end}}
OPTIONS:
{{range .Flags}}{{.}}
{{end}}
`
var helpCommand = Command{ var helpCommand = Command{
Name: "help", Name: "help",
ShortName: "h", ShortName: "h",
@ -58,6 +76,20 @@ var helpCommand = Command{
}, },
} }
var helpSubcommand = Command{
Name: "help",
ShortName: "h",
Usage: "Shows a list of commands or help for one command",
Action: func(c *Context) {
args := c.Args()
if args.Present() {
ShowCommandHelp(c, args.First())
} else {
ShowSubcommandHelp(c)
}
},
}
// Prints help for the App // Prints help for the App
var HelpPrinter = printHelp var HelpPrinter = printHelp
@ -87,6 +119,11 @@ func ShowCommandHelp(c *Context, command string) {
fmt.Printf("No help topic for '%v'\n", command) fmt.Printf("No help topic for '%v'\n", command)
} }
// Prints help for the given subcommand
func ShowSubcommandHelp(c *Context) {
HelpPrinter(SubcommandHelpTemplate, c.App)
}
// Prints the version number of the App // Prints the version number of the App
func ShowVersion(c *Context) { func ShowVersion(c *Context) {
fmt.Printf("%v version %v\n", c.App.Name, c.App.Version) fmt.Printf("%v version %v\n", c.App.Name, c.App.Version)
@ -145,6 +182,15 @@ func checkCommandHelp(c *Context, name string) bool {
return false return false
} }
func checkSubcommandHelp(c *Context) bool {
if c.GlobalBool("h") || c.GlobalBool("help") {
ShowSubcommandHelp(c)
return true
}
return false
}
func checkCompletions(c *Context) bool { func checkCompletions(c *Context) bool {
if c.GlobalBool(BashCompletionFlag.Name) && c.App.EnableBashCompletion { if c.GlobalBool(BashCompletionFlag.Name) && c.App.EnableBashCompletion {
ShowCompletions(c) ShowCompletions(c)