|
|
@ -3,6 +3,7 @@ package cli
|
|
|
|
import (
|
|
|
|
import (
|
|
|
|
"flag"
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"reflect"
|
|
|
|
"sort"
|
|
|
|
"sort"
|
|
|
|
"strings"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
)
|
|
|
@ -62,6 +63,14 @@ 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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
helpAction ActionFunc
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type Commands []*Command
|
|
|
|
type Commands []*Command
|
|
|
@ -89,10 +98,30 @@ 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 c.HasName(name) {
|
|
|
|
|
|
|
|
return c
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (c *Command) setup(ctx *Context) {
|
|
|
|
|
|
|
|
helpCmd := helpCommand
|
|
|
|
if len(c.Subcommands) > 0 {
|
|
|
|
if len(c.Subcommands) > 0 {
|
|
|
|
return c.startApp(ctx)
|
|
|
|
helpCmd = helpSubcommand
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if c.Command(helpCmd.Name) == nil && !c.HideHelp {
|
|
|
|
|
|
|
|
if !c.HideHelpCommand {
|
|
|
|
|
|
|
|
c.Subcommands = append(c.Subcommands, helpCmd)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if c.helpAction == nil {
|
|
|
|
|
|
|
|
c.helpAction = helpCmd.Action
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if !c.HideHelp && HelpFlag != nil {
|
|
|
|
if !c.HideHelp && HelpFlag != nil {
|
|
|
@ -104,46 +133,76 @@ 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))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var newCmds []*Command
|
|
|
|
|
|
|
|
for _, scmd := range c.Subcommands {
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
cCtx := NewContext(ctx.App, set, ctx)
|
|
|
|
if c.isRoot {
|
|
|
|
cCtx.Command = c
|
|
|
|
if checkCompletions(cCtx) {
|
|
|
|
if checkCommandCompletions(cCtx, c.Name) {
|
|
|
|
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, c.Name)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if checkCommandHelp(cCtx, c.Name) {
|
|
|
|
if checkHelp(cCtx) {
|
|
|
|
return nil
|
|
|
|
return c.helpAction(cCtx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cerr := cCtx.checkRequiredFlags(c.Flags)
|
|
|
|
if cCtx.Args().First() == "help" && c.Action == nil {
|
|
|
|
if cerr != nil {
|
|
|
|
return c.helpAction(cCtx)
|
|
|
|
if !c.HideHelp {
|
|
|
|
}
|
|
|
|
_ = ShowCommandHelp(cCtx, c.Name)
|
|
|
|
|
|
|
|
}
|
|
|
|
if c.isRoot && !cCtx.App.HideVersion && checkVersion(cCtx) {
|
|
|
|
return cerr
|
|
|
|
ShowVersion(cCtx)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if c.After != nil {
|
|
|
|
if c.After != nil && !cCtx.shellComplete {
|
|
|
|
defer func() {
|
|
|
|
defer func() {
|
|
|
|
afterErr := c.After(cCtx)
|
|
|
|
afterErr := c.After(cCtx)
|
|
|
|
if afterErr != nil {
|
|
|
|
if afterErr != nil {
|
|
|
@ -157,10 +216,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 +235,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 +297,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 +360,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 c.HelpName == "" {
|
|
|
|
if len(category.VisibleCommands()) > 0 {
|
|
|
|
app.HelpName = c.HelpName
|
|
|
|
return category
|
|
|
|
} else {
|
|
|
|
}
|
|
|
|
app.HelpName = app.Name
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}(); visible != nil {
|
|
|
|
|
|
|
|
ret = append(ret, visible)
|
|
|
|
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 ret
|
|
|
|
return app.RunAsSubcommand(ctx)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// VisibleCommands returns a slice of the Commands with Hidden=false
|
|
|
|
// VisibleCommands returns a slice of the Commands with Hidden=false
|
|
|
@ -314,6 +396,17 @@ func (c *Command) VisibleFlagCategories() []VisibleFlagCategory {
|
|
|
|
return c.flagCategories.VisibleCategories()
|
|
|
|
return c.flagCategories.VisibleCategories()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// VisibleCommands returns a slice of the Commands with Hidden=false
|
|
|
|
|
|
|
|
func (c *Command) VisibleCommands() []*Command {
|
|
|
|
|
|
|
|
var ret []*Command
|
|
|
|
|
|
|
|
for _, command := range c.Subcommands {
|
|
|
|
|
|
|
|
if !command.Hidden {
|
|
|
|
|
|
|
|
ret = append(ret, command)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// VisibleFlags returns a slice of the Flags with Hidden=false
|
|
|
|
// VisibleFlags returns a slice of the Flags with Hidden=false
|
|
|
|
func (c *Command) VisibleFlags() []Flag {
|
|
|
|
func (c *Command) VisibleFlags() []Flag {
|
|
|
|
return visibleFlags(c.Flags)
|
|
|
|
return visibleFlags(c.Flags)
|
|
|
|