a73x

internal/ignore/ignore.go

Ref:   Size: 1.3 KiB

package ignore

import (
	"os"
	"path/filepath"
	"slices"
	"strings"
	"sync"

	gitignore "github.com/sabhiram/go-gitignore"
)

var hardExcludeDirs = []string{
	".git", "node_modules", "dist", "build", "target", ".venv",
}
var hardExcludeSuffixes = []string{
	".swp", ".tmp", ".lock",
}

type Matcher struct {
	root string
	mu   sync.RWMutex
	gi   *gitignore.GitIgnore
}

func New(root string) (*Matcher, error) {
	m := &Matcher{root: root}
	if err := m.Reload(); err != nil {
		return nil, err
	}
	return m, nil
}

func (m *Matcher) Reload() error {
	path := filepath.Join(m.root, ".gitignore")
	var gi *gitignore.GitIgnore
	if _, err := os.Stat(path); err == nil {
		loaded, err := gitignore.CompileIgnoreFile(path)
		if err != nil {
			return err
		}
		gi = loaded
	}
	m.mu.Lock()
	m.gi = gi
	m.mu.Unlock()
	return nil
}

func (m *Matcher) ShouldIgnore(path string) bool {
	rel, err := filepath.Rel(m.root, path)
	if err != nil || strings.HasPrefix(rel, "..") {
		return true
	}
	for p := range strings.SplitSeq(rel, string(filepath.Separator)) {
		if slices.Contains(hardExcludeDirs, p) {
			return true
		}
	}
	base := filepath.Base(rel)
	for _, sfx := range hardExcludeSuffixes {
		if strings.HasSuffix(base, sfx) {
			return true
		}
	}
	m.mu.RLock()
	gi := m.gi
	m.mu.RUnlock()
	if gi != nil && gi.MatchesPath(rel) {
		return true
	}
	return false
}