diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index 5caeafe5ac..ef5195c241 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -40,6 +40,8 @@ jobs: GFLAGS: -tags urfave_cli_no_docs - run: make check-binary-size - run: make yamlfmt + - if: matrix.go == '1.19.x' && matrix.os == 'ubuntu-latest' + run: make generate - run: make diffcheck - if: matrix.go == '1.19.x' && matrix.os == 'ubuntu-latest' run: make v2diff diff --git a/app_test.go b/app_test.go index a4ce72c765..62007188d0 100644 --- a/app_test.go +++ b/app_test.go @@ -177,7 +177,7 @@ func ExampleApp_Run_commandHelp() { // greet describeit - use it to see a description // // USAGE: - // greet describeit [command options] [arguments...] + // greet describeit [arguments...] // // DESCRIPTION: // This is how we describe describeit the function diff --git a/command.go b/command.go index d24b61e239..e66985e71f 100644 --- a/command.go +++ b/command.go @@ -295,6 +295,17 @@ func (c *Command) startApp(ctx *Context) error { return app.RunAsSubcommand(ctx) } +// VisibleCommands returns a slice of the Commands with Hidden=false +func (c *Command) VisibleCommands() []*Command { + var ret []*Command + for _, command := range c.Subcommands { + if !command.Hidden { + ret = append(ret, command) + } + } + return ret +} + // VisibleFlagCategories returns a slice containing all the visible flag categories with the flags they contain func (c *Command) VisibleFlagCategories() []VisibleFlagCategory { if c.flagCategories == nil { diff --git a/command_test.go b/command_test.go index 9dfd46f476..a953e943cd 100644 --- a/command_test.go +++ b/command_test.go @@ -422,3 +422,30 @@ func TestCommand_CanAddVFlagOnCommands(t *testing.T) { err := app.Run([]string{"foo", "bar"}) expect(t, err, nil) } + +func TestCommand_VisibleSubcCommands(t *testing.T) { + + subc1 := &Command{ + Name: "subc1", + Usage: "subc1 command1", + } + subc3 := &Command{ + Name: "subc3", + Usage: "subc3 command2", + } + c := &Command{ + Name: "bar", + Usage: "this is for testing", + Subcommands: []*Command{ + subc1, + { + Name: "subc2", + Usage: "subc2 command2", + Hidden: true, + }, + subc3, + }, + } + + expect(t, c.VisibleCommands(), []*Command{subc1, subc3}) +} diff --git a/flag.go b/flag.go index 7b5ec498c7..a6fea1c493 100644 --- a/flag.go +++ b/flag.go @@ -129,6 +129,14 @@ type DocGenerationFlag interface { GetEnvVars() []string } +// DocGenerationSliceFlag extends DocGenerationFlag for slice-based flags. +type DocGenerationSliceFlag interface { + DocGenerationFlag + + // IsSliceFlag returns true for flags that can be given multiple times. + IsSliceFlag() bool +} + // VisibleFlag is an interface that allows to check if a flag is visible type VisibleFlag interface { Flag @@ -325,24 +333,13 @@ func stringifyFlag(f Flag) string { usageWithDefault := strings.TrimSpace(usage + defaultValueString) - return withEnvHint(df.GetEnvVars(), - fmt.Sprintf("%s\t%s", prefixedNames(df.Names(), placeholder), usageWithDefault)) -} - -func stringifySliceFlag(usage string, names, defaultVals []string) string { - placeholder, usage := unquoteUsage(usage) - if placeholder == "" { - placeholder = defaultPlaceholder - } - - defaultVal := "" - if len(defaultVals) > 0 { - defaultVal = fmt.Sprintf(formatDefault("%s"), strings.Join(defaultVals, ", ")) + pn := prefixedNames(df.Names(), placeholder) + sliceFlag, ok := f.(DocGenerationSliceFlag) + if ok && sliceFlag.IsSliceFlag() { + pn = pn + " [ " + pn + " ]" } - usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultVal)) - pn := prefixedNames(names, placeholder) - return fmt.Sprintf("%s [ %s ]\t%s", pn, pn, usageWithDefault) + return withEnvHint(df.GetEnvVars(), fmt.Sprintf("%s\t%s", pn, usageWithDefault)) } func hasFlag(flags []Flag, fl Flag) bool { diff --git a/flag_float64_slice.go b/flag_float64_slice.go index 2cb5e4adfa..413aa50e9f 100644 --- a/flag_float64_slice.go +++ b/flag_float64_slice.go @@ -83,7 +83,7 @@ func (f *Float64Slice) Get() interface{} { // String returns a readable representation of this value // (for usage defaults) func (f *Float64SliceFlag) String() string { - return withEnvHint(f.GetEnvVars(), f.stringify()) + return FlagStringer(f) } // TakesValue returns true if the flag takes a value, otherwise false @@ -104,10 +104,13 @@ func (f *Float64SliceFlag) GetCategory() string { // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *Float64SliceFlag) GetValue() string { - if f.Value != nil { - return f.Value.String() + var defaultVals []string + if f.Value != nil && len(f.Value.Value()) > 0 { + for _, i := range f.Value.Value() { + defaultVals = append(defaultVals, strings.TrimRight(strings.TrimRight(fmt.Sprintf("%f", i), "0"), ".")) + } } - return "" + return strings.Join(defaultVals, ", ") } // GetDefaultText returns the default text for this flag @@ -123,6 +126,11 @@ func (f *Float64SliceFlag) GetEnvVars() []string { return f.EnvVars } +// IsSliceFlag implements DocGenerationSliceFlag. +func (f *Float64SliceFlag) IsSliceFlag() bool { + return true +} + // Apply populates the flag given the flag set and environment func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error { // apply any default @@ -169,18 +177,6 @@ func (f *Float64SliceFlag) Get(ctx *Context) []float64 { return ctx.Float64Slice(f.Name) } -func (f *Float64SliceFlag) stringify() string { - var defaultVals []string - - if f.Value != nil && len(f.Value.Value()) > 0 { - for _, i := range f.Value.Value() { - defaultVals = append(defaultVals, strings.TrimRight(strings.TrimRight(fmt.Sprintf("%f", i), "0"), ".")) - } - } - - return stringifySliceFlag(f.Usage, f.Names(), defaultVals) -} - // RunAction executes flag action if set func (f *Float64SliceFlag) RunAction(c *Context) error { if f.Action != nil { diff --git a/flag_generic.go b/flag_generic.go index 5034728c42..358bd966e0 100644 --- a/flag_generic.go +++ b/flag_generic.go @@ -62,6 +62,10 @@ func (f *GenericFlag) Apply(set *flag.FlagSet) error { } for _, name := range f.Names() { + if f.Destination != nil { + set.Var(f.Destination, name, f.Usage) + continue + } set.Var(f.Value, name, f.Usage) } diff --git a/flag_int64_slice.go b/flag_int64_slice.go index d4a11b6a81..c45c43d3ab 100644 --- a/flag_int64_slice.go +++ b/flag_int64_slice.go @@ -84,7 +84,7 @@ func (i *Int64Slice) Get() interface{} { // String returns a readable representation of this value // (for usage defaults) func (f *Int64SliceFlag) String() string { - return withEnvHint(f.GetEnvVars(), f.stringify()) + return FlagStringer(f) } // TakesValue returns true of the flag takes a value, otherwise false @@ -105,10 +105,13 @@ func (f *Int64SliceFlag) GetCategory() string { // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *Int64SliceFlag) GetValue() string { - if f.Value != nil { - return f.Value.String() + var defaultVals []string + if f.Value != nil && len(f.Value.Value()) > 0 { + for _, i := range f.Value.Value() { + defaultVals = append(defaultVals, strconv.FormatInt(i, 10)) + } } - return "" + return strings.Join(defaultVals, ", ") } // GetDefaultText returns the default text for this flag @@ -124,6 +127,11 @@ func (f *Int64SliceFlag) GetEnvVars() []string { return f.EnvVars } +// IsSliceFlag implements DocGenerationSliceFlag. +func (f *Int64SliceFlag) IsSliceFlag() bool { + return true +} + // Apply populates the flag given the flag set and environment func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error { // apply any default @@ -168,17 +176,6 @@ func (f *Int64SliceFlag) Get(ctx *Context) []int64 { return ctx.Int64Slice(f.Name) } -func (f *Int64SliceFlag) stringify() string { - var defaultVals []string - if f.Value != nil && len(f.Value.Value()) > 0 { - for _, i := range f.Value.Value() { - defaultVals = append(defaultVals, strconv.FormatInt(i, 10)) - } - } - - return stringifySliceFlag(f.Usage, f.Names(), defaultVals) -} - // RunAction executes flag action if set func (f *Int64SliceFlag) RunAction(c *Context) error { if f.Action != nil { diff --git a/flag_int_slice.go b/flag_int_slice.go index 2cabe7202f..d4006e594c 100644 --- a/flag_int_slice.go +++ b/flag_int_slice.go @@ -95,7 +95,7 @@ func (i *IntSlice) Get() interface{} { // String returns a readable representation of this value // (for usage defaults) func (f *IntSliceFlag) String() string { - return withEnvHint(f.GetEnvVars(), f.stringify()) + return FlagStringer(f) } // TakesValue returns true of the flag takes a value, otherwise false @@ -116,10 +116,13 @@ func (f *IntSliceFlag) GetCategory() string { // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *IntSliceFlag) GetValue() string { - if f.Value != nil { - return f.Value.String() + var defaultVals []string + if f.Value != nil && len(f.Value.Value()) > 0 { + for _, i := range f.Value.Value() { + defaultVals = append(defaultVals, strconv.Itoa(i)) + } } - return "" + return strings.Join(defaultVals, ", ") } // GetDefaultText returns the default text for this flag @@ -135,6 +138,11 @@ func (f *IntSliceFlag) GetEnvVars() []string { return f.EnvVars } +// IsSliceFlag implements DocGenerationSliceFlag. +func (f *IntSliceFlag) IsSliceFlag() bool { + return true +} + // Apply populates the flag given the flag set and environment func (f *IntSliceFlag) Apply(set *flag.FlagSet) error { // apply any default @@ -188,17 +196,6 @@ func (f *IntSliceFlag) RunAction(c *Context) error { return nil } -func (f *IntSliceFlag) stringify() string { - var defaultVals []string - if f.Value != nil && len(f.Value.Value()) > 0 { - for _, i := range f.Value.Value() { - defaultVals = append(defaultVals, strconv.Itoa(i)) - } - } - - return stringifySliceFlag(f.Usage, f.Names(), defaultVals) -} - // IntSlice looks up the value of a local IntSliceFlag, returns // nil if not found func (cCtx *Context) IntSlice(name string) []int { diff --git a/flag_string_slice.go b/flag_string_slice.go index 7b46a24742..baca2a2fb9 100644 --- a/flag_string_slice.go +++ b/flag_string_slice.go @@ -74,7 +74,7 @@ func (s *StringSlice) Get() interface{} { // String returns a readable representation of this value // (for usage defaults) func (f *StringSliceFlag) String() string { - return withEnvHint(f.GetEnvVars(), f.stringify()) + return FlagStringer(f) } // TakesValue returns true of the flag takes a value, otherwise false @@ -95,10 +95,15 @@ func (f *StringSliceFlag) GetCategory() string { // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *StringSliceFlag) GetValue() string { - if f.Value != nil { - return f.Value.String() + var defaultVals []string + if f.Value != nil && len(f.Value.Value()) > 0 { + for _, s := range f.Value.Value() { + if len(s) > 0 { + defaultVals = append(defaultVals, strconv.Quote(s)) + } + } } - return "" + return strings.Join(defaultVals, ", ") } // GetDefaultText returns the default text for this flag @@ -114,6 +119,11 @@ func (f *StringSliceFlag) GetEnvVars() []string { return f.EnvVars } +// IsSliceFlag implements DocGenerationSliceFlag. +func (f *StringSliceFlag) IsSliceFlag() bool { + return true +} + // Apply populates the flag given the flag set and environment func (f *StringSliceFlag) Apply(set *flag.FlagSet) error { // apply any default @@ -158,19 +168,6 @@ func (f *StringSliceFlag) Get(ctx *Context) []string { return ctx.StringSlice(f.Name) } -func (f *StringSliceFlag) stringify() string { - var defaultVals []string - if f.Value != nil && len(f.Value.Value()) > 0 { - for _, s := range f.Value.Value() { - if len(s) > 0 { - defaultVals = append(defaultVals, strconv.Quote(s)) - } - } - } - - return stringifySliceFlag(f.Usage, f.Names(), defaultVals) -} - // RunAction executes flag action if set func (f *StringSliceFlag) RunAction(c *Context) error { if f.Action != nil { diff --git a/flag_test.go b/flag_test.go index ddd3e67e49..3aafa8bea2 100644 --- a/flag_test.go +++ b/flag_test.go @@ -307,12 +307,12 @@ func TestFlagStringifying(t *testing.T) { { name: "float64-slice-flag", fl: &Float64SliceFlag{Name: "pizzas"}, - expected: "--pizzas value\t", + expected: "--pizzas value [ --pizzas value ]\t", }, { name: "float64-slice-flag-with-default-text", fl: &Float64SliceFlag{Name: "pepperonis", DefaultText: "shaved"}, - expected: "--pepperonis value\t(default: shaved)", + expected: "--pepperonis value [ --pepperonis value ]\t(default: shaved)", }, { name: "generic-flag", @@ -337,7 +337,7 @@ func TestFlagStringifying(t *testing.T) { { name: "int-slice-flag", fl: &IntSliceFlag{Name: "pencils"}, - expected: "--pencils value\t", + expected: "--pencils value [ --pencils value ]\t", }, { name: "int-slice-flag-with-default-text", @@ -347,7 +347,7 @@ func TestFlagStringifying(t *testing.T) { { name: "uint-slice-flag", fl: &UintSliceFlag{Name: "pencils"}, - expected: "--pencils value\t", + expected: "--pencils value [ --pencils value ]\t", }, { name: "uint-slice-flag-with-default-text", @@ -367,22 +367,22 @@ func TestFlagStringifying(t *testing.T) { { name: "int64-slice-flag", fl: &Int64SliceFlag{Name: "drawers"}, - expected: "--drawers value\t", + expected: "--drawers value [ --drawers value ]\t", }, { name: "int64-slice-flag-with-default-text", fl: &Int64SliceFlag{Name: "handles", DefaultText: "-2"}, - expected: "--handles value\t(default: -2)", + expected: "--handles value [ --handles value ]\t(default: -2)", }, { name: "uint64-slice-flag", fl: &Uint64SliceFlag{Name: "drawers"}, - expected: "--drawers value\t", + expected: "--drawers value [ --drawers value ]\t", }, { name: "uint64-slice-flag-with-default-text", fl: &Uint64SliceFlag{Name: "handles", DefaultText: "-2"}, - expected: "--handles value\t(default: -2)", + expected: "--handles value [ --handles value ]\t(default: -2)", }, { name: "path-flag", @@ -407,12 +407,12 @@ func TestFlagStringifying(t *testing.T) { { name: "string-slice-flag", fl: &StringSliceFlag{Name: "meow-sounds"}, - expected: "--meow-sounds value\t", + expected: "--meow-sounds value [ --meow-sounds value ]\t", }, { name: "string-slice-flag-with-default-text", fl: &StringSliceFlag{Name: "moo-sounds", DefaultText: "awoo"}, - expected: "--moo-sounds value\t(default: awoo)", + expected: "--moo-sounds value [ --moo-sounds value ]\t(default: awoo)", }, { name: "timestamp-flag", @@ -2724,6 +2724,53 @@ func TestParseGeneric(t *testing.T) { }).Run([]string{"run", "-s", "10,20"}) } +type genericType struct { + s []string +} + +func (g *genericType) Set(value string) error { + g.s = strings.Split(value, "-") + return nil +} + +func (g *genericType) String() string { + return strings.Join(g.s, "-") +} + +func TestParseDestinationGeneric(t *testing.T) { + expectedString := "abc1-123d" + expectedGeneric := &genericType{[]string{"abc1", "123d"}} + dest := &genericType{} + + _ = (&App{ + Flags: []Flag{ + &GenericFlag{ + Name: "dest", + Destination: dest, + }, + }, + Action: func(ctx *Context) error { + + if !reflect.DeepEqual(dest, expectedGeneric) { + t.Errorf( + "expected destination generic: %+v, actual: %+v", + expectedGeneric, + dest, + ) + } + + if dest.String() != expectedString { + t.Errorf( + "expected destination string: %s, actual: %s", + expectedString, + dest.String(), + ) + } + return nil + }, + }).Run([]string{"run", "--dest", expectedString}) +} + func TestParseGenericFromEnv(t *testing.T) { defer resetEnv(os.Environ()) os.Clearenv() diff --git a/flag_uint64_slice.go b/flag_uint64_slice.go index e60c3ea8af..61bb30b551 100644 --- a/flag_uint64_slice.go +++ b/flag_uint64_slice.go @@ -88,7 +88,7 @@ func (i *Uint64Slice) Get() interface{} { // String returns a readable representation of this value // (for usage defaults) func (f *Uint64SliceFlag) String() string { - return withEnvHint(f.GetEnvVars(), f.stringify()) + return FlagStringer(f) } // TakesValue returns true of the flag takes a value, otherwise false @@ -109,10 +109,13 @@ func (f *Uint64SliceFlag) GetCategory() string { // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *Uint64SliceFlag) GetValue() string { - if f.Value != nil { - return f.Value.String() + var defaultVals []string + if f.Value != nil && len(f.Value.Value()) > 0 { + for _, i := range f.Value.Value() { + defaultVals = append(defaultVals, strconv.FormatUint(i, 10)) + } } - return "" + return strings.Join(defaultVals, ", ") } // GetDefaultText returns the default text for this flag @@ -128,6 +131,11 @@ func (f *Uint64SliceFlag) GetEnvVars() []string { return f.EnvVars } +// IsSliceFlag implements DocGenerationSliceFlag. +func (f *Uint64SliceFlag) IsSliceFlag() bool { + return true +} + // Apply populates the flag given the flag set and environment func (f *Uint64SliceFlag) Apply(set *flag.FlagSet) error { // apply any default @@ -172,17 +180,6 @@ func (f *Uint64SliceFlag) Get(ctx *Context) []uint64 { return ctx.Uint64Slice(f.Name) } -func (f *Uint64SliceFlag) stringify() string { - var defaultVals []string - if f.Value != nil && len(f.Value.Value()) > 0 { - for _, i := range f.Value.Value() { - defaultVals = append(defaultVals, strconv.FormatUint(i, 10)) - } - } - - return stringifySliceFlag(f.Usage, f.Names(), defaultVals) -} - // Uint64Slice looks up the value of a local Uint64SliceFlag, returns // nil if not found func (cCtx *Context) Uint64Slice(name string) []uint64 { diff --git a/flag_uint_slice.go b/flag_uint_slice.go index 350b29ccf0..363aa657f3 100644 --- a/flag_uint_slice.go +++ b/flag_uint_slice.go @@ -99,7 +99,7 @@ func (i *UintSlice) Get() interface{} { // String returns a readable representation of this value // (for usage defaults) func (f *UintSliceFlag) String() string { - return withEnvHint(f.GetEnvVars(), f.stringify()) + return FlagStringer(f) } // TakesValue returns true of the flag takes a value, otherwise false @@ -120,10 +120,13 @@ func (f *UintSliceFlag) GetCategory() string { // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *UintSliceFlag) GetValue() string { - if f.Value != nil { - return f.Value.String() + var defaultVals []string + if f.Value != nil && len(f.Value.Value()) > 0 { + for _, i := range f.Value.Value() { + defaultVals = append(defaultVals, strconv.FormatUint(uint64(i), 10)) + } } - return "" + return strings.Join(defaultVals, ", ") } // GetDefaultText returns the default text for this flag @@ -139,6 +142,11 @@ func (f *UintSliceFlag) GetEnvVars() []string { return f.EnvVars } +// IsSliceFlag implements DocGenerationSliceFlag. +func (f *UintSliceFlag) IsSliceFlag() bool { + return true +} + // Apply populates the flag given the flag set and environment func (f *UintSliceFlag) Apply(set *flag.FlagSet) error { // apply any default @@ -183,17 +191,6 @@ func (f *UintSliceFlag) Get(ctx *Context) []uint { return ctx.UintSlice(f.Name) } -func (f *UintSliceFlag) stringify() string { - var defaultVals []string - if f.Value != nil && len(f.Value.Value()) > 0 { - for _, i := range f.Value.Value() { - defaultVals = append(defaultVals, strconv.FormatUint(uint64(i), 10)) - } - } - - return stringifySliceFlag(f.Usage, f.Names(), defaultVals) -} - // UintSlice looks up the value of a local UintSliceFlag, returns // nil if not found func (cCtx *Context) UintSlice(name string) []uint { diff --git a/godoc-current.txt b/godoc-current.txt index efbac4d1e8..aae4e0bd41 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -32,7 +32,7 @@ var ( SuggestDidYouMeanTemplate string = suggestDidYouMeanTemplate ) var AppHelpTemplate = `NAME: - {{$v := offset .Name 6}}{{wrap .Name 3}}{{if .Usage}} - {{wrap .Usage $v}}{{end}} + {{template "helpNameTemplate" .}} USAGE: {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} @@ -41,52 +41,39 @@ VERSION: {{.Version}}{{end}}{{end}}{{if .Description}} DESCRIPTION: - {{wrap .Description 3}}{{end}}{{if len .Authors}} + {{template "descriptionTemplate" .}}{{end}} +{{- if len .Authors}} -AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: - {{range $index, $author := .Authors}}{{if $index}} - {{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}} +AUTHOR{{template "authorsTemplate" .}}{{end}}{{if .VisibleCommands}} -COMMANDS:{{range .VisibleCategories}}{{if .Name}} - {{.Name}}:{{range .VisibleCommands}} - {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{ $cv := offsetCommands .VisibleCommands 5}}{{range .VisibleCommands}} - {{$s := join .Names ", "}}{{$s}}{{ $sp := subtract $cv (offset $s 3) }}{{ indent $sp ""}}{{wrap .Usage $cv}}{{end}}{{end}}{{end}}{{end}}{{if .VisibleFlagCategories}} +COMMANDS:{{template "visibleCommandCategoryTemplate" .}}{{end}}{{if .VisibleFlagCategories}} -GLOBAL OPTIONS:{{range .VisibleFlagCategories}} - {{if .Name}}{{.Name}} - {{end}}{{range .Flags}}{{.}} - {{end}}{{end}}{{else}}{{if .VisibleFlags}} +GLOBAL OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}} -GLOBAL OPTIONS: - {{range $index, $option := .VisibleFlags}}{{if $index}} - {{end}}{{wrap $option.String 6}}{{end}}{{end}}{{end}}{{if .Copyright}} +GLOBAL OPTIONS:{{template "visibleFlagTemplate" .}}{{end}}{{if .Copyright}} COPYRIGHT: - {{wrap .Copyright 3}}{{end}} + {{template "copyrightTemplate" .}}{{end}} ` AppHelpTemplate is the text template for the Default help topic. cli.go uses text/template to render templates. You can render custom help text by setting this variable. var CommandHelpTemplate = `NAME: - {{$v := offset .HelpName 6}}{{wrap .HelpName 3}}{{if .Usage}} - {{wrap .Usage $v}}{{end}} + {{template "helpNameTemplate" .}} USAGE: - {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}} + {{template "usageTemplate" .}}{{if .Category}} CATEGORY: {{.Category}}{{end}}{{if .Description}} DESCRIPTION: - {{wrap .Description 3}}{{end}}{{if .VisibleFlagCategories}} + {{template "descriptionTemplate" .}}{{end}}{{if .VisibleFlagCategories}} -OPTIONS:{{range .VisibleFlagCategories}} - {{if .Name}}{{.Name}} - {{end}}{{range .Flags}}{{.}}{{end}}{{end}}{{else}}{{if .VisibleFlags}} +OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}} -OPTIONS: - {{range .VisibleFlags}}{{.}}{{end}}{{end}}{{end}} -` +OPTIONS:{{template "visibleFlagTemplate" .}}{{end}}` CommandHelpTemplate is the text template for the command help topic. cli.go uses text/template to render templates. You can render custom help text by setting this variable. @@ -145,22 +132,19 @@ var OsExiter = os.Exit os.Exit. var SubcommandHelpTemplate = `NAME: - {{.HelpName}} - {{.Usage}} + {{template "helpNameTemplate" .}} USAGE: {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}} DESCRIPTION: - {{wrap .Description 3}}{{end}} + {{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}} -COMMANDS:{{range .VisibleCategories}}{{if .Name}} - {{.Name}}:{{range .VisibleCommands}} - {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{ $cv := offsetCommands .VisibleCommands 5}}{{range .VisibleCommands}} - {{$s := join .Names ", "}}{{$s}}{{ $sp := subtract $cv (offset $s 3) }}{{ indent $sp ""}}{{wrap .Usage $cv}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} +COMMANDS:{{template "visibleCommandTemplate" .}}{{end}}{{if .VisibleFlagCategories}} -OPTIONS: - {{range .VisibleFlags}}{{.}}{{end}}{{end}} -` +OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}} + +OPTIONS:{{template "visibleFlagTemplate" .}}{{end}}` SubcommandHelpTemplate is the text template for the subcommand help topic. cli.go uses text/template to render templates. You can render custom help text by setting this variable. @@ -585,6 +569,9 @@ func (c *Command) Run(ctx *Context) (err error) Run invokes the command given the context, parses ctx.Args() to generate command-specific flags +func (c *Command) VisibleCommands() []*Command + VisibleCommands returns a slice of the Commands with Hidden=false + func (c *Command) VisibleFlagCategories() []VisibleFlagCategory VisibleFlagCategories returns a slice containing all the visible flag categories with the flags they contain @@ -753,6 +740,14 @@ type DocGenerationFlag interface { DocGenerationFlag is an interface that allows documentation generation for the flag +type DocGenerationSliceFlag interface { + DocGenerationFlag + + // IsSliceFlag returns true for flags that can be given multiple times. + IsSliceFlag() bool +} + DocGenerationSliceFlag extends DocGenerationFlag for slice-based flags. + type DurationFlag struct { Name string @@ -1071,6 +1066,9 @@ func (f *Float64SliceFlag) IsRequired() bool func (f *Float64SliceFlag) IsSet() bool IsSet returns whether or not the flag has been set through env or file +func (f *Float64SliceFlag) IsSliceFlag() bool + IsSliceFlag implements DocGenerationSliceFlag. + func (f *Float64SliceFlag) IsVisible() bool IsVisible returns true if the flag is not hidden, otherwise false @@ -1306,6 +1304,9 @@ func (f *Int64SliceFlag) IsRequired() bool func (f *Int64SliceFlag) IsSet() bool IsSet returns whether or not the flag has been set through env or file +func (f *Int64SliceFlag) IsSliceFlag() bool + IsSliceFlag implements DocGenerationSliceFlag. + func (f *Int64SliceFlag) IsVisible() bool IsVisible returns true if the flag is not hidden, otherwise false @@ -1471,6 +1472,9 @@ func (f *IntSliceFlag) IsRequired() bool func (f *IntSliceFlag) IsSet() bool IsSet returns whether or not the flag has been set through env or file +func (f *IntSliceFlag) IsSliceFlag() bool + IsSliceFlag implements DocGenerationSliceFlag. + func (f *IntSliceFlag) IsVisible() bool IsVisible returns true if the flag is not hidden, otherwise false @@ -1810,6 +1814,9 @@ func (f *StringSliceFlag) IsRequired() bool func (f *StringSliceFlag) IsSet() bool IsSet returns whether or not the flag has been set through env or file +func (f *StringSliceFlag) IsSliceFlag() bool + IsSliceFlag implements DocGenerationSliceFlag. + func (f *StringSliceFlag) IsVisible() bool IsVisible returns true if the flag is not hidden, otherwise false @@ -2071,6 +2078,9 @@ func (f *Uint64SliceFlag) IsRequired() bool func (f *Uint64SliceFlag) IsSet() bool IsSet returns whether or not the flag has been set through env or file +func (f *Uint64SliceFlag) IsSliceFlag() bool + IsSliceFlag implements DocGenerationSliceFlag. + func (f *Uint64SliceFlag) IsVisible() bool IsVisible returns true if the flag is not hidden, otherwise false @@ -2227,6 +2237,9 @@ func (f *UintSliceFlag) IsRequired() bool func (f *UintSliceFlag) IsSet() bool IsSet returns whether or not the flag has been set through env or file +func (f *UintSliceFlag) IsSliceFlag() bool + IsSliceFlag implements DocGenerationSliceFlag. + func (f *UintSliceFlag) IsVisible() bool IsVisible returns true if the flag is not hidden, otherwise false diff --git a/help.go b/help.go index 6929a226ab..d78444a0c6 100644 --- a/help.go +++ b/help.go @@ -242,7 +242,11 @@ func ShowCommandHelp(ctx *Context, command string) error { c.Subcommands = append(c.Subcommands, helpCommandDontUse) } if !ctx.App.HideHelp && HelpFlag != nil { - c.appendFlag(HelpFlag) + if c.flagCategories == nil { + c.flagCategories = newFlagCategoriesFromFlags([]Flag{HelpFlag}) + } else { + c.flagCategories.AddFlag("", HelpFlag) + } } templ := c.CustomHelpTemplate if templ == "" { @@ -358,6 +362,17 @@ func printHelpCustom(out io.Writer, templ string, data interface{}, customFuncs w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0) t := template.Must(template.New("help").Funcs(funcMap).Parse(templ)) + t.New("helpNameTemplate").Parse(helpNameTemplate) + t.New("usageTemplate").Parse(usageTemplate) + t.New("descriptionTemplate").Parse(descriptionTemplate) + t.New("visibleCommandTemplate").Parse(visibleCommandTemplate) + t.New("copyrightTemplate").Parse(copyrightTemplate) + t.New("versionTemplate").Parse(versionTemplate) + t.New("visibleFlagCategoryTemplate").Parse(visibleFlagCategoryTemplate) + t.New("visibleFlagTemplate").Parse(visibleFlagTemplate) + t.New("visibleGlobalFlagCategoryTemplate").Parse(strings.Replace(visibleFlagCategoryTemplate, "OPTIONS", "GLOBAL OPTIONS", -1)) + t.New("authorsTemplate").Parse(authorsTemplate) + t.New("visibleCommandCategoryTemplate").Parse(visibleCommandCategoryTemplate) err := t.Execute(w, data) if err != nil { diff --git a/help_test.go b/help_test.go index d7cdfa2d8f..37410a5449 100644 --- a/help_test.go +++ b/help_test.go @@ -1443,3 +1443,79 @@ OPTIONS: output.String(), expected) } } + +func TestWrappedHelpSubcommand(t *testing.T) { + + // Reset HelpPrinter after this test. + defer func(old helpPrinter) { + HelpPrinter = old + }(HelpPrinter) + + output := new(bytes.Buffer) + app := &App{ + Name: "cli.test", + Writer: output, + Commands: []*Command{ + { + Name: "bar", + Aliases: []string{"a"}, + Usage: "add a task to the list", + UsageText: "this is an even longer way of describing adding a task to the list", + Description: "and a description long enough to wrap in this test case", + Action: func(c *Context) error { + return nil + }, + Subcommands: []*Command{ + { + Name: "grok", + Usage: "remove an existing template", + UsageText: "longer usage text goes here, la la la, hopefully this is long enough to wrap even more", + Action: func(c *Context) error { + return nil + }, + Flags: []Flag{ + &StringFlag{ + Name: "test-f", + Usage: "my test usage", + }, + }, + }, + }, + }, + }, + } + + HelpPrinter = func(w io.Writer, templ string, data interface{}) { + funcMap := map[string]interface{}{ + "wrapAt": func() int { + return 30 + }, + } + + HelpPrinterCustom(w, templ, data, funcMap) + } + + _ = app.Run([]string{"foo", "bar", "help", "grok"}) + + expected := `NAME: + cli.test bar grok - remove + an + existing + template + +USAGE: + longer usage text goes + here, la la la, hopefully + this is long enough to wrap + even more + +OPTIONS: + --help, -h show help (default: false) + --test-f value my test usage +` + + if output.String() != expected { + t.Errorf("Unexpected wrapping, got:\n%s\nexpected: %s", + output.String(), expected) + } +} diff --git a/template.go b/template.go index 9e13604f35..1133b8907c 100644 --- a/template.go +++ b/template.go @@ -1,10 +1,38 @@ package cli +var helpNameTemplate = `{{$v := offset .HelpName 6}}{{wrap .HelpName 3}}{{if .Usage}} - {{wrap .Usage $v}}{{end}}` +var usageTemplate = `{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}` +var descriptionTemplate = `{{wrap .Description 3}}` +var authorsTemplate = `{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: + {{range $index, $author := .Authors}}{{if $index}} + {{end}}{{$author}}{{end}}` +var visibleCommandTemplate = `{{ $cv := offsetCommands .VisibleCommands 5}}{{range .VisibleCommands}} + {{$s := join .Names ", "}}{{$s}}{{ $sp := subtract $cv (offset $s 3) }}{{ indent $sp ""}}{{wrap .Usage $cv}}{{end}}` +var visibleCommandCategoryTemplate = `{{range .VisibleCategories}}{{if .Name}} + {{.Name}}:{{range .VisibleCommands}} + {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{template "visibleCommandTemplate" .}}{{end}}{{end}}` +var visibleFlagCategoryTemplate = `{{range .VisibleFlagCategories}} + {{if .Name}}{{.Name}} + + {{end}}{{$flglen := len .Flags}}{{range $i, $e := .Flags}}{{if eq (subtract $flglen $i) 1}}{{$e}} +{{else}}{{$e}} + {{end}}{{end}}{{end}}` + +var visibleFlagTemplate = `{{range $index, $option := .VisibleFlags}}{{if $index}}{{end}} + {{wrap $option.String 6}}{{end}}` + +var versionTemplate = `{{if .Version}}{{if not .HideVersion}} + +VERSION: + {{.Version}}{{end}}{{end}}` + +var copyrightTemplate = `{{wrap .Copyright 3}}` + // AppHelpTemplate is the text template for the Default help topic. // cli.go uses text/template to render templates. You can // render custom help text by setting this variable. var AppHelpTemplate = `NAME: - {{$v := offset .Name 6}}{{wrap .Name 3}}{{if .Usage}} - {{wrap .Usage $v}}{{end}} + {{template "helpNameTemplate" .}} USAGE: {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} @@ -13,73 +41,57 @@ VERSION: {{.Version}}{{end}}{{end}}{{if .Description}} DESCRIPTION: - {{wrap .Description 3}}{{end}}{{if len .Authors}} + {{template "descriptionTemplate" .}}{{end}} +{{- if len .Authors}} -AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: - {{range $index, $author := .Authors}}{{if $index}} - {{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}} +AUTHOR{{template "authorsTemplate" .}}{{end}}{{if .VisibleCommands}} -COMMANDS:{{range .VisibleCategories}}{{if .Name}} - {{.Name}}:{{range .VisibleCommands}} - {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{ $cv := offsetCommands .VisibleCommands 5}}{{range .VisibleCommands}} - {{$s := join .Names ", "}}{{$s}}{{ $sp := subtract $cv (offset $s 3) }}{{ indent $sp ""}}{{wrap .Usage $cv}}{{end}}{{end}}{{end}}{{end}}{{if .VisibleFlagCategories}} +COMMANDS:{{template "visibleCommandCategoryTemplate" .}}{{end}}{{if .VisibleFlagCategories}} -GLOBAL OPTIONS:{{range .VisibleFlagCategories}} - {{if .Name}}{{.Name}} - {{end}}{{range .Flags}}{{.}} - {{end}}{{end}}{{else}}{{if .VisibleFlags}} +GLOBAL OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}} -GLOBAL OPTIONS: - {{range $index, $option := .VisibleFlags}}{{if $index}} - {{end}}{{wrap $option.String 6}}{{end}}{{end}}{{end}}{{if .Copyright}} +GLOBAL OPTIONS:{{template "visibleFlagTemplate" .}}{{end}}{{if .Copyright}} COPYRIGHT: - {{wrap .Copyright 3}}{{end}} + {{template "copyrightTemplate" .}}{{end}} ` // CommandHelpTemplate is the text template for the command help topic. // cli.go uses text/template to render templates. You can // render custom help text by setting this variable. var CommandHelpTemplate = `NAME: - {{$v := offset .HelpName 6}}{{wrap .HelpName 3}}{{if .Usage}} - {{wrap .Usage $v}}{{end}} + {{template "helpNameTemplate" .}} USAGE: - {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}} + {{template "usageTemplate" .}}{{if .Category}} CATEGORY: {{.Category}}{{end}}{{if .Description}} DESCRIPTION: - {{wrap .Description 3}}{{end}}{{if .VisibleFlagCategories}} + {{template "descriptionTemplate" .}}{{end}}{{if .VisibleFlagCategories}} -OPTIONS:{{range .VisibleFlagCategories}} - {{if .Name}}{{.Name}} - {{end}}{{range .Flags}}{{.}}{{end}}{{end}}{{else}}{{if .VisibleFlags}} +OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}} -OPTIONS: - {{range .VisibleFlags}}{{.}}{{end}}{{end}}{{end}} -` +OPTIONS:{{template "visibleFlagTemplate" .}}{{end}}` // SubcommandHelpTemplate is the text template for the subcommand help topic. // cli.go uses text/template to render templates. You can // render custom help text by setting this variable. var SubcommandHelpTemplate = `NAME: - {{.HelpName}} - {{.Usage}} + {{template "helpNameTemplate" .}} USAGE: {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}} DESCRIPTION: - {{wrap .Description 3}}{{end}} + {{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}} -COMMANDS:{{range .VisibleCategories}}{{if .Name}} - {{.Name}}:{{range .VisibleCommands}} - {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{ $cv := offsetCommands .VisibleCommands 5}}{{range .VisibleCommands}} - {{$s := join .Names ", "}}{{$s}}{{ $sp := subtract $cv (offset $s 3) }}{{ indent $sp ""}}{{wrap .Usage $cv}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} +COMMANDS:{{template "visibleCommandTemplate" .}}{{end}}{{if .VisibleFlagCategories}} -OPTIONS: - {{range .VisibleFlags}}{{.}}{{end}}{{end}} -` +OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}} + +OPTIONS:{{template "visibleFlagTemplate" .}}{{end}}` var MarkdownDocTemplate = `{{if gt .SectionNum 0}}% {{ .App.Name }} {{ .SectionNum }} diff --git a/testdata/godoc-v2.x.txt b/testdata/godoc-v2.x.txt index efbac4d1e8..aae4e0bd41 100644 --- a/testdata/godoc-v2.x.txt +++ b/testdata/godoc-v2.x.txt @@ -32,7 +32,7 @@ var ( SuggestDidYouMeanTemplate string = suggestDidYouMeanTemplate ) var AppHelpTemplate = `NAME: - {{$v := offset .Name 6}}{{wrap .Name 3}}{{if .Usage}} - {{wrap .Usage $v}}{{end}} + {{template "helpNameTemplate" .}} USAGE: {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} @@ -41,52 +41,39 @@ VERSION: {{.Version}}{{end}}{{end}}{{if .Description}} DESCRIPTION: - {{wrap .Description 3}}{{end}}{{if len .Authors}} + {{template "descriptionTemplate" .}}{{end}} +{{- if len .Authors}} -AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: - {{range $index, $author := .Authors}}{{if $index}} - {{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}} +AUTHOR{{template "authorsTemplate" .}}{{end}}{{if .VisibleCommands}} -COMMANDS:{{range .VisibleCategories}}{{if .Name}} - {{.Name}}:{{range .VisibleCommands}} - {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{ $cv := offsetCommands .VisibleCommands 5}}{{range .VisibleCommands}} - {{$s := join .Names ", "}}{{$s}}{{ $sp := subtract $cv (offset $s 3) }}{{ indent $sp ""}}{{wrap .Usage $cv}}{{end}}{{end}}{{end}}{{end}}{{if .VisibleFlagCategories}} +COMMANDS:{{template "visibleCommandCategoryTemplate" .}}{{end}}{{if .VisibleFlagCategories}} -GLOBAL OPTIONS:{{range .VisibleFlagCategories}} - {{if .Name}}{{.Name}} - {{end}}{{range .Flags}}{{.}} - {{end}}{{end}}{{else}}{{if .VisibleFlags}} +GLOBAL OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}} -GLOBAL OPTIONS: - {{range $index, $option := .VisibleFlags}}{{if $index}} - {{end}}{{wrap $option.String 6}}{{end}}{{end}}{{end}}{{if .Copyright}} +GLOBAL OPTIONS:{{template "visibleFlagTemplate" .}}{{end}}{{if .Copyright}} COPYRIGHT: - {{wrap .Copyright 3}}{{end}} + {{template "copyrightTemplate" .}}{{end}} ` AppHelpTemplate is the text template for the Default help topic. cli.go uses text/template to render templates. You can render custom help text by setting this variable. var CommandHelpTemplate = `NAME: - {{$v := offset .HelpName 6}}{{wrap .HelpName 3}}{{if .Usage}} - {{wrap .Usage $v}}{{end}} + {{template "helpNameTemplate" .}} USAGE: - {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}} + {{template "usageTemplate" .}}{{if .Category}} CATEGORY: {{.Category}}{{end}}{{if .Description}} DESCRIPTION: - {{wrap .Description 3}}{{end}}{{if .VisibleFlagCategories}} + {{template "descriptionTemplate" .}}{{end}}{{if .VisibleFlagCategories}} -OPTIONS:{{range .VisibleFlagCategories}} - {{if .Name}}{{.Name}} - {{end}}{{range .Flags}}{{.}}{{end}}{{end}}{{else}}{{if .VisibleFlags}} +OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}} -OPTIONS: - {{range .VisibleFlags}}{{.}}{{end}}{{end}}{{end}} -` +OPTIONS:{{template "visibleFlagTemplate" .}}{{end}}` CommandHelpTemplate is the text template for the command help topic. cli.go uses text/template to render templates. You can render custom help text by setting this variable. @@ -145,22 +132,19 @@ var OsExiter = os.Exit os.Exit. var SubcommandHelpTemplate = `NAME: - {{.HelpName}} - {{.Usage}} + {{template "helpNameTemplate" .}} USAGE: {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}} DESCRIPTION: - {{wrap .Description 3}}{{end}} + {{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}} -COMMANDS:{{range .VisibleCategories}}{{if .Name}} - {{.Name}}:{{range .VisibleCommands}} - {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{ $cv := offsetCommands .VisibleCommands 5}}{{range .VisibleCommands}} - {{$s := join .Names ", "}}{{$s}}{{ $sp := subtract $cv (offset $s 3) }}{{ indent $sp ""}}{{wrap .Usage $cv}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} +COMMANDS:{{template "visibleCommandTemplate" .}}{{end}}{{if .VisibleFlagCategories}} -OPTIONS: - {{range .VisibleFlags}}{{.}}{{end}}{{end}} -` +OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}} + +OPTIONS:{{template "visibleFlagTemplate" .}}{{end}}` SubcommandHelpTemplate is the text template for the subcommand help topic. cli.go uses text/template to render templates. You can render custom help text by setting this variable. @@ -585,6 +569,9 @@ func (c *Command) Run(ctx *Context) (err error) Run invokes the command given the context, parses ctx.Args() to generate command-specific flags +func (c *Command) VisibleCommands() []*Command + VisibleCommands returns a slice of the Commands with Hidden=false + func (c *Command) VisibleFlagCategories() []VisibleFlagCategory VisibleFlagCategories returns a slice containing all the visible flag categories with the flags they contain @@ -753,6 +740,14 @@ type DocGenerationFlag interface { DocGenerationFlag is an interface that allows documentation generation for the flag +type DocGenerationSliceFlag interface { + DocGenerationFlag + + // IsSliceFlag returns true for flags that can be given multiple times. + IsSliceFlag() bool +} + DocGenerationSliceFlag extends DocGenerationFlag for slice-based flags. + type DurationFlag struct { Name string @@ -1071,6 +1066,9 @@ func (f *Float64SliceFlag) IsRequired() bool func (f *Float64SliceFlag) IsSet() bool IsSet returns whether or not the flag has been set through env or file +func (f *Float64SliceFlag) IsSliceFlag() bool + IsSliceFlag implements DocGenerationSliceFlag. + func (f *Float64SliceFlag) IsVisible() bool IsVisible returns true if the flag is not hidden, otherwise false @@ -1306,6 +1304,9 @@ func (f *Int64SliceFlag) IsRequired() bool func (f *Int64SliceFlag) IsSet() bool IsSet returns whether or not the flag has been set through env or file +func (f *Int64SliceFlag) IsSliceFlag() bool + IsSliceFlag implements DocGenerationSliceFlag. + func (f *Int64SliceFlag) IsVisible() bool IsVisible returns true if the flag is not hidden, otherwise false @@ -1471,6 +1472,9 @@ func (f *IntSliceFlag) IsRequired() bool func (f *IntSliceFlag) IsSet() bool IsSet returns whether or not the flag has been set through env or file +func (f *IntSliceFlag) IsSliceFlag() bool + IsSliceFlag implements DocGenerationSliceFlag. + func (f *IntSliceFlag) IsVisible() bool IsVisible returns true if the flag is not hidden, otherwise false @@ -1810,6 +1814,9 @@ func (f *StringSliceFlag) IsRequired() bool func (f *StringSliceFlag) IsSet() bool IsSet returns whether or not the flag has been set through env or file +func (f *StringSliceFlag) IsSliceFlag() bool + IsSliceFlag implements DocGenerationSliceFlag. + func (f *StringSliceFlag) IsVisible() bool IsVisible returns true if the flag is not hidden, otherwise false @@ -2071,6 +2078,9 @@ func (f *Uint64SliceFlag) IsRequired() bool func (f *Uint64SliceFlag) IsSet() bool IsSet returns whether or not the flag has been set through env or file +func (f *Uint64SliceFlag) IsSliceFlag() bool + IsSliceFlag implements DocGenerationSliceFlag. + func (f *Uint64SliceFlag) IsVisible() bool IsVisible returns true if the flag is not hidden, otherwise false @@ -2227,6 +2237,9 @@ func (f *UintSliceFlag) IsRequired() bool func (f *UintSliceFlag) IsSet() bool IsSet returns whether or not the flag has been set through env or file +func (f *UintSliceFlag) IsSliceFlag() bool + IsSliceFlag implements DocGenerationSliceFlag. + func (f *UintSliceFlag) IsVisible() bool IsVisible returns true if the flag is not hidden, otherwise false