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:
parent
53e421caad
commit
e1dadaae44
|
@ -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
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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]
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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=",
|
||||||
|
|
Loading…
Reference in New Issue