Skip to content

Commit d664ed7

Browse files
committed
Merge branch 'main' into async
2 parents eee7edc + b547374 commit d664ed7

File tree

10 files changed

+116
-10
lines changed

10 files changed

+116
-10
lines changed

Sources/ArgumentParser/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ add_library(ArgumentParser
3434

3535
Usage/DumpHelpGenerator.swift
3636
Usage/HelpCommand.swift
37+
Usage/HelpHiddenCommand.swift
3738
Usage/HelpGenerator.swift
3839
Usage/MessageInfo.swift
3940
Usage/UsageGenerator.swift

Sources/ArgumentParser/Parsable Types/ParsableArguments.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ extension ParsableArguments {
9797
switch try self.asCommand.parseAsRoot(arguments) {
9898
case is HelpCommand:
9999
throw ParserError.helpRequested
100+
case is HelpHiddenCommand:
101+
throw ParserError.helpHiddenRequested
100102
case let result as _WrappedParsableCommand<Self>:
101103
return result.options
102104
case var result as Self:
@@ -245,7 +247,7 @@ extension ArgumentSetProvider {
245247
}
246248

247249
extension ArgumentSet {
248-
init(_ type: ParsableArguments.Type, creatingHelp: Bool = false) {
250+
init(_ type: ParsableArguments.Type, creatingHelp: Bool = false, includeHidden: Bool = false) {
249251

250252
#if DEBUG
251253
do {
@@ -261,7 +263,7 @@ extension ArgumentSet {
261263
guard var codingKey = child.label else { return nil }
262264

263265
if let parsed = child.value as? ArgumentSetProvider {
264-
if creatingHelp {
266+
if creatingHelp && !includeHidden {
265267
guard !parsed._hiddenFromHelp else { return nil }
266268
}
267269

Sources/ArgumentParser/Parsing/CommandParser.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ struct CommandError: Error {
1515
}
1616

1717
struct HelpRequested: Error {}
18+
private struct HelpHiddenRequested: Error {}
1819

1920
struct CommandParser {
2021
let commandTree: Tree<ParsableCommand.Type>
@@ -74,11 +75,15 @@ extension CommandParser {
7475
/// Throws a `HelpRequested` error if the user has specified either of the
7576
/// built in help flags.
7677
func checkForBuiltInFlags(_ split: SplitArguments) throws {
78+
guard !split.contains(Name.long("experimental-help-hidden")) else {
79+
throw HelpHiddenRequested()
80+
}
81+
7782
// Look for help flags
7883
guard !split.contains(anyOf: self.commandStack.getHelpNames()) else {
7984
throw HelpRequested()
8085
}
81-
86+
8287
// Look for the "dump help" request
8388
guard !split.contains(Name.long("experimental-dump-help")) else {
8489
throw CommandError(commandStack: commandStack, parserError: .dumpHelpRequested)
@@ -228,6 +233,10 @@ extension CommandParser {
228233
try helpResult.buildCommandStack(with: self)
229234
return .success(helpResult)
230235
}
236+
if var helpResult = result as? HelpHiddenCommand {
237+
try helpResult.buildCommandStack(with: self)
238+
return .success(helpResult)
239+
}
231240
return .success(result)
232241
} catch let error as CommandError {
233242
return .failure(error)
@@ -236,6 +245,8 @@ extension CommandParser {
236245
return .failure(CommandError(commandStack: commandStack, parserError: error))
237246
} catch is HelpRequested {
238247
return .success(HelpCommand(commandStack: commandStack))
248+
} catch is HelpHiddenRequested {
249+
return .success(HelpHiddenCommand(commandStack: commandStack))
239250
} catch {
240251
return .failure(CommandError(commandStack: commandStack, parserError: .invalidState))
241252
}

Sources/ArgumentParser/Parsing/ParserError.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
/// Gets thrown while parsing and will be handled by the error output generation.
1313
enum ParserError: Error {
1414
case helpRequested
15+
case helpHiddenRequested
1516
case versionRequested
1617
case dumpHelpRequested
1718

Sources/ArgumentParser/Usage/HelpGenerator.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ internal struct HelpGenerator {
8989
var sections: [Section]
9090
var discussionSections: [DiscussionSection]
9191

92-
init(commandStack: [ParsableCommand.Type]) {
92+
init(commandStack: [ParsableCommand.Type], includeHidden: Bool = false) {
9393
guard let currentCommand = commandStack.last else {
9494
fatalError()
9595
}
@@ -122,23 +122,23 @@ internal struct HelpGenerator {
122122
self.abstract += "\n\(currentCommand.configuration.discussion)"
123123
}
124124

125-
self.sections = HelpGenerator.generateSections(commandStack: commandStack)
125+
self.sections = HelpGenerator.generateSections(commandStack: commandStack, includeHidden: includeHidden)
126126
self.discussionSections = []
127127
}
128128

129129
init(_ type: ParsableArguments.Type) {
130130
self.init(commandStack: [type.asCommand])
131131
}
132132

133-
static func generateSections(commandStack: [ParsableCommand.Type]) -> [Section] {
133+
private static func generateSections(commandStack: [ParsableCommand.Type], includeHidden: Bool) -> [Section] {
134134
guard !commandStack.isEmpty else { return [] }
135135

136136
var positionalElements: [Section.Element] = []
137137
var optionElements: [Section.Element] = []
138138

139139
/// Start with a full slice of the ArgumentSet so we can peel off one or
140140
/// more elements at a time.
141-
var args = commandStack.argumentsForHelp()[...]
141+
var args = commandStack.argumentsForHelp(includeHidden: includeHidden)[...]
142142

143143
while let arg = args.popFirst() {
144144
guard arg.help.shouldDisplay else { continue }
@@ -316,8 +316,8 @@ internal extension BidirectionalCollection where Element == ParsableCommand.Type
316316

317317
/// Returns the ArgumentSet for the last command in this stack, including
318318
/// help and version flags, when appropriate.
319-
func argumentsForHelp() -> ArgumentSet {
320-
guard var arguments = self.last.map({ ArgumentSet($0, creatingHelp: true) })
319+
func argumentsForHelp(includeHidden: Bool = false) -> ArgumentSet {
320+
guard var arguments = self.last.map({ ArgumentSet($0, creatingHelp: true, includeHidden: includeHidden) })
321321
else { return ArgumentSet() }
322322
self.versionArgumentDefinition().map { arguments.append($0) }
323323
self.helpArgumentDefinition().map { arguments.append($0) }
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//===----------------------------------------------------------*- swift -*-===//
2+
//
3+
// This source file is part of the Swift Argument Parser open source project
4+
//
5+
// Copyright (c) 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
struct HelpHiddenCommand: ParsableCommand {
13+
static var configuration = CommandConfiguration(
14+
commandName: "experimental-help-hidden",
15+
abstract: "Show help information, including hidden options",
16+
helpNames: [])
17+
18+
/// Any subcommand names provided after the `help` subcommand.
19+
@Argument var subcommands: [String] = []
20+
21+
/// Capture and ignore any extra help flags given by the user.
22+
@Flag(name: [.customLong("experimental-help-hidden", withSingleDash: false)], help: .hidden)
23+
var helpHidden = false
24+
25+
private(set) var commandStack: [ParsableCommand.Type] = []
26+
27+
init() {}
28+
29+
mutating func run() throws {
30+
throw CommandError(commandStack: commandStack, parserError: .helpHiddenRequested)
31+
}
32+
33+
mutating func buildCommandStack(with parser: CommandParser) throws {
34+
commandStack = parser.commandStack(for: subcommands)
35+
}
36+
37+
func generateHelp() -> String {
38+
return HelpGenerator(commandStack: commandStack, includeHidden: true).rendered()
39+
}
40+
41+
enum CodingKeys: CodingKey {
42+
case subcommands
43+
case helpHidden
44+
}
45+
46+
init(from decoder: Decoder) throws {
47+
let container = try decoder.container(keyedBy: CodingKeys.self)
48+
self.subcommands = try container.decode([String].self, forKey: .subcommands)
49+
self.helpHidden = try container.decode(Bool.self, forKey: .helpHidden)
50+
}
51+
52+
init(commandStack: [ParsableCommand.Type]) {
53+
self.commandStack = commandStack
54+
self.subcommands = commandStack.map { $0._commandName }
55+
self.helpHidden = false
56+
}
57+
}

Sources/ArgumentParser/Usage/MessageInfo.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ enum MessageInfo {
3131
self = .help(text: HelpGenerator(commandStack: e.commandStack).rendered())
3232
return
3333

34+
case .helpHiddenRequested:
35+
self = .help(text: HelpGenerator(commandStack: e.commandStack, includeHidden: true).rendered())
36+
return
3437

3538
case .dumpHelpRequested:
3639
self = .help(text: DumpHelpGenerator(commandStack: e.commandStack).rendered())

Sources/ArgumentParser/Usage/UsageGenerator.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ struct ErrorMessageGenerator {
161161
extension ErrorMessageGenerator {
162162
func makeErrorMessage() -> String? {
163163
switch error {
164-
case .helpRequested, .versionRequested, .completionScriptRequested, .completionScriptCustomResponse, .dumpHelpRequested:
164+
case .helpRequested, .helpHiddenRequested, .versionRequested, .completionScriptRequested, .completionScriptCustomResponse, .dumpHelpRequested:
165165
return nil
166166

167167
case .unsupportedShell(let shell?):

Sources/ArgumentParserTestHelpers/TestHelpers.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,20 @@ public func AssertHelp<T: ParsableCommand, U: ParsableCommand>(
139139
helpString, expected, file: file, line: line)
140140
}
141141

142+
public func AssertHelpHidden<T: ParsableArguments>(
143+
for _: T.Type, equals expected: String,
144+
file: StaticString = #file, line: UInt = #line
145+
) {
146+
do {
147+
_ = try T.parse(["--experimental-help-hidden"])
148+
XCTFail(file: (file), line: line)
149+
} catch {
150+
let helpString = T.fullMessage(for: error)
151+
AssertEqualStringsIgnoringTrailingWhitespace(
152+
helpString, expected, file: file, line: line)
153+
}
154+
}
155+
142156
public func AssertDump<T: ParsableArguments>(
143157
for _: T.Type, equals expected: String,
144158
file: StaticString = #file, line: UInt = #line

Tests/ArgumentParserUnitTests/HelpGenerationTests.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,23 @@ extension HelpGenerationTests {
506506
)
507507
}
508508

509+
func testHelpHiddenShowsAll() throws {
510+
AssertHelpHidden(for: HideDriver.self, equals: """
511+
OVERVIEW: Demo hiding option groups
512+
513+
USAGE: driver [--verbose] [--custom-name <custom-name>] [--timeout <timeout>]
514+
515+
OPTIONS:
516+
--verbose Verbose
517+
--custom-name <custom-name>
518+
Custom Name
519+
--timeout <timeout> Time to wait before timeout (in seconds)
520+
-h, --help Show help information.
521+
522+
"""
523+
)
524+
}
525+
509526
struct AllValues: ParsableCommand {
510527
enum Manual: Int, ExpressibleByArgument {
511528
case foo

0 commit comments

Comments
 (0)