From d8758bc4978ee0e0b5828e7c7aa9da4db010f926 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Fri, 26 Aug 2022 15:57:46 -0500 Subject: [PATCH 1/4] Fix default display in help for EnumerableFlag flags --- .../Parsable Properties/Flag.swift | 44 ++++++++++++++++--- .../HelpGenerationTests.swift | 4 +- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/Sources/ArgumentParser/Parsable Properties/Flag.swift b/Sources/ArgumentParser/Parsable Properties/Flag.swift index 8b0dc7060..d7e4cd88e 100644 --- a/Sources/ArgumentParser/Parsable Properties/Flag.swift +++ b/Sources/ArgumentParser/Parsable Properties/Flag.swift @@ -391,7 +391,15 @@ 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 }) @@ -399,11 +407,35 @@ extension Flag where Value: EnumerableFlag { 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 help = ArgumentDefinition.Help(options: initial != nil ? .isOptional : [], help: helpForCase, defaultValue: defaultValue, 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) - })) + + let helpForCase = caseHelps[i] ?? help + var defaultValueString: String? = "" + if hasCustomCaseHelp { + if value == initial { + defaultValueString = "this" + } + } else { + defaultValueString = defaultValueFlag + } + + let help = ArgumentDefinition.Help( + options: initial != nil ? .isOptional : [], + help: helpForCase, + 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 ArgumentSet(args) }) diff --git a/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift b/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift index f963d89e5..61eb4bb1e 100644 --- a/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift +++ b/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift @@ -207,7 +207,7 @@ extension HelpGenerationTests { --age Your age. (default: 20) --logging Whether logging is enabled. (default: false) --lucky Your lucky numbers. (default: 7, 14) - --optional/--required Vegan diet. (default: optional) + --optional/--required Vegan diet. (default: --optional) --degree Your degree. (default: bachelor) --directory Directory. (default: current directory) --manual Manual Option. (default: default-value) @@ -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. """) From 261f047e4e3f8c0bd259515b651db3e52bea66ac Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Thu, 1 Sep 2022 15:50:39 -0500 Subject: [PATCH 2/4] Use correct names for Bool flags with inversions In the help for flags like --prefix/--no-prefix, use the name of the default flag instead of true/false. --- .../ArgumentParser/Parsing/ArgumentSet.swift | 10 +++++++--- .../HelpTests.swift | 17 ++++++++++------- .../HelpGenerationTests.swift | 4 ++-- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/Sources/ArgumentParser/Parsing/ArgumentSet.swift b/Sources/ArgumentParser/Parsing/ArgumentSet.swift index 2eee0a4cc..6de19906d 100644 --- a/Sources/ArgumentParser/Parsing/ArgumentSet.swift +++ b/Sources/ArgumentParser/Parsing/ArgumentSet.swift @@ -116,11 +116,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(options: helpOptions, help: help, defaultValue: initialValue.map(String.init), key: key, isComposite: true) - let disableHelp = ArgumentDefinition.Help(options: [.isOptional], help: help, key: key) - let (enableNames, disableNames) = inversion.enableDisableNamePair(for: key, name: name) + let enableHelp = ArgumentDefinition.Help(options: helpOptions, help: help, defaultValue: initialValueNames?.first?.synopsisString, key: key, isComposite: true) + let disableHelp = ArgumentDefinition.Help(options: [.isOptional], help: help, key: key) var hasUpdated = false let enableArg = ArgumentDefinition(kind: .named(enableNames), help: enableHelp, completion: .default, update: .nullary({ (origin, name, values) in diff --git a/Tests/ArgumentParserPackageManagerTests/HelpTests.swift b/Tests/ArgumentParserPackageManagerTests/HelpTests.swift index aa52bf873..dd6ad2d7c 100644 --- a/Tests/ArgumentParserPackageManagerTests/HelpTests.swift +++ b/Tests/ArgumentParserPackageManagerTests/HelpTests.swift @@ -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 Change working directory before any other operation (default: .) diff --git a/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift b/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift index 61eb4bb1e..731852314 100644 --- a/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift +++ b/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift @@ -84,7 +84,7 @@ extension HelpGenerationTests { --hidden-title --hidden-flag --hidden-inverted-flag/--no-hidden-inverted-flag - (default: true) + (default: --hidden-inverted-flag) -h, --help Show help information. """) @@ -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. """) From 283bd5811d2b863e9d7553d9b6deb2069b76bc00 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Thu, 1 Sep 2022 15:50:55 -0500 Subject: [PATCH 3/4] Add a test for EnumerableFlag.help(for:) --- .../FlagsEndToEndTests.swift | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/Tests/ArgumentParserEndToEndTests/FlagsEndToEndTests.swift b/Tests/ArgumentParserEndToEndTests/FlagsEndToEndTests.swift index dd4403ddd..bfbc62cdd 100644 --- a/Tests/ArgumentParserEndToEndTests/FlagsEndToEndTests.swift +++ b/Tests/ArgumentParserEndToEndTests/FlagsEndToEndTests.swift @@ -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 } @@ -183,7 +183,7 @@ fileprivate struct Baz: ParsableArguments { @Flag() var color: Color - @Flag + @Flag(help: "The size to use.") var size: Size = .small @Flag() @@ -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: this) + -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([])) From 4d2e5d3605503eb56b18ff8747f3d25bd8f46839 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Fri, 2 Sep 2022 12:34:09 -0500 Subject: [PATCH 4/4] Show the correct flag name for EnumerableFlags When EnumerableFlag types have separate help strings, show the correct default flag name on the default flag in the help. --- Sources/ArgumentParser/Parsable Properties/Flag.swift | 4 ++-- Tests/ArgumentParserEndToEndTests/FlagsEndToEndTests.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/ArgumentParser/Parsable Properties/Flag.swift b/Sources/ArgumentParser/Parsable Properties/Flag.swift index d7e4cd88e..04a49604a 100644 --- a/Sources/ArgumentParser/Parsable Properties/Flag.swift +++ b/Sources/ArgumentParser/Parsable Properties/Flag.swift @@ -409,10 +409,10 @@ extension Flag where Value: EnumerableFlag { let name = Value.name(for: value) let helpForCase = caseHelps[i] ?? help - var defaultValueString: String? = "" + var defaultValueString: String? = nil if hasCustomCaseHelp { if value == initial { - defaultValueString = "this" + defaultValueString = defaultValueFlag } } else { defaultValueString = defaultValueFlag diff --git a/Tests/ArgumentParserEndToEndTests/FlagsEndToEndTests.swift b/Tests/ArgumentParserEndToEndTests/FlagsEndToEndTests.swift index bfbc62cdd..89c16e3be 100644 --- a/Tests/ArgumentParserEndToEndTests/FlagsEndToEndTests.swift +++ b/Tests/ArgumentParserEndToEndTests/FlagsEndToEndTests.swift @@ -263,7 +263,7 @@ extension FlagsEndToEndTests { OPTIONS: --pink/--purple/--silver - -s, --small A smallish size. (default: this) + -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.