diff --git a/Sources/ArgumentParser/Parsable Types/CommandConfiguration.swift b/Sources/ArgumentParser/Parsable Types/CommandConfiguration.swift index b208c1ea1..55506b84f 100644 --- a/Sources/ArgumentParser/Parsable Types/CommandConfiguration.swift +++ b/Sources/ArgumentParser/Parsable Types/CommandConfiguration.swift @@ -15,6 +15,10 @@ public struct CommandConfiguration: Sendable { /// /// If `nil`, the command name is derived by converting the name of /// the command type to hyphen-separated lowercase words. + /// + /// NOTE: In help and usage texts if this is the root command and commandName is + /// `nil`, the name shown will be derived from the binary being invoked (argv[0]), + /// otherwise `commandName` will be displayed. public var commandName: String? /// The name of this command's "super-command". (experimental) diff --git a/Sources/ArgumentParser/Usage/HelpGenerator.swift b/Sources/ArgumentParser/Usage/HelpGenerator.swift index 6dfb411a5..c7f7908fb 100644 --- a/Sources/ArgumentParser/Usage/HelpGenerator.swift +++ b/Sources/ArgumentParser/Usage/HelpGenerator.swift @@ -89,6 +89,7 @@ internal struct HelpGenerator { var commandStack: [ParsableCommand.Type] var abstract: String var usage: String + var commandNames: String var sections: [Section] var discussionSections: [DiscussionSection] @@ -101,15 +102,22 @@ internal struct HelpGenerator { self.commandStack = commandStack // Build the tool name and subcommand name from the command configuration - var toolName = commandStack.map { $0._commandName }.joined(separator: " ") if let superName = commandStack.first!.configuration._superCommandName { - toolName = "\(superName) \(toolName)" + self.commandNames = commandStack.map { $0._commandName }.joined(separator: " ") + self.commandNames = "\(superName) \(commandNames)" + } else { + // If the user explicitly provided a command name, use this. Otherwise default to showing argv[0]. + let binaryName = CommandLine._staticArguments[0].split(separator: "/").last.map(String.init) ?? "" + self.commandNames = commandStack[0].configuration.commandName ?? binaryName + if commandStack.count > 1 { + self.commandNames += " " + commandStack[1...].map { $0._commandName }.joined(separator: " ") + } } if let usage = currentCommand.configuration.usage { self.usage = usage } else { - var usage = UsageGenerator(toolName: toolName, definition: [currentArgSet]) + var usage = UsageGenerator(toolName: self.commandNames, definition: [currentArgSet]) .synopsis if !currentCommand.configuration.subcommands.isEmpty { if usage.last != " " { usage += " " } @@ -245,7 +253,11 @@ internal struct HelpGenerator { guard !usage.isEmpty else { return "" } return "Usage: \(usage.hangingIndentingEachLine(by: 7))" } - + + func getCommandNames() -> String { + return self.commandNames + } + var includesSubcommands: Bool { guard let subcommandSection = sections.first(where: { $0.header == .subcommands }) else { return false } diff --git a/Sources/ArgumentParser/Usage/MessageInfo.swift b/Sources/ArgumentParser/Usage/MessageInfo.swift index bb9a4ea1d..7b86a0fed 100644 --- a/Sources/ArgumentParser/Usage/MessageInfo.swift +++ b/Sources/ArgumentParser/Usage/MessageInfo.swift @@ -82,9 +82,10 @@ enum MessageInfo { parserError = .userValidationError(error) } - var usage = HelpGenerator(commandStack: commandStack, visibility: .default).usageMessage() + let generator = HelpGenerator(commandStack: commandStack, visibility: .default) + var usage = generator.usageMessage() + let commandNames = generator.getCommandNames() - let commandNames = commandStack.map { $0._commandName }.joined(separator: " ") if let helpName = commandStack.getPrimaryHelpName() { if !usage.isEmpty { usage += "\n" diff --git a/Sources/ArgumentParserTestHelpers/TestHelpers.swift b/Sources/ArgumentParserTestHelpers/TestHelpers.swift index fb3019f0e..4a7a9b2ca 100644 --- a/Sources/ArgumentParserTestHelpers/TestHelpers.swift +++ b/Sources/ArgumentParserTestHelpers/TestHelpers.swift @@ -119,6 +119,10 @@ public func AssertParse(_ type: A.Type, _ arguments: [String], file: StaticSt } } +public func getFirstArgument() -> String { + return CommandLine.arguments[0].split(separator: "/").last.map(String.init) ?? "" +} + public func AssertParseCommand(_ rootCommand: ParsableCommand.Type, _ type: A.Type, _ arguments: [String], file: StaticString = #file, line: UInt = #line, closure: (A) throws -> Void) { do { let command = try rootCommand.parseAsRoot(arguments) diff --git a/Tests/ArgumentParserEndToEndTests/FlagsEndToEndTests.swift b/Tests/ArgumentParserEndToEndTests/FlagsEndToEndTests.swift index 89c16e3be..36c17bd8e 100644 --- a/Tests/ArgumentParserEndToEndTests/FlagsEndToEndTests.swift +++ b/Tests/ArgumentParserEndToEndTests/FlagsEndToEndTests.swift @@ -179,7 +179,9 @@ enum Shape: String, EnumerableFlag { case oblong } -fileprivate struct Baz: ParsableArguments { +fileprivate struct Baz: ParsableArguments, ParsableCommand { + static let configuration = CommandConfiguration(commandName: "baz") + @Flag() var color: Color diff --git a/Tests/ArgumentParserEndToEndTests/SubcommandEndToEndTests.swift b/Tests/ArgumentParserEndToEndTests/SubcommandEndToEndTests.swift index 856ec6e93..e47897707 100644 --- a/Tests/ArgumentParserEndToEndTests/SubcommandEndToEndTests.swift +++ b/Tests/ArgumentParserEndToEndTests/SubcommandEndToEndTests.swift @@ -20,7 +20,7 @@ final class SubcommandEndToEndTests: XCTestCase { fileprivate struct Foo: ParsableCommand { static let configuration = - CommandConfiguration(subcommands: [CommandA.self, CommandB.self]) + CommandConfiguration(commandName: "foo", subcommands: [CommandA.self, CommandB.self]) @Option() var name: String } diff --git a/Tests/ArgumentParserEndToEndTests/TransformEndToEndTests.swift b/Tests/ArgumentParserEndToEndTests/TransformEndToEndTests.swift index 957eae5fe..1ef6fb080 100644 --- a/Tests/ArgumentParserEndToEndTests/TransformEndToEndTests.swift +++ b/Tests/ArgumentParserEndToEndTests/TransformEndToEndTests.swift @@ -37,8 +37,8 @@ extension Convert { fileprivate struct FooOption: Convert, ParsableArguments { static var usageString: String = """ - Usage: foo_option --string - See 'foo_option --help' for more information. + Usage: \(getFirstArgument()) --string + See '\(getFirstArgument()) --help' for more information. """ static var help: String = "Help: --string Convert string to integer\n" @@ -48,7 +48,8 @@ fileprivate struct FooOption: Convert, ParsableArguments { } fileprivate struct BarOption: Convert, ParsableCommand { - + static var configuration = CommandConfiguration(commandName: "bar-option") + static var usageString: String = """ Usage: bar-option [--strings ...] See 'bar-option --help' for more information. @@ -100,8 +101,8 @@ extension TransformEndToEndTests { fileprivate struct FooArgument: Convert, ParsableArguments { static var usageString: String = """ - Usage: foo_argument - See 'foo_argument --help' for more information. + Usage: \(getFirstArgument()) + See '\(getFirstArgument()) --help' for more information. """ static var help: String = "Help: Convert string to integer\n" @@ -115,6 +116,7 @@ fileprivate struct FooArgument: Convert, ParsableArguments { } fileprivate struct BarArgument: Convert, ParsableCommand { + static var configuration = CommandConfiguration(commandName: "bar-argument") static var usageString: String = """ Usage: bar-argument [ ...] diff --git a/Tests/ArgumentParserEndToEndTests/ValidationEndToEndTests.swift b/Tests/ArgumentParserEndToEndTests/ValidationEndToEndTests.swift index b4222bc3c..c5f228176 100644 --- a/Tests/ArgumentParserEndToEndTests/ValidationEndToEndTests.swift +++ b/Tests/ArgumentParserEndToEndTests/ValidationEndToEndTests.swift @@ -29,12 +29,12 @@ fileprivate enum UserValidationError: LocalizedError { fileprivate struct Foo: ParsableArguments { static var usageString: String = """ - Usage: foo [--count ] [ ...] [--version] [--throw] - See 'foo --help' for more information. + Usage: \(getFirstArgument()) [--count ] [ ...] [--version] [--throw] + See '\(getFirstArgument()) --help' for more information. """ static var helpString: String = """ - USAGE: foo [--count ] [ ...] [--version] [--throw] + USAGE: \(getFirstArgument()) [--count ] [ ...] [--version] [--throw] ARGUMENTS: diff --git a/Tests/ArgumentParserPackageManagerTests/HelpTests.swift b/Tests/ArgumentParserPackageManagerTests/HelpTests.swift index cb67aadb7..07ace1eb5 100644 --- a/Tests/ArgumentParserPackageManagerTests/HelpTests.swift +++ b/Tests/ArgumentParserPackageManagerTests/HelpTests.swift @@ -169,7 +169,7 @@ struct Simple: ParsableArguments { @Argument() var max: Int static var helpText = """ - USAGE: simple [--verbose] [--min ] + USAGE: \(getFirstArgument()) [--verbose] [--min ] ARGUMENTS: @@ -195,6 +195,7 @@ extension HelpTests { struct CustomHelp: ParsableCommand { static let configuration = CommandConfiguration( + commandName: "custom-help", helpNames: [.customShort("?"), .customLong("show-help")] ) } @@ -216,6 +217,7 @@ extension HelpTests { struct NoHelp: ParsableCommand { static let configuration = CommandConfiguration( + commandName: "no-help", helpNames: [] ) diff --git a/Tests/ArgumentParserPackageManagerTests/PackageManager/Options.swift b/Tests/ArgumentParserPackageManagerTests/PackageManager/Options.swift index a14ac3ed6..686c4101c 100644 --- a/Tests/ArgumentParserPackageManagerTests/PackageManager/Options.swift +++ b/Tests/ArgumentParserPackageManagerTests/PackageManager/Options.swift @@ -89,6 +89,7 @@ struct Options: ParsableArguments { struct Package: ParsableCommand { static let configuration = CommandConfiguration( + commandName: "package", subcommands: [Clean.self, Config.self, Describe.self, GenerateXcodeProject.self, Hidden.self]) } diff --git a/Tests/ArgumentParserUnitTests/HelpGenerationTests+AtArgument.swift b/Tests/ArgumentParserUnitTests/HelpGenerationTests+AtArgument.swift index fbda52028..bed1be9ed 100644 --- a/Tests/ArgumentParserUnitTests/HelpGenerationTests+AtArgument.swift +++ b/Tests/ArgumentParserUnitTests/HelpGenerationTests+AtArgument.swift @@ -22,41 +22,57 @@ extension HelpGenerationTests { struct A { } struct BareNoDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "bare-no-default") + @Argument(help: "example", transform: { _ in A() }) var arg0: A } struct BareDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "bare-default") + @Argument(help: "example", transform: { _ in A() }) var arg0: A = A() } struct OptionalNoDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "optional-no-default") + @Argument(help: "example", transform: { _ in A() }) var arg0: A? } struct OptionalDefaultNil: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "optional-default-nil") + @Argument(help: "example", transform: { _ in A() }) var arg0: A? = nil } struct OptionalDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "optional-default") + @Argument(help: "example", transform: { _ in A() }) var arg0: A? = A() } struct ArrayNoDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "array-no-default") + @Argument(help: "example", transform: { _ in A() }) var arg0: [A] } struct ArrayDefaultEmpty: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "array-default-empty") + @Argument(help: "example", transform: { _ in A() }) var arg0: [A] = [] } struct ArrayDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "array-default") + @Argument(help: "example", transform: { _ in A() }) var arg0: [A] = [A()] } @@ -202,42 +218,58 @@ extension HelpGenerationTests { } struct BareNoDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "bare-no-default") + @Argument(help: "example") var arg0: A } struct BareDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "bare-default") + @Argument(help: "example") var arg0: A = A() } struct OptionalNoDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "optional-no-default") + @Argument(help: "example") var arg0: A? } struct OptionalDefaultNil: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "optional-default-nil") + @Argument(help: "example") var arg0: A? = nil } @available(*, deprecated, message: "Included for test coverage") struct OptionalDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "optional-default") + @Argument(help: "example") var arg0: A? = A() } struct ArrayNoDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "array-no-default") + @Argument(help: "example") var arg0: [A] } struct ArrayDefaultEmpty: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "array-default-empty") + @Argument(help: "example") var arg0: [A] = [] } struct ArrayDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "array-default") + @Argument(help: "example") var arg0: [A] = [A()] } @@ -367,41 +399,57 @@ extension HelpGenerationTests { } struct BareNoDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "bare-no-default") + @Argument(help: "example", transform: { _ in A() }) var arg0: A } struct BareDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "bare-default") + @Argument(help: "example", transform: { _ in A() }) var arg0: A = A() } struct OptionalNoDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "optional-no-default") + @Argument(help: "example", transform: { _ in A() }) var arg0: A? } struct OptionalDefaultNil: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "optional-default-nil") + @Argument(help: "example", transform: { _ in A() }) var arg0: A? = nil } struct OptionalDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "optional-default") + @Argument(help: "example", transform: { _ in A() }) var arg0: A? = A() } struct ArrayNoDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "array-no-default") + @Argument(help: "example", transform: { _ in A() }) var arg0: [A] } struct ArrayDefaultEmpty: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "array-default-empty") + @Argument(help: "example", transform: { _ in A() }) var arg0: [A] = [] } struct ArrayDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "array-default") + @Argument(help: "example", transform: { _ in A() }) var arg0: [A] = [A()] } diff --git a/Tests/ArgumentParserUnitTests/HelpGenerationTests+AtOption.swift b/Tests/ArgumentParserUnitTests/HelpGenerationTests+AtOption.swift index ad008ae9f..dc2bc7219 100644 --- a/Tests/ArgumentParserUnitTests/HelpGenerationTests+AtOption.swift +++ b/Tests/ArgumentParserUnitTests/HelpGenerationTests+AtOption.swift @@ -22,41 +22,57 @@ extension HelpGenerationTests { struct A { } struct BareNoDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "bare-no-default") + @Option(help: "example", transform: { _ in A() }) var arg0: A } struct BareDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "bare-default") + @Option(help: "example", transform: { _ in A() }) var arg0: A = A() } struct OptionalNoDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "optional-no-default") + @Option(help: "example", transform: { _ in A() }) var arg0: A? } struct OptionalDefaultNil: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "optional-default-nil") + @Option(help: "example", transform: { _ in A() }) var arg0: A? = nil } struct OptionalDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "optional-default") + @Option(help: "example", transform: { _ in A() }) var arg0: A? = A() } struct ArrayNoDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "array-no-default") + @Option(help: "example", transform: { _ in A() }) var arg0: [A] } struct ArrayDefaultEmpty: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "array-default-empty") + @Option(help: "example", transform: { _ in A() }) var arg0: [A] = [] } struct ArrayDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "array-default") + @Option(help: "example", transform: { _ in A() }) var arg0: [A] = [A()] } @@ -162,42 +178,58 @@ extension HelpGenerationTests { } struct BareNoDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "bare-no-default") + @Option(help: "example") var arg0: A } struct BareDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "bare-default") + @Option(help: "example") var arg0: A = A() } struct OptionalNoDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "optional-no-default") + @Option(help: "example") var arg0: A? } struct OptionalDefaultNil: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "optional-default-nil") + @Option(help: "example") var arg0: A? = nil } @available(*, deprecated, message: "Included for test coverage") struct OptionalDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "optional-default") + @Option(help: "example") var arg0: A? = A() } struct ArrayNoDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "array-no-default") + @Option(help: "example") var arg0: [A] } struct ArrayDefaultEmpty: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "array-default-empty") + @Option(help: "example") var arg0: [A] = [] } struct ArrayDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "array-default") + @Option(help: "example") var arg0: [A] = [A()] } @@ -292,41 +324,57 @@ extension HelpGenerationTests { } struct BareNoDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "bare-no-default") + @Option(help: "example", transform: { _ in A() }) var arg0: A } struct BareDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "bare-default") + @Option(help: "example", transform: { _ in A() }) var arg0: A = A() } struct OptionalNoDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "optional-no-default") + @Option(help: "example", transform: { _ in A() }) var arg0: A? } struct OptionalDefaultNil: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "optional-default-nil") + @Option(help: "example", transform: { _ in A() }) var arg0: A? = nil } struct OptionalDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "optional-default") + @Option(help: "example", transform: { _ in A() }) var arg0: A? = A() } struct ArrayNoDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "array-no-default") + @Option(help: "example", transform: { _ in A() }) var arg0: [A] } struct ArrayDefaultEmpty: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "array-default-empty") + @Option(help: "example", transform: { _ in A() }) var arg0: [A] = [] } struct ArrayDefault: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "array-default") + @Option(help: "example", transform: { _ in A() }) var arg0: [A] = [A()] } diff --git a/Tests/ArgumentParserUnitTests/HelpGenerationTests+GroupName.swift b/Tests/ArgumentParserUnitTests/HelpGenerationTests+GroupName.swift index 62492057f..627bcda72 100644 --- a/Tests/ArgumentParserUnitTests/HelpGenerationTests+GroupName.swift +++ b/Tests/ArgumentParserUnitTests/HelpGenerationTests+GroupName.swift @@ -53,6 +53,8 @@ extension HelpGenerationTests { } fileprivate struct AllVisible: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "all-visible") + @OptionGroup(title: "Flags Group") var flags: Flags @@ -67,6 +69,8 @@ extension HelpGenerationTests { } fileprivate struct ContainsOptionGroup: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "all-visible") + @OptionGroup(title: "Flags Group") var flags: Flags @@ -122,6 +126,8 @@ extension HelpGenerationTests { } fileprivate struct Combined: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "combined") + @OptionGroup(title: "Extras") var flags: Flags @@ -179,6 +185,8 @@ extension HelpGenerationTests { } fileprivate struct HiddenGroups: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "hidden-groups") + @OptionGroup(title: "Flags Group", visibility: .hidden) var flags: Flags @@ -222,7 +230,7 @@ extension HelpGenerationTests { fileprivate struct ParentWithGroups: ParsableCommand { static var configuration: CommandConfiguration { - .init(subcommands: [ChildWithGroups.self]) + .init(commandName: "parent-with-groups", subcommands: [ChildWithGroups.self]) } @OptionGroup(title: "Extras") @@ -328,6 +336,8 @@ extension HelpGenerationTests { } fileprivate struct GroupsWithUnnamedGroups: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "groups-with-unnamed-groups") + @OptionGroup var extras: ContainsOptionGroup } @@ -351,6 +361,8 @@ extension HelpGenerationTests { } fileprivate struct GroupsWithNamedGroups: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "groups-with-named-groups") + @OptionGroup(title: "Nested") var extras: ContainsOptionGroup } diff --git a/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift b/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift index 047eae80c..d83cc4470 100644 --- a/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift +++ b/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift @@ -41,7 +41,7 @@ extension HelpGenerationTests { func testHelp() { AssertHelp(.default, for: A.self, equals: """ - USAGE: a --name [--title ] + USAGE: \(getFirstArgument()) --name <name> [--title <title>] OPTIONS: --name <name> Your name @@ -63,7 +63,7 @@ extension HelpGenerationTests { func testHelpWithHidden() { AssertHelp(.default, for: B.self, equals: """ - USAGE: b --name <name> [--title <title>] + USAGE: \(getFirstArgument()) --name <name> [--title <title>] OPTIONS: --name <name> Your name @@ -73,7 +73,7 @@ extension HelpGenerationTests { """) AssertHelp(.hidden, for: B.self, equals: """ - USAGE: b --name <name> [--title <title>] [<hidden-name>] [--hidden-title <hidden-title>] [--hidden-flag] [--hidden-inverted-flag] [--no-hidden-inverted-flag] + USAGE: \(getFirstArgument()) --name <name> [--title <title>] [<hidden-name>] [--hidden-title <hidden-title>] [--hidden-flag] [--hidden-inverted-flag] [--no-hidden-inverted-flag] ARGUMENTS: <hidden-name> @@ -98,7 +98,7 @@ extension HelpGenerationTests { func testHelpWithDiscussion() { AssertHelp(.default, for: C.self, equals: """ - USAGE: c --name <name> + USAGE: \(getFirstArgument()) --name <name> OPTIONS: --name <name> Your name. @@ -121,7 +121,7 @@ extension HelpGenerationTests { func testHelpWithDefaultValueButNoDiscussion() { AssertHelp(.default, for: Issue27.self, equals: """ - USAGE: issue27 [--two <two>] --three <three> [--four <four>] [--five <five>] + USAGE: \(getFirstArgument()) [--two <two>] --three <three> [--four <four>] [--five <five>] OPTIONS: --two <two> (default: 42) @@ -151,6 +151,8 @@ extension HelpGenerationTests { } struct D: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "d") + @Argument(help: "Your occupation.") var occupation: String = "--" @@ -220,6 +222,8 @@ extension HelpGenerationTests { } struct E: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "e") + enum OutputBehaviour: String, EnumerableFlag { case stats, count, list @@ -233,6 +237,8 @@ extension HelpGenerationTests { } struct F: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "f") + enum OutputBehaviour: String, EnumerableFlag { case stats, count, list @@ -246,6 +252,8 @@ extension HelpGenerationTests { } struct G: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "g") + @Flag(inversion: .prefixedNo, help: "Whether to flag") var flag: Bool = false } @@ -304,7 +312,7 @@ extension HelpGenerationTests { @Argument var argument: String = "" } - static let configuration = CommandConfiguration(subcommands: [CommandWithVeryLongName.self,ShortCommand.self,AnotherCommandWithVeryLongName.self,AnotherCommand.self]) + static let configuration = CommandConfiguration(commandName: "h", subcommands: [CommandWithVeryLongName.self,ShortCommand.self,AnotherCommandWithVeryLongName.self,AnotherCommand.self]) } func testHelpWithSubcommands() { @@ -342,7 +350,7 @@ extension HelpGenerationTests { } struct I: ParsableCommand { - static let configuration = CommandConfiguration(version: "1.0.0") + static let configuration = CommandConfiguration(commandName: "i", version: "1.0.0") } func testHelpWithVersion() { @@ -358,7 +366,7 @@ extension HelpGenerationTests { } struct J: ParsableCommand { - static let configuration = CommandConfiguration(discussion: "test") + static let configuration = CommandConfiguration(commandName: "j", discussion: "test") } func testOverviewButNoAbstractSpacing() { @@ -377,6 +385,8 @@ extension HelpGenerationTests { } struct K: ParsableCommand { + static let configuration = CommandConfiguration(commandName: "k") + @Argument(help: "A list of paths.") var paths: [String] = [] @@ -409,7 +419,7 @@ extension HelpGenerationTests { func testHelpWithMultipleCustomNames() { AssertHelp(.default, for: L.self, equals: """ - USAGE: l [--remote <remote>] + USAGE: \(getFirstArgument()) [--remote <remote>] OPTIONS: -t, -x, -y, --remote, --when, --time, -other, --there <remote> @@ -422,7 +432,7 @@ extension HelpGenerationTests { struct M: ParsableCommand { } struct N: ParsableCommand { - static let configuration = CommandConfiguration(subcommands: [M.self], defaultSubcommand: M.self) + static let configuration = CommandConfiguration(commandName: "n", subcommands: [M.self], defaultSubcommand: M.self) } func testHelpWithDefaultCommand() { @@ -461,7 +471,7 @@ extension HelpGenerationTests { func testHelpWithDefaultValueForArray() { AssertHelp(.default, for: P.self, equals: """ - USAGE: p [-o <o> ...] [<remainder> ...] + USAGE: \(getFirstArgument()) [-o <o> ...] [<remainder> ...] ARGUMENTS: <remainder> Help Message (default: large) @@ -662,7 +672,7 @@ extension HelpGenerationTests { func testHelpWithPrivate() { AssertHelp(.default, for: Q.self, equals: """ - USAGE: q --name <name> [--title <title>] + USAGE: \(getFirstArgument()) --name <name> [--title <title>] OPTIONS: --name <name> Your name @@ -718,6 +728,7 @@ extension HelpGenerationTests { } static let configuration = CommandConfiguration( + commandName: "non-custom-usage", subcommands: [ExampleSubcommand.self]) @Argument var file: String @@ -726,6 +737,7 @@ extension HelpGenerationTests { struct CustomUsageShort: ParsableCommand { static let configuration = CommandConfiguration( + commandName: "custom-usage-short", usage: """ example [--verbose] <file-name> """) @@ -736,6 +748,7 @@ extension HelpGenerationTests { struct CustomUsageLong: ParsableCommand { static let configuration = CommandConfiguration( + commandName: "custom-usage-long", usage: """ example <file-name> example --verbose <file-name> @@ -747,7 +760,7 @@ extension HelpGenerationTests { } struct CustomUsageHidden: ParsableCommand { - static let configuration = CommandConfiguration(usage: "") + static let configuration = CommandConfiguration(commandName: "custom-usage-hidden", usage: "") @Argument var file: String @Flag var verboseMode = false @@ -897,10 +910,82 @@ extension HelpGenerationTests { expected: """ """) } + + struct FunSubCommand: ParsableCommand {} + + struct NoCommandName: ParsableCommand { + @Flag(inversion: .prefixedNo, help: "Whether to flag") + var flag: Bool = false + } + + struct WithCommandName: ParsableCommand { + static var configuration = CommandConfiguration(commandName: "with-command-name") + + @Flag(inversion: .prefixedNo, help: "Whether to flag") + var flag: Bool = false + } + + struct NoCommandNameWithSubCommands: ParsableCommand { + static var configuration = CommandConfiguration(subcommands: [FunSubCommand.self]) + + @Flag(inversion: .prefixedNo, help: "Whether to flag") + var flag: Bool = false + } + + struct WithCommandNameWithSubCommand: ParsableCommand { + static var configuration = CommandConfiguration(commandName: "with-command-name", subcommands: [FunSubCommand.self]) + + @Flag(inversion: .prefixedNo, help: "Whether to flag") + var flag: Bool = false + } + + func testParsableCommandWithNoCommandName() { + AssertHelp(.default, for: NoCommandName.self, equals: """ + USAGE: \(getFirstArgument()) [--flag] [--no-flag] + + OPTIONS: + --flag/--no-flag Whether to flag (default: --no-flag) + -h, --help Show help information. + + """) + } + + func testParsableCommandWithCommandName() { + AssertHelp(.default, for: WithCommandName.self, equals: """ + USAGE: with-command-name [--flag] [--no-flag] + + OPTIONS: + --flag/--no-flag Whether to flag (default: --no-flag) + -h, --help Show help information. + + """) + } + + func testParsableCommandWithNoCommandNameAndSubCommand() { + AssertHelp(.default, for: FunSubCommand.self, root: NoCommandNameWithSubCommands.self, equals: """ + USAGE: \(getFirstArgument()) fun-sub-command + + OPTIONS: + -h, --help Show help information. + + """) + } + + func testParsableCommandWithCommandNameAndSubCommand() { + AssertHelp(.default, for: FunSubCommand.self, root: WithCommandNameWithSubCommand.self, equals: """ + USAGE: with-command-name fun-sub-command + + OPTIONS: + -h, --help Show help information. + + """) + } } extension HelpGenerationTests { private struct WideHelp: ParsableCommand { + static var configuration = CommandConfiguration(commandName: "wide-help") + @Argument(help: "54 characters of help, so as to wrap when columns < 80") var argument: String? }