|
1 | 1 | package cli |
2 | 2 |
|
3 | 3 | import ( |
4 | | - "cmp" |
5 | 4 | "context" |
6 | 5 | "flag" |
7 | 6 | "fmt" |
8 | | - "slices" |
9 | 7 | "strings" |
10 | 8 |
|
11 | 9 | "github.com/mfridman/cli/pkg/suggest" |
12 | | - "github.com/mfridman/cli/pkg/textutil" |
13 | 10 | ) |
14 | 11 |
|
15 | 12 | // NoExecError is returned when a command has no execution function. |
@@ -86,7 +83,7 @@ type FlagMetadata struct { |
86 | 83 | // f.String("output", "", "output file") |
87 | 84 | // f.Int("count", 0, "number of items") |
88 | 85 | // }) |
89 | | -func FlagsFunc(fn func(*flag.FlagSet)) *flag.FlagSet { |
| 86 | +func FlagsFunc(fn func(f *flag.FlagSet)) *flag.FlagSet { |
90 | 87 | fset := flag.NewFlagSet("", flag.ContinueOnError) |
91 | 88 | fn(fset) |
92 | 89 | return fset |
@@ -117,174 +114,6 @@ func (c *Command) formatUnknownCommandError(unknownCmd string) error { |
117 | 114 | return fmt.Errorf("unknown command %q", unknownCmd) |
118 | 115 | } |
119 | 116 |
|
120 | | -func defaultUsage(c *Command) string { |
121 | | - var b strings.Builder |
122 | | - |
123 | | - // Handle custom usage function |
124 | | - if c.UsageFunc != nil { |
125 | | - return c.UsageFunc(c) |
126 | | - } |
127 | | - |
128 | | - // Short help section |
129 | | - if c.ShortHelp != "" { |
130 | | - for _, line := range textutil.Wrap(c.ShortHelp, 80) { |
131 | | - b.WriteString(line) |
132 | | - b.WriteRune('\n') |
133 | | - } |
134 | | - b.WriteRune('\n') |
135 | | - } |
136 | | - |
137 | | - // Usage section |
138 | | - b.WriteString("Usage:\n ") |
139 | | - if c.Usage != "" { |
140 | | - b.WriteString(c.Usage) |
141 | | - b.WriteRune('\n') |
142 | | - } else { |
143 | | - usage := c.Name |
144 | | - if c.state != nil && len(c.state.commandPath) > 0 { |
145 | | - usage = getCommandPath(c.state.commandPath) |
146 | | - } |
147 | | - if c.Flags != nil { |
148 | | - usage += " [flags]" |
149 | | - } |
150 | | - if len(c.SubCommands) > 0 { |
151 | | - usage += " <command>" |
152 | | - } |
153 | | - b.WriteString(usage) |
154 | | - b.WriteRune('\n') |
155 | | - } |
156 | | - |
157 | | - // Available Commands section |
158 | | - if len(c.SubCommands) > 0 { |
159 | | - b.WriteString("Available Commands:\n") |
160 | | - |
161 | | - sortedCommands := slices.Clone(c.SubCommands) |
162 | | - slices.SortFunc(sortedCommands, func(a, b *Command) int { |
163 | | - return cmp.Compare(a.Name, b.Name) |
164 | | - }) |
165 | | - |
166 | | - maxLen := 0 |
167 | | - for _, sub := range sortedCommands { |
168 | | - if len(sub.Name) > maxLen { |
169 | | - maxLen = len(sub.Name) |
170 | | - } |
171 | | - } |
172 | | - |
173 | | - for _, sub := range sortedCommands { |
174 | | - if sub.ShortHelp == "" { |
175 | | - fmt.Fprintf(&b, " %s\n", sub.Name) |
176 | | - continue |
177 | | - } |
178 | | - |
179 | | - nameWidth := maxLen + 4 |
180 | | - wrapWidth := 80 - nameWidth |
181 | | - |
182 | | - lines := textutil.Wrap(sub.ShortHelp, wrapWidth) |
183 | | - padding := strings.Repeat(" ", maxLen-len(sub.Name)+4) |
184 | | - fmt.Fprintf(&b, " %s%s%s\n", sub.Name, padding, lines[0]) |
185 | | - |
186 | | - indentPadding := strings.Repeat(" ", nameWidth+2) |
187 | | - for _, line := range lines[1:] { |
188 | | - fmt.Fprintf(&b, "%s%s\n", indentPadding, line) |
189 | | - } |
190 | | - } |
191 | | - b.WriteRune('\n') |
192 | | - } |
193 | | - |
194 | | - var flags []flagInfo |
195 | | - |
196 | | - if c.state != nil && len(c.state.commandPath) > 0 { |
197 | | - for i, cmd := range c.state.commandPath { |
198 | | - if cmd.Flags == nil { |
199 | | - continue |
200 | | - } |
201 | | - isGlobal := i < len(c.state.commandPath)-1 |
202 | | - cmd.Flags.VisitAll(func(f *flag.Flag) { |
203 | | - flags = append(flags, flagInfo{ |
204 | | - name: "-" + f.Name, |
205 | | - usage: f.Usage, |
206 | | - defval: f.DefValue, |
207 | | - global: isGlobal, |
208 | | - }) |
209 | | - }) |
210 | | - } |
211 | | - } |
212 | | - |
213 | | - if len(flags) > 0 { |
214 | | - slices.SortFunc(flags, func(a, b flagInfo) int { |
215 | | - return cmp.Compare(a.name, b.name) |
216 | | - }) |
217 | | - |
218 | | - maxLen := 0 |
219 | | - for _, f := range flags { |
220 | | - if len(f.name) > maxLen { |
221 | | - maxLen = len(f.name) |
222 | | - } |
223 | | - } |
224 | | - |
225 | | - hasLocal := false |
226 | | - hasGlobal := false |
227 | | - for _, f := range flags { |
228 | | - if f.global { |
229 | | - hasGlobal = true |
230 | | - } else { |
231 | | - hasLocal = true |
232 | | - } |
233 | | - } |
234 | | - |
235 | | - if hasLocal { |
236 | | - b.WriteString("Flags:\n") |
237 | | - writeFlagSection(&b, flags, maxLen, false) |
238 | | - b.WriteRune('\n') |
239 | | - } |
240 | | - |
241 | | - if hasGlobal { |
242 | | - b.WriteString("Global Flags:\n") |
243 | | - writeFlagSection(&b, flags, maxLen, true) |
244 | | - b.WriteRune('\n') |
245 | | - } |
246 | | - } |
247 | | - |
248 | | - // Help suggestion for subcommands |
249 | | - if len(c.SubCommands) > 0 { |
250 | | - fmt.Fprintf(&b, "Use \"%s [command] --help\" for more information about a command.\n", |
251 | | - getCommandPath(c.state.commandPath)) |
252 | | - } |
253 | | - |
254 | | - return strings.TrimRight(b.String(), "\n") |
255 | | -} |
256 | | - |
257 | | -// writeFlagSection writes either the local or global flags section |
258 | | -func writeFlagSection(b *strings.Builder, flags []flagInfo, maxLen int, global bool) { |
259 | | - for _, f := range flags { |
260 | | - if f.global == global { |
261 | | - nameWidth := maxLen + 4 |
262 | | - wrapWidth := 80 - nameWidth |
263 | | - |
264 | | - usageText := f.usage |
265 | | - if f.defval != "" && f.defval != "false" { |
266 | | - usageText += fmt.Sprintf(" (default %s)", f.defval) |
267 | | - } |
268 | | - |
269 | | - lines := textutil.Wrap(usageText, wrapWidth) |
270 | | - padding := strings.Repeat(" ", maxLen-len(f.name)+4) |
271 | | - fmt.Fprintf(b, " %s%s%s\n", f.name, padding, lines[0]) |
272 | | - |
273 | | - indentPadding := strings.Repeat(" ", nameWidth+2) |
274 | | - for _, line := range lines[1:] { |
275 | | - fmt.Fprintf(b, "%s%s\n", indentPadding, line) |
276 | | - } |
277 | | - } |
278 | | - } |
279 | | -} |
280 | | - |
281 | | -type flagInfo struct { |
282 | | - name string |
283 | | - usage string |
284 | | - defval string |
285 | | - global bool |
286 | | -} |
287 | | - |
288 | 117 | func formatFlagName(name string) string { |
289 | 118 | return "-" + name |
290 | 119 | } |
|
0 commit comments