Skip to content

Commit 933245d

Browse files
committed
Rework help output
1 parent a6e1bd0 commit 933245d

File tree

1 file changed

+68
-12
lines changed

1 file changed

+68
-12
lines changed

defs.go

Lines changed: 68 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"io"
99
"io/fs"
1010
"maps"
11+
"os"
1112
"path/filepath"
1213
"slices"
1314
"sort"
@@ -16,14 +17,6 @@ import (
1617
"github.com/xo/ox/text"
1718
)
1819

19-
// StringWriter extends the [io.StringWriter] interface with a Len() method,
20-
// allowing both [strings.Builder] and [bytes.Buffer] to be used when
21-
// generating help output.
22-
type StringWriter interface {
23-
io.StringWriter
24-
Len() int
25-
}
26-
2720
// AddHelpFlag recursively adds a `--help` flag for all commands in the command
2821
// tree, copying the command's [CommandHelp.Sort], [CommandHelp.CommandSort],
2922
// and [CommandHelp.MaxDist] settings.
@@ -325,7 +318,7 @@ func (help *CommandHelp) SetContext(ctx *Context) {
325318

326319
// WriteTo satisfies the [io.WriterTo] interface.
327320
func (help *CommandHelp) WriteTo(w io.Writer) (int64, error) {
328-
var buf bytes.Buffer
321+
wr := NewStringWriter(w)
329322
for _, f := range []func(StringWriter){
330323
help.AddBanner,
331324
help.AddUsage,
@@ -335,10 +328,10 @@ func (help *CommandHelp) WriteTo(w io.Writer) (int64, error) {
335328
help.AddFlags,
336329
help.AddFooter,
337330
} {
338-
f(&buf)
331+
f(wr)
339332
}
340-
_ = buf.WriteByte('\n')
341-
return buf.WriteTo(w)
333+
_, _ = wr.WriteString("\n")
334+
return int64(wr.Len()), nil
342335
}
343336

344337
// AddBanner adds the command's banner.
@@ -665,6 +658,69 @@ func NewCompletion(name, usage string, dist int) Completion {
665658
}
666659
}
667660

661+
// StringWriter extends the [io.StringWriter] interface with a Len() method,
662+
// allowing both [strings.Builder] and [bytes.Buffer] to be used when
663+
// generating help output.
664+
type StringWriter interface {
665+
io.StringWriter
666+
Len() int
667+
}
668+
669+
// NewStringWriter converts a writer to a [StringWriter], or wraps the writer
670+
// when necessary. Wraps (or directly returns) [os.File], [strings.Builder],
671+
// [bytes.Buffer], or other [io.Writer].
672+
func NewStringWriter(w io.Writer) StringWriter {
673+
switch x := w.(type) {
674+
case *os.File:
675+
return &fileWriter{w: x}
676+
case *strings.Builder:
677+
return x
678+
case *bytes.Buffer:
679+
return x
680+
case StringWriter:
681+
return x
682+
}
683+
return &stringWriter{w: w}
684+
}
685+
686+
// fileWriter wraps writing to a file.
687+
type fileWriter struct {
688+
w *os.File
689+
n bool
690+
}
691+
692+
// WriteString satisfies the [StringWriter] interface.
693+
func (w *fileWriter) WriteString(s string) (int, error) {
694+
w.n = w.n || s != ""
695+
return w.w.WriteString(s)
696+
}
697+
698+
// Len satisfies the [StringWriter] interface.
699+
func (w *fileWriter) Len() int {
700+
if w.n {
701+
return 1
702+
}
703+
return 0
704+
}
705+
706+
type stringWriter struct {
707+
w io.Writer
708+
n bool
709+
}
710+
711+
func (w *stringWriter) WriteString(s string) (int, error) {
712+
w.n = w.n || s != ""
713+
return w.w.Write([]byte(s))
714+
}
715+
716+
// Len satisfies the [StringWriter] interface.
717+
func (w *stringWriter) Len() int {
718+
if w.n {
719+
return 1
720+
}
721+
return 0
722+
}
723+
668724
// has builds a string for a.
669725
func has[T inti | uinti](w StringWriter, a, b T, s string) {
670726
if a&b != 0 {

0 commit comments

Comments
 (0)