Skip to content

Commit 7506042

Browse files
authored
Fix default display in help for EnumerableFlag and other types (#486)
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.
1 parent 607021b commit 7506042

File tree

5 files changed

+75
-25
lines changed

5 files changed

+75
-25
lines changed

Sources/ArgumentParser/Parsable Properties/Flag.swift

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -391,25 +391,52 @@ extension Flag where Value: EnumerableFlag {
391391
// This gets flipped to `true` the first time one of these flags is
392392
// encountered.
393393
var hasUpdated = false
394-
let defaultValue = initial.map(String.init(describing:))
394+
395+
// Create a string representation of the default value. Since this is a
396+
// flag, the default value to show to the user is the `--value-name`
397+
// flag that a user would provide on the command line, not a Swift value.
398+
let defaultValueFlag = initial.flatMap { value in
399+
let defaultKey = InputKey(rawValue: String(describing: value))
400+
let defaultNames = Value.name(for: value).makeNames(defaultKey)
401+
return defaultNames.first?.synopsisString
402+
}
395403

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

399407
let args = Value.allCases.enumerated().map { (i, value) -> ArgumentDefinition in
400408
let caseKey = InputKey(rawValue: String(describing: value))
401409
let name = Value.name(for: value)
402-
let helpForCase = hasCustomCaseHelp ? (caseHelps[i] ?? help) : help
410+
411+
let helpForCase = caseHelps[i] ?? help
412+
var defaultValueString: String? = nil
413+
if hasCustomCaseHelp {
414+
if value == initial {
415+
defaultValueString = defaultValueFlag
416+
}
417+
} else {
418+
defaultValueString = defaultValueFlag
419+
}
420+
403421
let help = ArgumentDefinition.Help(
404422
allValues: [],
405-
options: initial != nil ? [.isOptional] : [],
423+
options: initial != nil ? .isOptional : [],
406424
help: helpForCase,
407-
defaultValue: defaultValue,
425+
defaultValue: defaultValueString,
408426
key: key,
409427
isComposite: !hasCustomCaseHelp)
410-
return ArgumentDefinition.flag(name: name, key: key, caseKey: caseKey, help: help, parsingStrategy: .default, initialValue: initial, update: .nullary({ (origin, name, values) in
411-
hasUpdated = try ArgumentSet.updateFlag(key: key, value: value, origin: origin, values: &values, hasUpdated: hasUpdated, exclusivity: exclusivity)
412-
}))
428+
429+
return ArgumentDefinition.flag(
430+
name: name,
431+
key: key,
432+
caseKey: caseKey,
433+
help: help,
434+
parsingStrategy: .default,
435+
initialValue: initial,
436+
update: .nullary({ (origin, name, values) in
437+
hasUpdated = try ArgumentSet.updateFlag(key: key, value: value, origin: origin, values: &values, hasUpdated: hasUpdated, exclusivity: exclusivity)
438+
})
439+
)
413440
}
414441
return ArgumentSet(args)
415442
})

Sources/ArgumentParser/Parsing/ArgumentSet.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,15 @@ extension ArgumentSet {
122122
help: ArgumentHelp?) -> ArgumentSet
123123
{
124124
let helpOptions: ArgumentDefinition.Help.Options = required ? [] : .isOptional
125+
126+
let (enableNames, disableNames) = inversion.enableDisableNamePair(for: key, name: name)
127+
let initialValueNames = initialValue.map {
128+
$0 ? enableNames : disableNames
129+
}
125130

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

129-
let (enableNames, disableNames) = inversion.enableDisableNamePair(for: key, name: name)
130-
131134
var hasUpdated = false
132135
let enableArg = ArgumentDefinition(kind: .named(enableNames), help: enableHelp, completion: .default, update: .nullary({ (origin, name, values) in
133136
hasUpdated = try ArgumentSet.updateFlag(key: key, value: true, origin: origin, values: &values, hasUpdated: hasUpdated, exclusivity: exclusivity)

Tests/ArgumentParserEndToEndTests/FlagsEndToEndTests.swift

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,11 +162,11 @@ enum Size: String, EnumerableFlag {
162162
static func help(for value: Size) -> ArgumentHelp? {
163163
switch value {
164164
case .small:
165-
return "A smallish size"
165+
return "A smallish size."
166166
case .medium:
167-
return "Not too big, not too small"
167+
return "Not too big, not too small."
168168
case .humongous:
169-
return "Roughly the size of a barge"
169+
return "Roughly the size of a barge."
170170
case .large, .extraLarge:
171171
return nil
172172
}
@@ -183,7 +183,7 @@ fileprivate struct Baz: ParsableArguments {
183183
@Flag()
184184
var color: Color
185185

186-
@Flag
186+
@Flag(help: "The size to use.")
187187
var size: Size = .small
188188

189189
@Flag()
@@ -257,6 +257,23 @@ extension FlagsEndToEndTests {
257257
}
258258
}
259259

260+
func testParsingCaseIterable_Help() throws {
261+
AssertHelp(.default, for: Baz.self, equals: """
262+
USAGE: baz --pink --purple --silver [--small] [--medium] [--large] [--extra-large] [--humongous] [--round] [--square] [--oblong]
263+
264+
OPTIONS:
265+
--pink/--purple/--silver
266+
-s, --small A smallish size. (default: --small)
267+
-m, --medium Not too big, not too small.
268+
-l, --large The size to use.
269+
--extra-large The size to use.
270+
--humongous, --huge Roughly the size of a barge.
271+
--round/--square/--oblong
272+
-h, --help Show help information.
273+
274+
""")
275+
}
276+
260277
func testParsingCaseIterable_Fails() throws {
261278
// Missing color
262279
XCTAssertThrowsError(try Baz.parse([]))

Tests/ArgumentParserPackageManagerTests/HelpTests.swift

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -119,21 +119,24 @@ extension HelpTests {
119119
Build with configuration (default: debug)
120120
--enable-automatic-resolution/--disable-automatic-resolution
121121
Use automatic resolution if Package.resolved file is
122-
out-of-date (default: true)
122+
out-of-date (default: --enable-automatic-resolution)
123123
--enable-index-store/--disable-index-store
124-
Use indexing-while-building feature (default: true)
124+
Use indexing-while-building feature (default:
125+
--enable-index-store)
125126
--enable-package-manifest-caching/--disable-package-manifest-caching
126-
Cache Package.swift manifests (default: true)
127+
Cache Package.swift manifests (default:
128+
--enable-package-manifest-caching)
127129
--enable-prefetching/--disable-prefetching
128-
(default: true)
130+
(default: --enable-prefetching)
129131
--enable-sandbox/--disable-sandbox
130132
Use sandbox when executing subprocesses (default:
131-
true)
133+
--enable-sandbox)
132134
--enable-pubgrub-resolver/--disable-pubgrub-resolver
133135
[Experimental] Enable the new Pubgrub dependency
134-
resolver (default: false)
136+
resolver (default: --disable-pubgrub-resolver)
135137
--static-swift-stdlib/--no-static-swift-stdlib
136-
Link Swift stdlib statically (default: false)
138+
Link Swift stdlib statically (default:
139+
--no-static-swift-stdlib)
137140
--package-path <package-path>
138141
Change working directory before any other operation
139142
(default: .)

Tests/ArgumentParserUnitTests/HelpGenerationTests.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ extension HelpGenerationTests {
8484
--hidden-title <hidden-title>
8585
--hidden-flag
8686
--hidden-inverted-flag/--no-hidden-inverted-flag
87-
(default: true)
87+
(default: --hidden-inverted-flag)
8888
-h, --help Show help information.
8989
9090
""")
@@ -207,7 +207,7 @@ extension HelpGenerationTests {
207207
--age <age> Your age. (default: 20)
208208
--logging <logging> Whether logging is enabled. (default: false)
209209
--lucky <numbers> Your lucky numbers. (default: 7, 14)
210-
--optional/--required Vegan diet. (default: optional)
210+
--optional/--required Vegan diet. (default: --optional)
211211
--degree <degree> Your degree.
212212
--directory <directory> Directory. (default: current directory)
213213
--manual <manual> Manual Option. (default: default-value)
@@ -264,7 +264,7 @@ extension HelpGenerationTests {
264264
USAGE: f [-s] [-c] [-l]
265265
266266
OPTIONS:
267-
-s/-c/-l Change the program output (default: list)
267+
-s/-c/-l Change the program output (default: -l)
268268
-h, --help Show help information.
269269
270270
""")
@@ -273,7 +273,7 @@ extension HelpGenerationTests {
273273
USAGE: g [--flag] [--no-flag]
274274
275275
OPTIONS:
276-
--flag/--no-flag Whether to flag (default: false)
276+
--flag/--no-flag Whether to flag (default: --no-flag)
277277
-h, --help Show help information.
278278
279279
""")

0 commit comments

Comments
 (0)