diff --git a/.travis.yml b/.travis.yml index a6a1386..94836d7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,7 @@ go: - 1.4.2 - 1.5.x - 1.6.x +- 1.7.x - master matrix: @@ -20,6 +21,8 @@ matrix: include: - go: 1.6.x os: osx + - go: 1.7.x + os: osx before_script: - go get github.com/urfave/gfmrun/... || true diff --git a/README.md b/README.md index a1e537c..bb5f61e 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ applications in an expressive way. * [Flags](#flags) + [Placeholder Values](#placeholder-values) + [Alternate Names](#alternate-names) + + [Ordering](#ordering) + [Values from the Environment](#values-from-the-environment) + [Values from alternate input sources (YAML, TOML, and others)](#values-from-alternate-input-sources-yaml-toml-and-others) * [Subcommands](#subcommands) @@ -450,6 +451,56 @@ That flag can then be set with `--lang spanish` or `-l spanish`. Note that giving two different forms of the same flag in the same command invocation is an error. +#### Ordering + +Flags for the application and commands are shown in the order they are defined. +However, it's possible to sort them from outside this library by using `FlagsByName` +with `sort`. + +For example this: + + +``` go +package main + +import ( + "os" + "sort" + + "github.com/urfave/cli" +) + +func main() { + app := cli.NewApp() + + app.Flags = []cli.Flag { + cli.StringFlag{ + Name: "lang, l", + Value: "english", + Usage: "Language for the greeting", + }, + cli.StringFlag{ + Name: "config, c", + Usage: "Load configuration from `FILE`", + }, + } + + sort.Sort(cli.FlagsByName(app.Flags)) + + app.Run(os.Args) +} +``` + +Will result in help output like: + +``` +--config FILE, -c FILE Load configuration from FILE +--lang value, -l value Language for the greeting (default: "english") +``` + #### Values from the Environment You can also have the default value set from the environment via `EnvVar`. e.g. diff --git a/app.go b/app.go index c7e2594..dd2d2df 100644 --- a/app.go +++ b/app.go @@ -42,6 +42,8 @@ type App struct { ArgsUsage string // Version of the program Version string + // Description of the program + Description string // List of commands to execute Commands []Command // List of flags to parse @@ -198,7 +200,7 @@ func (a *App) Run(arguments []string) (err error) { HandleExitCoder(err) return err } - fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.") + fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) ShowAppHelp(context) return err } @@ -319,7 +321,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { HandleExitCoder(err) return err } - fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.") + fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) ShowSubcommandHelp(context) return err } @@ -460,10 +462,10 @@ type Author struct { func (a Author) String() string { e := "" if a.Email != "" { - e = "<" + a.Email + "> " + e = " <" + a.Email + ">" } - return fmt.Sprintf("%v %v", a.Name, e) + return fmt.Sprintf("%v%v", a.Name, e) } // HandleAction uses ✧✧✧reflection✧✧✧ to figure out if the given Action is an diff --git a/app_test.go b/app_test.go index 59fea69..e36a8c2 100644 --- a/app_test.go +++ b/app_test.go @@ -90,7 +90,62 @@ func ExampleApp_Run_subcommand() { // Hello, Jeremy } -func ExampleApp_Run_help() { +func ExampleApp_Run_appHelp() { + // set args for examples sake + os.Args = []string{"greet", "help"} + + app := NewApp() + app.Name = "greet" + app.Version = "0.1.0" + app.Description = "This is how we describe greet the app" + app.Authors = []Author{ + {Name: "Harrison", Email: "harrison@lolwut.com"}, + {Name: "Oliver Allen", Email: "oliver@toyshop.com"}, + } + app.Flags = []Flag{ + StringFlag{Name: "name", Value: "bob", Usage: "a name to say"}, + } + app.Commands = []Command{ + { + Name: "describeit", + Aliases: []string{"d"}, + Usage: "use it to see a description", + Description: "This is how we describe describeit the function", + Action: func(c *Context) error { + fmt.Printf("i like to describe things") + return nil + }, + }, + } + app.Run(os.Args) + // Output: + // NAME: + // greet - A new cli application + // + // USAGE: + // greet [global options] command [command options] [arguments...] + // + // VERSION: + // 0.1.0 + // + // DESCRIPTION: + // This is how we describe greet the app + // + // AUTHORS: + // Harrison + // Oliver Allen + // + // COMMANDS: + // describeit, d use it to see a description + // help, h Shows a list of commands or help for one command + // + // GLOBAL OPTIONS: + // --name value a name to say (default: "bob") + // --help, -h show help + // --version, -v print the version +} + +func ExampleApp_Run_commandHelp() { // set args for examples sake os.Args = []string{"greet", "h", "describeit"} @@ -258,6 +313,23 @@ func TestApp_RunAsSubcommandParseFlags(t *testing.T) { expect(t, context.String("lang"), "spanish") } +func TestApp_RunAsSubCommandIncorrectUsage(t *testing.T) { + a := App{ + Flags: []Flag{ + StringFlag{Name: "--foo"}, + }, + Writer: bytes.NewBufferString(""), + } + + set := flag.NewFlagSet("", flag.ContinueOnError) + set.Parse([]string{"", "---foo"}) + c := &Context{flagSet: set} + + err := a.RunAsSubcommand(c) + + expect(t, err, errors.New("bad flag syntax: ---foo")) +} + func TestApp_CommandWithFlagBeforeTerminator(t *testing.T) { var parsedOption string var args []string diff --git a/command.go b/command.go index 96253b6..d955249 100644 --- a/command.go +++ b/command.go @@ -138,7 +138,7 @@ func (c Command) Run(ctx *Context) (err error) { HandleExitCoder(err) return err } - fmt.Fprintln(ctx.App.Writer, "Incorrect Usage.") + fmt.Fprintln(ctx.App.Writer, "Incorrect Usage:", err.Error()) fmt.Fprintln(ctx.App.Writer) ShowCommandHelp(ctx, c.Name) return err diff --git a/flag.go b/flag.go index e748c02..1ff28d3 100644 --- a/flag.go +++ b/flag.go @@ -37,6 +37,21 @@ var HelpFlag = BoolFlag{ // to display a flag. var FlagStringer FlagStringFunc = stringifyFlag +// FlagsByName is a slice of Flag. +type FlagsByName []Flag + +func (f FlagsByName) Len() int { + return len(f) +} + +func (f FlagsByName) Less(i, j int) bool { + return f[i].GetName() < f[j].GetName() +} + +func (f FlagsByName) Swap(i, j int) { + f[i], f[j] = f[j], f[i] +} + // Flag is a common interface related to parsing flags in cli. // For more advanced flag parsing techniques, it is recommended that // this interface be implemented. diff --git a/help.go b/help.go index ba34719..515f744 100644 --- a/help.go +++ b/help.go @@ -16,24 +16,28 @@ var AppHelpTemplate = `NAME: {{.Name}} - {{.Usage}} USAGE: - {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}} - {{if .Version}}{{if not .HideVersion}} + {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} + VERSION: - {{.Version}} - {{end}}{{end}}{{if len .Authors}} -AUTHOR(S): - {{range .Authors}}{{.}}{{end}} - {{end}}{{if .VisibleCommands}} + {{.Version}}{{end}}{{end}}{{if .Description}} + +DESCRIPTION: + {{.Description}}{{end}}{{if len .Authors}} + +AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: + {{range $index, $author := .Authors}}{{if $index}} + {{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}} + COMMANDS:{{range .VisibleCategories}}{{if .Name}} {{.Name}}:{{end}}{{range .VisibleCommands}} - {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}} -{{end}}{{end}}{{if .VisibleFlags}} + {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} + GLOBAL OPTIONS: - {{range .VisibleFlags}}{{.}} - {{end}}{{end}}{{if .Copyright}} + {{range $index, $option := .VisibleFlags}}{{if $index}} + {{end}}{{$option}}{{end}}{{end}}{{if .Copyright}} + COPYRIGHT: - {{.Copyright}} - {{end}} + {{.Copyright}}{{end}} ` // CommandHelpTemplate is the text template for the command help topic.