推出 Markdown 语言服务器
2022 年 8 月 16 日,作者:Matt Bierner,@MattBierner
Markdown 支持是我 2016 年加入 Visual Studio Code 时负责的第一个功能。哇,真的已经六年了吗?不过,这很契合。我使用 Markdown 的时间足够长,以至于我经常发现自己希望在 Twitter、Outlook 以及光标所及的每一个文本框中键入反引号和星号。多年来,发展 VS Code 内置的 Markdown 支持并看到我们的 Markdown 扩展如何直接和间接塑造了 Webviews 和 Notebooks 等核心功能,这令人难以置信地有益。
这就是为什么我很高兴分享我过去半年一直在悄悄进行的一个项目,以及我认为它代表了 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 作为文本阅读和编写的工具,而不是将 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 文件的链接诊断非常具有挑战性。在一个大型 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 代码库中逐步使用严格空值检查的方法,这也是我觉得可以快速、尽可能少地麻烦地将所有 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