Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 58 additions & 20 deletions confutils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ type
terminalWidth: int
namesWidth: int
flags: set[HelpFlag]
defaultValueDescs: seq[string]

CmdInfo = ref object
name: string
Expand All @@ -74,7 +75,6 @@ type
idx: int
flags: set[OptFlag]
hasDefault: bool
defaultInHelpText: string
obsoleteMsg: string
case kind: OptKind
of Discriminator:
Expand Down Expand Up @@ -194,6 +194,12 @@ template flushOutputAndQuit(exitCode: int) =
flushOutput
quit exitCode

func defaultValueDesc(appInfo: HelpAppInfo, optIdx: int): string =
if optIdx == -1:
""
else:
appInfo.defaultValueDescs[optIdx]

func helpOptDesc(appInfo: HelpAppInfo): string =
result = "Show this help message and exit"
if appInfo.hasDebugOpts:
Expand Down Expand Up @@ -367,7 +373,7 @@ proc describeInvocation(
let cliArg = "<" & arg.name & ">"
helpOutput fgArg, styleBright, cliArg
helpOutput padding(cliArg, appInfo.namesWidth)
help.writeDesc appInfo, arg.desc, arg.defaultInHelpText
help.writeDesc appInfo, arg.desc, appInfo.defaultValueDesc(arg.idx)
help.writeLongDesc appInfo, arg.longDesc
helpOutput "\p"

Expand Down Expand Up @@ -410,7 +416,7 @@ proc describeOptionsList(
if opt.desc.len > 0:
help.writeDesc appInfo,
opt.desc.replace("%t", opt.typename),
opt.defaultInHelpText
appInfo.defaultValueDesc(opt.idx)
help.writeLongDesc appInfo, opt.longDesc

helpOutput "\p"
Expand Down Expand Up @@ -445,14 +451,16 @@ proc describeOptions(
let helpOpt = OptInfo(
kind: CliSwitch,
name: "help",
desc: helpOptDesc(appInfo)
desc: helpOptDesc(appInfo),
idx: -1
)
describeOptionsList(help, [helpOpt], appInfo, excl)
if appInfo.hasVersion:
let versionOpt = OptInfo(
kind: CliSwitch,
name: "version",
desc: "Show program's version and exit"
desc: "Show program's version and exit",
idx: -1
)
describeOptionsList(help, [versionOpt], appInfo, excl)

Expand Down Expand Up @@ -828,6 +836,20 @@ proc generateFieldSetters(RecordType: NimNode): NimNode =
defaultValue = field.readPragma"defaultValue"
completerName = ident($field.name & "Complete")
isFieldDiscriminator = newLit field.isDiscriminator
defaultValueDesc = field.readPragma"defaultValueDesc"
defaultValueHelp =
if defaultValueDesc != nil:
defaultValueDesc
elif defaultValue != nil:
defaultValue
else:
newLit("")
defaultValueHelpRepr =
if defaultValueDesc == nil and defaultValue != nil:
newLit(repr defaultValue)
else:
newLit("")
defaultValueHelpName = ident($field.name & "DefaultValueHelp")

if defaultValue == nil:
defaultValue = newCall(makeDefaultValue, newTree(nnkTypeOfExpr, configField))
Expand All @@ -838,9 +860,11 @@ proc generateFieldSetters(RecordType: NimNode): NimNode =

settersArray.add newTree(nnkTupleConstr,
newLit($paramName),
setterName, completerName,
setterName,
completerName,
newCall(bindSym"requiresInput", fixedFieldType),
newCall(bindSym"acceptsMultipleValues", fixedFieldType))
newCall(bindSym"acceptsMultipleValues", fixedFieldType),
defaultValueHelpName)

result.add quote do:
{.push hint[XCannotRaiseY]: off.}
Expand Down Expand Up @@ -870,6 +894,22 @@ proc generateFieldSetters(RecordType: NimNode): NimNode =
else:
setField(`configField`, val, `defaultValue`)

proc `defaultValueHelpName`(): string {.
nimcall
gcsafe
sideEffect
raises: []
.} =
# defaultValue can be `config.otherField` which won't compile
# `$` may not be defined for this type
when compiles($`defaultValueHelp`):
when typeof($`defaultValueHelp`) is string:
$`defaultValueHelp`
else:
`defaultValueHelpRepr`
else:
`defaultValueHelpRepr`

result.add quote do:
{.pop.}

Expand Down Expand Up @@ -901,11 +941,6 @@ func findPath(parent, node: CmdInfo): seq[CmdInfo] =
doAssert validPath(result, parent, node)
result.add parent

func toText(n: NimNode): string =
if n == nil: ""
elif n.kind in {nnkStrLit..nnkTripleStrLit}: n.strVal
else: repr(n)

func readPragmaFlags(field: FieldDescription): set[OptFlag] =
result = {}
if field.readPragma("hidden") != nil:
Expand All @@ -927,10 +962,6 @@ proc cmdInfoFromType(T: NimNode): CmdInfo =
let
isImplicitlySelectable = field.readPragma"implicitlySelectable" != nil
defaultValue = field.readPragma"defaultValue"
defaultValueDesc = field.readPragma"defaultValueDesc"
defaultInHelp = if defaultValueDesc != nil: defaultValueDesc
else: defaultValue
defaultInHelpText = toText(defaultInHelp)
obsoleteMsg = field.readPragma"obsolete"
separator = field.readPragma"separator"
longDesc = field.readPragma"longDesc"
Expand All @@ -948,7 +979,6 @@ proc cmdInfoFromType(T: NimNode): CmdInfo =
name: $field.name,
flags: optFlags,
hasDefault: defaultValue != nil,
defaultInHelpText: defaultInHelpText,
typename: field.typ.repr)

if desc != nil: opt.desc = desc.strVal
Expand Down Expand Up @@ -1290,19 +1320,27 @@ proc loadImpl[C, SecondarySources](

return

proc lazyHelpAppInfo(flags: set[HelpFlag]): HelpAppInfo =
proc lazyHelpAppInfo(flags: set[HelpFlag], defaultValueDescs: seq[string]): HelpAppInfo =
HelpAppInfo(
copyrightBanner: copyrightBanner,
appInvocation: appInvocation(),
terminalWidth: termWidth,
hasVersion: version.len > 0,
flags: flags
flags: flags,
defaultValueDescs: defaultValueDescs
)

template getDefaultValueDescs(): seq[string] =
var defaultValueDescs = newSeq[string](fieldSetters.len)
for i in 0 ..< fieldSetters.len:
defaultValueDescs[i] = fieldSetters[i][5]()
defaultValueDescs

template processHelpAndVersionOptions(optKey, optVal: string) =
let key = optKey
if cmpIgnoreStyle(key, "help") == 0:
help.showHelp(lazyHelpAppInfo(optVal.toHelpFlags), activeCmds)
let defaultValueDescs = getDefaultValueDescs()
help.showHelp(lazyHelpAppInfo(optVal.toHelpFlags, defaultValueDescs), activeCmds)
elif version.len > 0 and cmpIgnoreStyle(key, "version") == 0:
help.helpOutput version, "\p"
flushOutputAndQuit QuitSuccess
Expand Down
17 changes: 15 additions & 2 deletions tests/help/snapshots/test_default_value_desc.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,18 @@ test_default_value_desc [OPTIONS]...

The following options are available:

--help Show this help message and exit.
--opt1 tcp port [=9000].
--help Show this help message and exit.
--opt1 tcp port [=9000].
--opt2 tcp port 2 [=9000].
--opt3 const default value desc [=ok].
--opt4 literal default value desc [=ok].
--opt5 distinct string [=ok].
--opt6 no dollar func defined [=NoDollar("default")].
--opt7 no dollar func defined with default desc [=ok].
--opt8 runtime value [=ok].
--opt9 runtime value [=ok].
--opt10 runtime value [=ok concat].
--opt11 default value can raise but default desc won't [=ok].
--opt12 default is config.opt11 [=config.opt11].
--opt13 default is config.opt11 with default desc [=ok].
--opt14 default enum is TestEnum.OK [=OK].
109 changes: 109 additions & 0 deletions tests/help/test_default_value_desc.nim
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,46 @@
# This file may not be copied, modified, or distributed except according to
# those terms.

import os
import ../../confutils

const defaultEth2TcpPort = 9000
const defaultDescOverride = "ok"

type TestEnum {.pure.} = enum
BAD
OK

type DisString = distinct string
type NoDollar = distinct string

proc `$`(s: DisString): string =
# this should show in the help message
"ok"

proc completeCmdArg(T: type DisString, val: string): seq[string] =
completeCmdArg(string, val)

proc parseCmdArg(T: type DisString, s: string): T =
parseCmdArg(string, s).DisString

proc completeCmdArg(T: type NoDollar, val: string): seq[string] =
completeCmdArg(string, val)

proc parseCmdArg(T: type NoDollar, s: string): T =
parseCmdArg(string, s).NoDollar

proc runtimeVal(): string =
# do something that's not available at comptime
{.cast(raises: []).}:
discard getCurrentProcessId()
"ok"

proc canRaise(): string {.raises: [ValueError].} =
# do something that's not available at comptime
if false:
raise (ref ValueError)(msg: "never raised")
"bad"

type
TestConf = object
Expand All @@ -19,4 +56,76 @@ type
desc: "tcp port"
name: "opt1" }: int

opt2 {.
defaultValue: defaultEth2TcpPort
desc: "tcp port 2"
name: "opt2" }: int

opt3 {.
defaultValue: "bad"
defaultValueDesc: defaultDescOverride
desc: "const default value desc"
name: "opt3" }: string

opt4 {.
defaultValue: "bad"
defaultValueDesc: "ok"
desc: "literal default value desc"
name: "opt4" }: string

opt5 {.
defaultValue: DisString("bad")
desc: "distinct string"
name: "opt5" }: DisString

opt6 {.
defaultValue: NoDollar("default")
desc: "no dollar func defined"
name: "opt6" }: NoDollar

opt7 {.
defaultValue: NoDollar("bad")
defaultValueDesc: "ok"
desc: "no dollar func defined with default desc"
name: "opt7" }: NoDollar

opt8 {.
defaultValue: runtimeVal()
desc: "runtime value"
name: "opt8" }: string

opt9 {.
defaultValue: "bad"
defaultValueDesc: runtimeVal()
desc: "runtime value"
name: "opt9" }: string

opt10 {.
defaultValue: "bad"
defaultValueDesc: runtimeVal() & " concat"
desc: "runtime value"
name: "opt10" }: string

opt11 {.
defaultValue: canRaise()
defaultValueDesc: "ok"
desc: "default value can raise but default desc won't"
name: "opt11" }: string

opt12 {.
defaultValue: config.opt11
desc: "default is config.opt11"
name: "opt12" }: string

opt13 {.
defaultValue: config.opt11
defaultValueDesc: "ok"
desc: "default is config.opt11 with default desc"
name: "opt13" }: string

opt14 {.
defaultValue: TestEnum.OK
desc: "default enum is TestEnum.OK"
name: "opt14" }: TestEnum

let c = TestConf.load(termWidth = int.high)