diff --git a/.travis.yml b/.travis.yml index 459df21..c2798f8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,8 @@ language: go sudo: false go: + - 1.9 - 1.8 - - tip before_install: - go get -u -t ./... diff --git a/command.go b/command.go index 6de48e9..82d37d5 100644 --- a/command.go +++ b/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 } @@ -56,10 +52,7 @@ func (f Flags) Predict(a Args) (prediction []string) { if flagHyphenStart && !lastHyphenStart { continue } - - if match.Prefix(flag, a.Last) { - prediction = append(prediction, flag) - } + prediction = append(prediction, flag) } return } diff --git a/complete.go b/complete.go index d7b6bf4..185d1e8 100644 --- a/complete.go +++ b/complete.go @@ -8,9 +8,11 @@ package complete import ( "flag" "fmt" + "io" "os" "github.com/posener/complete/cmd" + "github.com/posener/complete/match" ) const ( @@ -22,6 +24,7 @@ const ( type Complete struct { Command Command cmd.CLI + Out io.Writer } // New creates a new complete command. @@ -33,6 +36,7 @@ func New(name string, command Command) *Complete { return &Complete{ Command: command, CLI: cmd.CLI{Name: name}, + Out: os.Stdout, } } @@ -58,13 +62,20 @@ 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 } @@ -76,10 +87,9 @@ func getLine() (string, bool) { 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/complete_test.go b/complete_test.go index cd7880c..1611ad4 100644 --- a/complete_test.go +++ b/complete_test.go @@ -1,8 +1,10 @@ package complete import ( + "bytes" "os" "sort" + "strings" "testing" ) @@ -34,6 +36,7 @@ func TestCompleter_Complete(t *testing.T) { "-global1": PredictAnything, }, } + cmp := New("cmd", c) tests := []struct { args string @@ -195,18 +198,13 @@ func TestCompleter_Complete(t *testing.T) { for _, tt := range tests { t.Run(tt.args, func(t *testing.T) { - - tt.args = "cmd " + tt.args - os.Setenv(envComplete, tt.args) - line, _ := getLine() - - got := c.Predict(newArgs(line)) + got := runComplete(cmp, tt.args) sort.Strings(tt.want) sort.Strings(got) 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) } }) } @@ -242,6 +240,8 @@ func TestCompleter_Complete_SharedPrefix(t *testing.T) { }, } + cmp := New("cmd", c) + tests := []struct { args string want []string @@ -278,12 +278,7 @@ func TestCompleter_Complete_SharedPrefix(t *testing.T) { for _, tt := range tests { t.Run(tt.args, func(t *testing.T) { - - tt.args = "cmd " + tt.args - os.Setenv(envComplete, tt.args) - line, _ := getLine() - - got := c.Predict(newArgs(line)) + got := runComplete(cmp, tt.args) sort.Strings(tt.want) sort.Strings(got) @@ -295,6 +290,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 { if len(a) != len(b) { return false diff --git a/gocomplete/tests.go b/gocomplete/tests.go index a952dab..e755ae5 100644 --- a/gocomplete/tests.go +++ b/gocomplete/tests.go @@ -7,7 +7,6 @@ import ( "strings" "github.com/posener/complete" - "github.com/posener/complete/match" ) var ( @@ -21,14 +20,8 @@ var ( // for test names use prefix of 'Test' or 'Example', and for benchmark // test names use 'Benchmark' func funcPredict(funcRegexp *regexp.Regexp) complete.Predictor { - return complete.PredictFunc(func(a complete.Args) (prediction []string) { - tests := funcNames(funcRegexp) - for _, t := range tests { - if match.Prefix(t, a.Last) { - prediction = append(prediction, t) - } - } - return + return complete.PredictFunc(func(a complete.Args) []string { + return funcNames(funcRegexp) }) } diff --git a/gocomplete/tests_test.go b/gocomplete/tests_test.go index 6799157..150e2e2 100644 --- a/gocomplete/tests_test.go +++ b/gocomplete/tests_test.go @@ -22,38 +22,11 @@ func TestPredictions(t *testing.T) { predictor: predictTest, want: []string{"TestPredictions", "Example"}, }, - { - name: "predict tests not found", - predictor: predictTest, - last: "X", - }, { name: "predict benchmark ok", predictor: predictBenchmark, 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 { diff --git a/predict_set.go b/predict_set.go index 8fc59d7..fa4a34a 100644 --- a/predict_set.go +++ b/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/predict_test.go b/predict_test.go index 8baecf7..24df78d 100644 --- a/predict_test.go +++ b/predict_test.go @@ -21,12 +21,6 @@ func TestPredicate(t *testing.T) { p: PredictSet("a", "b", "c"), want: []string{"a", "b", "c"}, }, - { - name: "set with does", - p: PredictSet("./..", "./x"), - argList: []string{"./.", "./.."}, - want: []string{"./.."}, - }, { name: "set/empty", p: PredictSet(),