Skip to content

Commit 8f5dbcc

Browse files
committed
refactor: fix the short help positional args that are too long
1 parent cb3120b commit 8f5dbcc

3 files changed

Lines changed: 99 additions & 71 deletions

File tree

cli_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ func TestParsing(t *testing.T) {
389389
Case: ttCase(),
390390
args: []string{"one", "-h"},
391391
expErr: HelpRequestError{
392-
HelpMsg: "cmd one - \n\nusage:\n one [options]\n\noptions:\n --cc <arg> (required)\n -h, --help Show this help message and exit.\n",
392+
HelpMsg: "cmd one\n\nusage:\n one [options]\n\noptions:\n --cc <arg> (required)\n -h, --help Show this help message and exit.\n",
393393
},
394394
},
395395
{

help.go

Lines changed: 54 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -23,32 +23,10 @@ const (
2323

2424
func DefaultShortHelp(c *CommandInfo) string {
2525
u := strings.Builder{}
26-
u.Grow((len(c.Opts) + len(c.Args) + len(c.Subcmds)) * 200)
26+
u.Grow((len(c.Opts) + len(c.Args) + len(c.Subcmds)) * 500)
2727

28-
u.WriteString(strings.Join(c.Path, " "))
29-
u.WriteString(" - ")
30-
u.WriteString(c.HelpBlurb)
31-
32-
// build default usage line or range user provided ones
33-
u.WriteString("\n\nusage:\n")
34-
if len(c.HelpUsage) == 0 {
35-
u.WriteString(" ")
36-
u.WriteString(c.Path[len(c.Path)-1])
37-
u.WriteString(" [options]")
38-
switch {
39-
case len(c.Args) > 0:
40-
u.WriteString(" [arguments]")
41-
case len(c.Subcmds) > 0:
42-
u.WriteString(" <command>")
43-
}
44-
u.WriteByte('\n')
45-
} else {
46-
for i := range c.HelpUsage {
47-
u.WriteString(" ")
48-
u.WriteString(c.HelpUsage[i])
49-
u.WriteByte('\n')
50-
}
51-
}
28+
helpWriteHeader(&u, c)
29+
helpWriteUsageLines(&u, c)
5230

5331
u.WriteString("\noptions:\n")
5432
opts := slices.Clone(c.Opts)
@@ -136,18 +114,9 @@ func DefaultShortHelp(c *CommandInfo) string {
136114
if len(c.Args) > 0 {
137115
u.WriteString("\narguments:\n")
138116

139-
var argNameColWidth int
140-
for i := range c.Args {
141-
argName := c.Args[i].ID
142-
if c.Args[i].ValueName != "" {
143-
argName = c.Args[i].ValueName
144-
}
145-
if l := len(argName); l > argNameColWidth && l <= helpShortMsgMaxFirstColLen {
146-
argNameColWidth = l
147-
}
148-
}
149-
argNameColWidth += 2
150-
for _, a := range c.Args {
117+
argNames := make([]string, len(c.Args))
118+
argNameColWidth := 0
119+
for i, a := range c.Args {
151120
argName := a.ID
152121
if a.ValueName != "" {
153122
argName = a.ValueName
@@ -157,7 +126,12 @@ func DefaultShortHelp(c *CommandInfo) string {
157126
} else {
158127
argName = "[" + argName + "]"
159128
}
160-
paddedNameAndArg := fmt.Sprintf(" %-*s", argNameColWidth, argName)
129+
argNames[i] = argName
130+
if l := len(argName); l > argNameColWidth {
131+
argNameColWidth = l
132+
}
133+
}
134+
for i, a := range c.Args {
161135
desc := a.HelpBlurb
162136
if a.IsRequired {
163137
desc += " (required)"
@@ -168,14 +142,16 @@ func DefaultShortHelp(c *CommandInfo) string {
168142
if a.EnvVar != "" {
169143
desc += " [$" + a.EnvVar + "]"
170144
}
171-
content := paddedNameAndArg
172-
if len(paddedNameAndArg) > helpShortMsgMaxFirstColLen {
173-
content += "\n" + strings.Repeat(" ", argNameColWidth+5)
145+
146+
var content string
147+
if argNameColWidth > helpShortMsgMaxFirstColLen {
148+
u.WriteString(" " + argNames[i])
149+
u.WriteString("\n" + strings.Repeat(" ", 5))
150+
u.WriteString(wrapBlurb(desc, 5, helpMsgTextWidth))
174151
} else {
175-
content += " "
152+
fmt.Fprintf(&u, " %-*s ", argNameColWidth, argNames[i])
153+
u.WriteString(wrapBlurb(desc, len(content)+3, helpMsgTextWidth))
176154
}
177-
content += wrapBlurb(desc, len(paddedNameAndArg)+3, helpMsgTextWidth)
178-
u.WriteString(content)
179155
u.WriteByte('\n')
180156
}
181157
}
@@ -189,37 +165,16 @@ func DefaultShortHelp(c *CommandInfo) string {
189165

190166
func DefaultFullHelp(c *CommandInfo) string {
191167
u := strings.Builder{}
192-
u.Grow((len(c.Opts) + len(c.Args) + len(c.Subcmds)) * 200)
168+
u.Grow((len(c.Opts) + len(c.Args) + len(c.Subcmds)) * 500)
193169

194-
u.WriteString(strings.Join(c.Path, " "))
195-
u.WriteString(" - ")
196-
u.WriteString(c.HelpBlurb)
170+
helpWriteHeader(&u, c)
197171

198172
if c.HelpExtra != "" {
199173
u.WriteString("\n\noverview:\n")
200174
u.WriteString(" " + wrapBlurb(c.HelpExtra, 2, helpMsgTextWidth))
201175
}
202176

203-
// build default usage line or range user provided ones
204-
u.WriteString("\n\nusage:\n")
205-
if len(c.HelpUsage) == 0 {
206-
u.WriteString(" ")
207-
u.WriteString(c.Path[len(c.Path)-1])
208-
u.WriteString(" [options]")
209-
switch {
210-
case len(c.Args) > 0:
211-
u.WriteString(" [arguments]")
212-
case len(c.Subcmds) > 0:
213-
u.WriteString(" <command>")
214-
}
215-
u.WriteByte('\n')
216-
} else {
217-
for i := range c.HelpUsage {
218-
u.WriteString(" ")
219-
u.WriteString(c.HelpUsage[i])
220-
u.WriteByte('\n')
221-
}
222-
}
177+
helpWriteUsageLines(&u, c)
223178

224179
u.WriteString("\noptions:\n")
225180
opts := slices.Clone(c.Opts)
@@ -359,6 +314,37 @@ func (o *InputInfo) optUsgArgName() string {
359314
return "<arg>"
360315
}
361316

317+
func helpWriteHeader(u *strings.Builder, c *CommandInfo) {
318+
u.WriteString(strings.Join(c.Path, " "))
319+
if c.HelpBlurb != "" {
320+
u.WriteString(" - ")
321+
u.WriteString(c.HelpBlurb)
322+
}
323+
}
324+
325+
// Build default usage line or use the user provided ones.
326+
func helpWriteUsageLines(u *strings.Builder, c *CommandInfo) {
327+
u.WriteString("\n\nusage:\n")
328+
if len(c.HelpUsage) == 0 {
329+
u.WriteString(" ")
330+
u.WriteString(c.Path[len(c.Path)-1])
331+
u.WriteString(" [options]")
332+
switch {
333+
case len(c.Args) > 0:
334+
u.WriteString(" [arguments]")
335+
case len(c.Subcmds) > 0:
336+
u.WriteString(" <command>")
337+
}
338+
u.WriteByte('\n')
339+
} else {
340+
for i := range c.HelpUsage {
341+
u.WriteString(" ")
342+
u.WriteString(c.HelpUsage[i])
343+
u.WriteByte('\n')
344+
}
345+
}
346+
}
347+
362348
func helpWriteSubcmds(u *strings.Builder, c *CommandInfo) {
363349
var maxCmdNameLen int
364350
for i := range c.Subcmds {

help_test.go

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@ func TestDefaultHelps(t *testing.T) {
1212
{
1313
Case: ttCase(),
1414
cmdInfo: New().
15+
Help("test example").
1516
Opt(NewOpt("lorem").
1617
Short('l').
1718
Required().
1819
Help("ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.")).
1920
Opt(NewOpt("enim-ad-minim").
2021
Required().
2122
Help("veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.")),
22-
expectedShort: `cli.test -
23+
expectedShort: `cli.test - test example
2324
2425
usage:
2526
cli.test [options]
@@ -34,7 +35,7 @@ options:
3435
ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
3536
ut labore et dolore magna aliqua. (required)
3637
`,
37-
expectedFull: `cli.test -
38+
expectedFull: `cli.test - test example
3839
3940
usage:
4041
cli.test [options]
@@ -50,6 +51,47 @@ options:
5051
-l, --lorem <arg> (required)
5152
ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
5253
ut labore et dolore magna aliqua.
54+
`,
55+
},
56+
{
57+
Case: ttCase(),
58+
cmdInfo: New().
59+
Arg(NewArg("lorem").
60+
Required().
61+
Help("ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.")).
62+
Arg(NewArg("enim-ut-ad-minim-veniam").
63+
Help("quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.")),
64+
expectedShort: `cli.test
65+
66+
usage:
67+
cli.test [options] [arguments]
68+
69+
options:
70+
-h, --help Show this help message and exit.
71+
72+
arguments:
73+
<lorem>
74+
ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
75+
ut labore et dolore magna aliqua. (required)
76+
[enim-ut-ad-minim-veniam]
77+
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
78+
`,
79+
expectedFull: `cli.test
80+
81+
usage:
82+
cli.test [options] [arguments]
83+
84+
options:
85+
-h, --help
86+
Show this help message and exit.
87+
88+
arguments:
89+
<lorem> (required)
90+
ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
91+
ut labore et dolore magna aliqua.
92+
93+
[enim-ut-ad-minim-veniam]
94+
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
5395
`,
5496
},
5597
} {

0 commit comments

Comments
 (0)