From 9587ac85b7cb875671177460d765f53cd1b4ee0d Mon Sep 17 00:00:00 2001 From: Zayden Jung Date: Fri, 15 May 2026 23:54:10 +0800 Subject: [PATCH] study & make comments --- .DS_Store | Bin 0 -> 6148 bytes internal/bundle/bundle.go | 40 +++++++++++++++++++++++++++++++++++ internal/config/config.go | 16 ++++++++++++++ internal/gitignore/ignore.go | 10 +++++++++ internal/tree/tree.go | 24 +++++++++++++++++++++ 5 files changed, 90 insertions(+) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..429a3d69a2c22c3f2235818c58d923aea4176a94 GIT binary patch literal 6148 zcmeHKOR54v3{2F;fE$-C`wHHm5#I^CfPx}|j37_fvwG+9XjVRmGP-%-Mk z)M7Fb5uKkmGm(jiG;l-tv(Ps?H}BaXGYW*`j+3;qmcwOvYJ1hM6UJ?158khAANhx4 zyNi2Y6Pcv~RDcRl0V+TR{-uERURW~@WTXOAfC}6du6(HuTBpR75ds6QR|7cUpBfs9mu3Op+? zjP1nw{}24l{QpeijtWqLuTnrqi{)aDSIXYndpYa11-^n?%@uBjwNnth9Rs}`V`J@j d>P1mkY>o4p*abQrd8Y&UGhn*VsKBQcxB<7=6{7$E literal 0 HcmV?d00001 diff --git a/internal/bundle/bundle.go b/internal/bundle/bundle.go index 59f43d6..8b99c4d 100644 --- a/internal/bundle/bundle.go +++ b/internal/bundle/bundle.go @@ -11,12 +11,35 @@ import ( const maxFileSize = 1 * 1024 * 1024 // 1MB +// Build 根据 config 中定义的 bundle key,将多个文件内容聚合成一个字符串 +// +// 设计用途: +// 用于 CLI 工具中快速“打包”一组文件内容,例如: +// cliprepo bundle api +// => 输出 api 相关的一组代码文件内容,方便复制/分享/提交 prompt +// +// 行为特点: +// 1. 从 cliprepo.yaml 中读取 bundles 配置 +// 2. 根据 key 找到对应文件列表 +// 3. 逐个读取文件内容并拼接 +// 4. 自动跳过异常文件(不存在 / 读取失败) +// 5. 自动过滤过大的文件(> 1MB) +// +// 输出格式: +// +// ===== path/to/file ===== +// +// +// ===== path/to/another ===== +// func Build(key string) (string, error) { + // 读取配置文件(cliprepo.yaml) cfg, err := config.Load() if err != nil { return "", err } + // 获取对应 bundle 的文件列表 files, ok := cfg.Bundles[key] if !ok { return "", fmt.Errorf("bundle not found: %s", key) @@ -24,22 +47,31 @@ func Build(key string) (string, error) { var builder strings.Builder + // 遍历 bundle 中的所有文件路径 for _, file := range files { + // 获取文件信息(用于判断大小 / 是否存在) info, err := os.Stat(file) if err != nil { + // 文件不存在或无法访问时跳过 continue } + // 跳过过大的文件,避免 CLI 输出过载或内存占用过高 if info.Size() > maxFileSize { continue } + // 读取文件内容 data, err := os.ReadFile(file) if err != nil { + // 读取失败直接跳过,不中断整个 bundle 构建 continue } + // 写入文件分隔标记,便于阅读结构 builder.WriteString(fmt.Sprintf("===== %s =====\n", file)) + + // 写入文件内容 builder.Write(data) builder.WriteString("\n\n") } @@ -47,6 +79,14 @@ func Build(key string) (string, error) { return builder.String(), nil } +// Copy 将 bundle 内容写入系统剪贴板 +// +// 常见使用场景: +// CLI 一键复制 bundle 输出,用于: +// - prompt 输入 +// - 代码审查 +// - 远程分享 +// - AI 上下文输入 func Copy(content string) error { return clipboard.WriteAll(content) } \ No newline at end of file diff --git a/internal/config/config.go b/internal/config/config.go index bf70fa4..3d440fa 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -6,17 +6,33 @@ import ( "gopkg.in/yaml.v3" ) +// Config 定义 cliprepo 的配置结构 +// +// 当前支持的配置: +// Bundles:用于定义一组文件路径集合 +// key -> bundle 名称 +// value -> 对应的文件路径列表 type Config struct { Bundles map[string][]string `yaml:"bundles"` } +// Load 从项目根目录读取 cliprepo.yaml 配置文件,并解析为 Config 结构 +// +// 行为说明: +// 1. 默认读取当前工作目录下的 cliprepo.yaml +// 2. 如果文件不存在或读取失败,直接返回 error +// 3. 使用 YAML 解析配置内容到 Config 结构体 func Load() (*Config, error) { + // 读取配置文件内容 data, err := os.ReadFile("cliprepo.yaml") if err != nil { return nil, err } var cfg Config + + // 解析 YAML 到结构体 err = yaml.Unmarshal(data, &cfg) + return &cfg, err } \ No newline at end of file diff --git a/internal/gitignore/ignore.go b/internal/gitignore/ignore.go index 25e0313..bb12f96 100644 --- a/internal/gitignore/ignore.go +++ b/internal/gitignore/ignore.go @@ -7,12 +7,22 @@ import ( ignore "github.com/sabhiram/go-gitignore" ) +// Load 加载指定 root 目录下的 .gitignore 规则 +// +// 行为说明: +// 1. 如果 root/.gitignore 存在,则解析该文件 +// 2. 如果不存在,则返回一个“空规则集”(不会忽略任何文件) +// 3. 返回 go-gitignore 的 GitIgnore 对象,用于路径匹配 func Load(root string) (*ignore.GitIgnore, error) { + // 拼接 .gitignore 文件路径 path := filepath.Join(root, ".gitignore") + // 如果 .gitignore 不存在,则返回一个空的 ignore 规则 + // 这样后续 MatchesPath 不会 panic,也不会过滤任何文件 if _, err := os.Stat(path); os.IsNotExist(err) { return ignore.CompileIgnoreLines(), nil } + // 如果存在 .gitignore,则解析文件内容生成规则 return ignore.CompileIgnoreFile(path) } \ No newline at end of file diff --git a/internal/tree/tree.go b/internal/tree/tree.go index fe2adc3..d553020 100644 --- a/internal/tree/tree.go +++ b/internal/tree/tree.go @@ -10,23 +10,35 @@ import ( "github.com/atotto/clipboard" ) +// Generate 从 root 目录开始遍历文件树,并生成类似 `tree` 命令的目录结构字符串 +// +// 功能特点: +// 1. 递归遍历目录 +// 2. 支持 .gitignore 规则过滤 +// 3. 输出树形结构字符串 func Generate(root string) (string, error) { + // 加载 gitignore 规则(如果失败则忽略错误,默认不过滤) ig, _ := gitignore.Load(root) var builder strings.Builder + // 递归遍历目录结构 err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { if err != nil { return err } + // 根目录特殊处理,统一显示为 "." if path == root { builder.WriteString(".\n") return nil } + // 转换为相对路径,便于匹配 gitignore 和计算层级 rel, _ := filepath.Rel(root, path) + // 如果路径匹配 gitignore 规则,则跳过 + // 如果是目录,则直接跳过整个子树 if ig.MatchesPath(rel) { if info.IsDir() { return filepath.SkipDir @@ -34,9 +46,14 @@ func Generate(root string) (string, error) { return nil } + // 计算当前节点在目录树中的层级 + // 通过路径分隔符数量判断深度 level := strings.Count(rel, string(os.PathSeparator)) + + // 根据层级生成缩进 prefix := strings.Repeat("│ ", level) + // 写入当前节点(文件或目录) builder.WriteString(fmt.Sprintf("%s├── %s\n", prefix, info.Name())) return nil }) @@ -44,10 +61,17 @@ func Generate(root string) (string, error) { return builder.String(), err } +// Copy 将生成的字符串写入系统剪贴板 +// +// 常用于 CLI 工具:直接复制 tree 输出结果 func Copy(content string) error { return clipboard.WriteAll(content) } +// Write 将内容写入指定文件路径 +// +// 如果文件存在则覆盖 +// 权限默认设置为 0644(rw-r--r--) func Write(path, content string) error { return os.WriteFile(path, []byte(content), 0644) } \ No newline at end of file