Skip to content

Commit f314199

Browse files
authored
Exclude supercommands from help (#300)
* Ability to exclude super commands from --help
1 parent cdb0e71 commit f314199

File tree

3 files changed

+97
-53
lines changed

3 files changed

+97
-53
lines changed

Sources/ArgumentParser/Parsable Types/ParsableCommand.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ extension ParsableCommand {
4040
public static var configuration: CommandConfiguration {
4141
CommandConfiguration()
4242
}
43-
43+
4444
public mutating func run() throws {
4545
throw CleanExit.helpRequest(self)
4646
}

Sources/ArgumentParser/Usage/HelpGenerator.swift

Lines changed: 54 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -144,63 +144,65 @@ internal struct HelpGenerator {
144144
var optionElements: [Section.Element] = []
145145
/// Used to keep track of elements already seen from parent commands.
146146
var alreadySeenElements = Set<Section.Element>()
147-
148-
for commandType in commandStack {
149-
let args = Array(ArgumentSet(commandType))
147+
148+
guard let commandType = commandStack.last else {
149+
return []
150+
}
151+
152+
let args = Array(ArgumentSet(commandType))
153+
154+
var i = 0
155+
while i < args.count {
156+
defer { i += 1 }
157+
let arg = args[i]
150158

151-
var i = 0
152-
while i < args.count {
153-
defer { i += 1 }
154-
let arg = args[i]
155-
156-
guard arg.help.help?.shouldDisplay != false else { continue }
157-
158-
let synopsis: String
159-
let description: String
160-
161-
if args[i].help.isComposite {
162-
// If this argument is composite, we have a group of arguments to
163-
// output together.
164-
var groupedArgs = [arg]
165-
let defaultValue = arg.help.defaultValue.map { "(default: \($0))" } ?? ""
166-
while i < args.count - 1 && args[i + 1].help.keys == arg.help.keys {
167-
groupedArgs.append(args[i + 1])
168-
i += 1
169-
}
159+
guard arg.help.help?.shouldDisplay != false else { continue }
160+
161+
let synopsis: String
162+
let description: String
163+
164+
if args[i].help.isComposite {
165+
// If this argument is composite, we have a group of arguments to
166+
// output together.
167+
var groupedArgs = [arg]
168+
let defaultValue = arg.help.defaultValue.map { "(default: \($0))" } ?? ""
169+
while i < args.count - 1 && args[i + 1].help.keys == arg.help.keys {
170+
groupedArgs.append(args[i + 1])
171+
i += 1
172+
}
170173

171-
var synopsisString = ""
172-
for arg in groupedArgs {
173-
if !synopsisString.isEmpty { synopsisString.append("/") }
174-
synopsisString.append("\(arg.synopsisForHelp ?? "")")
175-
}
176-
synopsis = synopsisString
174+
var synopsisString = ""
175+
for arg in groupedArgs {
176+
if !synopsisString.isEmpty { synopsisString.append("/") }
177+
synopsisString.append("\(arg.synopsisForHelp ?? "")")
178+
}
179+
synopsis = synopsisString
177180

178-
var descriptionString: String?
179-
for arg in groupedArgs {
180-
if let desc = arg.help.help?.abstract {
181-
descriptionString = desc
182-
break
183-
}
181+
var descriptionString: String?
182+
for arg in groupedArgs {
183+
if let desc = arg.help.help?.abstract {
184+
descriptionString = desc
185+
break
184186
}
185-
description = [descriptionString, defaultValue]
186-
.compactMap { $0 }
187-
.joined(separator: " ")
188-
} else {
189-
let defaultValue = arg.help.defaultValue.flatMap { $0.isEmpty ? nil : "(default: \($0))" } ?? ""
190-
synopsis = arg.synopsisForHelp ?? ""
191-
description = [arg.help.help?.abstract, defaultValue]
192-
.compactMap { $0 }
193-
.joined(separator: " ")
194187
}
195-
196-
let element = Section.Element(label: synopsis, abstract: description, discussion: arg.help.help?.discussion ?? "")
197-
if !alreadySeenElements.contains(element) {
198-
alreadySeenElements.insert(element)
199-
if case .positional = arg.kind {
200-
positionalElements.append(element)
201-
} else {
202-
optionElements.append(element)
203-
}
188+
description = [descriptionString, defaultValue]
189+
.compactMap { $0 }
190+
.joined(separator: " ")
191+
} else {
192+
let defaultValue = arg.help.defaultValue.flatMap { $0.isEmpty ? nil : "(default: \($0))" } ?? ""
193+
synopsis = arg.synopsisForHelp ?? ""
194+
description = [arg.help.help?.abstract, defaultValue]
195+
.compactMap { $0 }
196+
.joined(separator: " ")
197+
}
198+
199+
let element = Section.Element(label: synopsis, abstract: description, discussion: arg.help.help?.discussion ?? "")
200+
if !alreadySeenElements.contains(element) {
201+
alreadySeenElements.insert(element)
202+
if case .positional = arg.kind {
203+
positionalElements.append(element)
204+
} else {
205+
optionElements.append(element)
204206
}
205207
}
206208
}

Tests/ArgumentParserUnitTests/HelpGenerationTests.swift

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,4 +431,46 @@ extension HelpGenerationTests {
431431
432432
""")
433433
}
434+
435+
struct Foo: ParsableCommand {
436+
public static var configuration = CommandConfiguration(
437+
commandName: "foo",
438+
abstract: "Perform some foo",
439+
subcommands: [
440+
Bar.self
441+
],
442+
helpNames: [.short, .long, .customLong("help", withSingleDash: true)])
443+
444+
@Option(help: "Name for foo")
445+
var fooName: String?
446+
447+
public init() {}
448+
}
449+
450+
struct Bar: ParsableCommand {
451+
static let configuration = CommandConfiguration(
452+
commandName: "bar",
453+
_superCommandName: "foo",
454+
abstract: "Perform bar operations",
455+
helpNames: [.short, .long, .customLong("help", withSingleDash: true)])
456+
457+
@Option(help: "Bar Strength")
458+
var barStrength: String?
459+
460+
public init() {}
461+
}
462+
463+
func testHelpExcludingSuperCommand() throws {
464+
AssertHelp(for: Bar.self, root: Foo.self, equals: """
465+
OVERVIEW: Perform bar operations
466+
467+
USAGE: foo bar [--bar-strength <bar-strength>]
468+
469+
OPTIONS:
470+
--bar-strength <bar-strength>
471+
Bar Strength
472+
-help, -h, --help Show help information.
473+
474+
""")
475+
}
434476
}

0 commit comments

Comments
 (0)