Headers for Parameters - Rogue help option

I am trying to customize headers in the help screen generated by SAP using @OptionGroup.

Question 1. Is this the best/only way to customized headers?

Question 2. If so, how do I get rid of the OPTIONS header in the example. Or change it to, say, “OTHER OPTIONS”?

Example:

This is not what I am doing, but it illustrates the issue.

[I] release> ./repeat-phrase --help
OVERVIEW: Repeat a phrase multiple times.

USAGE: repeat-phrase [--count <count>] [--prefix <prefix>] [--include-counter] [--uppercase] [--lowercase] <phrase>

ARGUMENTS:
  <phrase>                The phrase to repeat.

CONTENT:
  -c, --count <count>     How many times to repeat 'phrase'. (default: 2)
  -p, --prefix <prefix>   A word or two to prefix 'phrase'.
  -i, --include-counter   Include a counter with each repetition.

FORMATTING OPTIONS:
  -u, --uppercase         Print text in upper case
  -l, --lowercase         Print text in lower case.

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

Here is the code:

@main
struct RepeatPhrase: ParsableCommand {
    
    static let configuration = CommandConfiguration(
        abstract: "Repeat a phrase multiple times.")

    @OptionGroup(title: "CONTENT") var c: Content
    @OptionGroup(title: "FORMATTING OPTIONS") var f: Format

    @Argument(help: "The phrase to repeat.")
    var phrase: String
    
    mutating func run() throws {
        var text = phrase
        if let prefix = c.prefix { text = "\(prefix) - \(text)" }
        text = f.lowercase ? text.lowercased() : f.uppercase ? text.uppercased() : text
        for i in 1...(max(c.count, 1)) {
            if c.includeCounter { print("\(i): \(text)") } else { print(text) }
        }
    }
}

struct Format: ParsableArguments {
    @Flag(name: .shortAndLong, help: "Print text in upper case")
    var uppercase = false
    
    @Flag(name: .shortAndLong, help: "Print text in lower case.")
    var lowercase = false
}

struct Content: ParsableArguments {
    
    @Option(name: .shortAndLong ,help: "How many times to repeat 'phrase'.")
    var count: Int = 2
    
    @Option(name: .shortAndLong ,help: "A word or two to prefix 'phrase'.")
    var prefix: String?
    
    @Flag(name: .shortAndLong , help: "Include a counter with each repetition.")
    var includeCounter = false
}

If you are speaking about the OPTIONS: that is grouping "--help" option, it does not appear to be configurable.

Though I suspect the maintainers (which I am not) welcome PR's :slight_smile:

Indeed! Anything that isn't grouped in a titled option group gets put in an ARGUMENTS or OPTIONS bucket.

...which includes the --help flag. There isn't a built-in way to move this to a different group, but you could hack it by suppressing the default flag, and then providing your own help flag inside an option group, and throwing CleanExit.helpRequest(self) from either validate() or run().

1 Like

Thanks for the quick reply.

I am sure they do. They are very helpful and responsive. However this is way above my pay grade.:slightly_smiling_face:

I got this far with the hack.

[I] release> ./repeat-phrase --help
OVERVIEW: Repeat a phrase multiple times.

USAGE: repeat-phrase [--count <count>] [--prefix <prefix>] [--include-counter] [--uppercase] [--lowercase] [--help] <phrase>

ARGUMENTS:
  <phrase>                The phrase to repeat.

CONTENT:
  -c, --count <count>     How many times to repeat 'phrase'. (default: 2)
  -p, --prefix <prefix>   A word or two to prefix 'phrase'.
  -i, --include-counter   Include a counter with each repetition.

FORMATTING OPTIONS:
  -u, --uppercase         Print text in upper case
  -l, --lowercase         Print text in lower case.

OTHER OPTIONS:
  -h, --help              Trigger help

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

But I still need to suppress the default flag, and I don’t know how. Could someone please tell me how to do it?

The CommandConfiguration type has a helpNames property which can be set to [] (aka none). I think that should do it.

Thank you. Now the help screen looks good - same as above, without the OPTIONS group.

I noticed that the hack has one issue. The hacked --help option will not be detected by run() if there are errors in the command call.

[I] release> ./repeat-phrase --help
Error: Missing expected argument '<phrase>'
Help:  <phrase>  The phrase to repeat.
Usage: repeat-phrase [--count <count>] [--prefix <prefix>] [--include-counter] [--uppercase] [--lowercase] [--help] <phrase>

If a phrase , say “Hi”, is added, so that there are no errors, the hacked —help is detected.

[I] release> ./repeat-phrase hi --help
OVERVIEW: Repeat a phrase multiple times.

USAGE: repeat-phrase [--count <count>] [--prefix <prefix>] [--include-counter] [--uppercase] [--lowercase] [--help] <phrase>

ARGUMENTS:
  <phrase>                The phrase to repeat.

CONTENT:
  -c, --count <count>     How many times to repeat 'phrase'. (default: 2)
  -p, --prefix <prefix>   A word or two to prefix 'phrase'.
  -i, --include-counter   Include a counter with each repetition.

FORMATTING OPTIONS:
  -u, --uppercase         Print text in upper case
  -l, --lowercase         Print text in lower case.

OTHER OPTIONS:
  -h, --help              Trigger help

Here’s the code:

@main
struct RepeatPhrase: ParsableCommand {
    
    static let configuration = CommandConfiguration(
        abstract: "Repeat a phrase multiple times.", helpNames: [])

    @OptionGroup(title: "CONTENT") var c: Content
    @OptionGroup(title: "FORMATTING OPTIONS") var f: Format
    @OptionGroup(title: "OTHER OPTIONS") var o: OtherOptions

    @Argument(help: "The phrase to repeat.")
    var phrase: String
    
    mutating func run() throws {
        if o.help {
            throw CleanExit.helpRequest(self)
        }
        var text = phrase
        if let prefix = c.prefix { text = "\(prefix) - \(text)" }
        text = f.lowercase ? text.lowercased() : f.uppercase ? text.uppercased() : text
        for i in 1...(max(c.count, 1)) {
            if c.includeCounter { print("\(i): \(text)") } else { print(text) }
        }
    }
}

struct OtherOptions: ParsableCommand {
    
    @Flag(name: .shortAndLong, help: "Trigger help")
    var help = false
}

struct Format: ParsableArguments {
    @Flag(name: .shortAndLong, help: "Print text in upper case")
    var uppercase = false
    
    @Flag(name: .shortAndLong, help: "Print text in lower case.")
    var lowercase = false
}

struct Content: ParsableArguments {
    
    @Option(name: .shortAndLong ,help: "How many times to repeat 'phrase'.")
    var count: Int = 2
    
    @Option(name: .shortAndLong ,help: "A word or two to prefix 'phrase'.")
    var prefix: String?
    
    @Flag(name: .shortAndLong , help: "Include a counter with each repetition.")
    var includeCounter = false
}

Where I think we are now is that SAP supports custom headers in its built-in help screen, except for the help flag (and presumably the version option).

IMO custom headers in help screens are essential. And the help flag exception is a show-stopper.

  1. Is it possible to simply mark the help flag as not visible? It would still not be possible to put under a custom header, but that would be fine with me. (Everybody knows to enter —help to get help).
  2. Would it be possible to add another property to Configuration, say nameOfOptionGroupToContainTheHelpFlag: String?
  3. Can this be fixed some other way?