mcpp.toml 自动补全 — 重做方案(#4 )
状态:方案待 review,确认后动工。背景:#4 首轮实现(手写字段表)被 review 打回,本方案为重做稿。
1. 范围
做
不做
段头结构建议([package]、[targets.<name>] 等 snippet)
静态字段键/枚举值(standard、kind 等——等上游版本化 schema,接口已预留)
依赖包名 + 版本候选(动态数据)
诊断 / hover / 跳转
依赖/feature 写法模板(snippet)
市场中心 UI(二期,复用同一缓存层)
2. 架构
四层,单向依赖,跨层只传纯数据:
extension.ts 唯一 vscode 依赖:注册 provider、设置门、1:1 映射
↓
completion 查询层 compute(context) → 建议(每条带显式替换范围)
↓ ↓
parser+语义层 index 数据层
容错 TOML 解析 包名/版本候选
2.1 parser 层
自写容错 TOML 解析:未闭合输入容错([dep、simd = { flags = [ { )、带 token 行列位置、contextAt(line, ch) 返回光标上下文(段路径 / 键路径 / 键位或值位 / 替换范围)。
库选型已实测(2026-08,沙盒验证):
toml-eslint-parser:对未闭合段头/字符串/内联表、残缺键全部直接抛异常 ——它的错误恢复面向「完整但不合法」的 lint 场景,不面向补全时的半成品输入。排除。
@taplo/lib:lint 能容错并返回带 range 的错误,但 JS 绑定只暴露 lint/format/encode/decode,不暴露带位置的 AST ,回答不了「光标在哪个段哪个键」。排除。
结论:补全需要的是「光标上下文 + 容错位置」而非完整 TOML 一致性,自写范围收敛(段/键值/数组/内联表/字符串/注释),工程量可控且全程测试覆盖。
结构性修复 review 问题 2(key/value 无替换范围)与问题 3(内嵌 flags 分支不可达)。
2.2 语义层
段归属规范化、条件段规则(如 [target.<sel>.build] 只接受 build inputs——修复 review 问题 1)。
规则从 mcpp 源码(src/manifest/toml.cppm)提取,注释带出处文件 + 行号 + commit hash,升级时 git diff 对照同步。
2.3 index 数据层
扩展激活时后台对每个描述符执行 mcpp xpkg parse <file> --json(官方解析器,零漂移),结果缓存到扩展存储;每次补全只读缓存。
只读性已实测 (mcpp 2026.7.27.1):对 index 缓存内和外部目录的描述符分别执行 xpkg parse --json,前后对比 index 仓库 git 状态与 .xlings-index-cache.json mtime 均无变化——该命令直接解析单文件,不经过索引加载/缓存重建路径。考虑其重要性(见 §7),仍请上游把只读性确认为契约。
缓存键 = index 仓库的 git 状态 (.git/FETCH_HEAD mtime 或 HEAD commit hash),不用目录 mtime——目录 mtime 只在直接子项增删时变化,git pull 更新深层已有描述符时祖先目录 mtime 不动,会静默陈旧。项目级 path 索引通常没有 .git,fallback 为描述符集合的内容 hash(或 max mtime)。
已在真实索引(mcpplibs/mcpp-index,81 包 / 16 命名空间 / 122 版本)验证输出结构:identity + versions(按 OS)+ targets + unknown_keys。
降级行为:mcpp 二进制缺失、xpkg parse 失败或输出缺字段时,该数据源静默缺席,段头/模板等结构建议照常——任何数据层故障都不影响结构层。
未受信任工作区不 spawn 外部进程,静默降级为纯结构建议。
2.4 跨平台
mcpp home 定位按 src/home.cppm 的顺序:$MCPP_HOME > 二进制自包含布局 > ~/.mcpp(Windows 为 %USERPROFILE%\.mcpp)。自包含布局的判定:二进制位于 <dir>/bin/mcpp,且祖先路径 不含 target/(mcpp 源码开发构建产物)或 data/xpkgs/(xlings 包安装)——该检查只看二进制的上级目录,用户项目产生的 target/ 在项目内、不影响判定。扩展侧补充一条检查:PATH 上的 mcpp 可能是 xlings shim(符号链接追到调度器而非真实二进制),因此自包含分支额外要求 <dir>/registry 实际存在,否则落到默认 home。
index 路径 <home>/registry/data/mcpplibs/(src/xlings.cppm 硬编码)。
spawn mcpp 复用扩展现有 src/process.ts(Windows mcpp.exe)。
版本候选取 linux/macosx/windows 三平台并集(manifest 可能交叉构建),semver 倒序。
parser 兼容 CRLF;fixture 路径全部 path.join。
3. 数据源路径(均有源码依据)
~/.mcpp/registry/data/mcpplibs/pkgs/**/*.lua ← 库索引(唯一正确来源)
明确排除:
xim-pkgindex——xlings 工具链索引;
xim-index-repos/*——xlings 教学仓,扫错会建议出 mcpp 解析不了的包。
v1 只读默认 mcpplibs + 项目级 [indices] 的 path 索引;全局 config.toml 的 [indices] 语义实现时对照 src/pm/index_management.cppm。
4. 索引陈旧处理
扩展只读不刷新 (网络操作 + 工作区信任边界 + MCPP_OFFLINE 语义)。
候选 detail 显示索引年龄(.git/FETCH_HEAD mtime)。
超阈值追加提示「可运行 mcpp index update」,不弹窗、不打断。
MCPP_OFFLINE=1 或未受信任工作区:不提示。
设计立场:旧索引是 mcpp 的合法状态(caret 约束本就按本地已知版本求解),候选 = mcpp 实际会解析的结果,「与 mcpp 所见一致」优先于「新」 。
5. 设置
设置
类型
默认
说明
mcpp.tomlCompletion
boolean
true(建议改为默认开启)
补全总开关。范围已收窄为「结构 + 与 mcpp 所见一致的动态数据」,不存在生成无效配置的风险,「默认关闭」不再必要(待 wellwei 确认)
mcpp.tomlCompletionIndexStaleDays
number
30
索引超过 N 天未更新时提示;0 = 关闭提示
6. 测试
纯函数单测(parser / 语义 / completion / index fixture):node --test,零 vscode 依赖。
替换范围、部分输入(default-p、"c++2)不破坏文本有显式断言(review 问题 5)。
契约测试:段头 / 条件段规则生成最小 manifest 喂真实 mcpp,断言无 unsupported 诊断;无 mcpp 的环境 skip。硬承诺:语义层每条规则必须有对应契约测试 ——语义层仍是人工同步(漂移变慢但不是零),契约测试保证漂移发生时是测试红,而不是用户补全出错。
index 层 fixture 可用真实索引描述符(~/ln/code/test_mcpp/mcpp-index)做样本。
7. 待确认
@wellwei
段头 snippet + 写法模板是否保留?(我的立场:它们描述语法结构而非字段语义,属于允许手写的语义层)
mcpp.tomlCompletion 建议默认开启 (范围收窄后「默认关闭」已无必要),indexStaleDays 新设置是否接受?
重做方式:feat: add opt-in code completion for mcpp.toml #4 上 force-push 还是开新 PR?契约测试策略:本地强 CI 弱,还是 CI 钉版安装 mcpp?
@Sunrisepeak
依赖补全数据走 xpkg parse --json。建议给 --json 输出加一个 format/schema 版本字段 :扩展对未知版本降级(只用结构建议)而非解析出错——把「输出结构变动请提前告知」这种单向通知变成机器可判定的契约,对上游只是一个字段的成本。
请确认 xpkg parse --json 无写副作用 (不写 index cache / 不动磁盘状态)并可作为契约依赖。本机实测(2026.7.27.1)当前无写副作用,希望上游将其固定为保障。说明调用节奏:扩展不做高频调用 ——补全只读缓存,xpkg parse 仅在索引 git HEAD 变化后后台批量执行一次(典型触发:mcpp index update,或 mcpp add 添加本地索引中不存在的包时——cmd_add 源码确认仅本地未命中且归共享 registry 时才刷新)。
8. 演进预留
上游版本化 manifest schema 落地后:数据层加 fetchManifestSchema(),查询层加 staticFieldProvider,其余不动。
市场中心二期:复用 index 缓存层。
远期可迁移为独立 LSP:parser + completion 原样搬进程,只重写协议胶水。
mcpp.toml 自动补全 — 重做方案(#4)
1. 范围
[package]、[targets.<name>]等 snippet)standard、kind等——等上游版本化 schema,接口已预留)2. 架构
四层,单向依赖,跨层只传纯数据:
2.1 parser 层
[dep、simd = { flags = [ {)、带 token 行列位置、contextAt(line, ch)返回光标上下文(段路径 / 键路径 / 键位或值位 / 替换范围)。toml-eslint-parser:对未闭合段头/字符串/内联表、残缺键全部直接抛异常——它的错误恢复面向「完整但不合法」的 lint 场景,不面向补全时的半成品输入。排除。@taplo/lib:lint能容错并返回带 range 的错误,但 JS 绑定只暴露 lint/format/encode/decode,不暴露带位置的 AST,回答不了「光标在哪个段哪个键」。排除。2.2 语义层
[target.<sel>.build]只接受 build inputs——修复 review 问题 1)。src/manifest/toml.cppm)提取,注释带出处文件 + 行号 + commit hash,升级时git diff对照同步。2.3 index 数据层
mcpp xpkg parse <file> --json(官方解析器,零漂移),结果缓存到扩展存储;每次补全只读缓存。xpkg parse --json,前后对比 index 仓库 git 状态与.xlings-index-cache.jsonmtime 均无变化——该命令直接解析单文件,不经过索引加载/缓存重建路径。考虑其重要性(见 §7),仍请上游把只读性确认为契约。.git/FETCH_HEADmtime 或HEADcommit hash),不用目录 mtime——目录 mtime 只在直接子项增删时变化,git pull更新深层已有描述符时祖先目录 mtime 不动,会静默陈旧。项目级 path 索引通常没有.git,fallback 为描述符集合的内容 hash(或 max mtime)。xpkg parse失败或输出缺字段时,该数据源静默缺席,段头/模板等结构建议照常——任何数据层故障都不影响结构层。2.4 跨平台
src/home.cppm的顺序:$MCPP_HOME> 二进制自包含布局 >~/.mcpp(Windows 为%USERPROFILE%\.mcpp)。自包含布局的判定:二进制位于<dir>/bin/mcpp,且祖先路径不含target/(mcpp 源码开发构建产物)或data/xpkgs/(xlings 包安装)——该检查只看二进制的上级目录,用户项目产生的target/在项目内、不影响判定。扩展侧补充一条检查:PATH 上的mcpp可能是 xlings shim(符号链接追到调度器而非真实二进制),因此自包含分支额外要求<dir>/registry实际存在,否则落到默认 home。<home>/registry/data/mcpplibs/(src/xlings.cppm硬编码)。src/process.ts(Windowsmcpp.exe)。path.join。3. 数据源路径(均有源码依据)
明确排除:
xim-pkgindex——xlings 工具链索引;xim-index-repos/*——xlings 教学仓,扫错会建议出 mcpp 解析不了的包。v1 只读默认 mcpplibs + 项目级
[indices]的 path 索引;全局config.toml的[indices]语义实现时对照src/pm/index_management.cppm。4. 索引陈旧处理
MCPP_OFFLINE语义)。.git/FETCH_HEADmtime)。mcpp index update」,不弹窗、不打断。MCPP_OFFLINE=1或未受信任工作区:不提示。5. 设置
mcpp.tomlCompletiontrue(建议改为默认开启)mcpp.tomlCompletionIndexStaleDays300= 关闭提示6. 测试
default-p、"c++2)不破坏文本有显式断言(review 问题 5)。~/ln/code/test_mcpp/mcpp-index)做样本。7. 待确认
@wellwei
mcpp.tomlCompletion建议默认开启(范围收窄后「默认关闭」已无必要),indexStaleDays新设置是否接受?@Sunrisepeak
xpkg parse --json。建议给--json输出加一个 format/schema 版本字段:扩展对未知版本降级(只用结构建议)而非解析出错——把「输出结构变动请提前告知」这种单向通知变成机器可判定的契约,对上游只是一个字段的成本。xpkg parse --json无写副作用(不写 index cache / 不动磁盘状态)并可作为契约依赖。本机实测(2026.7.27.1)当前无写副作用,希望上游将其固定为保障。说明调用节奏:扩展不做高频调用——补全只读缓存,xpkg parse仅在索引 git HEAD 变化后后台批量执行一次(典型触发:mcpp index update,或mcpp add添加本地索引中不存在的包时——cmd_add源码确认仅本地未命中且归共享 registry 时才刷新)。8. 演进预留
fetchManifestSchema(),查询层加staticFieldProvider,其余不动。