Skip to content

Commit 7c392cb

Browse files
author
Andrew Carter
committed
Add exclude option
Added --exclude option that mimics ctag's exclude flag. The pattern is expected to be a shell glob pattern (which is what Go's filepath.Match method expects). It can be set multiple times to set several filters. Once the file list is built, the patterns will be applied to each file to filter out any names that match the exclude patterns before processing. Note: Glob pattern '**' does not work recursively. See open issue golang/go#11862
1 parent be986a3 commit 7c392cb

File tree

3 files changed

+168
-21
lines changed

3 files changed

+168
-21
lines changed

README.md

+13-9
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,19 @@ Or using package manager `brew` on OS X
1717

1818
## Usage
1919

20-
gotags [options] file(s)
21-
22-
-L="": source file names are read from the specified file. If file is "-", input is read from standard in.
23-
-R=false: recurse into directories in the file list.
24-
-f="": write output to specified file. If file is "-", output is written to standard out.
25-
-silent=false: do not produce any output on error.
26-
-sort=true: sort tags.
27-
-tag-relative=false: file paths should be relative to the directory containing the tag file.
28-
-v=false: print version.
20+
gotags [options] file(s)
21+
22+
-L="": source file names are read from the specified file. If file is "-", input is read from standard in.
23+
-R=false: recurse into directories in the file list.
24+
-exclude=[]: exclude files and directories matching 'pattern'. May be called multiple times.
25+
-f="": write output to specified file. If file is "-", output is written to standard out.
26+
-fields="": include selected extension fields (only +l).
27+
-list-languages=false: list supported languages.
28+
-silent=false: do not produce any output on error.
29+
-sort=true: sort tags.
30+
-tag-relative=false: file paths should be relative to the directory containing the tag file.
31+
-v=false: print version.
32+
2933

3034
## Vim [Tagbar][] configuration
3135

files_test.go

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package main
2+
3+
import (
4+
"reflect"
5+
"strings"
6+
"testing"
7+
)
8+
9+
var sources = []string{
10+
"LICENSE",
11+
"README.md",
12+
"fields.go",
13+
"fields_test.go",
14+
"files_test.go",
15+
"main.go",
16+
"parser.go",
17+
"parser_test.go",
18+
"tag.go",
19+
"tag_test.go",
20+
"tags",
21+
"tests/const.go-src",
22+
"tests/func.go-src",
23+
"tests/import.go-src",
24+
"tests/interface.go-src",
25+
"tests/range.go-src",
26+
"tests/simple.go-src",
27+
"tests/struct.go-src",
28+
"tests/type.go-src",
29+
"tests/var.go-src",
30+
}
31+
32+
func TestCommandLineFiles(t *testing.T) {
33+
patterns := patternList{}
34+
35+
files, err := getFileNames(sources, false, patterns)
36+
if err != nil {
37+
t.Error(err)
38+
}
39+
40+
if !reflect.DeepEqual(sources, files) {
41+
t.Errorf("%+v != %+v", sources, files)
42+
}
43+
}
44+
45+
func TestSingleExcludePattern(t *testing.T) {
46+
// Single pattern - exclude *_test.go files
47+
patterns := []string{"*_test.go"}
48+
files, err := getFileNames(sources, false, patterns)
49+
if err != nil {
50+
t.Error(err)
51+
}
52+
53+
for _, f := range files {
54+
if strings.HasSuffix(f, "_test.go") {
55+
t.Errorf("%v should not be included", f)
56+
}
57+
}
58+
}
59+
60+
func TestRecursiveExcludes(t *testing.T) {
61+
input := []string{"/usr/local/go/src"}
62+
patterns := []string{
63+
"*_test.go",
64+
"/usr/local/go/src/*/*/testdata/*",
65+
"/usr/local/go/src/*/*/testdata/*/*",
66+
"/usr/local/go/src/*/*/testdata/*/*/*",
67+
"/usr/local/go/src/*/*/testdata/*/*/*/*",
68+
"/usr/local/go/src/*/*/testdata/*/*/*/*/*",
69+
"/usr/local/go/src/*/*/testdata/*/*/*/*/*/*",
70+
"/usr/local/go/src/*/*/testdata/*/*/*/*/*/*/*",
71+
"/usr/local/go/src/*/*/*/testdata/*",
72+
}
73+
files, err := getFileNames(input, true, patterns)
74+
if err != nil {
75+
t.Error(err)
76+
}
77+
for _, f := range files {
78+
if strings.HasSuffix(f, "_test.go") || strings.Contains(f, "/testdata/") {
79+
t.Errorf("%v should not be included", f)
80+
}
81+
}
82+
t.Log(files)
83+
}

main.go

+72-12
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,28 @@ const (
2121
AuthorEmail = "stemmertech@gmail.com"
2222
)
2323

24+
type patternList []string
25+
26+
func (p *patternList) String() string {
27+
return fmt.Sprint(*p)
28+
}
29+
30+
func (p *patternList) Set(value string) error {
31+
*p = append(*p, value)
32+
return nil
33+
}
34+
2435
var (
25-
printVersion bool
26-
inputFile string
27-
outputFile string
28-
recurse bool
29-
sortOutput bool
30-
silent bool
31-
relative bool
32-
listLangs bool
33-
fields string
36+
printVersion bool
37+
inputFile string
38+
outputFile string
39+
recurse bool
40+
sortOutput bool
41+
silent bool
42+
relative bool
43+
listLangs bool
44+
fields string
45+
excludePatterns patternList
3446
)
3547

3648
// ignore unknown flags
@@ -47,6 +59,7 @@ func init() {
4759
flags.BoolVar(&relative, "tag-relative", false, "file paths should be relative to the directory containing the tag file.")
4860
flags.BoolVar(&listLangs, "list-languages", false, "list supported languages.")
4961
flags.StringVar(&fields, "fields", "", "include selected extension fields (only +l).")
62+
flags.Var(&excludePatterns, "exclude", "exclude files and directories matching 'pattern'. May be called multiple times.")
5063

5164
flags.Usage = func() {
5265
fmt.Fprintf(os.Stderr, "gotags version %s\n\n", Version)
@@ -69,6 +82,43 @@ func walkDir(names []string, dir string) ([]string, error) {
6982
return names, e
7083
}
7184

85+
// includeName checks if the name should be allowed or not based on the exclude patterns
86+
func includeName(name string, patterns []string) (bool, error) {
87+
for _, p := range patterns {
88+
// Compare pattern to full path and then to base filename
89+
for _, v := range []string{name, filepath.Base(name)} {
90+
m, err := filepath.Match(p, v)
91+
if err != nil {
92+
// Error - exclude
93+
return false, err
94+
}
95+
if m {
96+
// Matches filepath - exclude
97+
return false, nil
98+
}
99+
}
100+
}
101+
102+
// No filters matched - include the file
103+
return true, nil
104+
}
105+
106+
func filterNames(names []string, patterns []string) ([]string, error) {
107+
var ret []string
108+
109+
for _, f := range names {
110+
ok, err := includeName(f, patterns)
111+
if err != nil {
112+
return nil, err
113+
}
114+
if ok {
115+
ret = append(ret, f)
116+
}
117+
}
118+
119+
return ret, nil
120+
}
121+
72122
func recurseNames(names []string) ([]string, error) {
73123
var ret []string
74124
for _, name := range names {
@@ -113,22 +163,32 @@ func readNames(names []string) ([]string, error) {
113163
return names, nil
114164
}
115165

116-
func getFileNames() ([]string, error) {
166+
func getFileNames(files []string, recurse bool, excludes patternList) ([]string, error) {
117167
var names []string
118168

119-
names = append(names, flags.Args()...)
169+
// Start with list of supplied file names
170+
names = append(names, files...)
171+
172+
// Read filenames from input file if provided
120173
names, err := readNames(names)
121174
if err != nil {
122175
return nil, err
123176
}
124177

178+
// Recurse into directories in the file list
125179
if recurse {
126180
names, err = recurseNames(names)
127181
if err != nil {
128182
return nil, err
129183
}
130184
}
131185

186+
// Apply excludes patterns
187+
names, err = filterNames(names, excludes)
188+
if err != nil {
189+
return nil, err
190+
}
191+
132192
return names, nil
133193
}
134194

@@ -147,7 +207,7 @@ func main() {
147207
return
148208
}
149209

150-
files, err := getFileNames()
210+
files, err := getFileNames(flags.Args(), recurse, excludePatterns)
151211
if err != nil {
152212
fmt.Fprintf(os.Stderr, "cannot get specified files\n\n")
153213
flags.Usage()

0 commit comments

Comments
 (0)