Merge pull request #53 from posener/finally-filter-matches
Filter matches as a final stage
This commit is contained in:
commit
00c86494ff
|
@ -1,8 +1,8 @@
|
||||||
language: go
|
language: go
|
||||||
sudo: false
|
sudo: false
|
||||||
go:
|
go:
|
||||||
|
- 1.9
|
||||||
- 1.8
|
- 1.8
|
||||||
- tip
|
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- go get -u -t ./...
|
- go get -u -t ./...
|
||||||
|
|
13
command.go
13
command.go
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,11 +52,8 @@ func (f Flags) Predict(a Args) (prediction []string) {
|
||||||
if flagHyphenStart && !lastHyphenStart {
|
if flagHyphenStart && !lastHyphenStart {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if match.Prefix(flag, a.Last) {
|
|
||||||
prediction = append(prediction, flag)
|
prediction = append(prediction, flag)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
23
complete.go
23
complete.go
|
@ -8,10 +8,12 @@ package complete
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/posener/complete/cmd"
|
"github.com/posener/complete/cmd"
|
||||||
|
"github.com/posener/complete/match"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -23,6 +25,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 +37,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,13 +63,19 @@ 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)
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,10 +87,9 @@ func getLine() ([]string, bool) {
|
||||||
return strings.Split(line, " "), true
|
return strings.Split(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,8 +1,10 @@
|
||||||
package complete
|
package complete
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -34,6 +36,7 @@ func TestCompleter_Complete(t *testing.T) {
|
||||||
"-global1": PredictAnything,
|
"-global1": PredictAnything,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
cmp := New("cmd", c)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
args string
|
args string
|
||||||
|
@ -175,18 +178,13 @@ func TestCompleter_Complete(t *testing.T) {
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.args, func(t *testing.T) {
|
t.Run(tt.args, func(t *testing.T) {
|
||||||
|
got := runComplete(cmp, tt.args)
|
||||||
tt.args = "cmd " + tt.args
|
|
||||||
os.Setenv(envComplete, tt.args)
|
|
||||||
line, _ := getLine()
|
|
||||||
|
|
||||||
got := c.Predict(newArgs(line))
|
|
||||||
|
|
||||||
sort.Strings(tt.want)
|
sort.Strings(tt.want)
|
||||||
sort.Strings(got)
|
sort.Strings(got)
|
||||||
|
|
||||||
if !equalSlices(got, tt.want) {
|
if !equalSlices(got, tt.want) {
|
||||||
t.Errorf("failed '%s'\ngot = %s\nwant: %s", t.Name(), got, tt.want)
|
t.Errorf("failed '%s'\ngot: %s\nwant: %s", t.Name(), got, tt.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -222,6 +220,8 @@ func TestCompleter_Complete_SharedPrefix(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmp := New("cmd", c)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
args string
|
args string
|
||||||
want []string
|
want []string
|
||||||
|
@ -258,12 +258,7 @@ func TestCompleter_Complete_SharedPrefix(t *testing.T) {
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.args, func(t *testing.T) {
|
t.Run(tt.args, func(t *testing.T) {
|
||||||
|
got := runComplete(cmp, tt.args)
|
||||||
tt.args = "cmd " + tt.args
|
|
||||||
os.Setenv(envComplete, tt.args)
|
|
||||||
line, _ := getLine()
|
|
||||||
|
|
||||||
got := c.Predict(newArgs(line))
|
|
||||||
|
|
||||||
sort.Strings(tt.want)
|
sort.Strings(tt.want)
|
||||||
sort.Strings(got)
|
sort.Strings(got)
|
||||||
|
@ -275,6 +270,29 @@ func TestCompleter_Complete_SharedPrefix(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// runComplete runs the complete login for test purposes
|
||||||
|
// it gets the complete struct and command line arguments and returns
|
||||||
|
// the complete options
|
||||||
|
func runComplete(c *Complete, args string) (completions []string) {
|
||||||
|
os.Setenv(envComplete, "cmd "+args)
|
||||||
|
b := bytes.NewBuffer(nil)
|
||||||
|
c.Out = b
|
||||||
|
c.Complete()
|
||||||
|
completions = parseOutput(b.String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseOutput(output string) []string {
|
||||||
|
lines := strings.Split(output, "\n")
|
||||||
|
options := []string{}
|
||||||
|
for _, l := range lines {
|
||||||
|
if l != "" {
|
||||||
|
options = append(options, l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
func equalSlices(a, b []string) bool {
|
func equalSlices(a, b []string) bool {
|
||||||
if len(a) != len(b) {
|
if len(a) != len(b) {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/posener/complete"
|
"github.com/posener/complete"
|
||||||
"github.com/posener/complete/match"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -21,14 +20,8 @@ var (
|
||||||
// for test names use prefix of 'Test' or 'Example', and for benchmark
|
// for test names use prefix of 'Test' or 'Example', and for benchmark
|
||||||
// test names use 'Benchmark'
|
// test names use 'Benchmark'
|
||||||
func funcPredict(funcRegexp *regexp.Regexp) complete.Predictor {
|
func funcPredict(funcRegexp *regexp.Regexp) complete.Predictor {
|
||||||
return complete.PredictFunc(func(a complete.Args) (prediction []string) {
|
return complete.PredictFunc(func(a complete.Args) []string {
|
||||||
tests := funcNames(funcRegexp)
|
return funcNames(funcRegexp)
|
||||||
for _, t := range tests {
|
|
||||||
if match.Prefix(t, a.Last) {
|
|
||||||
prediction = append(prediction, t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,38 +22,11 @@ func TestPredictions(t *testing.T) {
|
||||||
predictor: predictTest,
|
predictor: predictTest,
|
||||||
want: []string{"TestPredictions", "Example"},
|
want: []string{"TestPredictions", "Example"},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "predict tests not found",
|
|
||||||
predictor: predictTest,
|
|
||||||
last: "X",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "predict benchmark ok",
|
name: "predict benchmark ok",
|
||||||
predictor: predictBenchmark,
|
predictor: predictBenchmark,
|
||||||
want: []string{"BenchmarkFake"},
|
want: []string{"BenchmarkFake"},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "predict benchmarks not found",
|
|
||||||
predictor: predictBenchmark,
|
|
||||||
last: "X",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "predict local ok",
|
|
||||||
predictor: complete.PredictFunc(predictPackages),
|
|
||||||
last: ".",
|
|
||||||
want: []string{"./"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "predict system ok",
|
|
||||||
predictor: complete.PredictFunc(predictPackages),
|
|
||||||
last: "github.com/posener/complete/goc",
|
|
||||||
want: []string{"github.com/posener/complete/gocomplete/"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "predict packages not found",
|
|
||||||
predictor: complete.PredictFunc(predictPackages),
|
|
||||||
last: "X",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,12 +21,6 @@ func TestPredicate(t *testing.T) {
|
||||||
p: PredictSet("a", "b", "c"),
|
p: PredictSet("a", "b", "c"),
|
||||||
want: []string{"a", "b", "c"},
|
want: []string{"a", "b", "c"},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "set with does",
|
|
||||||
p: PredictSet("./..", "./x"),
|
|
||||||
argList: []string{"./.", "./.."},
|
|
||||||
want: []string{"./.."},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "set/empty",
|
name: "set/empty",
|
||||||
p: PredictSet(),
|
p: PredictSet(),
|
||||||
|
|
Loading…
Reference in New Issue