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  }