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.
This commit is contained in:
Eyal Posener 2017-12-05 20:24:04 +02:00 committed by Martin Atkins
parent 53e421caad
commit e1dadaae44
7 changed files with 83 additions and 81 deletions

View File

@ -2,7 +2,6 @@ package command
import ( import (
"github.com/posener/complete" "github.com/posener/complete"
"github.com/posener/complete/match"
) )
// This file contains some re-usable predictors for auto-complete. The // This file contains some re-usable predictors for auto-complete. The
@ -63,17 +62,6 @@ func (m *Meta) completePredictWorkspaceName() complete.Predictor {
} }
names, _ := b.States() 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 return names
}) })
} }

View File

@ -28,7 +28,6 @@ func TestMetaCompletePredictWorkspaceName(t *testing.T) {
predictor := meta.completePredictWorkspaceName() predictor := meta.completePredictWorkspaceName()
t.Run("no prefix", func(t *testing.T) {
got := predictor.Predict(complete.Args{ got := predictor.Predict(complete.Args{
Last: "", Last: "",
}) })
@ -36,25 +35,4 @@ func TestMetaCompletePredictWorkspaceName(t *testing.T) {
if !reflect.DeepEqual(got, want) { if !reflect.DeepEqual(got, want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", 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)
}
})
} }

View File

@ -3,6 +3,8 @@ package complete
import ( import (
"os" "os"
"path/filepath" "path/filepath"
"strings"
"unicode"
) )
// Args describes command line arguments // Args describes command line arguments
@ -37,16 +39,41 @@ func (a Args) Directory() string {
return fixPathForm(a.Last, dir) return fixPathForm(a.Last, dir)
} }
func newArgs(line []string) Args { func newArgs(line string) Args {
completed := removeLast(line[1:]) var (
all []string
completed []string
)
parts := splitFields(line)
if len(parts) > 0 {
all = parts[1:]
completed = removeLast(parts[1:])
}
return Args{ return Args{
All: line[1:], All: all,
Completed: completed, Completed: completed,
Last: last(line), Last: last(parts),
LastCompleted: last(completed), 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 { func (a Args) from(i int) Args {
if i > len(a.All) { if i > len(a.All) {
i = len(a.All) i = len(a.All)
@ -67,9 +94,9 @@ func removeLast(a []string) []string {
return a return a
} }
func last(args []string) (last string) { func last(args []string) string {
if len(args) > 0 { if len(args) == 0 {
last = args[len(args)-1] return ""
} }
return return args[len(args)-1]
} }

View File

@ -1,7 +1,5 @@
package complete package complete
import "github.com/posener/complete/match"
// Command represents a command line // Command represents a command line
// It holds the data that enables auto completion of command line // It holds the data that enables auto completion of command line
// Command can also be a sub command. // 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 // Predict returns all possible predictions for args according to the command struct
func (c *Command) Predict(a Args) (predictions []string) { func (c *Command) Predict(a Args) []string {
predictions, _ = c.predict(a) options, _ := c.predict(a)
return return options
} }
// Commands is the type of Sub member, it maps a command name to a command struct // Commands is the type of Sub member, it maps a command name to a command struct
@ -36,10 +34,8 @@ type Commands map[string]Command
// Predict completion of sub command names names according to command line arguments // Predict completion of sub command names names according to command line arguments
func (c Commands) Predict(a Args) (prediction []string) { func (c Commands) Predict(a Args) (prediction []string) {
for sub := range c { for sub := range c {
if match.Prefix(sub, a.Last) {
prediction = append(prediction, sub) prediction = append(prediction, sub)
} }
}
return return
} }
@ -49,9 +45,14 @@ type Flags map[string]Predictor
// Predict completion of flags names according to command line arguments // Predict completion of flags names according to command line arguments
func (f Flags) Predict(a Args) (prediction []string) { func (f Flags) Predict(a Args) (prediction []string) {
for flag := range f { for flag := range f {
if match.Prefix(flag, a.Last) { // If the flag starts with a hyphen, we avoid emitting the prediction
prediction = append(prediction, flag) // 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 return
} }
@ -73,6 +74,10 @@ func (c *Command) predict(a Args) (options []string, only bool) {
if only { if only {
return return
} }
// We matched so stop searching. Continuing to search can accidentally
// match a subcommand with current set of commands, see issue #46.
break
} }
} }

View File

@ -8,10 +8,11 @@ package complete
import ( import (
"flag" "flag"
"fmt" "fmt"
"io"
"os" "os"
"strings"
"github.com/posener/complete/cmd" "github.com/posener/complete/cmd"
"github.com/posener/complete/match"
) )
const ( const (
@ -23,6 +24,7 @@ const (
type Complete struct { type Complete struct {
Command Command Command Command
cmd.CLI cmd.CLI
Out io.Writer
} }
// New creates a new complete command. // New creates a new complete command.
@ -34,6 +36,7 @@ func New(name string, command Command) *Complete {
return &Complete{ return &Complete{
Command: command, Command: command,
CLI: cmd.CLI{Name: name}, CLI: cmd.CLI{Name: name},
Out: os.Stdout,
} }
} }
@ -59,28 +62,34 @@ func (c *Complete) Complete() bool {
return c.CLI.Run() return c.CLI.Run()
} }
Log("Completing line: %s", line) Log("Completing line: %s", line)
a := newArgs(line) a := newArgs(line)
Log("Completing last field: %s", a.Last)
options := c.Command.Predict(a) options := c.Command.Predict(a)
Log("Options: %s", options)
Log("Completion: %s", options) // filter only options that match the last argument
output(options) 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 return true
} }
func getLine() ([]string, bool) { func getLine() (string, bool) {
line := os.Getenv(envComplete) line := os.Getenv(envComplete)
if line == "" { if line == "" {
return nil, false return "", false
} }
return strings.Split(line, " "), true return line, true
} }
func output(options []string) { func (c *Complete) output(options []string) {
Log("")
// stdout of program defines the complete options // stdout of program defines the complete options
for _, option := range options { for _, option := range options {
fmt.Println(option) fmt.Fprintln(c.Out, option)
} }
} }

View File

@ -1,7 +1,5 @@
package complete package complete
import "github.com/posener/complete/match"
// PredictSet expects specific set of terms, given in the options argument. // PredictSet expects specific set of terms, given in the options argument.
func PredictSet(options ...string) Predictor { func PredictSet(options ...string) Predictor {
return predictSet(options) return predictSet(options)
@ -9,11 +7,6 @@ func PredictSet(options ...string) Predictor {
type predictSet []string type predictSet []string
func (p predictSet) Predict(a Args) (prediction []string) { func (p predictSet) Predict(a Args) []string {
for _, m := range p { return p
if match.Prefix(m, a.Last) {
prediction = append(prediction, m)
}
}
return
} }

8
vendor/vendor.json vendored
View File

@ -2007,10 +2007,12 @@
"revisionTime": "2017-05-05T04:36:39Z" "revisionTime": "2017-05-05T04:36:39Z"
}, },
{ {
"checksumSHA1": "6OEUkwOM0qgI6YxR+BDEn6YMvpU=", "checksumSHA1": "Nt4Ol6ZM2n0XD5zatxjwEYBpQnw=",
"path": "github.com/posener/complete", "path": "github.com/posener/complete",
"revision": "f4461a52b6329c11190f11fe3384ec8aa964e21c", "revision": "dc2bc5a81accba8782bebea28628224643a8286a",
"revisionTime": "2017-07-30T19:30:24Z" "revisionTime": "2017-11-04T09:57:02Z",
"version": "=v1.1",
"versionExact": "v1.1"
}, },
{ {
"checksumSHA1": "NB7uVS0/BJDmNu68vPAlbrq4TME=", "checksumSHA1": "NB7uVS0/BJDmNu68vPAlbrq4TME=",