diff --git a/README.md b/README.md index 9b92d0c..2554efa 100644 --- a/README.md +++ b/README.md @@ -17,15 +17,19 @@ Or using package manager `brew` on OS X ## Usage - gotags [options] file(s) - - -L="": source file names are read from the specified file. If file is "-", input is read from standard in. - -R=false: recurse into directories in the file list. - -f="": write output to specified file. If file is "-", output is written to standard out. - -silent=false: do not produce any output on error. - -sort=true: sort tags. - -tag-relative=false: file paths should be relative to the directory containing the tag file. - -v=false: print version. + gotags [options] file(s) + + -L="": source file names are read from the specified file. If file is "-", input is read from standard in. + -R=false: recurse into directories in the file list. + -exclude=[]: exclude files and directories matching 'pattern'. May be called multiple times. + -f="": write output to specified file. If file is "-", output is written to standard out. + -fields="": include selected extension fields (only +l). + -list-languages=false: list supported languages. + -silent=false: do not produce any output on error. + -sort=true: sort tags. + -tag-relative=false: file paths should be relative to the directory containing the tag file. + -v=false: print version. + ## Vim [Tagbar][] configuration diff --git a/files_test.go b/files_test.go new file mode 100644 index 0000000..f30cf71 --- /dev/null +++ b/files_test.go @@ -0,0 +1,83 @@ +package main + +import ( + "reflect" + "strings" + "testing" +) + +var sources = []string{ + "LICENSE", + "README.md", + "fields.go", + "fields_test.go", + "files_test.go", + "main.go", + "parser.go", + "parser_test.go", + "tag.go", + "tag_test.go", + "tags", + "tests/const.go-src", + "tests/func.go-src", + "tests/import.go-src", + "tests/interface.go-src", + "tests/range.go-src", + "tests/simple.go-src", + "tests/struct.go-src", + "tests/type.go-src", + "tests/var.go-src", +} + +func TestCommandLineFiles(t *testing.T) { + patterns := patternList{} + + files, err := getFileNames(sources, false, patterns) + if err != nil { + t.Error(err) + } + + if !reflect.DeepEqual(sources, files) { + t.Errorf("%+v != %+v", sources, files) + } +} + +func TestSingleExcludePattern(t *testing.T) { + // Single pattern - exclude *_test.go files + patterns := []string{"*_test.go"} + files, err := getFileNames(sources, false, patterns) + if err != nil { + t.Error(err) + } + + for _, f := range files { + if strings.HasSuffix(f, "_test.go") { + t.Errorf("%v should not be included", f) + } + } +} + +func TestRecursiveExcludes(t *testing.T) { + input := []string{"/usr/local/go/src"} + patterns := []string{ + "*_test.go", + "/usr/local/go/src/*/*/testdata/*", + "/usr/local/go/src/*/*/testdata/*/*", + "/usr/local/go/src/*/*/testdata/*/*/*", + "/usr/local/go/src/*/*/testdata/*/*/*/*", + "/usr/local/go/src/*/*/testdata/*/*/*/*/*", + "/usr/local/go/src/*/*/testdata/*/*/*/*/*/*", + "/usr/local/go/src/*/*/testdata/*/*/*/*/*/*/*", + "/usr/local/go/src/*/*/*/testdata/*", + } + files, err := getFileNames(input, true, patterns) + if err != nil { + t.Error(err) + } + for _, f := range files { + if strings.HasSuffix(f, "_test.go") || strings.Contains(f, "/testdata/") { + t.Errorf("%v should not be included", f) + } + } + t.Log(files) +} diff --git a/main.go b/main.go index c954a01..f4f499b 100644 --- a/main.go +++ b/main.go @@ -21,16 +21,28 @@ const ( AuthorEmail = "stemmertech@gmail.com" ) +type patternList []string + +func (p *patternList) String() string { + return fmt.Sprint(*p) +} + +func (p *patternList) Set(value string) error { + *p = append(*p, value) + return nil +} + var ( - printVersion bool - inputFile string - outputFile string - recurse bool - sortOutput bool - silent bool - relative bool - listLangs bool - fields string + printVersion bool + inputFile string + outputFile string + recurse bool + sortOutput bool + silent bool + relative bool + listLangs bool + fields string + excludePatterns patternList ) // ignore unknown flags @@ -47,6 +59,7 @@ func init() { flags.BoolVar(&relative, "tag-relative", false, "file paths should be relative to the directory containing the tag file.") flags.BoolVar(&listLangs, "list-languages", false, "list supported languages.") flags.StringVar(&fields, "fields", "", "include selected extension fields (only +l).") + flags.Var(&excludePatterns, "exclude", "exclude files and directories matching 'pattern'. May be called multiple times.") flags.Usage = func() { fmt.Fprintf(os.Stderr, "gotags version %s\n\n", Version) @@ -69,6 +82,43 @@ func walkDir(names []string, dir string) ([]string, error) { return names, e } +// includeName checks if the name should be allowed or not based on the exclude patterns +func includeName(name string, patterns []string) (bool, error) { + for _, p := range patterns { + // Compare pattern to full path and then to base filename + for _, v := range []string{name, filepath.Base(name)} { + m, err := filepath.Match(p, v) + if err != nil { + // Error - exclude + return false, err + } + if m { + // Matches filepath - exclude + return false, nil + } + } + } + + // No filters matched - include the file + return true, nil +} + +func filterNames(names []string, patterns []string) ([]string, error) { + var ret []string + + for _, f := range names { + ok, err := includeName(f, patterns) + if err != nil { + return nil, err + } + if ok { + ret = append(ret, f) + } + } + + return ret, nil +} + func recurseNames(names []string) ([]string, error) { var ret []string for _, name := range names { @@ -113,15 +163,19 @@ func readNames(names []string) ([]string, error) { return names, nil } -func getFileNames() ([]string, error) { +func getFileNames(files []string, recurse bool, excludes patternList) ([]string, error) { var names []string - names = append(names, flags.Args()...) + // Start with list of supplied file names + names = append(names, files...) + + // Read filenames from input file if provided names, err := readNames(names) if err != nil { return nil, err } + // Recurse into directories in the file list if recurse { names, err = recurseNames(names) if err != nil { @@ -129,6 +183,12 @@ func getFileNames() ([]string, error) { } } + // Apply excludes patterns + names, err = filterNames(names, excludes) + if err != nil { + return nil, err + } + return names, nil } @@ -147,7 +207,7 @@ func main() { return } - files, err := getFileNames() + files, err := getFileNames(flags.Args(), recurse, excludePatterns) if err != nil { fmt.Fprintf(os.Stderr, "cannot get specified files\n\n") flags.Usage()