on main help sign in
fix: config.HomeDir sync.OnceValue, use user.Current().HomeDir
9719451Carlos Alexandro Becker
internal/config/load.go+13−9
@@ -1,16 +1,19 @@
1package config
1package config
2
2
3import (
3import (
4+ "cmp"
4 "encoding/json"
5 "encoding/json"
5 "fmt"
6 "fmt"
6 "io"
7 "io"
7 "log/slog"
8 "log/slog"
8 "maps"
9 "maps"
9 "os"
10 "os"
11+ "os/user"
10 "path/filepath"
12 "path/filepath"
11 "runtime"
13 "runtime"
12 "slices"
14 "slices"
13 "strings"
15 "strings"
16+ "sync"
14
17
15 "github.com/charmbracelet/catwalk/pkg/catwalk"
18 "github.com/charmbracelet/catwalk/pkg/catwalk"
16 "github.com/charmbracelet/crush/internal/csync"
19 "github.com/charmbracelet/crush/internal/csync"
@@ -538,13 +541,14 @@ func GlobalConfigData() string {
538 return filepath.Join(os.Getenv("HOME"), ".local", "share", appName, fmt.Sprintf("%s.json", appName))
541 return filepath.Join(os.Getenv("HOME"), ".local", "share", appName, fmt.Sprintf("%s.json", appName))
539}
542}
540
543
541−func HomeDir() string {
544+var HomeDir = sync.OnceValue(func() string {
542− homeDir := os.Getenv("HOME")
545+ u, err := user.Current()
543− if homeDir == "" {
546+ if err == nil {
544− homeDir = os.Getenv("USERPROFILE") // For Windows compatibility
547+ return u.HomeDir
545 }
548 }
546− if homeDir == "" {
547− homeDir = os.Getenv("HOMEPATH") // Fallback for some environments
548− }
549− return homeDir
549+ return cmp.Or(
550+ os.Getenv("HOME"),
551+ os.Getenv("USERPROFILE"),
552+ os.Getenv("HOMEPATH"),
553+ )
550−}
554+})
internal/fsext/ls.go+51−38
@@ -9,11 +9,12 @@ import (
9 "strings"
9 "strings"
10
10
11 "github.com/charlievieth/fastwalk"
11 "github.com/charlievieth/fastwalk"
12+ "github.com/charmbracelet/crush/internal/csync"
12 ignore "github.com/sabhiram/go-gitignore"
13 ignore "github.com/sabhiram/go-gitignore"
13)
14)
14
15
15−// CommonIgnorePatterns contains commonly ignored files and directories
16+// commonIgnorePatterns contains commonly ignored files and directories
16−var CommonIgnorePatterns = []string{
17+var commonIgnorePatterns = ignore.CompileIgnoreLines(
17 // Version control
18 // Version control
18 ".git",
19 ".git",
19 ".svn",
20 ".svn",
@@ -68,62 +69,74 @@ var CommonIgnorePatterns = []string{
68
69
69 // Crush
70 // Crush
70 ".crush",
71 ".crush",
71−}
72+)
72
73
73−type DirectoryLister struct {
74+type directoryLister struct {
74− ignores *ignore.GitIgnore
75+ ignores *csync.Map[string, ignore.IgnoreParser]
75 rootPath string
76 rootPath string
76}
77}
77
78
78−func NewDirectoryLister(rootPath string) *DirectoryLister {
79+func NewDirectoryLister(rootPath string) *directoryLister {
79− dl := &DirectoryLister{
80+ return &directoryLister{
80 rootPath: rootPath,
81 rootPath: rootPath,
82+ ignores: csync.NewMap[string, ignore.IgnoreParser](),
81 }
83 }
82−
83− dl.ignores = ignore.CompileIgnoreLines(append(CommonIgnorePatterns, strings.Split(parseIgnores(rootPath), "\n")...)...)
84−
85− return dl
86}
84}
87
85
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 {
109 relPath, err := filepath.Rel(dl.rootPath, path)
87 relPath, err := filepath.Rel(dl.rootPath, path)
110 if err != nil {
88 if err != nil {
111 relPath = path
89 relPath = path
112 }
90 }
113
91
114− if dl.ignores.MatchesPath(relPath) {
115− return true
116− }
117−
118 base := filepath.Base(path)
92 base := filepath.Base(path)
119−
120 for _, pattern := range ignorePatterns {
93 for _, pattern := range ignorePatterns {
121 matched, err := filepath.Match(pattern, base)
94 matched, err := filepath.Match(pattern, base)
122 if err == nil && matched {
95 if err == nil && matched {
96+ slog.Info("ignoring path", "path", path)
123 return true
97 return true
124 }
98 }
125 }
99 }
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
126− return false
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+ })
127}
140}
128
141
129// ListDirectory lists files and directories in the specified path,
142// ListDirectory lists files and directories in the specified path,