Add command autocompletion.
This PR adds command autocompletion to the CLI.
This commit is contained in:
parent
0821ee67f5
commit
810ab68f94
10
main.go
10
main.go
|
@ -39,10 +39,12 @@ func RunCustom(args []string, commands map[string]cli.CommandFactory) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
cli := &cli.CLI{
|
cli := &cli.CLI{
|
||||||
Version: PrettyVersion(GetVersionParts()),
|
Name: "nomad",
|
||||||
Args: args,
|
Version: PrettyVersion(GetVersionParts()),
|
||||||
Commands: commands,
|
Args: args,
|
||||||
HelpFunc: cli.FilteredHelpFunc(commandsInclude, cli.BasicHelpFunc("nomad")),
|
Commands: commands,
|
||||||
|
Autocomplete: true,
|
||||||
|
HelpFunc: cli.FilteredHelpFunc(commandsInclude, cli.BasicHelpFunc("nomad")),
|
||||||
}
|
}
|
||||||
|
|
||||||
exitCode, err := cli.Run()
|
exitCode, err := cli.Run()
|
||||||
|
|
43
vendor/github.com/mitchellh/cli/autocomplete.go
generated
vendored
Normal file
43
vendor/github.com/mitchellh/cli/autocomplete.go
generated
vendored
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/posener/complete/cmd/install"
|
||||||
|
)
|
||||||
|
|
||||||
|
// autocompleteInstaller is an interface to be implemented to perform the
|
||||||
|
// autocomplete installation and uninstallation with a CLI.
|
||||||
|
//
|
||||||
|
// This interface is not exported because it only exists for unit tests
|
||||||
|
// to be able to test that the installation is called properly.
|
||||||
|
type autocompleteInstaller interface {
|
||||||
|
Install(string) error
|
||||||
|
Uninstall(string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// realAutocompleteInstaller uses the real install package to do the
|
||||||
|
// install/uninstall.
|
||||||
|
type realAutocompleteInstaller struct{}
|
||||||
|
|
||||||
|
func (i *realAutocompleteInstaller) Install(cmd string) error {
|
||||||
|
return install.Install(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *realAutocompleteInstaller) Uninstall(cmd string) error {
|
||||||
|
return install.Uninstall(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// mockAutocompleteInstaller is used for tests to record the install/uninstall.
|
||||||
|
type mockAutocompleteInstaller struct {
|
||||||
|
InstallCalled bool
|
||||||
|
UninstallCalled bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *mockAutocompleteInstaller) Install(cmd string) error {
|
||||||
|
i.InstallCalled = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *mockAutocompleteInstaller) Uninstall(cmd string) error {
|
||||||
|
i.UninstallCalled = true
|
||||||
|
return nil
|
||||||
|
}
|
204
vendor/github.com/mitchellh/cli/cli.go
generated
vendored
204
vendor/github.com/mitchellh/cli/cli.go
generated
vendored
|
@ -11,6 +11,7 @@ import (
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
"github.com/armon/go-radix"
|
"github.com/armon/go-radix"
|
||||||
|
"github.com/posener/complete"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CLI contains the state necessary to run subcommands and parse the
|
// CLI contains the state necessary to run subcommands and parse the
|
||||||
|
@ -66,6 +67,32 @@ type CLI struct {
|
||||||
// Version of the CLI.
|
// Version of the CLI.
|
||||||
Version string
|
Version string
|
||||||
|
|
||||||
|
// Autocomplete enables or disables subcommand auto-completion support.
|
||||||
|
// This is enabled by default when NewCLI is called. Otherwise, this
|
||||||
|
// must enabled explicitly.
|
||||||
|
//
|
||||||
|
// Autocomplete requires the "Name" option to be set on CLI. This name
|
||||||
|
// should be set exactly to the binary name that is autocompleted.
|
||||||
|
//
|
||||||
|
// Autocompletion is supported via the github.com/posener/complete
|
||||||
|
// library. This library supports both bash and zsh. To add support
|
||||||
|
// for other shells, please see that library.
|
||||||
|
//
|
||||||
|
// AutocompleteInstall and AutocompleteUninstall are the global flag
|
||||||
|
// names for installing and uninstalling the autocompletion handlers
|
||||||
|
// for the user's shell. The flag should omit the hyphen(s) in front of
|
||||||
|
// the value. Both single and double hyphens will automatically be supported
|
||||||
|
// for the flag name. These default to `autocomplete-install` and
|
||||||
|
// `autocomplete-uninstall` respectively.
|
||||||
|
//
|
||||||
|
// AutocompleteGlobalFlags are a mapping of global flags for
|
||||||
|
// autocompletion. The help and version flags are automatically added.
|
||||||
|
Autocomplete bool
|
||||||
|
AutocompleteInstall string
|
||||||
|
AutocompleteUninstall string
|
||||||
|
AutocompleteGlobalFlags complete.Flags
|
||||||
|
autocompleteInstaller autocompleteInstaller // For tests
|
||||||
|
|
||||||
// HelpFunc and HelpWriter are used to output help information, if
|
// HelpFunc and HelpWriter are used to output help information, if
|
||||||
// requested.
|
// requested.
|
||||||
//
|
//
|
||||||
|
@ -78,23 +105,32 @@ type CLI struct {
|
||||||
HelpFunc HelpFunc
|
HelpFunc HelpFunc
|
||||||
HelpWriter io.Writer
|
HelpWriter io.Writer
|
||||||
|
|
||||||
|
//---------------------------------------------------------------
|
||||||
|
// Internal fields set automatically
|
||||||
|
|
||||||
once sync.Once
|
once sync.Once
|
||||||
|
autocomplete *complete.Complete
|
||||||
commandTree *radix.Tree
|
commandTree *radix.Tree
|
||||||
commandNested bool
|
commandNested bool
|
||||||
isHelp bool
|
|
||||||
subcommand string
|
subcommand string
|
||||||
subcommandArgs []string
|
subcommandArgs []string
|
||||||
topFlags []string
|
topFlags []string
|
||||||
|
|
||||||
isVersion bool
|
// These are true when special global flags are set. We can/should
|
||||||
|
// probably use a bitset for this one day.
|
||||||
|
isHelp bool
|
||||||
|
isVersion bool
|
||||||
|
isAutocompleteInstall bool
|
||||||
|
isAutocompleteUninstall bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClI returns a new CLI instance with sensible defaults.
|
// NewClI returns a new CLI instance with sensible defaults.
|
||||||
func NewCLI(app, version string) *CLI {
|
func NewCLI(app, version string) *CLI {
|
||||||
return &CLI{
|
return &CLI{
|
||||||
Name: app,
|
Name: app,
|
||||||
Version: version,
|
Version: version,
|
||||||
HelpFunc: BasicHelpFunc(app),
|
HelpFunc: BasicHelpFunc(app),
|
||||||
|
Autocomplete: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -129,6 +165,45 @@ func (c *CLI) Run() (int, error) {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we're attempting to install or uninstall autocomplete then handle
|
||||||
|
if c.Autocomplete {
|
||||||
|
// Autocomplete requires the "Name" to be set so that we know what
|
||||||
|
// command to setup the autocomplete on.
|
||||||
|
if c.Name == "" {
|
||||||
|
return 1, fmt.Errorf(
|
||||||
|
"internal error: CLI.Name must be specified for autocomplete to work")
|
||||||
|
}
|
||||||
|
|
||||||
|
// If both install and uninstall flags are specified, then error
|
||||||
|
if c.isAutocompleteInstall && c.isAutocompleteUninstall {
|
||||||
|
return 1, fmt.Errorf(
|
||||||
|
"Either the autocomplete install or uninstall flag may " +
|
||||||
|
"be specified, but not both.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the install flag is specified, perform the install or uninstall
|
||||||
|
if c.isAutocompleteInstall {
|
||||||
|
if err := c.autocompleteInstaller.Install(c.Name); err != nil {
|
||||||
|
return 1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.isAutocompleteUninstall {
|
||||||
|
if err := c.autocompleteInstaller.Uninstall(c.Name); err != nil {
|
||||||
|
return 1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is a autocompletion request, satisfy it
|
||||||
|
if c.autocomplete.Complete() {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Attempt to get the factory function for creating the command
|
// Attempt to get the factory function for creating the command
|
||||||
// implementation. If the command is invalid or blank, it is an error.
|
// implementation. If the command is invalid or blank, it is an error.
|
||||||
raw, ok := c.commandTree.Get(c.Subcommand())
|
raw, ok := c.commandTree.Get(c.Subcommand())
|
||||||
|
@ -268,10 +343,111 @@ func (c *CLI) init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup autocomplete if we have it enabled. We have to do this after
|
||||||
|
// the command tree is setup so we can use the radix tree to easily find
|
||||||
|
// all subcommands.
|
||||||
|
if c.Autocomplete {
|
||||||
|
c.initAutocomplete()
|
||||||
|
}
|
||||||
|
|
||||||
// Process the args
|
// Process the args
|
||||||
c.processArgs()
|
c.processArgs()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CLI) initAutocomplete() {
|
||||||
|
if c.AutocompleteInstall == "" {
|
||||||
|
c.AutocompleteInstall = defaultAutocompleteInstall
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.AutocompleteUninstall == "" {
|
||||||
|
c.AutocompleteUninstall = defaultAutocompleteUninstall
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.autocompleteInstaller == nil {
|
||||||
|
c.autocompleteInstaller = &realAutocompleteInstaller{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the root command
|
||||||
|
cmd := c.initAutocompleteSub("")
|
||||||
|
|
||||||
|
// For the root, we add the global flags to the "Flags". This way
|
||||||
|
// they don't show up on every command.
|
||||||
|
cmd.Flags = map[string]complete.Predictor{
|
||||||
|
"-" + c.AutocompleteInstall: complete.PredictNothing,
|
||||||
|
"-" + c.AutocompleteUninstall: complete.PredictNothing,
|
||||||
|
"-help": complete.PredictNothing,
|
||||||
|
"-version": complete.PredictNothing,
|
||||||
|
}
|
||||||
|
cmd.GlobalFlags = c.AutocompleteGlobalFlags
|
||||||
|
|
||||||
|
c.autocomplete = complete.New(c.Name, cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// initAutocompleteSub creates the complete.Command for a subcommand with
|
||||||
|
// the given prefix. This will continue recursively for all subcommands.
|
||||||
|
// The prefix "" (empty string) can be used for the root command.
|
||||||
|
func (c *CLI) initAutocompleteSub(prefix string) complete.Command {
|
||||||
|
var cmd complete.Command
|
||||||
|
walkFn := func(k string, raw interface{}) bool {
|
||||||
|
if len(prefix) > 0 {
|
||||||
|
// If we have a prefix, trim the prefix + 1 (for the space)
|
||||||
|
// Example: turns "sub one" to "one" with prefix "sub"
|
||||||
|
k = k[len(prefix)+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep track of the full key so that we can nest further if necessary
|
||||||
|
fullKey := k
|
||||||
|
|
||||||
|
if idx := strings.LastIndex(k, " "); idx >= 0 {
|
||||||
|
// If there is a space, we trim up to the space
|
||||||
|
k = k[:idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
if idx := strings.LastIndex(k, " "); idx >= 0 {
|
||||||
|
// This catches the scenario just in case where we see "sub one"
|
||||||
|
// before "sub". This will let us properly setup the subcommand
|
||||||
|
// regardless.
|
||||||
|
k = k[idx+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := cmd.Sub[k]; ok {
|
||||||
|
// If we already tracked this subcommand then ignore
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Sub == nil {
|
||||||
|
cmd.Sub = complete.Commands(make(map[string]complete.Command))
|
||||||
|
}
|
||||||
|
subCmd := c.initAutocompleteSub(fullKey)
|
||||||
|
|
||||||
|
// Instantiate the command so that we can check if the command is
|
||||||
|
// a CommandAutocomplete implementation. If there is an error
|
||||||
|
// creating the command, we just ignore it since that will be caught
|
||||||
|
// later.
|
||||||
|
impl, err := raw.(CommandFactory)()
|
||||||
|
if err != nil {
|
||||||
|
impl = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if it implements ComandAutocomplete. If so, setup the autocomplete
|
||||||
|
if c, ok := impl.(CommandAutocomplete); ok {
|
||||||
|
subCmd.Args = c.AutocompleteArgs()
|
||||||
|
subCmd.Flags = c.AutocompleteFlags()
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Sub[k] = subCmd
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
walkPrefix := prefix
|
||||||
|
if walkPrefix != "" {
|
||||||
|
walkPrefix += " "
|
||||||
|
}
|
||||||
|
|
||||||
|
c.commandTree.WalkPrefix(walkPrefix, walkFn)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
func (c *CLI) commandHelp(command Command) {
|
func (c *CLI) commandHelp(command Command) {
|
||||||
// Get the template to use
|
// Get the template to use
|
||||||
tpl := strings.TrimSpace(defaultHelpTemplate)
|
tpl := strings.TrimSpace(defaultHelpTemplate)
|
||||||
|
@ -404,6 +580,19 @@ func (c *CLI) processArgs() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for autocomplete flags
|
||||||
|
if c.Autocomplete {
|
||||||
|
if arg == "-"+c.AutocompleteInstall || arg == "--"+c.AutocompleteInstall {
|
||||||
|
c.isAutocompleteInstall = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if arg == "-"+c.AutocompleteUninstall || arg == "--"+c.AutocompleteUninstall {
|
||||||
|
c.isAutocompleteUninstall = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if c.subcommand == "" {
|
if c.subcommand == "" {
|
||||||
// Check for version flags if not in a subcommand.
|
// Check for version flags if not in a subcommand.
|
||||||
if arg == "-v" || arg == "-version" || arg == "--version" {
|
if arg == "-v" || arg == "-version" || arg == "--version" {
|
||||||
|
@ -456,6 +645,11 @@ func (c *CLI) processArgs() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// defaultAutocompleteInstall and defaultAutocompleteUninstall are the
|
||||||
|
// default values for the autocomplete install and uninstall flags.
|
||||||
|
const defaultAutocompleteInstall = "autocomplete-install"
|
||||||
|
const defaultAutocompleteUninstall = "autocomplete-uninstall"
|
||||||
|
|
||||||
const defaultHelpTemplate = `
|
const defaultHelpTemplate = `
|
||||||
{{.Help}}{{if gt (len .Subcommands) 0}}
|
{{.Help}}{{if gt (len .Subcommands) 0}}
|
||||||
|
|
||||||
|
|
20
vendor/github.com/mitchellh/cli/command.go
generated
vendored
20
vendor/github.com/mitchellh/cli/command.go
generated
vendored
|
@ -1,5 +1,9 @@
|
||||||
package cli
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/posener/complete"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// RunResultHelp is a value that can be returned from Run to signal
|
// RunResultHelp is a value that can be returned from Run to signal
|
||||||
// to the CLI to render the help output.
|
// to the CLI to render the help output.
|
||||||
|
@ -26,6 +30,22 @@ type Command interface {
|
||||||
Synopsis() string
|
Synopsis() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CommandAutocomplete is an extension of Command that enables fine-grained
|
||||||
|
// autocompletion. Subcommand autocompletion will work even if this interface
|
||||||
|
// is not implemented. By implementing this interface, more advanced
|
||||||
|
// autocompletion is enabled.
|
||||||
|
type CommandAutocomplete interface {
|
||||||
|
// AutocompleteArgs returns the argument predictor for this command.
|
||||||
|
// If argument completion is not supported, this should return
|
||||||
|
// complete.PredictNothing.
|
||||||
|
AutocompleteArgs() complete.Predictor
|
||||||
|
|
||||||
|
// AutocompleteFlags returns a mapping of supported flags and autocomplete
|
||||||
|
// options for this command. The map key for the Flags map should be the
|
||||||
|
// complete flag such as "-foo" or "--foo".
|
||||||
|
AutocompleteFlags() complete.Flags
|
||||||
|
}
|
||||||
|
|
||||||
// CommandHelpTemplate is an extension of Command that also has a function
|
// CommandHelpTemplate is an extension of Command that also has a function
|
||||||
// for returning a template for the help rather than the help itself. In
|
// for returning a template for the help rather than the help itself. In
|
||||||
// this scenario, both Help and HelpTemplate should be implemented.
|
// this scenario, both Help and HelpTemplate should be implemented.
|
||||||
|
|
21
vendor/github.com/mitchellh/cli/command_mock.go
generated
vendored
21
vendor/github.com/mitchellh/cli/command_mock.go
generated
vendored
|
@ -1,5 +1,9 @@
|
||||||
package cli
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/posener/complete"
|
||||||
|
)
|
||||||
|
|
||||||
// MockCommand is an implementation of Command that can be used for tests.
|
// MockCommand is an implementation of Command that can be used for tests.
|
||||||
// It is publicly exported from this package in case you want to use it
|
// It is publicly exported from this package in case you want to use it
|
||||||
// externally.
|
// externally.
|
||||||
|
@ -29,6 +33,23 @@ func (c *MockCommand) Synopsis() string {
|
||||||
return c.SynopsisText
|
return c.SynopsisText
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MockCommandAutocomplete is an implementation of CommandAutocomplete.
|
||||||
|
type MockCommandAutocomplete struct {
|
||||||
|
MockCommand
|
||||||
|
|
||||||
|
// Settable
|
||||||
|
AutocompleteArgsValue complete.Predictor
|
||||||
|
AutocompleteFlagsValue complete.Flags
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *MockCommandAutocomplete) AutocompleteArgs() complete.Predictor {
|
||||||
|
return c.AutocompleteArgsValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *MockCommandAutocomplete) AutocompleteFlags() complete.Flags {
|
||||||
|
return c.AutocompleteFlagsValue
|
||||||
|
}
|
||||||
|
|
||||||
// MockCommandHelpTemplate is an implementation of CommandHelpTemplate.
|
// MockCommandHelpTemplate is an implementation of CommandHelpTemplate.
|
||||||
type MockCommandHelpTemplate struct {
|
type MockCommandHelpTemplate struct {
|
||||||
MockCommand
|
MockCommand
|
||||||
|
|
21
vendor/github.com/posener/complete/LICENSE.txt
generated
vendored
Normal file
21
vendor/github.com/posener/complete/LICENSE.txt
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2017 Eyal Posener
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
75
vendor/github.com/posener/complete/args.go
generated
vendored
Normal file
75
vendor/github.com/posener/complete/args.go
generated
vendored
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
package complete
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Args describes command line arguments
|
||||||
|
type Args struct {
|
||||||
|
// All lists of all arguments in command line (not including the command itself)
|
||||||
|
All []string
|
||||||
|
// Completed lists of all completed arguments in command line,
|
||||||
|
// If the last one is still being typed - no space after it,
|
||||||
|
// it won't appear in this list of arguments.
|
||||||
|
Completed []string
|
||||||
|
// Last argument in command line, the one being typed, if the last
|
||||||
|
// character in the command line is a space, this argument will be empty,
|
||||||
|
// otherwise this would be the last word.
|
||||||
|
Last string
|
||||||
|
// LastCompleted is the last argument that was fully typed.
|
||||||
|
// If the last character in the command line is space, this would be the
|
||||||
|
// last word, otherwise, it would be the word before that.
|
||||||
|
LastCompleted string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Directory gives the directory of the current written
|
||||||
|
// last argument if it represents a file name being written.
|
||||||
|
// in case that it is not, we fall back to the current directory.
|
||||||
|
func (a Args) Directory() string {
|
||||||
|
if info, err := os.Stat(a.Last); err == nil && info.IsDir() {
|
||||||
|
return fixPathForm(a.Last, a.Last)
|
||||||
|
}
|
||||||
|
dir := filepath.Dir(a.Last)
|
||||||
|
if info, err := os.Stat(dir); err != nil || !info.IsDir() {
|
||||||
|
return "./"
|
||||||
|
}
|
||||||
|
return fixPathForm(a.Last, dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newArgs(line []string) Args {
|
||||||
|
completed := removeLast(line[1:])
|
||||||
|
return Args{
|
||||||
|
All: line[1:],
|
||||||
|
Completed: completed,
|
||||||
|
Last: last(line),
|
||||||
|
LastCompleted: last(completed),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Args) from(i int) Args {
|
||||||
|
if i > len(a.All) {
|
||||||
|
i = len(a.All)
|
||||||
|
}
|
||||||
|
a.All = a.All[i:]
|
||||||
|
|
||||||
|
if i > len(a.Completed) {
|
||||||
|
i = len(a.Completed)
|
||||||
|
}
|
||||||
|
a.Completed = a.Completed[i:]
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeLast(a []string) []string {
|
||||||
|
if len(a) > 0 {
|
||||||
|
return a[:len(a)-1]
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func last(args []string) (last string) {
|
||||||
|
if len(args) > 0 {
|
||||||
|
last = args[len(args)-1]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
128
vendor/github.com/posener/complete/cmd/cmd.go
generated
vendored
Normal file
128
vendor/github.com/posener/complete/cmd/cmd.go
generated
vendored
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
// Package cmd used for command line options for the complete tool
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/posener/complete/cmd/install"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CLI for command line
|
||||||
|
type CLI struct {
|
||||||
|
Name string
|
||||||
|
InstallName string
|
||||||
|
UninstallName string
|
||||||
|
|
||||||
|
install bool
|
||||||
|
uninstall bool
|
||||||
|
yes bool
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultInstallName = "install"
|
||||||
|
defaultUninstallName = "uninstall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Run is used when running complete in command line mode.
|
||||||
|
// this is used when the complete is not completing words, but to
|
||||||
|
// install it or uninstall it.
|
||||||
|
func (f *CLI) Run() bool {
|
||||||
|
err := f.validate()
|
||||||
|
if err != nil {
|
||||||
|
os.Stderr.WriteString(err.Error() + "\n")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case f.install:
|
||||||
|
f.prompt()
|
||||||
|
err = install.Install(f.Name)
|
||||||
|
case f.uninstall:
|
||||||
|
f.prompt()
|
||||||
|
err = install.Uninstall(f.Name)
|
||||||
|
default:
|
||||||
|
// non of the action flags matched,
|
||||||
|
// returning false should make the real program execute
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("%s failed! %s\n", f.action(), err)
|
||||||
|
os.Exit(3)
|
||||||
|
}
|
||||||
|
fmt.Println("Done!")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// prompt use for approval
|
||||||
|
// exit if approval was not given
|
||||||
|
func (f *CLI) prompt() {
|
||||||
|
defer fmt.Println(f.action() + "ing...")
|
||||||
|
if f.yes {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf("%s completion for %s? ", f.action(), f.Name)
|
||||||
|
var answer string
|
||||||
|
fmt.Scanln(&answer)
|
||||||
|
|
||||||
|
switch strings.ToLower(answer) {
|
||||||
|
case "y", "yes":
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
fmt.Println("Cancelling...")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFlags adds the CLI flags to the flag set.
|
||||||
|
// If flags is nil, the default command line flags will be taken.
|
||||||
|
// Pass non-empty strings as installName and uninstallName to override the default
|
||||||
|
// flag names.
|
||||||
|
func (f *CLI) AddFlags(flags *flag.FlagSet) {
|
||||||
|
if flags == nil {
|
||||||
|
flags = flag.CommandLine
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.InstallName == "" {
|
||||||
|
f.InstallName = defaultInstallName
|
||||||
|
}
|
||||||
|
if f.UninstallName == "" {
|
||||||
|
f.UninstallName = defaultUninstallName
|
||||||
|
}
|
||||||
|
|
||||||
|
if flags.Lookup(f.InstallName) == nil {
|
||||||
|
flags.BoolVar(&f.install, f.InstallName, false,
|
||||||
|
fmt.Sprintf("Install completion for %s command", f.Name))
|
||||||
|
}
|
||||||
|
if flags.Lookup(f.UninstallName) == nil {
|
||||||
|
flags.BoolVar(&f.uninstall, f.UninstallName, false,
|
||||||
|
fmt.Sprintf("Uninstall completion for %s command", f.Name))
|
||||||
|
}
|
||||||
|
if flags.Lookup("y") == nil {
|
||||||
|
flags.BoolVar(&f.yes, "y", false, "Don't prompt user for typing 'yes'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate the CLI
|
||||||
|
func (f *CLI) validate() error {
|
||||||
|
if f.install && f.uninstall {
|
||||||
|
return errors.New("Install and uninstall are mutually exclusive")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// action name according to the CLI values.
|
||||||
|
func (f *CLI) action() string {
|
||||||
|
switch {
|
||||||
|
case f.install:
|
||||||
|
return "Install"
|
||||||
|
case f.uninstall:
|
||||||
|
return "Uninstall"
|
||||||
|
default:
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
}
|
32
vendor/github.com/posener/complete/cmd/install/bash.go
generated
vendored
Normal file
32
vendor/github.com/posener/complete/cmd/install/bash.go
generated
vendored
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package install
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// (un)install in bash
|
||||||
|
// basically adds/remove from .bashrc:
|
||||||
|
//
|
||||||
|
// complete -C </path/to/completion/command> <command>
|
||||||
|
type bash struct {
|
||||||
|
rc string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b bash) Install(cmd, bin string) error {
|
||||||
|
completeCmd := b.cmd(cmd, bin)
|
||||||
|
if lineInFile(b.rc, completeCmd) {
|
||||||
|
return fmt.Errorf("already installed in %s", b.rc)
|
||||||
|
}
|
||||||
|
return appendToFile(b.rc, completeCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b bash) Uninstall(cmd, bin string) error {
|
||||||
|
completeCmd := b.cmd(cmd, bin)
|
||||||
|
if !lineInFile(b.rc, completeCmd) {
|
||||||
|
return fmt.Errorf("does not installed in %s", b.rc)
|
||||||
|
}
|
||||||
|
|
||||||
|
return removeFromFile(b.rc, completeCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bash) cmd(cmd, bin string) string {
|
||||||
|
return fmt.Sprintf("complete -C %s %s", bin, cmd)
|
||||||
|
}
|
92
vendor/github.com/posener/complete/cmd/install/install.go
generated
vendored
Normal file
92
vendor/github.com/posener/complete/cmd/install/install.go
generated
vendored
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
package install
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"os/user"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
|
)
|
||||||
|
|
||||||
|
type installer interface {
|
||||||
|
Install(cmd, bin string) error
|
||||||
|
Uninstall(cmd, bin string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install complete command given:
|
||||||
|
// cmd: is the command name
|
||||||
|
func Install(cmd string) error {
|
||||||
|
is := installers()
|
||||||
|
if len(is) == 0 {
|
||||||
|
return errors.New("Did not found any shells to install")
|
||||||
|
}
|
||||||
|
bin, err := getBinaryPath()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, i := range is {
|
||||||
|
errI := i.Install(cmd, bin)
|
||||||
|
if errI != nil {
|
||||||
|
err = multierror.Append(err, errI)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uninstall complete command given:
|
||||||
|
// cmd: is the command name
|
||||||
|
func Uninstall(cmd string) error {
|
||||||
|
is := installers()
|
||||||
|
if len(is) == 0 {
|
||||||
|
return errors.New("Did not found any shells to uninstall")
|
||||||
|
}
|
||||||
|
bin, err := getBinaryPath()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, i := range is {
|
||||||
|
errI := i.Uninstall(cmd, bin)
|
||||||
|
if errI != nil {
|
||||||
|
multierror.Append(err, errI)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func installers() (i []installer) {
|
||||||
|
for _, rc := range [...]string{".bashrc", ".bash_profile"} {
|
||||||
|
if f := rcFile(rc); f != "" {
|
||||||
|
i = append(i, bash{f})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if f := rcFile(".zshrc"); f != "" {
|
||||||
|
i = append(i, zsh{f})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBinaryPath() (string, error) {
|
||||||
|
bin, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return filepath.Abs(bin)
|
||||||
|
}
|
||||||
|
|
||||||
|
func rcFile(name string) string {
|
||||||
|
u, err := user.Current()
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
path := filepath.Join(u.HomeDir, name)
|
||||||
|
if _, err := os.Stat(path); err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
118
vendor/github.com/posener/complete/cmd/install/utils.go
generated
vendored
Normal file
118
vendor/github.com/posener/complete/cmd/install/utils.go
generated
vendored
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
package install
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func lineInFile(name string, lookFor string) bool {
|
||||||
|
f, err := os.Open(name)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
r := bufio.NewReader(f)
|
||||||
|
prefix := []byte{}
|
||||||
|
for {
|
||||||
|
line, isPrefix, err := r.ReadLine()
|
||||||
|
if err == io.EOF {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if isPrefix {
|
||||||
|
prefix = append(prefix, line...)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
line = append(prefix, line...)
|
||||||
|
if string(line) == lookFor {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
prefix = prefix[:0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendToFile(name string, content string) error {
|
||||||
|
f, err := os.OpenFile(name, os.O_RDWR|os.O_APPEND, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
_, err = f.WriteString(fmt.Sprintf("\n%s\n", content))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeFromFile(name string, content string) error {
|
||||||
|
backup := name + ".bck"
|
||||||
|
err := copyFile(name, backup)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
temp, err := removeContentToTempFile(name, content)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = copyFile(temp, name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.Remove(backup)
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeContentToTempFile(name, content string) (string, error) {
|
||||||
|
rf, err := os.Open(name)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer rf.Close()
|
||||||
|
wf, err := ioutil.TempFile("/tmp", "complete-")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer wf.Close()
|
||||||
|
|
||||||
|
r := bufio.NewReader(rf)
|
||||||
|
prefix := []byte{}
|
||||||
|
for {
|
||||||
|
line, isPrefix, err := r.ReadLine()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if isPrefix {
|
||||||
|
prefix = append(prefix, line...)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
line = append(prefix, line...)
|
||||||
|
str := string(line)
|
||||||
|
if str == content {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
wf.WriteString(str + "\n")
|
||||||
|
prefix = prefix[:0]
|
||||||
|
}
|
||||||
|
return wf.Name(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyFile(src string, dst string) error {
|
||||||
|
in, err := os.Open(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer in.Close()
|
||||||
|
out, err := os.Create(dst)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer out.Close()
|
||||||
|
_, err = io.Copy(out, in)
|
||||||
|
return err
|
||||||
|
}
|
39
vendor/github.com/posener/complete/cmd/install/zsh.go
generated
vendored
Normal file
39
vendor/github.com/posener/complete/cmd/install/zsh.go
generated
vendored
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
package install
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// (un)install in zsh
|
||||||
|
// basically adds/remove from .zshrc:
|
||||||
|
//
|
||||||
|
// autoload -U +X bashcompinit && bashcompinit"
|
||||||
|
// complete -C </path/to/completion/command> <command>
|
||||||
|
type zsh struct {
|
||||||
|
rc string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z zsh) Install(cmd, bin string) error {
|
||||||
|
completeCmd := z.cmd(cmd, bin)
|
||||||
|
if lineInFile(z.rc, completeCmd) {
|
||||||
|
return fmt.Errorf("already installed in %s", z.rc)
|
||||||
|
}
|
||||||
|
|
||||||
|
bashCompInit := "autoload -U +X bashcompinit && bashcompinit"
|
||||||
|
if !lineInFile(z.rc, bashCompInit) {
|
||||||
|
completeCmd = bashCompInit + "\n" + completeCmd
|
||||||
|
}
|
||||||
|
|
||||||
|
return appendToFile(z.rc, completeCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z zsh) Uninstall(cmd, bin string) error {
|
||||||
|
completeCmd := z.cmd(cmd, bin)
|
||||||
|
if !lineInFile(z.rc, completeCmd) {
|
||||||
|
return fmt.Errorf("does not installed in %s", z.rc)
|
||||||
|
}
|
||||||
|
|
||||||
|
return removeFromFile(z.rc, completeCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (zsh) cmd(cmd, bin string) string {
|
||||||
|
return fmt.Sprintf("complete -C %s %s", bin, cmd)
|
||||||
|
}
|
106
vendor/github.com/posener/complete/command.go
generated
vendored
Normal file
106
vendor/github.com/posener/complete/command.go
generated
vendored
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
package complete
|
||||||
|
|
||||||
|
import "github.com/posener/complete/match"
|
||||||
|
|
||||||
|
// Command represents a command line
|
||||||
|
// It holds the data that enables auto completion of command line
|
||||||
|
// Command can also be a sub command.
|
||||||
|
type Command struct {
|
||||||
|
// Sub is map of sub commands of the current command
|
||||||
|
// The key refer to the sub command name, and the value is it's
|
||||||
|
// Command descriptive struct.
|
||||||
|
Sub Commands
|
||||||
|
|
||||||
|
// Flags is a map of flags that the command accepts.
|
||||||
|
// The key is the flag name, and the value is it's predictions.
|
||||||
|
Flags Flags
|
||||||
|
|
||||||
|
// GlobalFlags is a map of flags that the command accepts.
|
||||||
|
// Global flags that can appear also after a sub command.
|
||||||
|
GlobalFlags Flags
|
||||||
|
|
||||||
|
// Args are extra arguments that the command accepts, those who are
|
||||||
|
// given without any flag before.
|
||||||
|
Args Predictor
|
||||||
|
}
|
||||||
|
|
||||||
|
// Predict returns all possible predictions for args according to the command struct
|
||||||
|
func (c *Command) Predict(a Args) (predictions []string) {
|
||||||
|
predictions, _ = c.predict(a)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commands is the type of Sub member, it maps a command name to a command struct
|
||||||
|
type Commands map[string]Command
|
||||||
|
|
||||||
|
// Predict completion of sub command names names according to command line arguments
|
||||||
|
func (c Commands) Predict(a Args) (prediction []string) {
|
||||||
|
for sub := range c {
|
||||||
|
if match.Prefix(sub, a.Last) {
|
||||||
|
prediction = append(prediction, sub)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flags is the type Flags of the Flags member, it maps a flag name to the flag predictions.
|
||||||
|
type Flags map[string]Predictor
|
||||||
|
|
||||||
|
// Predict completion of flags names according to command line arguments
|
||||||
|
func (f Flags) Predict(a Args) (prediction []string) {
|
||||||
|
for flag := range f {
|
||||||
|
if match.Prefix(flag, a.Last) {
|
||||||
|
prediction = append(prediction, flag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// predict options
|
||||||
|
// only is set to true if no more options are allowed to be returned
|
||||||
|
// those are in cases of special flag that has specific completion arguments,
|
||||||
|
// and other flags or sub commands can't come after it.
|
||||||
|
func (c *Command) predict(a Args) (options []string, only bool) {
|
||||||
|
|
||||||
|
// search sub commands for predictions first
|
||||||
|
subCommandFound := false
|
||||||
|
for i, arg := range a.Completed {
|
||||||
|
if cmd, ok := c.Sub[arg]; ok {
|
||||||
|
subCommandFound = true
|
||||||
|
|
||||||
|
// recursive call for sub command
|
||||||
|
options, only = cmd.predict(a.from(i))
|
||||||
|
if only {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if last completed word is a global flag that we need to complete
|
||||||
|
if predictor, ok := c.GlobalFlags[a.LastCompleted]; ok && predictor != nil {
|
||||||
|
Log("Predicting according to global flag %s", a.LastCompleted)
|
||||||
|
return predictor.Predict(a), true
|
||||||
|
}
|
||||||
|
|
||||||
|
options = append(options, c.GlobalFlags.Predict(a)...)
|
||||||
|
|
||||||
|
// if a sub command was entered, we won't add the parent command
|
||||||
|
// completions and we return here.
|
||||||
|
if subCommandFound {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// if last completed word is a command flag that we need to complete
|
||||||
|
if predictor, ok := c.Flags[a.LastCompleted]; ok && predictor != nil {
|
||||||
|
Log("Predicting according to flag %s", a.LastCompleted)
|
||||||
|
return predictor.Predict(a), true
|
||||||
|
}
|
||||||
|
|
||||||
|
options = append(options, c.Sub.Predict(a)...)
|
||||||
|
options = append(options, c.Flags.Predict(a)...)
|
||||||
|
if c.Args != nil {
|
||||||
|
options = append(options, c.Args.Predict(a)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
86
vendor/github.com/posener/complete/complete.go
generated
vendored
Normal file
86
vendor/github.com/posener/complete/complete.go
generated
vendored
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
// Package complete provides a tool for bash writing bash completion in go.
|
||||||
|
//
|
||||||
|
// Writing bash completion scripts is a hard work. This package provides an easy way
|
||||||
|
// to create bash completion scripts for any command, and also an easy way to install/uninstall
|
||||||
|
// the completion of the command.
|
||||||
|
package complete
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/posener/complete/cmd"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
envComplete = "COMP_LINE"
|
||||||
|
envDebug = "COMP_DEBUG"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Complete structs define completion for a command with CLI options
|
||||||
|
type Complete struct {
|
||||||
|
Command Command
|
||||||
|
cmd.CLI
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new complete command.
|
||||||
|
// name is the name of command we want to auto complete.
|
||||||
|
// IMPORTANT: it must be the same name - if the auto complete
|
||||||
|
// completes the 'go' command, name must be equal to "go".
|
||||||
|
// command is the struct of the command completion.
|
||||||
|
func New(name string, command Command) *Complete {
|
||||||
|
return &Complete{
|
||||||
|
Command: command,
|
||||||
|
CLI: cmd.CLI{Name: name},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run runs the completion and add installation flags beforehand.
|
||||||
|
// The flags are added to the main flag CommandLine variable.
|
||||||
|
func (c *Complete) Run() bool {
|
||||||
|
c.AddFlags(nil)
|
||||||
|
flag.Parse()
|
||||||
|
return c.Complete()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Complete a command from completion line in environment variable,
|
||||||
|
// and print out the complete options.
|
||||||
|
// returns success if the completion ran or if the cli matched
|
||||||
|
// any of the given flags, false otherwise
|
||||||
|
// For installation: it assumes that flags were added and parsed before
|
||||||
|
// it was called.
|
||||||
|
func (c *Complete) Complete() bool {
|
||||||
|
line, ok := getLine()
|
||||||
|
if !ok {
|
||||||
|
// make sure flags parsed,
|
||||||
|
// in case they were not added in the main program
|
||||||
|
return c.CLI.Run()
|
||||||
|
}
|
||||||
|
Log("Completing line: %s", line)
|
||||||
|
|
||||||
|
a := newArgs(line)
|
||||||
|
|
||||||
|
options := c.Command.Predict(a)
|
||||||
|
|
||||||
|
Log("Completion: %s", options)
|
||||||
|
output(options)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLine() ([]string, bool) {
|
||||||
|
line := os.Getenv(envComplete)
|
||||||
|
if line == "" {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return strings.Split(line, " "), true
|
||||||
|
}
|
||||||
|
|
||||||
|
func output(options []string) {
|
||||||
|
Log("")
|
||||||
|
// stdout of program defines the complete options
|
||||||
|
for _, option := range options {
|
||||||
|
fmt.Println(option)
|
||||||
|
}
|
||||||
|
}
|
23
vendor/github.com/posener/complete/log.go
generated
vendored
Normal file
23
vendor/github.com/posener/complete/log.go
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package complete
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Log is used for debugging purposes
|
||||||
|
// since complete is running on tab completion, it is nice to
|
||||||
|
// have logs to the stderr (when writing your own completer)
|
||||||
|
// to write logs, set the COMP_DEBUG environment variable and
|
||||||
|
// use complete.Log in the complete program
|
||||||
|
var Log = getLogger()
|
||||||
|
|
||||||
|
func getLogger() func(format string, args ...interface{}) {
|
||||||
|
var logfile io.Writer = ioutil.Discard
|
||||||
|
if os.Getenv(envDebug) != "" {
|
||||||
|
logfile = os.Stderr
|
||||||
|
}
|
||||||
|
return log.New(logfile, "complete ", log.Flags()).Printf
|
||||||
|
}
|
19
vendor/github.com/posener/complete/match/file.go
generated
vendored
Normal file
19
vendor/github.com/posener/complete/match/file.go
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package match
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// File returns true if prefix can match the file
|
||||||
|
func File(file, prefix string) bool {
|
||||||
|
// special case for current directory completion
|
||||||
|
if file == "./" && (prefix == "." || prefix == "") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if prefix == "." && strings.HasPrefix(file, ".") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
file = strings.TrimPrefix(file, "./")
|
||||||
|
prefix = strings.TrimPrefix(prefix, "./")
|
||||||
|
|
||||||
|
return strings.HasPrefix(file, prefix)
|
||||||
|
}
|
6
vendor/github.com/posener/complete/match/match.go
generated
vendored
Normal file
6
vendor/github.com/posener/complete/match/match.go
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
package match
|
||||||
|
|
||||||
|
// Match matches two strings
|
||||||
|
// it is used for comparing a term to the last typed
|
||||||
|
// word, the prefix, and see if it is a possible auto complete option.
|
||||||
|
type Match func(term, prefix string) bool
|
9
vendor/github.com/posener/complete/match/prefix.go
generated
vendored
Normal file
9
vendor/github.com/posener/complete/match/prefix.go
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package match
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// Prefix is a simple Matcher, if the word is it's prefix, there is a match
|
||||||
|
// Match returns true if a has the prefix as prefix
|
||||||
|
func Prefix(long, prefix string) bool {
|
||||||
|
return strings.HasPrefix(long, prefix)
|
||||||
|
}
|
21
vendor/github.com/posener/complete/metalinter.json
generated
vendored
Normal file
21
vendor/github.com/posener/complete/metalinter.json
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"Vendor": true,
|
||||||
|
"DisableAll": true,
|
||||||
|
"Enable": [
|
||||||
|
"gofmt",
|
||||||
|
"goimports",
|
||||||
|
"interfacer",
|
||||||
|
"goconst",
|
||||||
|
"misspell",
|
||||||
|
"unconvert",
|
||||||
|
"gosimple",
|
||||||
|
"golint",
|
||||||
|
"structcheck",
|
||||||
|
"deadcode",
|
||||||
|
"vet"
|
||||||
|
],
|
||||||
|
"Exclude": [
|
||||||
|
"initTests is unused"
|
||||||
|
],
|
||||||
|
"Deadline": "2m"
|
||||||
|
}
|
41
vendor/github.com/posener/complete/predict.go
generated
vendored
Normal file
41
vendor/github.com/posener/complete/predict.go
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package complete
|
||||||
|
|
||||||
|
// Predictor implements a predict method, in which given
|
||||||
|
// command line arguments returns a list of options it predicts.
|
||||||
|
type Predictor interface {
|
||||||
|
Predict(Args) []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// PredictOr unions two predicate functions, so that the result predicate
|
||||||
|
// returns the union of their predication
|
||||||
|
func PredictOr(predictors ...Predictor) Predictor {
|
||||||
|
return PredictFunc(func(a Args) (prediction []string) {
|
||||||
|
for _, p := range predictors {
|
||||||
|
if p == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
prediction = append(prediction, p.Predict(a)...)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// PredictFunc determines what terms can follow a command or a flag
|
||||||
|
// It is used for auto completion, given last - the last word in the already
|
||||||
|
// in the command line, what words can complete it.
|
||||||
|
type PredictFunc func(Args) []string
|
||||||
|
|
||||||
|
// Predict invokes the predict function and implements the Predictor interface
|
||||||
|
func (p PredictFunc) Predict(a Args) []string {
|
||||||
|
if p == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return p(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PredictNothing does not expect anything after.
|
||||||
|
var PredictNothing Predictor
|
||||||
|
|
||||||
|
// PredictAnything expects something, but nothing particular, such as a number
|
||||||
|
// or arbitrary name.
|
||||||
|
var PredictAnything = PredictFunc(func(Args) []string { return nil })
|
108
vendor/github.com/posener/complete/predict_files.go
generated
vendored
Normal file
108
vendor/github.com/posener/complete/predict_files.go
generated
vendored
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
package complete
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/posener/complete/match"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PredictDirs will search for directories in the given started to be typed
|
||||||
|
// path, if no path was started to be typed, it will complete to directories
|
||||||
|
// in the current working directory.
|
||||||
|
func PredictDirs(pattern string) Predictor {
|
||||||
|
return files(pattern, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PredictFiles will search for files matching the given pattern in the started to
|
||||||
|
// be typed path, if no path was started to be typed, it will complete to files that
|
||||||
|
// match the pattern in the current working directory.
|
||||||
|
// To match any file, use "*" as pattern. To match go files use "*.go", and so on.
|
||||||
|
func PredictFiles(pattern string) Predictor {
|
||||||
|
return files(pattern, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func files(pattern string, allowFiles bool) PredictFunc {
|
||||||
|
|
||||||
|
// search for files according to arguments,
|
||||||
|
// if only one directory has matched the result, search recursively into
|
||||||
|
// this directory to give more results.
|
||||||
|
return func(a Args) (prediction []string) {
|
||||||
|
prediction = predictFiles(a, pattern, allowFiles)
|
||||||
|
|
||||||
|
// if the number of prediction is not 1, we either have many results or
|
||||||
|
// have no results, so we return it.
|
||||||
|
if len(prediction) != 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// only try deeper, if the one item is a directory
|
||||||
|
if stat, err := os.Stat(prediction[0]); err != nil || !stat.IsDir() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
a.Last = prediction[0]
|
||||||
|
return predictFiles(a, pattern, allowFiles)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func predictFiles(a Args, pattern string, allowFiles bool) []string {
|
||||||
|
if strings.HasSuffix(a.Last, "/..") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dir := a.Directory()
|
||||||
|
files := listFiles(dir, pattern, allowFiles)
|
||||||
|
|
||||||
|
// add dir if match
|
||||||
|
files = append(files, dir)
|
||||||
|
|
||||||
|
return PredictFilesSet(files).Predict(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PredictFilesSet predict according to file rules to a given set of file names
|
||||||
|
func PredictFilesSet(files []string) PredictFunc {
|
||||||
|
return func(a Args) (prediction []string) {
|
||||||
|
// add all matching files to prediction
|
||||||
|
for _, f := range files {
|
||||||
|
f = fixPathForm(a.Last, f)
|
||||||
|
|
||||||
|
// test matching of file to the argument
|
||||||
|
if match.File(f, a.Last) {
|
||||||
|
prediction = append(prediction, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func listFiles(dir, pattern string, allowFiles bool) []string {
|
||||||
|
// set of all file names
|
||||||
|
m := map[string]bool{}
|
||||||
|
|
||||||
|
// list files
|
||||||
|
if files, err := filepath.Glob(filepath.Join(dir, pattern)); err == nil {
|
||||||
|
for _, f := range files {
|
||||||
|
if stat, err := os.Stat(f); err != nil || stat.IsDir() || allowFiles {
|
||||||
|
m[f] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// list directories
|
||||||
|
if dirs, err := ioutil.ReadDir(dir); err == nil {
|
||||||
|
for _, d := range dirs {
|
||||||
|
if d.IsDir() {
|
||||||
|
m[filepath.Join(dir, d.Name())] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
list := make([]string, 0, len(m))
|
||||||
|
for k := range m {
|
||||||
|
list = append(list, k)
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
19
vendor/github.com/posener/complete/predict_set.go
generated
vendored
Normal file
19
vendor/github.com/posener/complete/predict_set.go
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package complete
|
||||||
|
|
||||||
|
import "github.com/posener/complete/match"
|
||||||
|
|
||||||
|
// PredictSet expects specific set of terms, given in the options argument.
|
||||||
|
func PredictSet(options ...string) Predictor {
|
||||||
|
return predictSet(options)
|
||||||
|
}
|
||||||
|
|
||||||
|
type predictSet []string
|
||||||
|
|
||||||
|
func (p predictSet) Predict(a Args) (prediction []string) {
|
||||||
|
for _, m := range p {
|
||||||
|
if match.Prefix(m, a.Last) {
|
||||||
|
prediction = append(prediction, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
116
vendor/github.com/posener/complete/readme.md
generated
vendored
Normal file
116
vendor/github.com/posener/complete/readme.md
generated
vendored
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
# complete
|
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.org/posener/complete.svg?branch=master)](https://travis-ci.org/posener/complete)
|
||||||
|
[![codecov](https://codecov.io/gh/posener/complete/branch/master/graph/badge.svg)](https://codecov.io/gh/posener/complete)
|
||||||
|
[![GoDoc](https://godoc.org/github.com/posener/complete?status.svg)](http://godoc.org/github.com/posener/complete)
|
||||||
|
[![Go Report Card](https://goreportcard.com/badge/github.com/posener/complete)](https://goreportcard.com/report/github.com/posener/complete)
|
||||||
|
|
||||||
|
A tool for bash writing bash completion in go.
|
||||||
|
|
||||||
|
Writing bash completion scripts is a hard work. This package provides an easy way
|
||||||
|
to create bash completion scripts for any command, and also an easy way to install/uninstall
|
||||||
|
the completion of the command.
|
||||||
|
|
||||||
|
## go command bash completion
|
||||||
|
|
||||||
|
In [gocomplete](./gocomplete) there is an example for bash completion for the `go` command line.
|
||||||
|
|
||||||
|
This is an example that uses the `complete` package on the `go` command - the `complete` package
|
||||||
|
can also be used to implement any completions, see [Usage](#usage).
|
||||||
|
|
||||||
|
### Install
|
||||||
|
|
||||||
|
1. Type in your shell:
|
||||||
|
```
|
||||||
|
go get -u github.com/posener/complete/gocomplete
|
||||||
|
gocomplete -install
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Restart your shell
|
||||||
|
|
||||||
|
Uninstall by `gocomplete -uninstall`
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Complete `go` command, including sub commands and all flags.
|
||||||
|
- Complete packages names or `.go` files when necessary.
|
||||||
|
- Complete test names after `-run` flag.
|
||||||
|
|
||||||
|
## complete package
|
||||||
|
|
||||||
|
Supported shells:
|
||||||
|
|
||||||
|
- [x] bash
|
||||||
|
- [x] zsh
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
Assuming you have program called `run` and you want to have bash completion
|
||||||
|
for it, meaning, if you type `run` then space, then press the `Tab` key,
|
||||||
|
the shell will suggest relevant complete options.
|
||||||
|
|
||||||
|
In that case, we will create a program called `runcomplete`, a go program,
|
||||||
|
with a `func main()` and so, that will make the completion of the `run`
|
||||||
|
program. Once the `runcomplete` will be in a binary form, we could
|
||||||
|
`runcomplete -install` and that will add to our shell all the bash completion
|
||||||
|
options for `run`.
|
||||||
|
|
||||||
|
So here it is:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/posener/complete"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
// create a Command object, that represents the command we want
|
||||||
|
// to complete.
|
||||||
|
run := complete.Command{
|
||||||
|
|
||||||
|
// Sub defines a list of sub commands of the program,
|
||||||
|
// this is recursive, since every command is of type command also.
|
||||||
|
Sub: complete.Commands{
|
||||||
|
|
||||||
|
// add a build sub command
|
||||||
|
"build": complete.Command {
|
||||||
|
|
||||||
|
// define flags of the build sub command
|
||||||
|
Flags: complete.Flags{
|
||||||
|
// build sub command has a flag '-cpus', which
|
||||||
|
// expects number of cpus after it. in that case
|
||||||
|
// anything could complete this flag.
|
||||||
|
"-cpus": complete.PredictAnything,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// define flags of the 'run' main command
|
||||||
|
Flags: complete.Flags{
|
||||||
|
// a flag -o, which expects a file ending with .out after
|
||||||
|
// it, the tab completion will auto complete for files matching
|
||||||
|
// the given pattern.
|
||||||
|
"-o": complete.PredictFiles("*.out"),
|
||||||
|
},
|
||||||
|
|
||||||
|
// define global flags of the 'run' main command
|
||||||
|
// those will show up also when a sub command was entered in the
|
||||||
|
// command line
|
||||||
|
GlobalFlags: complete.Flags{
|
||||||
|
|
||||||
|
// a flag '-h' which does not expects anything after it
|
||||||
|
"-h": complete.PredictNothing,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// run the command completion, as part of the main() function.
|
||||||
|
// this triggers the autocompletion when needed.
|
||||||
|
// name must be exactly as the binary that we want to complete.
|
||||||
|
complete.New("run", run).Run()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Self completing program
|
||||||
|
|
||||||
|
In case that the program that we want to complete is written in go we
|
||||||
|
can make it self completing.
|
||||||
|
|
||||||
|
Here is an [example](./example/self/main.go)
|
12
vendor/github.com/posener/complete/test.sh
generated
vendored
Executable file
12
vendor/github.com/posener/complete/test.sh
generated
vendored
Executable file
|
@ -0,0 +1,12 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
echo "" > coverage.txt
|
||||||
|
|
||||||
|
for d in $(go list ./... | grep -v vendor); do
|
||||||
|
go test -v -race -coverprofile=profile.out -covermode=atomic $d
|
||||||
|
if [ -f profile.out ]; then
|
||||||
|
cat profile.out >> coverage.txt
|
||||||
|
rm profile.out
|
||||||
|
fi
|
||||||
|
done
|
46
vendor/github.com/posener/complete/utils.go
generated
vendored
Normal file
46
vendor/github.com/posener/complete/utils.go
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
package complete
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// fixPathForm changes a file name to a relative name
|
||||||
|
func fixPathForm(last string, file string) string {
|
||||||
|
// get wording directory for relative name
|
||||||
|
workDir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
|
abs, err := filepath.Abs(file)
|
||||||
|
if err != nil {
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
|
// if last is absolute, return path as absolute
|
||||||
|
if filepath.IsAbs(last) {
|
||||||
|
return fixDirPath(abs)
|
||||||
|
}
|
||||||
|
|
||||||
|
rel, err := filepath.Rel(workDir, abs)
|
||||||
|
if err != nil {
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
|
// fix ./ prefix of path
|
||||||
|
if rel != "." && strings.HasPrefix(last, ".") {
|
||||||
|
rel = "./" + rel
|
||||||
|
}
|
||||||
|
|
||||||
|
return fixDirPath(rel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fixDirPath(path string) string {
|
||||||
|
info, err := os.Stat(path)
|
||||||
|
if err == nil && info.IsDir() && !strings.HasSuffix(path, "/") {
|
||||||
|
path += "/"
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
38
vendor/vendor.json
vendored
38
vendor/vendor.json
vendored
|
@ -378,14 +378,14 @@
|
||||||
{
|
{
|
||||||
"checksumSHA1": "tdhmIGUaoOMEDymMC23qTS7bt0g=",
|
"checksumSHA1": "tdhmIGUaoOMEDymMC23qTS7bt0g=",
|
||||||
"path": "github.com/docker/docker/pkg/ioutils",
|
"path": "github.com/docker/docker/pkg/ioutils",
|
||||||
"revision": "52debcd58ac91bf68503ce60561536911b74ff05",
|
"revision": "da39e9a4f920a15683dd0f23923c302d4db6eed5",
|
||||||
"revisionTime": "2016-05-20T15:17:10Z"
|
"revisionTime": "2016-05-28T08:11:04Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "tdhmIGUaoOMEDymMC23qTS7bt0g=",
|
"checksumSHA1": "tdhmIGUaoOMEDymMC23qTS7bt0g=",
|
||||||
"path": "github.com/docker/docker/pkg/ioutils",
|
"path": "github.com/docker/docker/pkg/ioutils",
|
||||||
"revision": "da39e9a4f920a15683dd0f23923c302d4db6eed5",
|
"revision": "52debcd58ac91bf68503ce60561536911b74ff05",
|
||||||
"revisionTime": "2016-05-28T08:11:04Z"
|
"revisionTime": "2016-05-20T15:17:10Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "BlFSSK7zUjPzPuxkLmM/0wpvku8=",
|
"checksumSHA1": "BlFSSK7zUjPzPuxkLmM/0wpvku8=",
|
||||||
|
@ -960,10 +960,10 @@
|
||||||
"revision": "7e024ce8ce18b21b475ac6baf8fa3c42536bf2fa"
|
"revision": "7e024ce8ce18b21b475ac6baf8fa3c42536bf2fa"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "bUuI7AVR3IZPLlBaEKmw/ke7wqA=",
|
"checksumSHA1": "F9rKfF4/KI5jhVBBIMuf6eDnTN0=",
|
||||||
"path": "github.com/mitchellh/cli",
|
"path": "github.com/mitchellh/cli",
|
||||||
"revision": "b481eac70eea3ad671b7c360a013f89bb759b252",
|
"revision": "4796e5fef694378c14b647f7221591afa58e38cd",
|
||||||
"revisionTime": "2017-05-23T17:27:49Z"
|
"revisionTime": "2017-07-17T21:49:25Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "ttEN1Aupb7xpPMkQLqb3tzLFdXs=",
|
"checksumSHA1": "ttEN1Aupb7xpPMkQLqb3tzLFdXs=",
|
||||||
|
@ -1070,6 +1070,30 @@
|
||||||
"revision": "792786c7400a136282c1664665ae0a8db921c6c2",
|
"revision": "792786c7400a136282c1664665ae0a8db921c6c2",
|
||||||
"revisionTime": "2016-01-10T10:55:54Z"
|
"revisionTime": "2016-01-10T10:55:54Z"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "6OEUkwOM0qgI6YxR+BDEn6YMvpU=",
|
||||||
|
"path": "github.com/posener/complete",
|
||||||
|
"revision": "e8d6fef54b6283f8bd3fbcb4a7de8884eb540293",
|
||||||
|
"revisionTime": "2017-05-27T17:24:47Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "NB7uVS0/BJDmNu68vPAlbrq4TME=",
|
||||||
|
"path": "github.com/posener/complete/cmd",
|
||||||
|
"revision": "e8d6fef54b6283f8bd3fbcb4a7de8884eb540293",
|
||||||
|
"revisionTime": "2017-05-27T17:24:47Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "kuS9vs+TMQzTGzXteL6EZ5HuKrU=",
|
||||||
|
"path": "github.com/posener/complete/cmd/install",
|
||||||
|
"revision": "e8d6fef54b6283f8bd3fbcb4a7de8884eb540293",
|
||||||
|
"revisionTime": "2017-05-27T17:24:47Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "DMo94FwJAm9ZCYCiYdJU2+bh4no=",
|
||||||
|
"path": "github.com/posener/complete/match",
|
||||||
|
"revision": "e8d6fef54b6283f8bd3fbcb4a7de8884eb540293",
|
||||||
|
"revisionTime": "2017-05-27T17:24:47Z"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "M57Rrfc8Z966p+IBtQ91QOcUtcg=",
|
"checksumSHA1": "M57Rrfc8Z966p+IBtQ91QOcUtcg=",
|
||||||
"comment": "v2.0.1-8-g983d3a5",
|
"comment": "v2.0.1-8-g983d3a5",
|
||||||
|
|
|
@ -25,6 +25,16 @@ with the `-h` argument.
|
||||||
Each command has been conveniently documented on this website. Links to each
|
Each command has been conveniently documented on this website. Links to each
|
||||||
command can be found on the left.
|
command can be found on the left.
|
||||||
|
|
||||||
|
### Autocomplete
|
||||||
|
|
||||||
|
Nomad's CLI supports command autocomplete. Autocomplete can be installed or
|
||||||
|
uninstalled by running the following on bash or zsh shells:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ nomad -autocomplete-install
|
||||||
|
$ nomad -autocomplete-uninstall
|
||||||
|
```
|
||||||
|
|
||||||
### Command Contexts
|
### Command Contexts
|
||||||
|
|
||||||
Nomad's CLI commands have implied contexts in their naming convention. Because
|
Nomad's CLI commands have implied contexts in their naming convention. Because
|
||||||
|
|
Loading…
Reference in a new issue