arche / internal/wc/ignore.go

commit 154431fd
  1package wc
  2
  3import (
  4	"bufio"
  5	"os"
  6	"path/filepath"
  7	"strings"
  8)
  9
 10type Ignore struct {
 11	rules []ignoreRule
 12}
 13
 14type ignoreRule struct {
 15	pattern string
 16	negate  bool
 17	dirOnly bool
 18}
 19
 20func loadIgnore(root string) (*Ignore, error) {
 21	ig := &Ignore{}
 22
 23	if home, err := os.UserHomeDir(); err == nil {
 24		_ = ig.addFile(filepath.Join(home, ".config", "git", "ignore"))
 25		_ = ig.addFile(filepath.Join(home, ".gitignore_global"))
 26		_ = ig.addFile(filepath.Join(home, ".config", "arche", "ignore"))
 27	}
 28
 29	_ = ig.addFile(filepath.Join(root, ".gitignore"))
 30	_ = ig.addFile(filepath.Join(root, ".archeignore"))
 31
 32	return ig, nil
 33}
 34
 35func (ig *Ignore) addFile(path string) error {
 36	f, err := os.Open(path)
 37	if os.IsNotExist(err) {
 38		return nil
 39	}
 40	if err != nil {
 41		return err
 42	}
 43	defer f.Close()
 44
 45	sc := bufio.NewScanner(f)
 46	for sc.Scan() {
 47		line := sc.Text()
 48		if idx := strings.Index(line, " #"); idx >= 0 {
 49			line = line[:idx]
 50		}
 51		line = strings.TrimRight(line, " \t")
 52		if line == "" || strings.HasPrefix(line, "#") {
 53			continue
 54		}
 55
 56		rule := ignoreRule{}
 57		if strings.HasPrefix(line, "!") {
 58			rule.negate = true
 59			line = line[1:]
 60		}
 61		if strings.HasSuffix(line, "/") {
 62			rule.dirOnly = true
 63			line = strings.TrimSuffix(line, "/")
 64		}
 65		rule.pattern = line
 66		ig.rules = append(ig.rules, rule)
 67	}
 68	return sc.Err()
 69}
 70
 71func (ig *Ignore) Match(rel string) bool {
 72	matched := false
 73	for _, r := range ig.rules {
 74		if r.dirOnly {
 75			continue
 76		}
 77		if matchPattern(r.pattern, rel) {
 78			if r.negate {
 79				matched = false
 80			} else {
 81				matched = true
 82			}
 83		}
 84	}
 85	return matched
 86}
 87
 88func (ig *Ignore) MatchDir(rel string) bool {
 89	matched := false
 90	for _, r := range ig.rules {
 91		if matchPattern(r.pattern, rel) {
 92			if r.negate {
 93				matched = false
 94			} else {
 95				matched = true
 96			}
 97		}
 98	}
 99	return matched
100}
101
102func matchPattern(pattern, rel string) bool {
103	base := filepath.Base(rel)
104
105	if ok, _ := filepath.Match(pattern, base); ok {
106		return true
107	}
108
109	if ok, _ := filepath.Match(pattern, rel); ok {
110		return true
111	}
112
113	if strings.Contains(pattern, "**") {
114		repl := strings.ReplaceAll(pattern, "**", "*")
115		if ok, _ := filepath.Match(repl, rel); ok {
116			return true
117		}
118	}
119
120	if strings.HasPrefix(rel, pattern+"/") {
121		return true
122	}
123	return false
124}