mirror of
https://github.com/daylinmorgan/hwylterm.git
synced 2024-12-22 11:00:45 -06:00
Compare commits
3 commits
cbeefd675c
...
6082cc3835
Author | SHA1 | Date | |
---|---|---|---|
6082cc3835 | |||
5f5dd86c4d | |||
203082d893 |
3 changed files with 110 additions and 48 deletions
|
@ -25,8 +25,7 @@ type
|
||||||
cmd* = "bold"
|
cmd* = "bold"
|
||||||
settings*: set[HwylCliStyleSetting] = {Aliases}
|
settings*: set[HwylCliStyleSetting] = {Aliases}
|
||||||
HwylCliHelp* = object
|
HwylCliHelp* = object
|
||||||
usage*: string
|
header*, footer*, description*, usage*: string
|
||||||
desc*: string
|
|
||||||
subcmds*: seq[HwylSubCmdHelp]
|
subcmds*: seq[HwylSubCmdHelp]
|
||||||
flags*: seq[HwylFlagHelp]
|
flags*: seq[HwylFlagHelp]
|
||||||
styles*: HwylCliStyles
|
styles*: HwylCliStyles
|
||||||
|
@ -37,13 +36,15 @@ func firstLine(s: string): string =
|
||||||
s.strip().dedent().strip().splitlines()[0]
|
s.strip().dedent().strip().splitlines()[0]
|
||||||
|
|
||||||
func newHwylCliHelp*(
|
func newHwylCliHelp*(
|
||||||
|
header = "",
|
||||||
usage = "",
|
usage = "",
|
||||||
desc = "",
|
footer = "",
|
||||||
|
description = "",
|
||||||
subcmds: openArray[HwylSubCmdHelp] = @[],
|
subcmds: openArray[HwylSubCmdHelp] = @[],
|
||||||
flags: openArray[HwylFlagHelp] = @[],
|
flags: openArray[HwylFlagHelp] = @[],
|
||||||
styles = HwylCliStyles()
|
styles = HwylCliStyles()
|
||||||
): HwylCliHelp =
|
): HwylCliHelp =
|
||||||
result.desc = dedent(desc).strip()
|
result.description = dedent(description).strip()
|
||||||
if Aliases in styles.settings:
|
if Aliases in styles.settings:
|
||||||
result.subcmds =
|
result.subcmds =
|
||||||
subcmds.mapIt((it.name & " " & it.aliases, it.aliases, it.desc.firstLine))
|
subcmds.mapIt((it.name & " " & it.aliases, it.aliases, it.desc.firstLine))
|
||||||
|
@ -99,14 +100,17 @@ func render*(cli: HwylCliHelp, subcmd: HwylSubCmdHelp): string =
|
||||||
|
|
||||||
# TODO: split this into separate procs to make overriding more fluid
|
# TODO: split this into separate procs to make overriding more fluid
|
||||||
func render*(cli: HwylCliHelp): string =
|
func render*(cli: HwylCliHelp): string =
|
||||||
|
if cli.header != "":
|
||||||
|
result.add cli.header
|
||||||
|
result.add "\n"
|
||||||
if cli.usage != "":
|
if cli.usage != "":
|
||||||
result.add "[" & cli.styles.header & "]"
|
result.add "[" & cli.styles.header & "]"
|
||||||
result.add "usage[/]:\n"
|
result.add "usage[/]:\n"
|
||||||
result.add indent(cli.usage, 2 )
|
result.add indent(cli.usage, 2 )
|
||||||
result.add "\n"
|
result.add "\n"
|
||||||
if cli.desc != "":
|
if cli.description != "":
|
||||||
result.add "\n"
|
result.add "\n"
|
||||||
result.add cli.desc
|
result.add cli.description
|
||||||
result.add "\n"
|
result.add "\n"
|
||||||
if cli.subcmds.len > 0:
|
if cli.subcmds.len > 0:
|
||||||
result.add "\n"
|
result.add "\n"
|
||||||
|
@ -120,6 +124,8 @@ func render*(cli: HwylCliHelp): string =
|
||||||
result.add "flags[/]:\n"
|
result.add "flags[/]:\n"
|
||||||
for f in cli.flags:
|
for f in cli.flags:
|
||||||
result.add render(cli,f)
|
result.add render(cli,f)
|
||||||
|
if cli.footer != "":
|
||||||
|
result.add cli.footer
|
||||||
|
|
||||||
proc bb*(cli: HwylCliHelp): BbString =
|
proc bb*(cli: HwylCliHelp): BbString =
|
||||||
result = bb(render(cli))
|
result = bb(render(cli))
|
||||||
|
@ -161,18 +167,20 @@ type
|
||||||
flags: seq[string]
|
flags: seq[string]
|
||||||
groups: seq[string]
|
groups: seq[string]
|
||||||
|
|
||||||
|
CliHelp = object
|
||||||
|
header*, footer*, description*, usage*, styles*: NimNode
|
||||||
|
|
||||||
CliCfg = object
|
CliCfg = object
|
||||||
name*: string
|
name*: string
|
||||||
alias*: HashSet[string]
|
alias*: HashSet[string]
|
||||||
stopWords*: seq[string]
|
stopWords*: seq[string]
|
||||||
styles: NimNode
|
help: CliHelp
|
||||||
hidden*: seq[string]
|
hidden*: seq[string]
|
||||||
subcommands: seq[CliCfg]
|
subcommands: seq[CliCfg]
|
||||||
settings*: set[CliSetting]
|
settings*: set[CliSetting]
|
||||||
preSub*, postSub*, pre*, post*, run*: NimNode
|
preSub*, postSub*, pre*, post*, run*: NimNode
|
||||||
desc*: NimNode
|
|
||||||
subName*: string # used for help the generator
|
subName*: string # used for help the generator
|
||||||
version*, usage*: NimNode
|
version*: NimNode
|
||||||
flags*: seq[CliFlag]
|
flags*: seq[CliFlag]
|
||||||
builtinFlags*: seq[BuiltinFlag]
|
builtinFlags*: seq[BuiltinFlag]
|
||||||
flagDefs*: seq[CliFlag]
|
flagDefs*: seq[CliFlag]
|
||||||
|
@ -494,60 +502,110 @@ func propagate(c: var CliCfg) =
|
||||||
propagate(child)
|
propagate(child)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
func parseCliHelp(c: var CliCfg, node: NimNode) =
|
||||||
|
## some possible DSL inputs:
|
||||||
|
##
|
||||||
|
## ```
|
||||||
|
## help:
|
||||||
|
## header NimNode -> string
|
||||||
|
## usage NimNode -> string
|
||||||
|
## description NimNode -> string
|
||||||
|
## footer NimNode -> string
|
||||||
|
## styles NimNode -> HwylCliStyles()
|
||||||
|
## ```
|
||||||
|
##
|
||||||
|
## ```
|
||||||
|
## help NimNode
|
||||||
|
## ```
|
||||||
|
##
|
||||||
|
## ```
|
||||||
|
## ... NimNode
|
||||||
|
## `
|
||||||
|
|
||||||
|
expectLen node, 2
|
||||||
|
var help: CliHelp = c.help
|
||||||
|
case node.kind:
|
||||||
|
# help NimNode or ... NimNode
|
||||||
|
of nnkPrefix, nnkCommand:
|
||||||
|
help.description = node[1]
|
||||||
|
# help:
|
||||||
|
# description NimNode
|
||||||
|
# usage: NimNode
|
||||||
|
of nnkCall:
|
||||||
|
if node[1].kind != nnkStmtList:
|
||||||
|
error "expected list of arguments for help"
|
||||||
|
|
||||||
|
for n in node[1]:
|
||||||
|
expectLen n, 2
|
||||||
|
let id = n[0].strVal
|
||||||
|
var val: NimNode
|
||||||
|
case n.kind
|
||||||
|
of nnkCommand:
|
||||||
|
val =n[1]
|
||||||
|
of nnKCall:
|
||||||
|
val = n[1][0]
|
||||||
|
else: bad(n, id)
|
||||||
|
|
||||||
|
case id:
|
||||||
|
of "usage": help.usage = val
|
||||||
|
of "description": help.description = val
|
||||||
|
of "header": help.header = val
|
||||||
|
of "footer": help.footer = val
|
||||||
|
of "styles": help.styles = val
|
||||||
|
else: error "unknown help option: " & id
|
||||||
|
|
||||||
|
else: bad(node, "help")
|
||||||
|
|
||||||
|
c.help = help
|
||||||
|
|
||||||
func parseCliBody(body: NimNode, name = "", root = false): CliCfg =
|
func parseCliBody(body: NimNode, name = "", root = false): CliCfg =
|
||||||
result.name = name
|
result.name = name
|
||||||
result.root = root
|
result.root = root
|
||||||
# TDOO: change call to n or node?
|
for node in body:
|
||||||
for call in body:
|
if node.kind notin [nnkCall, nnkCommand, nnkPrefix]:
|
||||||
if call.kind notin [nnkCall, nnkCommand, nnkPrefix]:
|
error "unexpected node kind: " & $node.kind
|
||||||
error "unexpected node kind: " & $call.kind
|
let name = node[0].strVal
|
||||||
let name = call[0].strVal
|
|
||||||
case name:
|
case name:
|
||||||
of "name":
|
of "name":
|
||||||
expectKind call[1], nnkStrLit
|
expectKind node[1], nnkStrLit
|
||||||
result.name = call[1].strVal
|
result.name = node[1].strVal
|
||||||
of "alias":
|
of "alias":
|
||||||
if root: error "alias not supported for root command"
|
if root: error "alias not supported for root command"
|
||||||
pasrseCliAlias(result, call)
|
pasrseCliAlias(result, node)
|
||||||
of "version", "V":
|
of "version", "V":
|
||||||
result.version = call[1]
|
result.version = node[1]
|
||||||
of "usage", "?":
|
of "usage", "?":
|
||||||
result.usage = call[1]
|
result.help.usage = node[1]
|
||||||
of "description", "...":
|
of "...", "help":
|
||||||
result.desc = call[1]
|
parseCliHelp(result, node)
|
||||||
of "flags":
|
of "flags":
|
||||||
parseCliFlags(result, call[1])
|
parseCliFlags(result, node[1])
|
||||||
of "settings":
|
of "settings":
|
||||||
parseCliSettings(result, call)
|
parseCliSettings(result, node)
|
||||||
of "stopWords":
|
of "stopWords":
|
||||||
result.stopWords = parseIdentLikeList(call)
|
result.stopWords = parseIdentLikeList(node)
|
||||||
of "subcommands":
|
of "subcommands":
|
||||||
parseCliSubcommands(result, call)
|
parseCliSubcommands(result, node)
|
||||||
of "hidden":
|
of "hidden":
|
||||||
parseHiddenFlags(result, call)
|
parseHiddenFlags(result, node)
|
||||||
of "run":
|
of "run":
|
||||||
result.run = call[1]
|
result.run = node[1]
|
||||||
of "styles":
|
|
||||||
result.styles = call[1]
|
|
||||||
of "required":
|
of "required":
|
||||||
result.required = parseIdentLikeList(call)
|
result.required = parseIdentLikeList(node)
|
||||||
of "preSub":
|
of "preSub":
|
||||||
result.preSub = call[1]
|
result.preSub = node[1]
|
||||||
of "postSub":
|
of "postSub":
|
||||||
result.postSub = call[1]
|
result.postSub = node[1]
|
||||||
else:
|
else:
|
||||||
error "unknown hwylCli setting: " & name
|
error "unknown hwylCli setting: " & name
|
||||||
#
|
|
||||||
# for sub in result.subcommands.mitems:
|
|
||||||
# # sub.inheritFrom(result)
|
|
||||||
# sub.pre = result.preSub
|
|
||||||
# sub.post = result.postSub
|
|
||||||
|
|
||||||
if result.name == "":
|
if result.name == "":
|
||||||
error "missing required option: name"
|
error "missing required option: name"
|
||||||
|
|
||||||
# TODO: validate "required" flags exist here
|
# TODO: validate "required" flags exist here
|
||||||
result.addBuiltinFlags()
|
result.addBuiltinFlags()
|
||||||
|
|
||||||
if root:
|
if root:
|
||||||
propagate(result)
|
propagate(result)
|
||||||
|
|
||||||
|
@ -574,7 +632,7 @@ func subCmdsArray(cfg: CliCfg): NimNode =
|
||||||
for s in cfg.subcommands:
|
for s in cfg.subcommands:
|
||||||
let cmd = newLit(s.subName)
|
let cmd = newLit(s.subName)
|
||||||
let aliases = newLit(s.alias.mapIt("($1)" % [it]).join(" "))
|
let aliases = newLit(s.alias.mapIt("($1)" % [it]).join(" "))
|
||||||
let desc = s.desc or newLit("")
|
let desc = s.help.description or newLit("")
|
||||||
result.add quote do:
|
result.add quote do:
|
||||||
(`cmd`, `aliases`, `desc`)
|
(`cmd`, `aliases`, `desc`)
|
||||||
|
|
||||||
|
@ -588,19 +646,23 @@ func defaultUsage(cfg: CliCfg): NimNode =
|
||||||
s.add " [[[faint]flags[/]]"
|
s.add " [[[faint]flags[/]]"
|
||||||
newLit(s)
|
newLit(s)
|
||||||
|
|
||||||
func generateCliHelperProc(cfg: CliCfg, printHelpName: NimNode): NimNode =
|
func generateCliHelpProc(cfg: CliCfg, printHelpName: NimNode): NimNode =
|
||||||
let
|
let
|
||||||
desc = cfg.desc or newLit("")
|
description = cfg.help.description or newLit""
|
||||||
usage = cfg.usage or defaultUsage(cfg)
|
header = cfg.help.header or newLit""
|
||||||
|
footer = cfg.help.footer or newLit""
|
||||||
|
usage = cfg.help.usage or defaultUsage(cfg)
|
||||||
helpFlags = cfg.flagsArray()
|
helpFlags = cfg.flagsArray()
|
||||||
subcmds = cfg.subCmdsArray()
|
subcmds = cfg.subCmdsArray()
|
||||||
styles = cfg.styles or (quote do: HwylCliStyles())
|
styles = cfg.help.styles or (quote do: HwylCliStyles())
|
||||||
|
<<< usage
|
||||||
result = quote do:
|
result = quote do:
|
||||||
proc `printHelpName`() =
|
proc `printHelpName`() =
|
||||||
echo bb(render(newHwylCliHelp(
|
echo bb(render(newHwylCliHelp(
|
||||||
desc = `desc`,
|
header = `header`,
|
||||||
|
footer = `footer`,
|
||||||
usage = `usage`,
|
usage = `usage`,
|
||||||
|
description = `description`,
|
||||||
subcmds = `subcmds`,
|
subcmds = `subcmds`,
|
||||||
flags = `helpFlags`,
|
flags = `helpFlags`,
|
||||||
styles = `styles`,
|
styles = `styles`,
|
||||||
|
@ -814,7 +876,7 @@ func hwylCliImpl(cfg: CliCfg): NimNode =
|
||||||
key = ident"key"
|
key = ident"key"
|
||||||
val = ident"val"
|
val = ident"val"
|
||||||
(longNoVal, shortNoVal) = cfg.getNoVals()
|
(longNoVal, shortNoVal) = cfg.getNoVals()
|
||||||
printHelperProc = generateCliHelperProc(cfg, printHelpName)
|
printHelpProc = generateCliHelpProc(cfg, printHelpName)
|
||||||
flagVars = setFlagVars(cfg)
|
flagVars = setFlagVars(cfg)
|
||||||
|
|
||||||
result = newTree(nnkStmtList)
|
result = newTree(nnkStmtList)
|
||||||
|
@ -889,7 +951,7 @@ func hwylCliImpl(cfg: CliCfg): NimNode =
|
||||||
|
|
||||||
result.add quote do:
|
result.add quote do:
|
||||||
# block:
|
# block:
|
||||||
`printHelperProc`
|
`printHelpProc`
|
||||||
`flagVars`
|
`flagVars`
|
||||||
proc `parserProcName`(`cmdLine`: openArray[string] = commandLineParams()): seq[string] =
|
proc `parserProcName`(`cmdLine`: openArray[string] = commandLineParams()): seq[string] =
|
||||||
`parserBody`
|
`parserBody`
|
||||||
|
|
|
@ -10,7 +10,6 @@ type
|
||||||
hwylCli:
|
hwylCli:
|
||||||
name "example"
|
name "example"
|
||||||
V "0.1.0"
|
V "0.1.0"
|
||||||
... "a description of hwylterm"
|
|
||||||
flags:
|
flags:
|
||||||
[global]
|
[global]
|
||||||
color:
|
color:
|
||||||
|
|
3
todo.md
3
todo.md
|
@ -21,7 +21,8 @@
|
||||||
- [x] add support for inheriting a single flag from parent (even from a "group")
|
- [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 support to either (lengthen commands) or provide an alias for a subcommand
|
||||||
- [x] add command aliases to hwylcli help with switch
|
- [x] add command aliases to hwylcli help with switch
|
||||||
- [ ] don't recreate "global"" variables in var section
|
- [x] don't recreate "global"" variables in var section
|
||||||
|
- [ ] make proper test suite for cli generator
|
||||||
|
|
||||||
|
|
||||||
## features
|
## features
|
||||||
|
|
Loading…
Reference in a new issue