Files
dirmd/internal/tree/unfiltered.go
T
2026-09-15 13:11:20 +01:00

131 lines
3.0 KiB
Go

package tree
import (
"fmt"
"io/fs"
"path/filepath"
"slices"
"strings"
"git.flo.fo/FLO/dirmd/internal/config"
"git.flo.fo/FLO/dirmd/internal/filter"
)
func BuildFullTree(absRoot string, cfg *config.Config) (*Node, error) {
root := &Node{IsDir: true, Children: make(map[string]*Node)}
err := filepath.WalkDir(absRoot, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return nil
}
rel, rerr := filepath.Rel(absRoot, path)
if rerr != nil || rel == "." {
return nil
}
if filter.IsSymlink(path) {
return nil
}
depth := strings.Count(rel, string(filepath.Separator)) + 1
if depth > cfg.MaxDepth {
node := addPath(root, rel, d.IsDir())
node.Annotation = "exceeded --max-depth"
if d.IsDir() {
return filepath.SkipDir
}
return nil
}
name := filepath.Base(path)
if d.IsDir() {
if strings.HasPrefix(name, ".") {
node := addPath(root, rel, true)
node.Annotation = "ignored (default)"
return filepath.SkipDir
}
if annotation, ok := ignoreReason(name, cfg.Ignores); ok {
node := addPath(root, rel, true)
node.Annotation = annotation
return filepath.SkipDir
}
if cfg.OmitTest && filter.IsTestDir(rel) {
node := addPath(root, rel, true)
node.Annotation = "tests omitted (--omit-test)"
return filepath.SkipDir
}
addPath(root, rel, true)
return nil
}
if annotation, ok := fileAnnotation(rel, name, cfg); ok {
node := addPath(root, rel, false)
node.Annotation = annotation
return nil
}
addPath(root, rel, false)
return nil
})
if err != nil {
return nil, fmt.Errorf("cannot walk %s: %w", absRoot, err)
}
return root, nil
}
func ignoreReason(name string, ignores []string) (string, bool) {
for _, pattern := range ignores {
if matched, _ := filepath.Match(pattern, name); matched {
if slices.Contains(config.DefaultIgnores, pattern) {
return "ignored (default)", true
}
return "ignored (--ignore)", true
}
}
return "", false
}
func fileAnnotation(rel, name string, cfg *config.Config) (string, bool) {
if strings.HasPrefix(name, ".") {
return "", false
}
if annotation, ok := ignoreReason(name, cfg.Ignores); ok {
return annotation, true
}
if cfg.OmitTest && filter.IsTestFile(rel, name) {
return "tests omitted (--omit-test)", true
}
if cfg.OmitMd && filter.IsNonReadmeMarkdown(rel, name) {
return "omitted (--omit-md)", true
}
if cfg.FrontendOnly && !filter.IsFrontendOrReadme(rel, name) {
return "omitted (--frontend-only)", true
}
if cfg.SkipFrontend && filter.IsFrontendExt(rel) {
return "omitted (--skip-frontend)", true
}
return "", false
}
func RenderBreadcrumb(absRoot, relPath string) string {
var sb strings.Builder
rootName := filepath.Base(absRoot)
dir := filepath.Dir(relPath)
if dir == "." {
return rootName + "/" + filepath.Base(relPath)
}
parts := strings.Split(filepath.ToSlash(dir), "/")
sb.WriteString(rootName)
for _, part := range parts {
sb.WriteString(" / ")
sb.WriteString(part)
}
sb.WriteString(" / ")
sb.WriteString(filepath.Base(relPath))
return sb.String()
}