ESC
开源 2 分钟阅读

Emacs 31:Markdown-ts-mode非官方指南

本文是Emacs 31中markdown-ts-mode的非官方指南,讲解如何安装Tree-sitter语法、处理缺失语法导致的高亮问题,并详细介绍该模式的功能:强调、折叠、代码块原生高亮、表格、链接、目录、转换等。适合Emacs用户快速上手。

来源:Hacker News

Tree-sitter 需要你的发行版提供一个包,通常名为 tree-sitter-cli,它提供一个 tree-sitter 二进制文件,你可以通过 tree-sitter --version 检查是否已安装。

这是所有 tree-sitter 模式常见的痛点。许多人不喜欢自己编译语法,而是使用来自可信来源的编译文件,比如自己的发行版仓库,或者包含数百个预编译语法的包。我不会深入讨论这一点;获取语法的方式有很多,本指南将坚持“自己构建”。

你看,我有点忽悠你了。我告诉过你你应该看到那个,但实际上,“你看到的是否和我看到的一样”应该是这样的:

我们在这里提供了完整文件,包含多个默认主题,以便你比较自己的设置是否完整。

这也是 markdown-ts-mode 非常特别的部分原因。

此模式不仅适用于 markdown,还适用于所有其他可用的 -ts-mode!请记住这一点;我们稍后会讨论代码块。现在,我们需要理解一些事情。

在你的 test.md 文件中,有一个特殊的头部。Markdown 文件使用 toml 或 yaml 作为头部非常常见。

`— title: The Official ‘markdown-ts-mode.el’ Feature Test File author: Rahul Martim Juliato date: 2026-03-18 version: 0.1.0 parsers needed: markdown, markdown-inline, yaml, toml, html, c, javascript, python, ruby, rust

`

还需要别的东西来字体化(即被 Emacs 着色)。你能猜出缺少什么吗?如果你的答案是“我们需要一个 YAML 语法!”,恭喜你!

每当 -ts-mode 中某些内容无法正确字体化时,你很可能缺少一个语法。而且由于 markdown-ts-mode 旨在与所有可用的 ts-mode 配合使用,这里也不例外。

让我们用可靠的 M-x treesit-install-language-grammar RET yaml 安装 yaml 语法。

用 y 确认。嗯,看起来这次 yaml-ts-mode 在尝试用 treesit-install 注册其首选语法时出了问题,因为没有建议。我们可以手动提供它。但让我们先检查一下。看看 yaml-ts-mode.el,我们可以在其源代码中检查它期望哪个语法:

;; from yaml-ts-mode.el (add-to-list 'treesit-language-source-alist '(yaml "https://github.com/tree-sitter-grammars/tree-sitter-yaml" :commit "b733d3f5f5005890f324333dd57e1f0badec5c87") t)

太棒了!让我们简单地求值该代码块,然后再次尝试安装语法。或者像这次我做的,手动向已启动的交互式会话提供源 https://github.com/tree-sitter-grammars/tree-sitter-yaml:

然后我们继续使用默认值 RET RET RET...,直到库安装完成。

之后,重新加载 markdown-ts-mode,或使用 C-x x g,或重新打开你正在访问的文件。

我们在这里通过查看源代码所做的工作相当少见,大多数 -ts-mode 会自动建议它们将要编译的仓库。发生这种情况很好,这样我就可以向你展示该怎么做。

接下来怎么办?我们需要对遇到的每个没有字体化的块执行相同的 M-x treesit-install-language-grammar。如果你愿意,对于我们的测试文件,可以使用 C-x x f 强制字体化,并提示该文件使用的每个缺失语法。

到现在,你应该看到整个文档字体化,如这里所示。与之前的图像相同:

一个 -ts-mode 的好坏取决于其背后的 tree-sitter 语法。这意味着每个 -ts-mode 都需要不断跟上 grammar 的改进,而该语法由任何想要使用 tree-sitter 解析语言的编辑器或程序共享。

这也意味着我们在某种程度上依赖语法来实现某些约束和功能。Emacs 中几乎所有的 -ts-mode 代码都充满了关于局限性以及为什么和如何处理某些晦涩问题的注释。

Emacs 模式作者和维护者总是尝试在注释或模式代码中建议 ts-mode 准备使用的语法和 SHA 提交,就像你看到的 yaml 建议一样。维护 ts-mode 的一部分是跟上更新的语法版本变化。我们尽最大努力保持模式源文件中应该正常工作的版本。

这就是为什么我认为使用 Emacs 交互式地自己编译是保证良好体验的最佳方式。

具体来说,对于 markdown-ts-mode,我们使用 https://github.com/tree-sitter-grammars/tree-sitter-markdown 提供的语法,因为它是最完整、维护最活跃、被代码编辑器和程序广泛采用的。这并不意味着它没有错误或局限。同样,我们尽力绕过这些限制,甚至向语法和核心 tree-sitter 库提交问题。

恭喜!接下来呢?我需要多久做一次这些?只需要一次,在你第一次使用 -ts-mode 时,或者如果你已经通过其他方法安装了语法,则永远不需要。

现在让我们看看 markdown-ts-mode 已经提供了什么。

我们(顺便说一句,这个模式由我和 Stéphane Marks 编写)提供了一个 easy-menu 功能,以便快速发现功能。

你可以通过点击 mode-line 中的 Markdown,或者如果启用了 menu-bar-mode,在菜单栏中,或者在使用 markdown-ts-mode 的缓冲区上 Ctrl + 右键点击(Emacs 映射到你的操作系统输入的任何方式)来访问它。

这实际上是本指南的TL;DR,如果你想现在停止并自己探索(剧透警告)。

学习这个模式最快的方法是每种都输入一点。下面是一个快速浏览:你写什么,什么键能为你做什么。

Markdown 是纯文本,所以你总是可以自己输入标记:

或者让模式来做:C-c C-x C-f(markdown-ts-emphasize)然后一个按键:

如果区域处于活动状态,格式化会包裹区域。如果没有区域,它会包裹光标处的单词,或插入一对标记并将光标放在中间。

提示:C-c C-x RET(markdown-ts-toggle-hide-markup)隐藏标记本身,所以 **bold** 显示为 bold。阅读时非常方便,就像默认的 org-mode。

另一个提示:M-q 即使在列表和引用中也能正确填充。

输入它们:#,##,… 直到 ######。Setext 标题(=== 和 --- 下划线)也被识别。

在不重新输入井号的情况下提升和降级:

以及移动整个部分,包括正文和子部分:

在标题上按 TAB 循环其可见性(大纲折叠)。该模式是 outline-minor-mode 的公民,所以折叠正常工作。在标题上按 S-TAB 将循环所有标题的可见性。

重要:到目前为止,你可以看到这个模式在可能的情况下尝试与 org-mode 画平行线,所以习惯它的 Emacs 用户可以更容易适应 markdown。如果这些绑定不适合你,一切都可以定制。

注意你看到的项目符号和复选框(如果你切换了 C-c C-x RET)只是显示。缓冲区仍然持有 - 和 [x]。参见 markdown-ts-unordered-list-marker、markdown-ts-checked-checkbox 和 markdown-ts-unchecked-checkbox。

C-c C-,(markdown-ts-insert-structure)然后一个按键:

如果区域处于活动状态,它将包裹区域而不是插入空块。

这是派对技巧。带有语言标签的围栏块会被该语言自己的模式字体化:

def hello():
 return "world"
```
`

缺少颜色通常意味着缺少语法,和前面 `yaml` 头部的情况一样。

比颜色更好:将光标放在块内,你就处于 `markdown-ts-code-block-in-context-mode`(模式行中显示 ` [code]`)。在其中:

使用 `C-c C-v n` 和 `C-c C-v p` 移动到下一个/上一个代码块。

非 tree-sitter 模式也可以工作,包括 `elisp`。旋钮:`markdown-ts-code-block-modes`、`markdown-ts-default-code-block-mode`、`markdown-ts-fontify-code-blocks-natively`。

使用 `C-c C-,` `t` 或 `M-x markdown-ts-table-insert-table` 插入一个表格,它会要求你指定要插入的行数和列数。

`| Column 1 | Column 2 |
|----------|:---------|
| a | 1 |
`

在表格内你处于 `markdown-ts-in-table-mode`(显示 ` [table]`),按键会改变:

另外,从菜单:克隆行和列,区域的 CSV/TSV 导入和表格的 CSV/TSV 导出。

**注意:** 目前使用表格时有一些限制,主要是由于语法解析它们的方式,所以你在输入时可能会遇到未字体化的内容。根据 GFM 规范,所有有效的表格应该都可以正常使用。

链接是通常的 `[text](url)` 和 `[text][ref]`。像 `[intro](#intro)` 这样的片段链接是可点击的,并跳转到缓冲区中的标题,默认使用 GitHub 风格的 slug。

图片内联渲染。`C-c C-x C-v` 切换它们(`markdown-ts-toggle-inline-images`)。参见 `markdown-ts-image-max-width` 和 `markdown-ts-display-remote-inline-images` 了解大小以及是否获取远程 URL。

- TAB 在点处循环折叠
- C-c C-n / C-c C-p 下一个 / 上一个标题
- C-c C-u 上升到父标题
- C-c C-f / C-c C-b 下一个 / 上一个标题,同级
- M-x imenu 通过补全跳转到任何标题或命名代码块
- C-c C-v n / C-c C-v p 下一个 / 上一个代码块

`markdown-ts-default-folding` 决定文件打开方式:全部显示,或折叠。

`M-x markdown-ts-view-mode` 只读模式,带单键导航:`n`、`p`、`u`、`f`、`b`、`TAB`。适合阅读 README 而不必担心误输入。

下面的所有内容都位于 `markdown-ts-mode-x.el` 中,这就是我们在设置中加载它的原因。

目录由 HTML 注释分隔,因此它可以在任何地方渲染时保留:

`<!-- markdown-ts-toc: -->
<!-- markdown-ts-toc-end: -->
`

- M-x markdown-ts-toc-insert-template 插入这些标记,基本或完整(完整版列出每个参数及其默认值)
- M-x markdown-ts-toc-generate 填充它们,并在每次调用时重新填充
- M-x markdown-ts-toc-clear 清空,markdown-ts-toc-clear-and-remove 同时移除标记
- M-x markdown-ts-toc-update-before-save-mode 在保存时重新生成

参数内联在开头的注释中:`min-depth`、`max-depth`、`candidates`、`from`、`style`、`indent`、`no-link`、`relative-depth`、`ignore`。一个缓冲区可以容纳多个具有不同参数的表格。候选不仅仅是标题,列表项、setext 标题和命名代码块也可以提供表格内容。

`M-x markdown-ts-convert` 转换缓冲区,`markdown-ts-convert-file` 转换文件。除非你设置了 `markdown-ts-default-converter`,否则会询问你格式和转换器。开箱即支持:

使用前缀参数时,结果会显示,默认使用 `eww`。参见 `markdown-ts-convert-display-function` 以在浏览器中打开。这是你有点“实时”的预览。转换不会(目前)在更改时自动进行,也许将来会。

使用 `eww` 的示例,为此演示手动拆分:

`M-x markdown-ts-browse-commonmark-spec` 和 `M-x markdown-ts-browse-gfm-spec` 打开规范,供你需要解决争论时使用。

这仍然是实验中的实验,所以如果出现问题,不要责怪 `eglot` 的作者。请向 `markdown-ts-mode` 发送错误报告。

`(setopt eglot-documentation-renderer #'markdown-ts-view-mode)
`

Eglot 将尝试使用 `markdown-ts-mode` 渲染文档(通常是 LSP 服务器提供的 Markdown)。