add type metavars to help output

This commit is contained in:
Daylin Morgan 2025-01-30 13:22:07 -06:00
parent 5d7e404d0d
commit bf542a1470
Signed by: daylin
GPG key ID: 950D13E9719334AD
3 changed files with 78 additions and 71 deletions

View file

@ -38,13 +38,13 @@ export parseopt3, sets, bbansi
type type
HwylFlagHelp* = tuple[ HwylFlagHelp* = tuple[
short, long, description, defaultVal: string; required: bool short, long, description, typeRepr, defaultVal: string; required: bool
] ]
HwylSubCmdHelp* = tuple[ HwylSubCmdHelp* = tuple[
name, aliases, desc: string name, aliases, desc: string
] ]
HwylCliStyleSetting = enum HwylCliStyleSetting = enum
Aliases, Required, Defaults Aliases, Required, Defaults, Types
HwylCliStyles* = object HwylCliStyles* = object
header* = "bold cyan" header* = "bold cyan"
flagShort* = "yellow" flagShort* = "yellow"
@ -52,12 +52,13 @@ type
flagDesc* = "" flagDesc* = ""
default* = "faint" default* = "faint"
required* = "red" required* = "red"
cmd* = "bold" subcmd* = "bold"
typeRepr = "faint"
minCmdLen* = 8 minCmdLen* = 8
settings*: set[HwylCliStyleSetting] = {Aliases, Required, Defaults} settings*: set[HwylCliStyleSetting] = {Aliases, Required, Defaults, Types}
HwylCliLengths = object HwylCliLengths = object
subcmd*, subcmdDesc*, shortArg*, longArg*, descArg*, defaultVal*: int subcmd*, subcmdDesc*, shortArg*, longArg*, descArg*, typeRepr*, defaultVal*: int
HwylCliHelp* = object HwylCliHelp* = object
header*, footer*, description*, usage*: string header*, footer*, description*, usage*: string
@ -92,57 +93,63 @@ func newHwylCliHelp*(
result.flags = @flags result.flags = @flags
result.styles = styles result.styles = styles
result.lengths.subcmd = styles.minCmdLen result.lengths.subcmd = styles.minCmdLen
for f in flags: for f in flags:
# template?
result.lengths.shortArg = max(result.lengths.shortArg, f.short.len) result.lengths.shortArg = max(result.lengths.shortArg, f.short.len)
result.lengths.longArg = max(result.lengths.longArg, f.long.len) result.lengths.longArg = max(result.lengths.longArg, f.long.len)
result.lengths.descArg = max(result.lengths.descArg, f.description.len) result.lengths.descArg = max(result.lengths.descArg, f.description.len)
result.lengths.defaultVal = max(result.lengths.defaultVal, f.defaultVal.len) result.lengths.defaultVal = max(result.lengths.defaultVal, f.defaultVal.len)
# using "bb" before len.. to squash out the escaped [[
result.lengths.typeRepr = max(result.lengths.typeRepr, f.typeRepr.bb.len)
for s in result.subcmds: for s in result.subcmds:
result.lengths.subcmd = max(result.lengths.subcmd, s.name.len) result.lengths.subcmd = max(result.lengths.subcmd, s.name.len)
result.lengths.subcmdDesc = max(result.lengths.subcmdDesc, s.desc.len) result.lengths.subcmdDesc = max(result.lengths.subcmdDesc, s.desc.len)
func render*(cli: HwylCliHelp, f: HwylFlagHelp): string = func render*(cli: HwylCliHelp, f: HwylFlagHelp): string =
result.add " " result.add " "
if f.short != "": if f.short != "":
result.add "[" & cli.styles.flagShort & "]" result.add ("-" & f.short.alignLeft(cli.lengths.shortArg)).bbMarkup(cli.styles.flagShort)
result.add "-" & f.short.alignLeft(cli.lengths.shortArg)
result.add "[/" & cli.styles.flagShort & "]"
else: else:
result.add " ".repeat(1 + cli.lengths.shortArg) result.add " ".repeat(1 + cli.lengths.shortArg)
result.add " " result.add " "
if f.long != "": if f.long != "":
result.add "[" & cli.styles.flagLong & "]" result.add ("--" & f.long.alignLeft(cli.lengths.longArg)).bbMarkup(cli.styles.flagLong)
result.add "--" & f.long.alignLeft(cli.lengths.longArg)
result.add "[/" & cli.styles.flagLong & "]"
else: else:
result.add " ".repeat(2 + cli.lengths.longArg) result.add " ".repeat(2 + cli.lengths.longArg)
if Types in cli.styles.settings:
result.add " "
# BUG alignLeft isn't accounting for these '[['
let offset = int(
(f.typeRepr.len - f.typeRepr.replace("[[","").len) / 2
)
result.add f.typeRepr
.alignLeft(
cli.lengths.typeRepr + offset
)
.bbMarkup(cli.styles.typeRepr)
result.add " " result.add " "
if f.description != "": if f.description != "":
result.add "[" & cli.styles.flagDesc & "]" result.add f.description.bbMarkup(cli.styles.flagDesc)
result.add f.description
result.add "[/" & cli.styles.flagDesc & "]"
if f.defaultVal != "" and Defaults in cli.styles.settings: if f.defaultVal != "" and Defaults in cli.styles.settings:
result.add " " result.add " "
result.add "[" & cli.styles.default & "]" result.add ("(default: " & f.defaultVal & ")")
result.add "(" & f.defaultVal & ")" .bbMarkup(cli.styles.default)
result.add "[/" & cli.styles.default & "]"
if f.required and Required in cli.styles.settings: if f.required and Required in cli.styles.settings:
result.add " " result.add " "
result.add "[" & cli.styles.required & "]" result.add "(required)".bbMarkup(cli.styles.required)
result.add "(required)"
result.add "[/" & cli.styles.required & "]"
func render*(cli: HwylCliHelp, subcmd: HwylSubCmdHelp): string = func render*(cli: HwylCliHelp, subcmd: HwylSubCmdHelp): string =
result.add " " result.add " "
result.add "[" & cli.styles.cmd & "]" result.add subcmd.name.alignLeft(cli.lengths.subcmd).bbMarkup(cli.styles.subcmd)
result.add subcmd.name.alignLeft(cli.lengths.subcmd)
result.add "[/]"
result.add " " result.add " "
result.add subcmd.desc.alignLeft(cli.lengths.subcmdDesc) result.add subcmd.desc.alignLeft(cli.lengths.subcmdDesc)
@ -154,25 +161,13 @@ template render*(cli: HwylCliHelp): string =
if cli.header != "": if cli.header != "":
parts.add cli.header parts.add cli.header
if cli.usage != "": if cli.usage != "":
var part: string parts.add "usage".bbMarkup(cli.styles.header) & ":\n" & indent(cli.usage, 2 )
part.add "[" & cli.styles.header & "]"
part.add "usage[/]:\n"
part.add indent(cli.usage, 2 )
parts.add part
if cli.description != "": if cli.description != "":
parts.add cli.description parts.add cli.description
if cli.subcmds.len > 0: if cli.subcmds.len > 0:
var part: string parts.add "subcommands".bbMarkup(cli.styles.header) & ":\n" & cli.subcmds.mapIt(render(cli,it)).join("\n")
part.add "[" & cli.styles.header & "]"
part.add "subcommands[/]:\n"
part.add cli.subcmds.mapIt(render(cli,it)).join("\n")
parts.add part
if cli.flags.len > 0: if cli.flags.len > 0:
var part: string parts.add "flags".bbMarkup(cli.styles.header) & ":\n" & cli.flags.mapIt(render(cli, it)).join("\n")
part.add "[" & cli.styles.header & "]"
part.add "flags[/]:\n"
part.add cli.flags.mapIt(render(cli, it)).join("\n")
parts.add part
if cli.footer != "": if cli.footer != "":
parts.add cli.footer parts.add cli.footer
@ -191,8 +186,21 @@ type
val*: Y val*: Y
KVString* = KV[string, string] KVString* = KV[string, string]
proc `$`*(t: typedesc[KVString]): string =
result.add "k(string):v(string)"
proc `$`*[X,Y](t: typedesc[KV[X, Y]]): string =
result.add "k(" & $X & ")"
result.add ":"
result.add "v(" & $Y & ")"
proc `$`[X,Y](t: typedesc[seq[KV[X,Y]]]): string =
"seq[" & $(KV[X,Y]) & "]"
proc `$`(c: Count): string = $c.val proc `$`(c: Count): string = $c.val
# ---------------------------------------- # ----------------------------------------
type type
@ -882,7 +890,6 @@ func parseCliBody(body: NimNode, name = "", root = false): CliCfg =
if root: if root:
propagate(result) propagate(result)
func isBool(f: CliFlag | BuiltinFlag): bool = func isBool(f: CliFlag | BuiltinFlag): bool =
f.typeNode == ident"bool" f.typeNode == ident"bool"
@ -917,35 +924,44 @@ func flagToTuple(c: CliCfg, f: BuiltinFlag): NimNode =
# under the hood when parsing type/val # under the hood when parsing type/val
quote do: quote do:
(`short`, `long`, `help`, bbEscape($`defaultVal`), `required`) (`short`, `long`, `help`, "", bbEscape($`defaultVal`), `required`)
func flagToTuple(c: CliCfg, f: CliFlag): NimNode = func flagToTuple(c: CliCfg, f: CliFlag): NimNode =
let let
short = short =
if f.short != '\x00': newLit($f.short) if f.short != '\x00': newLit($f.short)
else: newLit("") else: newLit""
long = newLit(f.long)
help = f.help
defaultVal = defaultVal =
if (HideDefault in f.settings) or if (HideDefault in f.settings) or
(HideDefault in c.settings): (HideDefault in c.settings) or
f.defaultVal == nil:
newLit"" newLit""
else: else:
f.defaultVal or newLit"" let val = f.defaultVal
quote do:
bbEscape($`val`)
required = newLit(c.isRequiredFlag(f)) required = newLit(c.isRequiredFlag(f))
typeNode =
if f.isBool: newLit""
else:
let t = f.typeNode
quote do: bbEscape($`t`)
# BUG: if f.defaultVal is @[] `$` fails # BUG: if f.defaultVal is @[] `$` fails
# but works with `newSeq[T]()` # but works with `newSeq[T]()`
# could replace "defaultVal" with newSeq[T]() # could replace "defaultVal" with newSeq[T]()
# under the hood when parsing type/val # under the hood when parsing type/val
result = nnkTupleConstr.newTree( result = nnkTupleConstr.newTree(
short, short,
newLit(f.long), newLit(f.long),
f.help, f.help,
quote do: bbEscape($`defaultVal`), typeNode,
defaultVal,
required, required,
) )
# quote do: # quote do:

View file

@ -59,8 +59,8 @@ usage:
flag-settings [flags] flag-settings [flags]
flags: flags:
--input flag with default hidden --input string flag with default hidden
--count a count var with default (0) --count Count a count var with default (default: 0)
-h --help show this help -h --help show this help
""", """,
) )
@ -72,8 +72,8 @@ usage:
setting-hide-default [flags] setting-hide-default [flags]
flags: flags:
--input flag with default hidden --input string flag with default hidden
--count a count var with default --count Count a count var with default
-h --help show this help -h --help show this help
""", """,
) )

View file

@ -7,6 +7,8 @@ type
Color = enum Color = enum
red, blue, green red, blue, green
KVColor = KV[string, Color]
hwylCli: hwylCli:
name "example" name "example"
V "0.1.0" V "0.1.0"
@ -59,7 +61,6 @@ hwylCli:
run: run:
echo "hello from `example one` command!" echo "hello from `example one` command!"
echo args
echo fmt"{color=}" echo fmt"{color=}"
echo fmt"{verbose=}" echo fmt"{verbose=}"
echo fmt"{config=}" echo fmt"{config=}"
@ -71,21 +72,11 @@ hwylCli:
a longer mulitline description that will be visible in the subcommand help a longer mulitline description that will be visible in the subcommand help
and it will automatically be "bb"'ed [bold]this is bold text[/] and it will automatically be "bb"'ed [bold]this is bold text[/]
""" """
# args first, second
# or
args:
# default type is string
# only one 'arg' can be the seq[string]
# order matters here
# by default string
inputs:
T int
second seq[string]
flags: flags:
^something ^something
thing: inputs:
T seq[KV[string, Color]] T seq[KVColor]
? "some key value colors" ? "some key stuff"
b: b:
T seq[float] T seq[float]
? "multiple floats" ? "multiple floats"
@ -95,5 +86,5 @@ hwylCli:
* "the value" * "the value"
run: run:
echo "hello from `example b` command" echo "hello from `example b` command"
echo fmt"{thing=}, {b=}, {def=}" echo fmt"{inputs=}, {b=}, {def=}"