github.com/alexandrestein/complete@v1.1.2-0.20180313112007-cc6c1c3aa2ce/predict_files.go (about)

     1  package complete
     2  
     3  import (
     4  	"io/ioutil"
     5  	"os"
     6  	"path/filepath"
     7  	"strings"
     8  
     9  	"github.com/posener/complete/match"
    10  )
    11  
    12  // PredictDirs will search for directories in the given started to be typed
    13  // path, if no path was started to be typed, it will complete to directories
    14  // in the current working directory.
    15  func PredictDirs(pattern string) Predictor {
    16  	return files(pattern, false)
    17  }
    18  
    19  // PredictFiles will search for files matching the given pattern in the started to
    20  // be typed path, if no path was started to be typed, it will complete to files that
    21  // match the pattern in the current working directory.
    22  // To match any file, use "*" as pattern. To match go files use "*.go", and so on.
    23  func PredictFiles(pattern string) Predictor {
    24  	return files(pattern, true)
    25  }
    26  
    27  func files(pattern string, allowFiles bool) PredictFunc {
    28  
    29  	// search for files according to arguments,
    30  	// if only one directory has matched the result, search recursively into
    31  	// this directory to give more results.
    32  	return func(a Args) (prediction []string) {
    33  		prediction = predictFiles(a, pattern, allowFiles)
    34  
    35  		// if the number of prediction is not 1, we either have many results or
    36  		// have no results, so we return it.
    37  		if len(prediction) != 1 {
    38  			return
    39  		}
    40  
    41  		// only try deeper, if the one item is a directory
    42  		if stat, err := os.Stat(prediction[0]); err != nil || !stat.IsDir() {
    43  			return
    44  		}
    45  
    46  		a.Last = prediction[0]
    47  		return predictFiles(a, pattern, allowFiles)
    48  	}
    49  }
    50  
    51  func predictFiles(a Args, pattern string, allowFiles bool) []string {
    52  	if strings.HasSuffix(a.Last, "/..") {
    53  		return nil
    54  	}
    55  
    56  	dir := a.Directory()
    57  	files := listFiles(dir, pattern, allowFiles)
    58  
    59  	// add dir if match
    60  	files = append(files, dir)
    61  
    62  	return PredictFilesSet(files).Predict(a)
    63  }
    64  
    65  // PredictFilesSet predict according to file rules to a given set of file names
    66  func PredictFilesSet(files []string) PredictFunc {
    67  	return func(a Args) (prediction []string) {
    68  		// add all matching files to prediction
    69  		for _, f := range files {
    70  			f = fixPathForm(a.Last, f)
    71  
    72  			// test matching of file to the argument
    73  			if match.File(f, a.Last) {
    74  				prediction = append(prediction, f)
    75  			}
    76  		}
    77  		return
    78  	}
    79  }
    80  
    81  func listFiles(dir, pattern string, allowFiles bool) []string {
    82  	// set of all file names
    83  	m := map[string]bool{}
    84  
    85  	// list files
    86  	if files, err := filepath.Glob(filepath.Join(dir, pattern)); err == nil {
    87  		for _, f := range files {
    88  			if stat, err := os.Stat(f); err != nil || stat.IsDir() || allowFiles {
    89  				m[f] = true
    90  			}
    91  		}
    92  	}
    93  
    94  	// list directories
    95  	if dirs, err := ioutil.ReadDir(dir); err == nil {
    96  		for _, d := range dirs {
    97  			if d.IsDir() {
    98  				m[filepath.Join(dir, d.Name())] = true
    99  			}
   100  		}
   101  	}
   102  
   103  	list := make([]string, 0, len(m))
   104  	for k := range m {
   105  		list = append(list, k)
   106  	}
   107  	return list
   108  }