diff --git a/app.go b/app.go index 10386c4..1a77f3b 100644 --- a/app.go +++ b/app.go @@ -13,8 +13,12 @@ import ( type App struct { // The name of the program. Defaults to os.Args[0] Name string + // Full name of command for help, defaults to Name + HelpName string // Description of the program. Usage string + // Description of the program argument format. + ArgsUsage string // Version of the program Version string // List of commands to execute @@ -67,6 +71,7 @@ func compileTime() time.Time { func NewApp() *App { return &App{ Name: os.Args[0], + HelpName: os.Args[0], Usage: "A new cli application", Version: "0.0.0", BashComplete: DefaultAppComplete, @@ -82,6 +87,15 @@ func (a *App) Run(arguments []string) (err error) { a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email}) } + newCmds := []Command{} + for _, c := range a.Commands { + if c.HelpName == "" { + c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name) + } + newCmds = append(newCmds, c) + } + a.Commands = newCmds + // append help to commands if a.Command(helpCommand.Name) == nil && !a.HideHelp { a.Commands = append(a.Commands, helpCommand) @@ -185,6 +199,15 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { } } + newCmds := []Command{} + for _, c := range a.Commands { + if c.HelpName == "" { + c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name) + } + newCmds = append(newCmds, c) + } + a.Commands = newCmds + // append flags if a.EnableBashCompletion { a.appendFlag(BashCompletionFlag) diff --git a/app_test.go b/app_test.go index 4c6787a..ada5d69 100644 --- a/app_test.go +++ b/app_test.go @@ -90,10 +90,10 @@ func ExampleAppHelp() { app.Run(os.Args) // Output: // NAME: - // describeit - use it to see a description + // greet describeit - use it to see a description // // USAGE: - // command describeit [arguments...] + // greet describeit [arguments...] // // DESCRIPTION: // This is how we describe describeit the function @@ -737,13 +737,75 @@ func TestApp_Run_SubcommandFullPath(t *testing.T) { app := NewApp() buf := new(bytes.Buffer) app.Writer = buf + app.Name = "command" + subCmd := Command{ + Name: "bar", + Usage: "does bar things", + } + cmd := Command{ + Name: "foo", + Description: "foo commands", + Subcommands: []Command{subCmd}, + } + app.Commands = []Command{cmd} + + err := app.Run([]string{"command", "foo", "bar", "--help"}) + if err != nil { + t.Error(err) + } + + output := buf.String() + if !strings.Contains(output, "command foo bar - does bar things") { + t.Errorf("expected full path to subcommand: %s", output) + } + if !strings.Contains(output, "command foo bar [arguments...]") { + t.Errorf("expected full path to subcommand: %s", output) + } +} +func TestApp_Run_SubcommandHelpName(t *testing.T) { + app := NewApp() + buf := new(bytes.Buffer) + app.Writer = buf + app.Name = "command" + subCmd := Command{ + Name: "bar", + HelpName: "custom", + Usage: "does bar things", + } + cmd := Command{ + Name: "foo", + Description: "foo commands", + Subcommands: []Command{subCmd}, + } + app.Commands = []Command{cmd} + + err := app.Run([]string{"command", "foo", "bar", "--help"}) + if err != nil { + t.Error(err) + } + + output := buf.String() + if !strings.Contains(output, "custom - does bar things") { + t.Errorf("expected HelpName for subcommand: %s", output) + } + if !strings.Contains(output, "custom [arguments...]") { + t.Errorf("expected HelpName to subcommand: %s", output) + } +} + +func TestApp_Run_CommandHelpName(t *testing.T) { + app := NewApp() + buf := new(bytes.Buffer) + app.Writer = buf + app.Name = "command" subCmd := Command{ Name: "bar", Usage: "does bar things", } cmd := Command{ Name: "foo", + HelpName: "custom", Description: "foo commands", Subcommands: []Command{subCmd}, } @@ -755,7 +817,7 @@ func TestApp_Run_SubcommandFullPath(t *testing.T) { } output := buf.String() - if !strings.Contains(output, "foo bar - does bar things") { + if !strings.Contains(output, "command foo bar - does bar things") { t.Errorf("expected full path to subcommand: %s", output) } if !strings.Contains(output, "command foo bar [arguments...]") { @@ -763,6 +825,37 @@ func TestApp_Run_SubcommandFullPath(t *testing.T) { } } +func TestApp_Run_CommandSubcommandHelpName(t *testing.T) { + app := NewApp() + buf := new(bytes.Buffer) + app.Writer = buf + app.Name = "base" + subCmd := Command{ + Name: "bar", + HelpName: "custom", + Usage: "does bar things", + } + cmd := Command{ + Name: "foo", + Description: "foo commands", + Subcommands: []Command{subCmd}, + } + app.Commands = []Command{cmd} + + err := app.Run([]string{"command", "foo", "--help"}) + if err != nil { + t.Error(err) + } + + output := buf.String() + if !strings.Contains(output, "base foo - foo commands") { + t.Errorf("expected full path to subcommand: %s", output) + } + if !strings.Contains(output, "base foo command [command options] [arguments...]") { + t.Errorf("expected full path to subcommand: %s", output) + } +} + func TestApp_Run_Help(t *testing.T) { var helpArguments = [][]string{{"boom", "--help"}, {"boom", "-h"}, {"boom", "help"}} diff --git a/command.go b/command.go index 54617af..fac754d 100644 --- a/command.go +++ b/command.go @@ -18,6 +18,8 @@ type Command struct { Usage string // A longer explanation of how the command works Description string + // A short description of the arguments of this command + ArgsUsage string // The function to call when checking for bash command completions BashComplete func(context *Context) // An action to execute before any sub-subcommands are run, but after the context is ready @@ -37,6 +39,8 @@ type Command struct { // Boolean to hide built-in help command HideHelp bool + // Full name of command for help, defaults to full command name, including parent commands. + HelpName string commandNamePath []string } @@ -153,6 +157,12 @@ func (c Command) startApp(ctx *Context) error { // set the name and usage app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name) + if c.HelpName == "" { + app.HelpName = c.HelpName + } else { + app.HelpName = fmt.Sprintf("%s %s", ctx.App.Name, c.Name) + } + if c.Description != "" { app.Usage = c.Description } else { diff --git a/help.go b/help.go index 66ef2fb..e6ba0de 100644 --- a/help.go +++ b/help.go @@ -15,7 +15,7 @@ var AppHelpTemplate = `NAME: {{.Name}} - {{.Usage}} USAGE: - {{.Name}} {{if .Flags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} [arguments...] + {{.HelpName}} {{if .Flags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}} {{if .Version}} VERSION: {{.Version}} @@ -38,10 +38,10 @@ COPYRIGHT: // cli.go uses text/template to render templates. You can // render custom help text by setting this variable. var CommandHelpTemplate = `NAME: - {{.FullName}} - {{.Usage}} + {{.HelpName}} - {{.Usage}} USAGE: - command {{.FullName}}{{if .Flags}} [command options]{{end}} [arguments...]{{if .Description}} + {{.HelpName}}{{if .Flags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{if .Description}} DESCRIPTION: {{.Description}}{{end}}{{if .Flags}} @@ -55,10 +55,10 @@ OPTIONS: // cli.go uses text/template to render templates. You can // render custom help text by setting this variable. var SubcommandHelpTemplate = `NAME: - {{.Name}} - {{.Usage}} + {{.HelpName}} - {{.Usage}} USAGE: - {{.Name}} command{{if .Flags}} [command options]{{end}} [arguments...] + {{.HelpName}} command{{if .Flags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}} COMMANDS: {{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}} @@ -69,9 +69,10 @@ OPTIONS: ` var helpCommand = Command{ - Name: "help", - Aliases: []string{"h"}, - Usage: "Shows a list of commands or help for one command", + Name: "help", + Aliases: []string{"h"}, + Usage: "Shows a list of commands or help for one command", + ArgsUsage: "[command]", Action: func(c *Context) { args := c.Args() if args.Present() { @@ -83,9 +84,10 @@ var helpCommand = Command{ } var helpSubcommand = Command{ - Name: "help", - Aliases: []string{"h"}, - Usage: "Shows a list of commands or help for one command", + Name: "help", + Aliases: []string{"h"}, + Usage: "Shows a list of commands or help for one command", + ArgsUsage: "[command]", Action: func(c *Context) { args := c.Args() if args.Present() {