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, // if prev has something that needs to follow it,
// it is the most relevant completion // 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 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 // add additional expected argument of the command
if c.Args.Expects { if !c.Args.ExpectsNothing {
options = append(options, c.Args.predict()...) options = append(options, c.Args.predict()...)
} }

View File

@ -21,11 +21,11 @@ func New(c Command) *Completer {
func (c *Completer) Complete() { func (c *Completer) Complete() {
args := getLine() args := getLine()
logger("Completing args: %s", args) Log("Completing args: %s", args)
options := c.complete(args) options := c.complete(args)
logger("Completion: %s", options) Log("Completion: %s", options)
output(options) output(options)
} }

View File

@ -4,33 +4,187 @@ import (
"github.com/posener/complete" "github.com/posener/complete"
) )
var ( var predictEllipsis = complete.Predicate{
build = complete.Command{ 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{ Flags: complete.Flags{
"-o": complete.PredictFiles("*"), "-o": complete.PredictFiles("**"),
"-i": complete.PredictNothing, "-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{ Flags: complete.Flags{
"-run": complete.PredictAnything, "-exec": complete.PredictAnything,
"-count": 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{ Sub: complete.Commands{
"build": build, "build": build,
"test": test, "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{ Flags: complete.Flags{
"-h": complete.PredictNothing, "-h": complete.PredictNothing,
}, },
} }
)
func main() {
complete.New(gogo).Complete() 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" "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{}) { func getLogger() func(format string, args ...interface{}) {
var logfile io.Writer = ioutil.Discard var logfile io.Writer = ioutil.Discard

View File

@ -29,11 +29,11 @@ func (a ArgFileName) String() string {
func (a ArgFileName) Matches(prefix string) bool { func (a ArgFileName) Matches(prefix string) bool {
full, err := filepath.Abs(string(a)) full, err := filepath.Abs(string(a))
if err != nil { 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) prefixFull, err := filepath.Abs(prefix)
if err != nil { 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, // 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 // Predicate determines what terms can follow a command or a flag
type Predicate struct { 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 // flags/commands that do not expect any specific argument should
// leave it on false // leave it on false
Expects bool ExpectsNothing bool
// Predictor is function that returns list of arguments that can // Predictor is function that returns list of arguments that can
// come after the flag/command // come after the flag/command
Predictor func() []Option Predictor func() []Option
@ -20,8 +20,8 @@ type Predicate struct {
// returns the union of their predication // returns the union of their predication
func (p Predicate) Or(other Predicate) Predicate { func (p Predicate) Or(other Predicate) Predicate {
return Predicate{ return Predicate{
Expects: p.Expects && other.Expects, ExpectsNothing: p.ExpectsNothing && other.ExpectsNothing,
Predictor: func() []Option { return append(p.predict(), other.predict()...) }, Predictor: func() []Option { return append(p.predict(), other.predict()...) },
} }
} }
@ -33,23 +33,30 @@ func (p Predicate) predict() []Option {
} }
var ( var (
PredictNothing = Predicate{Expects: false} PredictNothing = Predicate{ExpectsNothing: true}
PredictAnything = Predicate{Expects: true} PredictAnything = Predicate{}
) )
func PredictFiles(pattern string) Predicate { func PredictSet(options []string) Predicate {
return Predicate{ return Predicate{
Expects: true, Predictor: func() []Option {
Predictor: glob(pattern), 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 { func PredictDirs(path string) Predicate {
return Predicate{ return Predicate{Predictor: dirs(path)}
Expects: true,
Predictor: dirs(path),
}
} }
func dirs(path string) func() []Option { func dirs(path string) func() []Option {
return func() (options []Option) { return func() (options []Option) {
dirs := []string{} dirs := []string{}
@ -70,7 +77,7 @@ func glob(pattern string) func() []Option {
return func() []Option { return func() []Option {
files, err := filepath.Glob(pattern) files, err := filepath.Glob(pattern)
if err != nil { 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) { if !filepath.IsAbs(pattern) {
filesToRel(files) filesToRel(files)