Skip to content

Commit 6f19d1f

Browse files
Move flag usage block writing into command.go
1 parent 6b9aa15 commit 6f19d1f

6 files changed

Lines changed: 35 additions & 182 deletions

File tree

command.go

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"strings"
1212
"unicode/utf8"
1313

14+
publicflag "go.followtheprocess.codes/cli/flag"
15+
1416
"go.followtheprocess.codes/cli/internal/arg"
1517
"go.followtheprocess.codes/cli/internal/flag"
1618
"go.followtheprocess.codes/cli/internal/style"
@@ -462,11 +464,6 @@ func showHelp(cmd *Command) error {
462464
return errors.New("showHelp called on a nil Command")
463465
}
464466

465-
usage, err := cmd.flagSet().Usage()
466-
if err != nil {
467-
return fmt.Errorf("could not write usage: %w", err)
468-
}
469-
470467
// Note: The decision to not use text/template here is intentional, template calls
471468
// reflect.Value.MethodByName() and/or reflect.Type.MethodByName() which disables dead
472469
// code elimination in the compiler, meaning any application that uses cli for it's
@@ -540,7 +537,10 @@ func showHelp(cmd *Command) error {
540537

541538
s.WriteString(style.Title.Text("Options"))
542539
s.WriteString(":\n\n")
543-
s.WriteString(usage)
540+
541+
if err := writeFlags(cmd, s); err != nil {
542+
return err
543+
}
544544

545545
// Subcommand help
546546
if len(cmd.subcommands) != 0 {
@@ -657,6 +657,35 @@ func writeSubcommands(cmd *Command, s *strings.Builder) error {
657657
return nil
658658
}
659659

660+
// writeFlags writes the flag usage block to the help text string builder.
661+
func writeFlags(cmd *Command, s *strings.Builder) error {
662+
tw := style.Tabwriter(s)
663+
664+
for name, fl := range cmd.flags.All() {
665+
var shorthand string
666+
if fl.Short() != publicflag.NoShortHand {
667+
shorthand = "-" + string(fl.Short())
668+
} else {
669+
shorthand = "N/A"
670+
}
671+
672+
fmt.Fprintf(
673+
tw,
674+
" %s\t--%s\t%s\t%s\n",
675+
style.Bold.Text(shorthand),
676+
style.Bold.Text(name),
677+
fl.Type(),
678+
fl.Usage(),
679+
)
680+
}
681+
682+
if err := tw.Flush(); err != nil {
683+
return fmt.Errorf("could not write flag usage: %w", err)
684+
}
685+
686+
return nil
687+
}
688+
660689
// writeFooter writes the footer to the help text string builder.
661690
func writeFooter(cmd *Command, s *strings.Builder) {
662691
s.WriteByte('\n')

internal/flag/set.go

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package flag
22

33
import (
4-
"bytes"
54
"errors"
65
"fmt"
76
"iter"
@@ -10,12 +9,8 @@ import (
109

1110
"go.followtheprocess.codes/cli/flag"
1211
"go.followtheprocess.codes/cli/internal/format"
13-
"go.followtheprocess.codes/cli/internal/style"
1412
)
1513

16-
// usageBufferSize is sufficient to hold most commands flag usage text.
17-
const usageBufferSize = 256
18-
1914
// Set is a set of command line flags.
2015
type Set struct {
2116
flags map[string]Value // The actual stored flags, can lookup by name
@@ -207,51 +202,6 @@ func (s *Set) All() iter.Seq2[string, Value] {
207202
}
208203
}
209204

210-
// Usage returns a string containing the usage info of all flags in the set.
211-
func (s *Set) Usage() (string, error) {
212-
buf := &bytes.Buffer{}
213-
buf.Grow(usageBufferSize)
214-
215-
// Flags should be sorted alphabetically
216-
names := make([]string, 0, len(s.flags))
217-
for name := range s.flags {
218-
names = append(names, name)
219-
}
220-
221-
slices.Sort(names)
222-
223-
tw := style.Tabwriter(buf)
224-
225-
for _, name := range names {
226-
f := s.flags[name]
227-
if f == nil {
228-
return "", fmt.Errorf("Value stored against key %s was nil", name) // Should never happen
229-
}
230-
231-
var shorthand string
232-
if f.Short() != flag.NoShortHand {
233-
shorthand = "-" + string(f.Short())
234-
} else {
235-
shorthand = "N/A"
236-
}
237-
238-
fmt.Fprintf(
239-
tw,
240-
" %s\t--%s\t%s\t%s\n",
241-
style.Bold.Text(shorthand),
242-
style.Bold.Text(name),
243-
f.Type(),
244-
f.Usage(),
245-
)
246-
}
247-
248-
if err := tw.Flush(); err != nil {
249-
return "", fmt.Errorf("could not format flags: %w", err)
250-
}
251-
252-
return buf.String(), nil
253-
}
254-
255205
// parseLongFlag parses a single long flag e.g. --delete. It is passed
256206
// the possible long flag and the rest of the argument list and returns
257207
// the remaining arguments after it's done parsing to the caller.

internal/flag/set_test.go

Lines changed: 0 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package flag_test
22

33
import (
4-
goflag "flag"
5-
"fmt"
64
"iter"
75
"maps"
86
"slices"
@@ -12,15 +10,9 @@ import (
1210
publicflag "go.followtheprocess.codes/cli/flag"
1311
"go.followtheprocess.codes/cli/internal/flag"
1412
"go.followtheprocess.codes/cli/internal/format"
15-
"go.followtheprocess.codes/snapshot"
1613
"go.followtheprocess.codes/test"
1714
)
1815

19-
var (
20-
debug = goflag.Bool("debug", false, "Print debug output during tests")
21-
update = goflag.Bool("update", false, "Update golden files")
22-
)
23-
2416
func TestParse(t *testing.T) {
2517
tests := []struct {
2618
name string // The name of the test case
@@ -1305,114 +1297,6 @@ func TestAll(t *testing.T) {
13051297
}
13061298
}
13071299

1308-
func TestUsage(t *testing.T) {
1309-
tests := []struct {
1310-
newSet func(t *testing.T) *flag.Set // Function to build the flag set under test
1311-
name string // Name of the test case
1312-
}{
1313-
{
1314-
name: "simple",
1315-
newSet: func(t *testing.T) *flag.Set {
1316-
help, err := flag.New(new(bool), "help", 'h', "Show help for test", flag.Config[bool]{})
1317-
test.Ok(t, err)
1318-
1319-
version, err := flag.New(new(bool), "version", 'V', "Show version info for test", flag.Config[bool]{})
1320-
test.Ok(t, err)
1321-
1322-
set := flag.NewSet()
1323-
1324-
err = flag.AddToSet(set, help)
1325-
test.Ok(t, err)
1326-
1327-
err = flag.AddToSet(set, version)
1328-
test.Ok(t, err)
1329-
1330-
return set
1331-
},
1332-
},
1333-
{
1334-
name: "no shorthand",
1335-
newSet: func(t *testing.T) *flag.Set {
1336-
help, err := flag.New(new(bool), "help", 'h', "Show help for test", flag.Config[bool]{})
1337-
test.Ok(t, err)
1338-
1339-
version, err := flag.New(new(bool), "version", 'V', "Show version info for test", flag.Config[bool]{})
1340-
test.Ok(t, err)
1341-
1342-
up, err := flag.New(new(bool), "update", publicflag.NoShortHand, "Update something", flag.Config[bool]{})
1343-
test.Ok(t, err)
1344-
1345-
set := flag.NewSet()
1346-
1347-
err = flag.AddToSet(set, help)
1348-
test.Ok(t, err)
1349-
1350-
err = flag.AddToSet(set, version)
1351-
test.Ok(t, err)
1352-
1353-
err = flag.AddToSet(set, up)
1354-
test.Ok(t, err)
1355-
1356-
return set
1357-
},
1358-
},
1359-
{
1360-
name: "full",
1361-
newSet: func(t *testing.T) *flag.Set {
1362-
help, err := flag.New(new(bool), "help", 'h', "Show help for test", flag.Config[bool]{})
1363-
test.Ok(t, err)
1364-
1365-
version, err := flag.New(new(bool), "version", 'V', "Show version info for test", flag.Config[bool]{})
1366-
test.Ok(t, err)
1367-
1368-
up, err := flag.New(new(bool), "update", publicflag.NoShortHand, "Update something", flag.Config[bool]{})
1369-
test.Ok(t, err)
1370-
1371-
count, err := flag.New(new(int), "count", 'c', "Count things", flag.Config[int]{})
1372-
test.Ok(t, err)
1373-
1374-
thing, err := flag.New(new(string), "thing", 't', "Name the thing", flag.Config[string]{})
1375-
test.Ok(t, err)
1376-
1377-
set := flag.NewSet()
1378-
1379-
err = flag.AddToSet(set, help)
1380-
test.Ok(t, err)
1381-
1382-
err = flag.AddToSet(set, version)
1383-
test.Ok(t, err)
1384-
1385-
err = flag.AddToSet(set, up)
1386-
test.Ok(t, err)
1387-
1388-
err = flag.AddToSet(set, count)
1389-
test.Ok(t, err)
1390-
1391-
err = flag.AddToSet(set, thing)
1392-
test.Ok(t, err)
1393-
1394-
return set
1395-
},
1396-
},
1397-
}
1398-
1399-
for _, tt := range tests {
1400-
t.Run(tt.name, func(t *testing.T) {
1401-
snap := snapshot.New(t, snapshot.Update(*update))
1402-
set := tt.newSet(t)
1403-
1404-
got, err := set.Usage()
1405-
test.Ok(t, err)
1406-
1407-
if *debug {
1408-
fmt.Printf("DEBUG (%s)\n_____\n\n%s\n", tt.name, got)
1409-
}
1410-
1411-
snap.Snap(got)
1412-
})
1413-
}
1414-
}
1415-
14161300
func BenchmarkParse(b *testing.B) {
14171301
set := flag.NewSet()
14181302
f, err := flag.New(new(int), "count", 'c', "Count something", flag.Config[int]{})

internal/flag/testdata/snapshots/TestUsage/full.snap.txt

Lines changed: 0 additions & 5 deletions
This file was deleted.

internal/flag/testdata/snapshots/TestUsage/no_shorthand.snap.txt

Lines changed: 0 additions & 3 deletions
This file was deleted.

internal/flag/testdata/snapshots/TestUsage/simple.snap.txt

Lines changed: 0 additions & 2 deletions
This file was deleted.

0 commit comments

Comments
 (0)