Merge pull request #1525 from urfave/v3-porting

Porting changes from `main` to `v3-dev-main` (circa 2022-10-09)
This commit is contained in:
Dan Buch 2022-10-16 14:08:16 -04:00 committed by GitHub
commit 25897ce4dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 623 additions and 364 deletions

View File

@ -43,9 +43,14 @@ jobs:
GFLAGS: -tags urfave_cli_no_docs
- run: make check-binary-size
- run: make yamlfmt
- run: make diffcheck
- if: matrix.go == '1.19.x' && matrix.os == 'ubuntu-latest'
run: make generate
- run: make diffcheck
# TODO: switch once v3 is released {{
# - if: matrix.go == '1.19.x' && matrix.os == 'ubuntu-latest'
- if: 'false'
run: make v2diff
# }}
- if: success() && matrix.go == '1.19.x' && matrix.os == 'ubuntu-latest'
uses: codecov/codecov-action@v3
with:

View File

@ -7,7 +7,7 @@
GO_RUN_BUILD := go run internal/build/build.go
.PHONY: all
all: generate vet test check-binary-size gfmrun yamlfmt v2diff
all: generate vet test check-binary-size gfmrun yamlfmt
# NOTE: this is a special catch-all rule to run any of the commands
# defined in internal/build/build.go with optional arguments passed

View File

@ -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
@ -956,6 +956,37 @@ func TestApp_UseShortOptionHandlingSubCommand_missing_value(t *testing.T) {
expect(t, err, errors.New("flag needs an argument: -n"))
}
func TestApp_UseShortOptionAfterSliceFlag(t *testing.T) {
var one, two bool
var name string
var sliceValDest StringSlice
var sliceVal []string
expected := "expectedName"
app := newTestApp()
app.UseShortOptionHandling = true
app.Flags = []Flag{
&StringSliceFlag{Name: "env", Aliases: []string{"e"}, Destination: &sliceValDest},
&BoolFlag{Name: "one", Aliases: []string{"o"}},
&BoolFlag{Name: "two", Aliases: []string{"t"}},
&StringFlag{Name: "name", Aliases: []string{"n"}},
}
app.Action = func(c *Context) error {
sliceVal = c.StringSlice("env")
one = c.Bool("one")
two = c.Bool("two")
name = c.String("name")
return nil
}
_ = app.Run([]string{"", "-e", "foo", "-on", expected})
expect(t, sliceVal, []string{"foo"})
expect(t, sliceValDest.Value(), []string{"foo"})
expect(t, one, true)
expect(t, two, false)
expect(t, name, expected)
}
func TestApp_Float64Flag(t *testing.T) {
var meters float64

2
cli.go
View File

@ -22,4 +22,4 @@
// }
package cli
//go:generate go run cmd/urfave-cli-genflags/main.go
//go:generate make -C cmd/urfave-cli-genflags run

View File

@ -1,6 +1,9 @@
GOIMPORTS_BIN ?= $(shell which goimports || true)
GOTEST_FLAGS ?= -v --coverprofile main.coverprofile --covermode count --cover github.com/urfave/cli/v2/cmd/urfave-cli-genflags
GOBUILD_FLAGS ?= -x
export GOIMPORTS_BIN
.PHONY: all
all: test build smoke-test
@ -19,3 +22,7 @@ smoke-test: build
.PHONY: show-cover
show-cover:
go tool cover -func main.coverprofile
.PHONY: run
run: build
./urfave-cli-genflags

View File

@ -75,16 +75,6 @@ func (f *{{.TypeName}}) TakesValue() bool {
return "{{.TypeName }}" != "BoolFlag"
}
{{if .GenerateDefaultText}}
// GetDefaultText returns the default text for this flag
func (f *{{.TypeName}}) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
{{end}}
{{end}}{{/* /if .GenerateFlagInterface */}}
{{end}}{{/* /range .SortedFlagTypes */}}

View File

@ -12,13 +12,13 @@ func Test{{.TypeName}}_SatisfiesFlagInterface(t *testing.T) {
}
func Test{{.TypeName}}_SatisfiesRequiredFlagInterface(t *testing.T) {
var f {{$.UrfaveCLITestNamespace}}Flag = &{{$.UrfaveCLITestNamespace}}{{.TypeName}}{}
var f {{$.UrfaveCLITestNamespace}}RequiredFlag = &{{$.UrfaveCLITestNamespace}}{{.TypeName}}{}
_ = f.IsRequired()
}
func Test{{.TypeName}}_SatisfiesVisibleFlagInterface(t *testing.T) {
var f {{$.UrfaveCLITestNamespace}}Flag = &{{$.UrfaveCLITestNamespace}}{{.TypeName}}{}
var f {{$.UrfaveCLITestNamespace}}VisibleFlag = &{{$.UrfaveCLITestNamespace}}{{.TypeName}}{}
_ = f.IsVisible()
}

View File

@ -4,6 +4,7 @@ import (
"bytes"
"context"
_ "embed"
"fmt"
"log"
"os"
"os/exec"
@ -37,6 +38,9 @@ var (
func sh(ctx context.Context, exe string, args ...string) (string, error) {
cmd := exec.CommandContext(ctx, exe, args...)
cmd.Stderr = os.Stderr
fmt.Fprintf(os.Stderr, "# ---> %s\n", cmd)
outBytes, err := cmd.Output()
return string(outBytes), err
}
@ -89,10 +93,19 @@ func main() {
Aliases: []string{"N"},
Value: "cli.",
},
&cli.PathFlag{
Name: "goimports",
EnvVars: []string{"GOIMPORTS_BIN"},
Value: filepath.Join(top, ".local/bin/goimports"),
},
},
Action: runGenFlags,
}
if err := os.Chdir(top); err != nil {
log.Fatal(err)
}
if err := app.RunContext(ctx, os.Args); err != nil {
log.Fatal(err)
}
@ -163,11 +176,11 @@ func runGenFlags(cCtx *cli.Context) error {
return err
}
if _, err := sh(cCtx.Context, "goimports", "-w", cCtx.Path("generated-output")); err != nil {
if _, err := sh(cCtx.Context, cCtx.Path("goimports"), "-w", cCtx.Path("generated-output")); err != nil {
return err
}
if _, err := sh(cCtx.Context, "goimports", "-w", cCtx.Path("generated-test-output")); err != nil {
if _, err := sh(cCtx.Context, cCtx.Path("goimports"), "-w", cCtx.Path("generated-test-output")); err != nil {
return err
}

View File

@ -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 {

View File

@ -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})
}

29
flag.go
View File

@ -135,6 +135,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
}
// Countable is an interface to enable detection of flag values which support
// repetitive flags
type Countable interface {
@ -332,24 +340,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
pn := prefixedNames(df.Names(), placeholder)
sliceFlag, ok := f.(DocGenerationSliceFlag)
if ok && sliceFlag.IsSliceFlag() {
pn = pn + " [ " + pn + " ]"
}
defaultVal := ""
if len(defaultVals) > 0 {
defaultVal = fmt.Sprintf(formatDefault("%s"), strings.Join(defaultVals, ", "))
}
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 {

View File

@ -12,6 +12,14 @@ func (f *DurationFlag) GetValue() string {
return f.Value.String()
}
// GetDefaultText returns the default text for this flag
func (f *DurationFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// Apply populates the flag given the flag set and environment
func (f *DurationFlag) Apply(set *flag.FlagSet) error {
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {

View File

@ -12,6 +12,14 @@ func (f *Float64Flag) GetValue() string {
return fmt.Sprintf("%v", f.Value)
}
// GetDefaultText returns the default text for this flag
func (f *Float64Flag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// Apply populates the flag given the flag set and environment
func (f *Float64Flag) Apply(set *flag.FlagSet) error {
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {

View File

@ -83,16 +83,32 @@ 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)
}
// 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
func (f *Float64SliceFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// IsSliceFlag implements DocGenerationSliceFlag.
func (f *Float64SliceFlag) IsSliceFlag() bool {
return true
}
// Apply populates the flag given the flag set and environment
@ -141,18 +157,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 {

View File

@ -20,6 +20,14 @@ func (f *GenericFlag) GetValue() string {
return ""
}
// GetDefaultText returns the default text for this flag
func (f *GenericFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// Apply takes the flagset and calls Set on the generic flag with the value
// provided by the user for parsing by the flag
func (f *GenericFlag) Apply(set *flag.FlagSet) error {
@ -34,6 +42,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)
}

View File

@ -12,6 +12,14 @@ func (f *IntFlag) GetValue() string {
return fmt.Sprintf("%d", f.Value)
}
// GetDefaultText returns the default text for this flag
func (f *IntFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// Apply populates the flag given the flag set and environment
func (f *IntFlag) Apply(set *flag.FlagSet) error {
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {

View File

@ -12,6 +12,14 @@ func (f *Int64Flag) GetValue() string {
return fmt.Sprintf("%d", f.Value)
}
// GetDefaultText returns the default text for this flag
func (f *Int64Flag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// Apply populates the flag given the flag set and environment
func (f *Int64Flag) Apply(set *flag.FlagSet) error {
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {

View File

@ -84,16 +84,32 @@ 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)
}
// 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
func (f *Int64SliceFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// IsSliceFlag implements DocGenerationSliceFlag.
func (f *Int64SliceFlag) IsSliceFlag() bool {
return true
}
// Apply populates the flag given the flag set and environment
@ -140,17 +156,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 {

View File

@ -95,16 +95,32 @@ 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)
}
// 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
func (f *IntSliceFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// IsSliceFlag implements DocGenerationSliceFlag.
func (f *IntSliceFlag) IsSliceFlag() bool {
return true
}
// Apply populates the flag given the flag set and environment
@ -151,17 +167,6 @@ func (f *IntSliceFlag) Get(ctx *Context) []int {
return ctx.IntSlice(f.Name)
}
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)
}
// RunAction executes flag action if set
func (f *IntSliceFlag) RunAction(c *Context) error {
if f.Action != nil {

View File

@ -74,16 +74,34 @@ 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)
}
// 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
func (f *StringSliceFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// IsSliceFlag implements DocGenerationSliceFlag.
func (f *StringSliceFlag) IsSliceFlag() bool {
return true
}
// Apply populates the flag given the flag set and environment
@ -130,19 +148,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 {

View File

@ -225,7 +225,7 @@ func TestFlagsFromEnv(t *testing.T) {
f, ok := test.flag.(DocGenerationFlag)
if !ok {
t.Errorf("flag %v needs to implement DocGenerationFlag to retrieve env vars", test.flag)
t.Errorf("flag %[1]q (%[1]T) needs to implement DocGenerationFlag to retrieve env vars", test.flag)
}
envVarSlice := f.GetEnvVars()
_ = os.Setenv(envVarSlice[0], test.input)
@ -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()

View File

@ -81,6 +81,14 @@ func (f *TimestampFlag) GetValue() string {
return ""
}
// GetDefaultText returns the default text for this flag
func (f *TimestampFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// Apply populates the flag given the flag set and environment
func (f *TimestampFlag) Apply(set *flag.FlagSet) error {
if f.Layout == "" {

View File

@ -37,6 +37,14 @@ func (f *UintFlag) GetValue() string {
return fmt.Sprintf("%d", f.Value)
}
// GetDefaultText returns the default text for this flag
func (f *UintFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// Get returns the flags value in the given Context.
func (f *UintFlag) Get(ctx *Context) uint {
return ctx.Uint(f.Name)

View File

@ -37,6 +37,14 @@ func (f *Uint64Flag) GetValue() string {
return fmt.Sprintf("%d", f.Value)
}
// GetDefaultText returns the default text for this flag
func (f *Uint64Flag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// Get returns the flags value in the given Context.
func (f *Uint64Flag) Get(ctx *Context) uint64 {
return ctx.Uint64(f.Name)

View File

@ -88,31 +88,19 @@ 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())
}
// TakesValue returns true of the flag takes a value, otherwise false
func (f *Uint64SliceFlag) TakesValue() bool {
return true
}
// GetUsage returns the usage string for the flag
func (f *Uint64SliceFlag) GetUsage() string {
return f.Usage
}
// GetCategory returns the category for the flag
func (f *Uint64SliceFlag) GetCategory() string {
return f.Category
return FlagStringer(f)
}
// 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
@ -123,9 +111,9 @@ func (f *Uint64SliceFlag) GetDefaultText() string {
return f.GetValue()
}
// GetEnvVars returns the env vars for this flag
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
@ -172,17 +160,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)
}
// RunAction executes flag action if set
func (f *Uint64SliceFlag) RunAction(c *Context) error {
if f.Action != nil {

View File

@ -99,31 +99,19 @@ 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())
}
// TakesValue returns true of the flag takes a value, otherwise false
func (f *UintSliceFlag) TakesValue() bool {
return true
}
// GetUsage returns the usage string for the flag
func (f *UintSliceFlag) GetUsage() string {
return f.Usage
}
// GetCategory returns the category for the flag
func (f *UintSliceFlag) GetCategory() string {
return f.Category
return FlagStringer(f)
}
// 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
@ -134,9 +122,9 @@ func (f *UintSliceFlag) GetDefaultText() string {
return f.GetValue()
}
// GetEnvVars returns the env vars for this flag
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
@ -183,17 +171,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)
}
// RunAction executes flag action if set
func (f *UintSliceFlag) RunAction(c *Context) error {
if f.Action != nil {

View File

@ -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.
@ -586,6 +570,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
@ -754,6 +741,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
@ -1077,6 +1072,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
@ -1312,6 +1310,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
@ -1477,6 +1478,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
@ -1817,6 +1821,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
@ -2057,7 +2064,7 @@ func (f *Uint64SliceFlag) Get(ctx *Context) []uint64
Get returns the flags value in the given Context.
func (f *Uint64SliceFlag) GetCategory() string
GetCategory returns the category for the flag
GetCategory returns the category of the flag
func (f *Uint64SliceFlag) GetDefaultText() string
GetDefaultText returns the default text for this flag
@ -2078,6 +2085,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
@ -2091,7 +2101,7 @@ func (f *Uint64SliceFlag) String() string
String returns a readable representation of this value (for usage defaults)
func (f *Uint64SliceFlag) TakesValue() bool
TakesValue returns true of the flag takes a value, otherwise false
TakesValue returns true if the flag takes a value, otherwise false
type UintFlag struct {
Name string
@ -2216,7 +2226,7 @@ func (f *UintSliceFlag) Get(ctx *Context) []uint
Get returns the flags value in the given Context.
func (f *UintSliceFlag) GetCategory() string
GetCategory returns the category for the flag
GetCategory returns the category of the flag
func (f *UintSliceFlag) GetDefaultText() string
GetDefaultText returns the default text for this flag
@ -2237,6 +2247,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
@ -2250,7 +2263,7 @@ func (f *UintSliceFlag) String() string
String returns a readable representation of this value (for usage defaults)
func (f *UintSliceFlag) TakesValue() bool
TakesValue returns true of the flag takes a value, otherwise false
TakesValue returns true if the flag takes a value, otherwise false
type VisibleFlag interface {
Flag

36
help.go
View File

@ -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 {
@ -468,25 +483,28 @@ func nindent(spaces int, v string) string {
}
func wrap(input string, offset int, wrapAt int) string {
var sb strings.Builder
var ss []string
lines := strings.Split(input, "\n")
padding := strings.Repeat(" ", offset)
for i, line := range lines {
if i != 0 {
sb.WriteString(padding)
}
if line == "" {
ss = append(ss, line)
} else {
wrapped := wrapLine(line, offset, wrapAt, padding)
if i == 0 {
ss = append(ss, wrapped)
} else {
ss = append(ss, padding+wrapped)
sb.WriteString(wrapLine(line, offset, wrapAt, padding))
}
if i != len(lines)-1 {
sb.WriteString("\n")
}
}
return sb.String()
return strings.Join(ss, "\n")
}
func wrapLine(input string, offset int, wrapAt int, padding string) string {

View File

@ -1213,6 +1213,13 @@ func TestDefaultCompleteWithFlags(t *testing.T) {
}
}
func TestWrap(t *testing.T) {
emptywrap := wrap("", 4, 16)
if emptywrap != "" {
t.Errorf("Wrapping empty line should return empty line. Got '%s'.", emptywrap)
}
}
func TestWrappedHelp(t *testing.T) {
// Reset HelpPrinter after this test.
@ -1276,7 +1283,7 @@ DESCRIPTION:
App.Description string long
enough that it should be
wrapped in this test
with a newline
and an indented line
@ -1294,8 +1301,8 @@ COPYRIGHT:
that it should be wrapped.
Including newlines.
And also indented lines.
And then another long line.
Blah blah blah does anybody
ever read these things?
@ -1436,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)
}
}

View File

@ -46,7 +46,10 @@ func parseIter(set *flag.FlagSet, ip iterativeParser, args []string, shellComple
}
// swap current argument with the split version
args = append(args[:i], append(shortOpts, args[i+1:]...)...)
// do not include args that parsed correctly so far as it would
// trigger Value.Set() on those args and would result in
// duplicates for slice type flags
args = append(shortOpts, args[i+1:]...)
argsWereSplit = true
break
}
@ -56,13 +59,6 @@ func parseIter(set *flag.FlagSet, ip iterativeParser, args []string, shellComple
if !argsWereSplit {
return err
}
// Since custom parsing failed, replace the flag set before retrying
newSet, err := ip.newFlagSet()
if err != nil {
return err
}
*set = *newSet
}
}

View File

@ -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 }}

View File

@ -66,14 +66,6 @@ func (f *Float64SliceFlag) TakesValue() bool {
return "Float64SliceFlag" != "BoolFlag"
}
// GetDefaultText returns the default text for this flag
func (f *Float64SliceFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// GenericFlag is a flag with type Generic
type GenericFlag struct {
Name string
@ -143,14 +135,6 @@ func (f *GenericFlag) TakesValue() bool {
return "GenericFlag" != "BoolFlag"
}
// GetDefaultText returns the default text for this flag
func (f *GenericFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// Int64SliceFlag is a flag with type *Int64Slice
type Int64SliceFlag struct {
Name string
@ -213,14 +197,6 @@ func (f *Int64SliceFlag) TakesValue() bool {
return "Int64SliceFlag" != "BoolFlag"
}
// GetDefaultText returns the default text for this flag
func (f *Int64SliceFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// IntSliceFlag is a flag with type *IntSlice
type IntSliceFlag struct {
Name string
@ -283,14 +259,6 @@ func (f *IntSliceFlag) TakesValue() bool {
return "IntSliceFlag" != "BoolFlag"
}
// GetDefaultText returns the default text for this flag
func (f *IntSliceFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// PathFlag is a flag with type Path
type PathFlag struct {
Name string
@ -424,14 +392,6 @@ func (f *StringSliceFlag) TakesValue() bool {
return "StringSliceFlag" != "BoolFlag"
}
// GetDefaultText returns the default text for this flag
func (f *StringSliceFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// TimestampFlag is a flag with type *Timestamp
type TimestampFlag struct {
Name string
@ -503,14 +463,6 @@ func (f *TimestampFlag) TakesValue() bool {
return "TimestampFlag" != "BoolFlag"
}
// GetDefaultText returns the default text for this flag
func (f *TimestampFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// Uint64SliceFlag is a flag with type *Uint64Slice
type Uint64SliceFlag struct {
Name string
@ -553,6 +505,26 @@ func (f *Uint64SliceFlag) IsVisible() bool {
return !f.Hidden
}
// GetCategory returns the category of the flag
func (f *Uint64SliceFlag) GetCategory() string {
return f.Category
}
// GetUsage returns the usage string for the flag
func (f *Uint64SliceFlag) GetUsage() string {
return f.Usage
}
// GetEnvVars returns the env vars for this flag
func (f *Uint64SliceFlag) GetEnvVars() []string {
return f.EnvVars
}
// TakesValue returns true if the flag takes a value, otherwise false
func (f *Uint64SliceFlag) TakesValue() bool {
return "Uint64SliceFlag" != "BoolFlag"
}
// UintSliceFlag is a flag with type *UintSlice
type UintSliceFlag struct {
Name string
@ -595,6 +567,26 @@ func (f *UintSliceFlag) IsVisible() bool {
return !f.Hidden
}
// GetCategory returns the category of the flag
func (f *UintSliceFlag) GetCategory() string {
return f.Category
}
// GetUsage returns the usage string for the flag
func (f *UintSliceFlag) GetUsage() string {
return f.Usage
}
// GetEnvVars returns the env vars for this flag
func (f *UintSliceFlag) GetEnvVars() []string {
return f.EnvVars
}
// TakesValue returns true if the flag takes a value, otherwise false
func (f *UintSliceFlag) TakesValue() bool {
return "UintSliceFlag" != "BoolFlag"
}
// BoolFlag is a flag with type bool
type BoolFlag struct {
Name string
@ -731,14 +723,6 @@ func (f *Float64Flag) TakesValue() bool {
return "Float64Flag" != "BoolFlag"
}
// GetDefaultText returns the default text for this flag
func (f *Float64Flag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// IntFlag is a flag with type int
type IntFlag struct {
Name string
@ -808,14 +792,6 @@ func (f *IntFlag) TakesValue() bool {
return "IntFlag" != "BoolFlag"
}
// GetDefaultText returns the default text for this flag
func (f *IntFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// Int64Flag is a flag with type int64
type Int64Flag struct {
Name string
@ -885,14 +861,6 @@ func (f *Int64Flag) TakesValue() bool {
return "Int64Flag" != "BoolFlag"
}
// GetDefaultText returns the default text for this flag
func (f *Int64Flag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// StringFlag is a flag with type string
type StringFlag struct {
Name string
@ -1029,14 +997,6 @@ func (f *DurationFlag) TakesValue() bool {
return "DurationFlag" != "BoolFlag"
}
// GetDefaultText returns the default text for this flag
func (f *DurationFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// UintFlag is a flag with type uint
type UintFlag struct {
Name string
@ -1106,14 +1066,6 @@ func (f *UintFlag) TakesValue() bool {
return "UintFlag" != "BoolFlag"
}
// GetDefaultText returns the default text for this flag
func (f *UintFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// Uint64Flag is a flag with type uint64
type Uint64Flag struct {
Name string
@ -1183,12 +1135,4 @@ func (f *Uint64Flag) TakesValue() bool {
return "Uint64Flag" != "BoolFlag"
}
// GetDefaultText returns the default text for this flag
func (f *Uint64Flag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// vim:ro

View File

@ -167,6 +167,18 @@ func TestUint64SliceFlag_SatisfiesFlagInterface(t *testing.T) {
_ = f.Names()
}
func TestUint64SliceFlag_SatisfiesRequiredFlagInterface(t *testing.T) {
var f cli.RequiredFlag = &cli.Uint64SliceFlag{}
_ = f.IsRequired()
}
func TestUint64SliceFlag_SatisfiesVisibleFlagInterface(t *testing.T) {
var f cli.VisibleFlag = &cli.Uint64SliceFlag{}
_ = f.IsVisible()
}
func TestUintSliceFlag_SatisfiesFlagInterface(t *testing.T) {
var f cli.Flag = &cli.UintSliceFlag{}
@ -174,6 +186,18 @@ func TestUintSliceFlag_SatisfiesFlagInterface(t *testing.T) {
_ = f.Names()
}
func TestUintSliceFlag_SatisfiesRequiredFlagInterface(t *testing.T) {
var f cli.RequiredFlag = &cli.UintSliceFlag{}
_ = f.IsRequired()
}
func TestUintSliceFlag_SatisfiesVisibleFlagInterface(t *testing.T) {
var f cli.VisibleFlag = &cli.UintSliceFlag{}
_ = f.IsVisible()
}
func TestBoolFlag_SatisfiesFlagInterface(t *testing.T) {
var f cli.Flag = &cli.BoolFlag{}