1package cli 2 3import ( 4 "fmt" 5 "io" 6 "os" 7 "strings" 8 "text/tabwriter" 9 "text/template" 10) 11 12// AppHelpTemplate is the text template for the Default help topic. 13// cli.go uses text/template to render templates. You can 14// render custom help text by setting this variable. 15var AppHelpTemplate = `NAME: 16 {{.Name}}{{if .Usage}} - {{.Usage}}{{end}} 17 18USAGE: 19 {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} 20 21VERSION: 22 {{.Version}}{{end}}{{end}}{{if .Description}} 23 24DESCRIPTION: 25 {{.Description}}{{end}}{{if len .Authors}} 26 27AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: 28 {{range $index, $author := .Authors}}{{if $index}} 29 {{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}} 30 31COMMANDS:{{range .VisibleCategories}}{{if .Name}} 32 {{.Name}}:{{end}}{{range .VisibleCommands}} 33 {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} 34 35GLOBAL OPTIONS: 36 {{range $index, $option := .VisibleFlags}}{{if $index}} 37 {{end}}{{$option}}{{end}}{{end}}{{if .Copyright}} 38 39COPYRIGHT: 40 {{.Copyright}}{{end}} 41` 42 43// CommandHelpTemplate is the text template for the command help topic. 44// cli.go uses text/template to render templates. You can 45// render custom help text by setting this variable. 46var CommandHelpTemplate = `NAME: 47 {{.HelpName}} - {{.Usage}} 48 49USAGE: 50 {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}} 51 52CATEGORY: 53 {{.Category}}{{end}}{{if .Description}} 54 55DESCRIPTION: 56 {{.Description}}{{end}}{{if .VisibleFlags}} 57 58OPTIONS: 59 {{range .VisibleFlags}}{{.}} 60 {{end}}{{end}} 61` 62 63// SubcommandHelpTemplate is the text template for the subcommand help topic. 64// cli.go uses text/template to render templates. You can 65// render custom help text by setting this variable. 66var SubcommandHelpTemplate = `NAME: 67 {{.HelpName}} - {{if .Description}}{{.Description}}{{else}}{{.Usage}}{{end}} 68 69USAGE: 70 {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}} 71 72COMMANDS:{{range .VisibleCategories}}{{if .Name}} 73 {{.Name}}:{{end}}{{range .VisibleCommands}} 74 {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}} 75{{end}}{{if .VisibleFlags}} 76OPTIONS: 77 {{range .VisibleFlags}}{{.}} 78 {{end}}{{end}} 79` 80 81var helpCommand = Command{ 82 Name: "help", 83 Aliases: []string{"h"}, 84 Usage: "Shows a list of commands or help for one command", 85 ArgsUsage: "[command]", 86 Action: func(c *Context) error { 87 args := c.Args() 88 if args.Present() { 89 return ShowCommandHelp(c, args.First()) 90 } 91 92 ShowAppHelp(c) 93 return nil 94 }, 95} 96 97var helpSubcommand = Command{ 98 Name: "help", 99 Aliases: []string{"h"}, 100 Usage: "Shows a list of commands or help for one command", 101 ArgsUsage: "[command]", 102 Action: func(c *Context) error { 103 args := c.Args() 104 if args.Present() { 105 return ShowCommandHelp(c, args.First()) 106 } 107 108 return ShowSubcommandHelp(c) 109 }, 110} 111 112// Prints help for the App or Command 113type helpPrinter func(w io.Writer, templ string, data interface{}) 114 115// Prints help for the App or Command with custom template function. 116type helpPrinterCustom func(w io.Writer, templ string, data interface{}, customFunc map[string]interface{}) 117 118// HelpPrinter is a function that writes the help output. If not set a default 119// is used. The function signature is: 120// func(w io.Writer, templ string, data interface{}) 121var HelpPrinter helpPrinter = printHelp 122 123// HelpPrinterCustom is same as HelpPrinter but 124// takes a custom function for template function map. 125var HelpPrinterCustom helpPrinterCustom = printHelpCustom 126 127// VersionPrinter prints the version for the App 128var VersionPrinter = printVersion 129 130// ShowAppHelpAndExit - Prints the list of subcommands for the app and exits with exit code. 131func ShowAppHelpAndExit(c *Context, exitCode int) { 132 ShowAppHelp(c) 133 os.Exit(exitCode) 134} 135 136// ShowAppHelp is an action that displays the help. 137func ShowAppHelp(c *Context) (err error) { 138 if c.App.CustomAppHelpTemplate == "" { 139 HelpPrinter(c.App.Writer, AppHelpTemplate, c.App) 140 return 141 } 142 customAppData := func() map[string]interface{} { 143 if c.App.ExtraInfo == nil { 144 return nil 145 } 146 return map[string]interface{}{ 147 "ExtraInfo": c.App.ExtraInfo, 148 } 149 } 150 HelpPrinterCustom(c.App.Writer, c.App.CustomAppHelpTemplate, c.App, customAppData()) 151 return nil 152} 153 154// DefaultAppComplete prints the list of subcommands as the default app completion method 155func DefaultAppComplete(c *Context) { 156 for _, command := range c.App.Commands { 157 if command.Hidden { 158 continue 159 } 160 for _, name := range command.Names() { 161 fmt.Fprintln(c.App.Writer, name) 162 } 163 } 164} 165 166// ShowCommandHelpAndExit - exits with code after showing help 167func ShowCommandHelpAndExit(c *Context, command string, code int) { 168 ShowCommandHelp(c, command) 169 os.Exit(code) 170} 171 172// ShowCommandHelp prints help for the given command 173func ShowCommandHelp(ctx *Context, command string) error { 174 // show the subcommand help for a command with subcommands 175 if command == "" { 176 HelpPrinter(ctx.App.Writer, SubcommandHelpTemplate, ctx.App) 177 return nil 178 } 179 180 for _, c := range ctx.App.Commands { 181 if c.HasName(command) { 182 if c.CustomHelpTemplate != "" { 183 HelpPrinterCustom(ctx.App.Writer, c.CustomHelpTemplate, c, nil) 184 } else { 185 HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c) 186 } 187 return nil 188 } 189 } 190 191 if ctx.App.CommandNotFound == nil { 192 return NewExitError(fmt.Sprintf("No help topic for '%v'", command), 3) 193 } 194 195 ctx.App.CommandNotFound(ctx, command) 196 return nil 197} 198 199// ShowSubcommandHelp prints help for the given subcommand 200func ShowSubcommandHelp(c *Context) error { 201 return ShowCommandHelp(c, c.Command.Name) 202} 203 204// ShowVersion prints the version number of the App 205func ShowVersion(c *Context) { 206 VersionPrinter(c) 207} 208 209func printVersion(c *Context) { 210 fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version) 211} 212 213// ShowCompletions prints the lists of commands within a given context 214func ShowCompletions(c *Context) { 215 a := c.App 216 if a != nil && a.BashComplete != nil { 217 a.BashComplete(c) 218 } 219} 220 221// ShowCommandCompletions prints the custom completions for a given command 222func ShowCommandCompletions(ctx *Context, command string) { 223 c := ctx.App.Command(command) 224 if c != nil && c.BashComplete != nil { 225 c.BashComplete(ctx) 226 } 227} 228 229func printHelpCustom(out io.Writer, templ string, data interface{}, customFunc map[string]interface{}) { 230 funcMap := template.FuncMap{ 231 "join": strings.Join, 232 } 233 if customFunc != nil { 234 for key, value := range customFunc { 235 funcMap[key] = value 236 } 237 } 238 239 w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0) 240 t := template.Must(template.New("help").Funcs(funcMap).Parse(templ)) 241 err := t.Execute(w, data) 242 if err != nil { 243 // If the writer is closed, t.Execute will fail, and there's nothing 244 // we can do to recover. 245 if os.Getenv("CLI_TEMPLATE_ERROR_DEBUG") != "" { 246 fmt.Fprintf(ErrWriter, "CLI TEMPLATE ERROR: %#v\n", err) 247 } 248 return 249 } 250 w.Flush() 251} 252 253func printHelp(out io.Writer, templ string, data interface{}) { 254 printHelpCustom(out, templ, data, nil) 255} 256 257func checkVersion(c *Context) bool { 258 found := false 259 if VersionFlag.GetName() != "" { 260 eachName(VersionFlag.GetName(), func(name string) { 261 if c.GlobalBool(name) || c.Bool(name) { 262 found = true 263 } 264 }) 265 } 266 return found 267} 268 269func checkHelp(c *Context) bool { 270 found := false 271 if HelpFlag.GetName() != "" { 272 eachName(HelpFlag.GetName(), func(name string) { 273 if c.GlobalBool(name) || c.Bool(name) { 274 found = true 275 } 276 }) 277 } 278 return found 279} 280 281func checkCommandHelp(c *Context, name string) bool { 282 if c.Bool("h") || c.Bool("help") { 283 ShowCommandHelp(c, name) 284 return true 285 } 286 287 return false 288} 289 290func checkSubcommandHelp(c *Context) bool { 291 if c.Bool("h") || c.Bool("help") { 292 ShowSubcommandHelp(c) 293 return true 294 } 295 296 return false 297} 298 299func checkShellCompleteFlag(a *App, arguments []string) (bool, []string) { 300 if !a.EnableBashCompletion { 301 return false, arguments 302 } 303 304 pos := len(arguments) - 1 305 lastArg := arguments[pos] 306 307 if lastArg != "--"+BashCompletionFlag.GetName() { 308 return false, arguments 309 } 310 311 return true, arguments[:pos] 312} 313 314func checkCompletions(c *Context) bool { 315 if !c.shellComplete { 316 return false 317 } 318 319 if args := c.Args(); args.Present() { 320 name := args.First() 321 if cmd := c.App.Command(name); cmd != nil { 322 // let the command handle the completion 323 return false 324 } 325 } 326 327 ShowCompletions(c) 328 return true 329} 330 331func checkCommandCompletions(c *Context, name string) bool { 332 if !c.shellComplete { 333 return false 334 } 335 336 ShowCommandCompletions(c, name) 337 return true 338} 339