wand/formatman.go
2025-08-26 03:21:35 +02:00

205 lines
4.5 KiB
Go

package wand
import (
"fmt"
"github.com/iancoleman/strcase"
"io"
"strings"
"time"
)
func formatManCommand(printf func(string, ...any), println func(...any), doc doc) {
println(".SH Synopsis")
printf(".B %s", escapeRoff(doc.synopsis.command))
if doc.synopsis.hasOptions {
printf(" [options...]")
}
for i := range doc.synopsis.arguments.names {
printf(
" [%s %s]",
doc.synopsis.arguments.names[i],
doc.synopsis.arguments.types[i],
)
}
min := doc.synopsis.arguments.minPositional
max := doc.synopsis.arguments.maxPositional
if doc.synopsis.arguments.variadic {
println("...")
if min > 0 || max > 0 {
println(".PP")
}
switch {
case min > 0 && max > 0:
printf("min %d and max %d total positional arguments\n", min, max)
case min > 0:
printf("min %d total positional arguments\n", min)
case max > 0:
printf("max %d total positional arguments\n", max)
}
}
if doc.synopsis.hasSubcommands {
if min > 0 || max > 0 {
println(".PP")
}
for i, sc := range doc.subcommands {
if i > 0 {
println(".br")
}
printf("%s %s\n", escapeRoff(doc.name), sc.name)
}
}
if doc.description != "" {
println(".SH Description")
println(manParagraphs(escapeRoff(doc.description)))
}
if len(doc.options) > 0 {
println(".SH Options")
if doc.hasBoolOptions || doc.hasListOptions {
println(".PP")
}
if doc.hasBoolOptions {
println(".B [b]:")
println("booelan flag, true or false, or no argument means true")
}
if doc.hasListOptions {
if doc.hasBoolOptions {
println(".br")
}
println(".B [*]:")
println("accepts multiple instances of the same option")
}
names, descriptions := prepareOptions(doc.options)
for _, n := range names {
println(".TP")
printf(".B %s\n", escapeRoff(n))
if descriptions[n] != "" {
println(manLines(escapeRoff(descriptions[n])))
}
}
}
if len(doc.options) > 0 && commandNameExpression.MatchString(doc.appName) {
println(".SH Environment Variables")
println(manParagraphs(escapeRoff(envDocs)))
println(".PP Example environment variable:")
o := doc.options[0]
println(".TP")
printf(strcase.ToSnake(fmt.Sprintf("%s-%s", doc.appName, o.name)))
printf("=")
if o.isBool {
printf("true")
} else {
printf("42")
}
println()
}
if len(doc.options) > 0 && len(doc.configFiles) > 0 {
println(".SH Configuration Files")
println(manParagraphs(escapeRoff(configDocs)))
println(".PP Config files:")
for i, cf := range doc.configFiles {
if i > 0 {
println(".br")
}
if cf.fromOption {
println(escapeRoff("zero or more configuration files defined by the --config option"))
continue
}
if cf.fn != "" {
printf(escapeRoff(cf.fn))
if cf.optional {
printf(" (optional)")
}
println()
continue
}
}
println(".PP Example configuration entry:")
println(".PP")
o := doc.options[0]
printf(escapeRoff(fmt.Sprintf("# default for --%s:\n", o.name)))
println(".br")
printf(escapeRoff(strcase.ToSnake(o.name)))
printf(" = ")
if o.isBool {
printf("true")
} else {
printf("42")
}
println()
println(".PP Example for discarding an inherited entry:")
println(".PP")
println("# discarding an inherited entry:")
println(".br")
println(escapeRoff(strcase.ToSnake(o.name)))
}
}
func formatManMultiCommand(out io.Writer, doc doc) error {
printf, println, finish := printer(out)
printf(".TH %s 1 %s \"%s\"\n", escapeRoff(doc.appName), escapeRoff(doc.date.Format(time.DateOnly)), escapeRoff(doc.appName))
printf(".SH Name\n%s\n", escapeRoff(doc.appName))
println(".SH Provides several commands:")
println(".PP")
allCommands := allCommands(doc)
for i, c := range allCommands {
if i > 0 {
println(".br")
}
println(escapeRoff(c.fullCommand))
}
for _, c := range allCommands {
printf(".SH %s\n", escapeRoff(strings.ToUpper(c.fullCommand)))
formatManCommand(printf, println, c)
}
return finish()
}
func formatManSingleCommand(out io.Writer, doc doc) error {
printf, println, finish := printer(out)
printf(".TH %s 1 %s \"%s\"\n", escapeRoff(doc.appName), escapeRoff(doc.date.Format(time.DateOnly)), escapeRoff(doc.appName))
printf(".SH Name\n%s\n", escapeRoff(doc.appName))
formatManCommand(printf, println, doc)
return finish()
}
func formatMan(out io.Writer, doc doc) error {
var hasSubcommands bool
for _, sc := range doc.subcommands {
if !sc.isHelp && !sc.isVersion {
continue
}
hasSubcommands = true
break
}
if hasSubcommands {
return formatManMultiCommand(out, doc)
}
return formatManSingleCommand(out, doc)
}