From c8263230e11aa755d3b0b964d4bf7ff296661f5e Mon Sep 17 00:00:00 2001 From: Eyal Posener Date: Fri, 5 May 2017 23:07:18 +0300 Subject: [PATCH] Add additional args add predition of directories add ability for prediction union --- command.go | 6 +++++ complete_test.go | 45 +++++++++++++++++++++++++++++++------ gocomplete/complete.go | 1 + option.go | 5 ++++- predicate.go | 51 +++++++++++++++++++++++++++++++++++------- tests/a.txt | 0 tests/b.txt | 0 tests/c.txt | 0 tests/readme.md | 3 +++ 9 files changed, 95 insertions(+), 16 deletions(-) create mode 100644 tests/a.txt create mode 100644 tests/b.txt create mode 100644 tests/c.txt create mode 100644 tests/readme.md diff --git a/command.go b/command.go index 9292a89..e2f5eb4 100644 --- a/command.go +++ b/command.go @@ -7,6 +7,7 @@ type Flags map[string]Predicate type Command struct { Sub Commands Flags Flags + Args Predicate } // options returns all available complete options for the given command @@ -38,6 +39,11 @@ func (c *Command) options(args []string) (options []Option, only bool) { options = append(options, Arg(flag)) } + // add additional expected argument of the command + if c.Args.Expects { + options = append(options, c.Args.predict()...) + } + return } diff --git a/complete_test.go b/complete_test.go index aad747e..a6daa98 100644 --- a/complete_test.go +++ b/complete_test.go @@ -27,12 +27,13 @@ func TestCompleter_Complete(t *testing.T) { "-flag2": PredictNothing, "-flag3": PredictNothing, }, + Args: PredictDirs("./tests/").Or(PredictFiles("./tests/*.md")), }, }, Flags: map[string]Predicate{ "-h": PredictNothing, "-global1": PredictAnything, - "-o": PredictFiles("./gocomplete/*.go"), + "-o": PredictFiles("./tests/*.txt"), }, }, } @@ -45,6 +46,8 @@ func TestCompleter_Complete(t *testing.T) { allGlobals = append(allGlobals, flag) } + testTXTFiles := []string{"./tests/a.txt", "./tests/b.txt", "./tests/c.txt"} + tests := []struct { args string want []string @@ -83,7 +86,19 @@ func TestCompleter_Complete(t *testing.T) { }, { args: "sub2 ", - want: []string{"-flag2", "-flag3", "-h", "-global1", "-o"}, + 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", @@ -119,15 +134,31 @@ func TestCompleter_Complete(t *testing.T) { }, { args: "-o ", - want: []string{"./gocomplete/complete.go"}, + want: []string{}, }, { - args: "-o goco", - want: []string{"./gocomplete/complete.go"}, + args: "-o ./tes", + want: []string{}, }, { - args: "-o ./goco", - want: []string{"./gocomplete/complete.go"}, + 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, }, } diff --git a/gocomplete/complete.go b/gocomplete/complete.go index 5b31cc6..9233b4e 100644 --- a/gocomplete/complete.go +++ b/gocomplete/complete.go @@ -10,6 +10,7 @@ var ( "-o": complete.PredictFiles("*"), "-i": complete.PredictNothing, }, + Args: complete.PredictFiles("**.go").Or(complete.PredictDirs("./")), } test = complete.Command{ diff --git a/option.go b/option.go index 9333e84..6a067ea 100644 --- a/option.go +++ b/option.go @@ -35,5 +35,8 @@ func (a ArgFileName) Matches(prefix string) bool { if err != nil { logger("failed getting abs path of %s: %s", prefix, err) } - return strings.HasPrefix(full, prefixFull) + + // if the file has the prefix as prefix, + // but we don't want to show too many files, so, if it is in a deeper directory - omit it. + return strings.HasPrefix(full, prefixFull) && (full == prefixFull || !strings.Contains(full[len(prefixFull)+1:], "/")) } diff --git a/predicate.go b/predicate.go index 5ba544b..1cc6cca 100644 --- a/predicate.go +++ b/predicate.go @@ -16,11 +16,20 @@ type Predicate struct { Predictor func() []Option } -func (f *Predicate) predict() []Option { - if f.Predictor == nil { +// Or unions two predicate struct, so that the result predicate +// returns the union of their predication +func (p Predicate) Or(other Predicate) Predicate { + return Predicate{ + Expects: p.Expects && other.Expects, + Predictor: func() []Option { return append(p.predict(), other.predict()...) }, + } +} + +func (p Predicate) predict() []Option { + if p.Predictor == nil { return nil } - return f.Predictor() + return p.Predictor() } var ( @@ -35,6 +44,28 @@ func PredictFiles(pattern string) Predicate { } } +func PredictDirs(path string) Predicate { + return Predicate{ + Expects: true, + Predictor: dirs(path), + } +} +func dirs(path string) func() []Option { + return func() (options []Option) { + dirs := []string{} + filepath.Walk(path, func(path string, info os.FileInfo, err error) error { + if info.IsDir() { + dirs = append(dirs, path) + } + return nil + }) + if !filepath.IsAbs(path) { + filesToRel(dirs) + } + return filesToOptions(dirs) + } +} + func glob(pattern string) func() []Option { return func() []Option { files, err := filepath.Glob(pattern) @@ -44,11 +75,7 @@ func glob(pattern string) func() []Option { if !filepath.IsAbs(pattern) { filesToRel(files) } - options := make([]Option, len(files)) - for i, f := range files { - options[i] = ArgFileName(f) - } - return options + return filesToOptions(files) } } func filesToRel(files []string) { @@ -69,3 +96,11 @@ func filesToRel(files []string) { } return } + +func filesToOptions(files []string) []Option { + options := make([]Option, len(files)) + for i, f := range files { + options[i] = ArgFileName(f) + } + return options +} diff --git a/tests/a.txt b/tests/a.txt new file mode 100644 index 0000000..e69de29 diff --git a/tests/b.txt b/tests/b.txt new file mode 100644 index 0000000..e69de29 diff --git a/tests/c.txt b/tests/c.txt new file mode 100644 index 0000000..e69de29 diff --git a/tests/readme.md b/tests/readme.md new file mode 100644 index 0000000..25ea22c --- /dev/null +++ b/tests/readme.md @@ -0,0 +1,3 @@ +# About this directory + +This directory is for testing file completion purposes \ No newline at end of file