mirror of
https://github.com/daylinmorgan/hwylterm.git
synced 2025-02-23 01:35:50 -06:00
add default values to help screen
This commit is contained in:
parent
c40a0a2038
commit
ea49ee81fc
10 changed files with 176 additions and 67 deletions
|
@ -3,7 +3,6 @@ import std/[strformat, strutils]
|
|||
|
||||
task test, "run tests":
|
||||
selfExec "r tests/tbbansi.nim"
|
||||
selfExec "r tests/tcli.nim"
|
||||
selfExec "r tests/cli/tester.nim"
|
||||
|
||||
task develop, "install cligen for development":
|
||||
|
|
|
@ -38,7 +38,7 @@ export parseopt3, sets, bbansi
|
|||
|
||||
type
|
||||
HwylFlagHelp* = tuple
|
||||
short, long, description: string
|
||||
short, long, description, defaultVal: string
|
||||
HwylSubCmdHelp* = tuple
|
||||
name, aliases, desc: string
|
||||
HwylCliStyleSetting = enum
|
||||
|
@ -48,6 +48,8 @@ type
|
|||
flagShort* = "yellow"
|
||||
flagLong* = "magenta"
|
||||
flagDesc* = ""
|
||||
default* = "faint"
|
||||
required* = "red"
|
||||
cmd* = "bold"
|
||||
settings*: set[HwylCliStyleSetting] = {Aliases}
|
||||
HwylCliHelp* = object
|
||||
|
@ -55,7 +57,8 @@ type
|
|||
subcmds*: seq[HwylSubCmdHelp]
|
||||
flags*: seq[HwylFlagHelp]
|
||||
styles*: HwylCliStyles
|
||||
subcmdLen*, subcmdDescLen*, shortArgLen*, longArgLen*, descArgLen*: int
|
||||
# make 'lengths' it's own object?
|
||||
subcmdLen*, subcmdDescLen*, shortArgLen*, longArgLen*, descArgLen*, defaultValLen*: int
|
||||
|
||||
# NOTE: do i need both strips?
|
||||
func firstLine(s: string): string =
|
||||
|
@ -88,6 +91,7 @@ func newHwylCliHelp*(
|
|||
result.shortArgLen = max(result.shortArgLen, f.short.len)
|
||||
result.longArgLen = max(result.longArgLen, f.long.len)
|
||||
result.descArgLen = max(result.descArgLen, f.description.len)
|
||||
result.defaultValLen = max(result.defaultValLen, f.defaultVal.len)
|
||||
for s in result.subcmds:
|
||||
result.subcmdLen = max(result.subcmdLen, s.name.len)
|
||||
result.subcmdDescLen = max(result.subcmdDescLen, s.desc.len)
|
||||
|
@ -109,11 +113,16 @@ func render*(cli: HwylCliHelp, f: HwylFlagHelp): string =
|
|||
result.add " ".repeat(2 + cli.longArgLen)
|
||||
|
||||
result.add " "
|
||||
|
||||
if f.description != "":
|
||||
result.add "[" & cli.styles.flagDesc & "]"
|
||||
result.add f.description
|
||||
result.add "[/" & cli.styles.flagDesc & "]"
|
||||
if f.defaultVal != "":
|
||||
result.add " "
|
||||
result.add "[" & cli.styles.default & "]"
|
||||
result.add "(" & f.defaultVal & ")"
|
||||
result.add "[/" & cli.styles.default & "]"
|
||||
|
||||
|
||||
func render*(cli: HwylCliHelp, subcmd: HwylSubCmdHelp): string =
|
||||
result.add " "
|
||||
|
@ -159,7 +168,6 @@ func render*(cli: HwylCliHelp): string =
|
|||
proc bb*(cli: HwylCliHelp): BbString =
|
||||
result = bb(render(cli))
|
||||
|
||||
# ----------------------------------------
|
||||
|
||||
type
|
||||
Count* = object ## Count type for an incrementing flag
|
||||
|
@ -169,6 +177,10 @@ type
|
|||
val*: Y
|
||||
KVString* = KV[string, string]
|
||||
|
||||
proc `$`(c: Count): string = $c.val
|
||||
|
||||
# ----------------------------------------
|
||||
|
||||
type
|
||||
CliSetting* = enum
|
||||
# Propagate, ## Include parent command settings in subcommand
|
||||
|
@ -176,8 +188,12 @@ type
|
|||
NoHelpFlag, ## Remove the builtin help flag
|
||||
ShowHelp, ## If cmdline empty show help
|
||||
NoNormalize, ## Don't normalize flags and commands
|
||||
NoPositional ## Raise error if any remaing positional arguments
|
||||
ExactArgs, ## Raise error if missing positional argument
|
||||
NoPositional, ## Raise error if any remaing positional arguments DEPRECATED
|
||||
HideDefault ## Don't show default values
|
||||
# ExactArgs, ## Raise error if missing positional argument
|
||||
|
||||
CliFlagSetting* = enum
|
||||
HideDefault ## Don't show default values
|
||||
|
||||
BuiltinFlag = object
|
||||
name*: string
|
||||
|
@ -185,11 +201,13 @@ type
|
|||
long*: string
|
||||
help*: NimNode
|
||||
node: NimNode
|
||||
defaultVal: NimNode
|
||||
settings*: set[CliFlagSetting]
|
||||
|
||||
CliFlag = object
|
||||
name*: string
|
||||
ident*: NimNode
|
||||
default*: NimNode
|
||||
defaultVal*: NimNode
|
||||
typeNode*: NimNode
|
||||
node*: NimNode
|
||||
short*: char
|
||||
|
@ -197,6 +215,7 @@ type
|
|||
help*: NimNode
|
||||
group*: string
|
||||
inherited*: bool
|
||||
settings*: set[CliFlagSetting]
|
||||
|
||||
Inherit = object
|
||||
settings: set[CliSetting]
|
||||
|
@ -270,6 +289,40 @@ func bad(n: NimNode, argument: string = "") =
|
|||
msg &= " for argument: " & argument
|
||||
error msg
|
||||
|
||||
# could I deduplicate these somehow? template?
|
||||
|
||||
func parseCliSetting(s: string): CliSetting =
|
||||
try: parseEnum[CliSetting](s)
|
||||
except: error "unknown cli setting: " & s
|
||||
|
||||
|
||||
func parseCliSettings(cfg: var CliCfg, node: NimNode) =
|
||||
case node.kind
|
||||
of nnkCommand:
|
||||
for n in node[1..^1]:
|
||||
cfg.settings.incl parseCliSetting(n.strVal)
|
||||
of nnkCall:
|
||||
expectKind node[1], nnkStmtList
|
||||
for n in node[1]:
|
||||
cfg.settings.incl parseCliSetting(n.strVal)
|
||||
else: assert false
|
||||
|
||||
func parseCliFlagSetting(s: string): CliFlagSetting =
|
||||
try: parseEnum[CliFlagSetting](s)
|
||||
except: error "unknown cli flag setting: " & s
|
||||
|
||||
|
||||
func parseCliFlagSettings(f: var CliFlag, node: NimNode) =
|
||||
case node.kind
|
||||
of nnkCommand:
|
||||
for n in node[1..^1]:
|
||||
f.settings.incl parseCliFlagSetting(n.strVal)
|
||||
of nnkCall:
|
||||
expectKind node[1], nnkStmtList
|
||||
for n in node[1]:
|
||||
f.settings.incl parseCliFlagSetting(n.strVal)
|
||||
else: assert false
|
||||
|
||||
func getFlagParamNode(node: NimNode): NimNode =
|
||||
case node.kind
|
||||
of nnkStrLit:
|
||||
|
@ -297,13 +350,15 @@ func parseFlagParams(f: var CliFlag, node: NimNode) =
|
|||
error "short flag must be a char"
|
||||
f.short = val[0].char
|
||||
of "*", "default":
|
||||
f.default = getFlagParamNode(n)
|
||||
f.defaultVal = getFlagParamNode(n)
|
||||
of "i", "ident":
|
||||
f.ident = getFlagParamNode(n).strVal.ident
|
||||
of "T":
|
||||
f.typeNode = n[1]
|
||||
of "node":
|
||||
f.node = n[1]
|
||||
of "settings", "S":
|
||||
parseCliFlagSettings(f, n)
|
||||
else:
|
||||
error "unexpected setting: " & n[0].strVal
|
||||
else:
|
||||
|
@ -393,22 +448,6 @@ func parseCliFlags(cfg: var CliCfg, node: NimNode) =
|
|||
else: bad(n, "flag")
|
||||
|
||||
|
||||
func parseCliSetting(s: string): CliSetting =
|
||||
try: parseEnum[CliSetting](s)
|
||||
except: error "unknown cli setting: " & s
|
||||
|
||||
|
||||
func parseCliSettings(cfg: var CliCfg, node: NimNode) =
|
||||
case node.kind
|
||||
of nnkCommand:
|
||||
for n in node[1..^1]:
|
||||
cfg.settings.incl parseCliSetting(n.strVal)
|
||||
of nnkCall:
|
||||
expectKind node[1], nnkStmtList
|
||||
for n in node[1]:
|
||||
cfg.settings.incl parseCliSetting(n.strVal)
|
||||
else: assert false
|
||||
|
||||
func parseIdentLikeList(node: NimNode): seq[string] =
|
||||
template check =
|
||||
if n.kind notin [nnkStrLit,nnkIdent]:
|
||||
|
@ -716,7 +755,7 @@ func parseCliBody(body: NimNode, name = "", root = false): CliCfg =
|
|||
parseCliHelp(result, node)
|
||||
of "flags":
|
||||
parseCliFlags(result, node[1])
|
||||
of "settings":
|
||||
of "settings", "S":
|
||||
parseCliSettings(result, node)
|
||||
of "stopWords":
|
||||
result.stopWords = parseIdentLikeList(node)
|
||||
|
@ -747,23 +786,30 @@ func parseCliBody(body: NimNode, name = "", root = false): CliCfg =
|
|||
if root:
|
||||
propagate(result)
|
||||
|
||||
func flagToTuple(f: CliFlag | BuiltinFlag): NimNode =
|
||||
func flagToTuple(c: CliCfg, f: CliFlag | BuiltinFlag): NimNode =
|
||||
let
|
||||
short =
|
||||
if f.short != '\x00': newLit($f.short)
|
||||
else: newLit("")
|
||||
long = newLit(f.long)
|
||||
help = f.help
|
||||
|
||||
defaultVal =
|
||||
if (HideDefault in f.settings) or
|
||||
(HideDefault in c.settings):
|
||||
newLit""
|
||||
else:
|
||||
f.defaultVal or newLit""
|
||||
quote do:
|
||||
(`short`, `long`, `help`)
|
||||
(`short`, `long`, `help`, bbEscape($`defaultVal`))
|
||||
|
||||
func flagsArray(cfg: CliCfg): NimNode =
|
||||
result = newTree(nnkBracket)
|
||||
for f in cfg.flags:
|
||||
if f.name in cfg.hidden: continue
|
||||
result.add f.flagToTuple()
|
||||
result.add cfg.flagToTuple(f)
|
||||
for f in cfg.builtinFlags:
|
||||
result.add f.flagToTuple()
|
||||
result.add cfg.flagToTuple(f)
|
||||
|
||||
func subCmdsArray(cfg: CliCfg): NimNode =
|
||||
result = newTree(nnkBracket)
|
||||
|
@ -1107,9 +1153,9 @@ func addPostParseHook(cfg: CliCfg, body: NimNode) =
|
|||
var required, default: seq[CliFlag]
|
||||
|
||||
for f in cfg.flags:
|
||||
if f.name in cfg.required and f.default == nil:
|
||||
if f.name in cfg.required and f.defaultVal == nil:
|
||||
required.add f
|
||||
elif f.default != nil:
|
||||
elif f.defaultVal != nil:
|
||||
default.add f
|
||||
|
||||
for f in required:
|
||||
|
@ -1123,10 +1169,10 @@ func addPostParseHook(cfg: CliCfg, body: NimNode) =
|
|||
let
|
||||
name = newLit(f.name)
|
||||
target = f.ident
|
||||
default = f.default
|
||||
defaultVal = f.defaultVal
|
||||
body.add quote do:
|
||||
if `name` notin `flagSet`:
|
||||
`target` = `default`
|
||||
`target` = `defaultVal`
|
||||
|
||||
|
||||
if hasSubcommands cfg:
|
||||
|
|
16
tests/cli/clis/cliCfgSettingHideDefault.nim
Normal file
16
tests/cli/clis/cliCfgSettingHideDefault.nim
Normal file
|
@ -0,0 +1,16 @@
|
|||
import hwylterm, hwylterm/hwylcli
|
||||
|
||||
hwylCli:
|
||||
name "setting-hide-default"
|
||||
settings HideDefault
|
||||
flags:
|
||||
input:
|
||||
T string
|
||||
* "a secret default"
|
||||
? "flag with default hidden"
|
||||
count:
|
||||
T Count
|
||||
* Count(val: 0)
|
||||
? "a count var with default"
|
||||
run:
|
||||
discard
|
18
tests/cli/clis/defaults.nim
Normal file
18
tests/cli/clis/defaults.nim
Normal file
|
@ -0,0 +1,18 @@
|
|||
import std/[strformat]
|
||||
import hwylterm, hwylterm/hwylcli
|
||||
|
||||
hwylCli:
|
||||
name "default-values"
|
||||
flags:
|
||||
input:
|
||||
T string
|
||||
* "testing"
|
||||
? "some help after default"
|
||||
outputs:
|
||||
T seq[string]
|
||||
count:
|
||||
T int
|
||||
* 5
|
||||
? "some number"
|
||||
run:
|
||||
echo fmt"{input=} {outputs=}"
|
16
tests/cli/clis/flagSettings.nim
Normal file
16
tests/cli/clis/flagSettings.nim
Normal file
|
@ -0,0 +1,16 @@
|
|||
import hwylterm, hwylterm/hwylcli
|
||||
|
||||
hwylCli:
|
||||
name "flag-settings"
|
||||
flags:
|
||||
input:
|
||||
S HideDefault
|
||||
T string
|
||||
* "a secret default"
|
||||
? "flag with default hidden"
|
||||
count:
|
||||
T Count
|
||||
* Count(val: 0)
|
||||
? "a count var with default"
|
||||
run:
|
||||
discard
|
|
@ -16,6 +16,8 @@ hwylCli:
|
|||
flags:
|
||||
input:
|
||||
T string
|
||||
* "testing"
|
||||
? "some help after default"
|
||||
outputs:
|
||||
T seq[string]
|
||||
run:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import std/[compilesettings, os, osproc, strutils, times, unittest]
|
||||
import std/[compilesettings, os, osproc, strutils, times, unittest, terminal]
|
||||
|
||||
const pathToSrc = querySetting(SingleValueSetting.projectPath)
|
||||
const binDir = pathToSrc / "bin"
|
||||
|
@ -13,21 +13,36 @@ proc runTestCli(module: string, args: string, code: int = 0): (string, int) =
|
|||
let (output, code) = execCmdEx(cmd)
|
||||
result = (output.strip(), code)
|
||||
|
||||
# poor man's progress meter
|
||||
proc status(s: string) =
|
||||
eraseLine stdout
|
||||
stdout.write(s.alignLeft(terminalWidth()).substr(0, terminalWidth()-1))
|
||||
flushFile stdout
|
||||
|
||||
proc preCompileWorkingModule(module: string) =
|
||||
let exe = binDir / module
|
||||
let srcModule = pathToSrc / "clis" / (module & ".nim")
|
||||
if not exe.fileExists or getFileInfo(exe).lastWriteTime < max(getFileInfo(srcModule).lastWriteTime, hwylCliWriteTime):
|
||||
if not exe.fileExists or getFileInfo(exe).lastWriteTime < max(getFileInfo(srcModule).lastWriteTime, hwylCliWriteTime) or defined(forceSetup):
|
||||
let cmd = "nim c -o:$1 $2" % [exe, srcModule]
|
||||
let code = execCmd(cmd)
|
||||
let (output, code) = execCmdEx(cmd)
|
||||
if code != 0:
|
||||
echo "cmd: ", cmd
|
||||
quit "failed to precompile test module"
|
||||
quit "failed to precompile test module:\n" & output
|
||||
|
||||
proc preCompileTestModules*() =
|
||||
var modules: seq[string]
|
||||
for srcModule in walkDirRec(pathToSrc / "clis"):
|
||||
if srcModule.endsWith(".nim"):
|
||||
let (_, moduleName, _) = srcModule.splitFile
|
||||
preCompileWorkingModule(moduleName)
|
||||
modules.add srcModule.splitFile().name
|
||||
|
||||
for i, module in modules:
|
||||
status "compiling [$2/$3] $1" % [ module, $(i+1), $modules.len]
|
||||
preCompileWorkingModule(module)
|
||||
|
||||
eraseLine stdout
|
||||
|
||||
#let (_, moduleName, _) = srcModule.splitFile
|
||||
# preCompileWorkingModule(moduleName)
|
||||
|
||||
template okWithArgs*(module: string, args = "", output = "") =
|
||||
preCompileWorkingModule(module)
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import std/[unittest]
|
||||
import ./lib
|
||||
|
||||
preCompileTestModules()
|
||||
|
||||
suite "hwylcli":
|
||||
setup:
|
||||
preCompileTestModules()
|
||||
|
||||
okWithArgs(
|
||||
"posBasic",
|
||||
|
@ -38,3 +38,22 @@ suite "hwylcli":
|
|||
flags:
|
||||
-h --help show this help""")
|
||||
|
||||
|
||||
okWithArgs("flagSettings", "--help",
|
||||
"""usage:
|
||||
flag-settings [flags]
|
||||
|
||||
flags:
|
||||
--input flag with default hidden
|
||||
--count a count var with default (0)
|
||||
-h --help show this help""")
|
||||
|
||||
okWithArgs("cliCfgSettingHideDefault", "--help",
|
||||
"""usage:
|
||||
setting-hide-default [flags]
|
||||
|
||||
flags:
|
||||
--input flag with default hidden
|
||||
--count a count var with default
|
||||
-h --help show this help""")
|
||||
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
# TODO: combine this with tests/cli/
|
||||
import std/[
|
||||
unittest,
|
||||
strutils
|
||||
]
|
||||
import hwylterm, hwylterm/hwylcli
|
||||
|
||||
suite "cli":
|
||||
test "cli":
|
||||
let expected = """[b]test-program[/] [[args...]
|
||||
|
||||
[bold cyan]flags[/]:
|
||||
[yellow]-h[/yellow] [magenta]--help [/magenta] []show this help[/]
|
||||
[yellow]-V[/yellow] [magenta]--version[/magenta] []print version[/]"""
|
||||
let cli =
|
||||
newHwylCliHelp(
|
||||
header = "[b]test-program[/] [[args...]",
|
||||
flags = [("h","help","show this help",),("V","version","print version")]
|
||||
)
|
||||
check render(cli) == expected
|
||||
check $bb(render(cli)) == $bb(expected)
|
1
todo.md
1
todo.md
|
@ -16,7 +16,6 @@
|
|||
### cli generator
|
||||
|
||||
- [ ] add support for types(metavars)/defaults/required in help output
|
||||
- [ ] add nargs to CliCfg
|
||||
- [x] add support for inheriting a single flag from parent (even from a "group")
|
||||
- [x] add support to either (lengthen commands) or provide an alias for a subcommand
|
||||
- [x] add command aliases to hwylcli help with switch
|
||||
|
|
Loading…
Add table
Reference in a new issue