Skip to content

Add abillity to generate hidden help #410

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

### Generating Help Text

- ``helpMessage(columns:)``
- ``helpMessage(includeHidden:columns:)``

### Handling Errors

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

### Generating Help Text

- ``helpMessage(for:columns:)``
- ``helpMessage(for:includeHidden:columns:)``

### Starting the Program

Expand Down
36 changes: 30 additions & 6 deletions Sources/ArgumentParser/Parsable Types/ParsableArguments.swift
Original file line number Diff line number Diff line change
Expand Up @@ -132,15 +132,39 @@ extension ParsableArguments {
) -> String {
MessageInfo(error: error, type: self).fullText(for: self)
}

/// Returns the text of the help screen for this type.
///
/// - Parameter columns: The column width to use when wrapping long lines in
/// the help screen. If `columns` is `nil`, uses the current terminal width,
/// or a default value of `80` if the terminal width is not available.
/// - Parameters:
/// - columns: The column width to use when wrapping long line in the
/// help screen. If `columns` is `nil`, uses the current terminal
/// width, or a default value of `80` if the terminal width is not
/// available.
/// - Returns: The full help screen for this type.
public static func helpMessage(columns: Int? = nil) -> String {
HelpGenerator(self).rendered(screenWidth: columns)
@_disfavoredOverload
@available(*, deprecated, message: "Use helpMessage(includeHidden:columns:) instead.")
public static func helpMessage(
columns: Int?
) -> String {
helpMessage(includeHidden: false, columns: columns)
}

/// Returns the text of the help screen for this type.
///
/// - Parameters:
/// - includeHidden: Include hidden help information in the generated
/// message.
/// - columns: The column width to use when wrapping long line in the
/// help screen. If `columns` is `nil`, uses the current terminal
/// width, or a default value of `80` if the terminal width is not
/// available.
/// - Returns: The full help screen for this type.
public static func helpMessage(
includeHidden: Bool = false,
columns: Int? = nil
) -> String {
HelpGenerator(self, includeHidden: includeHidden)
.rendered(screenWidth: columns)
}

/// Returns the JSON representation of this type.
Expand Down
31 changes: 29 additions & 2 deletions Sources/ArgumentParser/Parsable Types/ParsableCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,39 @@ extension ParsableCommand {
/// help screen. If `columns` is `nil`, uses the current terminal
/// width, or a default value of `80` if the terminal width is not
/// available.
/// - Returns: The full help screen for this type.
@_disfavoredOverload
@available(*, deprecated, message: "Use helpMessage(for:includeHidden:columns:) instead.")
public static func helpMessage(
for subcommand: ParsableCommand.Type,
columns: Int? = nil
) -> String {
let stack = CommandParser(self).commandStack(for: subcommand)
return HelpGenerator(commandStack: stack).rendered(screenWidth: columns)
helpMessage(for: subcommand, includeHidden: false, columns: columns)
}

/// Returns the text of the help screen for the given subcommand of this
/// command.
///
/// - Parameters:
/// - subcommand: The subcommand to generate the help screen for.
/// `subcommand` must be declared in the subcommand tree of this
/// command.
/// - includeHidden: Include hidden help information in the generated
/// message.
/// - columns: The column width to use when wrapping long line in the
/// help screen. If `columns` is `nil`, uses the current terminal
/// width, or a default value of `80` if the terminal width is not
/// available.
/// - Returns: The full help screen for this type.
public static func helpMessage(
for subcommand: ParsableCommand.Type,
includeHidden: Bool = false,
columns: Int? = nil
) -> String {
HelpGenerator(
commandStack: CommandParser(self).commandStack(for: subcommand),
includeHidden: includeHidden)
.rendered(screenWidth: columns)
}

/// Parses an instance of this type, or one of its subcommands, from
Expand Down
4 changes: 2 additions & 2 deletions Sources/ArgumentParser/Usage/HelpGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ internal struct HelpGenerator {
self.discussionSections = []
}

init(_ type: ParsableArguments.Type) {
self.init(commandStack: [type.asCommand])
init(_ type: ParsableArguments.Type, includeHidden: Bool = false) {
self.init(commandStack: [type.asCommand], includeHidden: includeHidden)
}

private static func generateSections(commandStack: [ParsableCommand.Type], includeHidden: Bool) -> [Section] {
Expand Down
69 changes: 45 additions & 24 deletions Sources/ArgumentParserTestHelpers/TestHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//
//===----------------------------------------------------------------------===//

import ArgumentParser
@testable import ArgumentParser
import ArgumentParserToolInfo
import XCTest

Expand Down Expand Up @@ -113,44 +113,65 @@ public func AssertEqualStringsIgnoringTrailingWhitespace(_ string1: String, _ st
}

public func AssertHelp<T: ParsableArguments>(
for _: T.Type, equals expected: String,
file: StaticString = #file, line: UInt = #line
_ visibility: ArgumentVisibility,
for _: T.Type,
equals expected: String,
file: StaticString = #file,
line: UInt = #line
) {
let flag: String
let includeHidden: Bool

switch visibility {
case .default:
flag = "--help"
includeHidden = false
case .hidden:
flag = "--help-hidden"
includeHidden = true
case .private:
XCTFail("Should not be called.")
return
}

do {
_ = try T.parse(["-h"])
XCTFail(file: (file), line: line)
_ = try T.parse([flag])
XCTFail(file: file, line: line)
} catch {
let helpString = T.fullMessage(for: error)
AssertEqualStringsIgnoringTrailingWhitespace(
helpString, expected, file: file, line: line)
}
let helpString = T.helpMessage()

let helpString = T.helpMessage(includeHidden: includeHidden, columns: nil)
AssertEqualStringsIgnoringTrailingWhitespace(
helpString, expected, file: file, line: line)
}

public func AssertHelp<T: ParsableCommand, U: ParsableCommand>(
for _: T.Type, root _: U.Type, equals expected: String,
file: StaticString = #file, line: UInt = #line
_ visibility: ArgumentVisibility,
for _: T.Type,
root _: U.Type,
equals expected: String,
file: StaticString = #file,
line: UInt = #line
) {
let helpString = U.helpMessage(for: T.self)
AssertEqualStringsIgnoringTrailingWhitespace(
helpString, expected, file: file, line: line)
}
let includeHidden: Bool

public func AssertHelpHidden<T: ParsableArguments>(
for _: T.Type, equals expected: String,
file: StaticString = #file, line: UInt = #line
) {
do {
_ = try T.parse(["--help-hidden"])
XCTFail(file: (file), line: line)
} catch {
let helpString = T.fullMessage(for: error)
AssertEqualStringsIgnoringTrailingWhitespace(
helpString, expected, file: file, line: line)
switch visibility {
case .default:
includeHidden = false
case .hidden:
includeHidden = true
case .private:
XCTFail("Should not be called.")
return
}

let helpString = U.helpMessage(
for: T.self, includeHidden: includeHidden, columns: nil)
AssertEqualStringsIgnoringTrailingWhitespace(
helpString, expected, file: file, line: line)
}

public func AssertDump<T: ParsableArguments>(
Expand Down
45 changes: 22 additions & 23 deletions Tests/ArgumentParserUnitTests/HelpGenerationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ extension HelpGenerationTests {
}

func testHelp() {
AssertHelp(for: A.self, equals: """
AssertHelp(.default, for: A.self, equals: """
USAGE: a --name <name> [--title <title>]

OPTIONS:
Expand All @@ -62,7 +62,7 @@ extension HelpGenerationTests {
}

func testHelpWithHidden() {
AssertHelp(for: B.self, equals: """
AssertHelp(.default, for: B.self, equals: """
USAGE: b --name <name> [--title <title>]

OPTIONS:
Expand All @@ -80,7 +80,7 @@ extension HelpGenerationTests {
}

func testHelpWithDiscussion() {
AssertHelp(for: C.self, equals: """
AssertHelp(.default, for: C.self, equals: """
USAGE: c --name <name>

OPTIONS:
Expand All @@ -103,7 +103,7 @@ extension HelpGenerationTests {
}

func testHelpWithDefaultValueButNoDiscussion() {
AssertHelp(for: Issue27.self, equals: """
AssertHelp(.default, for: Issue27.self, equals: """
USAGE: issue27 [--two <two>] --three <three> [--four <four>] [--five <five>]

OPTIONS:
Expand Down Expand Up @@ -160,7 +160,7 @@ extension HelpGenerationTests {
}

func testHelpWithDefaultValues() {
AssertHelp(for: D.self, equals: """
AssertHelp(.default, for: D.self, equals: """
USAGE: d [<occupation>] [--name <name>] [--age <age>] [--logging <logging>] [--lucky <numbers> ...] [--optional] [--required] [--degree <degree>] [--directory <directory>]

ARGUMENTS:
Expand Down Expand Up @@ -211,7 +211,7 @@ extension HelpGenerationTests {
}

func testHelpWithMutuallyExclusiveFlags() {
AssertHelp(for: E.self, equals: """
AssertHelp(.default, for: E.self, equals: """
USAGE: e --stats --count --list

OPTIONS:
Expand All @@ -221,7 +221,7 @@ extension HelpGenerationTests {

""")

AssertHelp(for: F.self, equals: """
AssertHelp(.default, for: F.self, equals: """
USAGE: f [-s] [-c] [-l]

OPTIONS:
Expand All @@ -230,7 +230,7 @@ extension HelpGenerationTests {

""")

AssertHelp(for: G.self, equals: """
AssertHelp(.default, for: G.self, equals: """
USAGE: g [--flag] [--no-flag]

OPTIONS:
Expand Down Expand Up @@ -268,7 +268,7 @@ extension HelpGenerationTests {
}

func testHelpWithSubcommands() {
AssertHelp(for: H.self, equals: """
AssertHelp(.default, for: H.self, equals: """
USAGE: h <subcommand>

OPTIONS:
Expand All @@ -284,7 +284,7 @@ extension HelpGenerationTests {
See 'h help <subcommand>' for detailed help.
""")

AssertHelp(for: H.AnotherCommand.self, root: H.self, equals: """
AssertHelp(.default, for: H.AnotherCommand.self, root: H.self, equals: """
USAGE: h another-command [--some-option-with-very-long-name <some-option-with-very-long-name>] [--option <option>] [<argument-with-very-long-name-and-help>] [<argument-with-very-long-name>] [<argument>]

ARGUMENTS:
Expand All @@ -306,7 +306,7 @@ extension HelpGenerationTests {
}

func testHelpWithVersion() {
AssertHelp(for: I.self, equals: """
AssertHelp(.default, for: I.self, equals: """
USAGE: i

OPTIONS:
Expand Down Expand Up @@ -347,7 +347,7 @@ extension HelpGenerationTests {
}

func testHelpWithNoValueForArray() {
AssertHelp(for: K.self, equals: """
AssertHelp(.default, for: K.self, equals: """
USAGE: k [<paths> ...]

ARGUMENTS:
Expand All @@ -367,7 +367,7 @@ extension HelpGenerationTests {
}

func testHelpWithMultipleCustomNames() {
AssertHelp(for: L.self, equals: """
AssertHelp(.default, for: L.self, equals: """
USAGE: l [--remote <remote>]

OPTIONS:
Expand All @@ -385,7 +385,7 @@ extension HelpGenerationTests {
}

func testHelpWithDefaultCommand() {
AssertHelp(for: N.self, equals: """
AssertHelp(.default, for: N.self, equals: """
USAGE: n <subcommand>

OPTIONS:
Expand Down Expand Up @@ -419,7 +419,7 @@ extension HelpGenerationTests {
}

func testHelpWithDefaultValueForArray() {
AssertHelp(for: P.self, equals: """
AssertHelp(.default, for: P.self, equals: """
USAGE: p [-o <o> ...] [<remainder> ...]

ARGUMENTS:
Expand Down Expand Up @@ -461,7 +461,7 @@ extension HelpGenerationTests {
}

func testHelpExcludingSuperCommand() throws {
AssertHelp(for: Bar.self, root: Foo.self, equals: """
AssertHelp(.default, for: Bar.self, root: Foo.self, equals: """
OVERVIEW: Perform bar operations

USAGE: foo bar [--bar-strength <bar-strength>]
Expand Down Expand Up @@ -515,8 +515,8 @@ extension HelpGenerationTests {
-h, --help Show help information.

"""
AssertHelp(for: HideOptionGroupLegacyDriver.self, equals: helpMessage)
AssertHelp(for: HideOptionGroupDriver.self, equals: helpMessage)
AssertHelp(.default, for: HideOptionGroupLegacyDriver.self, equals: helpMessage)
AssertHelp(.default, for: HideOptionGroupDriver.self, equals: helpMessage)
}

@available(*, deprecated)
Expand All @@ -534,8 +534,8 @@ extension HelpGenerationTests {
-h, --help Show help information.

"""
AssertHelpHidden(for: HideOptionGroupLegacyDriver.self, equals: helpHiddenMessage)
AssertHelpHidden(for: HideOptionGroupDriver.self, equals: helpHiddenMessage)
AssertHelp(.hidden, for: HideOptionGroupLegacyDriver.self, equals: helpHiddenMessage)
AssertHelp(.hidden, for: HideOptionGroupDriver.self, equals: helpHiddenMessage)
}

struct AllValues: ParsableCommand {
Expand Down Expand Up @@ -591,8 +591,7 @@ extension HelpGenerationTests {
}

func testHelpWithPrivate() {
// For now, hidden and private have the same behaviour
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment isn't accurate anymore - does this test still make sense?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll update the comment and/or tests in ~an hour!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test still adds coverage but I think I'll replace these visibility tests in a future PR.

AssertHelp(for: Q.self, equals: """
AssertHelp(.default, for: Q.self, equals: """
USAGE: q --name <name> [--title <title>]

OPTIONS:
Expand Down Expand Up @@ -627,7 +626,7 @@ extension HelpGenerationTests {
}

func testIssue278() {
AssertHelp(for: ParserBug.Sub.self, root: ParserBug.self, equals: """
AssertHelp(.default, for: ParserBug.Sub.self, root: ParserBug.self, equals: """
USAGE: parserBug sub [--example] [<argument>]

ARGUMENTS:
Expand Down