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}