diff --git a/internal/watcher/pattern.go b/internal/watcher/pattern.go index 73f22e32..5e6fda28 100644 --- a/internal/watcher/pattern.go +++ b/internal/watcher/pattern.go @@ -127,7 +127,7 @@ func (p *pattern) isValidPattern(fileName string) bool { // if the pattern has size 1 we can match it directly against the filename if len(p.parsedValues) == 1 { - return matchBracketPattern(p.parsedValues[0], fileNameWithoutDir) + return matchCurlyBracePattern(p.parsedValues[0], fileNameWithoutDir) } return p.matchPatterns(fileNameWithoutDir) @@ -159,7 +159,7 @@ func (p *pattern) matchPatterns(fileName string) bool { cursor = j subPattern := strings.Join(partsToMatch[j:j+patternSize], string(filepath.Separator)) - if matchBracketPattern(pattern, subPattern) { + if matchCurlyBracePattern(pattern, subPattern) { cursor = j + patternSize - 1 break @@ -174,24 +174,10 @@ func (p *pattern) matchPatterns(fileName string) bool { return true } -// we also check for the following bracket syntax: /path/*.{php,twig,yaml} -func matchBracketPattern(pattern string, fileName string) bool { - openingBracket := strings.Index(pattern, "{") - closingBracket := strings.Index(pattern, "}") - - // if there are no brackets we can match regularly - if openingBracket == -1 || closingBracket == -1 { - return matchPattern(pattern, fileName) - } - - beforeTheBrackets := pattern[:openingBracket] - betweenTheBrackets := pattern[openingBracket+1 : closingBracket] - afterTheBrackets := pattern[closingBracket+1:] - - // all bracket entries are checked individually, only one needs to match - // *.{php,twig,yaml} -> *.php, *.twig, *.yaml - for pattern := range strings.SplitSeq(betweenTheBrackets, ",") { - if matchPattern(beforeTheBrackets+pattern+afterTheBrackets, fileName) { +// we also check for the following syntax: /path/*.{php,twig,yaml} +func matchCurlyBracePattern(pattern string, fileName string) bool { + for _, subPattern := range expandCurlyBraces(pattern) { + if matchPattern(subPattern, fileName) { return true } } @@ -199,6 +185,26 @@ func matchBracketPattern(pattern string, fileName string) bool { return false } +// {dir1,dir2}/path -> []string{"dir1/path", "dir2/path"} +func expandCurlyBraces(s string) []string { + before, rest, found := strings.Cut(s, "{") + if !found { + return []string{s} + } + + inside, after, found := strings.Cut(rest, "}") + if !found { + return []string{s} // no closing brace + } + + var out []string + for _, subPattern := range strings.Split(inside, ",") { + out = append(out, expandCurlyBraces(before+subPattern+after)...) + } + + return out +} + func matchPattern(pattern string, fileName string) bool { if pattern == "" { return true diff --git a/internal/watcher/pattern_test.go b/internal/watcher/pattern_test.go index eddc3b33..25b4dd58 100644 --- a/internal/watcher/pattern_test.go +++ b/internal/watcher/pattern_test.go @@ -259,7 +259,7 @@ func TestInvalidDirectoryPatterns(t *testing.T) { } } -func TestValidExtendedPatterns(t *testing.T) { +func TestValidCurlyBracePatterns(t *testing.T) { data := []struct { pattern string dir string @@ -272,6 +272,10 @@ func TestValidExtendedPatterns(t *testing.T) { {"/path/{dir1,dir2}/file.php", "/path/dir2/file.php"}, {"/app/{app,config,resources}/**/*.php", "/app/app/subpath/file.php"}, {"/app/{app,config,resources}/**/*.php", "/app/config/subpath/file.php"}, + {"/path/{dir1,dir2}/{a,b}{a,b}.php", "/path/dir1/ab.php"}, + {"/path/{dir1,dir2}/{a,b}{a,b}.php", "/path/dir2/aa.php"}, + {"/path/{dir1,dir2}/{a,b}{a,b}.php", "/path/dir2/bb.php"}, + {"/path/{dir1/test.php,dir2/test.php}", "/path/dir1/test.php"}, } for _, d := range data { @@ -283,7 +287,7 @@ func TestValidExtendedPatterns(t *testing.T) { } } -func TestInvalidExtendedPatterns(t *testing.T) { +func TestInvalidCurlyBracePatterns(t *testing.T) { data := []struct { pattern string dir string @@ -293,6 +297,9 @@ func TestInvalidExtendedPatterns(t *testing.T) { {"/path/{file.php,file.twig}", "/path/file.txt"}, {"/path/{dir1,dir2}/file.php", "/path/dir3/file.php"}, {"/path/{dir1,dir2}/**/*.php", "/path/dir1/subpath/file.txt"}, + {"/path/{dir1,dir2}/{a,b}{a,b}.php", "/path/dir1/ac.php"}, + {"/path/{}/{a,b}{a,b}.php", "/path/dir1/ac.php"}, + {"/path/}dir{/{a,b}{a,b}.php", "/path/dir1/aa.php"}, } for _, d := range data {