Merge pull request #453 from urfave/merging-from-v1

Merging from v1
This commit is contained in:
Dan Buch 2016-06-10 13:25:24 -04:00 committed by GitHub
commit 1b5ad735df
3 changed files with 409 additions and 22 deletions

View File

@ -37,8 +37,13 @@
- testing on OS X - testing on OS X
- testing on Windows - testing on Windows
### Changed
- Use spaces for alignment in help/usage output instead of tabs, making the
output alignment consistent regardless of tab width
### Fixed ### Fixed
- Printing of command aliases in help text - Printing of command aliases in help text
- Printing of visible flags for both struct and struct pointer flags
## [1.17.0] - 2016-05-09 ## [1.17.0] - 2016-05-09
### Added ### Added

421
README.md
View File

@ -109,7 +109,7 @@ package main
import ( import (
"os" "os"
"github.com/urfave/cli" "gopkg.in/urfave/cli.v2"
) )
func main() { func main() {
@ -130,7 +130,7 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/urfave/cli" "gopkg.in/urfave/cli.v2"
) )
func main() { func main() {
@ -168,7 +168,7 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/urfave/cli" "gopkg.in/urfave/cli.v2"
) )
func main() { func main() {
@ -231,7 +231,7 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/urfave/cli" "gopkg.in/urfave/cli.v2"
) )
func main() { func main() {
@ -260,7 +260,7 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/urfave/cli" "gopkg.in/urfave/cli.v2"
) )
func main() { func main() {
@ -304,7 +304,7 @@ import (
"os" "os"
"fmt" "fmt"
"github.com/urfave/cli" "gopkg.in/urfave/cli.v2"
) )
func main() { func main() {
@ -357,7 +357,7 @@ package main
import ( import (
"os" "os"
"github.com/urfave/cli" "gopkg.in/urfave/cli.v2"
) )
func main() { func main() {
@ -399,7 +399,7 @@ package main
import ( import (
"os" "os"
"github.com/urfave/cli" "gopkg.in/urfave/cli.v2"
) )
func main() { func main() {
@ -436,7 +436,7 @@ package main
import ( import (
"os" "os"
"github.com/urfave/cli" "gopkg.in/urfave/cli.v2"
) )
func main() { func main() {
@ -469,7 +469,7 @@ package main
import ( import (
"os" "os"
"github.com/urfave/cli" "gopkg.in/urfave/cli.v2"
) )
func main() { func main() {
@ -531,8 +531,8 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/urfave/cli" "gopkg.in/urfave/cli.v2"
"github.com/urfave/cli/altsrc" "gopkg.in/urfave/cli.v2/altsrc"
) )
func main() { func main() {
@ -570,7 +570,7 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/urfave/cli" "gopkg.in/urfave/cli.v2"
) )
func main() { func main() {
@ -638,7 +638,7 @@ package main
import ( import (
"os" "os"
"github.com/urfave/cli" "gopkg.in/urfave/cli.v2"
) )
func main() { func main() {
@ -686,7 +686,7 @@ package main
import ( import (
"os" "os"
"github.com/urfave/cli" "gopkg.in/urfave/cli.v2"
) )
func main() { func main() {
@ -727,7 +727,7 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/urfave/cli" "gopkg.in/urfave/cli.v2"
) )
func main() { func main() {
@ -760,14 +760,14 @@ func main() {
} }
``` ```
#### To Enable #### Enabling
Source the `autocomplete/bash_autocomplete` file in your `.bashrc` file while Source the `autocomplete/bash_autocomplete` file in your `.bashrc` file while
setting the `PROG` variable to the name of your program: setting the `PROG` variable to the name of your program:
`PROG=myprogram source /.../cli/autocomplete/bash_autocomplete` `PROG=myprogram source /.../cli/autocomplete/bash_autocomplete`
#### To Distribute #### Distribution
Copy `autocomplete/bash_autocomplete` into `/etc/bash_completion.d/` and rename Copy `autocomplete/bash_autocomplete` into `/etc/bash_completion.d/` and rename
it to the name of the program you wish to add autocomplete support for (or it to the name of the program you wish to add autocomplete support for (or
@ -783,7 +783,48 @@ Alternatively, you can just document that users should source the generic
`autocomplete/bash_autocomplete` in their bash configuration with `$PROG` set `autocomplete/bash_autocomplete` in their bash configuration with `$PROG` set
to the name of their program (as above). to the name of their program (as above).
### Generated Help Text Customization #### Customization
The default bash completion flag (`--generate-bash-completion`) is defined as
`cli.BashCompletionFlag`, and may be redefined if desired, e.g.:
<!-- {
"args": ["&#45;&#45;compgen"],
"output": "wat\nhelp\nh"
} -->
``` go
package main
import (
"os"
"gopkg.in/urfave/cli.v2"
)
func main() {
cli.BashCompletionFlag = &cli.BoolFlag{
Name: "compgen",
Hidden: true,
}
app := cli.NewApp()
app.EnableBashCompletion = true
app.Commands = []*cli.Command{
{
Name: "wat",
},
}
app.Run(os.Args)
}
```
### Generated Help Text
The default help flag (`-h/--help`) is defined as `cli.HelpFlag` and is checked
by the cli internals in order to print generated help text for the app, command,
or subcommand, and break execution.
#### Customization
All of the help text generation may be customized, and at multiple levels. The All of the help text generation may be customized, and at multiple levels. The
templates are exposed as variables `AppHelpTemplate`, `CommandHelpTemplate`, and templates are exposed as variables `AppHelpTemplate`, `CommandHelpTemplate`, and
@ -802,7 +843,7 @@ import (
"io" "io"
"os" "os"
"github.com/urfave/cli" "gopkg.in/urfave/cli.v2"
) )
func main() { func main() {
@ -849,6 +890,346 @@ VERSION:
} }
``` ```
The default flag may be customized to something other than `-h/--help` by
setting `cli.HelpFlag`, e.g.:
<!-- {
"args": ["&#45;&#45halp"],
"output": "haaaaalp.*HALP"
} -->
``` go
package main
import (
"os"
"gopkg.in/urfave/cli.v2"
)
func main() {
cli.HelpFlag = &cli.BoolFlag{
Name: "haaaaalp", Aliases: []string{"halp"},
Usage: "HALP",
EnvVars: []string{"SHOW_HALP", "HALPPLZ"},
}
cli.NewApp().Run(os.Args)
}
```
### Version Flag
The default version flag (`-v/--version`) is defined as `cli.VersionFlag`, which
is checked by the cli internals in order to print the `App.Version` via
`cli.VersionPrinter` and break execution.
#### Customization
The default flag may be cusomized to something other than `-v/--version` by
setting `cli.VersionFlag`, e.g.:
<!-- {
"args": ["&#45;&#45print-version"],
"output": "partay version v19\\.99\\.0"
} -->
``` go
package main
import (
"os"
"gopkg.in/urfave/cli.v2"
)
func main() {
cli.VersionFlag = &cli.BoolFlag{
Name: "print-version", Aliases: []string{"V"},
Usage: "print only the version",
}
app := cli.NewApp()
app.Name = "partay"
app.Version = "v19.99.0"
app.Run(os.Args)
}
```
Alternatively, the version printer at `cli.VersionPrinter` may be overridden, e.g.:
<!-- {
"args": ["&#45;&#45version"],
"output": "version=v19\\.99\\.0 revision=fafafaf"
} -->
``` go
package main
import (
"fmt"
"os"
"gopkg.in/urfave/cli.v2"
)
var (
Revision = "fafafaf"
)
func main() {
cli.VersionPrinter = func(c *cli.Context) {
fmt.Printf("version=%s revision=%s\n", c.App.Version, Revision)
}
app := cli.NewApp()
app.Name = "partay"
app.Version = "v19.99.0"
app.Run(os.Args)
}
```
#### Full API Example
**NOTE**: This is a contrived (functioning) example meant strictly for API
demonstration purposes. Use of one's imagination is encouraged.
<!-- {
"output": "made it!\nPhew!"
} -->
``` go
package main
import (
"errors"
"flag"
"fmt"
"io"
"io/ioutil"
"os"
"time"
"gopkg.in/urfave/cli.v2"
)
func init() {
cli.AppHelpTemplate += "\nCUSTOMIZED: you bet ur muffins\n"
cli.CommandHelpTemplate += "\nYMMV\n"
cli.SubcommandHelpTemplate += "\nor something\n"
cli.HelpFlag = &cli.BoolFlag{Name: "halp"}
cli.BashCompletionFlag = &cli.BoolFlag{Name: "compgen", Hidden: true}
cli.VersionFlag = &cli.BoolFlag{Name: "print-version, V"}
cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) {
fmt.Fprintf(w, "best of luck to you\n")
}
cli.VersionPrinter = func(c *cli.Context) {
fmt.Fprintf(c.App.Writer, "version=%s\n", c.App.Version)
}
cli.OsExiter = func(c int) {
fmt.Fprintf(cli.ErrWriter, "refusing to exit %d\n", c)
}
cli.ErrWriter = ioutil.Discard
cli.FlagStringer = func(fl cli.Flag) string {
return fmt.Sprintf("\t\t%s", fl.Names()[0])
}
}
type hexWriter struct{}
func (w *hexWriter) Write(p []byte) (int, error) {
for _, b := range p {
fmt.Printf("%x", b)
}
fmt.Printf("\n")
return len(p), nil
}
func main() {
app := cli.NewApp()
app.Name = "kənˈtrīv"
app.Version = "v19.99.0"
app.Compiled = time.Now()
app.Authors = []*cli.Author{
&cli.Author{
Name: "Example Human",
Email: "human@example.com",
},
}
app.Copyright = "(c) 1999 Serious Enterprise"
app.HelpName = "contrive"
app.Usage = "demonstrate available API"
app.UsageText = "contrive - demonstrating the available API"
app.ArgsUsage = "[args and such]"
app.Commands = []*cli.Command{
&cli.Command{
Name: "doo",
Aliases: []string{"do"},
Category: "motion",
Usage: "do the doo",
UsageText: "doo - does the dooing",
Description: "no really, there is a lot of dooing to be done",
ArgsUsage: "[arrgh]",
Flags: []cli.Flag{
&cli.BoolFlag{Name: "forever, forevvarr"},
},
Subcommands: []*cli.Command{
&cli.Command{
Name: "wop",
Action: wopAction,
},
},
SkipFlagParsing: false,
HideHelp: false,
Hidden: false,
HelpName: "doo!",
BashComplete: func(c *cli.Context) {
fmt.Fprintf(c.App.Writer, "--better\n")
},
Before: func(c *cli.Context) error {
fmt.Fprintf(c.App.Writer, "brace for impact\n")
return nil
},
After: func(c *cli.Context) error {
fmt.Fprintf(c.App.Writer, "did we lose anyone?\n")
return nil
},
Action: func(c *cli.Context) error {
c.Command.FullName()
c.Command.HasName("wop")
c.Command.Names()
c.Command.VisibleFlags()
fmt.Fprintf(c.App.Writer, "dodododododoodododddooooododododooo\n")
if c.Bool("forever") {
c.Command.Run(c)
}
return nil
},
OnUsageError: func(c *cli.Context, err error, isSubcommand bool) error {
fmt.Fprintf(c.App.Writer, "for shame\n")
return err
},
},
}
app.Flags = []cli.Flag{
&cli.BoolFlag{Name: "fancy"},
&cli.BoolFlag{Value: true, Name: "fancier"},
&cli.StringFlag{Name: "dance-move", Aliases: []string{"d"}},
}
app.EnableBashCompletion = true
app.HideHelp = false
app.HideVersion = false
app.BashComplete = func(c *cli.Context) {
fmt.Fprintf(c.App.Writer, "lipstick\nkiss\nme\nlipstick\nringo\n")
}
app.Before = func(c *cli.Context) error {
fmt.Fprintf(c.App.Writer, "HEEEERE GOES\n")
return nil
}
app.After = func(c *cli.Context) error {
fmt.Fprintf(c.App.Writer, "Phew!\n")
return nil
}
app.CommandNotFound = func(c *cli.Context, command string) {
fmt.Fprintf(c.App.Writer, "Thar be no %q here.\n", command)
}
app.OnUsageError = func(c *cli.Context, err error, isSubcommand bool) error {
if isSubcommand {
return err
}
fmt.Fprintf(c.App.Writer, "WRONG: %#v\n", err)
return nil
}
app.Action = func(c *cli.Context) error {
cli.DefaultAppComplete(c)
cli.HandleExitCoder(errors.New("not an exit coder, though"))
cli.ShowAppHelp(c)
cli.ShowCommandCompletions(c, "nope")
cli.ShowCommandHelp(c, "also-nope")
cli.ShowCompletions(c)
cli.ShowSubcommandHelp(c)
cli.ShowVersion(c)
categories := c.App.Categories
categories.AddCommand("sounds", &cli.Command{
Name: "bloop",
})
for _, category := range c.App.Categories.Categories() {
fmt.Fprintf(c.App.Writer, "%s\n", category.Name)
fmt.Fprintf(c.App.Writer, "%#v\n", category.VisibleCommands())
fmt.Fprintf(c.App.Writer, "%#v\n", category.VisibleCommands())
}
fmt.Printf("%#v\n", c.App.Command("doo"))
if c.Bool("infinite") {
c.App.Run([]string{"app", "doo", "wop"})
}
if c.Bool("forevar") {
c.App.RunAsSubcommand(c)
}
c.App.Setup()
fmt.Printf("%#v\n", c.App.VisibleCategories())
fmt.Printf("%#v\n", c.App.VisibleCommands())
fmt.Printf("%#v\n", c.App.VisibleFlags())
fmt.Printf("%#v\n", c.Args().First())
if c.Args().Len() > 0 {
fmt.Printf("%#v\n", c.Args().Get(1))
}
fmt.Printf("%#v\n", c.Args().Present())
fmt.Printf("%#v\n", c.Args().Tail())
set := flag.NewFlagSet("contrive", 0)
nc := cli.NewContext(c.App, set, c)
fmt.Printf("%#v\n", nc.Args())
fmt.Printf("%#v\n", nc.Bool("nope"))
fmt.Printf("%#v\n", !nc.Bool("nerp"))
fmt.Printf("%#v\n", nc.Duration("howlong"))
fmt.Printf("%#v\n", nc.Float64("hay"))
fmt.Printf("%#v\n", nc.Generic("bloop"))
fmt.Printf("%#v\n", nc.Int("bips"))
fmt.Printf("%#v\n", nc.IntSlice("blups"))
fmt.Printf("%#v\n", nc.String("snurt"))
fmt.Printf("%#v\n", nc.StringSlice("snurkles"))
fmt.Printf("%#v\n", nc.FlagNames())
fmt.Printf("%#v\n", nc.IsSet("wat"))
fmt.Printf("%#v\n", nc.Set("wat", "nope"))
fmt.Printf("%#v\n", nc.NArg())
fmt.Printf("%#v\n", nc.NumFlags())
fmt.Printf("%#v\n", nc.Lineage()[1])
nc.Set("wat", "also-nope")
ec := cli.Exit("ohwell", 86)
fmt.Fprintf(c.App.Writer, "%d", ec.ExitCode())
fmt.Printf("made it!\n")
return ec
}
if os.Getenv("HEXY") != "" {
app.Writer = &hexWriter{}
app.ErrWriter = &hexWriter{}
}
app.Metadata = map[string]interface{}{
"layers": "many",
"explicable": false,
"whatever-values": 19.99,
}
app.Run(os.Args)
}
func wopAction(c *cli.Context) error {
fmt.Fprintf(c.App.Writer, ":wave: over here, eh\n")
return nil
}
```
## Contribution Guidelines ## Contribution Guidelines
Feel free to put up a pull request to fix a bug or maybe add a feature. I will Feel free to put up a pull request to fix a bug or maybe add a feature. I will

View File

@ -117,8 +117,9 @@ var HelpPrinter helpPrinter = printHelp
var VersionPrinter = printVersion var VersionPrinter = printVersion
// ShowAppHelp is an action that displays the help. // ShowAppHelp is an action that displays the help.
func ShowAppHelp(c *Context) { func ShowAppHelp(c *Context) error {
HelpPrinter(c.App.Writer, AppHelpTemplate, c.App) HelpPrinter(c.App.Writer, AppHelpTemplate, c.App)
return nil
} }
// DefaultAppComplete prints the list of subcommands as the default app completion method // DefaultAppComplete prints the list of subcommands as the default app completion method
@ -199,7 +200,7 @@ func printHelp(out io.Writer, templ string, data interface{}) {
"join": strings.Join, "join": strings.Join,
} }
w := tabwriter.NewWriter(out, 0, 8, 1, '\t', 0) w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0)
t := template.Must(template.New("help").Funcs(funcMap).Parse(templ)) t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))
errDebug := os.Getenv("CLI_TEMPLATE_ERROR_DEBUG") != "" errDebug := os.Getenv("CLI_TEMPLATE_ERROR_DEBUG") != ""