diff --git a/gocomplete/complete.go b/gocomplete/complete.go index bb3e92e..00392eb 100644 --- a/gocomplete/complete.go +++ b/gocomplete/complete.go @@ -44,7 +44,7 @@ func main() { Flags: complete.Flags{ "-exec": complete.PredictAnything, }, - Args: complete.PredictFunc(predictRunnableFiles), + Args: goFiles, } test := complete.Command{ diff --git a/gocomplete/pkgs.go b/gocomplete/pkgs.go index 356a0fa..e8f5261 100644 --- a/gocomplete/pkgs.go +++ b/gocomplete/pkgs.go @@ -1,76 +1,66 @@ package main import ( - "bytes" - "encoding/json" - "os/exec" + "go/build" + "io/ioutil" + "os" "path/filepath" - "regexp" - "strings" "github.com/posener/complete" ) -const goListFormat = `{"Name": "{{.Name}}", "Path": "{{.Dir}}", "FilesString": "{{.GoFiles}}"}` - -// regexp matches a main function -var reMainFunc = regexp.MustCompile("^main$") - func predictPackages(a complete.Args) (prediction []string) { - dir := a.Directory() - pkgs := listPackages(dir) + for { + prediction = complete.PredictFilesSet(listPackages(a)).Predict(a) - files := make([]string, 0, len(pkgs)) - for _, p := range pkgs { - files = append(files, p.Path) - } - return complete.PredictFilesSet(files).Predict(a) -} - -func predictRunnableFiles(a complete.Args) (prediction []string) { - dir := a.Directory() - pkgs := listPackages(dir) - - files := []string{} - for _, p := range pkgs { - // filter non main pacakges - if p.Name != "main" { - continue + // if the number of prediction is not 1, we either have many results or + // have no results, so we return it. + if len(prediction) != 1 { + return } - for _, f := range p.Files { - path := filepath.Join(p.Path, f) - if len(functionsInFile(path, reMainFunc)) > 0 { - files = append(files, path) - } + + // if the result is only one item, we might want to recursively check + // for more accurate results. + if prediction[0] == a.Last { + return } + + // only try deeper, if the one item is a directory + if stat, err := os.Stat(prediction[0]); err != nil || !stat.IsDir() { + return + } + + a.Last = prediction[0] } - complete.Log("FILES: %s", files) - return complete.PredictFilesSet(files).Predict(a) } -type pack struct { - Name string - Path string - FilesString string - Files []string -} - -func listPackages(dir string) (pkgs []pack) { - dir = strings.TrimRight(dir, "/") + "/..." - out, err := exec.Command("go", "list", "-f", goListFormat, dir).Output() +func listPackages(a complete.Args) (dirctories []string) { + dir := a.Directory() + complete.Log("listing packages in %s", dir) + // import current directory + pkg, err := build.ImportDir(dir, 0) if err != nil { + complete.Log("failed importing directory %s: %s", dir, err) return } - lines := bytes.Split(out, []byte("\n")) - for _, line := range lines { - var p pack - err := json.Unmarshal(line, &p) - if err != nil { + dirctories = append(dirctories, pkg.Dir) + + // import subdirectories + files, err := ioutil.ReadDir(dir) + if err != nil { + complete.Log("failed reading directory %s: %s", dir, err) + return + } + for _, f := range files { + if !f.IsDir() { continue } - // parse the FileString from a string "[file1 file2 file3]" to a list of files - p.Files = strings.Split(strings.Trim(p.FilesString, "[]"), " ") - pkgs = append(pkgs, p) + pkg, err := build.ImportDir(filepath.Join(dir, f.Name()), 0) + if err != nil { + complete.Log("failed importing subdirectory %s: %s", filepath.Join(dir, f.Name()), err) + continue + } + dirctories = append(dirctories, pkg.Dir) } return } diff --git a/gocomplete/tests_test.go b/gocomplete/tests_test.go index a72f38f..5683d24 100644 --- a/gocomplete/tests_test.go +++ b/gocomplete/tests_test.go @@ -47,16 +47,6 @@ func TestPredictions(t *testing.T) { predictor: complete.PredictFunc(predictPackages), last: "X", }, - { - name: "predict runnable ok", - predictor: complete.PredictFunc(predictRunnableFiles), - completion: []string{"complete.go"}, - }, - { - name: "predict runnable not found", - predictor: complete.PredictFunc(predictRunnableFiles), - last: "X", - }, } for _, tt := range tests {