Merge remote-tracking branch 'origin/master' into v2

This commit is contained in:
Dan Buch 2016-05-09 11:18:21 -04:00
commit 32ebecfbb5
No known key found for this signature in database
GPG Key ID: FAEF12936DD3E3EC
13 changed files with 151 additions and 109 deletions

View File

@ -19,6 +19,8 @@
for this reordering. for this reordering.
## [Unreleased] - (1.x series) ## [Unreleased] - (1.x series)
## [1.17.0] - 2016-05-09
### Added ### Added
- Pluggable flag-level help text rendering via `cli.DefaultFlagStringFunc` - Pluggable flag-level help text rendering via `cli.DefaultFlagStringFunc`
- `context.GlobalBoolT` was added as an analogue to `context.GlobalBool` - `context.GlobalBoolT` was added as an analogue to `context.GlobalBool`
@ -36,6 +38,8 @@
(previously they printed "No help topic for...", but still exited 0. This (previously they printed "No help topic for...", but still exited 0. This
makes it easier to script around apps built using `cli` since they can trust makes it easier to script around apps built using `cli` since they can trust
that a 0 exit code indicated a successful execution. that a 0 exit code indicated a successful execution.
- cleanups based on [Go Report Card
feedback](https://goreportcard.com/report/github.com/codegangsta/cli)
## [1.16.0] - 2016-05-02 ## [1.16.0] - 2016-05-02
### Added ### Added
@ -295,7 +299,8 @@ signature of `func(*cli.Context) error`, as defined by `cli.ActionFunc`.
### Added ### Added
- Initial implementation. - Initial implementation.
[Unreleased]: https://github.com/codegangsta/cli/compare/v1.16.0...HEAD [Unreleased]: https://github.com/codegangsta/cli/compare/v1.17.0...HEAD
[1.17.0]: https://github.com/codegangsta/cli/compare/v1.16.0...v1.17.0
[1.16.0]: https://github.com/codegangsta/cli/compare/v1.15.0...v1.16.0 [1.16.0]: https://github.com/codegangsta/cli/compare/v1.15.0...v1.16.0
[1.15.0]: https://github.com/codegangsta/cli/compare/v1.14.0...v1.15.0 [1.15.0]: https://github.com/codegangsta/cli/compare/v1.14.0...v1.15.0
[1.14.0]: https://github.com/codegangsta/cli/compare/v1.13.0...v1.14.0 [1.14.0]: https://github.com/codegangsta/cli/compare/v1.13.0...v1.14.0

27
app.go
View File

@ -51,7 +51,7 @@ type App struct {
HideHelp bool HideHelp bool
// Boolean to hide built-in version flag and the VERSION section of help // Boolean to hide built-in version flag and the VERSION section of help
HideVersion bool HideVersion bool
// Populate on app startup, only gettable throught method Categories() // Populate on app startup, only gettable through method Categories()
categories CommandCategories categories CommandCategories
// An action to execute when the bash-completion flag is set // An action to execute when the bash-completion flag is set
BashComplete BashCompleteFunc BashComplete BashCompleteFunc
@ -100,7 +100,8 @@ func compileTime() time.Time {
return info.ModTime() return info.ModTime()
} }
// Creates a new cli Application with some reasonable defaults for Name, Usage, Version and Action. // NewApp creates a new cli Application with some reasonable defaults for Name,
// Usage, Version and Action.
func NewApp() *App { func NewApp() *App {
return &App{ return &App{
Name: filepath.Base(os.Args[0]), Name: filepath.Base(os.Args[0]),
@ -162,7 +163,8 @@ func (a *App) Setup() {
} }
} }
// Entry point to the cli app. Parses the arguments slice and routes to the proper flag/args combination // Run is the entry point to the cli app. Parses the arguments slice and routes
// to the proper flag/args combination
func (a *App) Run(arguments []string) (err error) { func (a *App) Run(arguments []string) (err error) {
a.Setup() a.Setup()
@ -187,11 +189,10 @@ func (a *App) Run(arguments []string) (err error) {
err := a.OnUsageError(context, err, false) err := a.OnUsageError(context, err, false)
HandleExitCoder(err) HandleExitCoder(err)
return err return err
} else {
fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.")
ShowAppHelp(context)
return err
} }
fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.")
ShowAppHelp(context)
return err
} }
if !a.HideHelp && checkHelp(context) { if !a.HideHelp && checkHelp(context) {
@ -254,7 +255,8 @@ func (a *App) RunAndExitOnError() {
} }
} }
// Invokes the subcommand given the context, parses ctx.Args() to generate command-specific flags // RunAsSubcommand invokes the subcommand given the context, parses ctx.Args() to
// generate command-specific flags
func (a *App) RunAsSubcommand(ctx *Context) (err error) { func (a *App) RunAsSubcommand(ctx *Context) (err error) {
// append help to commands // append help to commands
if len(a.Commands) > 0 { if len(a.Commands) > 0 {
@ -307,11 +309,10 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
err = a.OnUsageError(context, err, true) err = a.OnUsageError(context, err, true)
HandleExitCoder(err) HandleExitCoder(err)
return err return err
} else {
fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.")
ShowSubcommandHelp(context)
return err
} }
fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.")
ShowSubcommandHelp(context)
return err
} }
if len(a.Commands) > 0 { if len(a.Commands) > 0 {
@ -363,7 +364,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
return err return err
} }
// Returns the named command on App. Returns nil if the command does not exist // Command 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 {
if c.HasName(name) { if c.HasName(name) {

View File

@ -33,7 +33,7 @@ func ExampleApp_Run() {
app.UsageText = "app [first_arg] [second_arg]" app.UsageText = "app [first_arg] [second_arg]"
app.Author = "Harrison" app.Author = "Harrison"
app.Email = "harrison@lolwut.com" app.Email = "harrison@lolwut.com"
app.Authors = []Author{Author{Name: "Oliver Allen", Email: "oliver@toyshop.com"}} app.Authors = []Author{{Name: "Oliver Allen", Email: "oliver@toyshop.com"}}
app.Run(os.Args) app.Run(os.Args)
// Output: // Output:
// Hello Jeremy // Hello Jeremy
@ -284,12 +284,12 @@ func TestApp_CommandWithNoFlagBeforeTerminator(t *testing.T) {
func TestApp_VisibleCommands(t *testing.T) { func TestApp_VisibleCommands(t *testing.T) {
app := NewApp() app := NewApp()
app.Commands = []Command{ app.Commands = []Command{
Command{ {
Name: "frob", Name: "frob",
HelpName: "foo frob", HelpName: "foo frob",
Action: func(_ *Context) error { return nil }, Action: func(_ *Context) error { return nil },
}, },
Command{ {
Name: "frib", Name: "frib",
HelpName: "foo frib", HelpName: "foo frib",
Hidden: true, Hidden: true,
@ -494,7 +494,7 @@ func TestApp_BeforeFunc(t *testing.T) {
} }
app.Commands = []Command{ app.Commands = []Command{
Command{ {
Name: "sub", Name: "sub",
Action: func(c *Context) error { Action: func(c *Context) error {
counts.Total++ counts.Total++
@ -586,7 +586,7 @@ func TestApp_AfterFunc(t *testing.T) {
} }
app.Commands = []Command{ app.Commands = []Command{
Command{ {
Name: "sub", Name: "sub",
Action: func(c *Context) error { Action: func(c *Context) error {
counts.Total++ counts.Total++
@ -701,7 +701,7 @@ func TestApp_CommandNotFound(t *testing.T) {
} }
app.Commands = []Command{ app.Commands = []Command{
Command{ {
Name: "bar", Name: "bar",
Action: func(c *Context) error { Action: func(c *Context) error {
counts.Total++ counts.Total++
@ -768,7 +768,7 @@ func TestApp_OrderOfOperations(t *testing.T) {
app.After = afterNoError app.After = afterNoError
app.Commands = []Command{ app.Commands = []Command{
Command{ {
Name: "bar", Name: "bar",
Action: func(c *Context) error { Action: func(c *Context) error {
counts.Total++ counts.Total++
@ -1103,15 +1103,15 @@ func TestApp_Run_Categories(t *testing.T) {
app := NewApp() app := NewApp()
app.Name = "categories" app.Name = "categories"
app.Commands = []Command{ app.Commands = []Command{
Command{ {
Name: "command1", Name: "command1",
Category: "1", Category: "1",
}, },
Command{ {
Name: "command2", Name: "command2",
Category: "1", Category: "1",
}, },
Command{ {
Name: "command3", Name: "command3",
Category: "2", Category: "2",
}, },
@ -1152,18 +1152,18 @@ func TestApp_VisibleCategories(t *testing.T) {
app := NewApp() app := NewApp()
app.Name = "visible-categories" app.Name = "visible-categories"
app.Commands = []Command{ app.Commands = []Command{
Command{ {
Name: "command1", Name: "command1",
Category: "1", Category: "1",
HelpName: "foo command1", HelpName: "foo command1",
Hidden: true, Hidden: true,
}, },
Command{ {
Name: "command2", Name: "command2",
Category: "2", Category: "2",
HelpName: "foo command2", HelpName: "foo command2",
}, },
Command{ {
Name: "command3", Name: "command3",
Category: "3", Category: "3",
HelpName: "foo command3", HelpName: "foo command3",
@ -1171,13 +1171,13 @@ func TestApp_VisibleCategories(t *testing.T) {
} }
expected := []*CommandCategory{ expected := []*CommandCategory{
&CommandCategory{ {
Name: "2", Name: "2",
Commands: []Command{ Commands: []Command{
app.Commands[1], app.Commands[1],
}, },
}, },
&CommandCategory{ {
Name: "3", Name: "3",
Commands: []Command{ Commands: []Command{
app.Commands[2], app.Commands[2],
@ -1191,19 +1191,19 @@ func TestApp_VisibleCategories(t *testing.T) {
app = NewApp() app = NewApp()
app.Name = "visible-categories" app.Name = "visible-categories"
app.Commands = []Command{ app.Commands = []Command{
Command{ {
Name: "command1", Name: "command1",
Category: "1", Category: "1",
HelpName: "foo command1", HelpName: "foo command1",
Hidden: true, Hidden: true,
}, },
Command{ {
Name: "command2", Name: "command2",
Category: "2", Category: "2",
HelpName: "foo command2", HelpName: "foo command2",
Hidden: true, Hidden: true,
}, },
Command{ {
Name: "command3", Name: "command3",
Category: "3", Category: "3",
HelpName: "foo command3", HelpName: "foo command3",
@ -1211,7 +1211,7 @@ func TestApp_VisibleCategories(t *testing.T) {
} }
expected = []*CommandCategory{ expected = []*CommandCategory{
&CommandCategory{ {
Name: "3", Name: "3",
Commands: []Command{ Commands: []Command{
app.Commands[2], app.Commands[2],
@ -1225,19 +1225,19 @@ func TestApp_VisibleCategories(t *testing.T) {
app = NewApp() app = NewApp()
app.Name = "visible-categories" app.Name = "visible-categories"
app.Commands = []Command{ app.Commands = []Command{
Command{ {
Name: "command1", Name: "command1",
Category: "1", Category: "1",
HelpName: "foo command1", HelpName: "foo command1",
Hidden: true, Hidden: true,
}, },
Command{ {
Name: "command2", Name: "command2",
Category: "2", Category: "2",
HelpName: "foo command2", HelpName: "foo command2",
Hidden: true, Hidden: true,
}, },
Command{ {
Name: "command3", Name: "command3",
Category: "3", Category: "3",
HelpName: "foo command3", HelpName: "foo command3",
@ -1273,9 +1273,9 @@ func TestApp_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) {
func TestApp_Run_SubcommandDoesNotOverwriteErrorFromBefore(t *testing.T) { func TestApp_Run_SubcommandDoesNotOverwriteErrorFromBefore(t *testing.T) {
app := NewApp() app := NewApp()
app.Commands = []Command{ app.Commands = []Command{
Command{ {
Subcommands: []Command{ Subcommands: []Command{
Command{ {
Name: "sub", Name: "sub",
}, },
}, },
@ -1313,7 +1313,7 @@ func TestApp_OnUsageError_WithWrongFlagValue(t *testing.T) {
return errors.New("intercepted: " + err.Error()) return errors.New("intercepted: " + err.Error())
} }
app.Commands = []Command{ app.Commands = []Command{
Command{ {
Name: "bar", Name: "bar",
}, },
} }
@ -1343,7 +1343,7 @@ func TestApp_OnUsageError_WithWrongFlagValue_ForSubcommand(t *testing.T) {
return errors.New("intercepted: " + err.Error()) return errors.New("intercepted: " + err.Error())
} }
app.Commands = []Command{ app.Commands = []Command{
Command{ {
Name: "bar", Name: "bar",
}, },
} }

View File

@ -1,7 +1,9 @@
package cli package cli
// CommandCategories is a slice of *CommandCategory.
type CommandCategories []*CommandCategory type CommandCategories []*CommandCategory
// CommandCategory is a category containing commands.
type CommandCategory struct { type CommandCategory struct {
Name string Name string
Commands Commands Commands Commands
@ -19,6 +21,7 @@ func (c CommandCategories) Swap(i, j int) {
c[i], c[j] = c[j], c[i] c[i], c[j] = c[j], c[i]
} }
// AddCommand adds a command to a category.
func (c CommandCategories) AddCommand(category string, command Command) CommandCategories { func (c CommandCategories) AddCommand(category string, command Command) CommandCategories {
for _, commandCategory := range c { for _, commandCategory := range c {
if commandCategory.Name == category { if commandCategory.Name == category {

View File

@ -56,7 +56,7 @@ type Command struct {
commandNamePath []string commandNamePath []string
} }
// Returns the full name of the command. // FullName returns the full name of the command.
// For subcommands this ensures that parent commands are part of the command path // For subcommands this ensures that parent commands are part of the command path
func (c Command) FullName() string { func (c Command) FullName() string {
if c.commandNamePath == nil { if c.commandNamePath == nil {
@ -65,9 +65,10 @@ func (c Command) FullName() string {
return strings.Join(c.commandNamePath, " ") return strings.Join(c.commandNamePath, " ")
} }
// Commands is a slice of Command
type Commands []Command type Commands []Command
// Invokes the command given the context, parses ctx.Args() to generate command-specific flags // Run invokes the command given the context, parses ctx.Args() to generate command-specific flags
func (c Command) Run(ctx *Context) (err error) { func (c Command) Run(ctx *Context) (err error) {
if len(c.Subcommands) > 0 { if len(c.Subcommands) > 0 {
return c.startApp(ctx) return c.startApp(ctx)
@ -99,12 +100,11 @@ func (c Command) Run(ctx *Context) (err error) {
err := c.OnUsageError(ctx, err, false) err := c.OnUsageError(ctx, err, false)
HandleExitCoder(err) HandleExitCoder(err)
return err return err
} else {
fmt.Fprintln(ctx.App.Writer, "Incorrect Usage.")
fmt.Fprintln(ctx.App.Writer)
ShowCommandHelp(ctx, c.Name)
return err
} }
fmt.Fprintln(ctx.App.Writer, "Incorrect Usage.")
fmt.Fprintln(ctx.App.Writer)
ShowCommandHelp(ctx, c.Name)
return err
} }
nerr := normalizeFlags(c.Flags, set) nerr := normalizeFlags(c.Flags, set)
@ -159,6 +159,7 @@ func (c Command) Run(ctx *Context) (err error) {
return err return err
} }
// Names returns the names including short names and aliases.
func (c Command) Names() []string { func (c Command) Names() []string {
names := []string{c.Name} names := []string{c.Name}
@ -169,7 +170,7 @@ func (c Command) Names() []string {
return append(names, c.Aliases...) return append(names, c.Aliases...)
} }
// Returns true if Command.Name or Command.ShortName matches given name // HasName returns true if Command.Name or Command.ShortName matches given name
func (c Command) HasName(name string) bool { func (c Command) HasName(name string) bool {
for _, n := range c.Names() { for _, n := range c.Names() {
if n == name { if n == name {

View File

@ -49,7 +49,7 @@ func TestCommandFlagParsing(t *testing.T) {
func TestCommand_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) { func TestCommand_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) {
app := NewApp() app := NewApp()
app.Commands = []Command{ app.Commands = []Command{
Command{ {
Name: "bar", Name: "bar",
Before: func(c *Context) error { Before: func(c *Context) error {
return fmt.Errorf("before error") return fmt.Errorf("before error")
@ -76,7 +76,7 @@ func TestCommand_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) {
func TestCommand_OnUsageError_WithWrongFlagValue(t *testing.T) { func TestCommand_OnUsageError_WithWrongFlagValue(t *testing.T) {
app := NewApp() app := NewApp()
app.Commands = []Command{ app.Commands = []Command{
Command{ {
Name: "bar", Name: "bar",
Flags: []Flag{ Flags: []Flag{
IntFlag{Name: "flag"}, IntFlag{Name: "flag"},

View File

@ -21,57 +21,62 @@ type Context struct {
parentContext *Context parentContext *Context
} }
// Creates a new context. For use in when invoking an App or Command action. // NewContext creates a new context. For use in when invoking an App or Command action.
func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context { func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context {
return &Context{App: app, flagSet: set, parentContext: parentCtx} return &Context{App: app, flagSet: set, parentContext: parentCtx}
} }
// Looks up the value of a local int flag, returns 0 if no int flag exists // Int looks up the value of a local int flag, returns 0 if no int flag exists
func (c *Context) Int(name string) int { func (c *Context) Int(name string) int {
return lookupInt(name, c.flagSet) return lookupInt(name, c.flagSet)
} }
// Looks up the value of a local time.Duration flag, returns 0 if no time.Duration flag exists // Duration looks up the value of a local time.Duration flag, returns 0 if no
// time.Duration flag exists
func (c *Context) Duration(name string) time.Duration { func (c *Context) Duration(name string) time.Duration {
return lookupDuration(name, c.flagSet) return lookupDuration(name, c.flagSet)
} }
// Looks up the value of a local float64 flag, returns 0 if no float64 flag exists // Float64 looks up the value of a local float64 flag, returns 0 if no float64
// flag exists
func (c *Context) Float64(name string) float64 { func (c *Context) Float64(name string) float64 {
return lookupFloat64(name, c.flagSet) return lookupFloat64(name, c.flagSet)
} }
// Looks up the value of a local bool flag, returns false if no bool flag exists // Bool looks up the value of a local bool flag, returns false if no bool flag exists
func (c *Context) Bool(name string) bool { func (c *Context) Bool(name string) bool {
return lookupBool(name, c.flagSet) return lookupBool(name, c.flagSet)
} }
// Looks up the value of a local boolT flag, returns false if no bool flag exists // BoolT looks up the value of a local boolT flag, returns false if no bool flag exists
func (c *Context) BoolT(name string) bool { func (c *Context) BoolT(name string) bool {
return lookupBoolT(name, c.flagSet) return lookupBoolT(name, c.flagSet)
} }
// Looks up the value of a local string flag, returns "" if no string flag exists // String looks up the value of a local string flag, returns "" if no string flag exists
func (c *Context) String(name string) string { func (c *Context) String(name string) string {
return lookupString(name, c.flagSet) return lookupString(name, c.flagSet)
} }
// Looks up the value of a local string slice flag, returns nil if no string slice flag exists // StringSlice looks up the value of a local string slice flag, returns nil if no
// string slice flag exists
func (c *Context) StringSlice(name string) []string { func (c *Context) StringSlice(name string) []string {
return lookupStringSlice(name, c.flagSet) return lookupStringSlice(name, c.flagSet)
} }
// Looks up the value of a local int slice flag, returns nil if no int slice flag exists // IntSlice looks up the value of a local int slice flag, returns nil if no int
// slice flag exists
func (c *Context) IntSlice(name string) []int { func (c *Context) IntSlice(name string) []int {
return lookupIntSlice(name, c.flagSet) return lookupIntSlice(name, c.flagSet)
} }
// Looks up the value of a local generic flag, returns nil if no generic flag exists // Generic looks up the value of a local generic flag, returns nil if no generic
// flag exists
func (c *Context) Generic(name string) interface{} { func (c *Context) Generic(name string) interface{} {
return lookupGeneric(name, c.flagSet) return lookupGeneric(name, c.flagSet)
} }
// Looks up the value of a global int flag, returns 0 if no int flag exists // GlobalInt looks up the value of a global int flag, returns 0 if no int flag exists
func (c *Context) GlobalInt(name string) int { func (c *Context) GlobalInt(name string) int {
if fs := lookupGlobalFlagSet(name, c); fs != nil { if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupInt(name, fs) return lookupInt(name, fs)
@ -79,8 +84,8 @@ func (c *Context) GlobalInt(name string) int {
return 0 return 0
} }
// Looks up the value of a global float64 flag, returns float64(0) if no float64 // GlobalFloat64 looks up the value of a global float64 flag, returns float64(0)
// flag exists // if no float64 flag exists
func (c *Context) GlobalFloat64(name string) float64 { func (c *Context) GlobalFloat64(name string) float64 {
if fs := lookupGlobalFlagSet(name, c); fs != nil { if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupFloat64(name, fs) return lookupFloat64(name, fs)
@ -88,7 +93,8 @@ func (c *Context) GlobalFloat64(name string) float64 {
return float64(0) return float64(0)
} }
// Looks up the value of a global time.Duration flag, returns 0 if no time.Duration flag exists // GlobalDuration looks up the value of a global time.Duration flag, returns 0
// if no time.Duration flag exists
func (c *Context) GlobalDuration(name string) time.Duration { func (c *Context) GlobalDuration(name string) time.Duration {
if fs := lookupGlobalFlagSet(name, c); fs != nil { if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupDuration(name, fs) return lookupDuration(name, fs)
@ -96,7 +102,8 @@ func (c *Context) GlobalDuration(name string) time.Duration {
return 0 return 0
} }
// Looks up the value of a global bool flag, returns false if no bool flag exists // GlobalBool looks up the value of a global bool flag, returns false if no bool
// flag exists
func (c *Context) GlobalBool(name string) bool { func (c *Context) GlobalBool(name string) bool {
if fs := lookupGlobalFlagSet(name, c); fs != nil { if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupBool(name, fs) return lookupBool(name, fs)
@ -104,7 +111,8 @@ func (c *Context) GlobalBool(name string) bool {
return false return false
} }
// Looks up the value of a global bool flag, returns true if no bool flag exists // GlobalBoolT looks up the value of a global bool flag, returns true if no bool
// flag exists
func (c *Context) GlobalBoolT(name string) bool { func (c *Context) GlobalBoolT(name string) bool {
if fs := lookupGlobalFlagSet(name, c); fs != nil { if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupBoolT(name, fs) return lookupBoolT(name, fs)
@ -112,7 +120,8 @@ func (c *Context) GlobalBoolT(name string) bool {
return false return false
} }
// Looks up the value of a global string flag, returns "" if no string flag exists // GlobalString looks up the value of a global string flag, returns "" if no
// string flag exists
func (c *Context) GlobalString(name string) string { func (c *Context) GlobalString(name string) string {
if fs := lookupGlobalFlagSet(name, c); fs != nil { if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupString(name, fs) return lookupString(name, fs)
@ -120,7 +129,8 @@ func (c *Context) GlobalString(name string) string {
return "" return ""
} }
// Looks up the value of a global string slice flag, returns nil if no string slice flag exists // GlobalStringSlice looks up the value of a global string slice flag, returns
// nil if no string slice flag exists
func (c *Context) GlobalStringSlice(name string) []string { func (c *Context) GlobalStringSlice(name string) []string {
if fs := lookupGlobalFlagSet(name, c); fs != nil { if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupStringSlice(name, fs) return lookupStringSlice(name, fs)
@ -128,7 +138,8 @@ func (c *Context) GlobalStringSlice(name string) []string {
return nil return nil
} }
// Looks up the value of a global int slice flag, returns nil if no int slice flag exists // GlobalIntSlice looks up the value of a global int slice flag, returns nil if
// no int slice flag exists
func (c *Context) GlobalIntSlice(name string) []int { func (c *Context) GlobalIntSlice(name string) []int {
if fs := lookupGlobalFlagSet(name, c); fs != nil { if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupIntSlice(name, fs) return lookupIntSlice(name, fs)
@ -136,7 +147,8 @@ func (c *Context) GlobalIntSlice(name string) []int {
return nil return nil
} }
// Looks up the value of a global generic flag, returns nil if no generic flag exists // GlobalGeneric looks up the value of a global generic flag, returns nil if no
// generic flag exists
func (c *Context) GlobalGeneric(name string) interface{} { func (c *Context) GlobalGeneric(name string) interface{} {
if fs := lookupGlobalFlagSet(name, c); fs != nil { if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupGeneric(name, fs) return lookupGeneric(name, fs)
@ -144,7 +156,7 @@ func (c *Context) GlobalGeneric(name string) interface{} {
return nil return nil
} }
// Returns the number of flags set // NumFlags returns the number of flags set
func (c *Context) NumFlags() int { func (c *Context) NumFlags() int {
return c.flagSet.NFlag() return c.flagSet.NFlag()
} }
@ -159,7 +171,7 @@ func (c *Context) GlobalSet(name, value string) error {
return globalContext(c).flagSet.Set(name, value) return globalContext(c).flagSet.Set(name, value)
} }
// Determines if the flag was actually set // IsSet determines if the flag was actually set
func (c *Context) IsSet(name string) bool { func (c *Context) IsSet(name string) bool {
if c.setFlags == nil { if c.setFlags == nil {
c.setFlags = make(map[string]bool) c.setFlags = make(map[string]bool)
@ -170,7 +182,7 @@ func (c *Context) IsSet(name string) bool {
return c.setFlags[name] == true return c.setFlags[name] == true
} }
// Determines if the global flag was actually set // GlobalIsSet determines if the global flag was actually set
func (c *Context) GlobalIsSet(name string) bool { func (c *Context) GlobalIsSet(name string) bool {
if c.globalSetFlags == nil { if c.globalSetFlags == nil {
c.globalSetFlags = make(map[string]bool) c.globalSetFlags = make(map[string]bool)
@ -187,7 +199,7 @@ func (c *Context) GlobalIsSet(name string) bool {
return c.globalSetFlags[name] return c.globalSetFlags[name]
} }
// Returns a slice of flag names used in this context. // FlagNames returns a slice of flag names used in this context.
func (c *Context) FlagNames() (names []string) { func (c *Context) FlagNames() (names []string) {
for _, flag := range c.Command.Flags { for _, flag := range c.Command.Flags {
name := strings.Split(flag.GetName(), ",")[0] name := strings.Split(flag.GetName(), ",")[0]
@ -199,7 +211,7 @@ func (c *Context) FlagNames() (names []string) {
return return
} }
// Returns a slice of global flag names used by the app. // GlobalFlagNames returns a slice of global flag names used by the app.
func (c *Context) GlobalFlagNames() (names []string) { func (c *Context) GlobalFlagNames() (names []string) {
for _, flag := range c.App.Flags { for _, flag := range c.App.Flags {
name := strings.Split(flag.GetName(), ",")[0] name := strings.Split(flag.GetName(), ",")[0]
@ -211,25 +223,26 @@ func (c *Context) GlobalFlagNames() (names []string) {
return return
} }
// Returns the parent context, if any // Parent returns the parent context, if any
func (c *Context) Parent() *Context { func (c *Context) Parent() *Context {
return c.parentContext return c.parentContext
} }
// Args contains apps console arguments
type Args []string type Args []string
// Returns the command line arguments associated with the context. // Args returns the command line arguments associated with the context.
func (c *Context) Args() Args { func (c *Context) Args() Args {
args := Args(c.flagSet.Args()) args := Args(c.flagSet.Args())
return args return args
} }
// Returns the number of the command line arguments. // NArg returns the number of the command line arguments.
func (c *Context) NArg() int { func (c *Context) NArg() int {
return len(c.Args()) return len(c.Args())
} }
// Returns the nth argument, or else a blank string // Get returns the nth argument, or else a blank string
func (a Args) Get(n int) string { func (a Args) Get(n int) string {
if len(a) > n { if len(a) > n {
return a[n] return a[n]
@ -237,12 +250,12 @@ func (a Args) Get(n int) string {
return "" return ""
} }
// Returns the first argument, or else a blank string // First returns the first argument, or else a blank string
func (a Args) First() string { func (a Args) First() string {
return a.Get(0) return a.Get(0)
} }
// Return the rest of the arguments (not the first one) // Tail returns the rest of the arguments (not the first one)
// or else an empty string slice // or else an empty string slice
func (a Args) Tail() []string { func (a Args) Tail() []string {
if len(a) >= 2 { if len(a) >= 2 {
@ -251,12 +264,12 @@ func (a Args) Tail() []string {
return []string{} return []string{}
} }
// Checks if there are any arguments present // Present checks if there are any arguments present
func (a Args) Present() bool { func (a Args) Present() bool {
return len(a) != 0 return len(a) != 0
} }
// Swaps arguments at the given indexes // Swap swaps arguments at the given indexes
func (a Args) Swap(from, to int) error { func (a Args) Swap(from, to int) error {
if from >= len(a) || to >= len(a) { if from >= len(a) || to >= len(a) {
return errors.New("index out of range") return errors.New("index out of range")

View File

@ -199,7 +199,7 @@ func TestContext_GlobalFlagsInSubcommands(t *testing.T) {
} }
app.Commands = []Command{ app.Commands = []Command{
Command{ {
Name: "foo", Name: "foo",
Flags: []Flag{ Flags: []Flag{
BoolFlag{Name: "parent, p", Usage: "Parent flag"}, BoolFlag{Name: "parent, p", Usage: "Parent flag"},

View File

@ -7,20 +7,24 @@ import (
"strings" "strings"
) )
// OsExiter is the function used when the app exits. If not set defaults to os.Exit.
var OsExiter = os.Exit var OsExiter = os.Exit
// ErrWriter is used to write errors to the user. This can be anything // ErrWriter is used to write errors to the user. This can be anything
// implementing the io.Writer interface and defaults to os.Stderr. // implementing the io.Writer interface and defaults to os.Stderr.
var ErrWriter io.Writer = os.Stderr var ErrWriter io.Writer = os.Stderr
// MultiError is an error that wraps multiple errors.
type MultiError struct { type MultiError struct {
Errors []error Errors []error
} }
// NewMultiError creates a new MultiError. Pass in one or more errors.
func NewMultiError(err ...error) MultiError { func NewMultiError(err ...error) MultiError {
return MultiError{Errors: err} return MultiError{Errors: err}
} }
// Error implents the error interface.
func (m MultiError) Error() string { func (m MultiError) Error() string {
errs := make([]string, len(m.Errors)) errs := make([]string, len(m.Errors))
for i, err := range m.Errors { for i, err := range m.Errors {

21
flag.go
View File

@ -16,19 +16,19 @@ const defaultPlaceholder = "value"
var slPfx = fmt.Sprintf("sl:::%d:::", time.Now().UTC().UnixNano()) var slPfx = fmt.Sprintf("sl:::%d:::", time.Now().UTC().UnixNano())
// This flag enables bash-completion for all commands and subcommands // BashCompletionFlag enables bash-completion for all commands and subcommands
var BashCompletionFlag = BoolFlag{ var BashCompletionFlag = BoolFlag{
Name: "generate-bash-completion", Name: "generate-bash-completion",
Hidden: true, Hidden: true,
} }
// This flag prints the version for the application // VersionFlag prints the version for the application
var VersionFlag = BoolFlag{ var VersionFlag = BoolFlag{
Name: "version, v", Name: "version, v",
Usage: "print the version", Usage: "print the version",
} }
// This flag prints the help for all commands and subcommands // HelpFlag prints the help for all commands and subcommands
// Set to the zero value (BoolFlag{}) to disable flag -- keeps subcommand // Set to the zero value (BoolFlag{}) to disable flag -- keeps subcommand
// unless HideHelp is set to true) // unless HideHelp is set to true)
var HelpFlag = BoolFlag{ var HelpFlag = BoolFlag{
@ -36,6 +36,8 @@ var HelpFlag = BoolFlag{
Usage: "show help", Usage: "show help",
} }
// FlagStringer converts a flag definition to a string. This is used by help
// to display a flag.
var FlagStringer FlagStringFunc = stringifyFlag var FlagStringer FlagStringFunc = stringifyFlag
// Serializeder is used to circumvent the limitations of flag.FlagSet.Set // Serializeder is used to circumvent the limitations of flag.FlagSet.Set
@ -111,6 +113,7 @@ func (f GenericFlag) Apply(set *flag.FlagSet) {
}) })
} }
// GetName returns the name of a flag.
func (f GenericFlag) GetName() string { func (f GenericFlag) GetName() string {
return f.Name return f.Name
} }
@ -201,6 +204,7 @@ func (f StringSliceFlag) Apply(set *flag.FlagSet) {
}) })
} }
// GetName returns the name of a flag.
func (f StringSliceFlag) GetName() string { func (f StringSliceFlag) GetName() string {
return f.Name return f.Name
} }
@ -243,9 +247,9 @@ func (i *IntSlice) Set(value string) error {
tmp, err := strconv.Atoi(value) tmp, err := strconv.Atoi(value)
if err != nil { if err != nil {
return err return err
} else {
i.slice = append(i.slice, tmp)
} }
i.slice = append(i.slice, tmp)
return nil return nil
} }
@ -309,6 +313,7 @@ func (f IntSliceFlag) Apply(set *flag.FlagSet) {
}) })
} }
// GetName returns the name of the flag.
func (f IntSliceFlag) GetName() string { func (f IntSliceFlag) GetName() string {
return f.Name return f.Name
} }
@ -352,6 +357,7 @@ func (f BoolFlag) Apply(set *flag.FlagSet) {
}) })
} }
// GetName returns the name of the flag.
func (f BoolFlag) GetName() string { func (f BoolFlag) GetName() string {
return f.Name return f.Name
} }
@ -396,6 +402,7 @@ func (f BoolTFlag) Apply(set *flag.FlagSet) {
}) })
} }
// GetName returns the name of the flag.
func (f BoolTFlag) GetName() string { func (f BoolTFlag) GetName() string {
return f.Name return f.Name
} }
@ -436,6 +443,7 @@ func (f StringFlag) Apply(set *flag.FlagSet) {
}) })
} }
// GetName returns the name of the flag.
func (f StringFlag) GetName() string { func (f StringFlag) GetName() string {
return f.Name return f.Name
} }
@ -480,6 +488,7 @@ func (f IntFlag) Apply(set *flag.FlagSet) {
}) })
} }
// GetName returns the name of the flag.
func (f IntFlag) GetName() string { func (f IntFlag) GetName() string {
return f.Name return f.Name
} }
@ -524,6 +533,7 @@ func (f DurationFlag) Apply(set *flag.FlagSet) {
}) })
} }
// GetName returns the name of the flag.
func (f DurationFlag) GetName() string { func (f DurationFlag) GetName() string {
return f.Name return f.Name
} }
@ -567,6 +577,7 @@ func (f Float64Flag) Apply(set *flag.FlagSet) {
}) })
} }
// GetName returns the name of the flag.
func (f Float64Flag) GetName() string { func (f Float64Flag) GetName() string {
return f.Name return f.Name
} }

View File

@ -1,23 +1,23 @@
package cli package cli
// An action to execute when the bash-completion flag is set // BashCompleteFunc is an action to execute when the bash-completion flag is set
type BashCompleteFunc func(*Context) type BashCompleteFunc func(*Context)
// An action to execute before any subcommands are run, but after the context is ready // BeforeFunc is an action to execute before any subcommands are run, but after
// If a non-nil error is returned, no subcommands are run // the context is ready if a non-nil error is returned, no subcommands are run
type BeforeFunc func(*Context) error type BeforeFunc func(*Context) error
// An action to execute after any subcommands are run, but after the subcommand has finished // AfterFunc is an action to execute after any subcommands are run, but after the
// It is run even if Action() panics // subcommand has finished it is run even if Action() panics
type AfterFunc func(*Context) error type AfterFunc func(*Context) error
// The action to execute when no subcommands are specified // ActionFunc is the action to execute when no subcommands are specified
type ActionFunc func(*Context) error type ActionFunc func(*Context) error
// Execute this function if the proper command cannot be found // CommandNotFoundFunc is executed if the proper command cannot be found
type CommandNotFoundFunc func(*Context, string) type CommandNotFoundFunc func(*Context, string)
// Execute this function if an usage error occurs. This is useful for displaying // OnUsageErrorFunc is executed if an usage error occurs. This is useful for displaying
// customized usage error messages. This function is able to replace the // customized usage error messages. This function is able to replace the
// original error messages. If this function is not set, the "Incorrect usage" // original error messages. If this function is not set, the "Incorrect usage"
// is displayed and the execution is interrupted. // is displayed and the execution is interrupted.

24
help.go
View File

@ -9,7 +9,7 @@ import (
"text/template" "text/template"
) )
// The text template for the Default help topic. // AppHelpTemplate is the text template for the Default help topic.
// cli.go uses text/template to render templates. You can // cli.go uses text/template to render templates. You can
// render custom help text by setting this variable. // render custom help text by setting this variable.
var AppHelpTemplate = `NAME: var AppHelpTemplate = `NAME:
@ -36,7 +36,7 @@ COPYRIGHT:
{{end}} {{end}}
` `
// The text template for the command help topic. // CommandHelpTemplate is the text template for the command help topic.
// cli.go uses text/template to render templates. You can // cli.go uses text/template to render templates. You can
// render custom help text by setting this variable. // render custom help text by setting this variable.
var CommandHelpTemplate = `NAME: var CommandHelpTemplate = `NAME:
@ -56,7 +56,7 @@ OPTIONS:
{{end}}{{end}} {{end}}{{end}}
` `
// The text template for the subcommand help topic. // SubcommandHelpTemplate is the text template for the subcommand help topic.
// cli.go uses text/template to render templates. You can // cli.go uses text/template to render templates. You can
// render custom help text by setting this variable. // render custom help text by setting this variable.
var SubcommandHelpTemplate = `NAME: var SubcommandHelpTemplate = `NAME:
@ -108,16 +108,20 @@ var helpSubcommand = Command{
// Prints help for the App or Command // Prints help for the App or Command
type helpPrinter func(w io.Writer, templ string, data interface{}) type helpPrinter func(w io.Writer, templ string, data interface{})
// HelpPrinter is a function that writes the help output. If not set a default
// is used. The function signature is:
// func(w io.Writer, templ string, data interface{})
var HelpPrinter helpPrinter = printHelp var HelpPrinter helpPrinter = printHelp
// Prints version for the App // VersionPrinter prints the version for the App
var VersionPrinter = printVersion var VersionPrinter = printVersion
// ShowAppHelp is an action that displays the help.
func ShowAppHelp(c *Context) { func ShowAppHelp(c *Context) {
HelpPrinter(c.App.Writer, AppHelpTemplate, c.App) HelpPrinter(c.App.Writer, AppHelpTemplate, c.App)
} }
// Prints the list of subcommands as the default app completion method // DefaultAppComplete prints the list of subcommands as the default app completion method
func DefaultAppComplete(c *Context) { func DefaultAppComplete(c *Context) {
for _, command := range c.App.Commands { for _, command := range c.App.Commands {
if command.Hidden { if command.Hidden {
@ -129,7 +133,7 @@ func DefaultAppComplete(c *Context) {
} }
} }
// Prints help for the given command // ShowCommandHelp prints help for the given command
func ShowCommandHelp(ctx *Context, command string) error { func ShowCommandHelp(ctx *Context, command string) error {
// show the subcommand help for a command with subcommands // show the subcommand help for a command with subcommands
if command == "" { if command == "" {
@ -152,12 +156,12 @@ func ShowCommandHelp(ctx *Context, command string) error {
return nil return nil
} }
// Prints help for the given subcommand // ShowSubcommandHelp prints help for the given subcommand
func ShowSubcommandHelp(c *Context) error { func ShowSubcommandHelp(c *Context) error {
return ShowCommandHelp(c, c.Command.Name) return ShowCommandHelp(c, c.Command.Name)
} }
// Prints the version number of the App // ShowVersion prints the version number of the App
func ShowVersion(c *Context) { func ShowVersion(c *Context) {
VersionPrinter(c) VersionPrinter(c)
} }
@ -166,7 +170,7 @@ func printVersion(c *Context) {
fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version) fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version)
} }
// Prints the lists of commands within a given context // ShowCompletions prints the lists of commands within a given context
func ShowCompletions(c *Context) { func ShowCompletions(c *Context) {
a := c.App a := c.App
if a != nil && a.BashComplete != nil { if a != nil && a.BashComplete != nil {
@ -174,7 +178,7 @@ func ShowCompletions(c *Context) {
} }
} }
// Prints the custom completions for a given command // ShowCommandCompletions prints the custom completions for a given command
func ShowCommandCompletions(ctx *Context, command string) { func ShowCommandCompletions(ctx *Context, command string) {
c := ctx.App.Command(command) c := ctx.App.Command(command)
if c != nil && c.BashComplete != nil { if c != nil && c.BashComplete != nil {

View File

@ -172,13 +172,13 @@ func Test_helpSubcommand_Action_ErrorIfNoTopic(t *testing.T) {
func TestShowAppHelp_HiddenCommand(t *testing.T) { func TestShowAppHelp_HiddenCommand(t *testing.T) {
app := &App{ app := &App{
Commands: []Command{ Commands: []Command{
Command{ {
Name: "frobbly", Name: "frobbly",
Action: func(ctx *Context) error { Action: func(ctx *Context) error {
return nil return nil
}, },
}, },
Command{ {
Name: "secretfrob", Name: "secretfrob",
Hidden: true, Hidden: true,
Action: func(ctx *Context) error { Action: func(ctx *Context) error {