vscode-textmate是最可控的高亮底层方案,因其提供Node.js环境下解析.tmLanguage、生成tokenization结果的能力,支持离线预处理与自定义scope映射,且需配合package.json声明语法路径、正确注册languageId及匹配theme作用域才能生效。

为什么 vscode-textmate 是最可控的高亮底层方案
VS Code 默认语法高亮基于 TextMate 语法规则,所有官方语言支持和主流插件(如 es7-react-js-snippets)都依赖这套体系。直接改写编辑器渲染层不现实,而 vscode-textmate 提供了在 Node.js 环境下解析 .tmLanguage、生成 tokenization 结果的能力,适合做离线预处理、自定义 scope 映射或构建轻量级高亮服务。
常见错误是试图用 monaco-editor 的 API 在插件里复用 Web 版本高亮逻辑——插件运行在 Electron 主进程或扩展主机中,没有 monaco 实例,强行 require 会报 Cannot find module 'monaco-editor'。
- 只在插件激活后调用
vscode.languages.setTextDocumentLanguage()注册新 languageId,否则vscode-textmate解析出的 tokens 不会被编辑器识别 -
.tmLanguage.json必须放在插件./syntaxes/下,并在package.json的contributes.grammars中声明路径,否则 VS Code 启动时不加载 - scope 名(如
support.class.builtin.python)必须与主题定义的 scope 匹配,否则即使 token 正确也无颜色——可用Developer: Inspect Editor Tokens and Scopes命令验证
如何用 injectionGrammar 复用现有语法高亮
不需要从头写 .tmLanguage,比如想在 Markdown 文件的代码块里支持 Zig 语法高亮,但 Zig 官方插件只注册了 zig languageId,没声明对 markdown 的 injection 支持。这时可在自己插件的 package.json 中添加:
{
"contributes": {
"grammars": [{
"language": "markdown",
"scopeName": "source.gfm",
"path": "./syntaxes/markdown-injection.json",
"injectTo": ["text.html.markdown"],
"embeddedLanguages": {
"meta.embedded.block.zig": "zig"
}
}]
}
}
关键点在于:injectTo 指定目标 languageId(不是文件后缀),embeddedLanguages 的 key 是目标语法中定义的 scope(如 Markdown 的 fenced code block 规则里写了 meta.embedded.block.zig),value 是已注册的 languageId。
- 若目标语法未定义对应 scope,需先 fork 其
.tmLanguage并添加 capture,不能仅靠 injection 声明“生效” -
injectTo不支持通配符,"injectTo": ["*"]无效;多目标要写成数组形式["plaintext", "markdown"] - 嵌入语法的 scope 优先级高于宿主语法,但 theme 颜色仍由最终合成的 scope chain 决定,不是简单叠加
editor.tokenColorCustomizations 覆盖主题颜色时的限制
用户级配置或插件通过 vscode.workspace.getConfiguration().update() 修改 tokenColorCustomizations 只能影响当前工作区,且无法覆盖所有 scope。例如设置 "support.type.property-value.css" 有效,但 "punctuation.section.embedded.source.php" 常被忽略——因为 PHP 的嵌入逻辑在 HTML 语法中,实际 scope 是 "source.php.embedded.html",而主题作者往往只配了顶层 source.php。
- scope 名必须完全匹配,大小写敏感,多一个点或少一个点都不生效
- 修改后需触发一次编辑器重绘(如切换 tab 或保存文件),不会实时响应
- 无法覆盖
foreground以外的属性(如fontStyle在部分主题中被锁定),此时只能换主题或改.tmTheme
调试高亮失效的三步定位法
当写好 grammar 却没颜色,别急着重写正则。先打开命令面板运行 Developer: Inspect Editor Tokens and Scopes,把光标停在目标文本上,看右下角弹出的 scope list 是否包含你期望的 token。没有?说明 grammar 没命中;有但没颜色?说明 theme 没配该 scope。
- 检查 grammar 是否被加载:在开发者工具控制台执行
vscode.languages.getLanguages(),确认 languageId 存在;再查vscode.languages.getLanguages().includes('your-lang-id') - 用
TextMateRegistry.getLanguageIdByScopeName('your.scope.name')验证 scope 到 languageId 的映射是否正确(返回undefined表示未注册) - 临时启用
"editor.semanticHighlighting.enabled": false,排除 Semantic Token 干扰——它会覆盖 TextMate 的 foreground 设置
scope 名嵌套越深,越容易漏配;theme 开发者常省略中间层,只照顾顶层 scope。真要全覆盖,得自己导出当前 theme 的 .tmTheme 文件,手动补全。


















