Skip to content

Print supported values in synopses, when practical #620

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

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ extension Math.Statistics {
abstract: "Print the average of the values.",
aliases: ["avg"])

enum Kind: String, ExpressibleByArgument {
enum Kind: String, ExpressibleByArgument, CaseIterable {
case mean, median, mode
}

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 @@ -114,9 +114,9 @@ internal struct HelpGenerator {
} else {
var usage = UsageGenerator(toolName: toolName, definition: [currentArgSet])
.synopsis
if !currentCommand.configuration.subcommands.isEmpty {
if !currentCommand.configuration.subcommands.isEmpty {
if usage.last != " " { usage += " " }
usage += "<subcommand>"
usage += "<subcommand>"
}
self.usage = usage
}
Expand Down
7 changes: 6 additions & 1 deletion Sources/ArgumentParser/Usage/UsageGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,12 @@ extension ArgumentDefinition {

switch update {
case .unary:
return "\(name.synopsisString) <\(valueName)>"
let joinedValues = help.allValueStrings.joined(separator: "|")
if help.allValueStrings.count > 1 && joinedValues.count <= 40 {
return "\(name.synopsisString) <\(joinedValues)>"
} else {
return "\(name.synopsisString) <\(valueName)>"
}
case .nullary:
return name.synopsisString
}
Expand Down
26 changes: 24 additions & 2 deletions Tests/ArgumentParserExampleTests/MathExampleTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,33 @@ final class MathExampleTests: XCTestCase {
try AssertExecuteCommand(command: "math help add --help", expected: helpText)
}

func testMath_StatsHelp() throws {
let helpText = """
OVERVIEW: Calculate descriptive statistics.

USAGE: math stats <subcommand>

OPTIONS:
--version Show the version.
-h, --help Show help information.

SUBCOMMANDS:
average, avg Print the average of the values.
stdev Print the standard deviation of the values.
quantiles Print the quantiles of the values (TBD).

See 'math help stats <subcommand>' for detailed help.
"""
try AssertExecuteCommand(command: "math stats -h", expected: helpText)
try AssertExecuteCommand(command: "math stats --help", expected: helpText)
try AssertExecuteCommand(command: "math help stats", expected: helpText)
}

func testMath_StatsMeanHelp() throws {
let helpText = """
OVERVIEW: Print the average of the values.

USAGE: math stats average [--kind <kind>] [<values> ...]
USAGE: math stats average [--kind <mean|median|mode>] [<values> ...]

ARGUMENTS:
<values> A group of floating-point values to operate on.
Expand Down Expand Up @@ -128,7 +150,7 @@ final class MathExampleTests: XCTestCase {
command: "math stats average --kind mode",
expected: """
Error: Please provide at least one value to calculate the mode.
Usage: math stats average [--kind <kind>] [<values> ...]
Usage: math stats average [--kind <mean|median|mode>] [<values> ...]
See 'math stats average --help' for more information.
""",
exitCode: .validationFailure)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//===----------------------------------------------------------*- swift -*-===//
//
// This source file is part of the Swift Argument Parser open source project
//
// Copyright (c) 2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
//
//===----------------------------------------------------------------------===//

import XCTest
import ArgumentParserTestHelpers
@testable import ArgumentParser

extension HelpGenerationTests {
enum Fruit: String, ExpressibleByArgument, CaseIterable {
case apple, banana, coconut, dragonFruit = "dragon-fruit", elderberry, fig, grape, honeydew
}

enum Action: String, ExpressibleByArgument, CaseIterable {
case purchase, sample, refund = "return"
}

enum Count: Int, ExpressibleByArgument, CaseIterable {
case zero, one, two, three, four, five, six, seven, eight, nine, ten, eleven, twelve, thirteen, fourteen, fifteen, sixteen, seventeen, eighteen, nineteen, twenty
}

enum Ripeness: String, ExpressibleByArgument, CaseIterable {
case under, perfect, over
}

struct FruitStore: ParsableArguments {
@Argument(help: "The transaction type")
var action: Action = .purchase

@Argument(help: "The fruit to purchase")
var fruit: Fruit

@Option(help: "The number of fruit to purchase")
var quantity: Count = .one

@Option(help: "The desired ripeness of fruit")
var ripeness: Ripeness = .perfect
}

func testFruitStoreHelp() {
AssertHelp(.default, for: FruitStore.self, equals: """
USAGE: fruit_store [<action>] <fruit> [--quantity <quantity>] [--ripeness <under|perfect|over>]

ARGUMENTS:
<action> The transaction type (values: purchase, sample,
return; default: purchase)
<fruit> The fruit to purchase (values: apple, banana,
coconut, dragon-fruit, elderberry, fig, grape,
honeydew)

OPTIONS:
--quantity <quantity> The number of fruit to purchase (values: 0, 1, 2, 3,
4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
19, 20; default: 1)
--ripeness <ripeness> The desired ripeness of fruit (values: under,
perfect, over; default: perfect)
-h, --help Show help information.

""")
}
}
2 changes: 1 addition & 1 deletion Tests/ArgumentParserUnitTests/HelpGenerationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ extension HelpGenerationTests {

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

ARGUMENTS:
<occupation> Your occupation. (default: --)
Expand Down