Skip to content

Commit

Permalink
feat: add roomy usage message layout
Browse files Browse the repository at this point in the history
  • Loading branch information
steverusso committed May 28, 2024
1 parent ed0c4e8 commit 97cf29d
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 33 deletions.
17 changes: 10 additions & 7 deletions clap.gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,15 @@ usage:
goclap [options]
options:
-type <arg> The root command struct name
-srcdir <arg> Directory of source files to parse (default ".")
-with-version Include goclap's version info in the generated code
-out <arg> Output file path (default "./clap.gen.go")
-usg-text-width <arg> Max width for lines of text in the usage message
-version Print version info and exit
-h Show this help message`
-type <arg> The root command struct name
-srcdir <arg> Directory of source files to parse (default ".")
-with-version Include goclap's version info in the generated code
-out <arg> Output file path (default "./clap.gen.go")
-usg-layout-kind <arg> How the usage message for each command will be structured
(possible values: packed or roomy)
-usg-text-width <arg> Max width for lines of text in the usage message
-version Print version info and exit
-h Show this help message`
}

func (c *goclap) Parse(args []string) {
Expand All @@ -140,6 +142,7 @@ func (c *goclap) Parse(args []string) {
{name: "srcdir", value: clapNewString(&c.srcDir)},
{name: "with-version", value: clapNewBool(&c.withVersion)},
{name: "out", value: clapNewString(&c.outFilePath)},
{name: "usg-layout-kind", value: clapNewString(&c.usgLayoutKind)},
{name: "usg-text-width", value: clapNewInt(&c.usgTextWidth)},
{name: "version", value: clapNewBool(&c.version)},
},
Expand Down
97 changes: 72 additions & 25 deletions generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ var (
parseFnTmplText string
)

func generate(incVersion bool, pkgName string, usgTextWidth int, root *command) ([]byte, error) {
g, err := newGenerator(usgTextWidth)
func generate(incVersion bool, pkgName string, usgTextWidth int, usgLayoutKind string, root *command) ([]byte, error) {
g, err := newGenerator(usgTextWidth, usgLayoutKind)
if err != nil {
return nil, fmt.Errorf("initializing generator: %w", err)
}
Expand All @@ -35,13 +35,14 @@ func generate(incVersion bool, pkgName string, usgTextWidth int, root *command)
}

type generator struct {
buf bytes.Buffer
usgTextWidth int
usgFnTmpl *template.Template
parseFnTmpl *template.Template
buf bytes.Buffer
usgTextWidth int
usgLayoutKind string
usgFnTmpl *template.Template
parseFnTmpl *template.Template
}

func newGenerator(usgTextWidth int) (generator, error) {
func newGenerator(usgTextWidth int, usgLayoutKind string) (generator, error) {
usgFnTmpl := template.Must(template.New("usagefunc").Parse(usgFnTmplText))

parseFuncs := template.FuncMap{
Expand All @@ -51,10 +52,12 @@ func newGenerator(usgTextWidth int) (generator, error) {
if err != nil {
return generator{}, fmt.Errorf("parsing template: %w", err)
}

return generator{
usgTextWidth: usgTextWidth,
usgFnTmpl: usgFnTmpl,
parseFnTmpl: parseFnTmpl,
usgTextWidth: usgTextWidth,
usgLayoutKind: usgLayoutKind,
usgFnTmpl: usgFnTmpl,
parseFnTmpl: parseFnTmpl,
}, nil
}

Expand Down Expand Up @@ -176,12 +179,25 @@ func (g *generator) genCmdUsageFunc(c *command) error {
}
}
for i, o := range c.Opts {
paddedNameAndArg := fmt.Sprintf(" %-*s ", optNameColWidth, o.usgNameAndArg())
desc := o.data.Blurb
if v, ok := o.data.getConfig("env"); ok {
desc += " [$" + v + "]"
switch g.usgLayoutKind {
case "roomy":
content := " " + o.usgNameAndArg() + "\n"
content += " " + wrapBlurb(o.data.Blurb, 6, g.usgTextWidth)
if v, ok := o.data.getConfig("env"); ok {
content += "\n\n [env: " + v + "]"
}
if i < len(c.Opts)-1 {
content += "\n"
}
optUsgs[i] = content
default:
paddedNameAndArg := fmt.Sprintf(" %-*s ", optNameColWidth, o.usgNameAndArg())
desc := o.data.Blurb
if v, ok := o.data.getConfig("env"); ok {
desc += " [$" + v + "]"
}
optUsgs[i] = paddedNameAndArg + wrapBlurb(desc, len(paddedNameAndArg), g.usgTextWidth)
}
optUsgs[i] = paddedNameAndArg + wrapBlurb(desc, len(paddedNameAndArg), g.usgTextWidth)
}
}

Expand All @@ -194,12 +210,25 @@ func (g *generator) genCmdUsageFunc(c *command) error {
}
}
for i, a := range c.Args {
paddedName := fmt.Sprintf(" %-*s ", argNameColWidth, a.UsgName())
desc := a.data.Blurb
if v, ok := a.data.getConfig("env"); ok {
desc += " [$" + v + "]"
switch g.usgLayoutKind {
case "roomy":
content := " " + a.UsgName() + "\n"
content += " " + wrapBlurb(a.data.Blurb, 6, g.usgTextWidth)
if v, ok := a.data.getConfig("env"); ok {
content += "\n\n [env: " + v + "]"
}
if i < len(c.Args)-1 {
content += "\n"
}
argUsgs[i] = content
default:
paddedName := fmt.Sprintf(" %-*s ", argNameColWidth, a.UsgName())
desc := a.data.Blurb
if v, ok := a.data.getConfig("env"); ok {
desc += " [$" + v + "]"
}
argUsgs[i] = paddedName + wrapBlurb(desc, len(paddedName), g.usgTextWidth)
}
argUsgs[i] = paddedName + wrapBlurb(desc, len(paddedName), g.usgTextWidth)
}
}

Expand All @@ -212,8 +241,18 @@ func (g *generator) genCmdUsageFunc(c *command) error {
}
}
for i, sc := range c.Subcmds {
paddedName := fmt.Sprintf(" %-*s ", subcmdNameColWidth, sc.UsgName())
subcmdUsgs[i] = paddedName + wrapBlurb(sc.Data.Blurb, len(paddedName), g.usgTextWidth)
switch g.usgLayoutKind {
case "roomy":
content := " " + sc.UsgName() + "\n"
content += " " + wrapBlurb(sc.Data.Blurb, 6, g.usgTextWidth)
if i < len(c.Subcmds)-1 {
content += "\n"
}
subcmdUsgs[i] = content
default:
paddedName := fmt.Sprintf(" %-*s ", subcmdNameColWidth, sc.UsgName())
subcmdUsgs[i] = paddedName + wrapBlurb(sc.Data.Blurb, len(paddedName), g.usgTextWidth)
}
}
}

Expand Down Expand Up @@ -286,9 +325,17 @@ func (c *command) Overview() string {
var s strings.Builder
for i := range paras {
s.WriteString(" ")
s.WriteString(paras[i])
if i != len(paras)-1 {
s.WriteString("\n\n")
// Drop any trailing new lines from the last paragraph so that there won't be any
// extra space separating it from the options section that follows.
if i == len(paras)-1 {
s.WriteString(strings.TrimRight(paras[i], "\n"))
} else {
s.WriteString(paras[i])
}
// Separate the over paragraphs with a blank line by ensuring each paragraph
// except the last one ends with two new lines.
if i != len(paras)-1 && !strings.HasSuffix(paras[i], "\n\n") {
s.WriteString("\n")
}
}
return s.String()
Expand Down
7 changes: 6 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ type goclap struct {
//
// clap:opt out
outFilePath string
// How the usage message for each command will be structured (possible values: packed
// or roomy).
//
// clap:opt usg-layout-kind
usgLayoutKind string
// Max width for lines of text in the usage message.
//
// clap:opt usg-text-width
Expand Down Expand Up @@ -163,7 +168,7 @@ func gen(c *goclap) error {
return err
}

code, err := generate(c.withVersion, pkgName, c.usgTextWidth, &rootCmd)
code, err := generate(c.withVersion, pkgName, c.usgTextWidth, c.usgLayoutKind, &rootCmd)
if err != nil {
return err
}
Expand Down

0 comments on commit 97cf29d

Please sign in to comment.