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
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module github.com/cespare/reflex

require (
github.com/bmatcuk/doublestar v1.1.1
github.com/fsnotify/fsnotify v1.4.7
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/kr/pretty v0.1.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/bmatcuk/doublestar v1.1.1 h1:YroD6BJCZBYx06yYFEWvUuKVWQn3vLLQAVmDmvTSaiQ=
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
Expand Down
40 changes: 37 additions & 3 deletions match.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ package main

import (
"fmt"
"path/filepath"
"regexp"
"regexp/syntax"
"strings"
"sync"

"github.com/bmatcuk/doublestar"
)

// A Matcher decides whether some filename matches its set of patterns.
Expand Down Expand Up @@ -67,14 +68,47 @@ type globMatcher struct {
}

func (m *globMatcher) Match(name string) bool {
matches, err := filepath.Match(m.glob, name)
matches, err := doublestar.PathMatch(m.glob, name)
if err != nil {
return false
}
return matches != m.inverse
}

func (m *globMatcher) ExcludePrefix(prefix string) bool { return false }
func (m *globMatcher) ExcludePrefix(prefix string) bool {
if !m.inverse || len(m.glob) == 0 {
return false
}

matches, err := doublestar.PathMatch(m.glob, prefix)
switch {
case err != nil:
return false
case matches:
return true
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

The documentation for the ExcludePrefix method says:

// ExcludePrefix returns whether all paths with this prefix cannot match.
// It is allowed to return false negatives but not false positives.

That is not what this function implements. For example, if the pattern is **/, that matches the prefix a/ but does not match the path a/b.txt. So this would incorrectly exclude a/b.txt from the watch.

It's not immediately obvious to me how to fix this problem. I designed the ExcludePrefix mechanism mostly with regexp matching in mind.

}

var i = 0
for i != len(m.glob) {
if m.glob[i] == '/' {
i++
}
pos := strings.Index(m.glob[i:], "/")
switch {
case pos == -1:
i = len(m.glob)
default:
i += pos
}

matches, _ := doublestar.PathMatch(m.glob[:i], prefix)
if matches {
return true
}
}

return false
}

func (m *globMatcher) String() string {
s := "Glob"
Expand Down
28 changes: 27 additions & 1 deletion match_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,33 @@ func TestMatchers(t *testing.T) {
}
}

func TestExcludePrefix(t *testing.T) {
func TestExcludePrefixGlob(t *testing.T) {
m := &globMatcher{glob: "foo"}
if m.ExcludePrefix("bar") {
t.Error("m.ExcludePrefix gave true for non-inverted matcher")
}

for _, tt := range []struct {
glob string
prefix string
want bool
}{
{"foo", "foo", true},
{"foo/*", "foo/bar", true},
{"foo/**", "foo/bar/baz", true},
{"foo/*", "foo/bar/baz", false},
{"bar/*", "foo", false},
{"bar", "foo", false},
} {
m := &globMatcher{glob: tt.glob, inverse: true}
if got := m.ExcludePrefix(tt.prefix); got != tt.want {
t.Errorf("(%v).ExcludePrefix(%q): got %t; want %t",
m, tt.prefix, got, tt.want)
}
}
}

func TestExcludePrefixRegex(t *testing.T) {
m := newRegexMatcher(regexp.MustCompile("foo"), false)
if m.ExcludePrefix("bar") {
t.Error("m.ExcludePrefix gave true for a non-inverted matcher")
Expand Down