Skip to content

design: mcpp.toml 补全重做方案 —— 语义模块 + index 动态数据(#4 重做) #8

Description

@Ximiaw

mcpp.toml 自动补全 — 重做方案(#4

状态:方案待 review,确认后动工。背景:#4 首轮实现(手写字段表)被 review 打回,本方案为重做稿。

1. 范围

不做
段头结构建议([package][targets.<name>] 等 snippet) 静态字段键/枚举值(standardkind 等——等上游版本化 schema,接口已预留)
依赖包名 + 版本候选(动态数据) 诊断 / hover / 跳转
依赖/feature 写法模板(snippet) 市场中心 UI(二期,复用同一缓存层)

2. 架构

四层,单向依赖,跨层只传纯数据:

extension.ts        唯一 vscode 依赖:注册 provider、设置门、1:1 映射
  ↓
completion 查询层   compute(context) → 建议(每条带显式替换范围)
  ↓                ↓
parser+语义层       index 数据层
容错 TOML 解析      包名/版本候选

2.1 parser 层

  • 自写容错 TOML 解析:未闭合输入容错([depsimd = { flags = [ { )、带 token 行列位置、contextAt(line, ch) 返回光标上下文(段路径 / 键路径 / 键位或值位 / 替换范围)。
  • 库选型已实测(2026-08,沙盒验证):
    • toml-eslint-parser:对未闭合段头/字符串/内联表、残缺键全部直接抛异常——它的错误恢复面向「完整但不合法」的 lint 场景,不面向补全时的半成品输入。排除。
    • @taplo/liblint 能容错并返回带 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

  1. 段头 snippet + 写法模板是否保留?(我的立场:它们描述语法结构而非字段语义,属于允许手写的语义层)
  2. mcpp.tomlCompletion 建议默认开启(范围收窄后「默认关闭」已无必要),indexStaleDays 新设置是否接受?
  3. 重做方式:feat: add opt-in code completion for mcpp.toml #4 上 force-push 还是开新 PR?契约测试策略:本地强 CI 弱,还是 CI 钉版安装 mcpp?

@Sunrisepeak

  1. 依赖补全数据走 xpkg parse --json。建议给 --json 输出加一个 format/schema 版本字段:扩展对未知版本降级(只用结构建议)而非解析出错——把「输出结构变动请提前告知」这种单向通知变成机器可判定的契约,对上游只是一个字段的成本。
  2. 请确认 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 原样搬进程,只重写协议胶水。

Metadata

Metadata

Labels

documentationImprovements or additions to documentationenhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions