mirror of
https://github.com/golang/go
synced 2024-11-19 03:44:40 -07:00
flag: nicer usage messages
Make PrintDefaults print an easier-to-read format, and allow the user to control it a bit by putting a hint into the usage string. Here is the new doc comment for PrintDefaults, which does the work: PrintDefaults prints, to standard error unless configured otherwise, a usage message showing the default settings of all defined command-line flags. For an integer valued flag x, the default output has the form -x int usage-message-for-x (default 7) The usage message will appear on a separate line except for single- letter boolean flags. Boolean flags omit the type, since they can be used without an actual value, and the parenthetical default is omitted if the default is the zero value for the type. The type, here int, can be replaced by a string of the user's choosing by placing in the usage string for the flag a back-quoted name; the first such item in the message is taken to be a parameter name to show in the message and the back quotes are stripped from the message when displayed. For instance, given flag.String("I", "", "search `directory` for include files") the output will be -I directory search directory for include files. Given A = flag.Bool("A", false, "for bootstrapping, allow 'any' type") B = flag.Bool("Alongflagname", false, "disable bounds checking") C = flag.Bool("C", true, "a boolean defaulting to true") D = flag.String("D", "", "set relative `path` for local imports") F = flag.Float64("F", 2.7, "a non-zero float") G = flag.Float64("G", 0, "a float that defaults to zero") N = flag.Int("N", 27, "a non-zero int") Z = flag.Int("Z", 0, "an int that defaults to zero") T = flag.Duration("deltaT", 0, "a duration") the old output was -A=false: for bootstrapping, allow 'any' type -Alongflagname=false: disable bounds checking -C=true: a boolean defaulting to true -D="": set relative `path` for local imports -F=2.7: a non-zero float -G=0: a float that defaults to zero -N=27: a non-zero int -Z=0: an int that defaults to zero -deltaT=0: a duration and the new output is -A for bootstrapping, allow 'any' type -Alongflagname disable bounds checking -C a boolean defaulting to true (default true) -D path set relative path for local imports -F float a non-zero float (default 2.7) -G float a float that defaults to zero -N int a non-zero int (default 27) -Z int an int that defaults to zero -deltaT duration a duration Change-Id: I54ab3cd5610d551422b004d95ab78305e06a395d Reviewed-on: https://go-review.googlesource.com/7330 Reviewed-by: Andrew Gerrand <adg@golang.org> Reviewed-by: Russ Cox <rsc@golang.org>
This commit is contained in:
parent
6d0e87afe6
commit
51d66601c4
108
src/flag/flag.go
108
src/flag/flag.go
@ -373,20 +373,110 @@ func Set(name, value string) error {
|
||||
return CommandLine.Set(name, value)
|
||||
}
|
||||
|
||||
// PrintDefaults prints, to standard error unless configured
|
||||
// otherwise, the default values of all defined flags in the set.
|
||||
// isZeroValue guesses whether the string represents the zero
|
||||
// value for a flag. It is not accurate but in practice works OK.
|
||||
func isZeroValue(value string) bool {
|
||||
switch value {
|
||||
case "false":
|
||||
return true
|
||||
case "":
|
||||
return true
|
||||
case "0":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// UnquoteUsage extracts a back-quoted name from the usage
|
||||
// string for a flag and returns it and the un-quoted usage.
|
||||
// Given "a `name` to show" it returns ("name", "a name to show").
|
||||
// If there are no back quotes, the name is an educated guess of the
|
||||
// type of the flag's value, or the empty string if the flag is boolean.
|
||||
func UnquoteUsage(flag *Flag) (name string, usage string) {
|
||||
// Look for a back-quoted name, but avoid the strings package.
|
||||
usage = flag.Usage
|
||||
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 // Only one back quote; use type name.
|
||||
}
|
||||
}
|
||||
// No explicit name, so use type if we can find one.
|
||||
name = "value"
|
||||
switch flag.Value.(type) {
|
||||
case boolFlag:
|
||||
name = ""
|
||||
case *durationValue:
|
||||
name = "duration"
|
||||
case *float64Value:
|
||||
name = "float"
|
||||
case *intValue, *int64Value:
|
||||
name = "int"
|
||||
case *stringValue:
|
||||
name = "string"
|
||||
case *uintValue, *uint64Value:
|
||||
name = "uint"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// PrintDefaults prints to standard error the default values of all
|
||||
// defined command-line flags in the set. See the documentation for
|
||||
// the global function PrintDefaults for more information.
|
||||
func (f *FlagSet) PrintDefaults() {
|
||||
f.VisitAll(func(flag *Flag) {
|
||||
format := " -%s=%s: %s\n"
|
||||
if _, ok := flag.Value.(*stringValue); ok {
|
||||
// put quotes on the value
|
||||
format = " -%s=%q: %s\n"
|
||||
s := fmt.Sprintf(" -%s", flag.Name) // Two spaces before -; see next two comments.
|
||||
name, usage := UnquoteUsage(flag)
|
||||
if len(name) > 0 {
|
||||
s += " " + name
|
||||
}
|
||||
fmt.Fprintf(f.out(), format, flag.Name, flag.DefValue, flag.Usage)
|
||||
// Boolean flags of one ASCII letter are so common we
|
||||
// treat them specially, putting their usage on the same line.
|
||||
if len(s) <= 4 { // space, space, '-', 'x'.
|
||||
s += "\t"
|
||||
} else {
|
||||
// Three spaces before the tab triggers good alignment
|
||||
// for both 4- and 8-space tab stops.
|
||||
s += "\n \t"
|
||||
}
|
||||
s += usage
|
||||
if !isZeroValue(flag.DefValue) {
|
||||
if _, ok := flag.Value.(*stringValue); ok {
|
||||
// put quotes on the value
|
||||
s += fmt.Sprintf(" (default %q)", flag.DefValue)
|
||||
} else {
|
||||
s += fmt.Sprintf(" (default %v)", flag.DefValue)
|
||||
}
|
||||
}
|
||||
fmt.Fprint(f.out(), s, "\n")
|
||||
})
|
||||
}
|
||||
|
||||
// PrintDefaults prints to standard error the default values of all defined command-line flags.
|
||||
// PrintDefaults prints, to standard error unless configured otherwise,
|
||||
// a usage message showing the default settings of all defined
|
||||
// command-line flags.
|
||||
// For an integer valued flag x, the default output has the form
|
||||
// -x int
|
||||
// usage-message-for-x (default 7)
|
||||
// The usage message will appear on a separate line for anything but
|
||||
// a bool flag with a one-byte name. For bool flags, the type is
|
||||
// omitted and if the flag name is one byte the usage message appears
|
||||
// on the same line. The parenthetical default is omitted if the
|
||||
// default is the zero value for the type. The listed type, here int,
|
||||
// can be changed by placing a back-quoted name in the flag's usage
|
||||
// string; the first such item in the message is taken to be a parameter
|
||||
// name to show in the message and the back quotes are stripped from
|
||||
// the message when displayed. For instance, given
|
||||
// flag.String("I", "", "search `directory` for include files")
|
||||
// the output will be
|
||||
// -I directory
|
||||
// search directory for include files.
|
||||
func PrintDefaults() {
|
||||
CommandLine.PrintDefaults()
|
||||
}
|
||||
@ -408,6 +498,8 @@ func defaultUsage(f *FlagSet) {
|
||||
// Usage prints to standard error a usage message documenting all defined command-line flags.
|
||||
// It is called when an error occurs while parsing flags.
|
||||
// The function is a variable that may be changed to point to a custom function.
|
||||
// By default it prints a simple header and calls PrintDefaults; for details about the
|
||||
// format of the output and how to control it, see the documentation for PrintDefaults.
|
||||
var Usage = func() {
|
||||
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
|
||||
PrintDefaults()
|
||||
|
@ -377,3 +377,41 @@ func TestHelp(t *testing.T) {
|
||||
t.Fatal("help was called; should not have been for defined help flag")
|
||||
}
|
||||
}
|
||||
|
||||
const defaultOutput = ` -A for bootstrapping, allow 'any' type
|
||||
-Alongflagname
|
||||
disable bounds checking
|
||||
-C a boolean defaulting to true (default true)
|
||||
-D path
|
||||
set relative path for local imports
|
||||
-F number
|
||||
a non-zero number (default 2.7)
|
||||
-G float
|
||||
a float that defaults to zero
|
||||
-N int
|
||||
a non-zero int (default 27)
|
||||
-Z int
|
||||
an int that defaults to zero
|
||||
-maxT timeout
|
||||
set timeout for dial
|
||||
`
|
||||
|
||||
func TestPrintDefaults(t *testing.T) {
|
||||
fs := NewFlagSet("print defaults test", ContinueOnError)
|
||||
var buf bytes.Buffer
|
||||
fs.SetOutput(&buf)
|
||||
fs.Bool("A", false, "for bootstrapping, allow 'any' type")
|
||||
fs.Bool("Alongflagname", false, "disable bounds checking")
|
||||
fs.Bool("C", true, "a boolean defaulting to true")
|
||||
fs.String("D", "", "set relative `path` for local imports")
|
||||
fs.Float64("F", 2.7, "a non-zero `number`")
|
||||
fs.Float64("G", 0, "a float that defaults to zero")
|
||||
fs.Int("N", 27, "a non-zero int")
|
||||
fs.Int("Z", 0, "an int that defaults to zero")
|
||||
fs.Duration("maxT", 0, "set `timeout` for dial")
|
||||
fs.PrintDefaults()
|
||||
got := buf.String()
|
||||
if got != defaultOutput {
|
||||
t.Errorf("got %q want %q\n", got, defaultOutput)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user