github.com/vlad2095/complete@v1.1.2/gocomplete/pkgs.go (about) 1 package main 2 3 import ( 4 "go/build" 5 "io/ioutil" 6 "os" 7 "os/user" 8 "path/filepath" 9 "strings" 10 11 "github.com/posener/complete" 12 ) 13 14 // predictPackages completes packages in the directory pointed by a.Last 15 // and packages that are one level below that package. 16 func predictPackages(a complete.Args) (prediction []string) { 17 prediction = []string{a.Last} 18 lastPrediction := "" 19 for len(prediction) == 1 && (lastPrediction == "" || lastPrediction != prediction[0]) { 20 // if only one prediction, predict files within this prediction, 21 // for example, if the user entered 'pk' and we have a package named 'pkg', 22 // which is the only package prefixed with 'pk', we will automatically go one 23 // level deeper and give the user the 'pkg' and all the nested packages within 24 // that package. 25 lastPrediction = prediction[0] 26 a.Last = prediction[0] 27 prediction = predictLocalAndSystem(a) 28 } 29 return 30 } 31 32 func predictLocalAndSystem(a complete.Args) []string { 33 localDirs := complete.PredictFilesSet(listPackages(a.Directory())).Predict(a) 34 // System directories are not actual file names, for example: 'github.com/posener/complete' could 35 // be the argument, but the actual filename is in $GOPATH/src/github.com/posener/complete'. this 36 // is the reason to use the PredictSet and not the PredictDirs in this case. 37 s := systemDirs(a.Last) 38 sysDirs := complete.PredictSet(s...).Predict(a) 39 return append(localDirs, sysDirs...) 40 } 41 42 // listPackages looks in current pointed dir and in all it's direct sub-packages 43 // and return a list of paths to go packages. 44 func listPackages(dir string) (directories []string) { 45 // add subdirectories 46 files, err := ioutil.ReadDir(dir) 47 if err != nil { 48 complete.Log("failed reading directory %s: %s", dir, err) 49 return 50 } 51 52 // build paths array 53 paths := make([]string, 0, len(files)+1) 54 for _, f := range files { 55 if f.IsDir() { 56 paths = append(paths, filepath.Join(dir, f.Name())) 57 } 58 } 59 paths = append(paths, dir) 60 61 // import packages according to given paths 62 for _, p := range paths { 63 pkg, err := build.ImportDir(p, 0) 64 if err != nil { 65 complete.Log("failed importing directory %s: %s", p, err) 66 continue 67 } 68 directories = append(directories, pkg.Dir) 69 } 70 return 71 } 72 73 func systemDirs(dir string) (directories []string) { 74 // get all paths from GOPATH environment variable and use their src directory 75 paths := findGopath() 76 for i := range paths { 77 paths[i] = filepath.Join(paths[i], "src") 78 } 79 80 // normalize the directory to be an actual directory since it could be with an additional 81 // characters after the last '/'. 82 if !strings.HasSuffix(dir, "/") { 83 dir = filepath.Dir(dir) 84 } 85 86 for _, basePath := range paths { 87 path := filepath.Join(basePath, dir) 88 files, err := ioutil.ReadDir(path) 89 if err != nil { 90 // path does not exists 91 continue 92 } 93 // add the base path as one of the completion options 94 switch dir { 95 case "", ".", "/", "./": 96 default: 97 directories = append(directories, dir) 98 } 99 // add all nested directories of the base path 100 // go supports only packages and not go files within the GOPATH 101 for _, f := range files { 102 if !f.IsDir() { 103 continue 104 } 105 directories = append(directories, filepath.Join(dir, f.Name())+"/") 106 } 107 } 108 return 109 } 110 111 func findGopath() []string { 112 gopath := os.Getenv("GOPATH") 113 if gopath == "" { 114 // By convention 115 // See rationale at https://github.com/golang/go/issues/17262 116 usr, err := user.Current() 117 if err != nil { 118 return nil 119 } 120 usrgo := filepath.Join(usr.HomeDir, "go") 121 return []string{usrgo} 122 } 123 listsep := string([]byte{os.PathListSeparator}) 124 entries := strings.Split(gopath, listsep) 125 return entries 126 }