Call FlagStringer in String() method of slice flags
The default help template relies on the String() method of Flag to render the flag. For most flag types, String() indirects through FlagStringer, so that is the best place to customize flag rendering. FlagStringer was not called for slice flags because their help output differs from other flags in two ways: there can be multiple default values, and the flag name is shown two times to indicate that the flag can be specified multiple times. To make multiple values work in the FlagStringer, I simply changed GetValue() to return all values. Showing the flag more than once is achieved through a new interface, DocGenerationSliceFlag, which the FlagStringer uses to decide whether the flag is a slice flag type.
This commit is contained in:
parent
4959a9fa9b
commit
82ea9f70c0
29
flag.go
29
flag.go
@ -126,6 +126,14 @@ type Flag interface {
|
||||
RunAction(*Context) error
|
||||
}
|
||||
|
||||
// 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 {
|
||||
@ -300,24 +308,13 @@ func stringifyFlag(f Flag) string {
|
||||
|
||||
usageWithDefault := strings.TrimSpace(usage + defaultValueString)
|
||||
|
||||
return withEnvHint(f.GetEnvVars(),
|
||||
fmt.Sprintf("%s\t%s", prefixedNames(f.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 {
|
||||
|
@ -83,16 +83,24 @@ 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, ", ")
|
||||
}
|
||||
|
||||
// IsSliceFlag implements DocGenerationSliceFlag.
|
||||
func (f *Float64SliceFlag) IsSliceFlag() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Apply populates the flag given the flag set and environment
|
||||
@ -141,18 +149,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 {
|
||||
|
@ -84,16 +84,24 @@ 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, ", ")
|
||||
}
|
||||
|
||||
// IsSliceFlag implements DocGenerationSliceFlag.
|
||||
func (f *Int64SliceFlag) IsSliceFlag() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Apply populates the flag given the flag set and environment
|
||||
@ -140,17 +148,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 {
|
||||
|
@ -95,16 +95,24 @@ 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, ", ")
|
||||
}
|
||||
|
||||
// IsSliceFlag implements DocGenerationSliceFlag.
|
||||
func (f *IntSliceFlag) IsSliceFlag() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Apply populates the flag given the flag set and environment
|
||||
@ -151,17 +159,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 {
|
||||
|
@ -74,16 +74,26 @@ 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, ", ")
|
||||
}
|
||||
|
||||
// IsSliceFlag implements DocGenerationSliceFlag.
|
||||
func (f *StringSliceFlag) IsSliceFlag() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Apply populates the flag given the flag set and environment
|
||||
@ -130,19 +140,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 {
|
||||
|
20
flag_test.go
20
flag_test.go
@ -298,12 +298,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",
|
||||
@ -328,7 +328,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",
|
||||
@ -338,7 +338,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",
|
||||
@ -358,22 +358,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",
|
||||
@ -398,12 +398,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",
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
// RunAction executes flag action if set
|
||||
func (f *Uint64SliceFlag) RunAction(c *Context) error {
|
||||
if f.Action != nil {
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
// RunAction executes flag action if set
|
||||
func (f *UintSliceFlag) RunAction(c *Context) error {
|
||||
if f.Action != nil {
|
||||
|
Loading…
Reference in New Issue
Block a user