From d33bac720bcaf13a5ee9f6f165293183d2e3e24d Mon Sep 17 00:00:00 2001 From: Eyal Posener Date: Sat, 6 May 2017 08:24:17 +0300 Subject: Remove Complete struct --- complete.go | 66 ---------------- complete_test.go | 206 ------------------------------------------------- gocomplete/complete.go | 2 +- run.go | 61 +++++++++++++++ run_test.go | 204 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 266 insertions(+), 273 deletions(-) delete mode 100644 complete.go delete mode 100644 complete_test.go create mode 100644 run.go create mode 100644 run_test.go diff --git a/complete.go b/complete.go deleted file mode 100644 index 302486c..0000000 --- a/complete.go +++ /dev/null @@ -1,66 +0,0 @@ -package complete - -import ( - "fmt" - "os" - "strings" -) - -const ( - envComplete = "COMP_LINE" - envDebug = "COMP_DEBUG" -) - -type Completer struct { - Command -} - -func New(c Command) *Completer { - return &Completer{Command: c} -} - -func (c *Completer) Complete() { - args := getLine() - Log("Completing args: %s", args) - - options := c.complete(args) - - Log("Completion: %s", options) - output(options) -} - -func (c *Completer) complete(args []string) []string { - all, _ := c.options(args[:len(args)-1]) - return c.chooseRelevant(last(args), all) -} - -func (c *Completer) chooseRelevant(last string, options []Option) (relevant []string) { - for _, option := range options { - if option.Matches(last) { - relevant = append(relevant, option.String()) - } - } - return -} - -func getLine() []string { - line := os.Getenv(envComplete) - if line == "" { - panic("should be run as a complete script") - } - return strings.Split(line, " ") -} - -func last(args []string) (last string) { - if len(args) > 0 { - last = args[len(args)-1] - } - return -} - -func output(options []string) { - // stdout of program defines the complete options - for _, option := range options { - fmt.Println(option) - } -} diff --git a/complete_test.go b/complete_test.go deleted file mode 100644 index 6bee548..0000000 --- a/complete_test.go +++ /dev/null @@ -1,206 +0,0 @@ -package complete - -import ( - "os" - "sort" - "testing" -) - -func TestCompleter_Complete(t *testing.T) { - t.Parallel() - - if testing.Verbose() { - os.Setenv(envDebug, "1") - } - - c := Completer{ - Command: Command{ - Sub: map[string]Command{ - "sub1": { - Flags: map[string]Predicate{ - "-flag1": PredictAnything, - "-flag2": PredictNothing, - }, - }, - "sub2": { - Flags: map[string]Predicate{ - "-flag2": PredictNothing, - "-flag3": PredictSet("opt1", "opt2", "opt12"), - }, - Args: PredictDirs("./tests/").Or(PredictFiles("./tests/*.md")), - }, - }, - Flags: map[string]Predicate{ - "-h": PredictNothing, - "-global1": PredictAnything, - "-o": PredictFiles("./tests/*.txt"), - }, - }, - } - - allGlobals := []string{} - for sub := range c.Sub { - allGlobals = append(allGlobals, sub) - } - for flag := range c.Flags { - allGlobals = append(allGlobals, flag) - } - - testTXTFiles := []string{"./tests/a.txt", "./tests/b.txt", "./tests/c.txt"} - - tests := []struct { - args string - want []string - }{ - { - args: "", - want: allGlobals, - }, - { - args: "-", - want: []string{"-h", "-global1", "-o"}, - }, - { - args: "-h ", - want: allGlobals, - }, - { - args: "-global1 ", // global1 is known follow flag - want: []string{}, - }, - { - args: "sub", - want: []string{"sub1", "sub2"}, - }, - { - args: "sub1", - want: []string{"sub1"}, - }, - { - args: "sub2", - want: []string{"sub2"}, - }, - { - args: "sub1 ", - want: []string{"-flag1", "-flag2", "-h", "-global1", "-o"}, - }, - { - args: "sub2 ", - want: []string{"./tests", "-flag2", "-flag3", "-h", "-global1", "-o"}, - }, - { - args: "sub2 tests", - want: []string{"./tests", "./tests/readme.md", "./tests/dir"}, - }, - { - args: "sub2 tests/re", - want: []string{"./tests/readme.md"}, - }, - { - args: "sub2 -flag2 ", - want: []string{"./tests", "-flag2", "-flag3", "-h", "-global1", "-o"}, - }, - { - args: "sub1 -fl", - want: []string{"-flag1", "-flag2"}, - }, - { - args: "sub1 -flag1", - want: []string{"-flag1"}, - }, - { - args: "sub1 -flag1 ", - want: []string{}, // flag1 is unknown follow flag - }, - { - args: "sub1 -flag2 ", - want: []string{"-flag1", "-flag2", "-h", "-global1", "-o"}, - }, - { - args: "-no-such-flag", - want: []string{}, - }, - { - args: "-no-such-flag ", - want: allGlobals, - }, - { - args: "no-such-command", - want: []string{}, - }, - { - args: "no-such-command ", - want: allGlobals, - }, - { - args: "-o ", - want: []string{}, - }, - { - args: "-o ./tes", - want: []string{}, - }, - { - args: "-o tests/", - want: testTXTFiles, - }, - { - args: "-o tests", - want: testTXTFiles, - }, - { - args: "-o ./compl", - want: []string{}, - }, - { - args: "-o ./complete.go", - want: []string{}, - }, - { - args: "-o ./complete.go ", - want: allGlobals, - }, - { - args: "-o sub2 -flag3 ", - want: []string{"opt1", "opt2", "opt12"}, - }, - { - args: "-o sub2 -flag3 opt1", - want: []string{"opt1", "opt12"}, - }, - { - args: "-o sub2 -flag3 opt", - want: []string{"opt1", "opt2", "opt12"}, - }, - } - - for _, tt := range tests { - t.Run(tt.args, func(t *testing.T) { - - tt.args = "cmd " + tt.args - os.Setenv(envComplete, tt.args) - args := getLine() - - got := c.complete(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) - } - }) - } -} - -func equalSlices(a, b []string) bool { - if len(a) != len(b) { - return false - } - for i := range a { - if a[i] != b[i] { - return false - } - } - return true -} diff --git a/gocomplete/complete.go b/gocomplete/complete.go index 11a948f..ca87f14 100644 --- a/gocomplete/complete.go +++ b/gocomplete/complete.go @@ -186,5 +186,5 @@ func main() { }, } - complete.New(gogo).Complete() + complete.Run(gogo) } diff --git a/run.go b/run.go new file mode 100644 index 0000000..d0c9a57 --- /dev/null +++ b/run.go @@ -0,0 +1,61 @@ +package complete + +import ( + "fmt" + "os" + "strings" +) + +const ( + envComplete = "COMP_LINE" + envDebug = "COMP_DEBUG" +) + +// Run get a command, get the typed arguments from environment +// variable, and print out the complete options +func Run(c Command) { + args := getLine() + Log("Completing args: %s", args) + + options := complete(c, args) + + Log("Completion: %s", options) + output(options) +} + +// complete get a command an command line arguments and returns +// matching completion options +func complete(c Command, args []string) (matching []string) { + options, _ := c.options(args[:len(args)-1]) + + // choose only matching options + l := last(args) + for _, option := range options { + if option.Matches(l) { + matching = append(matching, option.String()) + } + } + return +} + +func getLine() []string { + line := os.Getenv(envComplete) + if line == "" { + panic("should be run as a complete script") + } + return strings.Split(line, " ") +} + +func last(args []string) (last string) { + if len(args) > 0 { + last = args[len(args)-1] + } + return +} + +func output(options []string) { + // stdout of program defines the complete options + for _, option := range options { + fmt.Println(option) + } +} diff --git a/run_test.go b/run_test.go new file mode 100644 index 0000000..8f47431 --- /dev/null +++ b/run_test.go @@ -0,0 +1,204 @@ +package complete + +import ( + "os" + "sort" + "testing" +) + +func TestCompleter_Complete(t *testing.T) { + t.Parallel() + + if testing.Verbose() { + os.Setenv(envDebug, "1") + } + + c := Command{ + Sub: map[string]Command{ + "sub1": { + Flags: map[string]Predicate{ + "-flag1": PredictAnything, + "-flag2": PredictNothing, + }, + }, + "sub2": { + Flags: map[string]Predicate{ + "-flag2": PredictNothing, + "-flag3": PredictSet("opt1", "opt2", "opt12"), + }, + Args: PredictDirs("./tests/").Or(PredictFiles("./tests/*.md")), + }, + }, + Flags: map[string]Predicate{ + "-h": PredictNothing, + "-global1": PredictAnything, + "-o": PredictFiles("./tests/*.txt"), + }, + } + + allGlobals := []string{} + for sub := range c.Sub { + allGlobals = append(allGlobals, sub) + } + for flag := range c.Flags { + allGlobals = append(allGlobals, flag) + } + + testTXTFiles := []string{"./tests/a.txt", "./tests/b.txt", "./tests/c.txt"} + + tests := []struct { + args string + want []string + }{ + { + args: "", + want: allGlobals, + }, + { + args: "-", + want: []string{"-h", "-global1", "-o"}, + }, + { + args: "-h ", + want: allGlobals, + }, + { + args: "-global1 ", // global1 is known follow flag + want: []string{}, + }, + { + args: "sub", + want: []string{"sub1", "sub2"}, + }, + { + args: "sub1", + want: []string{"sub1"}, + }, + { + args: "sub2", + want: []string{"sub2"}, + }, + { + args: "sub1 ", + want: []string{"-flag1", "-flag2", "-h", "-global1", "-o"}, + }, + { + args: "sub2 ", + want: []string{"./tests", "-flag2", "-flag3", "-h", "-global1", "-o"}, + }, + { + args: "sub2 tests", + want: []string{"./tests", "./tests/readme.md", "./tests/dir"}, + }, + { + args: "sub2 tests/re", + want: []string{"./tests/readme.md"}, + }, + { + args: "sub2 -flag2 ", + want: []string{"./tests", "-flag2", "-flag3", "-h", "-global1", "-o"}, + }, + { + args: "sub1 -fl", + want: []string{"-flag1", "-flag2"}, + }, + { + args: "sub1 -flag1", + want: []string{"-flag1"}, + }, + { + args: "sub1 -flag1 ", + want: []string{}, // flag1 is unknown follow flag + }, + { + args: "sub1 -flag2 ", + want: []string{"-flag1", "-flag2", "-h", "-global1", "-o"}, + }, + { + args: "-no-such-flag", + want: []string{}, + }, + { + args: "-no-such-flag ", + want: allGlobals, + }, + { + args: "no-such-command", + want: []string{}, + }, + { + args: "no-such-command ", + want: allGlobals, + }, + { + args: "-o ", + want: []string{}, + }, + { + args: "-o ./tes", + want: []string{}, + }, + { + args: "-o tests/", + want: testTXTFiles, + }, + { + args: "-o tests", + want: testTXTFiles, + }, + { + args: "-o ./compl", + want: []string{}, + }, + { + args: "-o ./complete.go", + want: []string{}, + }, + { + args: "-o ./complete.go ", + want: allGlobals, + }, + { + args: "-o sub2 -flag3 ", + want: []string{"opt1", "opt2", "opt12"}, + }, + { + args: "-o sub2 -flag3 opt1", + want: []string{"opt1", "opt12"}, + }, + { + args: "-o sub2 -flag3 opt", + want: []string{"opt1", "opt2", "opt12"}, + }, + } + + for _, tt := range tests { + t.Run(tt.args, func(t *testing.T) { + + tt.args = "cmd " + tt.args + os.Setenv(envComplete, tt.args) + args := getLine() + + got := complete(c, 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) + } + }) + } +} + +func equalSlices(a, b []string) bool { + if len(a) != len(b) { + return false + } + for i := range a { + if a[i] != b[i] { + return false + } + } + return true +} -- cgit v1.2.3