Files
BakNRet/docs/adr/0009-prefix-completion-is-single-level.md
T
Shuery 2446c7c5c7 docs: 把"前缀补全只搜一层、且不提供深度开关"写成决策记录
深度递归不是"补上一个没实现的功能",而是引入一个具体的错。写成 ADR-0009 连同实测数据:26 条真实 Path 里 19 条直接命中、3 条补全救不了(软件没装)、1 条(%UserProfile%\fnm)在 5 层内会命中 AppData\Local\fnm_multishells 这个临时目录 —— 静默备份错的东西还报成功。

同时在 Find-BakNRetChildDirectoryByName 的注释里指回 ADR,免得下一个人把它当"漏了的功能"补回去。

顺带修掉 README 里一句与实现不符的话:原先写"前缀补全命中多个候选(同名目录分散在多处)",而只搜一层时多个候选只可能来自同一个父目录(既有 X 又有 X_后缀)。

全仓复查:MaxDepth / CatalogMaxDepth / 最大深度 / 向下找几层 除 ADR-0009 的历史叙述外 0 处。

验收:test.ps1 9/9 全绿(7 与 5.1)。
2026-09-27 11:34:34 +08:00

2.6 KiB
Raw Blame History

前缀补全只搜一层,而且不提供"向下找几层"的开关

名录里的 Path 按字面不存在时,会做一次前缀补全:在同一父目录下找 <名>_<后缀> 或 <名>-<后缀> 形式的目录。这个过程只看直接子目录,并且刻意没有"最多向下几层"的参数。

历史上确实有过这样一个参数:CatalogMaxDepth(出厂值 5),从配置一路传到 Resolve-BakNRetEntry → Get-BakNRetItemArchiveName → Get-BakNRetSoftwareCatalog → Find-BakNRetChildDirectoryByName。但它从未被使用 —— 最后一个函数接下了参数, 实现里却只有 Get-ChildItem -Directory。也就是说那个配置项写多少都不影响行为。

2026-09 的改造把整条链路删掉了(配置项、三个函数的参数、70 处调用点实参)。理由不只是 "文档不该承诺代码不做的事",还有一条拿真实名录量出来的发现。

实测:让深度生效会让一个条目备份错目录

拿真实的 SoftwareCatalog.psd1(26 条 Path)逐条试"按字面存在吗 / 1 层能救吗 / 5 层能救吗":

情况 条数 说明
直接命中 19 ——
补全救不了,无论几层 3 AutoDarkMode / dsh-desktop / twinkle-tray:软件没装,报"源不存在"是对的
深度生效后会命中错的东西 1 %UserProfile%\fnm:1 层内 0 个,5 层内命中 AppData\Local\fnm_multishells

fnm_multishells 是 fnm 做 shell 集成用的临时目录,不是它的数据目录。所以如果那个参数 真的生效,FastNodeManager 会静默备份错的东西并报告成功;而今天它的行为是明确报 "源不存在" —— 一个可见的失败,胜过一个不可见的成功。

根因:那条判据只在"同一个父目录下"才成立

补全的判据是"下一个字符是 _ 或 -"(^<名>(_|-))。原作者在注释里写明了它的用意 —— "避免把 Legendary 匹配成 LegendarySomething"。这个边界在同一层是保守的,但一旦允许 向下递归,它就会命中任意深度的"同名亲戚":fnm 与 AppData\Local\fnm_multishells 之间 隔着两层目录,判据对此毫无抵抗力。

所以

不要重新加回深度参数。 如果将来确实需要"路径变了也能找到",正确的顺序是先把判据收紧 (例如要求后缀像版本号:^<名>[-_]\d),并且把每次补全的事实打印出来 ("我把 X 解析成了 Y"),让人看见它猜了什么 —— 而不是扩大搜索范围,因为搜索范围越大, 这种启发式越容易命中看起来对、其实不对的东西。