From e1dadaae4467d7942c16a0a6d2654b971cc3d8fe Mon Sep 17 00:00:00 2001 From: Eyal Posener Date: Tue, 5 Dec 2017 20:24:04 +0200 Subject: [PATCH] command: use newer version of "complete" library This takes care of filtering results for us, so we don't need to do it on our end anymore. --- command/autocomplete.go | 12 ------ command/autocomplete_test.go | 34 +++------------ vendor/github.com/posener/complete/args.go | 43 +++++++++++++++---- vendor/github.com/posener/complete/command.go | 25 ++++++----- .../github.com/posener/complete/complete.go | 31 ++++++++----- .../posener/complete/predict_set.go | 11 +---- vendor/vendor.json | 8 ++-- 7 files changed, 83 insertions(+), 81 deletions(-) diff --git a/command/autocomplete.go b/command/autocomplete.go index cc1ad145b..1ce90e4c9 100644 --- a/command/autocomplete.go +++ b/command/autocomplete.go @@ -2,7 +2,6 @@ package command import ( "github.com/posener/complete" - "github.com/posener/complete/match" ) // This file contains some re-usable predictors for auto-complete. The @@ -63,17 +62,6 @@ func (m *Meta) completePredictWorkspaceName() complete.Predictor { } names, _ := b.States() - - if a.Last != "" { - // filter for names that match the prefix only - filtered := make([]string, 0, len(names)) - for _, name := range names { - if match.Prefix(name, a.Last) { - filtered = append(filtered, name) - } - } - names = filtered - } return names }) } diff --git a/command/autocomplete_test.go b/command/autocomplete_test.go index 7a799ac99..865afcbea 100644 --- a/command/autocomplete_test.go +++ b/command/autocomplete_test.go @@ -28,33 +28,11 @@ func TestMetaCompletePredictWorkspaceName(t *testing.T) { predictor := meta.completePredictWorkspaceName() - t.Run("no prefix", func(t *testing.T) { - got := predictor.Predict(complete.Args{ - Last: "", - }) - want := []string{"default"} - if !reflect.DeepEqual(got, want) { - t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, want) - } - }) - - t.Run("prefix that matches", func(t *testing.T) { - got := predictor.Predict(complete.Args{ - Last: "def", - }) - want := []string{"default"} - if !reflect.DeepEqual(got, want) { - t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, want) - } - }) - - t.Run("prefix that doesn't match", func(t *testing.T) { - got := predictor.Predict(complete.Args{ - Last: "x", - }) - want := []string{} - if !reflect.DeepEqual(got, want) { - t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, want) - } + got := predictor.Predict(complete.Args{ + Last: "", }) + want := []string{"default"} + if !reflect.DeepEqual(got, want) { + t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, want) + } } diff --git a/vendor/github.com/posener/complete/args.go b/vendor/github.com/posener/complete/args.go index 73c356d76..1ba4d6919 100644 --- a/vendor/github.com/posener/complete/args.go +++ b/vendor/github.com/posener/complete/args.go @@ -3,6 +3,8 @@ package complete import ( "os" "path/filepath" + "strings" + "unicode" ) // Args describes command line arguments @@ -37,16 +39,41 @@ func (a Args) Directory() string { return fixPathForm(a.Last, dir) } -func newArgs(line []string) Args { - completed := removeLast(line[1:]) +func newArgs(line string) Args { + var ( + all []string + completed []string + ) + parts := splitFields(line) + if len(parts) > 0 { + all = parts[1:] + completed = removeLast(parts[1:]) + } return Args{ - All: line[1:], + All: all, Completed: completed, - Last: last(line), + Last: last(parts), LastCompleted: last(completed), } } +func splitFields(line string) []string { + parts := strings.Fields(line) + if len(line) > 0 && unicode.IsSpace(rune(line[len(line)-1])) { + parts = append(parts, "") + } + parts = splitLastEqual(parts) + return parts +} + +func splitLastEqual(line []string) []string { + if len(line) == 0 { + return line + } + parts := strings.Split(line[len(line)-1], "=") + return append(line[:len(line)-1], parts...) +} + func (a Args) from(i int) Args { if i > len(a.All) { i = len(a.All) @@ -67,9 +94,9 @@ func removeLast(a []string) []string { return a } -func last(args []string) (last string) { - if len(args) > 0 { - last = args[len(args)-1] +func last(args []string) string { + if len(args) == 0 { + return "" } - return + return args[len(args)-1] } diff --git a/vendor/github.com/posener/complete/command.go b/vendor/github.com/posener/complete/command.go index eeeb9e027..82d37d529 100644 --- a/vendor/github.com/posener/complete/command.go +++ b/vendor/github.com/posener/complete/command.go @@ -1,7 +1,5 @@ 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. @@ -25,9 +23,9 @@ type Command struct { } // 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 +func (c *Command) Predict(a Args) []string { + options, _ := c.predict(a) + return options } // Commands is the type of Sub member, it maps a command name to a command struct @@ -36,9 +34,7 @@ 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) - } + prediction = append(prediction, sub) } return } @@ -49,9 +45,14 @@ 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) + // If the flag starts with a hyphen, we avoid emitting the prediction + // unless the last typed arg contains a hyphen as well. + flagHyphenStart := len(flag) != 0 && flag[0] == '-' + lastHyphenStart := len(a.Last) != 0 && a.Last[0] == '-' + if flagHyphenStart && !lastHyphenStart { + continue } + prediction = append(prediction, flag) } return } @@ -73,6 +74,10 @@ func (c *Command) predict(a Args) (options []string, only bool) { if only { return } + + // We matched so stop searching. Continuing to search can accidentally + // match a subcommand with current set of commands, see issue #46. + break } } diff --git a/vendor/github.com/posener/complete/complete.go b/vendor/github.com/posener/complete/complete.go index 1df66170b..185d1e8bd 100644 --- a/vendor/github.com/posener/complete/complete.go +++ b/vendor/github.com/posener/complete/complete.go @@ -8,10 +8,11 @@ package complete import ( "flag" "fmt" + "io" "os" - "strings" "github.com/posener/complete/cmd" + "github.com/posener/complete/match" ) const ( @@ -23,6 +24,7 @@ const ( type Complete struct { Command Command cmd.CLI + Out io.Writer } // New creates a new complete command. @@ -34,6 +36,7 @@ func New(name string, command Command) *Complete { return &Complete{ Command: command, CLI: cmd.CLI{Name: name}, + Out: os.Stdout, } } @@ -59,28 +62,34 @@ func (c *Complete) Complete() bool { return c.CLI.Run() } Log("Completing line: %s", line) - a := newArgs(line) - + Log("Completing last field: %s", a.Last) options := c.Command.Predict(a) + Log("Options: %s", options) - Log("Completion: %s", options) - output(options) + // filter only options that match the last argument + matches := []string{} + for _, option := range options { + if match.Prefix(option, a.Last) { + matches = append(matches, option) + } + } + Log("Matches: %s", matches) + c.output(matches) return true } -func getLine() ([]string, bool) { +func getLine() (string, bool) { line := os.Getenv(envComplete) if line == "" { - return nil, false + return "", false } - return strings.Split(line, " "), true + return line, true } -func output(options []string) { - Log("") +func (c *Complete) output(options []string) { // stdout of program defines the complete options for _, option := range options { - fmt.Println(option) + fmt.Fprintln(c.Out, option) } } diff --git a/vendor/github.com/posener/complete/predict_set.go b/vendor/github.com/posener/complete/predict_set.go index 8fc59d714..fa4a34ae4 100644 --- a/vendor/github.com/posener/complete/predict_set.go +++ b/vendor/github.com/posener/complete/predict_set.go @@ -1,7 +1,5 @@ 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) @@ -9,11 +7,6 @@ func PredictSet(options ...string) Predictor { 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 +func (p predictSet) Predict(a Args) []string { + return p } diff --git a/vendor/vendor.json b/vendor/vendor.json index 94ee9291f..9d1a12adf 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -2007,10 +2007,12 @@ "revisionTime": "2017-05-05T04:36:39Z" }, { - "checksumSHA1": "6OEUkwOM0qgI6YxR+BDEn6YMvpU=", + "checksumSHA1": "Nt4Ol6ZM2n0XD5zatxjwEYBpQnw=", "path": "github.com/posener/complete", - "revision": "f4461a52b6329c11190f11fe3384ec8aa964e21c", - "revisionTime": "2017-07-30T19:30:24Z" + "revision": "dc2bc5a81accba8782bebea28628224643a8286a", + "revisionTime": "2017-11-04T09:57:02Z", + "version": "=v1.1", + "versionExact": "v1.1" }, { "checksumSHA1": "NB7uVS0/BJDmNu68vPAlbrq4TME=",