介绍 Markdown 语言服务器
2022 年 8 月 16 日 作者:Matt Bierner, @MattBierner
当我 2016 年加入 Visual Studio Code 时,Markdown 支持是我负责的第一个功能。哇,真的已经六年了吗?这真是一个很棒的契合点。我使用 Markdown 的时间够长,以至于经常忍不住在 Twitter、Outlook 以及光标落入的任何文本框中输入反引号和星号。多年来,发展 VS Code 内置的 Markdown 支持并看到我们的 Markdown 扩展如何直接或间接塑造了 Webviews 和笔记本等核心功能,这真是令人无比满足。
这就是为什么我很激动地分享一个我过去半年一直在默默努力的项目,一个我认为代表 VS Code 的 Markdown 工具向前迈出的下一步的项目:Markdown 语言服务器。有了这个语言服务器,我们将 VS Code 内置的 Markdown 语言工具——从文档大纲、智能折叠到路径补全——提供给其他编辑器和工具。我们的目标是利用通常与编程语言相关的智能来推动 Markdown 工具的发展。
Markdown 语言服务器的工作分为两个新的(且名字相似的!)开源库
-
Markdown 语言服务 - 一个用于处理 Markdown 的 TypeScript 库。
-
Markdown 语言服务器 - 一个使用语言服务构建的 Markdown 语言服务器。
虽然这些库仍处于早期阶段,但它们已经被 VS Code 1.70+ 版本使用(希望您甚至没有注意到 :-))。这次切换甚至带来了一些好处,例如将 Markdown 工具转移到独立的进程中,这样就不会阻塞其他扩展。
但在我深入阐述之前,也许您想知道:为什么需要一个 Markdown 语言服务器?说实话,我自己花了这六年时间才转变观念。这也反映了我的演变过程:从简单地将 Markdown 看作带有一些星号、方括号和井号来活跃气氛的纯文本,转变为理解 Markdown 是一种标记语言,并且可以受益于我们为 TypeScript 或 Python 等编程语言提供的许多相同工具。
深入了解 Markdown 工具
在我发现 VS Code 之前,我主要使用简单的文本编辑器进行编码。这意味着我每次想使用符号名称时都必须记住并手动输入。如果我想重命名一个变量,我会进行文本查找/替换,并希望我的单元测试能够捕捉到那些名称被输错或修改导致出错的不可避免的情况。这是一种缓慢且不可靠的工作方式,但我当时很知足,因为我不知道可以做得更好。只有当我最终接触到更智能的工具后,我才真正明白我的工作流程有多么原始。
最近,我在 Markdown 方面也有了同样的感悟。多年来,我一直满足于使用 VS Code 相对简单的 Markdown 编辑器。我对语法高亮和内置的 Markdown 预览感到满意。文档大纲和可点击的编辑器链接只是额外的好处。我已经习惯了手动编写链接。我已经接受了这样一个事实:如果我更改了标题名称,就需要进行文本搜索来更新所有指向该标题的链接。因为我将 Markdown 看作只不过是花哨的纯文本,所以我甚至无法想象存在更好的方式。
但有一天,在我第一百次输错图片路径之后,我终于意识到:这太没意思了!为什么我要把时间浪费在手动输入和验证这些链接上?这正是工具的用途!我知道我想要的不是任何工具,我想要一个能帮助我将 Markdown 作为文本阅读和编写的工具,而不是将 Markdown 源代码隐藏在某种 WYSIWYG 风格的 UI 魔术后面。这与 VS Code 的精神以及我们如何看待对编程语言的支持非常一致。为什么我们为传统编程语言提供的许多相同智能不能也应用于 Markdown 呢?第二天我就开始着手研究链接补全功能了。
链接补全功能是帮助您编写指向当前文件或工作区中其他文件的标题的链接的建议。我甚至增加了对补全指向其他 Markdown 文件中标题的链接的支持。太棒了!这只是一个小小的增加,但对我的工作效率产生了巨大的影响。很快我就无法想象没有它的日子怎么过。
Markdown 补全功能的成功让我欣喜若狂,我陶醉于想象接下来还能为 Markdown 带来哪些其他的语言智能。我设想自己可以自信地按下 F2 键安全地重命名标题。我梦想着红色波浪线能穿透模糊的文本海洋,帮助识别无效链接。这一切看起来都如此显而易见!我几年前为什么没想到呢?我开始将 Markdown 理解为结构化文本,而不仅仅是纯文本,并且更好的 Markdown 工具的可能性似乎是无限的。
Markdown 语言特性
我不会用每个新功能背后的故事来让您感到无聊,也不会深入探讨它们是如何实现的血淋淋的细节。 suffice it to say(可以这样说),我采取了增量方法,这使得在有限的时间内投入到 VS Code 的 Markdown 支持中的所有努力成为可能。例如,我没有直接构建重命名支持,而是首先让一个稳定的版本 查找所有引用 运行起来(因为如果你想重命名一个符号,你首先需要知道所有引用它的地方)。增量工作并在彼此之上构建每个功能,这也有助于我在实现新功能时测试旧功能。例如,在链接上实现重命名帮助我发现了链接检测中的大量错误。(这种方法的唯一缺点是,你会意识到你所谓的“如此优雅”的塔是建立在几个非常复杂的正则表达式之上的)。
当报告无效文件/图片链接的实验性支持在春末推出时,我退后一步审视我的工作。现在,Markdown 语言特性集包括
- 文档大纲
- 工作区符号
- 文档链接
- 智能折叠
- 智能选择
- 补全
- 重命名
- 查找所有引用
- 转到定义
- 无效链接诊断
- 文件移动/重命名时更新链接
我知道这些新工具会使处理 Markdown 更快、更安全。但当我回顾这些编程语言常见的特性列表时,一个想法一直困扰着我。几个月前我把它当作荒谬的念头抛之脑后,但现在,当我再次仔细思考时,我意识到可能是时候推出一个 Markdown 语言服务器了。
正在为您服务吗?
到 2022 年春末,VS Code 的所有 Markdown 工具仍然运行在普通的扩展 API 上。虽然我想探索将所有这些工具迁移到一个合适的语言服务器上,但进行此更改会产生实际的工程成本。我需要确保它是值得的。
我对此反复思考了一个多月。尽管现有代码状况良好,但存在许多未知数。如果我只进行了一部分就发现它行不通怎么办?我之前甚至从未认真开发过语言服务器。
在我权衡这一切时,我让自己忙于重构 Markdown 扩展的源代码,就像它即将被迁移到语言服务器一样。我努力隔离对 VS Code 扩展 API 的依赖,将更多逻辑改为使用服务注入,并确保测试不依赖于文件系统。这样一来,即使我最终没有开发语言服务器,至少也在清理代码库。
一些考虑最终说服了我,Markdown 语言服务器是正确的下一步。首先是一个比较普通的原因:我发现为 Markdown 文件高效实现链接诊断非常具有挑战性。在一个大型的 Markdown 工作区,例如 vscode-docs,我总是意外地阻塞扩展主机数百毫秒。这不好。而语言服务器作为独立的进程运行。不仅如此,语言服务器现在还有了新的诊断拉取模型,我对此很兴奋,想尝试一下。
然后还有更崇高的原因。例如,Markdown 语言服务器对其他编辑器和工具也会有用。这包括 VS Code 团队发布的另一个编辑器:Monaco!更不用说像 Markdown CLI 工具这样的可能性了。如果我自己没有时间构建这样的工具,也许其他人可以,以语言服务器作为起点。我已经在 VS Code 的 Markdown 工具上投入了大量工作,如果所有这些工作也能惠及其他人,那就太好了。
通过提供一个新的语言服务器,我也许还能围绕改进 Markdown 工具启动一项共同努力。VS Code 既是开源软件的积极贡献者,也是积极使用者,我看到了这类项目带来的明显好处。一个开源的 Markdown 语言服务器将帮助其他编辑器,反过来也会吸引贡献,最终帮助 VS Code!与其让每个编辑器/工具重复努力实现自己的 Markdown 支持,一个语言服务器可以将开发者聚集在一起,为一个惠及所有人的更大项目而努力。
所有这些宏大的设想,如果没有实际构建语言服务器的计划,都是无关紧要的。即使在我完成了所有重构之后,将代码迁移到语言服务器仍然是大量的工作!这看起来令人不知所措,直到我意识到不必一次性完成所有工作。我可以增量地构建服务器,一次一个地将功能从 VS Code Markdown 扩展迁移到新的 Markdown 语言服务器。如果操作得当,我可以检查每个小的增量迁移,这样用户在构建新语言服务器时就能对其进行测试。理想情况下,当一个功能从扩展迁移到语言服务器时,用户不会注意到。
也许这很明显,但我已经成为这种处理大型代码变更的增量方法的忠实信徒。没有数十万行的 PR,也没有持续数月(甚至数年!)的大型特性分支。相反,对 main
分支进行一系列小而安全的变化。如果一切按计划进行,完成这项工作最终的提交应该是波澜不惊的。这就是我们逐步在整个 VS Code 代码库中采用严格的 null 检查所采取的方法,我也觉得这样可以将 VS Code 的所有 Markdown 工具快速、尽可能平稳地迁移到新的语言服务器。
剧透一下:它奏效了!我一次一个地迁移了语言特性。我边做边学,并在需要时进行了重构。诊断是最后一个迁移的功能,因为我不仅将它们迁移到语言服务器,还重写了它们,使其使用语言服务器新的拉取诊断模型。整个工作的最后一个提交主要删除了 Markdown 扩展中现在未使用的代码。因此,今天,如果您使用的是 VS Code 1.70+ 版本,几乎所有的 Markdown 语言特性都使用了新的语言服务器。
共同构建更好的 Markdown 工具
在许多方面,过去六个月 VS Code 的 Markdown 工具取得了比我过去六年在这个领域工作所取得的更多的进步。今天,我们发布了许多新工具,其中一些以前从未在 Markdown 中提供过。这些功能中的许多惠及最普通的 Markdown 读者和作者,而另一些则只有高级用户才能体会到。然而,尽管取得了这些进展,我知道我们在 Markdown 工具领域才刚刚开始探索可能性。
真正让我对 Markdown 语言服务器感到兴奋的是,现在这个项目已经不仅仅局限于 VS Code 了。通过使我们的 Markdown 工具易于使用,我希望我们能帮助推动 Markdown 工具向前发展,造福所有人。这些开源项目是邀请大家共同构建 Markdown 工具未来的邀请函。如果您有兴趣贡献,请查看这些新项目,看看您可以使用它们创造什么。您可以提交错误报告和功能请求,甚至可以提交 PR!还有无数智能的 Markdown 语言特性,我甚至还没有梦想过。让我们一起构建它们吧!
如果您有兴趣查看源代码或贡献,可以在 GitHub 和 npm 上找到 Markdown 语言服务和服务器
-
Markdown 语言服务 - 一个用于处理 Markdown 的 TypeScript 库。
-
Markdown 语言服务器 - 一个使用语言服务构建的 Markdown 语言服务器。
编码愉快!
Matt Bierner, @MattBierner