diff --git a/go.mod b/go.mod index c5c8791..0b8122f 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module src.lwithers.me.uk/go/gg go 1.20 require ( + github.com/bmatcuk/doublestar/v4 v4.6.0 github.com/logrusorgru/aurora/v4 v4.0.0 github.com/spf13/cobra v1.7.0 golang.org/x/sys v0.8.0 diff --git a/go.sum b/go.sum index 6bd418b..5843900 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/bmatcuk/doublestar/v4 v4.6.0 h1:HTuxyug8GyFbRkrffIpzNCSK4luc0TY3wzXvzIZhEXc= +github.com/bmatcuk/doublestar/v4 v4.6.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= diff --git a/main.go b/main.go index 5a93986..8cd4f8b 100644 --- a/main.go +++ b/main.go @@ -5,17 +5,18 @@ package main import ( "errors" + "fmt" "os" "path/filepath" "regexp" + "github.com/bmatcuk/doublestar/v4" "github.com/spf13/cobra" "golang.org/x/sys/unix" ) // TODO: // - bold of escaped output doesn't work -// - ignore files by extension (or glob?) func main() { if err := rootCmd.Execute(); err != nil { @@ -42,7 +43,11 @@ as paths to scan. Search defaults to case-sensitive but the -i flag may be passed to make regular expression searches case-insensitive. Alternatively, the "(?i)" construct may be added to a regular expression to make that specific expression case insensitive. -Fixed pattern matches are always case-sensitive.`, +Fixed pattern matches are always case-sensitive. + +Files and directories can be excluded with the -x option. This supports bash-style +globs with '*', '?', '[a-z]', '{this,that}', or '/**/' to match zero or more +directories. By default, .git and vim swap files are ignored.`, RunE: run, } @@ -53,10 +58,9 @@ var ( searchFixed []string searchBytes [][]byte searchPath []string - ignoreList []string + excludeList []string binaryFile notPlainTextFlag minifiedFile notPlainTextFlag - ignoreMap map[string]struct{} ignoreCase bool noColour bool display *Display @@ -66,7 +70,7 @@ var ( func init() { rootCmd.Flags().StringSliceVarP(&searchRegexp, "grep", "e", nil, "pattern to match (regular expression)") rootCmd.Flags().StringSliceVarP(&searchFixed, "fixed", "Q", nil, "pattern to match (fixed string)") - rootCmd.Flags().StringSliceVarP(&ignoreList, "exclude", "x", []string{".git"}, "files/directories to exclude") + rootCmd.Flags().StringSliceVarP(&excludeList, "exclude", "x", []string{".git", ".*.swp"}, "files/directories to exclude") rootCmd.Flags().BoolVarP(&ignoreCase, "ignore-case", "i", false, "make all searches case insensitive") rootCmd.Flags().BoolVarP(&noColour, "no-colour", "C", false, "disable colour output") rootCmd.Flags().Var(&binaryFile, "binary", "what to do with binary files") @@ -89,9 +93,10 @@ func run(c *cobra.Command, args []string) error { searchPath = append(searchPath, ".") } - ignoreMap = make(map[string]struct{}, len(ignoreList)) - for _, i := range ignoreList { - ignoreMap[i] = struct{}{} + for _, x := range excludeList { + if !doublestar.ValidatePattern(x) { + return fmt.Errorf("invalid exclude pattern %q", x) + } } for _, r := range searchRegexp { @@ -126,12 +131,15 @@ func recurse(path string) error { } var errs []error +NextFile: for _, de := range d { name := de.Name() fullPath := filepath.Join(path, name) - if _, ignored := ignoreMap[name]; ignored { - continue + for _, x := range excludeList { + if exclude, _ := doublestar.Match(x, fullPath); exclude { + continue NextFile + } } if err := search(fullPath); err != nil {