给 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 和错误模型,格式化和修复建议最后都会变成一堆边界条件。

常见的实现顺序通常是:

  1. 先有 grammar
  2. 再有语言配置和基本编辑行为
  3. 再做轻量补全
  4. 需要语义时再上 LSP
  5. 最后补 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