Merge f7b04442a2
into 5ec29ce755
This commit is contained in:
commit
bdad143a88
|
@ -284,6 +284,9 @@ $ ./example --version
|
||||||
someprogram 4.3.0
|
someprogram 4.3.0
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **Note**
|
||||||
|
> If a `--version` flag is defined in `args` or any subcommand, it overrides the above functionality.
|
||||||
|
|
||||||
### Overriding option names
|
### Overriding option names
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
|
50
usage.go
50
usage.go
|
@ -62,29 +62,39 @@ func (p *Parser) WriteUsageForSubcommand(w io.Writer, subcommand ...string) erro
|
||||||
// writeUsageForSubcommand writes usage information for the given subcommand
|
// writeUsageForSubcommand writes usage information for the given subcommand
|
||||||
func (p *Parser) writeUsageForSubcommand(w io.Writer, cmd *command) {
|
func (p *Parser) writeUsageForSubcommand(w io.Writer, cmd *command) {
|
||||||
var positionals, longOptions, shortOptions []*spec
|
var positionals, longOptions, shortOptions []*spec
|
||||||
|
var hasVersionOption bool
|
||||||
for _, spec := range cmd.specs {
|
for _, spec := range cmd.specs {
|
||||||
switch {
|
switch {
|
||||||
case spec.positional:
|
case spec.positional:
|
||||||
positionals = append(positionals, spec)
|
positionals = append(positionals, spec)
|
||||||
case spec.long != "":
|
case spec.long != "":
|
||||||
longOptions = append(longOptions, spec)
|
longOptions = append(longOptions, spec)
|
||||||
|
if spec.long == "version" {
|
||||||
|
hasVersionOption = true
|
||||||
|
}
|
||||||
case spec.short != "":
|
case spec.short != "":
|
||||||
shortOptions = append(shortOptions, spec)
|
shortOptions = append(shortOptions, spec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.version != "" {
|
|
||||||
fmt.Fprintln(w, p.version)
|
|
||||||
}
|
|
||||||
|
|
||||||
// make a list of ancestor commands so that we print with full context
|
// make a list of ancestor commands so that we print with full context
|
||||||
|
// also determine if any ancestor has a version option spec
|
||||||
var ancestors []string
|
var ancestors []string
|
||||||
ancestor := cmd
|
ancestor := cmd
|
||||||
for ancestor != nil {
|
for ancestor != nil {
|
||||||
|
for _, spec := range ancestor.specs {
|
||||||
|
if spec.long == "version" {
|
||||||
|
hasVersionOption = true
|
||||||
|
}
|
||||||
|
}
|
||||||
ancestors = append(ancestors, ancestor.name)
|
ancestors = append(ancestors, ancestor.name)
|
||||||
ancestor = ancestor.parent
|
ancestor = ancestor.parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !hasVersionOption && p.version != "" {
|
||||||
|
fmt.Fprintln(w, p.version)
|
||||||
|
}
|
||||||
|
|
||||||
// print the beginning of the usage string
|
// print the beginning of the usage string
|
||||||
fmt.Fprint(w, "Usage:")
|
fmt.Fprint(w, "Usage:")
|
||||||
for i := len(ancestors) - 1; i >= 0; i-- {
|
for i := len(ancestors) - 1; i >= 0; i-- {
|
||||||
|
@ -216,6 +226,9 @@ func (p *Parser) writeHelpForSubcommand(w io.Writer, cmd *command) {
|
||||||
positionals = append(positionals, spec)
|
positionals = append(positionals, spec)
|
||||||
case spec.long != "":
|
case spec.long != "":
|
||||||
longOptions = append(longOptions, spec)
|
longOptions = append(longOptions, spec)
|
||||||
|
if spec.long == "version" {
|
||||||
|
hasVersionOption = true
|
||||||
|
}
|
||||||
case spec.short != "":
|
case spec.short != "":
|
||||||
shortOptions = append(shortOptions, spec)
|
shortOptions = append(shortOptions, spec)
|
||||||
case spec.short == "" && spec.long == "":
|
case spec.short == "" && spec.long == "":
|
||||||
|
@ -223,6 +236,21 @@ func (p *Parser) writeHelpForSubcommand(w io.Writer, cmd *command) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// obtain a flattened list of options from all ancestors
|
||||||
|
// also determine if any ancestor has a version option spec
|
||||||
|
var globals []*spec
|
||||||
|
ancestor := cmd.parent
|
||||||
|
for ancestor != nil {
|
||||||
|
for _, spec := range ancestor.specs {
|
||||||
|
if spec.long == "version" {
|
||||||
|
hasVersionOption = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
globals = append(globals, ancestor.specs...)
|
||||||
|
ancestor = ancestor.parent
|
||||||
|
}
|
||||||
|
|
||||||
if p.description != "" {
|
if p.description != "" {
|
||||||
fmt.Fprintln(w, p.description)
|
fmt.Fprintln(w, p.description)
|
||||||
}
|
}
|
||||||
|
@ -244,28 +272,14 @@ func (p *Parser) writeHelpForSubcommand(w io.Writer, cmd *command) {
|
||||||
}
|
}
|
||||||
for _, spec := range longOptions {
|
for _, spec := range longOptions {
|
||||||
p.printOption(w, spec)
|
p.printOption(w, spec)
|
||||||
if spec.long == "version" {
|
|
||||||
hasVersionOption = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// obtain a flattened list of options from all ancestors
|
|
||||||
var globals []*spec
|
|
||||||
ancestor := cmd.parent
|
|
||||||
for ancestor != nil {
|
|
||||||
globals = append(globals, ancestor.specs...)
|
|
||||||
ancestor = ancestor.parent
|
|
||||||
}
|
|
||||||
|
|
||||||
// write the list of global options
|
// write the list of global options
|
||||||
if len(globals) > 0 {
|
if len(globals) > 0 {
|
||||||
fmt.Fprint(w, "\nGlobal options:\n")
|
fmt.Fprint(w, "\nGlobal options:\n")
|
||||||
for _, spec := range globals {
|
for _, spec := range globals {
|
||||||
p.printOption(w, spec)
|
p.printOption(w, spec)
|
||||||
if spec.long == "version" {
|
|
||||||
hasVersionOption = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
226
usage_test.go
226
usage_test.go
|
@ -236,7 +236,7 @@ func (versioned) Version() string {
|
||||||
return "example 3.2.1"
|
return "example 3.2.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUsageWithVersion(t *testing.T) {
|
func TestUsageWithBuiltinVersion(t *testing.T) {
|
||||||
expectedUsage := "example 3.2.1\nUsage: example"
|
expectedUsage := "example 3.2.1\nUsage: example"
|
||||||
|
|
||||||
expectedHelp := `
|
expectedHelp := `
|
||||||
|
@ -260,6 +260,230 @@ Options:
|
||||||
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUsageWithArgsVersion(t *testing.T) {
|
||||||
|
var args struct {
|
||||||
|
Version bool `arg:"-V,--version" help:"display version and build info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedUsage := "Usage: example [--version]"
|
||||||
|
|
||||||
|
expectedHelp := `
|
||||||
|
Usage: example [--version]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--version, -V display version and build info
|
||||||
|
--help, -h display this help and exit
|
||||||
|
`
|
||||||
|
os.Args[0] = "example"
|
||||||
|
p, err := NewParser(Config{}, &args)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var help bytes.Buffer
|
||||||
|
p.WriteHelp(&help)
|
||||||
|
assert.Equal(t, expectedHelp[1:], help.String())
|
||||||
|
|
||||||
|
var usage bytes.Buffer
|
||||||
|
p.WriteUsage(&usage)
|
||||||
|
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUsageWithBuiltinAndArgsVersion(t *testing.T) {
|
||||||
|
var args struct {
|
||||||
|
versioned
|
||||||
|
VersionFlag bool `arg:"-V,--version" help:"display version and build info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedUsage := "Usage: example [--version]"
|
||||||
|
|
||||||
|
expectedHelp := `
|
||||||
|
Usage: example [--version]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--version, -V display version and build info
|
||||||
|
--help, -h display this help and exit
|
||||||
|
`
|
||||||
|
os.Args[0] = "example"
|
||||||
|
p, err := NewParser(Config{}, &args)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var help bytes.Buffer
|
||||||
|
p.WriteHelp(&help)
|
||||||
|
assert.Equal(t, expectedHelp[1:], help.String())
|
||||||
|
|
||||||
|
var usage bytes.Buffer
|
||||||
|
p.WriteUsage(&usage)
|
||||||
|
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUsageWithBuiltinVersionAndSubcommands(t *testing.T) {
|
||||||
|
type cmd struct {
|
||||||
|
Test int `arg:"-t,--test" help:"test number"`
|
||||||
|
}
|
||||||
|
var args struct {
|
||||||
|
versioned
|
||||||
|
Cmd *cmd `arg:"subcommand"`
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedUsage := "example 3.2.1\nUsage: example <command> [<args>]"
|
||||||
|
|
||||||
|
expectedHelp := `
|
||||||
|
example 3.2.1
|
||||||
|
Usage: example <command> [<args>]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--help, -h display this help and exit
|
||||||
|
--version display version and exit
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
cmd
|
||||||
|
`
|
||||||
|
os.Args[0] = "example"
|
||||||
|
p, err := NewParser(Config{}, &args)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var help bytes.Buffer
|
||||||
|
p.WriteHelp(&help)
|
||||||
|
assert.Equal(t, expectedHelp[1:], help.String())
|
||||||
|
|
||||||
|
var usage bytes.Buffer
|
||||||
|
p.WriteUsage(&usage)
|
||||||
|
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
||||||
|
|
||||||
|
expectedUsage = "example 3.2.1\nUsage: example cmd [--test TEST]"
|
||||||
|
|
||||||
|
expectedHelp = `
|
||||||
|
example 3.2.1
|
||||||
|
Usage: example cmd [--test TEST]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--test TEST, -t TEST test number
|
||||||
|
--help, -h display this help and exit
|
||||||
|
--version display version and exit
|
||||||
|
`
|
||||||
|
_ = p.Parse([]string{"cmd"})
|
||||||
|
|
||||||
|
help = bytes.Buffer{}
|
||||||
|
p.WriteHelp(&help)
|
||||||
|
assert.Equal(t, expectedHelp[1:], help.String())
|
||||||
|
|
||||||
|
usage = bytes.Buffer{}
|
||||||
|
p.WriteUsage(&usage)
|
||||||
|
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUsageWithArgsVersionAndSubcommands(t *testing.T) {
|
||||||
|
type cmd struct {
|
||||||
|
Test int `arg:"-t,--test" help:"test number"`
|
||||||
|
}
|
||||||
|
var args struct {
|
||||||
|
Cmd *cmd `arg:"subcommand"`
|
||||||
|
Version bool `arg:"-V,--version" help:"display version and build info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedUsage := "Usage: example [--version] <command> [<args>]"
|
||||||
|
|
||||||
|
expectedHelp := `
|
||||||
|
Usage: example [--version] <command> [<args>]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--version, -V display version and build info
|
||||||
|
--help, -h display this help and exit
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
cmd
|
||||||
|
`
|
||||||
|
os.Args[0] = "example"
|
||||||
|
p, err := NewParser(Config{}, &args)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var help bytes.Buffer
|
||||||
|
p.WriteHelp(&help)
|
||||||
|
assert.Equal(t, expectedHelp[1:], help.String())
|
||||||
|
|
||||||
|
var usage bytes.Buffer
|
||||||
|
p.WriteUsage(&usage)
|
||||||
|
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
||||||
|
|
||||||
|
expectedUsage = "Usage: example cmd [--test TEST]"
|
||||||
|
|
||||||
|
expectedHelp = `
|
||||||
|
Usage: example cmd [--test TEST]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--test TEST, -t TEST test number
|
||||||
|
|
||||||
|
Global options:
|
||||||
|
--version, -V display version and build info
|
||||||
|
--help, -h display this help and exit
|
||||||
|
`
|
||||||
|
_ = p.Parse([]string{"cmd"})
|
||||||
|
|
||||||
|
help = bytes.Buffer{}
|
||||||
|
p.WriteHelp(&help)
|
||||||
|
assert.Equal(t, expectedHelp[1:], help.String())
|
||||||
|
|
||||||
|
usage = bytes.Buffer{}
|
||||||
|
p.WriteUsage(&usage)
|
||||||
|
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUsageWithBuiltinAndArgsVersionAndSubcommands(t *testing.T) {
|
||||||
|
type cmd struct {
|
||||||
|
Test int `arg:"-t,--test" help:"test number"`
|
||||||
|
}
|
||||||
|
var args struct {
|
||||||
|
versioned
|
||||||
|
Cmd *cmd `arg:"subcommand"`
|
||||||
|
Version bool `arg:"-V,--version" help:"display version and build info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedUsage := "Usage: example [--version] <command> [<args>]"
|
||||||
|
|
||||||
|
expectedHelp := `
|
||||||
|
Usage: example [--version] <command> [<args>]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--version, -V display version and build info
|
||||||
|
--help, -h display this help and exit
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
cmd
|
||||||
|
`
|
||||||
|
os.Args[0] = "example"
|
||||||
|
p, err := NewParser(Config{}, &args)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var help bytes.Buffer
|
||||||
|
p.WriteHelp(&help)
|
||||||
|
assert.Equal(t, expectedHelp[1:], help.String())
|
||||||
|
|
||||||
|
var usage bytes.Buffer
|
||||||
|
p.WriteUsage(&usage)
|
||||||
|
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
||||||
|
|
||||||
|
expectedUsage = "Usage: example cmd [--test TEST]"
|
||||||
|
|
||||||
|
expectedHelp = `
|
||||||
|
Usage: example cmd [--test TEST]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--test TEST, -t TEST test number
|
||||||
|
|
||||||
|
Global options:
|
||||||
|
--version, -V display version and build info
|
||||||
|
--help, -h display this help and exit
|
||||||
|
`
|
||||||
|
_ = p.Parse([]string{"cmd"})
|
||||||
|
|
||||||
|
help = bytes.Buffer{}
|
||||||
|
p.WriteHelp(&help)
|
||||||
|
assert.Equal(t, expectedHelp[1:], help.String())
|
||||||
|
|
||||||
|
usage = bytes.Buffer{}
|
||||||
|
p.WriteUsage(&usage)
|
||||||
|
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
||||||
|
}
|
||||||
|
|
||||||
type described struct{}
|
type described struct{}
|
||||||
|
|
||||||
// Described returns the description for this program
|
// Described returns the description for this program
|
||||||
|
|
Loading…
Reference in New Issue