fork of https://github.com/sourcegraph/zoekt
1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License at
4//
5// http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS,
9// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10// See the License for the specific language governing permissions and
11// limitations under the License.
12
13// Package ignore provides helpers to support ignore-files similar to .gitignore
14package ignore
15
16import (
17 "bufio"
18 "io"
19 "strings"
20
21 "github.com/gobwas/glob"
22)
23
24var (
25 lineComment = "#"
26 IgnoreFile = ".sourcegraph/ignore"
27)
28
29type Matcher struct {
30 ignoreList []glob.Glob
31}
32
33// ParseIgnoreFile parses an ignore-file according to the following rules
34//
35// - each line represents a glob-pattern relative to the root of the repository
36// - for patterns without any glob-characters, a trailing ** is implicit
37// - lines starting with # are ignored
38// - empty lines are ignored
39func ParseIgnoreFile(r io.Reader) (matcher *Matcher, error error) {
40 var patterns []glob.Glob
41 scanner := bufio.NewScanner(r)
42 for scanner.Scan() {
43 line := strings.TrimSpace(scanner.Text())
44 // ignore empty lines
45 if line == "" {
46 continue
47 }
48 // ignore comments
49 if strings.HasPrefix(line, lineComment) {
50 continue
51 }
52 line = strings.TrimPrefix(line, "/")
53 // implicit ** for patterns without glob-characters
54 if !strings.ContainsAny(line, ".][*?") {
55 line += "**"
56 }
57 // with separators = '/', * becomes path-aware
58 pattern, err := glob.Compile(line, '/')
59 if err != nil {
60 return nil, err
61 }
62 patterns = append(patterns, pattern)
63 }
64 return &Matcher{ignoreList: patterns}, scanner.Err()
65}
66
67// Match returns true if path has a prefix in common with any item in m.ignoreList
68func (m *Matcher) Match(path string) bool {
69 if len(m.ignoreList) == 0 {
70 return false
71 }
72 for _, pattern := range m.ignoreList {
73 if pattern.Match(path) {
74 return true
75 }
76 }
77 return false
78}