summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--command.go4
-rw-r--r--complete.go4
-rw-r--r--gocomplete/complete.go178
-rw-r--r--gocomplete/tests.go57
-rw-r--r--log.go7
-rw-r--r--option.go4
-rw-r--r--predicate.go35
7 files changed, 256 insertions, 33 deletions
diff --git a/command.go b/command.go
index e2f5eb4..32c5456 100644
--- a/command.go
+++ b/command.go
@@ -19,7 +19,7 @@ func (c *Command) options(args []string) (options []Option, only bool) {
// if prev has something that needs to follow it,
// it is the most relevant completion
- if predicate, ok := c.Flags[last(args)]; ok && predicate.Expects {
+ if predicate, ok := c.Flags[last(args)]; ok && !predicate.ExpectsNothing {
return predicate.predict(), true
}
@@ -40,7 +40,7 @@ func (c *Command) options(args []string) (options []Option, only bool) {
}
// add additional expected argument of the command
- if c.Args.Expects {
+ if !c.Args.ExpectsNothing {
options = append(options, c.Args.predict()...)
}
diff --git a/complete.go b/complete.go
index e7e209e..302486c 100644
--- a/complete.go
+++ b/complete.go
@@ -21,11 +21,11 @@ func New(c Command) *Completer {
func (c *Completer) Complete() {
args := getLine()
- logger("Completing args: %s", args)
+ Log("Completing args: %s", args)
options := c.complete(args)
- logger("Completion: %s", options)
+ Log("Completion: %s", options)
output(options)
}
diff --git a/gocomplete/complete.go b/gocomplete/complete.go
index 9233b4e..57fbaa1 100644
--- a/gocomplete/complete.go
+++ b/gocomplete/complete.go
@@ -4,33 +4,187 @@ import (
"github.com/posener/complete"
)
-var (
- build = complete.Command{
+var predictEllipsis = complete.Predicate{
+ Predictor: func() []complete.Option { return []complete.Option{complete.Arg("./...")} },
+}
+
+var goFilesOrPackages = complete.PredictFiles("**.go").
+ Or(complete.PredictDirs("./")).
+ Or(predictEllipsis)
+
+func main() {
+ build := complete.Command{
Flags: complete.Flags{
- "-o": complete.PredictFiles("*"),
+ "-o": complete.PredictFiles("**"),
"-i": complete.PredictNothing,
+
+ "-a": complete.PredictNothing,
+ "-n": complete.PredictNothing,
+ "-p": complete.PredictAnything,
+ "-race": complete.PredictNothing,
+ "-msan": complete.PredictNothing,
+ "-v": complete.PredictNothing,
+ "-work": complete.PredictNothing,
+ "-x": complete.PredictNothing,
+ "-asmflags": complete.PredictAnything,
+ "-buildmode": complete.PredictAnything,
+ "-compiler": complete.PredictAnything,
+ "-gccgoflags": complete.PredictAnything,
+ "-gcflags": complete.PredictAnything,
+ "-installsuffix": complete.PredictAnything,
+ "-ldflags": complete.PredictAnything,
+ "-linkshared": complete.PredictNothing,
+ "-pkgdir": complete.PredictDirs("./"),
+ "-tags": complete.PredictAnything,
+ "-toolexec": complete.PredictAnything,
},
- Args: complete.PredictFiles("**.go").Or(complete.PredictDirs("./")),
+ Args: goFilesOrPackages,
}
- test = complete.Command{
+ run := complete.Command{
Flags: complete.Flags{
- "-run": complete.PredictAnything,
- "-count": complete.PredictAnything,
+ "-exec": complete.PredictAnything,
},
+ Args: complete.PredictFiles("**.go"),
}
- gogo = complete.Command{
+ test := complete.Command{
+ Flags: complete.Flags{
+ "-args": complete.PredictAnything,
+ "-c": complete.PredictNothing,
+ "-exec": complete.PredictAnything,
+
+ "-bench": predictTest("Benchmark"),
+ "-benchtime": complete.PredictAnything,
+ "-count": complete.PredictAnything,
+ "-cover": complete.PredictNothing,
+ "-covermode": complete.PredictSet([]string{"set", "count", "atomic"}),
+ "-coverpkg": complete.PredictDirs("./"),
+ "-cpu": complete.PredictAnything,
+ "-run": predictTest("test"),
+ "-short": complete.PredictNothing,
+ "-timeout": complete.PredictAnything,
+
+ "-benchmem": complete.PredictNothing,
+ "-blockprofile": complete.PredictFiles("**.out"),
+ "-blockprofilerate": complete.PredictAnything,
+ "-coverprofile": complete.PredictFiles("**.out"),
+ "-cpuprofile": complete.PredictFiles("**.out"),
+ "-memprofile": complete.PredictFiles("**.out"),
+ "-memprofilerate": complete.PredictAnything,
+ "-mutexprofile": complete.PredictFiles("**.out"),
+ "-mutexprofilefraction": complete.PredictAnything,
+ "-outputdir": complete.PredictDirs("./"),
+ "-trace": complete.PredictFiles("**.out"),
+ },
+ Args: goFilesOrPackages,
+ }
+
+ fmt := complete.Command{
+ Flags: complete.Flags{
+ "-n": complete.PredictNothing,
+ "-x": complete.PredictNothing,
+ },
+ Args: goFilesOrPackages,
+ }
+
+ get := complete.Command{
+ Flags: complete.Flags{
+ "-d": complete.PredictNothing,
+ "-f": complete.PredictNothing,
+ "-fix": complete.PredictNothing,
+ "-insecure": complete.PredictNothing,
+ "-t": complete.PredictNothing,
+ "-u": complete.PredictNothing,
+ },
+ Args: goFilesOrPackages,
+ }
+
+ generate := complete.Command{
+ Flags: complete.Flags{
+ "-n": complete.PredictNothing,
+ "-x": complete.PredictNothing,
+ "-v": complete.PredictNothing,
+ "-run": complete.PredictAnything,
+ },
+ Args: goFilesOrPackages,
+ }
+
+ vet := complete.Command{
+ Flags: complete.Flags{
+ "-n": complete.PredictNothing,
+ "-x": complete.PredictNothing,
+ },
+ Args: complete.PredictDirs("./"),
+ }
+
+ list := complete.Command{
+ Flags: complete.Flags{
+ "-e": complete.PredictNothing,
+ "-f": complete.PredictAnything,
+ "-json": complete.PredictNothing,
+ },
+ Args: complete.PredictDirs("./"),
+ }
+
+ tool := complete.Command{
+ Flags: complete.Flags{
+ "-n": complete.PredictNothing,
+ },
+ Args: complete.PredictAnything,
+ }
+
+ clean := complete.Command{
+ Flags: complete.Flags{
+ "-i": complete.PredictNothing,
+ "-r": complete.PredictNothing,
+ "-n": complete.PredictNothing,
+ "-x": complete.PredictNothing,
+ },
+ Args: complete.PredictDirs("./"),
+ }
+
+ env := complete.Command{
+ Args: complete.PredictAnything,
+ }
+
+ bug := complete.Command{}
+ version := complete.Command{}
+
+ fix := complete.Command{
+ Args: complete.PredictDirs("./"),
+ }
+
+ // commands that also accepts the build flags
+ for name, options := range build.Flags {
+ test.Flags[name] = options
+ run.Flags[name] = options
+ list.Flags[name] = options
+ vet.Flags[name] = options
+ }
+
+ gogo := complete.Command{
Sub: complete.Commands{
- "build": build,
- "test": test,
+ "build": build,
+ "install": build, // install and build have the same flags
+ "run": run,
+ "test": test,
+ "fmt": fmt,
+ "get": get,
+ "generate": generate,
+ "vet": vet,
+ "list": list,
+ "tool": tool,
+ "clean": clean,
+ "env": env,
+ "bug": bug,
+ "fix": fix,
+ "version": version,
},
Flags: complete.Flags{
"-h": complete.PredictNothing,
},
}
-)
-func main() {
complete.New(gogo).Complete()
}
diff --git a/gocomplete/tests.go b/gocomplete/tests.go
new file mode 100644
index 0000000..388b7b7
--- /dev/null
+++ b/gocomplete/tests.go
@@ -0,0 +1,57 @@
+package main
+
+import (
+ "go/ast"
+ "go/parser"
+ "go/token"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/posener/complete"
+)
+
+func predictTest(testType string) complete.Predicate {
+ return complete.Predicate{
+ Predictor: func() []complete.Option {
+ tests := testNames(testType)
+ options := make([]complete.Option, len(tests))
+ for i := range tests {
+ options[i] = complete.Arg(tests[i])
+ }
+ return options
+ },
+ }
+}
+
+// get all test names in current directory
+func testNames(testType string) (tests []string) {
+ filepath.Walk("./", func(path string, info os.FileInfo, err error) error {
+ // if not a test file, skip
+ if !strings.HasSuffix(path, "_test.go") {
+ return nil
+ }
+ // inspect test file and append all the test names
+ tests = append(tests, testsInFile(testType, path)...)
+ return nil
+ })
+ return
+}
+
+func testsInFile(testType, path string) (tests []string) {
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, path, nil, 0)
+ if err != nil {
+ complete.Log("Failed parsing %s: %s", path, err)
+ return nil
+ }
+ for _, d := range f.Decls {
+ if f, ok := d.(*ast.FuncDecl); ok {
+ name := f.Name.String()
+ if strings.HasPrefix(name, testType) {
+ tests = append(tests, name)
+ }
+ }
+ }
+ return
+}
diff --git a/log.go b/log.go
index 0b0a54a..797a80c 100644
--- a/log.go
+++ b/log.go
@@ -7,7 +7,12 @@ import (
"os"
)
-var logger = getLogger()
+// Log is used for debugging purposes
+// since complete is running on tab completion, it is nice to
+// have logs to the stderr (when writing your own completer)
+// to write logs, set the COMP_DEBUG environment variable and
+// use complete.Log in the complete program
+var Log = getLogger()
func getLogger() func(format string, args ...interface{}) {
var logfile io.Writer = ioutil.Discard
diff --git a/option.go b/option.go
index 6a067ea..3915091 100644
--- a/option.go
+++ b/option.go
@@ -29,11 +29,11 @@ func (a ArgFileName) String() string {
func (a ArgFileName) Matches(prefix string) bool {
full, err := filepath.Abs(string(a))
if err != nil {
- logger("failed getting abs path of %s: %s", a, err)
+ Log("failed getting abs path of %s: %s", a, err)
}
prefixFull, err := filepath.Abs(prefix)
if err != nil {
- logger("failed getting abs path of %s: %s", prefix, err)
+ Log("failed getting abs path of %s: %s", prefix, err)
}
// if the file has the prefix as prefix,
diff --git a/predicate.go b/predicate.go
index 1cc6cca..08a6722 100644
--- a/predicate.go
+++ b/predicate.go
@@ -7,10 +7,10 @@ import (
// Predicate determines what terms can follow a command or a flag
type Predicate struct {
- // Expects determine if the predicate expects something after.
+ // ExpectsNothing determine if the predicate expects something after.
// flags/commands that do not expect any specific argument should
// leave it on false
- Expects bool
+ ExpectsNothing bool
// Predictor is function that returns list of arguments that can
// come after the flag/command
Predictor func() []Option
@@ -20,8 +20,8 @@ type Predicate struct {
// 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()...) },
+ ExpectsNothing: p.ExpectsNothing && other.ExpectsNothing,
+ Predictor: func() []Option { return append(p.predict(), other.predict()...) },
}
}
@@ -33,23 +33,30 @@ func (p Predicate) predict() []Option {
}
var (
- PredictNothing = Predicate{Expects: false}
- PredictAnything = Predicate{Expects: true}
+ PredictNothing = Predicate{ExpectsNothing: true}
+ PredictAnything = Predicate{}
)
-func PredictFiles(pattern string) Predicate {
+func PredictSet(options []string) Predicate {
return Predicate{
- Expects: true,
- Predictor: glob(pattern),
+ Predictor: func() []Option {
+ ret := make([]Option, len(options))
+ for i := range options {
+ ret[i] = Arg(options[i])
+ }
+ return ret
+ },
}
}
+func PredictFiles(pattern string) Predicate {
+ return Predicate{Predictor: glob(pattern)}
+}
+
func PredictDirs(path string) Predicate {
- return Predicate{
- Expects: true,
- Predictor: dirs(path),
- }
+ return Predicate{Predictor: dirs(path)}
}
+
func dirs(path string) func() []Option {
return func() (options []Option) {
dirs := []string{}
@@ -70,7 +77,7 @@ func glob(pattern string) func() []Option {
return func() []Option {
files, err := filepath.Glob(pattern)
if err != nil {
- logger("failed glob operation with pattern '%s': %s", pattern, err)
+ Log("failed glob operation with pattern '%s': %s", pattern, err)
}
if !filepath.IsAbs(pattern) {
filesToRel(files)