on main help sign in
fix: config.HomeDir sync.OnceValue, use user.Current().HomeDir

Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>

commit9719451a3d6fb3483ef94757923b7340470e4410 authorCarlos Alexandro Becker <caarlos0@users.noreply.github.com> dateThu 7 Aug 2025, 14:00 (1 year ago) parent84213b1
2 files changed+64−47
internal/config/load.go+13−9 internal/fsext/ls.go+51−38
internal/config/load.go+13−9
@@ -1,16 +1,19 @@
11package config
22
33import (
4+ "cmp"
45 "encoding/json"
56 "fmt"
67 "io"
78 "log/slog"
89 "maps"
910 "os"
11+ "os/user"
1012 "path/filepath"
1113 "runtime"
1214 "slices"
1315 "strings"
16+ "sync"
1417
1518 "github.com/charmbracelet/catwalk/pkg/catwalk"
1619 "github.com/charmbracelet/crush/internal/csync"
@@ -538,13 +541,14 @@ func GlobalConfigData() string {
538541 return filepath.Join(os.Getenv("HOME"), ".local", "share", appName, fmt.Sprintf("%s.json", appName))
539542}
540543
541−func HomeDir() string {
542− homeDir := os.Getenv("HOME")
543− if homeDir == "" {
544− homeDir = os.Getenv("USERPROFILE") // For Windows compatibility
544+var HomeDir = sync.OnceValue(func() string {
545+ u, err := user.Current()
546+ if err == nil {
547+ return u.HomeDir
545548 }
546− if homeDir == "" {
547− homeDir = os.Getenv("HOMEPATH") // Fallback for some environments
548− }
549− return homeDir
550−}
549+ return cmp.Or(
550+ os.Getenv("HOME"),
551+ os.Getenv("USERPROFILE"),
552+ os.Getenv("HOMEPATH"),
553+ )
554+})
internal/fsext/ls.go+51−38
@@ -9,11 +9,12 @@ import (
99 "strings"
1010
1111 "github.com/charlievieth/fastwalk"
12+ "github.com/charmbracelet/crush/internal/csync"
1213 ignore "github.com/sabhiram/go-gitignore"
1314)
1415
15−// CommonIgnorePatterns contains commonly ignored files and directories
16−var CommonIgnorePatterns = []string{
16+// commonIgnorePatterns contains commonly ignored files and directories
17+var commonIgnorePatterns = ignore.CompileIgnoreLines(
1718 // Version control
1819 ".git",
1920 ".svn",
@@ -68,62 +69,74 @@ var CommonIgnorePatterns = []string{
6869
6970 // Crush
7071 ".crush",
71−}
72+)
7273
73−type DirectoryLister struct {
74− ignores *ignore.GitIgnore
74+type directoryLister struct {
75+ ignores *csync.Map[string, ignore.IgnoreParser]
7576 rootPath string
7677}
7778
78−func NewDirectoryLister(rootPath string) *DirectoryLister {
79− dl := &DirectoryLister{
79+func NewDirectoryLister(rootPath string) *directoryLister {
80+ return &directoryLister{
8081 rootPath: rootPath,
82+ ignores: csync.NewMap[string, ignore.IgnoreParser](),
8183 }
82−
83− dl.ignores = ignore.CompileIgnoreLines(append(CommonIgnorePatterns, strings.Split(parseIgnores(rootPath), "\n")...)...)
84−
85− return dl
8684}
8785
88−func parseIgnores(path string) string {
89− var b bytes.Buffer
90− for _, ign := range []string{".crushignore", ".gitignore"} {
91− p := filepath.Join(path, ign)
92− if _, err := os.Stat(p); err == nil {
93− f, err := os.Open(p)
94− if err != nil {
95− _ = f.Close()
96− slog.Error("Failed to open ignore file", "path", p, "error", err)
97− continue
98− }
99− if _, err := io.Copy(&b, f); err != nil {
100− slog.Error("Failed to read ignore file", "path", p, "error", err)
101− }
102− _ = f.Close()
103− }
104− }
105− return b.String()
106−}
107−
108−func (dl *DirectoryLister) shouldIgnore(path string, ignorePatterns []string) bool {
86+func (dl *directoryLister) shouldIgnore(path string, ignorePatterns []string) bool {
10987 relPath, err := filepath.Rel(dl.rootPath, path)
11088 if err != nil {
11189 relPath = path
11290 }
11391
114− if dl.ignores.MatchesPath(relPath) {
115− return true
116− }
117−
11892 base := filepath.Base(path)
119−
12093 for _, pattern := range ignorePatterns {
12194 matched, err := filepath.Match(pattern, base)
12295 if err == nil && matched {
96+ slog.Info("ignoring path", "path", path)
12397 return true
12498 }
12599 }
126− return false
100+
101+ if commonIgnorePatterns.MatchesPath(relPath) || dl.getIgnore(path).MatchesPath(relPath) {
102+ slog.Info("ignoring path", "path", path)
103+ return true
104+ }
105+
106+ parent := filepath.Dir(path)
107+ for {
108+ if dl.getIgnore(parent).MatchesPath(path) {
109+ slog.Info("ignoring path", "path", path, "parent", parent)
110+ return true
111+ }
112+ if parent == "/" || parent == "." { // TODO: windows
113+ return false
114+ }
115+ parent = filepath.Dir(parent)
116+ }
117+}
118+
119+func (dl *directoryLister) getIgnore(path string) ignore.IgnoreParser {
120+ return dl.ignores.GetOrSet(path, func() ignore.IgnoreParser {
121+ var b bytes.Buffer
122+ for _, ign := range []string{".crushignore", ".gitignore"} {
123+ p := filepath.Join(path, ign)
124+ if _, err := os.Stat(p); err == nil {
125+ slog.Info("loading ignore file", "path", p)
126+ f, err := os.Open(p)
127+ if err != nil {
128+ _ = f.Close()
129+ slog.Error("Failed to open ignore file", "path", p, "error", err)
130+ continue
131+ }
132+ if _, err := io.Copy(&b, f); err != nil {
133+ slog.Error("Failed to read ignore file", "path", p, "error", err)
134+ }
135+ _ = f.Close()
136+ }
137+ }
138+ return ignore.CompileIgnoreLines(strings.Split(b.String(), "\n")...)
139+ })
127140}
128141
129142// ListDirectory lists files and directories in the specified path,