Roughly add all go commands

This commit is contained in:
Eyal Posener 2017-05-06 00:25:27 +03:00
parent c8263230e1
commit 1af7c0b3b7
7 changed files with 256 additions and 33 deletions

View File

@ -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()...)
}

View File

@ -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)
}

View File

@ -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()
}

57
gocomplete/tests.go Normal file
View File

@ -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
}

7
log.go
View File

@ -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

View File

@ -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,

View File

@ -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)