This repository has been archived by the owner on Mar 6, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 149
/
match.go
149 lines (135 loc) · 4.12 KB
/
match.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package match
import (
"fmt"
"os"
"path"
"path/filepath"
"regexp"
"strings"
"github.com/constabulary/gb/internal/debug"
)
// importPathsNoDotExpansion returns the import paths to use for the given
// command line, but it does no ... expansion.
func importPathsNoDotExpansion(srcdir string, cwd string, args []string) []string {
srcdir, _ = filepath.Rel(srcdir, cwd)
debug.Debugf("%s %s", cwd, srcdir)
if srcdir == ".." {
srcdir = "."
}
if len(args) == 0 {
args = []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)
}
a = path.Join(srcdir, path.Clean(a))
out = append(out, a)
}
return out
}
// ImportPaths returns the import paths to use for the given command line.
func ImportPaths(srcdir, cwd string, args []string) []string {
args = importPathsNoDotExpansion(srcdir, cwd, args)
var out []string
for _, a := range args {
if strings.Contains(a, "...") {
pkgs, err := matchPackages(srcdir, a)
if err != nil {
fmt.Printf("could not load all packages: %v\n", err)
}
out = append(out, pkgs...)
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)
}
}
// matchPackages returns all the packages that can be found under the srcdir directory.
// The pattern is a path including "...".
func matchPackages(srcdir, pattern string) ([]string, error) {
debug.Debugf("matchPackages: %v", pattern)
match := matchPattern(pattern)
treeCanMatch := treeCanMatchPattern(pattern)
var pkgs []string
src := srcdir + string(filepath.Separator)
err := filepath.Walk(src, 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.
if skipElem(fi.Name()) {
return filepath.SkipDir
}
name := filepath.ToSlash(path[len(src):])
if pattern == "std" && strings.Contains(name, ".") {
return filepath.SkipDir
}
if !treeCanMatch(name) {
return filepath.SkipDir
}
if match(name) {
pkgs = append(pkgs, name)
}
return nil
})
return pkgs, err
}
// IsLocalImport reports whether the import path is
// a local import path, like ".", "..", "./foo", or "../foo".
func isLocalImport(path string) bool {
return path == "." || path == ".." || strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../")
}
// skipElem returns true of the path element should be ignored.thub.com/foo/bar" "github.com/quxx/bar"]
func skipElem(elem string) bool {
return strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata"
}