给 VSCode 做一门语言支持,最小可行路径是什么
很多人准备给 VSCode 做一门语言支持时,第一反应是“要不要上 LSP”。这个问题本身不算错,但问得太早了。
更实际的问题应该是:要让一门语言在编辑器里“先能用起来”,最小可行路径是什么。因为“语言支持”不是一个按钮,而是一串层层递进的能力:高亮、注释、括号、补全、跳转、诊断、格式化、代码动作,每一层的成本都不一样。
如果一开始就按“完整语言服务器”来设计,项目很容易在真正可用之前就被复杂度拖住。很多扩展的实现路径,都会按用户最先感知到的价值逐层往上搭。
第一步:先把基础高亮做出来
第一层通常是 TextMate grammar。
这一步的目标不是“完整理解语言”,重点是尽快把这些最基础的东西稳定下来:
- 关键字
- 字符串
- 注释
- 数字字面量
- 操作符
- 明确边界的代码块
只要 grammar 写得足够稳,用户一打开文件就能立刻分清代码结构。哪怕后面什么语义能力都还没有,编辑体验也已经比纯文本状态强很多。
这通常会成为最先落地的一层,因为它投入相对较小、反馈很快,而且不会绑定后续必须采用哪种 parser 或分析架构。
第二步:补齐编辑器级基础能力
在 grammar 之后,很多扩展会继续补这些“还不到语义分析层、但非常影响手感”的功能:
- 注释切换
- 括号配对
- 自动闭合
surroundingPairs- 基本缩进规则
这些能力很多都可以直接通过 VSCode 扩展清单和语言配置完成,不一定需要语言服务。它们的技术门槛不高,但用户对它们的存在感很强。
一个经常被忽视的事实是:很多人对一门语言“支不支持”的第一印象,往往不是跳转定义,而是输入括号、回车、注释时手感顺不顺。
第三步:加最小补全,而不是完整智能
再往上,一般可以开始做最小补全。
这里说的“最小”,是指先覆盖那些不依赖全项目语义,也能稳定给出的内容:
- 关键字补全
- 代码片段
- 内建函数名
- 常用模板
做到这里,通常已经足够让一门新语言在 VSCode 里具备“能写”的基本体验。很多 DSL、配置语言甚至到这一步就已经够用,不一定需要继续走到完整 LSP。
第四步:再决定要不要上 Language Server
当你开始遇到这些需求时,才真的需要认真考虑 LSP:
- 跳转到定义
- 查找引用
- 重命名
- 诊断错误
- 语义补全
- semantic token
- 悬浮信息
这些能力的共同点是:它们通常需要 AST、符号表、作用域分析,很多时候还要读到整个工作区,而不只是当前打开的一个文件。
换句话说,一旦你需要的是“理解程序”,不再只是“把文本切得更漂亮”,就应该从 grammar 层进入语言服务层。
第五步:格式化和代码动作是后段工程
Formatter、Code Action、Quick Fix 这些能力很有价值,但通常不该放在最前面。
原因很简单:它们往往依赖前面的语法分析和诊断能力。没有稳定 AST 和错误模型,格式化和修复建议最后都会变成一堆边界条件。
常见的实现顺序通常是:
- 先有 grammar
- 再有语言配置和基本编辑行为
- 再做轻量补全
- 需要语义时再上 LSP
- 最后补 formatter、diagnostics、code action
什么时候不用 LSP 也完全没问题
不是每门语言都值得上完整语言服务。
如果你的语言属于下面这些类型,只做 grammar 加少量编辑器配置,往往就已经够用了:
- 配置格式
- 模板语言
- 查询语言的子集
- 规则描述 DSL
- 主要以“编辑和阅读”为主、而不是“大型工程重构”为主的语言
这不是能力不足,更多是投入和需求匹配的问题。语言支持做得是否合适,关键不在于功能列表有多长,而在于它有没有解决用户最常遇到的编辑痛点。
一条比较稳的工程路线
如果要把上面的路径压缩成一句更可执行的话,可以写成:
先让文件打开时“看得清”,再让输入时“写得顺”,最后才去追求“懂得深”。
对应到实现上,就是:
- 用 TextMate grammar 做基础高亮
- 用语言配置补注释、括号、自动闭合和缩进
- 用 snippets 和轻量补全提升可写性
- 真正需要全局理解时再实现 LSP
这种路线的特点是,每往前走一步,用户都能立刻感知到价值;不用在一个庞大的语言服务器里积压很久,最后连最基础的编辑体验都还没落地。
小结
给 VSCode 做一门语言支持,最小可行路径通常不是“先上完整 LSP”。更常见的做法,是从基础 grammar 和语言配置开始,一层层往上补。
先解决高亮和编辑手感,再决定要不要进入语义分析,这是很多语言扩展都会采用的一条实现路线。
参考资料:Language Extension Guide | VSCode API / Syntax Highlight Guide | VSCode API / Programmatic Language Features | VSCode API