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