最近在使用 VitePress 搭建技术文档网站,遇到了一个小问题。虽然 VitePress 的 Markdown 代码块支持非常多语言的语法高亮,但我们的文档中存在许多 DSL(领域特定语言)代码,这些代码没有现成的语言能够适配,只能以纯文本(Plain Text)样式显示,导致阅读起来非常不直观。因此,我决定为 VitePress 添加自定义的语言进行语法高亮,来改善这种情况。

通过阅读 VitePress 的文档 Syntax Highlighting in Code Blocks,我了解到它使用了 markdown-it 进行 Markdown 解析,并通过 Shiki 实现了代码块的语法高亮。不过,在我搜寻了一番 VitePress 的文档后,最终也没有找到有关于添加自定义语言进行语法高亮的具体说明。看来这个只能自己摸索了。

VitePress 文档中有提到可以通过配置 markdown 选项来自定义语法高亮的主题,我顺着查看了 jsdocs ,发现除了可以配置主题外,还有一个 shikiSetup 选项,可以用来自定义 Shiki 的 Highlighter 配置。

/**
* Setup Shiki instance
*/
shikiSetup?: (shiki: Highlighter) => void | Promise<void>

接下来,我又查看了 Shiki 的文档 Load Custom Languages,得知 Highlighter 有一个 loadLanguage 方法,可以用来加载自定义语言。于是,我就有了具体的实现思路。

实现步骤

下面通过一个简单的例子来具体说明如何为 VitePress 添加自定义语言进行代码高亮。

Shiki 是基于 TextMate 的语法,如果我们要自定义语言的语法高亮,就需要先编写一个 TextMate 语法文件。先在 .vitepress 目录下创建一个名为 my-lang.js 文件,内容如下:

export default {
  "displayName": "UP",
  "name": "up",
  "scopeName": "source.up",
  "patterns": [
    {
      "match": "^\\s*(package|module)\\b",
      "name": "keyword.up"
    },
    {
      "begin": "<",
      "end": ">",
      "name": "comment.block.angle.up",
      "contentName": "string.quoted.angle.up"
    },
    {
      "match": "//.*",
      "name": "comment.line.double-slash.up"
    }
  ]
}

其中 name 是语言的标识符,也是对应 Markdown 代码块声明起始三个反引号后的语言标识符。

然后在 VitePress 的配置文件 config.mts 中配置 shikiSetup 选项,加载自定义语言(其实就是一个 JavaScript 对象,通过解析前面编写的 JSON 文件得到):

import {defineConfig} from "vitepress";
import myLang from "./my-lang";

// https://vitepress.dev/reference/site-config
export default defineConfig({
    // ...
    markdown: {
        async shikiSetup(highlighter) {
            await highlighter.loadLanguage([myLang]);
        }
    },
})

最后,就可以在 Markdown 中指定对应的语言名 up 使用自定义的语言进行代码高亮了:

```up
project your.package.name

// this is a comment

module platform <平台>
module app      <应用>
module user     <用户>
```

下面是展示效果截图:

进一步阅读

  • 关于语法规则的编写,可以参考 TextMate Manual
  • 关于 Shiki 的更多用法,可以参考 Shiki
  • 关于 VitePress 的更多用法,可以参考 VitePress