diff --git a/cmd/run.go b/cmd/run.go index ab19931..f5a80b3 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -10,7 +10,6 @@ import ( "cliprepo/internal/config" "cliprepo/internal/resolver" "cliprepo/internal/tree" - "cliprepo/internal/utils" "github.com/atotto/clipboard" "github.com/spf13/cobra" @@ -49,49 +48,15 @@ var runCmd = &cobra.Command{ } patterns := []string(raw) - var files []string for _, pattern := range patterns { - - // ========================= - // FULL REPO SCAN - // ========================= - if pattern == "*" { - all, err := r.WalkAll() - if err != nil { - return err - } - files = append(files, all...) - continue - } - - // ========================= - // recursive glob support - // ========================= - if strings.Contains(pattern, "**") { - matches, err := utils.ExpandGlob([]string{pattern}) - if err != nil { - return err - } - - for _, m := range matches { - r.AddFile(m, &files) - } - continue - } - - // ========================= - // normal glob - // ========================= - matches, err := utils.ExpandGlob([]string{pattern}) + // 统一交给 resolver 处理,无论是 "*", "cmd/*" 还是 "**/*.go" + matches, err := r.ResolvePattern(pattern) if err != nil { return err } - - for _, m := range matches { - r.AddFile(m, &files) - } + files = append(files, matches...) } // 3. dedup + stable order @@ -106,13 +71,11 @@ var runCmd = &cobra.Command{ // 5. render for _, file := range files { - content, err := os.ReadFile(file) if err != nil { continue } - // 先初始化 filePath 变量,避免作用域及同名包冲突 filePath := file if cfg.Config.PathMode == "absolute" { abs, _ := filepath.Abs(file) diff --git a/internal/resolver/resolver.go b/internal/resolver/resolver.go index 4f9ace8..6788c03 100644 --- a/internal/resolver/resolver.go +++ b/internal/resolver/resolver.go @@ -18,7 +18,59 @@ func New(root string) *Resolver { } } -// WalkAll 遍历所有文件,并自动跳过 .git 和 .gitignore 中的文件 +// ResolvePattern 智能解析各种 pattern 并过滤 ignore 文件 +func (r *Resolver) ResolvePattern(pattern string) ([]string, error) { + if pattern == "*" { + return r.WalkAll() + } + + var matches []string + // 处理类似 cmd/* 或 internal/tree/* 的情况 + // 如果包含 * 且不是以 ** 开头,转换为通过 filepath.Walk 匹配或直接 Glob 过滤 + rawMatches, err := filepath.Glob(pattern) + if err != nil { + return nil, err + } + + ig, _ := tree.Load(r.root) + + for _, m := range rawMatches { + info, err := os.Stat(m) + if err != nil { + continue + } + + rel, _ := filepath.Rel(r.root, m) + // 统一检查 .gitignore + if ig != nil && ig.MatchesPath(rel) { + continue + } + + if info.IsDir() { + // 如果匹配到的是目录(比如 internal/* 匹配到了 internal/config),则深层遍历该目录 + err := filepath.Walk(m, func(path string, fInfo os.FileInfo, walkErr error) error { + if walkErr != nil || fInfo.IsDir() { + return nil + } + fRel, _ := filepath.Rel(r.root, path) + if ig != nil && ig.MatchesPath(fRel) { + return nil + } + r.add(fRel, &matches) + return nil + }) + if err != nil { + return nil, err + } + } else { + r.add(rel, &matches) + } + } + + return matches, nil +} + +// WalkAll 遍历全库并过滤 func (r *Resolver) WalkAll() ([]string, error) { var result []string ig, _ := tree.Load(r.root) @@ -28,14 +80,12 @@ func (r *Resolver) WalkAll() ([]string, error) { return nil } - // 显式跳过 .git 目录 if info.IsDir() && info.Name() == ".git" { return filepath.SkipDir } rel, _ := filepath.Rel(r.root, path) - // 遵循 .gitignore 规则 if ig != nil && ig.MatchesPath(rel) { if info.IsDir() { return filepath.SkipDir @@ -66,7 +116,6 @@ func (r *Resolver) add(path string, result *[]string) { *result = append(*result, path) } -// Unique 保留此方法供 run.go 调用 func (r *Resolver) Unique(in []string) []string { seen := make(map[string]struct{}) out := make([]string, 0, len(in))