-
Notifications
You must be signed in to change notification settings - Fork 280
Expand file tree
/
Copy pathusage.go
More file actions
164 lines (144 loc) · 5 KB
/
Copy pathusage.go
File metadata and controls
164 lines (144 loc) · 5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package kingpin
import (
"bytes"
"errors"
"fmt"
"go/doc"
"io"
"strings"
)
var preIndent = " "
// FormatTwoColumns formats rows of two-column data (e.g., flags and their descriptions)
// into aligned output written to w, with the given indent, padding, and total width.
func FormatTwoColumns(w io.Writer, indent, padding, width int, rows [][2]string) {
// Find size of first column.
s := 0
for _, row := range rows {
if c := len(row[0]); c > s && c < 30 {
s = c
}
}
indentStr := strings.Repeat(" ", indent)
offsetStr := strings.Repeat(" ", s+padding)
for _, row := range rows {
buf := bytes.NewBuffer(nil)
doc.ToText(buf, row[1], "", preIndent, width-s-padding-indent)
lines := strings.Split(strings.TrimRight(buf.String(), "\n"), "\n")
fmt.Fprintf(w, "%s%-*s%*s", indentStr, s, row[0], padding, "")
if len(row[0]) >= 30 {
fmt.Fprintf(w, "\n%s%s", indentStr, offsetStr)
}
fmt.Fprintf(w, "%s\n", lines[0])
for _, line := range lines[1:] {
fmt.Fprintf(w, "%s%s%s\n", indentStr, offsetStr, line)
}
}
}
// Usage writes application usage to w. It parses args to determine
// appropriate help context, such as which command to show help for.
func (a *Application) Usage(args []string) {
context, err := a.parseContext(true, args)
a.FatalIfError(err, "")
if err := a.UsageForContext(context); err != nil {
panic(err)
}
}
func formatAppUsage(app *ApplicationModel) string {
s := []string{app.Name}
if len(app.Flags) > 0 {
s = append(s, app.FlagSummary())
}
if len(app.Args) > 0 {
s = append(s, app.ArgSummary())
}
return strings.Join(s, " ")
}
func formatCmdUsage(app *ApplicationModel, cmd *CmdModel) string {
s := []string{app.Name, cmd.String()}
if len(cmd.Flags) > 0 {
s = append(s, cmd.FlagSummary())
}
if len(cmd.Args) > 0 {
s = append(s, cmd.ArgSummary())
}
return strings.Join(s, " ")
}
// FormatFlagCompact formats a flag name without placeholder value for compact display.
func FormatFlagCompact(haveShort bool, flag *FlagModel) string {
flagString := ""
flagName := flag.Name
if flag.IsBoolFlag() {
flagName = "[no-]" + flagName
}
if flag.Short != 0 {
flagString += fmt.Sprintf("-%c, --%s", flag.Short, flagName)
} else {
if haveShort {
flagString += fmt.Sprintf(" --%s", flagName)
} else {
flagString += fmt.Sprintf("--%s", flagName)
}
}
return flagString
}
// FormatFlag formats a flag with its placeholder value and cumulative indicator.
func FormatFlag(haveShort bool, flag *FlagModel) string {
flagString := FormatFlagCompact(haveShort, flag)
if !flag.IsBoolFlag() {
flagString += fmt.Sprintf("=%s", flag.FormatPlaceHolder())
}
if v, ok := flag.Value.(repeatableFlag); ok && v.IsCumulative() {
flagString += " ..."
}
return flagString
}
// UsageForContext displays usage information from a ParseContext (obtained from
// Application.ParseContext() or Action(f) callbacks).
func (a *Application) UsageForContext(context *ParseContext) error {
if a.usageTemplate != "" && a.templateRenderer != nil {
return a.templateRenderer(a, context, 2, a.usageTemplate)
}
if a.usageRenderer != nil {
return a.usageForContextWithUsageRenderer(context, 2, a.usageRenderer)
}
if a.usageFuncs != nil && a.templateRenderer != nil {
return a.templateRenderer(a, context, 2, DefaultUsageTemplate)
}
return a.usageForContextWithUsageRenderer(context, 2, RenderDefault)
}
// UsageForContextWithUsageRenderer renders usage without using text/template.
// This is the fallback path when no UsageRenderer is set and the template doesn't match
// a built-in renderer. Callers who want to avoid pulling in text/template should
// use UsageRenderer or one of the built-in renderer constants.
func (a *Application) UsageForContextWithUsageRenderer(context *ParseContext, indent int) error {
return a.usageForContextWithUsageRenderer(context, indent, a.usageRenderer)
}
func (a *Application) usageForContextWithUsageRenderer(context *ParseContext, indent int, fn UsageRenderer) error {
if fn == nil {
return errors.New("no usage renderer provided")
}
width := guessWidth(a.usageWriter)
var selectedCommand *CmdModel
if context.SelectedCommand != nil {
selectedCommand = context.SelectedCommand.Model()
}
return fn(a.usageWriter, &UsageContext{
App: a.Model(),
Indent: indent,
Width: width,
Context: &UsageParseContext{
SelectedCommand: selectedCommand,
FlagGroupModel: context.flags.Model(),
ArgGroupModel: context.arguments.Model(),
},
})
}
// UsageForContextWithTemplate renders usage using text/template for custom template strings.
// This is the fallback path when no UsageRenderer is set. Callers who want to avoid pulling in text/template
// should specify a UsageRenderer and call UsageForContextWithUsageRenderer instead.
//
// Note: calling this method directly will cause text/template to be linked into the binary.
// For dead code elimination, prefer UsageRenderer with a UsageRenderer.
func (a *Application) UsageForContextWithTemplate(context *ParseContext, indent int, tmpl string) error {
return templateRenderFunc(a, context, indent, tmpl)
}