VSCode插件无法动态加载snippet,因contributes.snippets仅支持静态JSON路径;需改用CompletionItemProvider实现动态补全,手动处理选区与转义。

为什么不能直接在插件里写死 snippet JSON?
VSCode 插件无法通过 package.json 的 contributes.snippets 字段动态加载运行时生成的 snippet 内容。该字段只接受静态 JSON 文件路径(如 "./snippets/javascript.json"),不支持函数、Promise 或变量拼接。硬编码到 JSON 文件里就失去了“动态注入”的意义——改个文件名都要重写文件再 reload。
用 vscode.languages.registerCompletionItemProvider 模拟 snippet 行为
真正可控的动态方式是绕过 snippet 系统,改用补全提供器(CompletionItemProvider)。它能实时计算内容、过滤条件、甚至读取当前选中/光标位置。
- 触发时机更灵活:可基于文件类型、行前缀、是否选中文本等判断是否显示
- 内容完全动态:比如根据
vscode.window.activeTextEditor?.selections获取多光标选区,逐个包裹${TM_SELECTED_TEXT} - 支持异步逻辑:例如调用 API 获取枚举值后生成补全项,snippet 做不到
示例关键代码:
vscode.languages.registerCompletionItemProvider('javascript', {
provideCompletionItems(document, position) {
const line = document.lineAt(position).text;
if (line.trim().startsWith('wrap-')) {
const selection = document.getText(document.selection);
return [
new vscode.CompletionItem('wrap-parens', vscode.CompletionItemKind.Snippet),
// 设置 insertText 为动态字符串
new vscode.CompletionItem('wrap-curly', vscode.CompletionItemKind.Snippet)
.insertText = new vscode.SnippetString(`{${selection}}`)
];
}
}
});
$TM_SELECTED_TEXT 在插件里根本不会自动替换
这是最常踩的坑:把 snippet JSON 里的 $TM_SELECTED_TEXT 直接塞进 CompletionItem.insertText,结果插入的是字面量 $TM_SELECTED_TEXT,不是选中的内容。VSCode 只在原生 snippet 系统里解析这些变量。
- 正确做法:手动读取
vscode.window.activeTextEditor?.selection,拼进SnippetString - 注意多光标场景:如果用户开了多个 selection,得遍历
editor.selections并分别处理 - 别忘了转义:选中文本含
$、{、}时,要用vscode.SnippetString.appendText或正则转义
想保留原生 snippet 体验?只能走“生成临时文件 + 重载”路线
如果必须用 contributes.snippets,唯一办法是让插件在运行时生成一个物理 JSON 文件(如 ./.vscode/dynamic-snippets.json),然后调用 vscode.commands.executeCommand('workbench.action.reloadWindow') 强制重载。但这会打断用户当前操作,且无法响应实时上下文变化。
- 适合场景:项目初始化时一次性注入团队规范 snippet,而非编辑中动态响应
- 风险点:文件路径权限问题(尤其 Windows 下写入
.vscode可能被拦截) - 替代方案:用
vscode.workspace.fs.writeFile写入用户 data 目录,再在package.json中引用该绝对路径(需用户重启)
真正需要“动态”的地方,基本都卡在变量不解析和 reload 成本高这两点上——要么换补全提供器,要么接受静态化妥协。


















