Merge pull request #78 from zenoss/feature/bash-completion
Bash completion
This commit is contained in:
commit
640826c88f
41
README.md
41
README.md
@ -191,5 +191,46 @@ app.Commands = []cli.Command{
|
|||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Bash Completion
|
||||||
|
|
||||||
|
You can enable completion commands by setting the EnableBashCompletion
|
||||||
|
flag on the App object. By default, this setting will only auto-complete to
|
||||||
|
show an app's subcommands, but you can write your own completion methods for
|
||||||
|
the App or its subcommands.
|
||||||
|
```go
|
||||||
|
...
|
||||||
|
var tasks = []string{"cook", "clean", "laundry", "eat", "sleep", "code"}
|
||||||
|
app := cli.NewApp()
|
||||||
|
app.EnableBashCompletion = true
|
||||||
|
app.Commands = []cli.Command{
|
||||||
|
{
|
||||||
|
Name: "complete",
|
||||||
|
ShortName: "c",
|
||||||
|
Usage: "complete a task on the list",
|
||||||
|
Action: func(c *cli.Context) {
|
||||||
|
println("completed task: ", c.Args().First())
|
||||||
|
},
|
||||||
|
BashComplete: func(c *cli.Context) {
|
||||||
|
// This will complete if no args are passed
|
||||||
|
if len(c.Args()) > 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, t := range tasks {
|
||||||
|
println(t)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
#### To Enable
|
||||||
|
|
||||||
|
Source the autocomplete/bash_autocomplete file in your .bashrc file while
|
||||||
|
setting the PROG variable to the name of your program:
|
||||||
|
|
||||||
|
`PROG=myprogram source /.../cli/autocomplete/bash_autocomplete`
|
||||||
|
|
||||||
|
|
||||||
## About
|
## About
|
||||||
cli.go is written by none other than the [Code Gangsta](http://codegangsta.io)
|
cli.go is written by none other than the [Code Gangsta](http://codegangsta.io)
|
||||||
|
26
app.go
26
app.go
@ -20,6 +20,10 @@ type App struct {
|
|||||||
Commands []Command
|
Commands []Command
|
||||||
// List of flags to parse
|
// List of flags to parse
|
||||||
Flags []Flag
|
Flags []Flag
|
||||||
|
// Boolean to enable bash completion commands
|
||||||
|
EnableBashCompletion bool
|
||||||
|
// An action to execute when the bash-completion flag is set
|
||||||
|
BashComplete func(context *Context)
|
||||||
// An action to execute before any subcommands are run, but after the context is ready
|
// An action to execute before any subcommands are run, but after the context is ready
|
||||||
// If a non-nil error is returned, no subcommands are run
|
// If a non-nil error is returned, no subcommands are run
|
||||||
Before func(context *Context) error
|
Before func(context *Context) error
|
||||||
@ -46,13 +50,14 @@ func compileTime() time.Time {
|
|||||||
// Creates a new cli Application with some reasonable defaults for Name, Usage, Version and Action.
|
// Creates a new cli Application with some reasonable defaults for Name, Usage, Version and Action.
|
||||||
func NewApp() *App {
|
func NewApp() *App {
|
||||||
return &App{
|
return &App{
|
||||||
Name: os.Args[0],
|
Name: os.Args[0],
|
||||||
Usage: "A new cli application",
|
Usage: "A new cli application",
|
||||||
Version: "0.0.0",
|
Version: "0.0.0",
|
||||||
Action: helpCommand.Action,
|
BashComplete: DefaultAppComplete,
|
||||||
Compiled: compileTime(),
|
Action: helpCommand.Action,
|
||||||
Author: "Author",
|
Compiled: compileTime(),
|
||||||
Email: "unknown@email",
|
Author: "Author",
|
||||||
|
Email: "unknown@email",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,6 +69,9 @@ func (a *App) Run(arguments []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//append version/help flags
|
//append version/help flags
|
||||||
|
if a.EnableBashCompletion {
|
||||||
|
a.appendFlag(BashCompletionFlag)
|
||||||
|
}
|
||||||
a.appendFlag(BoolFlag{"version, v", "print the version"})
|
a.appendFlag(BoolFlag{"version, v", "print the version"})
|
||||||
a.appendFlag(BoolFlag{"help, h", "show help"})
|
a.appendFlag(BoolFlag{"help, h", "show help"})
|
||||||
|
|
||||||
@ -88,6 +96,10 @@ func (a *App) Run(arguments []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if checkCompletions(context) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if checkHelp(context) {
|
if checkHelp(context) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
41
app_test.go
41
app_test.go
@ -35,9 +35,9 @@ func ExampleAppHelp() {
|
|||||||
}
|
}
|
||||||
app.Commands = []cli.Command{
|
app.Commands = []cli.Command{
|
||||||
{
|
{
|
||||||
Name: "describeit",
|
Name: "describeit",
|
||||||
ShortName: "d",
|
ShortName: "d",
|
||||||
Usage: "use it to see a description",
|
Usage: "use it to see a description",
|
||||||
Description: "This is how we describe describeit the function",
|
Description: "This is how we describe describeit the function",
|
||||||
Action: func(c *cli.Context) {
|
Action: func(c *cli.Context) {
|
||||||
fmt.Printf("i like to describe things")
|
fmt.Printf("i like to describe things")
|
||||||
@ -58,6 +58,41 @@ func ExampleAppHelp() {
|
|||||||
// OPTIONS:
|
// OPTIONS:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleAppBashComplete() {
|
||||||
|
// set args for examples sake
|
||||||
|
os.Args = []string{"greet", "--generate-bash-completion"}
|
||||||
|
|
||||||
|
app := cli.NewApp()
|
||||||
|
app.Name = "greet"
|
||||||
|
app.EnableBashCompletion = true
|
||||||
|
app.Commands = []cli.Command{
|
||||||
|
{
|
||||||
|
Name: "describeit",
|
||||||
|
ShortName: "d",
|
||||||
|
Usage: "use it to see a description",
|
||||||
|
Description: "This is how we describe describeit the function",
|
||||||
|
Action: func(c *cli.Context) {
|
||||||
|
fmt.Printf("i like to describe things")
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
Name: "next",
|
||||||
|
Usage: "next example",
|
||||||
|
Description: "more stuff to see when generating bash completion",
|
||||||
|
Action: func(c *cli.Context) {
|
||||||
|
fmt.Printf("the next example")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
app.Run(os.Args)
|
||||||
|
// Output:
|
||||||
|
// describeit
|
||||||
|
// d
|
||||||
|
// next
|
||||||
|
// help
|
||||||
|
// h
|
||||||
|
}
|
||||||
|
|
||||||
func TestApp_Run(t *testing.T) {
|
func TestApp_Run(t *testing.T) {
|
||||||
s := ""
|
s := ""
|
||||||
|
|
||||||
|
13
autocomplete/bash_autocomplete
Normal file
13
autocomplete/bash_autocomplete
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
_cli_bash_autocomplete() {
|
||||||
|
local cur prev opts base
|
||||||
|
COMPREPLY=()
|
||||||
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||||
|
opts=$( ${COMP_WORDS[@]:0:COMP_CWORD} --generate-bash-completion )
|
||||||
|
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
complete -F _cli_bash_autocomplete $PROG
|
11
command.go
11
command.go
@ -16,6 +16,8 @@ type Command struct {
|
|||||||
Usage string
|
Usage string
|
||||||
// A longer explaination of how the command works
|
// A longer explaination of how the command works
|
||||||
Description string
|
Description string
|
||||||
|
// The function to call when checking for bash command completions
|
||||||
|
BashComplete func(context *Context)
|
||||||
// The function to call when this command is invoked
|
// The function to call when this command is invoked
|
||||||
Action func(context *Context)
|
Action func(context *Context)
|
||||||
// List of flags to parse
|
// List of flags to parse
|
||||||
@ -30,6 +32,10 @@ func (c Command) Run(ctx *Context) error {
|
|||||||
BoolFlag{"help, h", "show help"},
|
BoolFlag{"help, h", "show help"},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if ctx.App.EnableBashCompletion {
|
||||||
|
c.Flags = append(c.Flags, BashCompletionFlag)
|
||||||
|
}
|
||||||
|
|
||||||
set := flagSet(c.Name, c.Flags)
|
set := flagSet(c.Name, c.Flags)
|
||||||
set.SetOutput(ioutil.Discard)
|
set.SetOutput(ioutil.Discard)
|
||||||
|
|
||||||
@ -67,6 +73,11 @@ func (c Command) Run(ctx *Context) error {
|
|||||||
return nerr
|
return nerr
|
||||||
}
|
}
|
||||||
context := NewContext(ctx.App, set, ctx.globalSet)
|
context := NewContext(ctx.App, set, ctx.globalSet)
|
||||||
|
|
||||||
|
if checkCommandCompletions(context, c.Name) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if checkCommandHelp(context, c.Name) {
|
if checkCommandHelp(context, c.Name) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
3
flag.go
3
flag.go
@ -7,6 +7,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// This flag enables bash-completion for all commands and subcommands
|
||||||
|
var BashCompletionFlag = BoolFlag{"generate-bash-completion", ""}
|
||||||
|
|
||||||
// Flag is a common interface related to parsing flags in cli.
|
// Flag is a common interface related to parsing flags in cli.
|
||||||
// For more advanced flag parsing techniques, it is recomended that
|
// For more advanced flag parsing techniques, it is recomended that
|
||||||
// this interface be implemented.
|
// this interface be implemented.
|
||||||
|
45
help.go
45
help.go
@ -60,10 +60,21 @@ var helpCommand = Command{
|
|||||||
|
|
||||||
// Prints help for the App
|
// Prints help for the App
|
||||||
var HelpPrinter = printHelp
|
var HelpPrinter = printHelp
|
||||||
|
|
||||||
func ShowAppHelp(c *Context) {
|
func ShowAppHelp(c *Context) {
|
||||||
HelpPrinter(AppHelpTemplate, c.App)
|
HelpPrinter(AppHelpTemplate, c.App)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prints the list of subcommands as the default app completion method
|
||||||
|
func DefaultAppComplete(c *Context) {
|
||||||
|
for _, command := range c.App.Commands {
|
||||||
|
fmt.Println(command.Name)
|
||||||
|
if command.ShortName != "" {
|
||||||
|
fmt.Println(command.ShortName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Prints help for the given command
|
// Prints help for the given command
|
||||||
func ShowCommandHelp(c *Context, command string) {
|
func ShowCommandHelp(c *Context, command string) {
|
||||||
for _, c := range c.App.Commands {
|
for _, c := range c.App.Commands {
|
||||||
@ -81,6 +92,22 @@ func ShowVersion(c *Context) {
|
|||||||
fmt.Printf("%v version %v\n", c.App.Name, c.App.Version)
|
fmt.Printf("%v version %v\n", c.App.Name, c.App.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prints the lists of commands within a given context
|
||||||
|
func ShowCompletions(c *Context) {
|
||||||
|
a := c.App
|
||||||
|
if a != nil && a.BashComplete != nil {
|
||||||
|
a.BashComplete(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prints the custom completions for a given command
|
||||||
|
func ShowCommandCompletions(ctx *Context, command string) {
|
||||||
|
c := ctx.App.Command(command)
|
||||||
|
if c != nil && c.BashComplete != nil {
|
||||||
|
c.BashComplete(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func printHelp(templ string, data interface{}) {
|
func printHelp(templ string, data interface{}) {
|
||||||
w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
|
w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
|
||||||
t := template.Must(template.New("help").Parse(templ))
|
t := template.Must(template.New("help").Parse(templ))
|
||||||
@ -117,3 +144,21 @@ func checkCommandHelp(c *Context, name string) bool {
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkCompletions(c *Context) bool {
|
||||||
|
if c.GlobalBool(BashCompletionFlag.Name) && c.App.EnableBashCompletion {
|
||||||
|
ShowCompletions(c)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkCommandCompletions(c *Context, name string) bool {
|
||||||
|
if c.Bool(BashCompletionFlag.Name) && c.App.EnableBashCompletion {
|
||||||
|
ShowCommandCompletions(c, name)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user