Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 13 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
83 changes: 83 additions & 0 deletions files_test.go
Original file line number Diff line number Diff line change
@@ -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) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test can be removed. I don't have Go installed in that directory (the test doesn't fail in that case btw), so it's not so useful. For now, I trust the implementation of filepath.Match and the previous tests should be enough to test that it's being used correctly.

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)
}
84 changes: 72 additions & 12 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,28 @@ const (
AuthorEmail = "[email protected]"
)

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
Expand All @@ -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)
Expand All @@ -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 {
Expand Down Expand Up @@ -113,22 +163,32 @@ 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 {
return nil, err
}
}

// Apply excludes patterns
names, err = filterNames(names, excludes)
if err != nil {
return nil, err
}

return names, nil
}

Expand All @@ -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()
Expand Down