diff --git a/go/README.md b/go/README.md index 754eb7cf8..367cc573b 100644 --- a/go/README.md +++ b/go/README.md @@ -256,6 +256,8 @@ claim is measured at real scale rather than against a fixture with four flags: - **A typed front door.** The conversions exist; what is missing is generated code that calls them, so a CLI author gets a struct rather than events. -- **Per-shell completion output.** `Walk` and `Candidates` answer _what_ could go - at the cursor; turning that into the text bash, zsh, fish or PowerShell expect - is still to do, as is running the `complete` scripts a spec can declare. +- **The shell scripts themselves.** A CLI can now answer a completion request — + `mycli __complete_word__ --shell zsh --line "…"` — in the text each shell reads. + What is missing is the handful of lines that register that callback with bash, + zsh, fish, nushell and PowerShell, and running the `run=` scripts a spec can + declare, which needs a subprocess this package has no business starting. diff --git a/go/argv/complete.go b/go/argv/complete.go index e7be2f945..e9ac03f5c 100644 --- a/go/argv/complete.go +++ b/go/argv/complete.go @@ -191,10 +191,16 @@ func Candidates(pos Position, partial string, help HelpTable, meta Metadata) []C commands() } - // Flags, only where one could still be typed, and taken from the parser's own - // scope so that shadowing is respected: a subcommand redeclaring an inherited - // name offers its own. - if pos.FlagsPossible { + // Flags, only where one could still be typed *and* the user has started typing + // one. The reference offers no flags for a bare cursor — `ex ⌶` lists + // subcommands, `ex -⌶` lists both forms, `ex --⌶` the longs — and checked + // against `usage complete-word` rather than assumed. Offering them anyway made + // every position look answered, which is also what decides whether the shell + // should fall back to paths. + // + // The prefix filter below would narrow them to the same set; what this changes + // is the empty prefix, where it would not. + if pos.FlagsPossible && strings.HasPrefix(partial, "-") { for _, s := range flagsInScope(pos.Chain) { if h := help.Lookup(s.flag.Key); h != nil && h.Hide { continue diff --git a/go/argv/complete_shell.go b/go/argv/complete_shell.go index 96fc0cf2e..414136879 100644 --- a/go/argv/complete_shell.go +++ b/go/argv/complete_shell.go @@ -21,6 +21,35 @@ const ( PowerShell ) +// doublesQuotes reports whether a quote inside a quoted string is written by +// doubling it, which is PowerShell's rule and nobody else's. +func (s Shell) doublesQuotes() bool { return s == PowerShell } + +// backtickEscapes reports whether an escape is written with a backtick rather +// than a backslash — PowerShell again. +func (s Shell) backtickEscapes() bool { return s == PowerShell } + +// ShellNamed is the shell a `--shell` argument names, and whether it named one. +// +// A completion request comes from a script this package wrote, so the name is one +// of five — but it arrives as text off a command line, and a shell that sends +// something else should get an answer rather than a crash. +func ShellNamed(name string) (Shell, bool) { + switch name { + case "bash": + return Bash, true + case "zsh": + return Zsh, true + case "fish": + return Fish, true + case "nu", "nushell": + return Nu, true + case "powershell", "pwsh": + return PowerShell, true + } + return Bash, false +} + // Files says whether paths belong at this position as well as the candidates. type Files uint8 diff --git a/go/argv/complete_test.go b/go/argv/complete_test.go index 12b9a0c31..0d9accdb5 100644 --- a/go/argv/complete_test.go +++ b/go/argv/complete_test.go @@ -61,8 +61,24 @@ func complete(words []string, partial string) []string { } func TestCompletionOffersCommandsFlagsAndAliases(t *testing.T) { + // A bare cursor is asking which command to run. The reference offers no flags + // there — checked against `usage complete-word`, which answers `install` and + // `run` for a spec whose root also has `-v --verbose`. got := complete(nil, "") - for _, want := range []string{"run", "list", "r", "--verbose", "-v", "--color", "--no-color"} { + for _, want := range []string{"run", "list", "r"} { + if !offered(got, want) { + t.Errorf("want %q offered, got %v", want, got) + } + } + for _, unwanted := range []string{"--verbose", "-v"} { + if offered(got, unwanted) { + t.Errorf("a bare cursor is not asking for flags: %v", got) + } + } + + // A dash is: a lone one offers both forms. + got = complete(nil, "-") + for _, want := range []string{"--verbose", "-v", "--color", "--no-color"} { if !offered(got, want) { t.Errorf("want %q offered, got %v", want, got) } @@ -87,7 +103,7 @@ func TestCompletionFiltersByThePartialWord(t *testing.T) { // A global is offered inside a subcommand, because the parser accepts it there. func TestAGlobalIsOfferedInsideASubcommand(t *testing.T) { - got := complete([]string{"run"}, "") + got := complete([]string{"run"}, "-") if !offered(got, "--verbose") { t.Errorf("an inherited global should be offered: %v", got) } @@ -213,7 +229,7 @@ func TestOnlyTheClaimedSpellingIsWithdrawn(t *testing.T) { help := HelpTable{{Key: 1}, {Key: 2}, {Key: 3}, {Key: 4}} meta := Metadata{{Key: 1}, {Key: 2}, {Key: 3}, {Key: 4}} - got := values(Candidates(Walk(root, []string{"run"}), "", help, meta)) + got := values(Candidates(Walk(root, []string{"run"}), "-", help, meta)) // `--jobs` is the subcommand's now, and still offered once. if n := count(got, "--jobs"); n != 1 { t.Errorf("--jobs should appear once, got %d: %v", n, got) @@ -264,7 +280,7 @@ func TestANegationLosesToALongOfTheSameSpelling(t *testing.T) { if f := binds(t, root, []string{"run", "--no-color"}); f != global { t.Fatalf("the parser binds --no-color to %v, so the premise is wrong", f) } - got := values(Candidates(Walk(root, []string{"run"}), "", help, meta)) + got := values(Candidates(Walk(root, []string{"run"}), "-", help, meta)) if n := count(got, "--no-color"); n != 1 { t.Errorf("--no-color should be offered once, by the flag that binds it, got %d: %v", n, got) @@ -288,7 +304,7 @@ func TestAnInheritedNegationSurvivesItsFlagsOtherSpellings(t *testing.T) { if f := binds(t, root, []string{"run", "--no-color"}); f != global { t.Fatalf("the parser binds --no-color to %v, so the premise is wrong", f) } - got := values(Candidates(Walk(root, []string{"run"}), "", help, meta)) + got := values(Candidates(Walk(root, []string{"run"}), "-", help, meta)) if !offered(got, "--no-color") { t.Errorf("--no-color still binds, so it should be offered: %v", got) } @@ -339,7 +355,7 @@ func TestANegationSpelledLikeItsOwnLongIsOfferedOnce(t *testing.T) { help := HelpTable{{Key: 1}, {Key: 2}} meta := Metadata{{Key: 1}, {Key: 2}} - got := values(Candidates(Walk(root, nil), "", help, meta)) + got := values(Candidates(Walk(root, nil), "-", help, meta)) if n := count(got, "--no-color"); n != 1 { t.Errorf("--no-color should be offered once, got %d: %v", n, got) } @@ -368,19 +384,24 @@ func TestAVariadicStillCollectingOffersFlagsToo(t *testing.T) { } got := values(Candidates(Walk(root, []string{"--tools", "a"}), "", help, meta)) - if !offered(got, "--force") { - t.Errorf("a flag ends the collection and binds, so it belongs here: %v", got) - } if !offered(got, "node") { t.Errorf("the variadic's own values belong here too: %v", got) } + // And once a dash is typed, the flags that would end the collection — the + // same rule as anywhere else, which is the point: this position is not the + // exclusive one a flag owed its value is. + dashed := values(Candidates(Walk(root, []string{"--tools", "a"}), "-", help, meta)) + if !offered(dashed, "--force") { + t.Errorf("a flag ends the collection and binds, so it belongs here: %v", dashed) + } // A plain word goes to the variadic, so nothing a plain word cannot be. if offered(got, "run") { t.Errorf("a subcommand name would be collected as a value, not bound: %v", got) } - // And a flag still owed its first value keeps the position to itself. - owed := values(Candidates(Walk(root, []string{"--tools"}), "", help, meta)) + // And a flag still owed its first value keeps the position to itself, dash or + // no dash: the parser refuses a flag-like token there. + owed := values(Candidates(Walk(root, []string{"--tools"}), "-", help, meta)) if offered(owed, "--force") { t.Errorf("a flag-like token is refused where a value is owed: %v", owed) } @@ -403,7 +424,7 @@ func TestANearerFlagTakesTheSpellingFromAnInheritedNegation(t *testing.T) { if f := binds(t, root, []string{"run", "--x"}); f != local { t.Fatalf("the parser binds --x to the nearer flag, got %v", f) } - got := values(Candidates(Walk(root, []string{"run"}), "", help, meta)) + got := values(Candidates(Walk(root, []string{"run"}), "-", help, meta)) if n := count(got, "--x"); n != 1 { t.Errorf("--x should be offered once, by the flag that binds it, got %d: %v", n, got) } diff --git a/go/argv/post.go b/go/argv/post.go index 928ee40ed..6aa5c8492 100644 --- a/go/argv/post.go +++ b/go/argv/post.go @@ -39,6 +39,16 @@ type Meta struct { // between them can name a different flag entirely. Empty for an argument, // which is typed as its value rather than as a form. Spelling string + // ValueName is what a flag's value is called — the `DIR` of `--into ` — + // and empty where the entry is an argument, which is named by its value + // already. Read by completion rather than by any rule here: what a value is + // called is what says whether a path belongs there. + ValueName string + // CompleteType is the type a spec's `complete` block names for this entry, + // where it names one. Also completion's, and carried for the same reason: an + // author who wrote `complete "input" type="file"` said what the position + // takes, and the alternative is inferring it from a name they did not choose. + CompleteType string // Flag distinguishes a missing flag from a missing argument, which the // grammar reports as different classes. Flag bool diff --git a/go/argv/request.go b/go/argv/request.go new file mode 100644 index 000000000..371c0fc84 --- /dev/null +++ b/go/argv/request.go @@ -0,0 +1,215 @@ +package argv + +import "strings" + +// The hidden command a shell calls, and what it answers. +// +// A completion is not something the CLI runs. It is recognized before the parse +// rather than inside it: putting it in the tables would make it a command — +// visible to the grammar, to the help, to the spec — and every CLI would grow a +// subcommand nobody typed. +// +// The request is usage-argv's, spelled the same way on purpose: +// +// mycli __complete_word__ --shell zsh --line "mycli install no" +// +// One convention, so a shell script written for either framework says the same +// thing, and so a spec's `complete "x" run="mycli __complete_word__ …"` means one +// thing whichever language answers it. + +// RequestName is the argument that marks a completion request. +// +// Long and ugly on purpose: it is typed by a script, never by a person, and it +// has to be a word no CLI would want for itself. +const RequestName = "__complete_word__" + +// Request is a completion request as a shell sent it. +type Request struct { + Shell Shell + // Line is the command line as typed, and Cursor a byte offset into it. + Line string + Cursor int +} + +// ParseRequest reads a completion request out of argv, and reports whether this +// was one at all. +// +// `argv` is what the program was given, without its own name — the same slice +// [New] takes, so a caller asks this first and parses only if the answer is no. +// +// The flags are read by hand. There are three, and reading them with the parser +// would mean putting them in the tables this is deliberately outside of. Anything +// unrecognized is ignored rather than refused: a completion that errors out is a +// shell that beeps at every keystroke. +func ParseRequest(argv []string) (Request, bool) { + if len(argv) == 0 || argv[0] != RequestName { + return Request{}, false + } + req := Request{Shell: Bash, Cursor: -1} + for i := 1; i < len(argv); i++ { + switch argv[i] { + case "--shell": + if i+1 < len(argv) { + i++ + if shell, ok := ShellNamed(argv[i]); ok { + req.Shell = shell + } + } + case "--line": + if i+1 < len(argv) { + i++ + req.Line = argv[i] + } + case "--cursor": + if i+1 < len(argv) { + i++ + if n, err := atoi(argv[i]); err == nil { + req.Cursor = n + } + } + } + } + // No cursor means the end of the line, which is where a shell puts it when it + // has no way to say — nushell, whose completer only ever sees the words. + if req.Cursor < 0 { + req.Cursor = len(req.Line) + } + return req, true +} + +// Answer works out what could be typed at the cursor. +// +// The candidates come from the tables, and so does the question of whether paths +// belong here — a shell can complete those itself, and asking it to is how a CLI +// says "anything at all goes here" without listing the filesystem. +func (r Request) Answer(root *Command, help HelpTable, meta Metadata) Answer { + split := Split(r.Line, r.Cursor, r.Shell) + pos := Walk(root, split.Argv()) + candidates := Candidates(pos, split.Prefix, help, meta) + return Answer{Candidates: candidates, Files: filesAt(pos, split, candidates, meta)} +} + +// Respond is the whole of what a CLI has to do: recognize the request, answer it, +// and write the text its shell reads. +// +// Returns false where argv is an ordinary invocation, which is the caller's cue +// to parse it as one. +func Respond(argv []string, root *Command, help HelpTable, meta Metadata) (string, bool) { + req, ok := ParseRequest(argv) + if !ok { + return "", false + } + return RenderAnswer(req.Answer(root, help, meta), req.Shell), true +} + +// filesAt decides whether the shell should offer paths here as well. +// +// Four questions, in the order the reference asks them: +// +// - A dash-prefixed word is a flag or nothing. No path starts with one. +// - An argument that is not readable until after a `--` is not fillable yet, so +// nothing belongs there — not even a path, which the parser would refuse +// exactly as it refuses a value. +// - Did the spec say what this position takes? A `complete` block naming a type +// answers outright, and so does a name like `` or ``: the reference +// resolves a completer by name and falls back to reading the name as the type. +// - Otherwise: is the position *closed*? Something was offered, or the entry +// declares its own set, in which case an unmatched prefix means "no matches" +// rather than "ask somebody else". Offering the working directory for a +// mistyped choice answers the second as though it were the first. +func filesAt(pos Position, split SplitLine, candidates []Candidate, meta Metadata) Files { + if pos.FlagsPossible && strings.HasPrefix(split.Prefix, "-") { + return NoFiles + } + // Only when the cursor is *at* that argument. A flag taking a value is a + // different position that happens to have an unfilled positional behind it, + // and a rule about the positional has nothing to say about the flag: `ex + // --from ⌶` takes a path whatever the argument after it needs. A variadic + // still collecting is the same position by a weaker claim, and the reference + // folds the two together here. + if pos.AwaitingValue == nil && pos.Collecting == nil && pos.NextArg != nil && + pos.NextArg.DoubleDash == DoubleDashRequired && !pos.SeparatorSeen { + return NoFiles + } + + var m *Meta + named := "" + switch { + case pos.AwaitingValue != nil: + m = meta.Lookup(pos.AwaitingValue.Key) + named = pos.AwaitingValue.Name + case pos.Collecting != nil: + m = meta.Lookup(pos.Collecting.Key) + named = pos.Collecting.Name + case pos.NextArg != nil: + m = meta.Lookup(pos.NextArg.Key) + named = pos.NextArg.Name + } + if m != nil { + if m.ValueName != "" { + named = m.ValueName + } + if asked := filesFor(m.CompleteType); asked != NoFiles { + return asked + } + } + if asked := filesFor(named); asked != NoFiles { + return asked + } + + declaresChoices := m != nil && len(m.Choices) > 0 + if len(candidates) > 0 || declaresChoices || pos.HelpTopic { + return NoFiles + } + return AnyFile +} + +// filesFor is the paths a name asks for, by the name itself. +// +// The same names the reference reads, compared without case: an argument called +// `` completes files and `` directories without a spec saying so. +func filesFor(name string) Files { + switch { + case name == "": + return NoFiles + case strings.EqualFold(name, "file"), strings.EqualFold(name, "path"), + strings.EqualFold(name, "config_file"): + return AnyFile + case strings.EqualFold(name, "dir"), strings.EqualFold(name, "directory"): + return Dirs + } + return NoFiles +} + +// atoi is [strconv.Atoi] for a non-negative number, written out because this +// package takes no imports it can avoid. +// +// A number too large to hold is refused rather than wrapped. Wrapping is worse +// than refusing here: a cursor that came back as a small number would describe a +// position near the start of the line, and the completion would answer confidently +// about the wrong word — where a refusal falls back to the end of the line, which +// is where a shell means when it says nothing. +func atoi(s string) (int, error) { + const maxInt = int(^uint(0) >> 1) + if s == "" { + return 0, errNotANumber + } + n := 0 + for i := 0; i < len(s); i++ { + if s[i] < '0' || s[i] > '9' { + return 0, errNotANumber + } + digit := int(s[i] - '0') + if n > (maxInt-digit)/10 { + return 0, errNotANumber + } + n = n*10 + digit + } + return n, nil +} + +type numberError struct{} + +func (numberError) Error() string { return "not a number" } + +var errNotANumber = numberError{} diff --git a/go/argv/request_test.go b/go/argv/request_test.go new file mode 100644 index 000000000..dfd08f9ef --- /dev/null +++ b/go/argv/request_test.go @@ -0,0 +1,223 @@ +package argv + +import ( + "strings" + "testing" +) + +// A CLI with the shapes the files rule turns on: a named path, a named +// directory, a declared set, and an argument that reads only after a separator. +func requestFixture() (*Command, HelpTable, Metadata) { + into := &Flag{Key: 2, Name: "into", Longs: []string{"into"}, TakesValue: true} + tool := &Flag{Key: 3, Name: "tool", Longs: []string{"tool"}, TakesValue: true} + file := &Arg{Key: 4, Name: "FILE"} + edit := &Command{Key: 5, Name: "edit", Flags: []*Flag{into}, Args: []*Arg{file}} + use := &Command{Key: 6, Name: "use", Flags: []*Flag{tool}, + Args: []*Arg{{Key: 7, Name: "TOOL"}}} + after := &Command{Key: 8, Name: "run", + Args: []*Arg{{Key: 9, Name: "TASK", DoubleDash: DoubleDashRequired}}} + root := &Command{Key: 1, Name: "ex", Subcommands: []*Command{edit, use, after}} + + help := HelpTable{{Key: 1}, {Key: 2}, {Key: 3}, {Key: 4}, {Key: 5, Short: "Edit a file"}, + {Key: 6, Short: "Use a tool"}, {Key: 7}, {Key: 8, Short: "Run a task"}, {Key: 9}} + meta := Metadata{ + {Key: 1}, + {Key: 2, Name: "into", Flag: true, ValueName: "DIR"}, + {Key: 3, Name: "tool", Flag: true, ValueName: "TOOL", + Choices: []string{"node", "python"}}, + {Key: 4, Name: "FILE"}, + {Key: 5}, {Key: 6}, + {Key: 7, Name: "TOOL", Choices: []string{"node", "python"}}, + {Key: 8}, + {Key: 9, Name: "TASK"}, + } + return root, help, meta +} + +func ask(t *testing.T, shell Shell, line string) (Answer, string) { + t.Helper() + root, help, meta := requestFixture() + cursor := strings.Index(line, "⌶") + if cursor < 0 { + cursor = len(line) + } + line = strings.Replace(line, "⌶", "", 1) + + argv := []string{RequestName, "--shell", shellName(shell), "--line", line} + out, ok := Respond(argv, root, help, meta) + if !ok { + t.Fatalf("%q should be a completion request", argv) + } + req, _ := ParseRequest(argv) + req.Cursor = cursor + return req.Answer(root, help, meta), out +} + +func shellName(s Shell) string { + switch s { + case Zsh: + return "zsh" + case Fish: + return "fish" + case Nu: + return "nu" + case PowerShell: + return "powershell" + } + return "bash" +} + +// An ordinary invocation is left alone. The request is recognized before the +// parse, and a CLI that answered completions for `ex --help` would be one nobody +// could run. +func TestAnOrdinaryInvocationIsNotARequest(t *testing.T) { + root, help, meta := requestFixture() + for _, argv := range [][]string{{}, {"edit"}, {"--into", "x"}, {"_complete"}} { + if _, ok := Respond(argv, root, help, meta); ok { + t.Errorf("%v is a command line, not a completion request", argv) + } + } +} + +// The three arguments a shell passes, and what a missing one means. +func TestReadingTheRequest(t *testing.T) { + req, ok := ParseRequest([]string{RequestName, "--shell", "zsh", + "--line", "ex use no", "--cursor", "6"}) + if !ok || req.Shell != Zsh || req.Line != "ex use no" || req.Cursor != 6 { + t.Fatalf("read back wrong: %+v ok=%v", req, ok) + } + + // No cursor is the end of the line, which is where a shell puts it when it has + // no way to say — nushell, whose completer only ever sees the words. + req, _ = ParseRequest([]string{RequestName, "--line", "ex use"}) + if req.Cursor != len("ex use") { + t.Errorf("want the end of the line, got %d", req.Cursor) + } + + // A shell passing something this version does not know about is answered, not + // refused: a completion that errors out beeps at every keystroke. + req, ok = ParseRequest([]string{RequestName, "--wat", "1", "--shell", "klingon", + "--line", "ex ", "--cursor", "nope"}) + if !ok || req.Shell != Bash || req.Line != "ex " { + t.Errorf("unknown arguments should be ignored: %+v", req) + } +} + +// What the answer says, end to end, in the shape the shell reads. +func TestAnsweringARequest(t *testing.T) { + _, out := ask(t, Bash, "ex ⌶") + for _, want := range []string{"edit", "use", "run"} { + if !strings.Contains(out, want+"\n") { + t.Errorf("want %q offered:\n%s", want, out) + } + } + // zsh takes three fields, and the description comes from the same table the + // page uses. + _, out = ask(t, Zsh, "ex ed⌶") + if !strings.HasPrefix(out, "edit\tEdit a file\tedit\n") { + t.Errorf("want the zsh triple, got %q", out) + } +} + +// Whether the shell should offer paths as well — the question a CLI answers by +// saying nothing about it. +func TestWhenPathsBelongAtTheCursor(t *testing.T) { + for _, c := range []struct { + line string + want Files + why string + }{ + {"ex ⌶", NoFiles, "subcommands were offered, so the position is answered"}, + {"ex -⌶", NoFiles, "a dash-prefixed word is a flag or nothing"}, + {"ex edit ⌶", AnyFile, "the argument is called FILE"}, + {"ex edit --into ⌶", Dirs, "the value is called DIR"}, + {"ex use ⌶", NoFiles, "the argument declares its own set"}, + {"ex use --tool ⌶", NoFiles, "so does the value"}, + {"ex run ⌶", NoFiles, "that argument is not readable until after a --"}, + {"ex run -- ⌶", AnyFile, "and past the separator it is anything at all"}, + } { + answer, _ := ask(t, Bash, c.line) + if answer.Files != c.want { + t.Errorf("%q: want %v, got %v — %s", c.line, c.want, answer.Files, c.why) + } + } +} + +// A command having flags does not close the position. +// +// `Candidates` offers flags only for a dash-prefixed word, which is what the +// reference does — and what keeps the file fallback working, since the fallback +// asks whether *anything* was offered. Offering every flag at a bare cursor made +// every position on every command with a flag look answered. +func TestFlagsDoNotCloseThePositionToPaths(t *testing.T) { + root := &Command{Key: 1, Name: "ex", + Flags: []*Flag{{Key: 2, Name: "verbose", Longs: []string{"verbose"}}}, + Args: []*Arg{{Key: 3, Name: "INPUT"}}} + help := HelpTable{{Key: 1}, {Key: 2}, {Key: 3}} + meta := Metadata{{Key: 1}, {Key: 2, Name: "verbose", Flag: true}, {Key: 3, Name: "INPUT"}} + + answer := Request{Shell: Bash, Line: "ex ", Cursor: 3}.Answer(root, help, meta) + if answer.Files != AnyFile { + t.Errorf("nothing describes INPUT, so the shell should offer paths: %v", answer.Files) + } + if len(answer.Candidates) != 0 { + t.Errorf("a bare cursor is not asking for flags: %v", values(answer.Candidates)) + } + // And a dash asks for them, which closes the position — no path starts with + // one. + dashed := Request{Shell: Bash, Line: "ex -", Cursor: 4}.Answer(root, help, meta) + if dashed.Files != NoFiles || len(dashed.Candidates) == 0 { + t.Errorf("a dash asks for flags and nothing else: %v %v", + dashed.Files, values(dashed.Candidates)) + } +} + +// A variadic still collecting is on the flag's value, not on the argument behind +// it — so an argument owing a separator has nothing to say about this position. +func TestASeparatorOwedBehindACollectingFlag(t *testing.T) { + tools := &Flag{Key: 2, Name: "tools", Longs: []string{"tools"}, + TakesValue: true, Variadic: true} + root := &Command{Key: 1, Name: "ex", Flags: []*Flag{tools}, + Args: []*Arg{{Key: 3, Name: "TASK", DoubleDash: DoubleDashRequired}}} + help := HelpTable{{Key: 1}, {Key: 2}, {Key: 3}} + meta := Metadata{{Key: 1}, {Key: 2, Name: "tools", Flag: true, ValueName: "PATH"}, + {Key: 3, Name: "TASK"}} + + const line = "ex --tools a " + if got := (Request{Shell: Bash, Line: line, Cursor: len(line)}). + Answer(root, help, meta).Files; got != AnyFile { + t.Errorf("the cursor is on the flag's value, which is called PATH: %v", got) + } +} + +// A cursor too large to hold falls back to the end of the line. +// +// Wrapping would be worse than refusing: a small number describes a position near +// the start of the line, and the completion would answer confidently about the +// wrong word. +func TestACursorTooLargeToHold(t *testing.T) { + for _, huge := range []string{"18446744073709551616", "99999999999999999999"} { + req, ok := ParseRequest([]string{RequestName, "--line", "ex use", "--cursor", huge}) + if !ok || req.Cursor != len("ex use") { + t.Errorf("%s should fall back to the end of the line, got %d", huge, req.Cursor) + } + } +} + +// A completer's declared type outranks the name, because the author wrote it. +func TestADeclaredCompleterTypeDecidesIt(t *testing.T) { + root := &Command{Key: 1, Name: "ex", Args: []*Arg{{Key: 2, Name: "INPUT"}}} + help := HelpTable{{Key: 1}, {Key: 2}} + meta := Metadata{{Key: 1}, {Key: 2, Name: "INPUT", CompleteType: "dir"}} + + req := Request{Shell: Bash, Line: "ex ", Cursor: 3} + if got := req.Answer(root, help, meta).Files; got != Dirs { + t.Errorf("`complete type=\"dir\"` says directories, got %v", got) + } + // And with nothing declared and nothing known, the position is open: a name + // nobody described could be anything, so the shell should offer paths. + meta[1].CompleteType = "" + if got := req.Answer(root, help, meta).Files; got != AnyFile { + t.Errorf("an undescribed position takes anything, got %v", got) + } +} diff --git a/go/argv/split.go b/go/argv/split.go new file mode 100644 index 000000000..f01aa0aa0 --- /dev/null +++ b/go/argv/split.go @@ -0,0 +1,233 @@ +package argv + +import "unicode/utf8" + +// Turning a command line back into the words a shell would have passed. +// +// A completion request arrives as text, not as argv: the shell hands over the +// line and where the cursor is in it, because the words do not exist yet — the +// one being typed is half-written and may be inside quotes that are not closed. +// So the words have to be recovered here, by the same rules the shell would have +// applied had the line been run. +// +// Ported from usage-argv's `split`, which is where the rules were worked out. +// Both sides answer the same request from the same kind of shell script, so a +// line that means one thing to a Rust CLI and another to a Go one would be the +// two frameworks disagreeing about the shell rather than about the spec. + +// SplitLine is a command line as the shell would have passed it, plus where the +// cursor was. +type SplitLine struct { + // Words are the words, unquoted — what argv would hold had the line been run. + // + // Always at least one: a cursor sitting after a space is completing a word + // that does not exist yet, and an empty word is how that is said. Candidates + // for "anything at all" and candidates for "something starting with `no`" are + // the same question with a different prefix, and a caller should not have to + // special-case the empty one. + Words []string + // Cword is which of Words the cursor is in. + Cword int + // Prefix is the part of that word before the cursor, unquoted — what a + // candidate must start with. + Prefix string +} + +// Argv is the words a parser should walk: after the program name, before the +// cursor's word. +// +// Two things dropped for two reasons. The program name, because argv does not +// contain it and the parse tables describe what comes *after* it. The word being +// completed, because it is half-typed by definition — feeding it in would ask +// what can follow a word the user has not finished, when the question is what +// that word could be. +func (s SplitLine) Argv() []string { + start := min(1, s.Cword) + return s.Words[start:s.Cword] +} + +// Split splits a line at a byte cursor, the way `shell` would have split it. +// +// `cursor` is a byte offset into `line`; anything past the end is treated as the +// end, and an offset landing inside a multi-byte character is moved back to that +// character's start rather than being taken literally — a completion request is +// not a place to be strict about a shell's arithmetic. +func Split(line string, cursor int, shell Shell) SplitLine { + if cursor > len(line) { + cursor = len(line) + } + if cursor < 0 { + cursor = 0 + } + cursor = floorCharBoundary(line, cursor) + + var words []string + var word []rune + // Whether anything has been written into `word` — including a quote that so + // far contains nothing, so that `ex ""` is a word and not a gap between two. + started := false + cword, prefix, found := 0, "", false + // Whether the cursor sat inside a word rather than in the gap before one. A + // gap is a word the user is about to type, so one has to be made for them. + cursorInWord := false + + // The cursor is reached before the character it sits in front of is read, so + // the word in hand is the word being completed and what is in it is the + // prefix. Checked at the top of each character and again before an escape + // swallows the one after it: only checking the top left a cursor sitting on an + // escaped character unnoticed, and the split then described the last word of + // the line rather than the one being typed. + reached := func(i int) { + if i == cursor && !found { + cword, prefix, found = len(words), string(word), true + cursorInWord = started + } + } + + var quote rune + runes := []rune(line) + // Byte offsets alongside, because the cursor is one. + offsets := make([]int, len(runes)+1) + at := 0 + for i, r := range runes { + offsets[i] = at + at += utf8.RuneLen(r) + } + offsets[len(runes)] = at + + for i := 0; i < len(runes); i++ { + c := runes[i] + reached(offsets[i]) + + peek := func() (rune, bool) { + if i+1 < len(runes) { + return runes[i+1], true + } + return 0, false + } + + switch { + case quote == '\'': + if c == '\'' { + // PowerShell writes a quote inside a quoted string by doubling it; + // the POSIX-shaped shells have no such rule, and there a second + // quote always ends the string. + if next, ok := peek(); shell.doublesQuotes() && ok && next == '\'' { + reached(offsets[i+1]) + word = append(word, '\'') + i++ + } else { + quote = 0 + } + } else { + word = append(word, c) + } + + case quote != 0: + if c == quote { + if next, ok := peek(); shell.doublesQuotes() && ok && next == quote { + reached(offsets[i+1]) + word = append(word, quote) + i++ + } else { + quote = 0 + } + } else if isEscape(c, shell) { + // Inside double quotes an escape is only an escape before a + // character it could mean something to; before anything else it is + // a literal, which is why a Windows path in double quotes survives. + if next, ok := peek(); ok && escapableInQuotes(next, shell) { + reached(offsets[i+1]) + word = append(word, next) + i++ + } else { + word = append(word, c) + } + } else { + word = append(word, c) + } + + default: + switch { + case c == '\'' || c == '"': + quote = c + started = true + case isEscape(c, shell): + // Before the check, because the escape has already started the + // word: a cursor on the escaped character is inside it, not in the + // gap before it. + started = true + if next, ok := peek(); ok { + reached(offsets[i+1]) + word = append(word, next) + i++ + } + // A trailing escape is a line the user is still typing, not a + // mistake to report: it escapes the character they have not typed. + case isSpace(c): + if started { + words = append(words, string(word)) + word = word[:0] + started = false + } + default: + word = append(word, c) + started = true + } + } + } + + // The cursor at the very end of the line: the loop above only sees positions it + // reads a character at, and there is no character there. + if !found { + cword, prefix = len(words), string(word) + cursorInWord = started + } + if started { + words = append(words, string(word)) + } + + // A cursor in a gap is completing a word that is not in the line yet — at the + // end of it, or between two that are already there. `ex ⌶use` is asking what + // can go *before* `use`, and answering about `use` itself would complete the + // wrong word. + if !cursorInWord { + words = append(words, "") + copy(words[cword+1:], words[cword:]) + words[cword] = "" + } + return SplitLine{Words: words, Cword: cword, Prefix: prefix} +} + +// isEscape reports whether a character starts an escape in this shell. +func isEscape(c rune, shell Shell) bool { + if shell.backtickEscapes() { + return c == '`' + } + return c == '\\' +} + +// escapableInQuotes reports whether an escape inside double quotes applies to the +// character after it. +func escapableInQuotes(c rune, shell Shell) bool { + if shell.backtickEscapes() { + return c == '"' || c == '`' || c == '$' + } + return c == '"' || c == '\\' || c == '$' || c == '`' +} + +// isSpace is the whitespace a shell splits words on. Written out rather than +// taken from `unicode`, which would pull the tables in for a package whose whole +// claim is that it costs nothing unused. +func isSpace(c rune) bool { + return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\v' || c == '\f' +} + +// floorCharBoundary is the start of the character containing `index`. +func floorCharBoundary(s string, index int) int { + // The end of the string is a boundary and has no byte to look at. + for index > 0 && index < len(s) && !utf8.RuneStart(s[index]) { + index-- + } + return index +} diff --git a/go/argv/split_test.go b/go/argv/split_test.go new file mode 100644 index 000000000..f95c7c6c6 --- /dev/null +++ b/go/argv/split_test.go @@ -0,0 +1,116 @@ +package argv + +import ( + "reflect" + "strings" + "testing" +) + +// The cursor is written as `⌶` in these cases, and cut out before the split, so a +// case reads as the line the user is looking at. +func at(line string, shell Shell) SplitLine { + cursor := strings.Index(line, "⌶") + if cursor < 0 { + cursor = len(line) + } + return Split(strings.Replace(line, "⌶", "", 1), cursor, shell) +} + +func TestSplittingALine(t *testing.T) { + for _, c := range []struct { + line string + words []string + cword int + prefix string + }{ + // The plain cases: a word being typed, and a gap after one. + {"ex inst⌶", []string{"ex", "inst"}, 1, "inst"}, + {"ex ⌶", []string{"ex", ""}, 1, ""}, + {"ex install ⌶", []string{"ex", "install", ""}, 2, ""}, + // A cursor inside a word ignores the rest of it: what follows is a tail the + // user has not decided on, and it should not narrow what can be typed. + {"ex ins⌶tall", []string{"ex", "install"}, 1, "ins"}, + // A cursor in the gap *before* a word is completing a word that is not + // there yet, so one is made for it. + {"ex ⌶install", []string{"ex", "", "install"}, 1, ""}, + // Quotes hold a word together, and an unclosed one is a word being typed. + {`ex "two words⌶`, []string{"ex", "two words"}, 1, "two words"}, + {`ex 'two words' ⌶`, []string{"ex", "two words", ""}, 2, ""}, + // An empty quoted string is a word, not a gap. + {`ex "" ⌶`, []string{"ex", "", ""}, 2, ""}, + // An escape takes the character after it, and a trailing one is a line + // still being typed rather than a mistake. + {`ex two\ wo⌶`, []string{"ex", "two wo"}, 1, "two wo"}, + {`ex two\⌶`, []string{"ex", "two"}, 1, "two"}, + // Nothing at all is still one word: the cursor is completing it. + {"", []string{""}, 0, ""}, + {"ex⌶", []string{"ex"}, 0, "ex"}, + } { + got := at(c.line, Bash) + want := SplitLine{Words: c.words, Cword: c.cword, Prefix: c.prefix} + if !reflect.DeepEqual(got, want) { + t.Errorf("%q:\n got %+v\nwant %+v", c.line, got, want) + } + } +} + +// Inside double quotes a backslash escapes only what it could mean something to, +// which is what lets a Windows path survive being quoted. +func TestAnEscapeInsideQuotes(t *testing.T) { + got := at(`ex "C:\Users\me⌶`, Bash) + if want := `C:\Users\me`; got.Prefix != want { + t.Errorf("want %q, got %q", want, got.Prefix) + } + got = at(`ex "say \"hi\"⌶`, Bash) + if want := `say "hi"`; got.Prefix != want { + t.Errorf("want %q, got %q", want, got.Prefix) + } +} + +// PowerShell escapes with a backtick and writes a quote by doubling it. Splitting +// its lines by POSIX rules turned a path into an escape sequence. +func TestPowerShellQuotesItsOwnWay(t *testing.T) { + if got := at("ex C:\\Users\\me⌶", PowerShell); got.Prefix != `C:\Users\me` { + t.Errorf("a backslash is not an escape in PowerShell: %q", got.Prefix) + } + if got := at("ex `\"quoted⌶", PowerShell); got.Prefix != `"quoted` { + t.Errorf("a backtick escapes: %q", got.Prefix) + } + if got := at("ex 'it''s'⌶", PowerShell); got.Prefix != "it's" { + t.Errorf("a doubled quote is one quote: %q", got.Prefix) + } + // And the same doubled quote is two words to bash, which is the point of + // asking which shell sent the line. + if got := at("ex 'it''s'⌶", Bash); got.Prefix != "its" { + t.Errorf("bash has no doubling rule: %q", got.Prefix) + } +} + +// A cursor is a byte offset from a shell's arithmetic, and a completion request is +// not the place to be strict about it. +func TestACursorOutsideTheLine(t *testing.T) { + if got := Split("ex run", 999, Bash); got.Prefix != "run" { + t.Errorf("past the end is the end: %+v", got) + } + if got := Split("ex ünïcode", 5, Bash); got.Prefix != "ü" { + // Byte 5 is inside `ï`; the character it belongs to starts at 4, and the + // word so far is `ü`. Moved back rather than cutting a rune in half. + t.Errorf("a cursor inside a character moves back to its start: %+v", got) + } +} + +// The words a parser walks: after the program name, before the word being typed. +func TestWhatTheParserIsGiven(t *testing.T) { + got := at("ex install no⌶", Bash) + if want := []string{"install"}; !reflect.DeepEqual(got.Argv(), want) { + t.Errorf("want %v, got %v", want, got.Argv()) + } + // Nothing but the program name yet, so nothing to walk. + if got := at("ex ⌶", Bash); len(got.Argv()) != 0 { + t.Errorf("want nothing, got %v", got.Argv()) + } + // Not even the program name: a line that is only a cursor. + if got := at("⌶", Bash); len(got.Argv()) != 0 { + t.Errorf("want nothing, got %v", got.Argv()) + } +} diff --git a/go/conformance/complete_test.go b/go/conformance/complete_test.go new file mode 100644 index 000000000..7c6922dd1 --- /dev/null +++ b/go/conformance/complete_test.go @@ -0,0 +1,151 @@ +package conformance + +import ( + "context" + "os/exec" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/jdx/usage/go/argv" +) + +// Does the cursor get the same answer here as it does from usage-lib? +// +// The same standard the pages are held to, and for the same reason: these rules +// were reimplemented, and reimplemented rules drift. `usage complete-word` is the +// reference a shell would have called before any of this existed, so it is the +// oracle — over mise's real spec, which is the largest one there is. +// +// This exists because a divergence got through. `Candidates` offered every flag +// at a bare cursor; the reference offers none until a `-` is typed, and nothing +// noticed until a reviewer read the two. A hand-written test asserts what its +// author believed; this asks. + +// completeWord is the reference's answer for a partial word at a command path. +// +// Every non-empty line it printed, whatever the line holds. Two of the things it +// can hold are not this side's to reproduce — a listing of the working directory +// where nothing else fit, and whatever a spec's `complete` block printed when the +// reference ran it — so the *positions* asked about are chosen to avoid both, +// rather than the lines being filtered afterwards. Filtering would quietly hide a +// real difference behind a rule about what a path looks like. +// +// Bounded, because it is another program: a reference that hangs should fail this +// test rather than the suite. +func completeWord(t *testing.T, usageBin, kdl string, words []string, cword int) []string { + t.Helper() + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + args := []string{"complete-word", "--shell", "bash", "-f", kdl, + "--cword", itoa(cword), "--"} + args = append(args, words...) + out, err := exec.CommandContext(ctx, usageBin, args...).Output() + if err != nil { + t.Fatalf("the reference should answer %v: %v", words, err) + } + var lines []string + for _, line := range strings.Split(strings.TrimRight(string(out), "\n"), "\n") { + if line != "" { + lines = append(lines, line) + } + } + return lines +} + +func TestTheCursorGetsTheReferencesAnswer(t *testing.T) { + usageBin := findUsage(t) + kdl := filepath.Join("..", "..", "benches", "mise.usage.kdl") + lowered := lowerFile(t, usageBin, kdl) + root, meta, help := lowered.BuildAll() + + // Lines where the reference answers from the spec alone. Two kinds are left + // out on purpose, because they are answers this side does not claim to give: + // a position where the reference lists the working directory (compared as a + // marker below), and one where it *runs* a spec's `complete` block — `mise ⌶` + // and `mise settings ⌶` both do, and shelling out on a Tab is the piece this + // package has deliberately not built. + // + // The root's own cursor is one of those: mise's default subcommand is `run`, + // whose `TASK` runs a completer, so even `mise plug⌶` is answered partly by a + // subprocess. The lines below sit under a subcommand that has neither. + // + // What is left covers the branches: subcommands, aliases, a nested command, + // both flag forms, a long that narrows, and a word that matches nothing. + for _, line := range []string{ + "mise config ", + "mise config l", + "mise -", + "mise --", + "mise --log-", + "mise use -", + "mise plugins ", + "mise plugins in", + "mise plugins wat", + "mise plugins install -", + } { + split := argv.Split(line, len(line), argv.Bash) + want := completeWord(t, usageBin, kdl, split.Words, split.Cword) + + answer := argv.Request{Shell: argv.Bash, Line: line, Cursor: len(line)}. + Answer(root, help, meta) + var got []string + for _, c := range answer.Candidates { + got = append(got, c.Value) + } + + if !sameSet(got, want) { + t.Errorf("%q:\n ours: %v\n lib: %v", line, got, want) + } + } +} + +// And where the reference falls back to the filesystem, the Go side says so +// rather than listing it. +// +// The two are the same answer said differently: usage-lib prints the directory +// because it is answering a shell that has already asked; the tables say "paths +// belong here" and let the script call the shell's own path completion, which +// knows about `~`, variables and remote paths. +func TestAPathFallbackIsAMarkerRatherThanAListing(t *testing.T) { + usageBin := findUsage(t) + kdl := filepath.Join("..", "..", "benches", "mise.usage.kdl") + lowered := lowerFile(t, usageBin, kdl) + root, meta, help := lowered.BuildAll() + + // `mise edit` takes a file, and neither side has anything else to offer there. + const line = "mise edit " + split := argv.Split(line, len(line), argv.Bash) + want := completeWord(t, usageBin, kdl, split.Words, split.Cword) + if len(want) == 0 { + t.Skip("the reference listed nothing here, so there is no fallback to compare") + } + + answer := argv.Request{Shell: argv.Bash, Line: line, Cursor: len(line)}. + Answer(root, help, meta) + if answer.Files != argv.AnyFile { + t.Errorf("the reference fell back to paths at %q, and this did not: %v", + line, answer.Files) + } + if len(answer.Candidates) != 0 { + t.Errorf("nothing of the spec's own belongs there: %v", answer.Candidates) + } +} + +func sameSet(a, b []string) bool { + if len(a) != len(b) { + return false + } + seen := map[string]int{} + for _, s := range a { + seen[s]++ + } + for _, s := range b { + seen[s]-- + if seen[s] < 0 { + return false + } + } + return true +} diff --git a/go/internal/shadow/mise/tables.go b/go/internal/shadow/mise/tables.go index 471c6bfb8..fd7698f2f 100644 --- a/go/internal/shadow/mise/tables.go +++ b/go/internal/shadow/mise/tables.go @@ -3785,25 +3785,25 @@ var cmdWhich = &argv.Command{ var Meta = argv.Metadata{ {}, {Key: FlagContinueOnError, Name: "continue-on-error", Flag: true, Spelling: "--continue-on-error"}, - {Key: FlagCd, Name: "cd", Flag: true, Spelling: "--cd"}, - {Key: FlagEnv, Name: "env", Flag: true, Spelling: "--env"}, + {Key: FlagCd, Name: "cd", Flag: true, Spelling: "--cd", ValueName: "DIR"}, + {Key: FlagEnv, Name: "env", Flag: true, Spelling: "--env", ValueName: "ENV"}, {Key: FlagForce, Name: "force", Flag: true, Spelling: "--force"}, - {Key: FlagJobs, Name: "jobs", Flag: true, Spelling: "--jobs"}, + {Key: FlagJobs, Name: "jobs", Flag: true, Spelling: "--jobs", ValueName: "JOBS"}, {Key: FlagDryRun, Name: "dry-run", Flag: true, Spelling: "--dry-run"}, - {Key: FlagProfile, Name: "profile", Flag: true, Spelling: "--profile"}, + {Key: FlagProfile, Name: "profile", Flag: true, Spelling: "--profile", ValueName: "PROFILE"}, {Key: FlagQuiet, Name: "quiet", Flag: true, Spelling: "--quiet"}, - {Key: FlagShell, Name: "shell", Flag: true, Spelling: "--shell"}, - {Key: FlagTool, Name: "tool", Flag: true, Spelling: "--tool"}, + {Key: FlagShell, Name: "shell", Flag: true, Spelling: "--shell", ValueName: "SHELL"}, + {Key: FlagTool, Name: "tool", Flag: true, Spelling: "--tool", ValueName: "TOOL@VERSION"}, {Key: FlagVerbose, Name: "verbose", Flag: true, Spelling: "--verbose"}, {Key: FlagVersion, Name: "version", Flag: true, Spelling: "--version"}, {Key: FlagYes, Name: "yes", Flag: true, Spelling: "--yes"}, {Key: FlagDebug, Name: "debug", Flag: true, Spelling: "--debug"}, - {Key: FlagLogLevel, Name: "log-level", Flag: true, Spelling: "--log-level", Choices: []string{"trace", "debug", "info", "warning", "error"}}, + {Key: FlagLogLevel, Name: "log-level", Flag: true, Spelling: "--log-level", ValueName: "LEVEL", Choices: []string{"trace", "debug", "info", "warning", "error"}}, {Key: FlagNoConfig, Name: "no-config", Flag: true, Spelling: "--no-config"}, {Key: FlagNoEnv, Name: "no-env", Flag: true, Spelling: "--no-env"}, {Key: FlagNoHooks, Name: "no-hooks", Flag: true, Spelling: "--no-hooks"}, {Key: FlagNoTimings, Name: "no-timings", Flag: true, Spelling: "--no-timings"}, - {Key: FlagOutput, Name: "output", Flag: true, Spelling: "--output"}, + {Key: FlagOutput, Name: "output", Flag: true, Spelling: "--output", ValueName: "OUTPUT"}, {Key: FlagRaw, Name: "raw", Flag: true, Spelling: "--raw"}, {Key: FlagLocked, Name: "locked", Flag: true, Spelling: "--locked"}, {Key: FlagSilent, Name: "silent", Flag: true, Spelling: "--silent"}, @@ -3814,13 +3814,13 @@ var Meta = argv.Metadata{ {Key: ArgTaskArgsLast, Name: "TASK_ARGS_LAST"}, {}, {Key: FlagActivateQuiet, Name: "quiet", Flag: true, Spelling: "--quiet"}, - {Key: FlagActivateShell, Name: "shell", Flag: true, Spelling: "--shell", Choices: []string{"bash", "elvish", "fish", "nu", "xonsh", "zsh", "pwsh"}}, + {Key: FlagActivateShell, Name: "shell", Flag: true, Spelling: "--shell", ValueName: "SHELL", Choices: []string{"bash", "elvish", "fish", "nu", "xonsh", "zsh", "pwsh"}}, {Key: FlagActivateNoHookEnv, Name: "no-hook-env", Flag: true, Spelling: "--no-hook-env"}, {Key: FlagActivateShims, Name: "shims", Flag: true, Spelling: "--shims"}, {Key: FlagActivateStatus, Name: "status", Flag: true, Spelling: "--status"}, {Key: ArgActivateShellType, Name: "SHELL_TYPE", Choices: []string{"bash", "elvish", "fish", "nu", "xonsh", "zsh", "pwsh"}}, {}, - {Key: FlagToolAliasTool, Name: "tool", Flag: true, Spelling: "--tool"}, + {Key: FlagToolAliasTool, Name: "tool", Flag: true, Spelling: "--tool", ValueName: "TOOL"}, {Key: FlagToolAliasNoHeader, Name: "no-header", Flag: true, Spelling: "--no-header"}, {}, {Key: ArgToolAliasGetTool, Name: "TOOL", Required: true}, @@ -3847,9 +3847,9 @@ var Meta = argv.Metadata{ {Key: FlagBootstrapDryRun, Name: "dry-run", Flag: true, Spelling: "--dry-run"}, {Key: FlagBootstrapYes, Name: "yes", Flag: true, Spelling: "--yes"}, {Key: FlagBootstrapForceDotfiles, Name: "force-dotfiles", Flag: true, Spelling: "--force-dotfiles"}, - {Key: FlagBootstrapOnly, Name: "only", Flag: true, Spelling: "--only", Choices: []string{"plugins", "packages", "accounts", "files", "services", "firewall", "compose", "repos", "dotfiles", "mise-shell-activate", "shell", "macos-defaults", "defaults", "macos-launchd-agents", "launchd", "linux-systemd-units", "systemd", "user", "tools", "task", "final-hook"}}, + {Key: FlagBootstrapOnly, Name: "only", Flag: true, Spelling: "--only", ValueName: "ONLY", Choices: []string{"plugins", "packages", "accounts", "files", "services", "firewall", "compose", "repos", "dotfiles", "mise-shell-activate", "shell", "macos-defaults", "defaults", "macos-launchd-agents", "launchd", "linux-systemd-units", "systemd", "user", "tools", "task", "final-hook"}}, {Key: FlagBootstrapPromptSecrets, Name: "prompt-secrets", Flag: true, Spelling: "--prompt-secrets"}, - {Key: FlagBootstrapSkip, Name: "skip", Flag: true, Spelling: "--skip", Choices: []string{"plugins", "packages", "accounts", "files", "services", "firewall", "compose", "repos", "dotfiles", "mise-shell-activate", "shell", "macos-defaults", "defaults", "macos-launchd-agents", "launchd", "linux-systemd-units", "systemd", "user", "tools", "task", "final-hook"}}, + {Key: FlagBootstrapSkip, Name: "skip", Flag: true, Spelling: "--skip", ValueName: "SKIP", Choices: []string{"plugins", "packages", "accounts", "files", "services", "firewall", "compose", "repos", "dotfiles", "mise-shell-activate", "shell", "macos-defaults", "defaults", "macos-launchd-agents", "launchd", "linux-systemd-units", "systemd", "user", "tools", "task", "final-hook"}}, {Key: FlagBootstrapUpdate, Name: "update", Flag: true, Spelling: "--update"}, {}, {}, @@ -3876,11 +3876,11 @@ var Meta = argv.Metadata{ {Key: FlagBootstrapDotfilesAddForce, Name: "force", Flag: true, Spelling: "--force"}, {Key: FlagBootstrapDotfilesAddGlobal, Name: "global", Flag: true, Spelling: "--global"}, {Key: FlagBootstrapDotfilesAddLocal, Name: "local", Flag: true, Spelling: "--local"}, - {Key: FlagBootstrapDotfilesAddMode, Name: "mode", Flag: true, Spelling: "--mode"}, + {Key: FlagBootstrapDotfilesAddMode, Name: "mode", Flag: true, Spelling: "--mode", ValueName: "MODE"}, {Key: FlagBootstrapDotfilesAddDryRun, Name: "dry-run", Flag: true, Spelling: "--dry-run"}, {Key: FlagBootstrapDotfilesAddNoApply, Name: "no-apply", Flag: true, Spelling: "--no-apply"}, - {Key: FlagBootstrapDotfilesAddPath, Name: "path", Flag: true, Spelling: "--path"}, - {Key: FlagBootstrapDotfilesAddSource, Name: "source", Flag: true, Spelling: "--source"}, + {Key: FlagBootstrapDotfilesAddPath, Name: "path", Flag: true, Spelling: "--path", ValueName: "PATH"}, + {Key: FlagBootstrapDotfilesAddSource, Name: "source", Flag: true, Spelling: "--source", ValueName: "PATH"}, {Key: FlagBootstrapDotfilesAddYes, Name: "yes", Flag: true, Spelling: "--yes"}, {Key: ArgBootstrapDotfilesAddTarget, Name: "TARGET", Required: true}, {}, @@ -3890,8 +3890,8 @@ var Meta = argv.Metadata{ {Key: ArgBootstrapDotfilesApplyTarget, Name: "TARGET"}, {}, {Key: FlagBootstrapDotfilesEditApply, Name: "apply", Flag: true, Spelling: "--apply"}, - {Key: FlagBootstrapDotfilesEditMode, Name: "mode", Flag: true, Spelling: "--mode"}, - {Key: FlagBootstrapDotfilesEditSource, Name: "source", Flag: true, Spelling: "--source"}, + {Key: FlagBootstrapDotfilesEditMode, Name: "mode", Flag: true, Spelling: "--mode", ValueName: "MODE"}, + {Key: FlagBootstrapDotfilesEditSource, Name: "source", Flag: true, Spelling: "--source", ValueName: "PATH"}, {Key: FlagBootstrapDotfilesEditYes, Name: "yes", Flag: true, Spelling: "--yes"}, {Key: ArgBootstrapDotfilesEditTarget, Name: "TARGET", Required: true}, {}, @@ -3965,7 +3965,7 @@ var Meta = argv.Metadata{ {Key: FlagBootstrapMiseShellActivateStatusMissing, Name: "missing", Flag: true, Spelling: "--missing"}, {}, {}, - {Key: FlagBootstrapPackagesApplyManager, Name: "manager", Flag: true, Spelling: "--manager"}, + {Key: FlagBootstrapPackagesApplyManager, Name: "manager", Flag: true, Spelling: "--manager", ValueName: "MANAGER"}, {Key: FlagBootstrapPackagesApplyDryRun, Name: "dry-run", Flag: true, Spelling: "--dry-run"}, {Key: FlagBootstrapPackagesApplyYes, Name: "yes", Flag: true, Spelling: "--yes"}, {Key: FlagBootstrapPackagesApplyUpdate, Name: "update", Flag: true, Spelling: "--update"}, @@ -3974,38 +3974,38 @@ var Meta = argv.Metadata{ {}, {Key: FlagBootstrapPackagesBrewTapLocal, Name: "local", Flag: true, Spelling: "--local"}, {Key: FlagBootstrapPackagesBrewTapDryRun, Name: "dry-run", Flag: true, Spelling: "--dry-run"}, - {Key: FlagBootstrapPackagesBrewTapPath, Name: "path", Flag: true, Spelling: "--path"}, + {Key: FlagBootstrapPackagesBrewTapPath, Name: "path", Flag: true, Spelling: "--path", ValueName: "PATH"}, {Key: ArgBootstrapPackagesBrewTapTap, Name: "TAP", Required: true}, {Key: ArgBootstrapPackagesBrewTapUrl, Name: "URL"}, {}, {Key: FlagBootstrapPackagesBrewUntapLocal, Name: "local", Flag: true, Spelling: "--local"}, {Key: FlagBootstrapPackagesBrewUntapDryRun, Name: "dry-run", Flag: true, Spelling: "--dry-run"}, - {Key: FlagBootstrapPackagesBrewUntapPath, Name: "path", Flag: true, Spelling: "--path"}, + {Key: FlagBootstrapPackagesBrewUntapPath, Name: "path", Flag: true, Spelling: "--path", ValueName: "PATH"}, {Key: ArgBootstrapPackagesBrewUntapTaps, Name: "TAPS", Required: true}, {}, - {Key: FlagBootstrapPackagesImportEnv, Name: "env", Flag: true, Spelling: "--env"}, + {Key: FlagBootstrapPackagesImportEnv, Name: "env", Flag: true, Spelling: "--env", ValueName: "ENV"}, {Key: FlagBootstrapPackagesImportGlobal, Name: "global", Flag: true, Spelling: "--global"}, - {Key: FlagBootstrapPackagesImportManager, Name: "manager", Flag: true, Spelling: "--manager", Choices: []string{"brew"}, Default: []string{"brew"}}, + {Key: FlagBootstrapPackagesImportManager, Name: "manager", Flag: true, Spelling: "--manager", ValueName: "MANAGER", Choices: []string{"brew"}, Default: []string{"brew"}}, {Key: FlagBootstrapPackagesImportAll, Name: "all", Flag: true, Spelling: "--all"}, {Key: FlagBootstrapPackagesImportDryRun, Name: "dry-run", Flag: true, Spelling: "--dry-run"}, - {Key: FlagBootstrapPackagesImportPath, Name: "path", Flag: true, Spelling: "--path"}, + {Key: FlagBootstrapPackagesImportPath, Name: "path", Flag: true, Spelling: "--path", ValueName: "PATH"}, {}, - {Key: FlagBootstrapPackagesPruneManager, Name: "manager", Flag: true, Spelling: "--manager", Choices: []string{"brew"}, Default: []string{"brew"}}, + {Key: FlagBootstrapPackagesPruneManager, Name: "manager", Flag: true, Spelling: "--manager", ValueName: "MANAGER", Choices: []string{"brew"}, Default: []string{"brew"}}, {Key: FlagBootstrapPackagesPruneDryRun, Name: "dry-run", Flag: true, Spelling: "--dry-run"}, {Key: FlagBootstrapPackagesPruneYes, Name: "yes", Flag: true, Spelling: "--yes"}, {}, {Key: FlagBootstrapPackagesStatusJson, Name: "json", Flag: true, Spelling: "--json"}, {Key: FlagBootstrapPackagesStatusMissing, Name: "missing", Flag: true, Spelling: "--missing"}, {}, - {Key: FlagBootstrapPackagesUpgradeManager, Name: "manager", Flag: true, Spelling: "--manager"}, + {Key: FlagBootstrapPackagesUpgradeManager, Name: "manager", Flag: true, Spelling: "--manager", ValueName: "MANAGER"}, {Key: FlagBootstrapPackagesUpgradeDryRun, Name: "dry-run", Flag: true, Spelling: "--dry-run"}, {Key: FlagBootstrapPackagesUpgradeYes, Name: "yes", Flag: true, Spelling: "--yes"}, {Key: ArgBootstrapPackagesUpgradePackage, Name: "PACKAGE"}, {}, - {Key: FlagBootstrapPackagesUseEnv, Name: "env", Flag: true, Spelling: "--env"}, + {Key: FlagBootstrapPackagesUseEnv, Name: "env", Flag: true, Spelling: "--env", ValueName: "ENV"}, {Key: FlagBootstrapPackagesUseGlobal, Name: "global", Flag: true, Spelling: "--global"}, {Key: FlagBootstrapPackagesUseDryRun, Name: "dry-run", Flag: true, Spelling: "--dry-run"}, - {Key: FlagBootstrapPackagesUsePath, Name: "path", Flag: true, Spelling: "--path"}, + {Key: FlagBootstrapPackagesUsePath, Name: "path", Flag: true, Spelling: "--path", ValueName: "PATH"}, {Key: FlagBootstrapPackagesUseYes, Name: "yes", Flag: true, Spelling: "--yes"}, {Key: ArgBootstrapPackagesUsePackage, Name: "PACKAGE", Required: true}, {}, @@ -4019,24 +4019,24 @@ var Meta = argv.Metadata{ {Key: FlagBootstrapPluginsStatusMissing, Name: "missing", Flag: true, Spelling: "--missing"}, {}, {Key: FlagBootstrapRemoteAll, Name: "all", Flag: true, Spelling: "--all"}, - {Key: FlagBootstrapRemoteBootstrapCommand, Name: "bootstrap-command", Flag: true, Spelling: "--bootstrap-command"}, - {Key: FlagBootstrapRemoteConnectTimeout, Name: "connect-timeout", Flag: true, Spelling: "--connect-timeout", Default: []string{"10"}}, - {Key: FlagBootstrapRemoteExclude, Name: "exclude", Flag: true, Spelling: "--exclude"}, + {Key: FlagBootstrapRemoteBootstrapCommand, Name: "bootstrap-command", Flag: true, Spelling: "--bootstrap-command", ValueName: "COMMAND"}, + {Key: FlagBootstrapRemoteConnectTimeout, Name: "connect-timeout", Flag: true, Spelling: "--connect-timeout", ValueName: "CONNECT_TIMEOUT", Default: []string{"10"}}, + {Key: FlagBootstrapRemoteExclude, Name: "exclude", Flag: true, Spelling: "--exclude", ValueName: "PATTERN"}, {Key: FlagBootstrapRemoteFailFast, Name: "fail-fast", Flag: true, Spelling: "--fail-fast"}, {Key: FlagBootstrapRemoteForceDotfiles, Name: "force-dotfiles", Flag: true, Spelling: "--force-dotfiles"}, - {Key: FlagBootstrapRemoteHost, Name: "host", Flag: true, Spelling: "--host"}, - {Key: FlagBootstrapRemoteIdentityFile, Name: "identity-file", Flag: true, Spelling: "--identity-file"}, + {Key: FlagBootstrapRemoteHost, Name: "host", Flag: true, Spelling: "--host", ValueName: "[USER@]HOST"}, + {Key: FlagBootstrapRemoteIdentityFile, Name: "identity-file", Flag: true, Spelling: "--identity-file", ValueName: "IDENTITY_FILE"}, {Key: FlagBootstrapRemoteDryRun, Name: "dry-run", Flag: true, Spelling: "--dry-run"}, {Key: FlagBootstrapRemoteKeepStaging, Name: "keep-staging", Flag: true, Spelling: "--keep-staging"}, - {Key: FlagBootstrapRemoteMiseBin, Name: "mise-bin", Flag: true, Spelling: "--mise-bin"}, - {Key: FlagBootstrapRemoteOnly, Name: "only", Flag: true, Spelling: "--only", Choices: []string{"plugins", "packages", "accounts", "files", "services", "firewall", "compose", "repos", "dotfiles", "mise-shell-activate", "shell", "macos-defaults", "defaults", "macos-launchd-agents", "launchd", "linux-systemd-units", "systemd", "user", "tools", "task", "final-hook"}}, - {Key: FlagBootstrapRemotePort, Name: "port", Flag: true, Spelling: "--port"}, + {Key: FlagBootstrapRemoteMiseBin, Name: "mise-bin", Flag: true, Spelling: "--mise-bin", ValueName: "MISE_BIN"}, + {Key: FlagBootstrapRemoteOnly, Name: "only", Flag: true, Spelling: "--only", ValueName: "ONLY", Choices: []string{"plugins", "packages", "accounts", "files", "services", "firewall", "compose", "repos", "dotfiles", "mise-shell-activate", "shell", "macos-defaults", "defaults", "macos-launchd-agents", "launchd", "linux-systemd-units", "systemd", "user", "tools", "task", "final-hook"}}, + {Key: FlagBootstrapRemotePort, Name: "port", Flag: true, Spelling: "--port", ValueName: "PORT"}, {Key: FlagBootstrapRemotePromptSecrets, Name: "prompt-secrets", Flag: true, Spelling: "--prompt-secrets"}, - {Key: FlagBootstrapRemoteRemoteMise, Name: "remote-mise", Flag: true, Spelling: "--remote-mise"}, - {Key: FlagBootstrapRemoteSkip, Name: "skip", Flag: true, Spelling: "--skip", Choices: []string{"plugins", "packages", "accounts", "files", "services", "firewall", "compose", "repos", "dotfiles", "mise-shell-activate", "shell", "macos-defaults", "defaults", "macos-launchd-agents", "launchd", "linux-systemd-units", "systemd", "user", "tools", "task", "final-hook"}}, - {Key: FlagBootstrapRemoteSource, Name: "source", Flag: true, Spelling: "--source"}, - {Key: FlagBootstrapRemoteSshOption, Name: "ssh-option", Flag: true, Spelling: "--ssh-option"}, - {Key: FlagBootstrapRemoteTag, Name: "tag", Flag: true, Spelling: "--tag"}, + {Key: FlagBootstrapRemoteRemoteMise, Name: "remote-mise", Flag: true, Spelling: "--remote-mise", ValueName: "COMMAND"}, + {Key: FlagBootstrapRemoteSkip, Name: "skip", Flag: true, Spelling: "--skip", ValueName: "SKIP", Choices: []string{"plugins", "packages", "accounts", "files", "services", "firewall", "compose", "repos", "dotfiles", "mise-shell-activate", "shell", "macos-defaults", "defaults", "macos-launchd-agents", "launchd", "linux-systemd-units", "systemd", "user", "tools", "task", "final-hook"}}, + {Key: FlagBootstrapRemoteSource, Name: "source", Flag: true, Spelling: "--source", ValueName: "SOURCE"}, + {Key: FlagBootstrapRemoteSshOption, Name: "ssh-option", Flag: true, Spelling: "--ssh-option", ValueName: "OPTION"}, + {Key: FlagBootstrapRemoteTag, Name: "tag", Flag: true, Spelling: "--tag", ValueName: "TAG"}, {Key: FlagBootstrapRemoteUpdate, Name: "update", Flag: true, Spelling: "--update"}, {Key: FlagBootstrapRemoteYes, Name: "yes", Flag: true, Spelling: "--yes"}, {Key: ArgBootstrapRemoteTarget, Name: "TARGET"}, @@ -4088,7 +4088,7 @@ var Meta = argv.Metadata{ {}, {}, {Key: FlagCacheClearOutdate, Name: "outdate", Flag: true, Spelling: "--outdate"}, - {Key: FlagCacheClearTask, Name: "task", Flag: true, Spelling: "--task"}, + {Key: FlagCacheClearTask, Name: "task", Flag: true, Spelling: "--task", ValueName: "TASK"}, {Key: ArgCacheClearTool, Name: "TOOL"}, {}, {}, @@ -4099,7 +4099,7 @@ var Meta = argv.Metadata{ {Key: FlagCacheTaskJson, Name: "json", Flag: true, Spelling: "--json"}, {Key: ArgCacheTaskTask, Name: "TASK", Required: true}, {}, - {Key: FlagCompletionShell, Name: "shell", Flag: true, Spelling: "--shell", Choices: []string{"bash", "fish", "powershell", "zsh"}}, + {Key: FlagCompletionShell, Name: "shell", Flag: true, Spelling: "--shell", ValueName: "SHELL_TYPE", Choices: []string{"bash", "fish", "powershell", "zsh"}}, {Key: FlagCompletionIncludeBashCompletionLib, Name: "include-bash-completion-lib", Flag: true, Spelling: "--include-bash-completion-lib"}, {Key: FlagCompletionUsage, Name: "usage", Flag: true, Spelling: "--usage"}, {Key: ArgCompletionShell, Name: "SHELL", Choices: []string{"bash", "fish", "powershell", "zsh"}}, @@ -4108,15 +4108,15 @@ var Meta = argv.Metadata{ {Key: FlagConfigNoHeader, Name: "no-header", Flag: true, Spelling: "--no-header"}, {Key: FlagConfigTrackedConfigs, Name: "tracked-configs", Flag: true, Spelling: "--tracked-configs"}, {}, - {Key: FlagConfigGetFile, Name: "file", Flag: true, Spelling: "--file"}, + {Key: FlagConfigGetFile, Name: "file", Flag: true, Spelling: "--file", ValueName: "FILE"}, {Key: ArgConfigGetKey, Name: "KEY"}, {}, {Key: FlagConfigLsJson, Name: "json", Flag: true, Spelling: "--json"}, {Key: FlagConfigLsNoHeader, Name: "no-header", Flag: true, Spelling: "--no-header"}, {Key: FlagConfigLsTrackedConfigs, Name: "tracked-configs", Flag: true, Spelling: "--tracked-configs"}, {}, - {Key: FlagConfigSetFile, Name: "file", Flag: true, Spelling: "--file"}, - {Key: FlagConfigSetType, Name: "type", Flag: true, Spelling: "--type", Choices: []string{"infer", "string", "integer", "float", "bool", "list", "set"}, Default: []string{"infer"}}, + {Key: FlagConfigSetFile, Name: "file", Flag: true, Spelling: "--file", ValueName: "FILE"}, + {Key: FlagConfigSetType, Name: "type", Flag: true, Spelling: "--type", ValueName: "TYPE", Choices: []string{"infer", "string", "integer", "float", "bool", "list", "set"}, Default: []string{"infer"}}, {Key: ArgConfigSetKey, Name: "KEY", Required: true}, {Key: ArgConfigSetValue, Name: "VALUE"}, {}, @@ -4131,11 +4131,11 @@ var Meta = argv.Metadata{ {Key: FlagDotfilesAddForce, Name: "force", Flag: true, Spelling: "--force"}, {Key: FlagDotfilesAddGlobal, Name: "global", Flag: true, Spelling: "--global"}, {Key: FlagDotfilesAddLocal, Name: "local", Flag: true, Spelling: "--local"}, - {Key: FlagDotfilesAddMode, Name: "mode", Flag: true, Spelling: "--mode"}, + {Key: FlagDotfilesAddMode, Name: "mode", Flag: true, Spelling: "--mode", ValueName: "MODE"}, {Key: FlagDotfilesAddDryRun, Name: "dry-run", Flag: true, Spelling: "--dry-run"}, {Key: FlagDotfilesAddNoApply, Name: "no-apply", Flag: true, Spelling: "--no-apply"}, - {Key: FlagDotfilesAddPath, Name: "path", Flag: true, Spelling: "--path"}, - {Key: FlagDotfilesAddSource, Name: "source", Flag: true, Spelling: "--source"}, + {Key: FlagDotfilesAddPath, Name: "path", Flag: true, Spelling: "--path", ValueName: "PATH"}, + {Key: FlagDotfilesAddSource, Name: "source", Flag: true, Spelling: "--source", ValueName: "PATH"}, {Key: FlagDotfilesAddYes, Name: "yes", Flag: true, Spelling: "--yes"}, {Key: ArgDotfilesAddTarget, Name: "TARGET", Required: true}, {}, @@ -4145,8 +4145,8 @@ var Meta = argv.Metadata{ {Key: ArgDotfilesApplyTarget, Name: "TARGET"}, {}, {Key: FlagDotfilesEditApply, Name: "apply", Flag: true, Spelling: "--apply"}, - {Key: FlagDotfilesEditMode, Name: "mode", Flag: true, Spelling: "--mode"}, - {Key: FlagDotfilesEditSource, Name: "source", Flag: true, Spelling: "--source"}, + {Key: FlagDotfilesEditMode, Name: "mode", Flag: true, Spelling: "--mode", ValueName: "MODE"}, + {Key: FlagDotfilesEditSource, Name: "source", Flag: true, Spelling: "--source", ValueName: "PATH"}, {Key: FlagDotfilesEditYes, Name: "yes", Flag: true, Spelling: "--yes"}, {Key: ArgDotfilesEditTarget, Name: "TARGET", Required: true}, {}, @@ -4163,23 +4163,23 @@ var Meta = argv.Metadata{ {}, {Key: FlagDoctorPathFull, Name: "full", Flag: true, Spelling: "--full"}, {}, - {Key: FlagEnShell, Name: "shell", Flag: true, Spelling: "--shell"}, + {Key: FlagEnShell, Name: "shell", Flag: true, Spelling: "--shell", ValueName: "SHELL"}, {Key: ArgEnDir, Name: "DIR", Default: []string{"."}}, {}, {Key: FlagEnvDotenv, Name: "dotenv", Flag: true, Spelling: "--dotenv"}, {Key: FlagEnvJson, Name: "json", Flag: true, Spelling: "--json"}, - {Key: FlagEnvShell, Name: "shell", Flag: true, Spelling: "--shell", Choices: []string{"bash", "elvish", "fish", "nu", "xonsh", "zsh", "pwsh"}}, + {Key: FlagEnvShell, Name: "shell", Flag: true, Spelling: "--shell", ValueName: "SHELL", Choices: []string{"bash", "elvish", "fish", "nu", "xonsh", "zsh", "pwsh"}}, {Key: FlagEnvJsonExtended, Name: "json-extended", Flag: true, Spelling: "--json-extended"}, {Key: FlagEnvRedacted, Name: "redacted", Flag: true, Spelling: "--redacted"}, {Key: FlagEnvValues, Name: "values", Flag: true, Spelling: "--values"}, {Key: ArgEnvToolVersion, Name: "TOOL@VERSION"}, {}, - {Key: FlagExecCommand, Name: "command", Flag: true, Spelling: "--command"}, - {Key: FlagExecJobs, Name: "jobs", Flag: true, Spelling: "--jobs"}, - {Key: FlagExecAllowEnv, Name: "allow-env", Flag: true, Spelling: "--allow-env"}, - {Key: FlagExecAllowNet, Name: "allow-net", Flag: true, Spelling: "--allow-net"}, - {Key: FlagExecAllowRead, Name: "allow-read", Flag: true, Spelling: "--allow-read"}, - {Key: FlagExecAllowWrite, Name: "allow-write", Flag: true, Spelling: "--allow-write"}, + {Key: FlagExecCommand, Name: "command", Flag: true, Spelling: "--command", ValueName: "C"}, + {Key: FlagExecJobs, Name: "jobs", Flag: true, Spelling: "--jobs", ValueName: "JOBS"}, + {Key: FlagExecAllowEnv, Name: "allow-env", Flag: true, Spelling: "--allow-env", ValueName: "VAR"}, + {Key: FlagExecAllowNet, Name: "allow-net", Flag: true, Spelling: "--allow-net", ValueName: "HOST"}, + {Key: FlagExecAllowRead, Name: "allow-read", Flag: true, Spelling: "--allow-read", ValueName: "PATH"}, + {Key: FlagExecAllowWrite, Name: "allow-write", Flag: true, Spelling: "--allow-write", ValueName: "PATH"}, {Key: FlagExecDenyAll, Name: "deny-all", Flag: true, Spelling: "--deny-all"}, {Key: FlagExecDenyEnv, Name: "deny-env", Flag: true, Spelling: "--deny-env"}, {Key: FlagExecDenyNet, Name: "deny-net", Flag: true, Spelling: "--deny-net"}, @@ -4197,49 +4197,49 @@ var Meta = argv.Metadata{ {}, {}, {Key: FlagGenerateBootstrapLocalize, Name: "localize", Flag: true, Spelling: "--localize"}, - {Key: FlagGenerateBootstrapVersion, Name: "version", Flag: true, Spelling: "--version"}, - {Key: FlagGenerateBootstrapWrite, Name: "write", Flag: true, Spelling: "--write"}, - {Key: FlagGenerateBootstrapLocalizedDir, Name: "localized-dir", Flag: true, Spelling: "--localized-dir", Default: []string{".mise"}}, + {Key: FlagGenerateBootstrapVersion, Name: "version", Flag: true, Spelling: "--version", ValueName: "VERSION"}, + {Key: FlagGenerateBootstrapWrite, Name: "write", Flag: true, Spelling: "--write", ValueName: "WRITE"}, + {Key: FlagGenerateBootstrapLocalizedDir, Name: "localized-dir", Flag: true, Spelling: "--localized-dir", ValueName: "LOCALIZED_DIR", Default: []string{".mise"}}, {}, {Key: FlagGenerateConfigGlobal, Name: "global", Flag: true, Spelling: "--global"}, {Key: FlagGenerateConfigDryRun, Name: "dry-run", Flag: true, Spelling: "--dry-run"}, - {Key: FlagGenerateConfigToolVersions, Name: "tool-versions", Flag: true, Spelling: "--tool-versions"}, + {Key: FlagGenerateConfigToolVersions, Name: "tool-versions", Flag: true, Spelling: "--tool-versions", ValueName: "TOOL_VERSIONS"}, {Key: ArgGenerateConfigPath, Name: "PATH"}, {}, - {Key: FlagGenerateDevcontainerImage, Name: "image", Flag: true, Spelling: "--image"}, + {Key: FlagGenerateDevcontainerImage, Name: "image", Flag: true, Spelling: "--image", ValueName: "IMAGE"}, {Key: FlagGenerateDevcontainerMountMiseData, Name: "mount-mise-data", Flag: true, Spelling: "--mount-mise-data"}, - {Key: FlagGenerateDevcontainerName, Name: "name", Flag: true, Spelling: "--name"}, + {Key: FlagGenerateDevcontainerName, Name: "name", Flag: true, Spelling: "--name", ValueName: "NAME"}, {Key: FlagGenerateDevcontainerWrite, Name: "write", Flag: true, Spelling: "--write"}, {}, - {Key: FlagGenerateGitPreCommitTask, Name: "task", Flag: true, Spelling: "--task", Default: []string{"pre-commit"}}, + {Key: FlagGenerateGitPreCommitTask, Name: "task", Flag: true, Spelling: "--task", ValueName: "TASK", Default: []string{"pre-commit"}}, {Key: FlagGenerateGitPreCommitWrite, Name: "write", Flag: true, Spelling: "--write"}, - {Key: FlagGenerateGitPreCommitHook, Name: "hook", Flag: true, Spelling: "--hook", Default: []string{"pre-commit"}}, + {Key: FlagGenerateGitPreCommitHook, Name: "hook", Flag: true, Spelling: "--hook", ValueName: "HOOK", Default: []string{"pre-commit"}}, {}, - {Key: FlagGenerateGithubActionTask, Name: "task", Flag: true, Spelling: "--task", Default: []string{"ci"}}, + {Key: FlagGenerateGithubActionTask, Name: "task", Flag: true, Spelling: "--task", ValueName: "TASK", Default: []string{"ci"}}, {Key: FlagGenerateGithubActionWrite, Name: "write", Flag: true, Spelling: "--write"}, - {Key: FlagGenerateGithubActionName, Name: "name", Flag: true, Spelling: "--name", Default: []string{"ci"}}, + {Key: FlagGenerateGithubActionName, Name: "name", Flag: true, Spelling: "--name", ValueName: "NAME", Default: []string{"ci"}}, {}, {Key: FlagGenerateTaskDocsInject, Name: "inject", Flag: true, Spelling: "--inject"}, {Key: FlagGenerateTaskDocsIndex, Name: "index", Flag: true, Spelling: "--index"}, {Key: FlagGenerateTaskDocsMulti, Name: "multi", Flag: true, Spelling: "--multi"}, - {Key: FlagGenerateTaskDocsOutput, Name: "output", Flag: true, Spelling: "--output"}, - {Key: FlagGenerateTaskDocsRoot, Name: "root", Flag: true, Spelling: "--root"}, - {Key: FlagGenerateTaskDocsStyle, Name: "style", Flag: true, Spelling: "--style", Choices: []string{"simple", "detailed"}, Default: []string{"simple"}}, + {Key: FlagGenerateTaskDocsOutput, Name: "output", Flag: true, Spelling: "--output", ValueName: "OUTPUT"}, + {Key: FlagGenerateTaskDocsRoot, Name: "root", Flag: true, Spelling: "--root", ValueName: "ROOT"}, + {Key: FlagGenerateTaskDocsStyle, Name: "style", Flag: true, Spelling: "--style", ValueName: "STYLE", Choices: []string{"simple", "detailed"}, Default: []string{"simple"}}, {}, - {Key: FlagGenerateTaskStubsDir, Name: "dir", Flag: true, Spelling: "--dir", Default: []string{"bin"}}, - {Key: FlagGenerateTaskStubsMiseBin, Name: "mise-bin", Flag: true, Spelling: "--mise-bin", Default: []string{"mise"}}, + {Key: FlagGenerateTaskStubsDir, Name: "dir", Flag: true, Spelling: "--dir", ValueName: "DIR", Default: []string{"bin"}}, + {Key: FlagGenerateTaskStubsMiseBin, Name: "mise-bin", Flag: true, Spelling: "--mise-bin", ValueName: "MISE_BIN", Default: []string{"mise"}}, {}, - {Key: FlagGenerateToolStubBin, Name: "bin", Flag: true, Spelling: "--bin"}, + {Key: FlagGenerateToolStubBin, Name: "bin", Flag: true, Spelling: "--bin", ValueName: "BIN"}, {Key: FlagGenerateToolStubBootstrap, Name: "bootstrap", Flag: true, Spelling: "--bootstrap"}, - {Key: FlagGenerateToolStubBootstrapVersion, Name: "bootstrap-version", Flag: true, Spelling: "--bootstrap-version"}, + {Key: FlagGenerateToolStubBootstrapVersion, Name: "bootstrap-version", Flag: true, Spelling: "--bootstrap-version", ValueName: "BOOTSTRAP_VERSION"}, {Key: FlagGenerateToolStubFetch, Name: "fetch", Flag: true, Spelling: "--fetch"}, - {Key: FlagGenerateToolStubHttp, Name: "http", Flag: true, Spelling: "--http", Default: []string{"http"}}, + {Key: FlagGenerateToolStubHttp, Name: "http", Flag: true, Spelling: "--http", ValueName: "HTTP", Default: []string{"http"}}, {Key: FlagGenerateToolStubLock, Name: "lock", Flag: true, Spelling: "--lock"}, - {Key: FlagGenerateToolStubPlatformBin, Name: "platform-bin", Flag: true, Spelling: "--platform-bin"}, - {Key: FlagGenerateToolStubPlatformUrl, Name: "platform-url", Flag: true, Spelling: "--platform-url"}, + {Key: FlagGenerateToolStubPlatformBin, Name: "platform-bin", Flag: true, Spelling: "--platform-bin", ValueName: "PLATFORM_BIN"}, + {Key: FlagGenerateToolStubPlatformUrl, Name: "platform-url", Flag: true, Spelling: "--platform-url", ValueName: "PLATFORM_URL"}, {Key: FlagGenerateToolStubSkipDownload, Name: "skip-download", Flag: true, Spelling: "--skip-download"}, - {Key: FlagGenerateToolStubUrl, Name: "url", Flag: true, Spelling: "--url"}, - {Key: FlagGenerateToolStubVersion, Name: "version", Flag: true, Spelling: "--version", Default: []string{"latest"}}, + {Key: FlagGenerateToolStubUrl, Name: "url", Flag: true, Spelling: "--url", ValueName: "URL"}, + {Key: FlagGenerateToolStubVersion, Name: "version", Flag: true, Spelling: "--version", ValueName: "VERSION", Default: []string{"latest"}}, {Key: ArgGenerateToolStubOutput, Name: "OUTPUT", Required: true}, {}, {}, @@ -4252,16 +4252,16 @@ var Meta = argv.Metadata{ {Key: FlagGlobalFuzzy, Name: "fuzzy", Flag: true, Spelling: "--fuzzy"}, {Key: FlagGlobalPath, Name: "path", Flag: true, Spelling: "--path"}, {Key: FlagGlobalPin, Name: "pin", Flag: true, Spelling: "--pin"}, - {Key: FlagGlobalRemove, Name: "remove", Flag: true, Spelling: "--remove"}, + {Key: FlagGlobalRemove, Name: "remove", Flag: true, Spelling: "--remove", ValueName: "TOOL"}, {Key: ArgGlobalToolVersion, Name: "TOOL@VERSION"}, {}, {Key: FlagHookEnvForce, Name: "force", Flag: true, Spelling: "--force"}, {Key: FlagHookEnvQuiet, Name: "quiet", Flag: true, Spelling: "--quiet"}, - {Key: FlagHookEnvShell, Name: "shell", Flag: true, Spelling: "--shell", Choices: []string{"bash", "elvish", "fish", "nu", "xonsh", "zsh", "pwsh"}}, - {Key: FlagHookEnvReason, Name: "reason", Flag: true, Spelling: "--reason", Choices: []string{"precmd", "chpwd"}}, + {Key: FlagHookEnvShell, Name: "shell", Flag: true, Spelling: "--shell", ValueName: "SHELL", Choices: []string{"bash", "elvish", "fish", "nu", "xonsh", "zsh", "pwsh"}}, + {Key: FlagHookEnvReason, Name: "reason", Flag: true, Spelling: "--reason", ValueName: "REASON", Choices: []string{"precmd", "chpwd"}}, {Key: FlagHookEnvStatus, Name: "status", Flag: true, Spelling: "--status"}, {}, - {Key: FlagHookNotFoundShell, Name: "shell", Flag: true, Spelling: "--shell", Choices: []string{"bash", "elvish", "fish", "nu", "xonsh", "zsh", "pwsh"}}, + {Key: FlagHookNotFoundShell, Name: "shell", Flag: true, Spelling: "--shell", ValueName: "SHELL", Choices: []string{"bash", "elvish", "fish", "nu", "xonsh", "zsh", "pwsh"}}, {Key: ArgHookNotFoundBin, Name: "BIN", Required: true}, {}, {Key: FlagImplodeDryRun, Name: "dry-run", Flag: true, Spelling: "--dry-run"}, @@ -4269,18 +4269,18 @@ var Meta = argv.Metadata{ {}, {Key: FlagEditGlobal, Name: "global", Flag: true, Spelling: "--global"}, {Key: FlagEditDryRun, Name: "dry-run", Flag: true, Spelling: "--dry-run"}, - {Key: FlagEditToolVersions, Name: "tool-versions", Flag: true, Spelling: "--tool-versions"}, + {Key: FlagEditToolVersions, Name: "tool-versions", Flag: true, Spelling: "--tool-versions", ValueName: "TOOL_VERSIONS"}, {Key: ArgEditPath, Name: "PATH"}, {}, {Key: FlagInstallForce, Name: "force", Flag: true, Spelling: "--force"}, - {Key: FlagInstallJobs, Name: "jobs", Flag: true, Spelling: "--jobs"}, + {Key: FlagInstallJobs, Name: "jobs", Flag: true, Spelling: "--jobs", ValueName: "JOBS"}, {Key: FlagInstallDryRun, Name: "dry-run", Flag: true, Spelling: "--dry-run"}, {Key: FlagInstallVerbose, Name: "verbose", Flag: true, Spelling: "--verbose"}, {Key: FlagInstallDryRunCode, Name: "dry-run-code", Flag: true, Spelling: "--dry-run-code"}, - {Key: FlagInstallMinimumReleaseAge, Name: "minimum-release-age", Flag: true, Spelling: "--minimum-release-age"}, + {Key: FlagInstallMinimumReleaseAge, Name: "minimum-release-age", Flag: true, Spelling: "--minimum-release-age", ValueName: "MINIMUM_RELEASE_AGE"}, {Key: FlagInstallMonorepo, Name: "monorepo", Flag: true, Spelling: "--monorepo"}, {Key: FlagInstallRaw, Name: "raw", Flag: true, Spelling: "--raw"}, - {Key: FlagInstallShared, Name: "shared", Flag: true, Spelling: "--shared"}, + {Key: FlagInstallShared, Name: "shared", Flag: true, Spelling: "--shared", ValueName: "SHARED"}, {Key: FlagInstallSystem, Name: "system", Flag: true, Spelling: "--system"}, {Key: ArgInstallToolVersion, Name: "TOOL@VERSION"}, {}, @@ -4288,7 +4288,7 @@ var Meta = argv.Metadata{ {Key: ArgInstallIntoPath, Name: "PATH", Required: true}, {}, {Key: FlagLatestInstalled, Name: "installed", Flag: true, Spelling: "--installed"}, - {Key: FlagLatestMinimumReleaseAge, Name: "minimum-release-age", Flag: true, Spelling: "--minimum-release-age"}, + {Key: FlagLatestMinimumReleaseAge, Name: "minimum-release-age", Flag: true, Spelling: "--minimum-release-age", ValueName: "MINIMUM_RELEASE_AGE"}, {Key: ArgLatestToolVersion, Name: "TOOL@VERSION", Required: true}, {Key: ArgLatestAsdfVersion, Name: "ASDF_VERSION"}, {}, @@ -4300,17 +4300,17 @@ var Meta = argv.Metadata{ {Key: FlagLocalFuzzy, Name: "fuzzy", Flag: true, Spelling: "--fuzzy"}, {Key: FlagLocalPath, Name: "path", Flag: true, Spelling: "--path"}, {Key: FlagLocalPin, Name: "pin", Flag: true, Spelling: "--pin"}, - {Key: FlagLocalRemove, Name: "remove", Flag: true, Spelling: "--remove"}, + {Key: FlagLocalRemove, Name: "remove", Flag: true, Spelling: "--remove", ValueName: "TOOL"}, {Key: ArgLocalToolVersion, Name: "TOOL@VERSION"}, {}, {Key: FlagLockGlobal, Name: "global", Flag: true, Spelling: "--global"}, - {Key: FlagLockJobs, Name: "jobs", Flag: true, Spelling: "--jobs"}, + {Key: FlagLockJobs, Name: "jobs", Flag: true, Spelling: "--jobs", ValueName: "JOBS"}, {Key: FlagLockDryRun, Name: "dry-run", Flag: true, Spelling: "--dry-run"}, - {Key: FlagLockPlatform, Name: "platform", Flag: true, Spelling: "--platform"}, + {Key: FlagLockPlatform, Name: "platform", Flag: true, Spelling: "--platform", ValueName: "PLATFORM"}, {Key: FlagLockBump, Name: "bump", Flag: true, Spelling: "--bump"}, {Key: FlagLockJson, Name: "json", Flag: true, Spelling: "--json"}, {Key: FlagLockLocal, Name: "local", Flag: true, Spelling: "--local"}, - {Key: FlagLockMinimumReleaseAge, Name: "minimum-release-age", Flag: true, Spelling: "--minimum-release-age"}, + {Key: FlagLockMinimumReleaseAge, Name: "minimum-release-age", Flag: true, Spelling: "--minimum-release-age", ValueName: "MINIMUM_RELEASE_AGE"}, {Key: ArgLockTool, Name: "TOOL"}, {}, {Key: FlagLsCurrent, Name: "current", Flag: true, Spelling: "--current"}, @@ -4320,17 +4320,17 @@ var Meta = argv.Metadata{ {Key: FlagLsLocal, Name: "local", Flag: true, Spelling: "--local"}, {Key: FlagLsMissing, Name: "missing", Flag: true, Spelling: "--missing"}, {Key: FlagLsOffline, Name: "offline", Flag: true, Spelling: "--offline"}, - {Key: FlagLsPlugin, Name: "plugin", Flag: true, Spelling: "--plugin"}, + {Key: FlagLsPlugin, Name: "plugin", Flag: true, Spelling: "--plugin", ValueName: "TOOL_FLAG"}, {Key: FlagLsAllSources, Name: "all-sources", Flag: true, Spelling: "--all-sources"}, {Key: FlagLsMonorepo, Name: "monorepo", Flag: true, Spelling: "--monorepo"}, {Key: FlagLsNoHeader, Name: "no-header", Flag: true, Spelling: "--no-header"}, {Key: FlagLsOutdated, Name: "outdated", Flag: true, Spelling: "--outdated"}, - {Key: FlagLsPrefix, Name: "prefix", Flag: true, Spelling: "--prefix"}, + {Key: FlagLsPrefix, Name: "prefix", Flag: true, Spelling: "--prefix", ValueName: "PREFIX"}, {Key: FlagLsPrunable, Name: "prunable", Flag: true, Spelling: "--prunable"}, {Key: ArgLsInstalledTool, Name: "INSTALLED_TOOL"}, {}, {Key: FlagLsRemoteAll, Name: "all", Flag: true, Spelling: "--all"}, - {Key: FlagLsRemoteMinimumReleaseAge, Name: "minimum-release-age", Flag: true, Spelling: "--minimum-release-age"}, + {Key: FlagLsRemoteMinimumReleaseAge, Name: "minimum-release-age", Flag: true, Spelling: "--minimum-release-age", ValueName: "MINIMUM_RELEASE_AGE"}, {Key: FlagLsRemoteJson, Name: "json", Flag: true, Spelling: "--json"}, {Key: FlagLsRemoteNoVersionsHost, Name: "no-versions-host", Flag: true, Spelling: "--no-versions-host"}, {Key: FlagLsRemotePrerelease, Name: "prerelease", Flag: true, Spelling: "--prerelease"}, @@ -4340,39 +4340,39 @@ var Meta = argv.Metadata{ {}, {}, {}, - {Key: FlagOciBuildCopy, Name: "copy", Flag: true, Spelling: "--copy"}, - {Key: FlagOciBuildOutput, Name: "output", Flag: true, Spelling: "--output", Default: []string{"./mise-oci"}}, - {Key: FlagOciBuildFrom, Name: "from", Flag: true, Spelling: "--from"}, + {Key: FlagOciBuildCopy, Name: "copy", Flag: true, Spelling: "--copy", ValueName: "HOST_PATH:IMAGE_PATH"}, + {Key: FlagOciBuildOutput, Name: "output", Flag: true, Spelling: "--output", ValueName: "OUTPUT", Default: []string{"./mise-oci"}}, + {Key: FlagOciBuildFrom, Name: "from", Flag: true, Spelling: "--from", ValueName: "FROM"}, {Key: FlagOciBuildIncludeGlobal, Name: "include-global", Flag: true, Spelling: "--include-global"}, - {Key: FlagOciBuildTag, Name: "tag", Flag: true, Spelling: "--tag"}, - {Key: FlagOciBuildMountPoint, Name: "mount-point", Flag: true, Spelling: "--mount-point"}, + {Key: FlagOciBuildTag, Name: "tag", Flag: true, Spelling: "--tag", ValueName: "TAG"}, + {Key: FlagOciBuildMountPoint, Name: "mount-point", Flag: true, Spelling: "--mount-point", ValueName: "MOUNT_POINT"}, {Key: FlagOciBuildNoMise, Name: "no-mise", Flag: true, Spelling: "--no-mise"}, - {Key: FlagOciBuildOwner, Name: "owner", Flag: true, Spelling: "--owner"}, + {Key: FlagOciBuildOwner, Name: "owner", Flag: true, Spelling: "--owner", ValueName: "UID[:GID]"}, {}, - {Key: FlagOciPushCacheFrom, Name: "cache-from", Flag: true, Spelling: "--cache-from"}, - {Key: FlagOciPushFrom, Name: "from", Flag: true, Spelling: "--from"}, - {Key: FlagOciPushImageDir, Name: "image-dir", Flag: true, Spelling: "--image-dir"}, + {Key: FlagOciPushCacheFrom, Name: "cache-from", Flag: true, Spelling: "--cache-from", ValueName: "REF"}, + {Key: FlagOciPushFrom, Name: "from", Flag: true, Spelling: "--from", ValueName: "FROM"}, + {Key: FlagOciPushImageDir, Name: "image-dir", Flag: true, Spelling: "--image-dir", ValueName: "IMAGE_DIR"}, {Key: FlagOciPushIncludeGlobal, Name: "include-global", Flag: true, Spelling: "--include-global"}, - {Key: FlagOciPushMountPoint, Name: "mount-point", Flag: true, Spelling: "--mount-point"}, + {Key: FlagOciPushMountPoint, Name: "mount-point", Flag: true, Spelling: "--mount-point", ValueName: "MOUNT_POINT"}, {Key: FlagOciPushNoCache, Name: "no-cache", Flag: true, Spelling: "--no-cache"}, {Key: FlagOciPushNoMise, Name: "no-mise", Flag: true, Spelling: "--no-mise"}, - {Key: FlagOciPushOwner, Name: "owner", Flag: true, Spelling: "--owner"}, + {Key: FlagOciPushOwner, Name: "owner", Flag: true, Spelling: "--owner", ValueName: "UID[:GID]"}, {Key: FlagOciPushUpdateIndex, Name: "update-index", Flag: true, Spelling: "--update-index"}, {Key: ArgOciPushRef, Name: "REF", Required: true}, {}, - {Key: FlagOciRunEngine, Name: "engine", Flag: true, Spelling: "--engine", Choices: []string{"auto", "podman", "docker"}, Default: []string{"auto"}}, - {Key: FlagOciRunFrom, Name: "from", Flag: true, Spelling: "--from"}, - {Key: FlagOciRunImageDir, Name: "image-dir", Flag: true, Spelling: "--image-dir"}, + {Key: FlagOciRunEngine, Name: "engine", Flag: true, Spelling: "--engine", ValueName: "ENGINE", Choices: []string{"auto", "podman", "docker"}, Default: []string{"auto"}}, + {Key: FlagOciRunFrom, Name: "from", Flag: true, Spelling: "--from", ValueName: "FROM"}, + {Key: FlagOciRunImageDir, Name: "image-dir", Flag: true, Spelling: "--image-dir", ValueName: "IMAGE_DIR"}, {Key: FlagOciRunIncludeGlobal, Name: "include-global", Flag: true, Spelling: "--include-global"}, {Key: FlagOciRunKeep, Name: "keep", Flag: true, Spelling: "--keep"}, - {Key: FlagOciRunMountPoint, Name: "mount-point", Flag: true, Spelling: "--mount-point"}, + {Key: FlagOciRunMountPoint, Name: "mount-point", Flag: true, Spelling: "--mount-point", ValueName: "MOUNT_POINT"}, {Key: FlagOciRunNoMise, Name: "no-mise", Flag: true, Spelling: "--no-mise"}, - {Key: FlagOciRunOwner, Name: "owner", Flag: true, Spelling: "--owner"}, - {Key: FlagOciRunVolume, Name: "volume", Flag: true, Spelling: "--volume"}, - {Key: FlagOciRunEnv, Name: "env", Flag: true, Spelling: "--env"}, + {Key: FlagOciRunOwner, Name: "owner", Flag: true, Spelling: "--owner", ValueName: "UID[:GID]"}, + {Key: FlagOciRunVolume, Name: "volume", Flag: true, Spelling: "--volume", ValueName: "HOST:CONTAINER"}, + {Key: FlagOciRunEnv, Name: "env", Flag: true, Spelling: "--env", ValueName: "KEY=VAL"}, {Key: FlagOciRunInteractive, Name: "interactive", Flag: true, Spelling: "--interactive"}, {Key: FlagOciRunTty, Name: "tty", Flag: true, Spelling: "--tty"}, - {Key: FlagOciRunWorkdir, Name: "workdir", Flag: true, Spelling: "--workdir"}, + {Key: FlagOciRunWorkdir, Name: "workdir", Flag: true, Spelling: "--workdir", ValueName: "WORKDIR"}, {Key: ArgOciRunCmd, Name: "CMD"}, {}, {Key: FlagOutdatedJson, Name: "json", Flag: true, Spelling: "--json"}, @@ -4394,7 +4394,7 @@ var Meta = argv.Metadata{ {}, {Key: FlagPluginsInstallAll, Name: "all", Flag: true, Spelling: "--all"}, {Key: FlagPluginsInstallForce, Name: "force", Flag: true, Spelling: "--force"}, - {Key: FlagPluginsInstallJobs, Name: "jobs", Flag: true, Spelling: "--jobs"}, + {Key: FlagPluginsInstallJobs, Name: "jobs", Flag: true, Spelling: "--jobs", ValueName: "JOBS"}, {Key: FlagPluginsInstallVerbose, Name: "verbose", Flag: true, Spelling: "--verbose"}, {Key: ArgPluginsInstallNewPlugin, Name: "NEW_PLUGIN"}, {Key: ArgPluginsInstallGitUrl, Name: "GIT_URL"}, @@ -4418,7 +4418,7 @@ var Meta = argv.Metadata{ {Key: FlagPluginsUninstallPurge, Name: "purge", Flag: true, Spelling: "--purge"}, {Key: ArgPluginsUninstallPlugin, Name: "PLUGIN"}, {}, - {Key: FlagPluginsUpdateJobs, Name: "jobs", Flag: true, Spelling: "--jobs"}, + {Key: FlagPluginsUpdateJobs, Name: "jobs", Flag: true, Spelling: "--jobs", ValueName: "JOBS"}, {Key: ArgPluginsUpdatePlugin, Name: "PLUGIN"}, {}, {Key: FlagDepsExplain, Name: "explain", Flag: true, Spelling: "--explain"}, @@ -4426,8 +4426,8 @@ var Meta = argv.Metadata{ {Key: FlagDepsDryRun, Name: "dry-run", Flag: true, Spelling: "--dry-run"}, {Key: FlagDepsList, Name: "list", Flag: true, Spelling: "--list"}, {Key: FlagDepsMonorepo, Name: "monorepo", Flag: true, Spelling: "--monorepo"}, - {Key: FlagDepsOnly, Name: "only", Flag: true, Spelling: "--only"}, - {Key: FlagDepsSkip, Name: "skip", Flag: true, Spelling: "--skip"}, + {Key: FlagDepsOnly, Name: "only", Flag: true, Spelling: "--only", ValueName: "ONLY"}, + {Key: FlagDepsSkip, Name: "skip", Flag: true, Spelling: "--skip", ValueName: "SKIP"}, {Key: ArgDepsProvider, Name: "PROVIDER"}, {}, {Key: FlagDepsAddDev, Name: "dev", Flag: true, Spelling: "--dev"}, @@ -4438,8 +4438,8 @@ var Meta = argv.Metadata{ {Key: FlagDepsInstallDryRun, Name: "dry-run", Flag: true, Spelling: "--dry-run"}, {Key: FlagDepsInstallList, Name: "list", Flag: true, Spelling: "--list"}, {Key: FlagDepsInstallMonorepo, Name: "monorepo", Flag: true, Spelling: "--monorepo"}, - {Key: FlagDepsInstallOnly, Name: "only", Flag: true, Spelling: "--only"}, - {Key: FlagDepsInstallSkip, Name: "skip", Flag: true, Spelling: "--skip"}, + {Key: FlagDepsInstallOnly, Name: "only", Flag: true, Spelling: "--only", ValueName: "ONLY"}, + {Key: FlagDepsInstallSkip, Name: "skip", Flag: true, Spelling: "--skip", ValueName: "SKIP"}, {Key: ArgDepsInstallProvider, Name: "PROVIDER"}, {}, {Key: ArgDepsRemovePackages, Name: "PACKAGES", Required: true}, @@ -4451,7 +4451,7 @@ var Meta = argv.Metadata{ {Key: FlagPruneTools, Name: "tools", Flag: true, Spelling: "--tools"}, {Key: ArgPruneInstalledTool, Name: "INSTALLED_TOOL"}, {}, - {Key: FlagRegistryBackend, Name: "backend", Flag: true, Spelling: "--backend"}, + {Key: FlagRegistryBackend, Name: "backend", Flag: true, Spelling: "--backend", ValueName: "BACKEND"}, {Key: FlagRegistryComplete, Name: "complete", Flag: true, Spelling: "--complete"}, {Key: FlagRegistryHideAliased, Name: "hide-aliased", Flag: true, Spelling: "--hide-aliased"}, {Key: FlagRegistryJson, Name: "json", Flag: true, Spelling: "--json"}, @@ -4464,25 +4464,25 @@ var Meta = argv.Metadata{ {Key: ArgReshimVersion, Name: "VERSION"}, {}, {Key: FlagRunAffected, Name: "affected", Flag: true, Spelling: "--affected"}, - {Key: FlagRunAffectedBase, Name: "affected-base", Flag: true, Spelling: "--affected-base"}, + {Key: FlagRunAffectedBase, Name: "affected-base", Flag: true, Spelling: "--affected-base", ValueName: "REV"}, {Key: FlagRunAffectedExplain, Name: "affected-explain", Flag: true, Spelling: "--affected-explain"}, - {Key: FlagRunAffectedHead, Name: "affected-head", Flag: true, Spelling: "--affected-head"}, + {Key: FlagRunAffectedHead, Name: "affected-head", Flag: true, Spelling: "--affected-head", ValueName: "REV"}, {Key: FlagRunAffectedJson, Name: "affected-json", Flag: true, Spelling: "--affected-json"}, {Key: FlagRunContinueOnError, Name: "continue-on-error", Flag: true, Spelling: "--continue-on-error"}, - {Key: FlagRunCd, Name: "cd", Flag: true, Spelling: "--cd"}, + {Key: FlagRunCd, Name: "cd", Flag: true, Spelling: "--cd", ValueName: "CD"}, {Key: FlagRunForce, Name: "force", Flag: true, Spelling: "--force"}, - {Key: FlagRunJobs, Name: "jobs", Flag: true, Spelling: "--jobs"}, + {Key: FlagRunJobs, Name: "jobs", Flag: true, Spelling: "--jobs", ValueName: "JOBS"}, {Key: FlagRunDryRun, Name: "dry-run", Flag: true, Spelling: "--dry-run"}, - {Key: FlagRunOutput, Name: "output", Flag: true, Spelling: "--output"}, + {Key: FlagRunOutput, Name: "output", Flag: true, Spelling: "--output", ValueName: "OUTPUT"}, {Key: FlagRunQuiet, Name: "quiet", Flag: true, Spelling: "--quiet"}, {Key: FlagRunRaw, Name: "raw", Flag: true, Spelling: "--raw"}, - {Key: FlagRunShell, Name: "shell", Flag: true, Spelling: "--shell"}, + {Key: FlagRunShell, Name: "shell", Flag: true, Spelling: "--shell", ValueName: "SHELL"}, {Key: FlagRunSilent, Name: "silent", Flag: true, Spelling: "--silent"}, - {Key: FlagRunTool, Name: "tool", Flag: true, Spelling: "--tool"}, - {Key: FlagRunAllowEnv, Name: "allow-env", Flag: true, Spelling: "--allow-env"}, - {Key: FlagRunAllowNet, Name: "allow-net", Flag: true, Spelling: "--allow-net"}, - {Key: FlagRunAllowRead, Name: "allow-read", Flag: true, Spelling: "--allow-read"}, - {Key: FlagRunAllowWrite, Name: "allow-write", Flag: true, Spelling: "--allow-write"}, + {Key: FlagRunTool, Name: "tool", Flag: true, Spelling: "--tool", ValueName: "TOOL@VERSION"}, + {Key: FlagRunAllowEnv, Name: "allow-env", Flag: true, Spelling: "--allow-env", ValueName: "VAR"}, + {Key: FlagRunAllowNet, Name: "allow-net", Flag: true, Spelling: "--allow-net", ValueName: "HOST"}, + {Key: FlagRunAllowRead, Name: "allow-read", Flag: true, Spelling: "--allow-read", ValueName: "PATH"}, + {Key: FlagRunAllowWrite, Name: "allow-write", Flag: true, Spelling: "--allow-write", ValueName: "PATH"}, {Key: FlagRunDenyAll, Name: "deny-all", Flag: true, Spelling: "--deny-all"}, {Key: FlagRunDenyEnv, Name: "deny-env", Flag: true, Spelling: "--deny-env"}, {Key: FlagRunDenyNet, Name: "deny-net", Flag: true, Spelling: "--deny-net"}, @@ -4494,15 +4494,15 @@ var Meta = argv.Metadata{ {Key: FlagRunNoTimings, Name: "no-timings", Flag: true, Spelling: "--no-timings"}, {Key: FlagRunSkipDeps, Name: "skip-deps", Flag: true, Spelling: "--skip-deps"}, {Key: FlagRunSkipTools, Name: "skip-tools", Flag: true, Spelling: "--skip-tools"}, - {Key: FlagRunTaskCache, Name: "task-cache", Flag: true, Spelling: "--task-cache", Choices: []string{"read-write", "read-only", "write-only", "off", "local-only"}, Default: []string{"read-write"}}, + {Key: FlagRunTaskCache, Name: "task-cache", Flag: true, Spelling: "--task-cache", ValueName: "TASK_CACHE", Choices: []string{"read-write", "read-only", "write-only", "off", "local-only"}, Default: []string{"read-write"}}, {Key: FlagRunTaskCacheExplain, Name: "task-cache-explain", Flag: true, Spelling: "--task-cache-explain"}, {Key: FlagRunTaskCacheExplainJson, Name: "task-cache-explain-json", Flag: true, Spelling: "--task-cache-explain-json"}, {Key: FlagRunTaskCacheStats, Name: "task-cache-stats", Flag: true, Spelling: "--task-cache-stats"}, - {Key: FlagRunTimeout, Name: "timeout", Flag: true, Spelling: "--timeout"}, + {Key: FlagRunTimeout, Name: "timeout", Flag: true, Spelling: "--timeout", ValueName: "TIMEOUT"}, {Key: FlagRunTimings, Name: "timings", Flag: true, Spelling: "--timings"}, {}, {Key: FlagSearchInteractive, Name: "interactive", Flag: true, Spelling: "--interactive"}, - {Key: FlagSearchMatchType, Name: "match-type", Flag: true, Spelling: "--match-type", Choices: []string{"equal", "contains", "fuzzy"}, Default: []string{"fuzzy"}}, + {Key: FlagSearchMatchType, Name: "match-type", Flag: true, Spelling: "--match-type", ValueName: "MATCH_TYPE", Choices: []string{"equal", "contains", "fuzzy"}, Default: []string{"fuzzy"}}, {Key: FlagSearchNoHeader, Name: "no-header", Flag: true, Spelling: "--no-header"}, {Key: ArgSearchName, Name: "NAME"}, {}, @@ -4511,17 +4511,17 @@ var Meta = argv.Metadata{ {Key: FlagSelfUpdateNoPlugins, Name: "no-plugins", Flag: true, Spelling: "--no-plugins"}, {Key: ArgSelfUpdateVersion, Name: "VERSION"}, {}, - {Key: FlagSetEnv, Name: "env", Flag: true, Spelling: "--env"}, + {Key: FlagSetEnv, Name: "env", Flag: true, Spelling: "--env", ValueName: "ENV"}, {Key: FlagSetGlobal, Name: "global", Flag: true, Spelling: "--global"}, {Key: FlagSetAgeEncrypt, Name: "age-encrypt", Flag: true, Spelling: "--age-encrypt"}, - {Key: FlagSetAgeKeyFile, Name: "age-key-file", Flag: true, Spelling: "--age-key-file"}, - {Key: FlagSetAgeRecipient, Name: "age-recipient", Flag: true, Spelling: "--age-recipient"}, - {Key: FlagSetAgeSshRecipient, Name: "age-ssh-recipient", Flag: true, Spelling: "--age-ssh-recipient"}, + {Key: FlagSetAgeKeyFile, Name: "age-key-file", Flag: true, Spelling: "--age-key-file", ValueName: "PATH"}, + {Key: FlagSetAgeRecipient, Name: "age-recipient", Flag: true, Spelling: "--age-recipient", ValueName: "RECIPIENT"}, + {Key: FlagSetAgeSshRecipient, Name: "age-ssh-recipient", Flag: true, Spelling: "--age-ssh-recipient", ValueName: "PATH_OR_PUBKEY"}, {Key: FlagSetComplete, Name: "complete", Flag: true, Spelling: "--complete"}, - {Key: FlagSetFile, Name: "file", Flag: true, Spelling: "--file"}, + {Key: FlagSetFile, Name: "file", Flag: true, Spelling: "--file", ValueName: "FILE"}, {Key: FlagSetNoRedact, Name: "no-redact", Flag: true, Spelling: "--no-redact"}, {Key: FlagSetPrompt, Name: "prompt", Flag: true, Spelling: "--prompt"}, - {Key: FlagSetRemove, Name: "remove", Flag: true, Spelling: "--remove"}, + {Key: FlagSetRemove, Name: "remove", Flag: true, Spelling: "--remove", ValueName: "ENV_KEY"}, {Key: FlagSetStdin, Name: "stdin", Flag: true, Spelling: "--stdin"}, {Key: ArgSetEnvVar, Name: "ENV_VAR"}, {}, @@ -4556,7 +4556,7 @@ var Meta = argv.Metadata{ {Key: FlagSettingsUnsetLocal, Name: "local", Flag: true, Spelling: "--local"}, {Key: ArgSettingsUnsetKey, Name: "KEY", Required: true}, {}, - {Key: FlagShellJobs, Name: "jobs", Flag: true, Spelling: "--jobs"}, + {Key: FlagShellJobs, Name: "jobs", Flag: true, Spelling: "--jobs", ValueName: "JOBS"}, {Key: FlagShellUnset, Name: "unset", Flag: true, Spelling: "--unset"}, {Key: FlagShellRaw, Name: "raw", Flag: true, Spelling: "--raw"}, {Key: ArgShellToolVersion, Name: "TOOL@VERSION", Required: true}, @@ -4592,25 +4592,25 @@ var Meta = argv.Metadata{ {Key: FlagTasksHidden, Name: "hidden", Flag: true, Spelling: "--hidden"}, {Key: FlagTasksNameOnly, Name: "name-only", Flag: true, Spelling: "--name-only"}, {Key: FlagTasksNoHeader, Name: "no-header", Flag: true, Spelling: "--no-header"}, - {Key: FlagTasksSort, Name: "sort", Flag: true, Spelling: "--sort", Choices: []string{"name", "alias", "description", "source"}}, - {Key: FlagTasksSortOrder, Name: "sort-order", Flag: true, Spelling: "--sort-order", Choices: []string{"asc", "desc"}}, + {Key: FlagTasksSort, Name: "sort", Flag: true, Spelling: "--sort", ValueName: "COLUMN", Choices: []string{"name", "alias", "description", "source"}}, + {Key: FlagTasksSortOrder, Name: "sort-order", Flag: true, Spelling: "--sort-order", ValueName: "SORT_ORDER", Choices: []string{"asc", "desc"}}, {Key: FlagTasksUsage, Name: "usage", Flag: true, Spelling: "--usage"}, {Key: ArgTasksTask, Name: "TASK"}, {}, - {Key: FlagTasksAddAlias, Name: "alias", Flag: true, Spelling: "--alias"}, - {Key: FlagTasksAddDepends, Name: "depends", Flag: true, Spelling: "--depends"}, - {Key: FlagTasksAddDir, Name: "dir", Flag: true, Spelling: "--dir"}, + {Key: FlagTasksAddAlias, Name: "alias", Flag: true, Spelling: "--alias", ValueName: "ALIAS"}, + {Key: FlagTasksAddDepends, Name: "depends", Flag: true, Spelling: "--depends", ValueName: "DEPENDS"}, + {Key: FlagTasksAddDir, Name: "dir", Flag: true, Spelling: "--dir", ValueName: "DIR"}, {Key: FlagTasksAddFile, Name: "file", Flag: true, Spelling: "--file"}, {Key: FlagTasksAddHide, Name: "hide", Flag: true, Spelling: "--hide"}, {Key: FlagTasksAddQuiet, Name: "quiet", Flag: true, Spelling: "--quiet"}, {Key: FlagTasksAddRaw, Name: "raw", Flag: true, Spelling: "--raw"}, - {Key: FlagTasksAddSources, Name: "sources", Flag: true, Spelling: "--sources"}, - {Key: FlagTasksAddWaitFor, Name: "wait-for", Flag: true, Spelling: "--wait-for"}, - {Key: FlagTasksAddDependsPost, Name: "depends-post", Flag: true, Spelling: "--depends-post"}, - {Key: FlagTasksAddDescription, Name: "description", Flag: true, Spelling: "--description"}, - {Key: FlagTasksAddOutputs, Name: "outputs", Flag: true, Spelling: "--outputs"}, - {Key: FlagTasksAddRunWindows, Name: "run-windows", Flag: true, Spelling: "--run-windows"}, - {Key: FlagTasksAddShell, Name: "shell", Flag: true, Spelling: "--shell"}, + {Key: FlagTasksAddSources, Name: "sources", Flag: true, Spelling: "--sources", ValueName: "SOURCES"}, + {Key: FlagTasksAddWaitFor, Name: "wait-for", Flag: true, Spelling: "--wait-for", ValueName: "WAIT_FOR"}, + {Key: FlagTasksAddDependsPost, Name: "depends-post", Flag: true, Spelling: "--depends-post", ValueName: "DEPENDS_POST"}, + {Key: FlagTasksAddDescription, Name: "description", Flag: true, Spelling: "--description", ValueName: "DESCRIPTION"}, + {Key: FlagTasksAddOutputs, Name: "outputs", Flag: true, Spelling: "--outputs", ValueName: "OUTPUTS"}, + {Key: FlagTasksAddRunWindows, Name: "run-windows", Flag: true, Spelling: "--run-windows", ValueName: "RUN_WINDOWS"}, + {Key: FlagTasksAddShell, Name: "shell", Flag: true, Spelling: "--shell", ValueName: "SHELL"}, {Key: FlagTasksAddSilent, Name: "silent", Flag: true, Spelling: "--silent"}, {Key: ArgTasksAddTask, Name: "TASK", Required: true}, {Key: ArgTasksAddRun, Name: "RUN"}, @@ -4639,30 +4639,30 @@ var Meta = argv.Metadata{ {Key: FlagTasksLsHidden, Name: "hidden", Flag: true, Spelling: "--hidden"}, {Key: FlagTasksLsNameOnly, Name: "name-only", Flag: true, Spelling: "--name-only"}, {Key: FlagTasksLsNoHeader, Name: "no-header", Flag: true, Spelling: "--no-header"}, - {Key: FlagTasksLsSort, Name: "sort", Flag: true, Spelling: "--sort", Choices: []string{"name", "alias", "description", "source"}}, - {Key: FlagTasksLsSortOrder, Name: "sort-order", Flag: true, Spelling: "--sort-order", Choices: []string{"asc", "desc"}}, + {Key: FlagTasksLsSort, Name: "sort", Flag: true, Spelling: "--sort", ValueName: "COLUMN", Choices: []string{"name", "alias", "description", "source"}}, + {Key: FlagTasksLsSortOrder, Name: "sort-order", Flag: true, Spelling: "--sort-order", ValueName: "SORT_ORDER", Choices: []string{"asc", "desc"}}, {Key: FlagTasksLsUsage, Name: "usage", Flag: true, Spelling: "--usage"}, {}, {Key: FlagTasksRunAffected, Name: "affected", Flag: true, Spelling: "--affected"}, - {Key: FlagTasksRunAffectedBase, Name: "affected-base", Flag: true, Spelling: "--affected-base"}, + {Key: FlagTasksRunAffectedBase, Name: "affected-base", Flag: true, Spelling: "--affected-base", ValueName: "REV"}, {Key: FlagTasksRunAffectedExplain, Name: "affected-explain", Flag: true, Spelling: "--affected-explain"}, - {Key: FlagTasksRunAffectedHead, Name: "affected-head", Flag: true, Spelling: "--affected-head"}, + {Key: FlagTasksRunAffectedHead, Name: "affected-head", Flag: true, Spelling: "--affected-head", ValueName: "REV"}, {Key: FlagTasksRunAffectedJson, Name: "affected-json", Flag: true, Spelling: "--affected-json"}, {Key: FlagTasksRunContinueOnError, Name: "continue-on-error", Flag: true, Spelling: "--continue-on-error"}, - {Key: FlagTasksRunCd, Name: "cd", Flag: true, Spelling: "--cd"}, + {Key: FlagTasksRunCd, Name: "cd", Flag: true, Spelling: "--cd", ValueName: "CD"}, {Key: FlagTasksRunForce, Name: "force", Flag: true, Spelling: "--force"}, - {Key: FlagTasksRunJobs, Name: "jobs", Flag: true, Spelling: "--jobs"}, + {Key: FlagTasksRunJobs, Name: "jobs", Flag: true, Spelling: "--jobs", ValueName: "JOBS"}, {Key: FlagTasksRunDryRun, Name: "dry-run", Flag: true, Spelling: "--dry-run"}, - {Key: FlagTasksRunOutput, Name: "output", Flag: true, Spelling: "--output"}, + {Key: FlagTasksRunOutput, Name: "output", Flag: true, Spelling: "--output", ValueName: "OUTPUT"}, {Key: FlagTasksRunQuiet, Name: "quiet", Flag: true, Spelling: "--quiet"}, {Key: FlagTasksRunRaw, Name: "raw", Flag: true, Spelling: "--raw"}, - {Key: FlagTasksRunShell, Name: "shell", Flag: true, Spelling: "--shell"}, + {Key: FlagTasksRunShell, Name: "shell", Flag: true, Spelling: "--shell", ValueName: "SHELL"}, {Key: FlagTasksRunSilent, Name: "silent", Flag: true, Spelling: "--silent"}, - {Key: FlagTasksRunTool, Name: "tool", Flag: true, Spelling: "--tool"}, - {Key: FlagTasksRunAllowEnv, Name: "allow-env", Flag: true, Spelling: "--allow-env"}, - {Key: FlagTasksRunAllowNet, Name: "allow-net", Flag: true, Spelling: "--allow-net"}, - {Key: FlagTasksRunAllowRead, Name: "allow-read", Flag: true, Spelling: "--allow-read"}, - {Key: FlagTasksRunAllowWrite, Name: "allow-write", Flag: true, Spelling: "--allow-write"}, + {Key: FlagTasksRunTool, Name: "tool", Flag: true, Spelling: "--tool", ValueName: "TOOL@VERSION"}, + {Key: FlagTasksRunAllowEnv, Name: "allow-env", Flag: true, Spelling: "--allow-env", ValueName: "VAR"}, + {Key: FlagTasksRunAllowNet, Name: "allow-net", Flag: true, Spelling: "--allow-net", ValueName: "HOST"}, + {Key: FlagTasksRunAllowRead, Name: "allow-read", Flag: true, Spelling: "--allow-read", ValueName: "PATH"}, + {Key: FlagTasksRunAllowWrite, Name: "allow-write", Flag: true, Spelling: "--allow-write", ValueName: "PATH"}, {Key: FlagTasksRunDenyAll, Name: "deny-all", Flag: true, Spelling: "--deny-all"}, {Key: FlagTasksRunDenyEnv, Name: "deny-env", Flag: true, Spelling: "--deny-env"}, {Key: FlagTasksRunDenyNet, Name: "deny-net", Flag: true, Spelling: "--deny-net"}, @@ -4674,11 +4674,11 @@ var Meta = argv.Metadata{ {Key: FlagTasksRunNoTimings, Name: "no-timings", Flag: true, Spelling: "--no-timings"}, {Key: FlagTasksRunSkipDeps, Name: "skip-deps", Flag: true, Spelling: "--skip-deps"}, {Key: FlagTasksRunSkipTools, Name: "skip-tools", Flag: true, Spelling: "--skip-tools"}, - {Key: FlagTasksRunTaskCache, Name: "task-cache", Flag: true, Spelling: "--task-cache", Choices: []string{"read-write", "read-only", "write-only", "off", "local-only"}, Default: []string{"read-write"}}, + {Key: FlagTasksRunTaskCache, Name: "task-cache", Flag: true, Spelling: "--task-cache", ValueName: "TASK_CACHE", Choices: []string{"read-write", "read-only", "write-only", "off", "local-only"}, Default: []string{"read-write"}}, {Key: FlagTasksRunTaskCacheExplain, Name: "task-cache-explain", Flag: true, Spelling: "--task-cache-explain"}, {Key: FlagTasksRunTaskCacheExplainJson, Name: "task-cache-explain-json", Flag: true, Spelling: "--task-cache-explain-json"}, {Key: FlagTasksRunTaskCacheStats, Name: "task-cache-stats", Flag: true, Spelling: "--task-cache-stats"}, - {Key: FlagTasksRunTimeout, Name: "timeout", Flag: true, Spelling: "--timeout"}, + {Key: FlagTasksRunTimeout, Name: "timeout", Flag: true, Spelling: "--timeout", ValueName: "TIMEOUT"}, {Key: FlagTasksRunTimings, Name: "timings", Flag: true, Spelling: "--timings"}, {Key: ArgTasksRunTask, Name: "TASK", Default: []string{"default"}}, {Key: ArgTasksRunArgs, Name: "ARGS"}, @@ -4689,7 +4689,7 @@ var Meta = argv.Metadata{ {Key: ArgTasksValidateTasks, Name: "TASKS"}, {}, {Key: FlagTestToolAll, Name: "all", Flag: true, Spelling: "--all"}, - {Key: FlagTestToolJobs, Name: "jobs", Flag: true, Spelling: "--jobs"}, + {Key: FlagTestToolJobs, Name: "jobs", Flag: true, Spelling: "--jobs", ValueName: "JOBS"}, {Key: FlagTestToolAllConfig, Name: "all-config", Flag: true, Spelling: "--all-config"}, {Key: FlagTestToolIncludeNonDefined, Name: "include-non-defined", Flag: true, Spelling: "--include-non-defined"}, {Key: FlagTestToolRaw, Name: "raw", Flag: true, Spelling: "--raw"}, @@ -4725,70 +4725,70 @@ var Meta = argv.Metadata{ {Key: FlagTrustIgnore, Name: "ignore", Flag: true, Spelling: "--ignore"}, {Key: FlagTrustShow, Name: "show", Flag: true, Spelling: "--show"}, {Key: FlagTrustUntrust, Name: "untrust", Flag: true, Spelling: "--untrust"}, - {Key: ArgTrustConfigFile, Name: "CONFIG_FILE"}, + {Key: ArgTrustConfigFile, Name: "CONFIG_FILE", CompleteType: "file"}, {}, {Key: FlagUninstallAll, Name: "all", Flag: true, Spelling: "--all"}, {Key: FlagUninstallDryRun, Name: "dry-run", Flag: true, Spelling: "--dry-run"}, {Key: FlagUninstallDryRunCode, Name: "dry-run-code", Flag: true, Spelling: "--dry-run-code"}, {Key: ArgUninstallInstalledToolVersion, Name: "INSTALLED_TOOL@VERSION"}, {}, - {Key: FlagUnsetFile, Name: "file", Flag: true, Spelling: "--file"}, + {Key: FlagUnsetFile, Name: "file", Flag: true, Spelling: "--file", ValueName: "FILE"}, {Key: FlagUnsetGlobal, Name: "global", Flag: true, Spelling: "--global"}, {Key: ArgUnsetEnvKey, Name: "ENV_KEY"}, {}, - {Key: ArgUntrustConfigFile, Name: "CONFIG_FILE"}, + {Key: ArgUntrustConfigFile, Name: "CONFIG_FILE", CompleteType: "file"}, {}, - {Key: FlagUnuseEnv, Name: "env", Flag: true, Spelling: "--env"}, + {Key: FlagUnuseEnv, Name: "env", Flag: true, Spelling: "--env", ValueName: "ENV"}, {Key: FlagUnuseGlobal, Name: "global", Flag: true, Spelling: "--global"}, - {Key: FlagUnusePath, Name: "path", Flag: true, Spelling: "--path"}, + {Key: FlagUnusePath, Name: "path", Flag: true, Spelling: "--path", ValueName: "PATH"}, {Key: FlagUnuseNoPrune, Name: "no-prune", Flag: true, Spelling: "--no-prune"}, {Key: ArgUnuseInstalledToolVersion, Name: "INSTALLED_TOOL@VERSION", Required: true}, {}, {Key: FlagUpgradeInteractive, Name: "interactive", Flag: true, Spelling: "--interactive"}, - {Key: FlagUpgradeJobs, Name: "jobs", Flag: true, Spelling: "--jobs"}, + {Key: FlagUpgradeJobs, Name: "jobs", Flag: true, Spelling: "--jobs", ValueName: "JOBS"}, {Key: FlagUpgradeBump, Name: "bump", Flag: true, Spelling: "--bump"}, {Key: FlagUpgradeDryRun, Name: "dry-run", Flag: true, Spelling: "--dry-run"}, - {Key: FlagUpgradeExclude, Name: "exclude", Flag: true, Spelling: "--exclude"}, + {Key: FlagUpgradeExclude, Name: "exclude", Flag: true, Spelling: "--exclude", ValueName: "INSTALLED_TOOL"}, {Key: FlagUpgradeDryRunCode, Name: "dry-run-code", Flag: true, Spelling: "--dry-run-code"}, {Key: FlagUpgradeInactive, Name: "inactive", Flag: true, Spelling: "--inactive"}, {Key: FlagUpgradeLocal, Name: "local", Flag: true, Spelling: "--local"}, - {Key: FlagUpgradeMinimumReleaseAge, Name: "minimum-release-age", Flag: true, Spelling: "--minimum-release-age"}, + {Key: FlagUpgradeMinimumReleaseAge, Name: "minimum-release-age", Flag: true, Spelling: "--minimum-release-age", ValueName: "MINIMUM_RELEASE_AGE"}, {Key: FlagUpgradeMonorepo, Name: "monorepo", Flag: true, Spelling: "--monorepo"}, {Key: FlagUpgradeNoPrune, Name: "no-prune", Flag: true, Spelling: "--no-prune"}, {Key: FlagUpgradeRaw, Name: "raw", Flag: true, Spelling: "--raw"}, {Key: ArgUpgradeInstalledToolVersion, Name: "INSTALLED_TOOL@VERSION"}, {}, {}, - {Key: FlagUseEnv, Name: "env", Flag: true, Spelling: "--env"}, + {Key: FlagUseEnv, Name: "env", Flag: true, Spelling: "--env", ValueName: "ENV"}, {Key: FlagUseForce, Name: "force", Flag: true, Spelling: "--force"}, {Key: FlagUseGlobal, Name: "global", Flag: true, Spelling: "--global"}, - {Key: FlagUseJobs, Name: "jobs", Flag: true, Spelling: "--jobs"}, + {Key: FlagUseJobs, Name: "jobs", Flag: true, Spelling: "--jobs", ValueName: "JOBS"}, {Key: FlagUseDryRun, Name: "dry-run", Flag: true, Spelling: "--dry-run"}, - {Key: FlagUsePath, Name: "path", Flag: true, Spelling: "--path"}, + {Key: FlagUsePath, Name: "path", Flag: true, Spelling: "--path", ValueName: "PATH"}, {Key: FlagUseDryRunCode, Name: "dry-run-code", Flag: true, Spelling: "--dry-run-code"}, {Key: FlagUseFuzzy, Name: "fuzzy", Flag: true, Spelling: "--fuzzy"}, - {Key: FlagUseMinimumReleaseAge, Name: "minimum-release-age", Flag: true, Spelling: "--minimum-release-age"}, + {Key: FlagUseMinimumReleaseAge, Name: "minimum-release-age", Flag: true, Spelling: "--minimum-release-age", ValueName: "MINIMUM_RELEASE_AGE"}, {Key: FlagUsePin, Name: "pin", Flag: true, Spelling: "--pin"}, {Key: FlagUseRaw, Name: "raw", Flag: true, Spelling: "--raw"}, - {Key: FlagUseRemove, Name: "remove", Flag: true, Spelling: "--remove"}, + {Key: FlagUseRemove, Name: "remove", Flag: true, Spelling: "--remove", ValueName: "TOOL"}, {Key: ArgUseToolVersion, Name: "TOOL@VERSION"}, {}, {Key: FlagVersionJson, Name: "json", Flag: true, Spelling: "--json"}, {}, - {Key: FlagWatchTaskFlag, Name: "task-flag", Flag: true, Spelling: "--task-flag"}, - {Key: FlagWatchGlob, Name: "glob", Flag: true, Spelling: "--glob"}, + {Key: FlagWatchTaskFlag, Name: "task-flag", Flag: true, Spelling: "--task-flag", ValueName: "TASK_FLAG"}, + {Key: FlagWatchGlob, Name: "glob", Flag: true, Spelling: "--glob", ValueName: "GLOB"}, {Key: FlagWatchSkipDeps, Name: "skip-deps", Flag: true, Spelling: "--skip-deps"}, - {Key: FlagWatchWatch, Name: "watch", Flag: true, Spelling: "--watch"}, - {Key: FlagWatchWatchNonRecursive, Name: "watch-non-recursive", Flag: true, Spelling: "--watch-non-recursive"}, - {Key: FlagWatchWatchFile, Name: "watch-file", Flag: true, Spelling: "--watch-file"}, - {Key: FlagWatchClear, Name: "clear", Flag: true, Spelling: "--clear", Choices: []string{"clear", "reset"}}, - {Key: FlagWatchOnBusyUpdate, Name: "on-busy-update", Flag: true, Spelling: "--on-busy-update", Choices: []string{"queue", "do-nothing", "restart", "signal"}, Default: []string{"do-nothing"}}, + {Key: FlagWatchWatch, Name: "watch", Flag: true, Spelling: "--watch", ValueName: "PATH"}, + {Key: FlagWatchWatchNonRecursive, Name: "watch-non-recursive", Flag: true, Spelling: "--watch-non-recursive", ValueName: "PATH"}, + {Key: FlagWatchWatchFile, Name: "watch-file", Flag: true, Spelling: "--watch-file", ValueName: "PATH"}, + {Key: FlagWatchClear, Name: "clear", Flag: true, Spelling: "--clear", ValueName: "MODE", Choices: []string{"clear", "reset"}}, + {Key: FlagWatchOnBusyUpdate, Name: "on-busy-update", Flag: true, Spelling: "--on-busy-update", ValueName: "MODE", Choices: []string{"queue", "do-nothing", "restart", "signal"}, Default: []string{"do-nothing"}}, {Key: FlagWatchRestart, Name: "restart", Flag: true, Spelling: "--restart"}, - {Key: FlagWatchSignal, Name: "signal", Flag: true, Spelling: "--signal"}, - {Key: FlagWatchStopSignal, Name: "stop-signal", Flag: true, Spelling: "--stop-signal"}, - {Key: FlagWatchStopTimeout, Name: "stop-timeout", Flag: true, Spelling: "--stop-timeout", Default: []string{"10s"}}, - {Key: FlagWatchMapSignal, Name: "map-signal", Flag: true, Spelling: "--map-signal"}, - {Key: FlagWatchDebounce, Name: "debounce", Flag: true, Spelling: "--debounce", Default: []string{"50ms"}}, + {Key: FlagWatchSignal, Name: "signal", Flag: true, Spelling: "--signal", ValueName: "SIGNAL"}, + {Key: FlagWatchStopSignal, Name: "stop-signal", Flag: true, Spelling: "--stop-signal", ValueName: "SIGNAL"}, + {Key: FlagWatchStopTimeout, Name: "stop-timeout", Flag: true, Spelling: "--stop-timeout", ValueName: "TIMEOUT", Default: []string{"10s"}}, + {Key: FlagWatchMapSignal, Name: "map-signal", Flag: true, Spelling: "--map-signal", ValueName: "SIGNAL:SIGNAL"}, + {Key: FlagWatchDebounce, Name: "debounce", Flag: true, Spelling: "--debounce", ValueName: "TIMEOUT", Default: []string{"50ms"}}, {Key: FlagWatchStdinQuit, Name: "stdin-quit", Flag: true, Spelling: "--stdin-quit"}, {Key: FlagWatchNoVcsIgnore, Name: "no-vcs-ignore", Flag: true, Spelling: "--no-vcs-ignore"}, {Key: FlagWatchNoProjectIgnore, Name: "no-project-ignore", Flag: true, Spelling: "--no-project-ignore"}, @@ -4797,28 +4797,28 @@ var Meta = argv.Metadata{ {Key: FlagWatchNoDiscoverIgnore, Name: "no-discover-ignore", Flag: true, Spelling: "--no-discover-ignore"}, {Key: FlagWatchIgnoreNothing, Name: "ignore-nothing", Flag: true, Spelling: "--ignore-nothing"}, {Key: FlagWatchPostpone, Name: "postpone", Flag: true, Spelling: "--postpone"}, - {Key: FlagWatchDelayRun, Name: "delay-run", Flag: true, Spelling: "--delay-run"}, - {Key: FlagWatchPoll, Name: "poll", Flag: true, Spelling: "--poll"}, - {Key: FlagWatchShell, Name: "shell", Flag: true, Spelling: "--shell"}, + {Key: FlagWatchDelayRun, Name: "delay-run", Flag: true, Spelling: "--delay-run", ValueName: "DURATION"}, + {Key: FlagWatchPoll, Name: "poll", Flag: true, Spelling: "--poll", ValueName: "INTERVAL"}, + {Key: FlagWatchShell, Name: "shell", Flag: true, Spelling: "--shell", ValueName: "SHELL"}, {Key: FlagWatchN, Name: "n", Flag: true, Spelling: "-n"}, - {Key: FlagWatchEmitEventsTo, Name: "emit-events-to", Flag: true, Spelling: "--emit-events-to", Choices: []string{"environment", "stdio", "file", "json-stdio", "json-file", "none"}, Default: []string{"none"}}, + {Key: FlagWatchEmitEventsTo, Name: "emit-events-to", Flag: true, Spelling: "--emit-events-to", ValueName: "MODE", Choices: []string{"environment", "stdio", "file", "json-stdio", "json-file", "none"}, Default: []string{"none"}}, {Key: FlagWatchOnlyEmitEvents, Name: "only-emit-events", Flag: true, Spelling: "--only-emit-events"}, - {Key: FlagWatchEnv, Name: "env", Flag: true, Spelling: "--env"}, - {Key: FlagWatchWrapProcess, Name: "wrap-process", Flag: true, Spelling: "--wrap-process", Choices: []string{"group", "session", "none"}}, + {Key: FlagWatchEnv, Name: "env", Flag: true, Spelling: "--env", ValueName: "KEY=VALUE"}, + {Key: FlagWatchWrapProcess, Name: "wrap-process", Flag: true, Spelling: "--wrap-process", ValueName: "MODE", Choices: []string{"group", "session", "none"}}, {Key: FlagWatchNotify, Name: "notify", Flag: true, Spelling: "--notify"}, - {Key: FlagWatchColor, Name: "color", Flag: true, Spelling: "--color", Choices: []string{"auto", "always", "never"}, Default: []string{"auto"}}, + {Key: FlagWatchColor, Name: "color", Flag: true, Spelling: "--color", ValueName: "MODE", Choices: []string{"auto", "always", "never"}, Default: []string{"auto"}}, {Key: FlagWatchTimings, Name: "timings", Flag: true, Spelling: "--timings"}, {Key: FlagWatchQuiet, Name: "quiet", Flag: true, Spelling: "--quiet"}, {Key: FlagWatchBell, Name: "bell", Flag: true, Spelling: "--bell"}, - {Key: FlagWatchProjectOrigin, Name: "project-origin", Flag: true, Spelling: "--project-origin"}, - {Key: FlagWatchWorkdir, Name: "workdir", Flag: true, Spelling: "--workdir"}, - {Key: FlagWatchExts, Name: "exts", Flag: true, Spelling: "--exts"}, - {Key: FlagWatchFilter, Name: "filter", Flag: true, Spelling: "--filter"}, - {Key: FlagWatchFilterFile, Name: "filter-file", Flag: true, Spelling: "--filter-file"}, - {Key: FlagWatchFilterProg, Name: "filter-prog", Flag: true, Spelling: "--filter-prog"}, - {Key: FlagWatchIgnore, Name: "ignore", Flag: true, Spelling: "--ignore"}, - {Key: FlagWatchIgnoreFile, Name: "ignore-file", Flag: true, Spelling: "--ignore-file"}, - {Key: FlagWatchFsEvents, Name: "fs-events", Flag: true, Spelling: "--fs-events", Choices: []string{"access", "create", "remove", "rename", "modify", "metadata"}, Default: []string{"create", "remove", "rename", "modify", "metadata"}}, + {Key: FlagWatchProjectOrigin, Name: "project-origin", Flag: true, Spelling: "--project-origin", ValueName: "DIRECTORY"}, + {Key: FlagWatchWorkdir, Name: "workdir", Flag: true, Spelling: "--workdir", ValueName: "DIRECTORY"}, + {Key: FlagWatchExts, Name: "exts", Flag: true, Spelling: "--exts", ValueName: "EXTENSIONS"}, + {Key: FlagWatchFilter, Name: "filter", Flag: true, Spelling: "--filter", ValueName: "PATTERN"}, + {Key: FlagWatchFilterFile, Name: "filter-file", Flag: true, Spelling: "--filter-file", ValueName: "PATH"}, + {Key: FlagWatchFilterProg, Name: "filter-prog", Flag: true, Spelling: "--filter-prog", ValueName: "EXPRESSION"}, + {Key: FlagWatchIgnore, Name: "ignore", Flag: true, Spelling: "--ignore", ValueName: "PATTERN"}, + {Key: FlagWatchIgnoreFile, Name: "ignore-file", Flag: true, Spelling: "--ignore-file", ValueName: "PATH"}, + {Key: FlagWatchFsEvents, Name: "fs-events", Flag: true, Spelling: "--fs-events", ValueName: "EVENTS", Choices: []string{"access", "create", "remove", "rename", "modify", "metadata"}, Default: []string{"create", "remove", "rename", "modify", "metadata"}}, {Key: FlagWatchNoMeta, Name: "no-meta", Flag: true, Spelling: "--no-meta"}, {Key: FlagWatchPrintEvents, Name: "print-events", Flag: true, Spelling: "--print-events"}, {Key: FlagWatchManual, Name: "manual", Flag: true, Spelling: "--manual"}, @@ -4828,7 +4828,7 @@ var Meta = argv.Metadata{ {Key: ArgWhereToolVersion, Name: "TOOL@VERSION", Required: true}, {Key: ArgWhereAsdfVersion, Name: "ASDF_VERSION"}, {}, - {Key: FlagWhichTool, Name: "tool", Flag: true, Spelling: "--tool"}, + {Key: FlagWhichTool, Name: "tool", Flag: true, Spelling: "--tool", ValueName: "TOOL@VERSION"}, {Key: FlagWhichComplete, Name: "complete", Flag: true, Spelling: "--complete"}, {Key: FlagWhichPlugin, Name: "plugin", Flag: true, Spelling: "--plugin"}, {Key: FlagWhichVersion, Name: "version", Flag: true, Spelling: "--version"}, diff --git a/go/internal/spec/spec.go b/go/internal/spec/spec.go index c8b3adc9a..3160d508c 100644 --- a/go/internal/spec/spec.go +++ b/go/internal/spec/spec.go @@ -41,11 +41,15 @@ type Spec struct { Version string `json:"version"` About string `json:"about"` AboutLong string `json:"about_long"` - BeforeHelp string `json:"before_help"` - AfterHelp string `json:"after_help"` - BeforeHelpLong string `json:"before_help_long"` - AfterHelpLong string `json:"after_help_long"` - Usage string `json:"usage"` + // Complete is the completers a spec declares, keyed by the lowercased name of + // the argument or flag value they belong to — which is how usage-lib keys + // them, and how a lookup has to be spelled. + Complete map[string]Completer `json:"complete"` + BeforeHelp string `json:"before_help"` + AfterHelp string `json:"after_help"` + BeforeHelpLong string `json:"before_help_long"` + AfterHelpLong string `json:"after_help_long"` + Usage string `json:"usage"` } // HelpSpec is what a page needs from the spec's root rather than from a command. @@ -63,6 +67,14 @@ func (s *Spec) HelpSpec() argv.HelpSpec { } } +// Completer is one `complete` block. Only the type is read here: running the +// `run=` script is a completion's job at run time, and needs a subprocess this +// package has no business starting. +type Completer struct { + Name string `json:"name"` + Type string `json:"type_"` +} + // Cmd is one command in the lowered spec. type Cmd struct { Name string `json:"name"` @@ -319,7 +331,7 @@ func (s *Spec) Build() (*argv.Command, argv.Metadata) { // help one. They share the keys that tie them together, so they are built in one // pass for the same reason the first two were. func (s *Spec) BuildAll() (*argv.Command, argv.Metadata, argv.HelpTable) { - b := &builder{} + b := &builder{complete: s.Complete} root := b.command(&s.Cmd, unknownFlags(s.UnknownFlags, argv.UnknownFlagsValue)) // default_subcommand is a property of the spec rather than of a command, so it @@ -398,6 +410,8 @@ type builder struct { // it needs the original. A spec declaring `negate="-no-color"` is not named // by `--no-color`, and usage-lib does not resolve it either. negation map[uint64]string + // complete is the spec's `complete` blocks, keyed as usage-lib keys them. + complete map[string]Completer } // record files an entry's cold half at the position its key indexes. @@ -442,6 +456,26 @@ func visibleAliases(c *Cmd) []string { return out } +// valueOf is what a flag's value is called, or empty where the flag takes none. +func valueOf(f *Flag) string { + if f.Arg == nil { + return "" + } + return f.Arg.Name +} + +// completeType is the type the spec's `complete` block names for an entry. +// +// By lowercased name, which is how usage-lib files them: `complete "FILE"` and an +// argument written `` are the same position as far as the reference is +// concerned. +func (b *builder) completeType(name string) string { + if b.complete == nil || name == "" { + return "" + } + return b.complete[strings.ToLower(name)].Type +} + func first(values ...string) string { for _, v := range values { if v != "" { @@ -672,14 +706,16 @@ func (b *builder) flag(f *Flag) *argv.Flag { Default: f.defaults(), }) b.record(out.Key, argv.Meta{ - Name: f.Name, - Flag: true, - Spelling: spelling(f), - Required: f.Required, - Choices: f.choices(), - Default: f.defaults(), - Env: f.Env, - VarMin: clampVarMax(f.VarMin), + Name: f.Name, + Flag: true, + Spelling: spelling(f), + ValueName: valueOf(f), + CompleteType: b.completeType(first(valueOf(f), f.Name)), + Required: f.Required, + Choices: f.choices(), + Default: f.defaults(), + Env: f.Env, + VarMin: clampVarMax(f.VarMin), // Occurrences. The per-occurrence value bound is a limit binding applies, // and is set on the parse table below rather than here. VarMax: clampVarMax(f.VarMax), @@ -719,12 +755,13 @@ func (b *builder) arg(a *Arg) *argv.Arg { Default: a.Default, }) b.record(out.Key, argv.Meta{ - Name: a.Name, - Required: a.Required, - Choices: a.Choices.list(), - Default: a.Default, - Env: a.Env, - VarMin: clampVarMax(a.VarMin), + Name: a.Name, + Required: a.Required, + CompleteType: b.completeType(a.Name), + Choices: a.Choices.list(), + Default: a.Default, + Env: a.Env, + VarMin: clampVarMax(a.VarMin), // No VarMax: for an argument the bound is a limit binding applies, which // is what makes `[a]… [b]` fillable at all, so judging it again here would // fail an invocation that never broke it. diff --git a/lib/src/go/mod.rs b/lib/src/go/mod.rs index 3d11b3169..35dd785fe 100644 --- a/lib/src/go/mod.rs +++ b/lib/src/go/mod.rs @@ -408,7 +408,7 @@ impl Emitter<'_> { by_key.insert(named.number, self.flag_meta(flag, named, e, commands)); } for (arg, named) in &e.args { - by_key.insert(named.number, arg_meta(arg, named)); + by_key.insert(named.number, arg_meta(self.spec, arg, named)); } } @@ -465,6 +465,19 @@ impl Emitter<'_> { } else if let Some(short) = flag.short.first() { fields.push(format!("Spelling: {}", go_string(&format!("-{short}")))); } + // What the value is called, which is what says whether a path belongs + // there — `--into ` completes directories because of the name. + if let Some(value) = flag.arg.as_ref() { + fields.push(format!("ValueName: {}", go_string(&value.name))); + } + let named_value = flag + .arg + .as_ref() + .map(|a| a.name.as_str()) + .unwrap_or(flag.name.as_str()); + if let Some(kind) = complete_type(self.spec, named_value) { + fields.push(format!("CompleteType: {}", go_string(kind))); + } // Written on the value a flag takes, never on the flag. if let Some(choices) = flag.arg.as_ref().and_then(|a| a.choices.as_ref()) { fields.push(format!("Choices: {}", string_slice(&choices.choices))); @@ -512,7 +525,18 @@ impl Emitter<'_> { } /// The cold half of a positional argument. -fn arg_meta(arg: &SpecArg, named: &Named) -> String { +/// The type a spec's `complete` block names for an entry, if it names one. +/// +/// By lowercased name, which is how usage-lib files them: `complete "FILE"` and +/// an argument written `` are the same position as far as the reference is +/// concerned. +fn complete_type<'a>(spec: &'a Spec, name: &str) -> Option<&'a str> { + spec.complete + .get(&name.to_lowercase()) + .and_then(|c| c.type_.as_deref()) +} + +fn arg_meta(spec: &Spec, arg: &SpecArg, named: &Named) -> String { let mut fields = vec![ format!("Key: {}", named.key), format!("Name: {}", go_string(&arg.name)), @@ -520,6 +544,13 @@ fn arg_meta(arg: &SpecArg, named: &Named) -> String { if arg.required { fields.push("Required: true".to_string()); } + // What the position takes, where the spec said so. Read by completion rather + // than by any post-binding rule: an author who wrote `complete "input" + // type="file"` named what belongs there, and the alternative is inferring it + // from a name they did not choose. + if let Some(kind) = complete_type(spec, &arg.name) { + fields.push(format!("CompleteType: {}", go_string(kind))); + } if let Some(choices) = &arg.choices { fields.push(format!("Choices: {}", string_slice(&choices.choices))); } diff --git a/lib/src/go/snapshots/usage__go__tests__a_whole_cli.snap b/lib/src/go/snapshots/usage__go__tests__a_whole_cli.snap index 7865e9bcc..4e944f28f 100644 --- a/lib/src/go/snapshots/usage__go__tests__a_whole_cli.snap +++ b/lib/src/go/snapshots/usage__go__tests__a_whole_cli.snap @@ -95,8 +95,8 @@ var Meta = argv.Metadata{ {}, {Key: FlagVerbose, Name: "verbose", Flag: true, Spelling: "--verbose"}, {Key: FlagColor, Name: "color", Flag: true, Spelling: "--color"}, - {Key: FlagJobs, Name: "jobs", Flag: true, Spelling: "--jobs"}, - {Key: FlagInclude, Name: "include", Flag: true, Spelling: "--include", VarMax: 3}, + {Key: FlagJobs, Name: "jobs", Flag: true, Spelling: "--jobs", ValueName: "n"}, + {Key: FlagInclude, Name: "include", Flag: true, Spelling: "--include", ValueName: "pattern", VarMax: 3}, {Key: ArgFile, Name: "file", Required: true}, {Key: ArgRest, Name: "rest"}, {},