diff options
Diffstat (limited to 'vendor/github.com/mgechev/dots')
-rw-r--r-- | vendor/github.com/mgechev/dots/.travis.yml | 2 | ||||
-rw-r--r-- | vendor/github.com/mgechev/dots/LICENSE | 21 | ||||
-rw-r--r-- | vendor/github.com/mgechev/dots/README.md | 100 | ||||
-rw-r--r-- | vendor/github.com/mgechev/dots/resolve.go | 456 |
4 files changed, 579 insertions, 0 deletions
diff --git a/vendor/github.com/mgechev/dots/.travis.yml b/vendor/github.com/mgechev/dots/.travis.yml new file mode 100644 index 0000000000..f4a4a7363c --- /dev/null +++ b/vendor/github.com/mgechev/dots/.travis.yml @@ -0,0 +1,2 @@ +language: go +go: master diff --git a/vendor/github.com/mgechev/dots/LICENSE b/vendor/github.com/mgechev/dots/LICENSE new file mode 100644 index 0000000000..c617c7e012 --- /dev/null +++ b/vendor/github.com/mgechev/dots/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Minko Gechev + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/mgechev/dots/README.md b/vendor/github.com/mgechev/dots/README.md new file mode 100644 index 0000000000..1203aef5f7 --- /dev/null +++ b/vendor/github.com/mgechev/dots/README.md @@ -0,0 +1,100 @@ +[![Build Status](https://travis-ci.org/mgechev/dots.svg?branch=master)](https://travis-ci.org/mgechev/dots) + +# Dots + +Implements the wildcard file matching in Go used by golint, go test etc. + +## Usage + +```go +import "github.com/mgechev/dots" + +func main() { + result, err := dots.Resolve([]string{"./fixtures/..."}, []string{"./fixtures/foo"}) + for _, f := range result { + fmt.Println(f); + } +} +``` + +If we suppose that we have the following directory structure: + +```text +├── README.md +├── fixtures +│ ├── bar +│ │ ├── bar1.go +│ │ └── bar2.go +│ ├── baz +│ │ ├── baz1.go +│ │ ├── baz2.go +│ │ └── baz3.go +│ └── foo +│ ├── foo1.go +│ ├── foo2.go +│ └── foo3.go +└── main.go +``` + +The result will be: + +```text +fixtures/bar/bar1.go +fixtures/bar/bar2.go +fixtures/baz/baz1.go +fixtures/baz/baz2.go +fixtures/baz/baz3.go +``` + +`dots` supports wildcard in both - the first and the last argument of `Resolve`, which means that you can ignore files based on a wildcard: + +```go +dots.Resolve([]string{"github.com/mgechev/dots"}, []string{"./..."}) // empty list +dots.Resolve([]string{"./fixtures/bar/..."}, []string{"./fixture/foo/...", "./fixtures/baz/..."}) // bar1.go, bar2.go +``` + +## Preserve package structure + +`dots` allow you to receive a slice of slices where each nested slice represents an individual package: + +```go +dots.ResolvePackages([]string{"github.com/mgechev/dots/..."}, []string{}) +``` + +So we will get the result: + +```text +[ + [ + "$GOROOT/src/github.com/mgechev/dots/fixtures/dummy/bar/bar1.go", + "$GOROOT/src/github.com/mgechev/dots/fixtures/dummy/bar/bar2.go" + ], + [ + "$GOROOT/src/github.com/mgechev/dots/fixtures/dummy/baz/baz1.go", + "$GOROOT/src/github.com/mgechev/dots/fixtures/dummy/baz/baz2.go", + "$GOROOT/src/github.com/mgechev/dots/fixtures/dummy/baz/baz3.go" + ], + [ + "$GOROOT/src/github.com/mgechev/dots/fixtures/dummy/foo/foo1.go", + "$GOROOT/src/github.com/mgechev/dots/fixtures/dummy/foo/foo2.go", + "$GOROOT/src/github.com/mgechev/dots/fixtures/dummy/foo/foo3.go" + ], + [ + "$GOROOT/src/github.com/mgechev/dots/fixtures/pkg/baz/baz1.go", + "$GOROOT/src/github.com/mgechev/dots/fixtures/pkg/baz/baz2.go" + ], + [ + "$GOROOT/src/github.com/mgechev/dots/fixtures/pkg/foo/foo1.go", + "$GOROOT/src/github.com/mgechev/dots/fixtures/pkg/foo/foo2.go" + ], + [ + "$GOROOT/src/github.com/mgechev/dots/fixtures/pkg/foo/bar/bar1.go" + ] +] +``` + +This method is especially useful, when you want to perform type checking over given package from the result. + +## License + +MIT diff --git a/vendor/github.com/mgechev/dots/resolve.go b/vendor/github.com/mgechev/dots/resolve.go new file mode 100644 index 0000000000..309ba18ad2 --- /dev/null +++ b/vendor/github.com/mgechev/dots/resolve.go @@ -0,0 +1,456 @@ +package dots + +import ( + "go/build" + "log" + "os" + "path" + "path/filepath" + "regexp" + "runtime" + "strings" +) + +var ( + buildContext = build.Default + goroot = filepath.Clean(runtime.GOROOT()) + gorootSrc = filepath.Join(goroot, "src") +) + +func flatten(arr [][]string) []string { + var res []string + for _, e := range arr { + res = append(res, e...) + } + return res +} + +// Resolve accepts a slice of paths with optional "..." placeholder and a slice with paths to be skipped. +// The final result is the set of all files from the selected directories subtracted with +// the files in the skip slice. +func Resolve(includePatterns, skipPatterns []string) ([]string, error) { + skip, err := resolvePatterns(skipPatterns) + filter := newPathFilter(flatten(skip)) + if err != nil { + return nil, err + } + + pathSet := map[string]bool{} + includePackages, err := resolvePatterns(includePatterns) + include := flatten(includePackages) + if err != nil { + return nil, err + } + + var result []string + for _, i := range include { + if _, ok := pathSet[i]; !ok && !filter(i) { + pathSet[i] = true + result = append(result, i) + } + } + return result, err +} + +// ResolvePackages accepts a slice of paths with optional "..." placeholder and a slice with paths to be skipped. +// The final result is the set of all files from the selected directories subtracted with +// the files in the skip slice. The difference between `Resolve` and `ResolvePackages` +// is that `ResolvePackages` preserves the package structure in the nested slices. +func ResolvePackages(includePatterns, skipPatterns []string) ([][]string, error) { + skip, err := resolvePatterns(skipPatterns) + filter := newPathFilter(flatten(skip)) + if err != nil { + return nil, err + } + + pathSet := map[string]bool{} + include, err := resolvePatterns(includePatterns) + if err != nil { + return nil, err + } + + var result [][]string + for _, p := range include { + var packageFiles []string + for _, f := range p { + if _, ok := pathSet[f]; !ok && !filter(f) { + pathSet[f] = true + packageFiles = append(packageFiles, f) + } + } + result = append(result, packageFiles) + } + return result, err +} + +func isDir(filename string) bool { + fi, err := os.Stat(filename) + return err == nil && fi.IsDir() +} + +func exists(filename string) bool { + _, err := os.Stat(filename) + return err == nil +} + +func resolveDir(dirname string) ([]string, error) { + pkg, err := build.ImportDir(dirname, 0) + return resolveImportedPackage(pkg, err) +} + +func resolvePackage(pkgname string) ([]string, error) { + pkg, err := build.Import(pkgname, ".", 0) + return resolveImportedPackage(pkg, err) +} + +func resolveImportedPackage(pkg *build.Package, err error) ([]string, error) { + if err != nil { + if _, nogo := err.(*build.NoGoError); nogo { + // Don't complain if the failure is due to no Go source files. + return nil, nil + } + return nil, err + } + + var files []string + files = append(files, pkg.GoFiles...) + files = append(files, pkg.CgoFiles...) + files = append(files, pkg.TestGoFiles...) + if pkg.Dir != "." { + for i, f := range files { + files[i] = filepath.Join(pkg.Dir, f) + } + } + return files, nil +} + +func resolvePatterns(patterns []string) ([][]string, error) { + var files [][]string + for _, pattern := range patterns { + f, err := resolvePattern(pattern) + if err != nil { + return nil, err + } + files = append(files, f...) + } + return files, nil +} + +func resolvePattern(pattern string) ([][]string, error) { + // dirsRun, filesRun, and pkgsRun indicate whether golint is applied to + // directory, file or package targets. The distinction affects which + // checks are run. It is no valid to mix target types. + var dirsRun, filesRun, pkgsRun int + var matches []string + + if strings.HasSuffix(pattern, "/...") && isDir(pattern[:len(pattern)-len("/...")]) { + dirsRun = 1 + for _, dirname := range matchPackagesInFS(pattern) { + matches = append(matches, dirname) + } + } else if isDir(pattern) { + dirsRun = 1 + matches = append(matches, pattern) + } else if exists(pattern) { + filesRun = 1 + matches = append(matches, pattern) + } else { + pkgsRun = 1 + matches = append(matches, pattern) + } + + result := [][]string{} + switch { + case dirsRun == 1: + for _, dir := range matches { + res, err := resolveDir(dir) + if err != nil { + return nil, err + } + result = append(result, res) + } + case filesRun == 1: + return [][]string{matches}, nil + case pkgsRun == 1: + for _, pkg := range importPaths(matches) { + res, err := resolvePackage(pkg) + if err != nil { + return nil, err + } + result = append(result, res) + } + } + return result, nil +} + +func newPathFilter(skip []string) func(string) bool { + filter := map[string]bool{} + for _, name := range skip { + filter[name] = true + } + + return func(path string) bool { + base := filepath.Base(path) + if filter[base] || filter[path] { + return true + } + return base != "." && base != ".." && strings.ContainsAny(base[0:1], "_.") + } +} + +// importPathsNoDotExpansion returns the import paths to use for the given +// command line, but it does no ... expansion. +func importPathsNoDotExpansion(args []string) []string { + if len(args) == 0 { + return []string{"."} + } + var out []string + for _, a := range args { + // Arguments are supposed to be import paths, but + // as a courtesy to Windows developers, rewrite \ to / + // in command-line arguments. Handles .\... and so on. + if filepath.Separator == '\\' { + a = strings.Replace(a, `\`, `/`, -1) + } + + // Put argument in canonical form, but preserve leading ./. + if strings.HasPrefix(a, "./") { + a = "./" + path.Clean(a) + if a == "./." { + a = "." + } + } else { + a = path.Clean(a) + } + if a == "all" || a == "std" { + out = append(out, matchPackages(a)...) + continue + } + out = append(out, a) + } + return out +} + +// importPaths returns the import paths to use for the given command line. +func importPaths(args []string) []string { + args = importPathsNoDotExpansion(args) + var out []string + for _, a := range args { + if strings.Contains(a, "...") { + if build.IsLocalImport(a) { + out = append(out, matchPackagesInFS(a)...) + } else { + out = append(out, matchPackages(a)...) + } + continue + } + out = append(out, a) + } + return out +} + +// matchPattern(pattern)(name) reports whether +// name matches pattern. Pattern is a limited glob +// pattern in which '...' means 'any string' and there +// is no other special syntax. +func matchPattern(pattern string) func(name string) bool { + re := regexp.QuoteMeta(pattern) + re = strings.Replace(re, `\.\.\.`, `.*`, -1) + // Special case: foo/... matches foo too. + if strings.HasSuffix(re, `/.*`) { + re = re[:len(re)-len(`/.*`)] + `(/.*)?` + } + reg := regexp.MustCompile(`^` + re + `$`) + return func(name string) bool { + return reg.MatchString(name) + } +} + +// hasPathPrefix reports whether the path s begins with the +// elements in prefix. +func hasPathPrefix(s, prefix string) bool { + switch { + default: + return false + case len(s) == len(prefix): + return s == prefix + case len(s) > len(prefix): + if prefix != "" && prefix[len(prefix)-1] == '/' { + return strings.HasPrefix(s, prefix) + } + return s[len(prefix)] == '/' && s[:len(prefix)] == prefix + } +} + +// treeCanMatchPattern(pattern)(name) reports whether +// name or children of name can possibly match pattern. +// Pattern is the same limited glob accepted by matchPattern. +func treeCanMatchPattern(pattern string) func(name string) bool { + wildCard := false + if i := strings.Index(pattern, "..."); i >= 0 { + wildCard = true + pattern = pattern[:i] + } + return func(name string) bool { + return len(name) <= len(pattern) && hasPathPrefix(pattern, name) || + wildCard && strings.HasPrefix(name, pattern) + } +} + +func matchPackages(pattern string) []string { + match := func(string) bool { return true } + treeCanMatch := func(string) bool { return true } + if pattern != "all" && pattern != "std" { + match = matchPattern(pattern) + treeCanMatch = treeCanMatchPattern(pattern) + } + + have := map[string]bool{ + "builtin": true, // ignore pseudo-package that exists only for documentation + } + if !buildContext.CgoEnabled { + have["runtime/cgo"] = true // ignore during walk + } + var pkgs []string + + // Commands + cmd := filepath.Join(goroot, "src/cmd") + string(filepath.Separator) + filepath.Walk(cmd, func(path string, fi os.FileInfo, err error) error { + if err != nil || !fi.IsDir() || path == cmd { + return nil + } + name := path[len(cmd):] + if !treeCanMatch(name) { + return filepath.SkipDir + } + // Commands are all in cmd/, not in subdirectories. + if strings.Contains(name, string(filepath.Separator)) { + return filepath.SkipDir + } + + // We use, e.g., cmd/gofmt as the pseudo import path for gofmt. + name = "cmd/" + name + if have[name] { + return nil + } + have[name] = true + if !match(name) { + return nil + } + _, err = buildContext.ImportDir(path, 0) + if err != nil { + if _, noGo := err.(*build.NoGoError); !noGo { + log.Print(err) + } + return nil + } + pkgs = append(pkgs, name) + return nil + }) + + for _, src := range buildContext.SrcDirs() { + if (pattern == "std" || pattern == "cmd") && src != gorootSrc { + continue + } + src = filepath.Clean(src) + string(filepath.Separator) + root := src + if pattern == "cmd" { + root += "cmd" + string(filepath.Separator) + } + filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { + if err != nil || !fi.IsDir() || path == src { + return nil + } + + // Avoid .foo, _foo, and testdata directory trees. + _, elem := filepath.Split(path) + if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { + return filepath.SkipDir + } + + name := filepath.ToSlash(path[len(src):]) + if pattern == "std" && (strings.Contains(name, ".") || name == "cmd") { + // The name "std" is only the standard library. + // If the name is cmd, it's the root of the command tree. + return filepath.SkipDir + } + if !treeCanMatch(name) { + return filepath.SkipDir + } + if have[name] { + return nil + } + have[name] = true + if !match(name) { + return nil + } + _, err = buildContext.ImportDir(path, 0) + if err != nil { + if _, noGo := err.(*build.NoGoError); noGo { + return nil + } + } + pkgs = append(pkgs, name) + return nil + }) + } + return pkgs +} + +func matchPackagesInFS(pattern string) []string { + // Find directory to begin the scan. + // Could be smarter but this one optimization + // is enough for now, since ... is usually at the + // end of a path. + i := strings.Index(pattern, "...") + dir, _ := path.Split(pattern[:i]) + + // pattern begins with ./ or ../. + // path.Clean will discard the ./ but not the ../. + // We need to preserve the ./ for pattern matching + // and in the returned import paths. + prefix := "" + if strings.HasPrefix(pattern, "./") { + prefix = "./" + } + match := matchPattern(pattern) + + var pkgs []string + filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { + if err != nil || !fi.IsDir() { + return nil + } + if path == dir { + // filepath.Walk starts at dir and recurses. For the recursive case, + // the path is the result of filepath.Join, which calls filepath.Clean. + // The initial case is not Cleaned, though, so we do this explicitly. + // + // This converts a path like "./io/" to "io". Without this step, running + // "cd $GOROOT/src/pkg; go list ./io/..." would incorrectly skip the io + // package, because prepending the prefix "./" to the unclean path would + // result in "././io", and match("././io") returns false. + path = filepath.Clean(path) + } + + // Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..". + _, elem := filepath.Split(path) + dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".." + if dot || strings.HasPrefix(elem, "_") || elem == "testdata" { + return filepath.SkipDir + } + + name := prefix + filepath.ToSlash(path) + if !match(name) { + return nil + } + if _, err = build.ImportDir(path, 0); err != nil { + if _, noGo := err.(*build.NoGoError); !noGo { + log.Print(err) + } + return nil + } + pkgs = append(pkgs, name) + return nil + }) + return pkgs +} |