commands/cli: Added a helptext generator
Matt Bell committed
Nov 8, 2014 at 01:52 UTC
c169fca5a2826af8bb360bb12e8d7e9886de6fdb
1 file changed
+193
commands/cli/helptext.go
new
+193
@@ -0,0 +1,193 @@
1
+package cli
2
+
3
+import (
4
+ "fmt"
5
+ "strings"
6
+
7
+ cmds "github.com/jbenet/go-ipfs/commands"
8
+)
9
+
10
+const (
11
+ requiredArg = "<%v>"
12
+ optionalArg = "[<%v>]"
13
+ variadicArg = "%v..."
14
+ optionFlag = "-%v"
15
+ optionType = "(%v)"
16
+
17
+ whitespace = "\r\n\t "
18
+)
19
+
20
+// HelpText returns a formatted CLI helptext string, generated for the given command
21
+func HelpText(rootName string, root *cmds.Command, path []string) (string, error) {
22
+ cmd, err := root.Get(path)
23
+ if err != nil {
24
+ return "", err
25
+ }
26
+
27
+ s := ""
28
+ usage := usageText(cmd)
29
+ if len(usage) > 0 {
30
+ usage += " "
31
+ }
32
+ s += fmt.Sprintf("%v %v %v- %v\n\n", rootName, strings.Join(path, " "), usage, cmd.Description)
33
+
34
+ if len(cmd.Help) > 0 {
35
+ s += fmt.Sprintf("%v\n\n", strings.Trim(cmd.Help, whitespace))
36
+ }
37
+
38
+ if cmd.Arguments != nil {
39
+ lines := indent(argumentText(cmd), " ")
40
+ s += fmt.Sprintf("Arguments:\n%v\n\n", strings.Join(lines, "\n"))
41
+ }
42
+
43
+ if cmd.Subcommands != nil {
44
+ lines := indent(subcommandText(cmd, rootName, path), " ")
45
+ s += fmt.Sprintf("Subcommands:\n%v\n\n", strings.Join(lines, "\n"))
46
+ }
47
+
48
+ if cmd.Options != nil {
49
+ lines := indent(optionText(cmd), " ")
50
+ s += fmt.Sprintf("Options:\n%v\n\n", strings.Join(lines, "\n"))
51
+ }
52
+
53
+ return s, nil
54
+}
55
+
56
+func argumentText(cmd *cmds.Command) []string {
57
+ lines := make([]string, len(cmd.Arguments))
58
+
59
+ for i, arg := range cmd.Arguments {
60
+ lines[i] = argUsageText(arg)
61
+ lines[i] += "\n" + arg.Description
62
+ lines[i] = indentString(lines[i], " ") + "\n"
63
+ }
64
+
65
+ return lines
66
+}
67
+
68
+func optionText(cmd ...*cmds.Command) []string {
69
+ // get a slice of the options we want to list out
70
+ options := make([]cmds.Option, 0)
71
+ for _, c := range cmd {
72
+ for _, opt := range c.Options {
73
+ options = append(options, opt)
74
+ }
75
+ }
76
+
77
+ // add option names to output (with each name aligned)
78
+ lines := make([]string, 0)
79
+ j := 0
80
+ for {
81
+ done := true
82
+ i := 0
83
+ for _, opt := range options {
84
+ if len(lines) < i+1 {
85
+ lines = append(lines, "")
86
+ }
87
+ if len(opt.Names) >= j+1 {
88
+ lines[i] += fmt.Sprintf(optionFlag, opt.Names[j])
89
+ }
90
+ if len(opt.Names) > j+1 {
91
+ lines[i] += ", "
92
+ done = false
93
+ }
94
+
95
+ i++
96
+ }
97
+
98
+ if done {
99
+ break
100
+ }
101
+
102
+ lines = align(lines)
103
+ j++
104
+ }
105
+
106
+ // add option types to output
107
+ for i, opt := range options {
108
+ lines[i] += " " + fmt.Sprintf(optionType, opt.Type)
109
+ }
110
+ lines = align(lines)
111
+
112
+ // add option descriptions to output
113
+ for i, opt := range options {
114
+ lines[i] += "\n" + opt.Description
115
+ lines[i] = indentString(lines[i], " ") + "\n"
116
+ }
117
+
118
+ return lines
119
+}
120
+
121
+func subcommandText(cmd *cmds.Command, rootName string, path []string) []string {
122
+ prefix := fmt.Sprintf("%v %v", rootName, strings.Join(path, " "))
123
+ lines := make([]string, len(cmd.Subcommands))
124
+
125
+ i := 0
126
+ for name, sub := range cmd.Subcommands {
127
+ usage := usageText(sub)
128
+ lines[i] = fmt.Sprintf("%v %v %v", prefix, name, usage)
129
+ lines[i] += fmt.Sprintf("\n%v", sub.Description)
130
+ lines[i] = indentString(lines[i], " ") + "\n"
131
+ i++
132
+ }
133
+
134
+ return lines
135
+}
136
+
137
+func usageText(cmd *cmds.Command) string {
138
+ s := ""
139
+ for i, arg := range cmd.Arguments {
140
+ if i != 0 {
141
+ s += " "
142
+ }
143
+ s += argUsageText(arg)
144
+ }
145
+
146
+ return s
147
+}
148
+
149
+func argUsageText(arg cmds.Argument) string {
150
+ s := arg.Name
151
+
152
+ if arg.Required {
153
+ s = fmt.Sprintf(requiredArg, s)
154
+ } else {
155
+ s = fmt.Sprintf(optionalArg, s)
156
+ }
157
+
158
+ if arg.Variadic {
159
+ s = fmt.Sprintf(variadicArg, s)
160
+ }
161
+
162
+ return s
163
+}
164
+
165
+func align(lines []string) []string {
166
+ longest := 0
167
+ for _, line := range lines {
168
+ length := len(line)
169
+ if length > longest {
170
+ longest = length
171
+ }
172
+ }
173
+
174
+ for i, line := range lines {
175
+ length := len(line)
176
+ if length > 0 {
177
+ lines[i] += strings.Repeat(" ", longest-length)
178
+ }
179
+ }
180
+
181
+ return lines
182
+}
183
+
184
+func indent(lines []string, prefix string) []string {
185
+ for i, line := range lines {
186
+ lines[i] = prefix + indentString(line, prefix)
187
+ }
188
+ return lines
189
+}
190
+
191
+func indentString(line string, prefix string) string {
192
+ return strings.Replace(line, "\n", "\n"+prefix, -1)
193
+}