Skip to content

Commit

Permalink
Fix default display in help for EnumerableFlag and other types (#486)
Browse files Browse the repository at this point in the history
In the help for flags like --prefix/--no-prefix, use the name of the
default flag instead of true/false. When EnumerableFlag types
have separate help strings, show the correct default flag name
on the default flag in the help.
  • Loading branch information
natecook1000 committed Sep 14, 2022
1 parent 607021b commit 7506042
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 25 deletions.
41 changes: 34 additions & 7 deletions Sources/ArgumentParser/Parsable Properties/Flag.swift
Expand Up @@ -391,25 +391,52 @@ extension Flag where Value: EnumerableFlag {
// This gets flipped to `true` the first time one of these flags is
// encountered.
var hasUpdated = false
let defaultValue = initial.map(String.init(describing:))

// Create a string representation of the default value. Since this is a
// flag, the default value to show to the user is the `--value-name`
// flag that a user would provide on the command line, not a Swift value.
let defaultValueFlag = initial.flatMap { value in
let defaultKey = InputKey(rawValue: String(describing: value))
let defaultNames = Value.name(for: value).makeNames(defaultKey)
return defaultNames.first?.synopsisString
}

let caseHelps = Value.allCases.map { Value.help(for: $0) }
let hasCustomCaseHelp = caseHelps.contains(where: { $0 != nil })

let args = Value.allCases.enumerated().map { (i, value) -> ArgumentDefinition in
let caseKey = InputKey(rawValue: String(describing: value))
let name = Value.name(for: value)
let helpForCase = hasCustomCaseHelp ? (caseHelps[i] ?? help) : help

let helpForCase = caseHelps[i] ?? help
var defaultValueString: String? = nil
if hasCustomCaseHelp {
if value == initial {
defaultValueString = defaultValueFlag
}
} else {
defaultValueString = defaultValueFlag
}

let help = ArgumentDefinition.Help(
allValues: [],
options: initial != nil ? [.isOptional] : [],
options: initial != nil ? .isOptional : [],
help: helpForCase,
defaultValue: defaultValue,
defaultValue: defaultValueString,
key: key,
isComposite: !hasCustomCaseHelp)
return ArgumentDefinition.flag(name: name, key: key, caseKey: caseKey, help: help, parsingStrategy: .default, initialValue: initial, update: .nullary({ (origin, name, values) in
hasUpdated = try ArgumentSet.updateFlag(key: key, value: value, origin: origin, values: &values, hasUpdated: hasUpdated, exclusivity: exclusivity)
}))

return ArgumentDefinition.flag(
name: name,
key: key,
caseKey: caseKey,
help: help,
parsingStrategy: .default,
initialValue: initial,
update: .nullary({ (origin, name, values) in
hasUpdated = try ArgumentSet.updateFlag(key: key, value: value, origin: origin, values: &values, hasUpdated: hasUpdated, exclusivity: exclusivity)
})
)
}
return ArgumentSet(args)
})
Expand Down
9 changes: 6 additions & 3 deletions Sources/ArgumentParser/Parsing/ArgumentSet.swift
Expand Up @@ -122,12 +122,15 @@ extension ArgumentSet {
help: ArgumentHelp?) -> ArgumentSet
{
let helpOptions: ArgumentDefinition.Help.Options = required ? [] : .isOptional

let (enableNames, disableNames) = inversion.enableDisableNamePair(for: key, name: name)
let initialValueNames = initialValue.map {
$0 ? enableNames : disableNames
}

let enableHelp = ArgumentDefinition.Help(allValues: [], options: helpOptions, help: help, defaultValue: initialValue.map(String.init), key: key, isComposite: true)
let enableHelp = ArgumentDefinition.Help(allValues: [], options: helpOptions, help: help, defaultValue: initialValueNames?.first?.synopsisString, key: key, isComposite: true)
let disableHelp = ArgumentDefinition.Help(allValues: [], options: [.isOptional], help: help, defaultValue: nil, key: key, isComposite: false)

let (enableNames, disableNames) = inversion.enableDisableNamePair(for: key, name: name)

var hasUpdated = false
let enableArg = ArgumentDefinition(kind: .named(enableNames), help: enableHelp, completion: .default, update: .nullary({ (origin, name, values) in
hasUpdated = try ArgumentSet.updateFlag(key: key, value: true, origin: origin, values: &values, hasUpdated: hasUpdated, exclusivity: exclusivity)
Expand Down
25 changes: 21 additions & 4 deletions Tests/ArgumentParserEndToEndTests/FlagsEndToEndTests.swift
Expand Up @@ -162,11 +162,11 @@ enum Size: String, EnumerableFlag {
static func help(for value: Size) -> ArgumentHelp? {
switch value {
case .small:
return "A smallish size"
return "A smallish size."
case .medium:
return "Not too big, not too small"
return "Not too big, not too small."
case .humongous:
return "Roughly the size of a barge"
return "Roughly the size of a barge."
case .large, .extraLarge:
return nil
}
Expand All @@ -183,7 +183,7 @@ fileprivate struct Baz: ParsableArguments {
@Flag()
var color: Color

@Flag
@Flag(help: "The size to use.")
var size: Size = .small

@Flag()
Expand Down Expand Up @@ -257,6 +257,23 @@ extension FlagsEndToEndTests {
}
}

func testParsingCaseIterable_Help() throws {
AssertHelp(.default, for: Baz.self, equals: """
USAGE: baz --pink --purple --silver [--small] [--medium] [--large] [--extra-large] [--humongous] [--round] [--square] [--oblong]
OPTIONS:
--pink/--purple/--silver
-s, --small A smallish size. (default: --small)
-m, --medium Not too big, not too small.
-l, --large The size to use.
--extra-large The size to use.
--humongous, --huge Roughly the size of a barge.
--round/--square/--oblong
-h, --help Show help information.
""")
}

func testParsingCaseIterable_Fails() throws {
// Missing color
XCTAssertThrowsError(try Baz.parse([]))
Expand Down
17 changes: 10 additions & 7 deletions Tests/ArgumentParserPackageManagerTests/HelpTests.swift
Expand Up @@ -119,21 +119,24 @@ extension HelpTests {
Build with configuration (default: debug)
--enable-automatic-resolution/--disable-automatic-resolution
Use automatic resolution if Package.resolved file is
out-of-date (default: true)
out-of-date (default: --enable-automatic-resolution)
--enable-index-store/--disable-index-store
Use indexing-while-building feature (default: true)
Use indexing-while-building feature (default:
--enable-index-store)
--enable-package-manifest-caching/--disable-package-manifest-caching
Cache Package.swift manifests (default: true)
Cache Package.swift manifests (default:
--enable-package-manifest-caching)
--enable-prefetching/--disable-prefetching
(default: true)
(default: --enable-prefetching)
--enable-sandbox/--disable-sandbox
Use sandbox when executing subprocesses (default:
true)
--enable-sandbox)
--enable-pubgrub-resolver/--disable-pubgrub-resolver
[Experimental] Enable the new Pubgrub dependency
resolver (default: false)
resolver (default: --disable-pubgrub-resolver)
--static-swift-stdlib/--no-static-swift-stdlib
Link Swift stdlib statically (default: false)
Link Swift stdlib statically (default:
--no-static-swift-stdlib)
--package-path <package-path>
Change working directory before any other operation
(default: .)
Expand Down
8 changes: 4 additions & 4 deletions Tests/ArgumentParserUnitTests/HelpGenerationTests.swift
Expand Up @@ -84,7 +84,7 @@ extension HelpGenerationTests {
--hidden-title <hidden-title>
--hidden-flag
--hidden-inverted-flag/--no-hidden-inverted-flag
(default: true)
(default: --hidden-inverted-flag)
-h, --help Show help information.
""")
Expand Down Expand Up @@ -207,7 +207,7 @@ extension HelpGenerationTests {
--age <age> Your age. (default: 20)
--logging <logging> Whether logging is enabled. (default: false)
--lucky <numbers> Your lucky numbers. (default: 7, 14)
--optional/--required Vegan diet. (default: optional)
--optional/--required Vegan diet. (default: --optional)
--degree <degree> Your degree.
--directory <directory> Directory. (default: current directory)
--manual <manual> Manual Option. (default: default-value)
Expand Down Expand Up @@ -264,7 +264,7 @@ extension HelpGenerationTests {
USAGE: f [-s] [-c] [-l]
OPTIONS:
-s/-c/-l Change the program output (default: list)
-s/-c/-l Change the program output (default: -l)
-h, --help Show help information.
""")
Expand All @@ -273,7 +273,7 @@ extension HelpGenerationTests {
USAGE: g [--flag] [--no-flag]
OPTIONS:
--flag/--no-flag Whether to flag (default: false)
--flag/--no-flag Whether to flag (default: --no-flag)
-h, --help Show help information.
""")
Expand Down

0 comments on commit 7506042

Please sign in to comment.