扩展点机制
声明与实现分离
一条贡献分两半:
| 声明(decl) | 实现(impl) | |
|---|---|---|
| 是什么 | manifest 的 contributes["<贡献键>"] 里那条纯数据 | 激活后由代码绑定的函数 / 组件 / 入口 |
| 什么时候注册 | 装载时——一行插件代码都不跑 | 激活事件触发时(可能是几小时后) |
| 有什么用 | 可发现、可校验、可显示(命令面板能列出未激活插件的命令) | 真的有东西可被调用 |
这是「装 50 个插件不拖慢启动」的全部原因,也是 VS Code 生态的基石设计。T0 插件只有声明;T1/T2 插件 = 声明(懒激活)+ 实现。
宿主的 PluginRegistry 按贡献键收集、校验、分派声明。每个扩展点的 decl 校验发生在它的 owner 进程(UI 点在渲染进程、媒体点在媒体进程),问题聚合回插件列表逐条展示。
协议只增不改
manifest 与扩展点 decl 发布后只能追加可选字段。改类型、改必填、删字段都是破坏性变更,只能新开扩展点 id + 走弃用周期。这条对你也是保护:今天写的插件不会被明天的宿主版本悄悄弄坏。
同理,插件 id 是不可变主键:卡的依赖按它写、设置与存储按它建键,发布后改名等于发一个新插件。
插件也可以开自己的插座
manifest 的 extensionPoints 字段可以声明 <你的插件id>.* 扩展点,别的插件就能往里插贡献——生态长在生态上。声明贡献是 T0 就能做的;要对贡献做更强的校验,由你的 T2 侧承担。
该开扩展点,还是走别的路?
三种东西经常被混为一谈:
| 是什么 | 谁实现 | |
|---|---|---|
扩展点贡献(contributes) | 插件提供能力给宿主装配 | 你的插件 |
capability:invoke | 插件调用别的插件注册的能力 | 另一个插件 |
| suite 桥 op | T1 页面调用宿主的特权能力 | 宿主(封闭白名单) |
判断顺序:要给宿主装东西 → 贡献;要用别家的能力(出图 / 配音 / 起浏览器)→ 在 permissions 里声明 capability:invoke 并逐条点名能力 id;要读 app 已加载的数据(素材清单 / 条目 / 台账)→ 查 suite 桥 op 白名单,表里没有的数据面可以向官方提需求——判据是「app 本来就已加载 / 已授权的数据都该让 T1 读到」。
白名单永远不收「通用通道」
readFile(任意路径)、exec、invoke(任意通道) 这类通用逃逸口永不进 T1 白名单——一开,T1 就等于「没有隔离的 T2」。op 必须是具体语义。
这个需求能不能做成插件(判断法)
① 它要维护一份平行的真相吗(时间轴 / 播放时钟 / 显存)?
是 → 内核红线,改成「提议 + 内核执行」的形态
否 ↓
② 它要的数据在 suite 桥白名单里吗?
在 → 现在就能做(T1)
不在 ↓
③ 它能靠自己的 T2 算出来吗(读文件 / 跑命令 / 调 API)?
能 → 现在就能做(T2 + rpc)
不能 → 该向官方提「开新扩展点 / 新 op」的需求,不是被禁止
大多数需求落在 ②③,现在就能做。