编写代码后,检测并清理三个层面的重复——复制粘贴块、跨包共享代码、不必要的包装器以及概念层面的单一真实来源(SoT)违规。使用 indexion 检测、修复并验证。
indexion refactor — 代码库重构
使用 indexion 的分析命令检测并消除文本、结构和概念三个层面的重复,然后验证 SoT 是否得到执行。
何时使用
- 添加新抽象(类型、模块、API 层)后
- 引入新文件格式或 I/O 边界后
- 修复需要因同一原因修改 3 个以上文件时
- 添加了“防护”或“跳过”以解决结构性问题时
- 当
opendir、ENOENT或类似文件系统错误从意外路径出现时 - 跨包提取共享代码时
- 重构后清理(移除琐碎的包装函数)时
- 定期对代码库进行 SoT 健康检查
三个层面的重复
| 层面 | 定义 | 工具 | 示例 |
|---|---|---|---|
| 文本 | 复制粘贴的代码块、相同的函数 | plan refactor |
is_whitespace 在 5 个模块中复制 |
| 结构 | 相同逻辑结构但不同名称 | plan solid、plan unwrap |
跨包提取候选、琐碎包装器 |
| 概念 | 同一领域概念独立实现 | explore + 手动分析 |
三个模块各自判断“这是归档文件吗?” |
文本重复容易发现和修复。概念重复最难且最危险——它不会产生复制粘贴匹配,但意味着更改一个概念需要更新每个分散的实现。
工作流程
阶段 1:清除文本重复(plan refactor)
从高置信度匹配开始,逐步降低。
# 步骤 1:查找 90% 以上的重复(高置信度)
indexion plan refactor --threshold=0.9 \
--include='*.mbt' --exclude='*_wbtest.mbt' \
--exclude='*moon.pkg*' --exclude='*pkg.generated*' \
cmd/indexion/
indexion plan refactor --threshold=0.9 \
--include='*.mbt' --exclude='*_wbtest.mbt' \
--exclude='*moon.pkg*' --exclude='*pkg.generated*' \
src/
将输出分为三部分阅读:
| 部分 | 发现内容 | 操作 |
|---|---|---|
| 相似文件 | 整体相似度高的文件 | 调查是否可结构合并 |
| 重复代码块 | 文件间行级相同代码 | 提取到 @common 或共享模块 |
| 函数级重复 | 结构相似的函数(基于 TF-IDF 对函数体) | 统一为单个 SoT 函数 |
同文件重复(同一文件内 90% 以上相似的函数)是最高价值目标——最容易修复,收益最明显。例如:get_global_data_dir 和 get_global_cache_dir 共享 95% 的结构,提取为 resolve_os_dir。
# 步骤 2:在合并前使用 grep 追踪引用
indexion grep "TypeIdent:TfidfEmbeddingProvider" src/
indexion grep --semantic=name:is_whitespace src/
# 步骤 3:修复,然后重新运行以确认重复已消除
indexion plan refactor --threshold=0.9 --include='*.mbt' ...
# 步骤 4:降低阈值并迭代
indexion plan refactor --threshold=0.85 --include='*.mbt' ...
plan refactor 选项:
| 选项 | 默认值 | 描述 |
|---|---|---|
--threshold=FLOAT |
0.7 | 最小相似度阈值 |
--strategy=NAME |
hybrid | 相似度算法:hybrid、tfidf、bm25、jsd、ncd |
--fdr=FLOAT |
0 | FDR 校正(0=禁用) |
--style=STYLE |
raw | 输出风格:raw、structured |
--format=FORMAT |
md | 输出格式:md、json、text、github-issue |
--name=NAME |
-- | 项目名称(用于 structured 风格) |
--include=PATTERN |
-- | 包含模式(可重复) |
--exclude=PATTERN |
-- | 排除模式(可重复) |
-o, --output=FILE |
stdout | 输出文件路径 |
--specs-dir=DIR |
kgfs | KGF 规范目录 |
清理后保留的内容(停止信号):
- 平台桩(
native.mbt/stub.mbt)——有意的平台分支 - 类型方法相似性(不同类型上的
to_string)——不同类型,相同模式 - CLI 命令样板代码(
command()函数)——@argparse API 模式,非重复 - 语义不同但结构相似的函数(
is_disqualifying_keywordvsis_skip_token)——不同目的
阶段 2:提取跨包共享代码(plan solid)
在清理每个目录内部后,查找应在包间共享的代码。
# 查找两个包之间的重叠
indexion plan solid --from=src/a,src/b
# 指定提取目标
indexion plan solid --from=src/a,src/b --to=src/common
# 使用树编辑距离进行精确的函数级匹配
indexion plan solid --from=src/a,src/b --strategy=apted
# 更高阈值以获得更严格的匹配
indexion plan solid --from=src/a,src/b --threshold=0.95
# 过滤文件
indexion plan solid --from=src/a,src/b --include='*.mbt' --exclude='*_test.mbt'
plan solid 与 plan refactor 的区别:
plan refactor |
plan solid |
|
|---|---|---|
| 范围 | 目录内部重复 | 跨目录重叠 |
| 目标 | 在代码库内合并 | 提取共享代码到新包 |
| 输入 | <path> |
--from=dirA,dirB |
plan solid 选项:
| 选项 | 默认值 | 描述 |
|---|---|---|
--from=DIRS |
(必需) | 源目录(逗号分隔或可重复) |
--to=DIR |
-- | 提取目标目录 |
--rules=FILE |
-- | 规则文件(.solidrc) |
--rule=RULE |
-- | 内联规则(可重复) |
--threshold=FLOAT |
0.9 | 最小相似度阈值 |
--strategy=NAME |
tfidf | 相似度算法:tfidf、apted、tsed |
--include=PATTERN |
-- | 包含模式(可重复) |
--exclude=PATTERN |
-- | 排除模式(可重复) |
--format=FORMAT |
md | 输出格式:md、json、github-issue |
-o, --output=FILE |
stdout | 输出文件路径 |
--specs-dir=DIR |
kgfs | KGF 规范目录 |
工作流程:
- 先对每个目录单独运行
plan refactor清理内部重复 - 运行
plan solid --from=dirA,dirB查找跨目录提取候选 - 按照计划建议提取共享代码
- 使用
indexion grep "TypeIdent:SharedType"验证所有引用已更新
阶段 3:移除不必要的包装器(plan unwrap)
合并后,清理那些增加间接性而无价值的琐碎委托函数。
# 步骤 1:快速检查
indexion grep --semantic=proxy src/
# 步骤 2:详细报告
indexion plan unwrap --include='*.mbt' --exclude='*_wbtest.mbt' \
--exclude='*moon.pkg*' --exclude='*pkg.generated*' src/
# 步骤 3:预览更改(安全——不修改文件)
indexion plan unwrap --dry-run --include='*.mbt' --exclude='*_wbtest.mbt' \
--exclude='*moon.pkg*' --exclude='*pkg.generated*' src/
# 步骤 4:应用修复
indexion plan unwrap --fix --include='*.mbt' --exclude='*_wbtest.mbt' \
--exclude='*moon.pkg*' --exclude='*pkg.generated*' src/
# 步骤 5:运行测试
moon test --target native
检测内容: 函数体为单个函数调用且所有参数作为简单标识符转发——无控制流、无转换。
// 检测到(默认)——琐碎委托
fn matches_pattern(text : String, pat : String) -> Bool {
@glob.glob_match(text, pat)
}
// 默认排除(使用 --all 包含)
fn length(self : MyList) -> Int {
self.items.length() // 自委托(封装)
}
fn emit(value : String) -> Action {
Emit(value) // 裸构造函数
}
plan unwrap 模式:
| 模式 | 标志 | 描述 |
|---|---|---|
| 报告 | (默认) | 列出找到的包装器 |
| 预览 | --dry-run |
显示所有编辑而不修改文件 |
| 修复 | --fix |
将编辑应用到文件 |
plan unwrap 选项:
| 选项 | 默认值 | 描述 |
|---|---|---|
--dry-run |
-- | 预览编辑 |
--fix |
-- | 应用编辑 |
--all |
-- | 包含自委托和裸构造函数包装器 |
--include-self |
-- | 包含 self.field.method 模式 |
--include-bare |
-- | 包含裸构造函数包装器 |
--include=PATTERN |
-- | 包含模式(可重复) |
--exclude=PATTERN |
-- | 排除模式(可重复) |
--format=FORMAT |
md | 输出格式:md、json、text |
-o, --output=FILE |
stdout | 输出文件路径 |
--specs-dir=DIR |
kgfs | KGF 规范目录 |
移除前审查:
- 平台包装器(FFI、
@osenv_path)是抽象层,而非意外间接性 - 外部包使用的公共 API 包装器——移除它们是破坏性变更
- 始终先执行
--dry-run
阶段 4:检测概念级重复(explore + 分析)
这是最难的层面。文本和结构工具无法发现它,因为代码不同——但概念相同。
# 查找共享词汇的文件(= 在同一概念领域工作)
indexion explore --threshold=0.4 \
--include='*.mbt' --exclude='*_wbtest.mbt' \
--exclude='*moon.pkg*' --exclude='*pkg.generated*' \
src/ cmd/
相似度在 40-60% 且无结构重复的文件是概念邻居——它们使用相同术语,因为处理同一领域。
对于每个高相似度对,问:“它们共享什么概念,谁拥有它?”
# 通过树结构比较检查共享词汇
indexion explore file_a.mbt file_b.mbt --threshold=0 --strategy=apted
概念泄漏的常见模式:
| 症状 | 泄漏的概念 | 修复 |
|---|---|---|
两个文件都调用 is_X(spec) 然后 Y::from_spec(spec) |
“判断是否为 X 并配置 Y” | 将 try_do_X(path, spec) 提取到拥有 X 的模块 |
两个文件在内容已加载时都调用 @fs.read_file_to_string(path) |
“读取文件内容” | 将内容作为参数传递,不要重新读取 |
两个文件都调用 parent_dir(path) 然后 @fs.read_dir(dir) |
“列出同级文件” | 将目录遍历集中到管道中 |
多个 if is_virtual_path(x) { skip } 防护 |
“真实路径 vs 虚拟路径” | 让类型系统阻止虚拟路径到达此处 |
两个文件都调用 buf.write_string("\n"); buf.write_string(x) |
“连接文本条目” | 将 join_text_entries() 提取到拥有模块 |
阶段 5:合并到 SoT
定义概念的模块应该是唯一实现逻辑的模块。
规则:
- 一个概念,一个模块,一个函数。 如果“从归档中提取文本”出现在
vfs.mbt、discover.mbt和args.mbt中,它应仅属于vfs.mbt。 - 调用者接收结果,而非原料。 不要分别导出
is_archive_spec+ArchiveSpec::from_spec+expand_archive。导出try_extract_archive_text(path, spec) -> String?。 - 防护是症状,而非修复。
if is_virtual_path(x) { skip }意味着虚拟路径根本不应到达此处。修复源头,而非接收端。 - 从磁盘重新读取已在内存中的内容是概念泄漏。 如果
SupportedFile.content保存了文本,下游代码不应调用@fs.read_file_to_string(file.path)。
阶段 6:验证
# 确认文本重复已消除
indexion plan refactor --threshold=0.9 \
--include='*.mbt' --exclude='*_wbtest.mbt' \
--exclude='*moon.pkg*' --exclude='*pkg.generated*' \
src/ cmd/indexion/
# 确认概念相似度降低
indexion explore file_a.mbt file_b.mbt --threshold=0
# 确认包装器已清理
indexion plan unwrap --include='*.mbt' --exclude='*_wbtest.mbt' \
--exclude='*moon.pkg*' --exclude='*pkg.generated*' src/
# 运行测试
moon test --target native
SoT 合并后:
- 概念拥有者与其调用者之间的文本相似度降低
- 调用者变得更短(一个 API 调用而非多步逻辑)
- 概念拥有者可能变大,但它是唯一需要修改的地方
阶段 7:通过测试证明不再复发
编写一个从结构上防止旧模式复发的测试:
test "SoT: SupportedFile.path 始终是真实文件系统路径" {
// 创建归档,运行 load_supported_file_info
// 断言:没有路径包含 "!/"
// 断言:每个路径通过 @fs.path_exists
}
该测试不检查行为——它检查SoT 不变性。
危险信号
“我需要在这里添加一个防护”
如果你正在向一个不应接收特殊情况的函数添加 if is_special_case(x) { skip },问题在上游。该函数的调用者绝不应传递该值。
“它能工作,但向 stderr 打印错误”
来自 C 运行时的 stderr 消息(opendir: No such file or directory)意味着无效数据到达了系统调用。catch 吸收了错误,但 perror() 已经打印。唯一的修复是阻止无效数据到达调用。
“我会在每个命令中分别修复”
如果相同的修复在 explore、search、grep、reconcile、plan documentation 中都需要……修复应属于共享管道,而非每个命令。
“相似度只是共享词汇,并非真正的重复”
在不应该共享概念的模块之间出现 40-60% 的 TF-IDF 相似度是一个警告。词汇匹配本身就是信号。
快速参考:何时使用哪个命令
| 问题 | 命令 |
|---|---|
| “哪些文件相似?” | explore --format=list |
| “具体重复了什么?” | plan refactor --threshold=0.9 |
| “包 A 和 B 之间有哪些代码重叠?” | plan solid --from=A,B |
| “哪些函数是琐碎包装器?” | plan unwrap 或 grep --semantic=proxy |
| “这些文件共享什么概念?” | explore file_a file_b --threshold=0 --strategy=apted |
| “重复是否已修复?” | 使用相同阈值重新运行 plan refactor |






