推出 Markdown 语言服务器
2022 年 8 月 16 日,作者:Matt Bierner,@MattBierner
2016 年我加入 Visual Studio Code 时,Markdown 支持是我负责的第一个功能。哇,真的已经六年了吗?这是一个完美的结合。我使用 Markdown 的时间够久了,以至于我经常发现自己充满希望地在 Twitter、Outlook 以及我的光标所停留的几乎每一个文本框中输入反引号和星号。多年来,发展 VS Code 内置的 Markdown 支持,并看到我们的 Markdown 扩展如何直接或间接地塑造了 Webview 和笔记本等核心功能,让我非常有成就感。
这就是为什么我很高兴分享一个我过去半年一直在悄悄推进的项目,一个我认为代表着 VS Code Markdown 工具下一阶段的项目:Markdown 语言服务器。通过这个语言服务器,我们正在将 VS Code 的大部分内置 Markdown 语言工具——从文档大纲到智能折叠再到路径补全——提供给其他编辑器和工具。我们的目标是利用通常与编程语言相关的智能功能来推动 Markdown 工具的发展。
Markdown 语言服务器工作分为两个新的(且名称相似!)开源库
-
Markdown Language Service - 一个提供 Markdown 处理工具的 TypeScript 库。
-
Markdown Language Server - 一个使用语言服务构建的 Markdown 语言服务器。
虽然这些库仍处于早期阶段,但它们已经被 VS Code 1.70+ 使用(希望你甚至没有注意到 :-))。我们甚至从这次转换中看到了一些好处,例如将 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 语言功能
我不会用每个新功能背后的故事来让你感到无聊,也不会深入探讨它们是如何实现的血腥细节。简单地说,我采取了增量方法,这使得在我投入到 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 既是开源软件的 prolific 生产者也是使用者,我看到了这类项目带来的明显好处。一个开源的 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 Language Service - 一个提供 Markdown 处理工具的 TypeScript 库。
-
Markdown Language Server - 一个使用语言服务构建的 Markdown 语言服务器。
编码愉快!
Matt Bierner,@MattBierner