From ef65245544538d1c4c87140a811a223a2a2a1686 Mon Sep 17 00:00:00 2001 From: Tristan Zajonc Date: Mon, 3 Aug 2015 16:51:11 -0700 Subject: [PATCH 1/5] add ArgsUsage to App and Command --- app.go | 3 +++ command.go | 5 +++++ help.go | 20 +++++++++++--------- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/app.go b/app.go index e7caec9..aadbea6 100644 --- a/app.go +++ b/app.go @@ -15,6 +15,8 @@ type App struct { Name 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 @@ -68,6 +70,7 @@ func NewApp() *App { return &App{ Name: os.Args[0], Usage: "A new cli application", + ArgsUsage: "[arguments...]", Version: "0.0.0", BashComplete: DefaultAppComplete, Action: helpCommand.Action, diff --git a/command.go b/command.go index 54617af..5b6cd66 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 @@ -158,6 +160,9 @@ func (c Command) startApp(ctx *Context) error { } else { app.Usage = c.Usage } + if c.ArgsUsage == "" { + c.ArgsUsage = "[arguments...]" + } // set CommandNotFound app.CommandNotFound = ctx.App.CommandNotFound diff --git a/help.go b/help.go index 66ef2fb..0855eb6 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...] + {{.Name}} {{if .Flags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{.ArgsUsage}} {{if .Version}} VERSION: {{.Version}} @@ -41,7 +41,7 @@ var CommandHelpTemplate = `NAME: {{.FullName}} - {{.Usage}} USAGE: - command {{.FullName}}{{if .Flags}} [command options]{{end}} [arguments...]{{if .Description}} + command {{.FullName}}{{if .Flags}} [command options]{{end}} {{.ArgsUsage}}{{if .Description}} DESCRIPTION: {{.Description}}{{end}}{{if .Flags}} @@ -58,7 +58,7 @@ var SubcommandHelpTemplate = `NAME: {{.Name}} - {{.Usage}} USAGE: - {{.Name}} command{{if .Flags}} [command options]{{end}} [arguments...] + {{.Name}} command{{if .Flags}} [command options]{{end}} {{.ArgsUsage}} 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() { From ecb0b5ac0a95eadc723d375ebb5d4f45af9f36b2 Mon Sep 17 00:00:00 2001 From: Tristan Zajonc Date: Tue, 4 Aug 2015 21:13:28 -0700 Subject: [PATCH 2/5] improve help by including parent command for subcommands --- command.go | 4 ++++ help.go | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/command.go b/command.go index 5b6cd66..d03b7e7 100644 --- a/command.go +++ b/command.go @@ -39,6 +39,9 @@ type Command struct { // Boolean to hide built-in help command HideHelp bool + // Name of parent command for help, defaults to app.Name and parent + // command.Name. + ParentName string commandNamePath []string } @@ -196,6 +199,7 @@ func (c Command) startApp(ctx *Context) error { var newCmds []Command for _, cc := range app.Commands { + cc.ParentName = app.Name cc.commandNamePath = []string{c.Name, cc.Name} newCmds = append(newCmds, cc) } diff --git a/help.go b/help.go index 0855eb6..ff739ff 100644 --- a/help.go +++ b/help.go @@ -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}} + {{.ParentName}} {{.Name}} - {{.Usage}} USAGE: - command {{.FullName}}{{if .Flags}} [command options]{{end}} {{.ArgsUsage}}{{if .Description}} + {{.ParentName}} {{.Name}}{{if .Flags}} [command options]{{end}} {{.ArgsUsage}}{{if .Description}} DESCRIPTION: {{.Description}}{{end}}{{if .Flags}} From aced6e8739166ac46d05f741f60891d3cf2331e9 Mon Sep 17 00:00:00 2001 From: Tristan Zajonc Date: Wed, 12 Aug 2015 21:43:14 -0700 Subject: [PATCH 3/5] fix tests --- app.go | 18 +++++++++++++++++- app_test.go | 8 ++++---- command.go | 10 +++------- help.go | 10 +++++----- 4 files changed, 29 insertions(+), 17 deletions(-) diff --git a/app.go b/app.go index aadbea6..7da79d8 100644 --- a/app.go +++ b/app.go @@ -13,6 +13,8 @@ import ( type App struct { // The name of the program. Defaults to os.Args[0] Name string + // Name of command for help, defaults to Name + HelpName string // Description of the program. Usage string // Description of the program argument format. @@ -69,8 +71,8 @@ func compileTime() time.Time { func NewApp() *App { return &App{ Name: os.Args[0], + HelpName: os.Args[0], Usage: "A new cli application", - ArgsUsage: "[arguments...]", Version: "0.0.0", BashComplete: DefaultAppComplete, Action: helpCommand.Action, @@ -85,6 +87,13 @@ 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 { + 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) @@ -188,6 +197,13 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { } } + newCmds := []Command{} + for _, c := range a.Commands { + 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..2b53b4f 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,7 +737,7 @@ 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", @@ -755,7 +755,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...]") { diff --git a/command.go b/command.go index d03b7e7..2851291 100644 --- a/command.go +++ b/command.go @@ -39,9 +39,8 @@ type Command struct { // Boolean to hide built-in help command HideHelp bool - // Name of parent command for help, defaults to app.Name and parent - // command.Name. - ParentName string + // Name of command for help, defaults to full command name + HelpName string commandNamePath []string } @@ -158,14 +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) + app.HelpName = fmt.Sprintf("%s %s", ctx.App.Name, c.Name) if c.Description != "" { app.Usage = c.Description } else { app.Usage = c.Usage } - if c.ArgsUsage == "" { - c.ArgsUsage = "[arguments...]" - } // set CommandNotFound app.CommandNotFound = ctx.App.CommandNotFound @@ -199,7 +196,6 @@ func (c Command) startApp(ctx *Context) error { var newCmds []Command for _, cc := range app.Commands { - cc.ParentName = app.Name cc.commandNamePath = []string{c.Name, cc.Name} newCmds = append(newCmds, cc) } diff --git a/help.go b/help.go index ff739ff..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}} {{.ArgsUsage}} + {{.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: - {{.ParentName}} {{.Name}} - {{.Usage}} + {{.HelpName}} - {{.Usage}} USAGE: - {{.ParentName}} {{.Name}}{{if .Flags}} [command options]{{end}} {{.ArgsUsage}}{{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}} {{.ArgsUsage}} + {{.HelpName}} command{{if .Flags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}} COMMANDS: {{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}} From cc46ca102013a8010e23ce62ee94986e40a48c3c Mon Sep 17 00:00:00 2001 From: Tristan Zajonc Date: Wed, 12 Aug 2015 21:58:25 -0700 Subject: [PATCH 4/5] allow overriding help name --- app.go | 8 ++++++-- command.go | 7 ++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/app.go b/app.go index 7da79d8..26dfd7d 100644 --- a/app.go +++ b/app.go @@ -89,7 +89,9 @@ func (a *App) Run(arguments []string) (err error) { newCmds := []Command{} for _, c := range a.Commands { - c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name) + if c.HelpName == "" { + c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name) + } newCmds = append(newCmds, c) } a.Commands = newCmds @@ -199,7 +201,9 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { newCmds := []Command{} for _, c := range a.Commands { - c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name) + if c.HelpName == "" { + c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name) + } newCmds = append(newCmds, c) } a.Commands = newCmds diff --git a/command.go b/command.go index 2851291..9491c30 100644 --- a/command.go +++ b/command.go @@ -157,7 +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) - app.HelpName = 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 { From c7aac252f1b9115886ff47f2ffbba0db3d693d99 Mon Sep 17 00:00:00 2001 From: Tristan Zajonc Date: Wed, 12 Aug 2015 22:14:26 -0700 Subject: [PATCH 5/5] add tests --- app.go | 2 +- app_test.go | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++ command.go | 2 +- 3 files changed, 95 insertions(+), 2 deletions(-) diff --git a/app.go b/app.go index 26dfd7d..4d40014 100644 --- a/app.go +++ b/app.go @@ -13,7 +13,7 @@ import ( type App struct { // The name of the program. Defaults to os.Args[0] Name string - // Name of command for help, defaults to Name + // Full name of command for help, defaults to Name HelpName string // Description of the program. Usage string diff --git a/app_test.go b/app_test.go index 2b53b4f..ada5d69 100644 --- a/app_test.go +++ b/app_test.go @@ -763,6 +763,99 @@ func TestApp_Run_SubcommandFullPath(t *testing.T) { } } +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}, + } + 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_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 9491c30..fac754d 100644 --- a/command.go +++ b/command.go @@ -39,7 +39,7 @@ type Command struct { // Boolean to hide built-in help command HideHelp bool - // Name of command for help, defaults to full command name + // Full name of command for help, defaults to full command name, including parent commands. HelpName string commandNamePath []string }