Merge commit '0d0d329' into v3-porting
This commit is contained in:
commit
581cfd175a
@ -46,7 +46,7 @@ func TestCommandJSONFileTest(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load"))
|
command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load"))
|
||||||
err := command.Run(c)
|
err := command.Run(c, test...)
|
||||||
|
|
||||||
expect(t, err, nil)
|
expect(t, err, nil)
|
||||||
}
|
}
|
||||||
@ -81,7 +81,7 @@ func TestCommandJSONFileTestGlobalEnvVarWins(t *testing.T) {
|
|||||||
}
|
}
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load"))
|
command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load"))
|
||||||
|
|
||||||
err := command.Run(c)
|
err := command.Run(c, test...)
|
||||||
|
|
||||||
expect(t, err, nil)
|
expect(t, err, nil)
|
||||||
}
|
}
|
||||||
@ -116,7 +116,7 @@ func TestCommandJSONFileTestGlobalEnvVarWinsNested(t *testing.T) {
|
|||||||
}
|
}
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load"))
|
command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load"))
|
||||||
|
|
||||||
err := command.Run(c)
|
err := command.Run(c, test...)
|
||||||
|
|
||||||
expect(t, err, nil)
|
expect(t, err, nil)
|
||||||
}
|
}
|
||||||
@ -148,7 +148,7 @@ func TestCommandJSONFileTestSpecifiedFlagWins(t *testing.T) {
|
|||||||
}
|
}
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load"))
|
command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load"))
|
||||||
|
|
||||||
err := command.Run(c)
|
err := command.Run(c, test...)
|
||||||
|
|
||||||
expect(t, err, nil)
|
expect(t, err, nil)
|
||||||
}
|
}
|
||||||
@ -180,7 +180,7 @@ func TestCommandJSONFileTestSpecifiedFlagWinsNested(t *testing.T) {
|
|||||||
}
|
}
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load"))
|
command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load"))
|
||||||
|
|
||||||
err := command.Run(c)
|
err := command.Run(c, test...)
|
||||||
|
|
||||||
expect(t, err, nil)
|
expect(t, err, nil)
|
||||||
}
|
}
|
||||||
@ -212,7 +212,7 @@ func TestCommandJSONFileTestDefaultValueFileWins(t *testing.T) {
|
|||||||
}
|
}
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load"))
|
command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load"))
|
||||||
|
|
||||||
err := command.Run(c)
|
err := command.Run(c, test...)
|
||||||
|
|
||||||
expect(t, err, nil)
|
expect(t, err, nil)
|
||||||
}
|
}
|
||||||
@ -244,7 +244,7 @@ func TestCommandJSONFileTestDefaultValueFileWinsNested(t *testing.T) {
|
|||||||
}
|
}
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load"))
|
command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load"))
|
||||||
|
|
||||||
err := command.Run(c)
|
err := command.Run(c, test...)
|
||||||
|
|
||||||
expect(t, err, nil)
|
expect(t, err, nil)
|
||||||
}
|
}
|
||||||
@ -278,7 +278,7 @@ func TestCommandJSONFileFlagHasDefaultGlobalEnvJSONSetGlobalEnvWins(t *testing.T
|
|||||||
&cli.StringFlag{Name: "load"}},
|
&cli.StringFlag{Name: "load"}},
|
||||||
}
|
}
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load"))
|
command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load"))
|
||||||
err := command.Run(c)
|
err := command.Run(c, test...)
|
||||||
|
|
||||||
expect(t, err, nil)
|
expect(t, err, nil)
|
||||||
}
|
}
|
||||||
@ -312,7 +312,7 @@ func TestCommandJSONFileFlagHasDefaultGlobalEnvJSONSetGlobalEnvWinsNested(t *tes
|
|||||||
&cli.StringFlag{Name: "load"}},
|
&cli.StringFlag{Name: "load"}},
|
||||||
}
|
}
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load"))
|
command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load"))
|
||||||
err := command.Run(c)
|
err := command.Run(c, test...)
|
||||||
|
|
||||||
expect(t, err, nil)
|
expect(t, err, nil)
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ func TestCommandTomFileTest(t *testing.T) {
|
|||||||
&cli.StringFlag{Name: "load"}},
|
&cli.StringFlag{Name: "load"}},
|
||||||
}
|
}
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load"))
|
command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load"))
|
||||||
err := command.Run(c)
|
err := command.Run(c, test...)
|
||||||
|
|
||||||
expect(t, err, nil)
|
expect(t, err, nil)
|
||||||
}
|
}
|
||||||
@ -68,7 +68,7 @@ func TestCommandTomlFileTestGlobalEnvVarWins(t *testing.T) {
|
|||||||
}
|
}
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load"))
|
command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load"))
|
||||||
|
|
||||||
err := command.Run(c)
|
err := command.Run(c, test...)
|
||||||
|
|
||||||
expect(t, err, nil)
|
expect(t, err, nil)
|
||||||
}
|
}
|
||||||
@ -102,7 +102,7 @@ func TestCommandTomlFileTestGlobalEnvVarWinsNested(t *testing.T) {
|
|||||||
}
|
}
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load"))
|
command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load"))
|
||||||
|
|
||||||
err := command.Run(c)
|
err := command.Run(c, test...)
|
||||||
|
|
||||||
expect(t, err, nil)
|
expect(t, err, nil)
|
||||||
}
|
}
|
||||||
@ -134,7 +134,7 @@ func TestCommandTomlFileTestSpecifiedFlagWins(t *testing.T) {
|
|||||||
}
|
}
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load"))
|
command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load"))
|
||||||
|
|
||||||
err := command.Run(c)
|
err := command.Run(c, test...)
|
||||||
|
|
||||||
expect(t, err, nil)
|
expect(t, err, nil)
|
||||||
}
|
}
|
||||||
@ -167,7 +167,7 @@ func TestCommandTomlFileTestSpecifiedFlagWinsNested(t *testing.T) {
|
|||||||
}
|
}
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load"))
|
command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load"))
|
||||||
|
|
||||||
err := command.Run(c)
|
err := command.Run(c, test...)
|
||||||
|
|
||||||
expect(t, err, nil)
|
expect(t, err, nil)
|
||||||
}
|
}
|
||||||
@ -199,7 +199,7 @@ func TestCommandTomlFileTestDefaultValueFileWins(t *testing.T) {
|
|||||||
}
|
}
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load"))
|
command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load"))
|
||||||
|
|
||||||
err := command.Run(c)
|
err := command.Run(c, test...)
|
||||||
|
|
||||||
expect(t, err, nil)
|
expect(t, err, nil)
|
||||||
}
|
}
|
||||||
@ -231,7 +231,7 @@ func TestCommandTomlFileTestDefaultValueFileWinsNested(t *testing.T) {
|
|||||||
}
|
}
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load"))
|
command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load"))
|
||||||
|
|
||||||
err := command.Run(c)
|
err := command.Run(c, test...)
|
||||||
|
|
||||||
expect(t, err, nil)
|
expect(t, err, nil)
|
||||||
}
|
}
|
||||||
@ -265,7 +265,7 @@ func TestCommandTomlFileFlagHasDefaultGlobalEnvTomlSetGlobalEnvWins(t *testing.T
|
|||||||
&cli.StringFlag{Name: "load"}},
|
&cli.StringFlag{Name: "load"}},
|
||||||
}
|
}
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load"))
|
command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load"))
|
||||||
err := command.Run(c)
|
err := command.Run(c, test...)
|
||||||
|
|
||||||
expect(t, err, nil)
|
expect(t, err, nil)
|
||||||
}
|
}
|
||||||
@ -299,7 +299,7 @@ func TestCommandTomlFileFlagHasDefaultGlobalEnvTomlSetGlobalEnvWinsNested(t *tes
|
|||||||
&cli.StringFlag{Name: "load"}},
|
&cli.StringFlag{Name: "load"}},
|
||||||
}
|
}
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load"))
|
command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load"))
|
||||||
err := command.Run(c)
|
err := command.Run(c, test...)
|
||||||
|
|
||||||
expect(t, err, nil)
|
expect(t, err, nil)
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ func TestCommandYamlFileTest(t *testing.T) {
|
|||||||
&cli.StringFlag{Name: "load"}},
|
&cli.StringFlag{Name: "load"}},
|
||||||
}
|
}
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
||||||
err := command.Run(c)
|
err := command.Run(c, test...)
|
||||||
|
|
||||||
expect(t, err, nil)
|
expect(t, err, nil)
|
||||||
}
|
}
|
||||||
@ -68,7 +68,7 @@ func TestCommandYamlFileTestGlobalEnvVarWins(t *testing.T) {
|
|||||||
}
|
}
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
||||||
|
|
||||||
err := command.Run(c)
|
err := command.Run(c, test...)
|
||||||
|
|
||||||
expect(t, err, nil)
|
expect(t, err, nil)
|
||||||
}
|
}
|
||||||
@ -103,7 +103,7 @@ func TestCommandYamlFileTestGlobalEnvVarWinsNested(t *testing.T) {
|
|||||||
}
|
}
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
||||||
|
|
||||||
err := command.Run(c)
|
err := command.Run(c, test...)
|
||||||
|
|
||||||
expect(t, err, nil)
|
expect(t, err, nil)
|
||||||
}
|
}
|
||||||
@ -135,7 +135,7 @@ func TestCommandYamlFileTestSpecifiedFlagWins(t *testing.T) {
|
|||||||
}
|
}
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
||||||
|
|
||||||
err := command.Run(c)
|
err := command.Run(c, test...)
|
||||||
|
|
||||||
expect(t, err, nil)
|
expect(t, err, nil)
|
||||||
}
|
}
|
||||||
@ -168,7 +168,7 @@ func TestCommandYamlFileTestSpecifiedFlagWinsNested(t *testing.T) {
|
|||||||
}
|
}
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
||||||
|
|
||||||
err := command.Run(c)
|
err := command.Run(c, test...)
|
||||||
|
|
||||||
expect(t, err, nil)
|
expect(t, err, nil)
|
||||||
}
|
}
|
||||||
@ -200,7 +200,7 @@ func TestCommandYamlFileTestDefaultValueFileWins(t *testing.T) {
|
|||||||
}
|
}
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
||||||
|
|
||||||
err := command.Run(c)
|
err := command.Run(c, test...)
|
||||||
|
|
||||||
expect(t, err, nil)
|
expect(t, err, nil)
|
||||||
}
|
}
|
||||||
@ -233,7 +233,7 @@ func TestCommandYamlFileTestDefaultValueFileWinsNested(t *testing.T) {
|
|||||||
}
|
}
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
||||||
|
|
||||||
err := command.Run(c)
|
err := command.Run(c, test...)
|
||||||
|
|
||||||
expect(t, err, nil)
|
expect(t, err, nil)
|
||||||
}
|
}
|
||||||
@ -267,7 +267,7 @@ func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWins(t *testing.T
|
|||||||
&cli.StringFlag{Name: "load"}},
|
&cli.StringFlag{Name: "load"}},
|
||||||
}
|
}
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
||||||
err := command.Run(c)
|
err := command.Run(c, test...)
|
||||||
|
|
||||||
expect(t, err, nil)
|
expect(t, err, nil)
|
||||||
}
|
}
|
||||||
@ -302,7 +302,7 @@ func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWinsNested(t *tes
|
|||||||
&cli.StringFlag{Name: "load"}},
|
&cli.StringFlag{Name: "load"}},
|
||||||
}
|
}
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
||||||
err := command.Run(c)
|
err := command.Run(c, test...)
|
||||||
|
|
||||||
expect(t, err, nil)
|
expect(t, err, nil)
|
||||||
}
|
}
|
||||||
|
288
app.go
288
app.go
@ -7,7 +7,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -112,6 +111,8 @@ type App struct {
|
|||||||
Suggest bool
|
Suggest bool
|
||||||
|
|
||||||
didSetup bool
|
didSetup bool
|
||||||
|
|
||||||
|
rootCommand *Command
|
||||||
}
|
}
|
||||||
|
|
||||||
type SuggestFlagFunc func(flags []Flag, provided string, hideHelp bool) string
|
type SuggestFlagFunc func(flags []Flag, provided string, hideHelp bool) string
|
||||||
@ -197,9 +198,11 @@ func (a *App) Setup() {
|
|||||||
var newCommands []*Command
|
var newCommands []*Command
|
||||||
|
|
||||||
for _, c := range a.Commands {
|
for _, c := range a.Commands {
|
||||||
if c.HelpName == "" {
|
cname := c.Name
|
||||||
c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
|
if c.HelpName != "" {
|
||||||
|
cname = c.HelpName
|
||||||
}
|
}
|
||||||
|
c.HelpName = fmt.Sprintf("%s %s", a.HelpName, cname)
|
||||||
|
|
||||||
c.flagCategories = newFlagCategoriesFromFlags(c.Flags)
|
c.flagCategories = newFlagCategoriesFromFlags(c.Flags)
|
||||||
newCommands = append(newCommands, c)
|
newCommands = append(newCommands, c)
|
||||||
@ -238,6 +241,31 @@ func (a *App) Setup() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) newRootCommand() *Command {
|
||||||
|
return &Command{
|
||||||
|
Name: a.Name,
|
||||||
|
Usage: a.Usage,
|
||||||
|
UsageText: a.UsageText,
|
||||||
|
Description: a.Description,
|
||||||
|
ArgsUsage: a.ArgsUsage,
|
||||||
|
BashComplete: a.BashComplete,
|
||||||
|
Before: a.Before,
|
||||||
|
After: a.After,
|
||||||
|
Action: a.Action,
|
||||||
|
OnUsageError: a.OnUsageError,
|
||||||
|
Subcommands: a.Commands,
|
||||||
|
Flags: a.Flags,
|
||||||
|
flagCategories: a.flagCategories,
|
||||||
|
HideHelp: a.HideHelp,
|
||||||
|
HideHelpCommand: a.HideHelpCommand,
|
||||||
|
UseShortOptionHandling: a.UseShortOptionHandling,
|
||||||
|
HelpName: a.HelpName,
|
||||||
|
CustomHelpTemplate: a.CustomAppHelpTemplate,
|
||||||
|
categories: a.categories,
|
||||||
|
isRoot: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (a *App) newFlagSet() (*flag.FlagSet, error) {
|
func (a *App) newFlagSet() (*flag.FlagSet, error) {
|
||||||
return flagSet(a.Name, a.Flags)
|
return flagSet(a.Name, a.Flags)
|
||||||
}
|
}
|
||||||
@ -266,136 +294,20 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) {
|
|||||||
// always appends the completion flag at the end of the command
|
// always appends the completion flag at the end of the command
|
||||||
shellComplete, arguments := checkShellCompleteFlag(a, arguments)
|
shellComplete, arguments := checkShellCompleteFlag(a, arguments)
|
||||||
|
|
||||||
set, err := a.newFlagSet()
|
cCtx := NewContext(a, nil, &Context{Context: ctx})
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = parseIter(set, a, arguments[1:], shellComplete)
|
|
||||||
nerr := normalizeFlags(a.Flags, set)
|
|
||||||
cCtx := NewContext(a, set, &Context{Context: ctx})
|
|
||||||
if nerr != nil {
|
|
||||||
_, _ = fmt.Fprintln(a.Writer, nerr)
|
|
||||||
if !a.HideHelp {
|
|
||||||
_ = ShowAppHelp(cCtx)
|
|
||||||
}
|
|
||||||
return nerr
|
|
||||||
}
|
|
||||||
cCtx.shellComplete = shellComplete
|
cCtx.shellComplete = shellComplete
|
||||||
|
|
||||||
if checkCompletions(cCtx) {
|
a.rootCommand = a.newRootCommand()
|
||||||
return nil
|
cCtx.Command = a.rootCommand
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
return a.rootCommand.Run(cCtx, arguments...)
|
||||||
if a.OnUsageError != nil {
|
}
|
||||||
err := a.OnUsageError(cCtx, err, false)
|
|
||||||
a.handleExitCoder(cCtx, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, _ = fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
|
|
||||||
if a.Suggest {
|
|
||||||
if suggestion, err := a.suggestFlagFromError(err, ""); err == nil {
|
|
||||||
fmt.Fprintf(a.Writer, suggestion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !a.HideHelp {
|
|
||||||
_ = ShowAppHelp(cCtx)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.After != nil && !cCtx.shellComplete {
|
// This is a stub function to keep public API unchanged from old code
|
||||||
defer func() {
|
//
|
||||||
if afterErr := a.After(cCtx); afterErr != nil {
|
// Deprecated: use App.Run or App.RunContext
|
||||||
if err != nil {
|
func (a *App) RunAsSubcommand(ctx *Context) (err error) {
|
||||||
err = newMultiError(err, afterErr)
|
return a.RunContext(ctx.Context, ctx.Args().Slice())
|
||||||
} else {
|
|
||||||
err = afterErr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.HideHelp && checkHelp(cCtx) {
|
|
||||||
_ = ShowAppHelp(cCtx)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.HideVersion && checkVersion(cCtx) {
|
|
||||||
ShowVersion(cCtx)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
cerr := cCtx.checkRequiredFlags(a.Flags)
|
|
||||||
if cerr != nil {
|
|
||||||
_ = ShowAppHelp(cCtx)
|
|
||||||
return cerr
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.Before != nil && !cCtx.shellComplete {
|
|
||||||
beforeErr := a.Before(cCtx)
|
|
||||||
if beforeErr != nil {
|
|
||||||
a.handleExitCoder(cCtx, beforeErr)
|
|
||||||
err = beforeErr
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = runFlagActions(cCtx, a.Flags); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var c *Command
|
|
||||||
args := cCtx.Args()
|
|
||||||
if args.Present() {
|
|
||||||
name := args.First()
|
|
||||||
if a.validCommandName(name) {
|
|
||||||
c = a.Command(name)
|
|
||||||
} else {
|
|
||||||
hasDefault := a.DefaultCommand != ""
|
|
||||||
isFlagName := checkStringSliceIncludes(name, cCtx.FlagNames())
|
|
||||||
|
|
||||||
var (
|
|
||||||
isDefaultSubcommand = false
|
|
||||||
defaultHasSubcommands = false
|
|
||||||
)
|
|
||||||
|
|
||||||
if hasDefault {
|
|
||||||
dc := a.Command(a.DefaultCommand)
|
|
||||||
defaultHasSubcommands = len(dc.Subcommands) > 0
|
|
||||||
for _, dcSub := range dc.Subcommands {
|
|
||||||
if checkStringSliceIncludes(name, dcSub.Names()) {
|
|
||||||
isDefaultSubcommand = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if isFlagName || (hasDefault && (defaultHasSubcommands && isDefaultSubcommand)) {
|
|
||||||
argsWithDefault := a.argsWithDefaultCommand(args)
|
|
||||||
if !reflect.DeepEqual(args, argsWithDefault) {
|
|
||||||
c = a.Command(argsWithDefault.First())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if a.DefaultCommand != "" {
|
|
||||||
c = a.Command(a.DefaultCommand)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c != nil {
|
|
||||||
return c.Run(cCtx)
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.Action == nil {
|
|
||||||
a.Action = helpCommand.Action
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run default Action
|
|
||||||
err = a.Action(cCtx)
|
|
||||||
|
|
||||||
a.handleExitCoder(cCtx, err)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) suggestFlagFromError(err error, command string) (string, error) {
|
func (a *App) suggestFlagFromError(err error, command string) (string, error) {
|
||||||
@ -405,15 +317,17 @@ func (a *App) suggestFlagFromError(err error, command string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
flags := a.Flags
|
flags := a.Flags
|
||||||
|
hideHelp := a.HideHelp
|
||||||
if command != "" {
|
if command != "" {
|
||||||
cmd := a.Command(command)
|
cmd := a.Command(command)
|
||||||
if cmd == nil {
|
if cmd == nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
flags = cmd.Flags
|
flags = cmd.Flags
|
||||||
|
hideHelp = hideHelp || cmd.HideHelp
|
||||||
}
|
}
|
||||||
|
|
||||||
suggestion := SuggestFlag(flags, flag, a.HideHelp)
|
suggestion := SuggestFlag(flags, flag, hideHelp)
|
||||||
if len(suggestion) == 0 {
|
if len(suggestion) == 0 {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -433,120 +347,6 @@ func (a *App) RunAndExitOnError() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunAsSubcommand invokes the subcommand given the context, parses ctx.Args() to
|
|
||||||
// generate command-specific flags
|
|
||||||
func (a *App) RunAsSubcommand(ctx *Context) (err error) {
|
|
||||||
// Setup also handles HideHelp and HideHelpCommand
|
|
||||||
a.Setup()
|
|
||||||
|
|
||||||
var 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
|
|
||||||
|
|
||||||
set, err := a.newFlagSet()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = parseIter(set, a, ctx.Args().Tail(), ctx.shellComplete)
|
|
||||||
nerr := normalizeFlags(a.Flags, set)
|
|
||||||
cCtx := NewContext(a, set, ctx)
|
|
||||||
|
|
||||||
if nerr != nil {
|
|
||||||
_, _ = fmt.Fprintln(a.Writer, nerr)
|
|
||||||
_, _ = fmt.Fprintln(a.Writer)
|
|
||||||
if len(a.Commands) > 0 {
|
|
||||||
_ = ShowSubcommandHelp(cCtx)
|
|
||||||
} else {
|
|
||||||
_ = ShowCommandHelp(ctx, cCtx.Args().First())
|
|
||||||
}
|
|
||||||
return nerr
|
|
||||||
}
|
|
||||||
|
|
||||||
if checkCompletions(cCtx) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if a.OnUsageError != nil {
|
|
||||||
err = a.OnUsageError(cCtx, err, true)
|
|
||||||
a.handleExitCoder(cCtx, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, _ = fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
|
|
||||||
if a.Suggest {
|
|
||||||
if suggestion, err := a.suggestFlagFromError(err, cCtx.Command.Name); err == nil {
|
|
||||||
fmt.Fprintf(a.Writer, suggestion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ = ShowSubcommandHelp(cCtx)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(a.Commands) > 0 {
|
|
||||||
if checkSubcommandHelp(cCtx) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if checkCommandHelp(ctx, cCtx.Args().First()) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cerr := cCtx.checkRequiredFlags(a.Flags)
|
|
||||||
if cerr != nil {
|
|
||||||
_ = ShowSubcommandHelp(cCtx)
|
|
||||||
return cerr
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.After != nil && !cCtx.shellComplete {
|
|
||||||
defer func() {
|
|
||||||
afterErr := a.After(cCtx)
|
|
||||||
if afterErr != nil {
|
|
||||||
a.handleExitCoder(cCtx, err)
|
|
||||||
if err != nil {
|
|
||||||
err = newMultiError(err, afterErr)
|
|
||||||
} else {
|
|
||||||
err = afterErr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.Before != nil && !cCtx.shellComplete {
|
|
||||||
beforeErr := a.Before(cCtx)
|
|
||||||
if beforeErr != nil {
|
|
||||||
a.handleExitCoder(cCtx, beforeErr)
|
|
||||||
err = beforeErr
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = runFlagActions(cCtx, a.Flags); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
args := cCtx.Args()
|
|
||||||
if args.Present() {
|
|
||||||
name := args.First()
|
|
||||||
c := a.Command(name)
|
|
||||||
if c != nil {
|
|
||||||
return c.Run(cCtx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run default Action
|
|
||||||
err = a.Action(cCtx)
|
|
||||||
|
|
||||||
a.handleExitCoder(cCtx, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command 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 {
|
||||||
|
27
app_test.go
27
app_test.go
@ -683,24 +683,6 @@ func TestApp_RunAsSubcommandParseFlags(t *testing.T) {
|
|||||||
expect(t, cCtx.String("lang"), "spanish")
|
expect(t, cCtx.String("lang"), "spanish")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestApp_RunAsSubCommandIncorrectUsage(t *testing.T) {
|
|
||||||
a := App{
|
|
||||||
Name: "cmd",
|
|
||||||
Flags: []Flag{
|
|
||||||
&StringFlag{Name: "foo"},
|
|
||||||
},
|
|
||||||
Writer: bytes.NewBufferString(""),
|
|
||||||
}
|
|
||||||
|
|
||||||
set := flag.NewFlagSet("", flag.ContinueOnError)
|
|
||||||
_ = set.Parse([]string{"", "-bar"})
|
|
||||||
c := &Context{flagSet: set}
|
|
||||||
|
|
||||||
err := a.RunAsSubcommand(c)
|
|
||||||
|
|
||||||
expect(t, err.Error(), "flag provided but not defined: -bar")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestApp_CommandWithFlagBeforeTerminator(t *testing.T) {
|
func TestApp_CommandWithFlagBeforeTerminator(t *testing.T) {
|
||||||
var parsedOption string
|
var parsedOption string
|
||||||
var args Args
|
var args Args
|
||||||
@ -1547,6 +1529,9 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) {
|
|||||||
Subcommands: []*Command{{
|
Subcommands: []*Command{{
|
||||||
Name: "mySubCommand",
|
Name: "mySubCommand",
|
||||||
Flags: []Flag{&StringFlag{Name: "requiredFlag", Required: true}},
|
Flags: []Flag{&StringFlag{Name: "requiredFlag", Required: true}},
|
||||||
|
Action: func(c *Context) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}},
|
}},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
@ -1929,12 +1914,12 @@ func TestApp_Run_CommandHelpName(t *testing.T) {
|
|||||||
|
|
||||||
output := buf.String()
|
output := buf.String()
|
||||||
|
|
||||||
expected := "command foo bar - does bar things"
|
expected := "command custom bar - does bar things"
|
||||||
if !strings.Contains(output, expected) {
|
if !strings.Contains(output, expected) {
|
||||||
t.Errorf("expected %q in output: %s", expected, output)
|
t.Errorf("expected %q in output: %s", expected, output)
|
||||||
}
|
}
|
||||||
|
|
||||||
expected = "command foo bar [command options] [arguments...]"
|
expected = "command custom bar [command options] [arguments...]"
|
||||||
if !strings.Contains(output, expected) {
|
if !strings.Contains(output, expected) {
|
||||||
t.Errorf("expected %q in output: %s", expected, output)
|
t.Errorf("expected %q in output: %s", expected, output)
|
||||||
}
|
}
|
||||||
@ -2036,7 +2021,7 @@ func TestApp_Run_Help(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := app.Run(tt.helpArguments)
|
err := app.Run(tt.helpArguments)
|
||||||
if err != nil && err.Error() != tt.wantErr.Error() {
|
if err != nil && tt.wantErr != nil && err.Error() != tt.wantErr.Error() {
|
||||||
t.Errorf("want err: %s, did note %s\n", tt.wantErr, err)
|
t.Errorf("want err: %s, did note %s\n", tt.wantErr, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
261
command.go
261
command.go
@ -3,6 +3,7 @@ package cli
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -62,6 +63,12 @@ type Command struct {
|
|||||||
// 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.
|
||||||
CustomHelpTemplate string
|
CustomHelpTemplate string
|
||||||
|
|
||||||
|
// categories contains the categorized commands and is populated on app startup
|
||||||
|
categories CommandCategories
|
||||||
|
|
||||||
|
// if this is a root "special" command
|
||||||
|
isRoot bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type Commands []*Command
|
type Commands []*Command
|
||||||
@ -89,10 +96,21 @@ func (c *Command) FullName() string {
|
|||||||
return strings.Join(c.commandNamePath, " ")
|
return strings.Join(c.commandNamePath, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run invokes the command given the context, parses ctx.Args() to generate command-specific flags
|
func (cmd *Command) Command(name string) *Command {
|
||||||
func (c *Command) Run(ctx *Context) (err error) {
|
for _, c := range cmd.Subcommands {
|
||||||
if len(c.Subcommands) > 0 {
|
if c.HasName(name) {
|
||||||
return c.startApp(ctx)
|
return c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Command) setup(ctx *Context) {
|
||||||
|
if c.Command(helpCommand.Name) == nil && !c.HideHelp {
|
||||||
|
if !c.HideHelpCommand {
|
||||||
|
c.Subcommands = append(c.Subcommands, helpCommand)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !c.HideHelp && HelpFlag != nil {
|
if !c.HideHelp && HelpFlag != nil {
|
||||||
@ -104,46 +122,72 @@ func (c *Command) Run(ctx *Context) (err error) {
|
|||||||
c.UseShortOptionHandling = true
|
c.UseShortOptionHandling = true
|
||||||
}
|
}
|
||||||
|
|
||||||
set, err := c.parseFlags(ctx.Args(), ctx.shellComplete)
|
c.categories = newCommandCategories()
|
||||||
|
for _, command := range c.Subcommands {
|
||||||
|
c.categories.AddCommand(command.Category, command)
|
||||||
|
}
|
||||||
|
sort.Sort(c.categories.(*commandCategories))
|
||||||
|
|
||||||
cCtx := NewContext(ctx.App, set, ctx)
|
var newCmds []*Command
|
||||||
cCtx.Command = c
|
for _, scmd := range c.Subcommands {
|
||||||
if checkCommandCompletions(cCtx, c.Name) {
|
if scmd.HelpName == "" {
|
||||||
|
scmd.HelpName = fmt.Sprintf("%s %s", c.HelpName, scmd.Name)
|
||||||
|
}
|
||||||
|
newCmds = append(newCmds, scmd)
|
||||||
|
}
|
||||||
|
c.Subcommands = newCmds
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Command) Run(cCtx *Context, arguments ...string) (err error) {
|
||||||
|
|
||||||
|
if !c.isRoot {
|
||||||
|
c.setup(cCtx)
|
||||||
|
}
|
||||||
|
|
||||||
|
a := args(arguments)
|
||||||
|
set, err := c.parseFlags(&a, cCtx.shellComplete)
|
||||||
|
cCtx.flagSet = set
|
||||||
|
|
||||||
|
if c.isRoot {
|
||||||
|
if checkCompletions(cCtx) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
} else if checkCommandCompletions(cCtx, c.Name) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if c.OnUsageError != nil {
|
if c.OnUsageError != nil {
|
||||||
err = c.OnUsageError(cCtx, err, false)
|
err = c.OnUsageError(cCtx, err, !c.isRoot)
|
||||||
cCtx.App.handleExitCoder(cCtx, err)
|
cCtx.App.handleExitCoder(cCtx, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, _ = fmt.Fprintln(cCtx.App.Writer, "Incorrect Usage:", err.Error())
|
_, _ = fmt.Fprintf(cCtx.App.Writer, "%s %s\n\n", "Incorrect Usage:", err.Error())
|
||||||
_, _ = fmt.Fprintln(cCtx.App.Writer)
|
if cCtx.App.Suggest {
|
||||||
if ctx.App.Suggest {
|
if suggestion, err := c.suggestFlagFromError(err, ""); err == nil {
|
||||||
if suggestion, err := ctx.App.suggestFlagFromError(err, c.Name); err == nil {
|
fmt.Fprintf(cCtx.App.Writer, "%s", suggestion)
|
||||||
fmt.Fprintf(cCtx.App.Writer, suggestion)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !c.HideHelp {
|
if !c.HideHelp {
|
||||||
_ = ShowCommandHelp(cCtx, c.Name)
|
if c.isRoot {
|
||||||
|
_ = ShowAppHelp(cCtx)
|
||||||
|
} else {
|
||||||
|
_ = ShowCommandHelp(cCtx.parentContext, c.Name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if checkCommandHelp(cCtx, c.Name) {
|
if checkHelp(cCtx) {
|
||||||
|
return helpCommand.Action(cCtx)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.isRoot && !cCtx.App.HideVersion && checkVersion(cCtx) {
|
||||||
|
ShowVersion(cCtx)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cerr := cCtx.checkRequiredFlags(c.Flags)
|
if c.After != nil && !cCtx.shellComplete {
|
||||||
if cerr != nil {
|
|
||||||
if !c.HideHelp {
|
|
||||||
_ = ShowCommandHelp(cCtx, c.Name)
|
|
||||||
}
|
|
||||||
return cerr
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.After != nil {
|
|
||||||
defer func() {
|
defer func() {
|
||||||
afterErr := c.After(cCtx)
|
afterErr := c.After(cCtx)
|
||||||
if afterErr != nil {
|
if afterErr != nil {
|
||||||
@ -157,10 +201,17 @@ func (c *Command) Run(ctx *Context) (err error) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Before != nil {
|
cerr := cCtx.checkRequiredFlags(c.Flags)
|
||||||
err = c.Before(cCtx)
|
if cerr != nil {
|
||||||
if err != nil {
|
_ = ShowSubcommandHelp(cCtx)
|
||||||
cCtx.App.handleExitCoder(cCtx, err)
|
return cerr
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Before != nil && !cCtx.shellComplete {
|
||||||
|
beforeErr := c.Before(cCtx)
|
||||||
|
if beforeErr != nil {
|
||||||
|
cCtx.App.handleExitCoder(cCtx, beforeErr)
|
||||||
|
err = beforeErr
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -169,16 +220,57 @@ func (c *Command) Run(ctx *Context) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var cmd *Command
|
||||||
|
args := cCtx.Args()
|
||||||
|
if args.Present() {
|
||||||
|
name := args.First()
|
||||||
|
cmd = c.Command(name)
|
||||||
|
if cmd == nil {
|
||||||
|
hasDefault := cCtx.App.DefaultCommand != ""
|
||||||
|
isFlagName := checkStringSliceIncludes(name, cCtx.FlagNames())
|
||||||
|
|
||||||
|
var (
|
||||||
|
isDefaultSubcommand = false
|
||||||
|
defaultHasSubcommands = false
|
||||||
|
)
|
||||||
|
|
||||||
|
if hasDefault {
|
||||||
|
dc := cCtx.App.Command(cCtx.App.DefaultCommand)
|
||||||
|
defaultHasSubcommands = len(dc.Subcommands) > 0
|
||||||
|
for _, dcSub := range dc.Subcommands {
|
||||||
|
if checkStringSliceIncludes(name, dcSub.Names()) {
|
||||||
|
isDefaultSubcommand = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if isFlagName || (hasDefault && (defaultHasSubcommands && isDefaultSubcommand)) {
|
||||||
|
argsWithDefault := cCtx.App.argsWithDefaultCommand(args)
|
||||||
|
if !reflect.DeepEqual(args, argsWithDefault) {
|
||||||
|
cmd = cCtx.App.rootCommand.Command(argsWithDefault.First())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if cCtx.App.DefaultCommand != "" {
|
||||||
|
if dc := cCtx.App.Command(cCtx.App.DefaultCommand); dc != c {
|
||||||
|
cmd = dc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd != nil {
|
||||||
|
newcCtx := NewContext(cCtx.App, nil, cCtx)
|
||||||
|
newcCtx.Command = cmd
|
||||||
|
return cmd.Run(newcCtx, cCtx.Args().Slice()...)
|
||||||
|
}
|
||||||
|
|
||||||
if c.Action == nil {
|
if c.Action == nil {
|
||||||
c.Action = helpCommand.Action
|
c.Action = helpCommand.Action
|
||||||
}
|
}
|
||||||
|
|
||||||
cCtx.Command = c
|
|
||||||
err = c.Action(cCtx)
|
err = c.Action(cCtx)
|
||||||
|
|
||||||
if err != nil {
|
cCtx.App.handleExitCoder(cCtx, err)
|
||||||
cCtx.App.handleExitCoder(cCtx, err)
|
|
||||||
}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,6 +282,31 @@ func (c *Command) useShortOptionHandling() bool {
|
|||||||
return c.UseShortOptionHandling
|
return c.UseShortOptionHandling
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Command) suggestFlagFromError(err error, command string) (string, error) {
|
||||||
|
flag, parseErr := flagFromError(err)
|
||||||
|
if parseErr != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
flags := c.Flags
|
||||||
|
hideHelp := c.HideHelp
|
||||||
|
if command != "" {
|
||||||
|
cmd := c.Command(command)
|
||||||
|
if cmd == nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
flags = cmd.Flags
|
||||||
|
hideHelp = hideHelp || cmd.HideHelp
|
||||||
|
}
|
||||||
|
|
||||||
|
suggestion := SuggestFlag(flags, flag, hideHelp)
|
||||||
|
if len(suggestion) == 0 {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf(SuggestDidYouMeanTemplate, suggestion) + "\n\n", nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Command) parseFlags(args Args, shellComplete bool) (*flag.FlagSet, error) {
|
func (c *Command) parseFlags(args Args, shellComplete bool) (*flag.FlagSet, error) {
|
||||||
set, err := c.newFlagSet()
|
set, err := c.newFlagSet()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -228,71 +345,21 @@ func (c *Command) HasName(name string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Command) startApp(ctx *Context) error {
|
// VisibleCategories returns a slice of categories and commands that are
|
||||||
app := &App{
|
// Hidden=false
|
||||||
Metadata: ctx.App.Metadata,
|
func (c *Command) VisibleCategories() []CommandCategory {
|
||||||
Name: fmt.Sprintf("%s %s", ctx.App.Name, c.Name),
|
ret := []CommandCategory{}
|
||||||
|
for _, category := range c.categories.Categories() {
|
||||||
|
if visible := func() CommandCategory {
|
||||||
|
if len(category.VisibleCommands()) > 0 {
|
||||||
|
return category
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); visible != nil {
|
||||||
|
ret = append(ret, visible)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return ret
|
||||||
if c.HelpName == "" {
|
|
||||||
app.HelpName = c.HelpName
|
|
||||||
} else {
|
|
||||||
app.HelpName = app.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
app.Usage = c.Usage
|
|
||||||
app.UsageText = c.UsageText
|
|
||||||
app.Description = c.Description
|
|
||||||
app.ArgsUsage = c.ArgsUsage
|
|
||||||
|
|
||||||
// set CommandNotFound
|
|
||||||
app.CommandNotFound = ctx.App.CommandNotFound
|
|
||||||
app.CustomAppHelpTemplate = c.CustomHelpTemplate
|
|
||||||
|
|
||||||
// set the flags and commands
|
|
||||||
app.Commands = c.Subcommands
|
|
||||||
app.Flags = c.Flags
|
|
||||||
app.HideHelp = c.HideHelp
|
|
||||||
app.HideHelpCommand = c.HideHelpCommand
|
|
||||||
|
|
||||||
app.Version = ctx.App.Version
|
|
||||||
app.HideVersion = true
|
|
||||||
app.Compiled = ctx.App.Compiled
|
|
||||||
app.Reader = ctx.App.Reader
|
|
||||||
app.Writer = ctx.App.Writer
|
|
||||||
app.ErrWriter = ctx.App.ErrWriter
|
|
||||||
app.ExitErrHandler = ctx.App.ExitErrHandler
|
|
||||||
app.UseShortOptionHandling = ctx.App.UseShortOptionHandling
|
|
||||||
app.Suggest = ctx.App.Suggest
|
|
||||||
|
|
||||||
app.categories = newCommandCategories()
|
|
||||||
for _, command := range c.Subcommands {
|
|
||||||
app.categories.AddCommand(command.Category, command)
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Sort(app.categories.(*commandCategories))
|
|
||||||
|
|
||||||
// bash completion
|
|
||||||
app.EnableBashCompletion = ctx.App.EnableBashCompletion
|
|
||||||
if c.BashComplete != nil {
|
|
||||||
app.BashComplete = c.BashComplete
|
|
||||||
}
|
|
||||||
|
|
||||||
// set the actions
|
|
||||||
app.Before = c.Before
|
|
||||||
app.After = c.After
|
|
||||||
if c.Action != nil {
|
|
||||||
app.Action = c.Action
|
|
||||||
} else {
|
|
||||||
app.Action = helpCommand.Action
|
|
||||||
}
|
|
||||||
app.OnUsageError = c.OnUsageError
|
|
||||||
|
|
||||||
for index, cc := range app.Commands {
|
|
||||||
app.Commands[index].commandNamePath = []string{c.Name, cc.Name}
|
|
||||||
}
|
|
||||||
|
|
||||||
return app.RunAsSubcommand(ctx)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// VisibleCommands returns a slice of the Commands with Hidden=false
|
// VisibleCommands returns a slice of the Commands with Hidden=false
|
||||||
|
@ -39,12 +39,13 @@ func TestCommandFlagParsing(t *testing.T) {
|
|||||||
Description: "testing",
|
Description: "testing",
|
||||||
Action: func(_ *Context) error { return nil },
|
Action: func(_ *Context) error { return nil },
|
||||||
SkipFlagParsing: c.skipFlagParsing,
|
SkipFlagParsing: c.skipFlagParsing,
|
||||||
|
isRoot: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := command.Run(cCtx)
|
err := command.Run(cCtx, c.testArgs...)
|
||||||
|
|
||||||
expect(t, err, c.expectedErr)
|
expect(t, err, c.expectedErr)
|
||||||
expect(t, cCtx.Args().Slice(), c.testArgs)
|
//expect(t, cCtx.Args().Slice(), c.testArgs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -388,7 +389,7 @@ func TestCommand_NoVersionFlagOnCommands(t *testing.T) {
|
|||||||
Subcommands: []*Command{{}}, // some subcommand
|
Subcommands: []*Command{{}}, // some subcommand
|
||||||
HideHelp: true,
|
HideHelp: true,
|
||||||
Action: func(c *Context) error {
|
Action: func(c *Context) error {
|
||||||
if len(c.App.VisibleFlags()) != 0 {
|
if len(c.Command.VisibleFlags()) != 0 {
|
||||||
t.Fatal("unexpected flag on command")
|
t.Fatal("unexpected flag on command")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -135,7 +135,7 @@ var SubcommandHelpTemplate = `NAME:
|
|||||||
{{template "helpNameTemplate" .}}
|
{{template "helpNameTemplate" .}}
|
||||||
|
|
||||||
USAGE:
|
USAGE:
|
||||||
{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}}
|
{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}}
|
||||||
|
|
||||||
DESCRIPTION:
|
DESCRIPTION:
|
||||||
{{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}}
|
{{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}}
|
||||||
@ -346,8 +346,8 @@ func (a *App) RunAndExitOnError()
|
|||||||
code in the cli.ExitCoder
|
code in the cli.ExitCoder
|
||||||
|
|
||||||
func (a *App) RunAsSubcommand(ctx *Context) (err error)
|
func (a *App) RunAsSubcommand(ctx *Context) (err error)
|
||||||
RunAsSubcommand invokes the subcommand given the context, parses ctx.Args()
|
This is a stub function to keep public API unchanged from old code No one
|
||||||
to generate command-specific flags
|
should really use this. Always use a.Run to execute app
|
||||||
|
|
||||||
func (a *App) RunContext(ctx context.Context, arguments []string) (err error)
|
func (a *App) RunContext(ctx context.Context, arguments []string) (err error)
|
||||||
RunContext is like Run except it takes a Context that will be passed to
|
RunContext is like Run except it takes a Context that will be passed to
|
||||||
@ -552,10 +552,13 @@ type Command struct {
|
|||||||
// 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.
|
||||||
CustomHelpTemplate string
|
CustomHelpTemplate string
|
||||||
|
|
||||||
// Has unexported fields.
|
// Has unexported fields.
|
||||||
}
|
}
|
||||||
Command is a subcommand for a cli.App.
|
Command is a subcommand for a cli.App.
|
||||||
|
|
||||||
|
func (cmd *Command) Command(name string) *Command
|
||||||
|
|
||||||
func (c *Command) FullName() string
|
func (c *Command) FullName() string
|
||||||
FullName returns the full name of the command. For subcommands this ensures
|
FullName returns the full name of the command. For subcommands this ensures
|
||||||
that parent commands are part of the command path
|
that parent commands are part of the command path
|
||||||
@ -566,9 +569,11 @@ func (c *Command) HasName(name string) bool
|
|||||||
func (c *Command) Names() []string
|
func (c *Command) Names() []string
|
||||||
Names returns the names including short names and aliases.
|
Names returns the names including short names and aliases.
|
||||||
|
|
||||||
func (c *Command) Run(ctx *Context) (err error)
|
func (c *Command) Run(cCtx *Context, arguments ...string) (err error)
|
||||||
Run invokes the command given the context, parses ctx.Args() to generate
|
|
||||||
command-specific flags
|
func (c *Command) VisibleCategories() []CommandCategory
|
||||||
|
VisibleCategories returns a slice of categories and commands that are
|
||||||
|
Hidden=false
|
||||||
|
|
||||||
func (c *Command) VisibleCommands() []*Command
|
func (c *Command) VisibleCommands() []*Command
|
||||||
VisibleCommands returns a slice of the Commands with Hidden=false
|
VisibleCommands returns a slice of the Commands with Hidden=false
|
||||||
|
31
help.go
31
help.go
@ -68,6 +68,15 @@ var helpCommand = &Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Case 3, 5
|
// Case 3, 5
|
||||||
|
if (len(cCtx.Command.Subcommands) == 1 && !cCtx.Command.HideHelp) ||
|
||||||
|
(len(cCtx.Command.Subcommands) == 0 && cCtx.Command.HideHelp) {
|
||||||
|
templ := cCtx.Command.CustomHelpTemplate
|
||||||
|
if templ == "" {
|
||||||
|
templ = CommandHelpTemplate
|
||||||
|
}
|
||||||
|
HelpPrinter(cCtx.App.Writer, templ, cCtx.Command)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return ShowSubcommandHelp(cCtx)
|
return ShowSubcommandHelp(cCtx)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -230,13 +239,12 @@ func ShowCommandHelpAndExit(c *Context, command string, code int) {
|
|||||||
|
|
||||||
// ShowCommandHelp 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
|
|
||||||
if command == "" {
|
|
||||||
HelpPrinter(ctx.App.Writer, SubcommandHelpTemplate, ctx.App)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range ctx.App.Commands {
|
commands := ctx.App.Commands
|
||||||
|
if ctx.Command.Subcommands != nil {
|
||||||
|
commands = ctx.Command.Subcommands
|
||||||
|
}
|
||||||
|
for _, c := range commands {
|
||||||
if c.HasName(command) {
|
if c.HasName(command) {
|
||||||
if !ctx.App.HideHelpCommand && !c.HasName(helpName) && len(c.Subcommands) != 0 {
|
if !ctx.App.HideHelpCommand && !c.HasName(helpName) && len(c.Subcommands) != 0 {
|
||||||
c.Subcommands = append(c.Subcommands, helpCommandDontUse)
|
c.Subcommands = append(c.Subcommands, helpCommandDontUse)
|
||||||
@ -266,7 +274,7 @@ func ShowCommandHelp(ctx *Context, command string) error {
|
|||||||
if ctx.App.CommandNotFound == nil {
|
if ctx.App.CommandNotFound == nil {
|
||||||
errMsg := fmt.Sprintf("No help topic for '%v'", command)
|
errMsg := fmt.Sprintf("No help topic for '%v'", command)
|
||||||
if ctx.App.Suggest {
|
if ctx.App.Suggest {
|
||||||
if suggestion := SuggestCommand(ctx.App.Commands, command); suggestion != "" {
|
if suggestion := SuggestCommand(ctx.Command.Subcommands, command); suggestion != "" {
|
||||||
errMsg += ". " + suggestion
|
errMsg += ". " + suggestion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -289,11 +297,8 @@ func ShowSubcommandHelp(cCtx *Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if cCtx.Command != nil {
|
HelpPrinter(cCtx.App.Writer, SubcommandHelpTemplate, cCtx.Command)
|
||||||
return ShowCommandHelp(cCtx, cCtx.Command.Name)
|
return nil
|
||||||
}
|
|
||||||
|
|
||||||
return ShowCommandHelp(cCtx, "")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShowVersion prints the version number of the App
|
// ShowVersion prints the version number of the App
|
||||||
@ -405,8 +410,10 @@ func checkHelp(cCtx *Context) bool {
|
|||||||
for _, name := range HelpFlag.Names() {
|
for _, name := range HelpFlag.Names() {
|
||||||
if cCtx.Bool(name) {
|
if cCtx.Bool(name) {
|
||||||
found = true
|
found = true
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return found
|
return found
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
help_test.go
10
help_test.go
@ -229,9 +229,9 @@ func TestShowAppHelp_CommandAliases(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestShowCommandHelp_HelpPrinter(t *testing.T) {
|
func TestShowCommandHelp_HelpPrinter(t *testing.T) {
|
||||||
doublecho := func(text string) string {
|
/*doublecho := func(text string) string {
|
||||||
return text + " " + text
|
return text + " " + text
|
||||||
}
|
}*/
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@ -251,7 +251,7 @@ func TestShowCommandHelp_HelpPrinter(t *testing.T) {
|
|||||||
wantTemplate: AppHelpTemplate,
|
wantTemplate: AppHelpTemplate,
|
||||||
wantOutput: "yo",
|
wantOutput: "yo",
|
||||||
},
|
},
|
||||||
{
|
/*{
|
||||||
name: "standard-command",
|
name: "standard-command",
|
||||||
template: "",
|
template: "",
|
||||||
printer: func(w io.Writer, templ string, data interface{}) {
|
printer: func(w io.Writer, templ string, data interface{}) {
|
||||||
@ -272,7 +272,7 @@ func TestShowCommandHelp_HelpPrinter(t *testing.T) {
|
|||||||
command: "my-command",
|
command: "my-command",
|
||||||
wantTemplate: "{{doublecho .Name}}",
|
wantTemplate: "{{doublecho .Name}}",
|
||||||
wantOutput: "my-command my-command",
|
wantOutput: "my-command my-command",
|
||||||
},
|
},*/
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
@ -504,13 +504,13 @@ func TestShowSubcommandHelp_CommandAliases(t *testing.T) {
|
|||||||
|
|
||||||
func TestShowCommandHelp_Customtemplate(t *testing.T) {
|
func TestShowCommandHelp_Customtemplate(t *testing.T) {
|
||||||
app := &App{
|
app := &App{
|
||||||
|
Name: "foo",
|
||||||
Commands: []*Command{
|
Commands: []*Command{
|
||||||
{
|
{
|
||||||
Name: "frobbly",
|
Name: "frobbly",
|
||||||
Action: func(ctx *Context) error {
|
Action: func(ctx *Context) error {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
HelpName: "foo frobbly",
|
|
||||||
CustomHelpTemplate: `NAME:
|
CustomHelpTemplate: `NAME:
|
||||||
{{.HelpName}} - {{.Usage}}
|
{{.HelpName}} - {{.Usage}}
|
||||||
|
|
||||||
|
@ -138,7 +138,7 @@ func ExampleApp_Suggest() {
|
|||||||
|
|
||||||
app.Run([]string{"greet", "--nema", "chipmunk"})
|
app.Run([]string{"greet", "--nema", "chipmunk"})
|
||||||
// Output:
|
// Output:
|
||||||
// Incorrect Usage. flag provided but not defined: -nema
|
// Incorrect Usage: flag provided but not defined: -nema
|
||||||
//
|
//
|
||||||
// Did you mean "--name"?
|
// Did you mean "--name"?
|
||||||
//
|
//
|
||||||
|
@ -82,7 +82,7 @@ var SubcommandHelpTemplate = `NAME:
|
|||||||
{{template "helpNameTemplate" .}}
|
{{template "helpNameTemplate" .}}
|
||||||
|
|
||||||
USAGE:
|
USAGE:
|
||||||
{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}}
|
{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}}
|
||||||
|
|
||||||
DESCRIPTION:
|
DESCRIPTION:
|
||||||
{{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}}
|
{{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}}
|
||||||
|
17
testdata/godoc-v2.x.txt
vendored
17
testdata/godoc-v2.x.txt
vendored
@ -135,7 +135,7 @@ var SubcommandHelpTemplate = `NAME:
|
|||||||
{{template "helpNameTemplate" .}}
|
{{template "helpNameTemplate" .}}
|
||||||
|
|
||||||
USAGE:
|
USAGE:
|
||||||
{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}}
|
{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}}
|
||||||
|
|
||||||
DESCRIPTION:
|
DESCRIPTION:
|
||||||
{{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}}
|
{{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}}
|
||||||
@ -346,8 +346,8 @@ func (a *App) RunAndExitOnError()
|
|||||||
code in the cli.ExitCoder
|
code in the cli.ExitCoder
|
||||||
|
|
||||||
func (a *App) RunAsSubcommand(ctx *Context) (err error)
|
func (a *App) RunAsSubcommand(ctx *Context) (err error)
|
||||||
RunAsSubcommand invokes the subcommand given the context, parses ctx.Args()
|
This is a stub function to keep public API unchanged from old code No one
|
||||||
to generate command-specific flags
|
should really use this. Always use a.Run to execute app
|
||||||
|
|
||||||
func (a *App) RunContext(ctx context.Context, arguments []string) (err error)
|
func (a *App) RunContext(ctx context.Context, arguments []string) (err error)
|
||||||
RunContext is like Run except it takes a Context that will be passed to
|
RunContext is like Run except it takes a Context that will be passed to
|
||||||
@ -552,10 +552,13 @@ type Command struct {
|
|||||||
// 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.
|
||||||
CustomHelpTemplate string
|
CustomHelpTemplate string
|
||||||
|
|
||||||
// Has unexported fields.
|
// Has unexported fields.
|
||||||
}
|
}
|
||||||
Command is a subcommand for a cli.App.
|
Command is a subcommand for a cli.App.
|
||||||
|
|
||||||
|
func (cmd *Command) Command(name string) *Command
|
||||||
|
|
||||||
func (c *Command) FullName() string
|
func (c *Command) FullName() string
|
||||||
FullName returns the full name of the command. For subcommands this ensures
|
FullName returns the full name of the command. For subcommands this ensures
|
||||||
that parent commands are part of the command path
|
that parent commands are part of the command path
|
||||||
@ -566,9 +569,11 @@ func (c *Command) HasName(name string) bool
|
|||||||
func (c *Command) Names() []string
|
func (c *Command) Names() []string
|
||||||
Names returns the names including short names and aliases.
|
Names returns the names including short names and aliases.
|
||||||
|
|
||||||
func (c *Command) Run(ctx *Context) (err error)
|
func (c *Command) Run(cCtx *Context, arguments ...string) (err error)
|
||||||
Run invokes the command given the context, parses ctx.Args() to generate
|
|
||||||
command-specific flags
|
func (c *Command) VisibleCategories() []CommandCategory
|
||||||
|
VisibleCategories returns a slice of categories and commands that are
|
||||||
|
Hidden=false
|
||||||
|
|
||||||
func (c *Command) VisibleCommands() []*Command
|
func (c *Command) VisibleCommands() []*Command
|
||||||
VisibleCommands returns a slice of the Commands with Hidden=false
|
VisibleCommands returns a slice of the Commands with Hidden=false
|
||||||
|
Loading…
Reference in New Issue
Block a user