Skip to content
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
14 changes: 9 additions & 5 deletions go/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,12 @@ argv.UsageLine([]string{"mise"}, mise.Root, mise.HelpText)
// mise [FLAGS] [TASK] <SUBCOMMAND>
```

**All 211 of mise's usage lines match usage-lib's byte for byte**, which is the
test that keeps it honest. usage-lib builds the line from a spec through a
`argv.ShortHelp` renders the whole page `-h` prints — header, `Commands`,
`Arguments`, `Flags` and `Global flags`, with the columns lined up and the
inherited globals worked out the way the parser resolves them.

**All 211 of mise's usage lines and all 211 of its pages match usage-lib's byte
for byte**, which is the test that keeps both honest. usage-lib builds the line from a spec through a
template over a runtime model; this builds it from static tables. Reimplemented
rules drift, so both are run over mise's real spec and compared — the same check
`benches/gate/tests/help.rs` makes for usage-argv, against the same reference.
Expand Down Expand Up @@ -183,9 +187,9 @@ claim is measured at real scale rather than against a fixture with four flags:
- **Typed values.** Binding collects text. Something still has to turn `"8"` into
an `int` and `"1m"` into a `time.Duration`, and report the ones that will not
convert.
- **The help pages themselves.** The usage line is done and the table behind it
carries the text; `-h` and `--help` still need laying out, which on the Rust
side is most of `argv/src/help.rs`.
- **The long page.** `-h` is done; `--help` wraps long descriptions and switches
to a two-line layout for entries that have a longer form, which `ShortHelp`
does not do.
- **Errors worth reading.** `Error()` returns `unknown flag: --wat`, which names
the problem and helps nobody fix it. usage-argv renders these through miette
with the offending token underlined.
Expand Down
40 changes: 35 additions & 5 deletions go/argv/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,29 @@ type Help struct {
Long string
// Heading groups an entry into a section of the page. Presentational only.
Heading string

// The rest a page prints and the usage line does not.

// VisibleAliases are the aliases a command advertises. The parse table merges
// hidden ones in beside these, because binding does not care which is which;
// a page does, and that is the whole of the distinction.
VisibleAliases []string
// Choices, Env and Default are the annotations a page appends to an entry's
// help: `[a, b]`, `[env: X]`, `(default: y)`.
//
// Duplicated from [Meta] rather than read from it, which is the price of
// keeping the two tables separable: a CLI that prints help should not have to
// carry the post-binding table, and one that applies the rules should not have
// to carry the help strings.
Choices []string
Env string
Default []string
// BeforeHelp and AfterHelp bracket this command's page, overriding the
// spec-wide text.
BeforeHelp string
AfterHelp string
// Examples are worked invocations, printed last.
Examples []Example
}

// HelpTable is the cold help table, indexed by key: entry `Key` sits at
Expand Down Expand Up @@ -144,16 +167,23 @@ func UsageLine(path []string, cmd *Command, help HelpTable) string {
}

// flagUsage is how one flag appears in the usage line: `-f --force`, plus its
// value if it takes one.
// value if it takes one. The line always offers every spelling, since nothing on
// it is competing for a word.
func flagUsage(f *Flag, h *Help) string {
return flagUsageShown(f, allShown(f), h)
}

// flagUsageShown is the same, restricted to the spellings a page is still
// offering for this flag — see [shown].
func flagUsageShown(f *Flag, show shown, h *Help) string {
var out strings.Builder

long, short := "", byte(0)
if len(f.Longs) > 0 {
long = f.Longs[0]
if show.hasLong {
long = show.long
}
if len(f.Shorts) > 0 {
short = f.Shorts[0]
if show.hasShort {
short = show.short
}

// The declared name, when it is not the one the forms would imply. A flag
Expand Down
Loading