From 13b7eedc1340210c208c0853dbad66e6cd02162f Mon Sep 17 00:00:00 2001 From: Kevin Cantwell Date: Thu, 21 Apr 2016 23:02:06 -0400 Subject: [PATCH 1/3] Parses usage placeholders via back quotes. Resolves #333 --- flag.go | 52 +++++++++++++++++++++++++++++++++++++++++----------- flag_test.go | 28 +++++++++++++++------------- 2 files changed, 56 insertions(+), 24 deletions(-) diff --git a/flag.go b/flag.go index e951c2d..42f6a61 100644 --- a/flag.go +++ b/flag.go @@ -74,7 +74,8 @@ type GenericFlag struct { // help text to the user (uses the String() method of the generic flag to show // the value) func (f GenericFlag) String() string { - return withEnvHint(f.EnvVar, fmt.Sprintf("%s %v\t%v", prefixedNames(f.Name), f.FormatValueHelp(), f.Usage)) + placeholder, usage := unquoteUsage(f.Usage) + return withEnvHint(f.EnvVar, fmt.Sprintf("%s %v\t%v", prefixedNames(f.Name, placeholder), f.FormatValueHelp(), usage)) } func (f GenericFlag) FormatValueHelp() string { @@ -143,7 +144,8 @@ type StringSliceFlag struct { func (f StringSliceFlag) String() string { firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ") pref := prefixFor(firstName) - return withEnvHint(f.EnvVar, fmt.Sprintf("%s [%v]\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage)) + placeholder, usage := unquoteUsage(f.Usage) + return withEnvHint(f.EnvVar, fmt.Sprintf("%s [%v]\t%v", prefixedNames(f.Name, placeholder), pref+firstName+" option "+pref+firstName+" option", usage)) } // Apply populates the flag given the flag set and environment @@ -212,7 +214,8 @@ type IntSliceFlag struct { func (f IntSliceFlag) String() string { firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ") pref := prefixFor(firstName) - return withEnvHint(f.EnvVar, fmt.Sprintf("%s [%v]\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage)) + placeholder, usage := unquoteUsage(f.Usage) + return withEnvHint(f.EnvVar, fmt.Sprintf("%s [%v]\t%v", prefixedNames(f.Name, placeholder), pref+firstName+" option "+pref+firstName+" option", usage)) } // Apply populates the flag given the flag set and environment @@ -257,7 +260,8 @@ type BoolFlag struct { // String returns a readable representation of this value (for usage defaults) func (f BoolFlag) String() string { - return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage)) + placeholder, usage := unquoteUsage(f.Usage) + return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name, placeholder), usage)) } // Apply populates the flag given the flag set and environment @@ -300,7 +304,8 @@ type BoolTFlag struct { // String returns a readable representation of this value (for usage defaults) func (f BoolTFlag) String() string { - return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage)) + placeholder, usage := unquoteUsage(f.Usage) + return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name, placeholder), usage)) } // Apply populates the flag given the flag set and environment @@ -343,7 +348,8 @@ type StringFlag struct { // String returns the usage func (f StringFlag) String() string { - return withEnvHint(f.EnvVar, fmt.Sprintf("%s %v\t%v", prefixedNames(f.Name), f.FormatValueHelp(), f.Usage)) + placeholder, usage := unquoteUsage(f.Usage) + return withEnvHint(f.EnvVar, fmt.Sprintf("%s %v\t%v", prefixedNames(f.Name, placeholder), f.FormatValueHelp(), usage)) } func (f StringFlag) FormatValueHelp() string { @@ -391,7 +397,8 @@ type IntFlag struct { // String returns the usage func (f IntFlag) String() string { - return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage)) + placeholder, usage := unquoteUsage(f.Usage) + return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name, placeholder), f.Value, usage)) } // Apply populates the flag given the flag set and environment @@ -434,7 +441,8 @@ type DurationFlag struct { // String returns a readable representation of this value (for usage defaults) func (f DurationFlag) String() string { - return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage)) + placeholder, usage := unquoteUsage(f.Usage) + return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name, placeholder), f.Value, usage)) } // Apply populates the flag given the flag set and environment @@ -477,7 +485,8 @@ type Float64Flag struct { // String returns the usage func (f Float64Flag) String() string { - return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage)) + placeholder, usage := unquoteUsage(f.Usage) + return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name, placeholder), f.Value, usage)) } // Apply populates the flag given the flag set and environment @@ -517,16 +526,37 @@ func prefixFor(name string) (prefix string) { return } -func prefixedNames(fullName string) (prefixed string) { +// Returns the placeholder, if any, and the unquoted usage string. +func unquoteUsage(usage string) (string, string) { + for i := 0; i < len(usage); i++ { + if usage[i] == '`' { + for j := i + 1; j < len(usage); j++ { + if usage[j] == '`' { + name := usage[i+1 : j] + usage = usage[:i] + name + usage[j+1:] + return name, usage + } + } + break + } + } + return "", usage +} + +func prefixedNames(fullName, placeholder string) string { + var prefixed string parts := strings.Split(fullName, ",") for i, name := range parts { name = strings.Trim(name, " ") prefixed += prefixFor(name) + name + if placeholder != "" { + prefixed += " " + placeholder + } if i < len(parts)-1 { prefixed += ", " } } - return + return prefixed } func withEnvHint(envVar, str string) string { diff --git a/flag_test.go b/flag_test.go index 3caa70a..6bebf1b 100644 --- a/flag_test.go +++ b/flag_test.go @@ -4,9 +4,9 @@ import ( "fmt" "os" "reflect" + "runtime" "strings" "testing" - "runtime" ) var boolFlagTests = []struct { @@ -31,19 +31,21 @@ func TestBoolFlagHelpOutput(t *testing.T) { var stringFlagTests = []struct { name string + usage string value string expected string }{ - {"help", "", "--help \t"}, - {"h", "", "-h \t"}, - {"h", "", "-h \t"}, - {"test", "Something", "--test \"Something\"\t"}, + {"help", "", "", "--help \t"}, + {"h", "", "", "-h \t"}, + {"h", "", "", "-h \t"}, + {"test", "", "Something", "--test \"Something\"\t"}, + {"config,c", "Load configuration from `FILE`", "", "--config FILE, -c FILE \tLoad configuration from FILE"}, } func TestStringFlagHelpOutput(t *testing.T) { for _, test := range stringFlagTests { - flag := StringFlag{Name: test.name, Value: test.value} + flag := StringFlag{Name: test.name, Usage: test.usage, Value: test.value} output := flag.String() if output != test.expected { @@ -64,7 +66,7 @@ func TestStringFlagWithEnvVarHelpOutput(t *testing.T) { expectedSuffix = " [%APP_FOO%]" } if !strings.HasSuffix(output, expectedSuffix) { - t.Errorf("%s does not end with" + expectedSuffix, output) + t.Errorf("%s does not end with"+expectedSuffix, output) } } } @@ -120,7 +122,7 @@ func TestStringSliceFlagWithEnvVarHelpOutput(t *testing.T) { expectedSuffix = " [%APP_QWWX%]" } if !strings.HasSuffix(output, expectedSuffix) { - t.Errorf("%q does not end with" + expectedSuffix, output) + t.Errorf("%q does not end with"+expectedSuffix, output) } } } @@ -157,7 +159,7 @@ func TestIntFlagWithEnvVarHelpOutput(t *testing.T) { expectedSuffix = " [%APP_BAR%]" } if !strings.HasSuffix(output, expectedSuffix) { - t.Errorf("%s does not end with" + expectedSuffix, output) + t.Errorf("%s does not end with"+expectedSuffix, output) } } } @@ -194,7 +196,7 @@ func TestDurationFlagWithEnvVarHelpOutput(t *testing.T) { expectedSuffix = " [%APP_BAR%]" } if !strings.HasSuffix(output, expectedSuffix) { - t.Errorf("%s does not end with" + expectedSuffix, output) + t.Errorf("%s does not end with"+expectedSuffix, output) } } } @@ -238,7 +240,7 @@ func TestIntSliceFlagWithEnvVarHelpOutput(t *testing.T) { expectedSuffix = " [%APP_SMURF%]" } if !strings.HasSuffix(output, expectedSuffix) { - t.Errorf("%q does not end with" + expectedSuffix, output) + t.Errorf("%q does not end with"+expectedSuffix, output) } } } @@ -275,7 +277,7 @@ func TestFloat64FlagWithEnvVarHelpOutput(t *testing.T) { expectedSuffix = " [%APP_BAZ%]" } if !strings.HasSuffix(output, expectedSuffix) { - t.Errorf("%s does not end with" + expectedSuffix, output) + t.Errorf("%s does not end with"+expectedSuffix, output) } } } @@ -313,7 +315,7 @@ func TestGenericFlagWithEnvVarHelpOutput(t *testing.T) { expectedSuffix = " [%APP_ZAP%]" } if !strings.HasSuffix(output, expectedSuffix) { - t.Errorf("%s does not end with" + expectedSuffix, output) + t.Errorf("%s does not end with"+expectedSuffix, output) } } } From c1c8825a460d1440306cb6875ec9d3a9be36a93e Mon Sep 17 00:00:00 2001 From: Kevin Cantwell Date: Mon, 25 Apr 2016 10:50:29 -0400 Subject: [PATCH 2/3] updates README with flag usage placeholder instructions --- README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README.md b/README.md index d9371cf..524723e 100644 --- a/README.md +++ b/README.md @@ -195,6 +195,27 @@ app.Action = func(c *cli.Context) { See full list of flags at http://godoc.org/github.com/codegangsta/cli +#### Placeholder Values + +Sometimes it's useful to specify a flag's value within the usage string itself. Such placeholders are +indicated with back quotes. + +For example this: +```go +cli.StringFlag{ + Name: "config, c", + Usage: "Load configuration from `FILE`", +} +``` + +Will result in help output like: + +``` +--config FILE, -c FILE Load configuration from FILE +``` + +Note that only the first placeholder is used. Subsequent back-quoted words will be left as-is. + #### Alternate Names You can set alternate (or short) names for flags by providing a comma-delimited list for the `Name`. e.g. From 4733699ce30faa633c4db36ebfbe427963593d7d Mon Sep 17 00:00:00 2001 From: Jesse Szwedko Date: Mon, 25 Apr 2016 19:47:03 -0700 Subject: [PATCH 3/3] Update changelog with placeholder support --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 907637b..074bfb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ ## [Unreleased] +### Added +- Support for placeholders in flag usage strings + ## [1.14.0] - 2016-04-03 (backfilled 2016-04-25) ### Added - Codebeat badge