Biome Markdown 格式化器如何逐字节保留围栏代码块内容:mdn-background-6 测试用例与源码解析
2026/9/21 0:07:16 网站建设 项目流程

Biome Markdown 格式化器如何逐字节保留围栏代码块内容:mdn-background-6 测试用例与源码解析

【免费下载链接】biomeA toolchain for web projects, aimed to provide functionalities to maintain them. Biome offers formatter and linter, usable via CLI and LSP.项目地址: https://gitcode.com/gh_mirrors/bi/biome

导读

crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-background-6.md是 Biome Markdown 格式化器 Prettier 一致性测试套件中的一个输入用例,内容是一段来自 MDN 文档的 CSS 代码(叠放式径向渐变背景)。本文以该用例为核心,讲解 Biome 的 Markdown 格式化器如何处理围栏代码块(fenced code block):为什么代码块内部的空行、缩进和原始字符会被逐字节保留,而文档开头的空行会被规范化移除;并结合格式化器源码(fenced_code_block.rscode_content.rs)与测试基础设施(prettier_tests.rs)给出可验证的依据。读完本文,你将理解 Biome Markdown 格式化对代码块的"内容不动、围栏归一"设计原则,以及这套 Prettier 一致性测试的运行方式。

一、这个测试用例是什么:一份来自 MDN 的 CSS 代码块

该文件位于 Biome Markdown 格式化器的 Prettier 一致性测试目录下,与其配对存在的是同名.prettier-snap快照文件(mdn-background-6.md.prettier-snap)。从目录命名看,mdn-background-1.mdmdn-background-9.md是一组取自 MDN(Mozilla Developer Network)文档的background相关 CSS 示例(同目录下还有mdn-filter-1.mdmdn-padding-1.mdmdn-transform.md等 MDN 命名用例),用于验证格式化器对真实世界 Markdown 文档中代码块的输出稳定性。

输入文件 mdn-background-6.md 的完整内容如下(注意:文件第一行是一个空行,其后是 ```css 围栏;围栏内有三个连续空行;CSS 主体中第三个radial-gradient的缩进与其他两个不一致,是刻意保留的"不整齐"写法):

```css .stacked-radial { background: radial-gradient( circle at 50% 0, rgb(255 0 0 / 50%), rgb(255 0 0 / 0%) 70.71% ), radial-gradient( circle at 6.7% 75%, rgb(0 0 255 / 50%), rgb(0 0 255 / 0%) 70.71% ), radial-gradient( circle at 93.3% 75%, rgb(0 255 0 / 50%), rgb(0 255 0 / 0%) 70.71% ) beige; border-radius: 50%; } ```

这段 CSS 演示了经典的"三色叠放径向渐变"圆形头像背景技巧:三个radial-gradient分别从顶部、左下、右下发出红、蓝、绿三色,并以beige作为兜底色。对格式化器而言,它真正关心的不是 CSS 语义,而是这段代码块的书写形式:围栏后存在 3 个空行、梯度参数缩进混乱(第三个梯度的circle at 93.3% 75%多缩进了 4 个空格,收尾的) beige;缩进与其他梯度不同)。这些"不整齐"恰恰是测试要锁定的目标。

二、它验证了什么行为:内容逐字节保留,前导空行被移除

将输入文件与配对快照逐行对比,可以得到 Biome 必须复现的 Prettier 行为。期望输出(mdn-background-6.md.prettier-snap)如下:

```css .stacked-radial { background: radial-gradient( circle at 50% 0, rgb(255 0 0 / 50%), rgb(255 0 0 / 0%) 70.71% ), radial-gradient( circle at 6.7% 75%, rgb(0 0 255 / 50%), rgb(0 0 255 / 0%) 70.71% ), radial-gradient( circle at 93.3% 75%, rgb(0 255 0 / 50%), rgb(0 255 0 / 0%) 70.71% ) beige; border-radius: 50%; } ```

对比输入与期望输出,可以提炼出三个精确的行为观察:

  1. 文档开头的空行被移除:输入第一行是空行,期望输出直接以 ```css 开头。这是文档级前导空白(leading trivia)的规范化处理,不属于围栏内容本身。
  2. 围栏内部的空行被完整保留:```css 之后的 3 个空行原样保留,一处不少。这符合 Markdown 语义——围栏代码块内的空白是代码的一部分,改动它会破坏被嵌入代码(如 CSS、Python)的含义。
  3. 代码内容逐字节保留:三个梯度的缩进差异(8 空格 vs 4 空格)、) beige;的收尾缩进、rgb(255 0 0 / 50%)这种现代 CSS 空格语法全部原样输出,格式化器没有对围栏内的 CSS 做任何重排或重缩进。

同样的规律在同目录的 mdn-background-2.md 中也能印证:其输入在 ```css 前有 2 个空行,期望输出 mdn-background-2.md.prettier-snap 同样先移除前导空行,再把围栏内 3 个空行与参差不齐的linear-gradient缩进原样保留。两个用例互相印证,说明这是稳定规则而非偶然。

三、运行机制:这套 Prettier 一致性测试如何工作

该用例不是手工维护的普通快照,而是由自动化宏批量接入测试的。在 prettier_tests.rs 中有一行核心声明:

tests_macros::gen_tests! {"tests/specs/prettier/markdown/**/*.{md}", crate::test_snapshot, ""}

gen_tests!宏会把tests/specs/prettier/markdown/下所有*.md文件(递归匹配**)各生成一个测试用例,mdn-background-6.md因此自动成为测试输入。测试体(同文件 L13-L29)的流程是:

  • PrettierTestFile::new(input, root_path)加载输入文件与同目录的.prettier-snap期望输出;
  • 构造MdFormatOptions::default(),并显式设置IndentStyle::Space与默认IndentWidth(L22-L24);
  • 通过MarkdownTestFormatLanguage::gfm()以 GFM 模式解析 Markdown(见 language.rs,内部调用parse_markdown_with_cache(..., MarkdownParserOptions::default().with_gfm(true)));
  • MdFormatLanguage格式化后交给PrettierSnapshot::test(),将 Biome 输出与 Prettier 记录的期望输出逐字节比对。

也就是说,.prettier-snap文件是 Prettier 的黄金输出(golden output),Biome 的目标不是"自己看着合理",而是与 Prettier 在这些真实 MDN 用例上保持字节级一致

四、源码级原理:格式化器内部如何实现"内容不动、围栏归一"

围绕这个用例的行为,可以在 Biome Markdown 格式化器源码中找到对应的实现路径。

4.1 围栏本身的归一化:CommonMark §4.5 与最长反引号序列

围栏由 fenced_code_block.rs 中的FormatMdFencedCodeBlock处理。它首先计算围栏长度(L27-L34):

// Compute the minimum fence length needed (CommonMark §4.5). // The fence must be strictly longer than any same-character sequence // in the content, otherwise the inner sequence would be parsed as a // closing fence. E.g. if the content contains ``` (3 backticks), // the outer fence needs at least 4. let max_inner = longest_fence_char_sequence(node, '`'); let fence_len = (max_inner + 1).max(3); let normalized_fence: String = std::iter::repeat_n('`', fence_len).collect();

longest_fence_char_sequence(L176-L205)遍历代码块内容,统计内容中连续反引号的最大长度。若内容里出现了 3 个连续反引号,外层围栏就必须升到 4 个反引号,否则内容中的```会被 CommonMark 解析成围栏结束符。在本用例中,CSS 内容不含反引号,max_inner = 0,因此fence_len取最小值 3,围栏维持 ```css 不变——这与快照中输出完全一致。此外,格式化器还会把开、闭围栏统一替换为同一长度(format_replaced),保证成对出现。

4.2 内容区:逐行输出的FormatMdCodeContent

围栏内容由 code_content.rs 中的FormatMdCodeContent负责。其核心思路是把代码块内容当作原文字面量逐行输出(L19-L85):值 token 以围栏开头的换行为起点,随后按行切分,每一行调用format_sliceliteral_line_breaks()原样打印,而不是走 Markdown 常规的缩进/换行排版逻辑。这正是"围栏内 3 个空行原样保留、梯度缩进差异原样保留"的机制来源。

实现中还有两个值得注意的细节:

  • 围栏缩进的按行裁剪FormatMdCodeContentOptions.opening_fence_indent记录了开围栏的缩进宽度,输出每行时会先跳过不超过该宽度的前导空格(L42-L50)。在 mdn-background-6 中,围栏位于第 0 列,opening_fence_indent = 0,因此代码行一个空格都不会被裁剪,circle at 93.3% 75%,的 8 空格缩进得以完整存活。
  • 跨平台换行处理:L36-L40 与 L57-L81 对\r\n\r\n三种换行分别处理,\r\n被归一为统一换行输出,单独的\r则用literal_line_break_without_parent兜底,保证 Windows 风格的 Markdown 也能稳定格式化。

4.3 文档级前导空行:不属于代码块的部分

用例中"输入首行空行被移除"的行为发生在围栏之外——那是文档根级对前导空白(leading trivia)的规范化,属于 Markdown 文档整体排版的一部分,与代码块内容无关。从本用例与 mdn-background-2 的快照对比可以确认:凡是围栏之外的前导空行都会被规整掉,凡是围栏之内的空行都原样保留。这条边界正是"格式化 Markdown 结构"与"绝不触碰嵌入代码"两条原则的分水岭。

五、同组用例对照:代码块格式化的行为边界

code/目录(crates/biome_markdown_formatter/tests/specs/prettier/markdown/code)下聚集了专门刻画代码块行为的用例族,除本用例外还包括:

  • mdn-background-1.md~mdn-background-9.mdmdn-filter-*.mdmdn-padding-*.mdmdn-transform.md等 MDN 用例:覆盖真实文档中各种语法高亮围栏(css、js 等)内代码的稳定性;
  • indent.mdleading-trailing-newlines.md:分别考察围栏的缩进处理与围栏内首尾空行的保留。以 leading-trailing-newlines.md.prettier-snap 为例,一个无语言标记的围栏内,"123" 前后各 2 个空行在期望输出中均被完整保留;
  • backtick.mdformat.mdlang.mdadditional-space.mdts-trailing-comma.md等:覆盖内容含反引号、信息字符串(info string)、语言标签等边缘情形(作为同目录输入文件存在,均有对应.prettier-snap)。

把这些用例放在一起看,Biome Markdown 格式化器对围栏代码块的行为边界非常清晰:围栏的开闭标记与信息字符串属于 Markdown 语法,可以被规范化;围栏包裹的内容属于"嵌入的另一种语言",必须原样保留FormatMdFencedCodeBlock中的has_quote_prefixinside_list等分支(fenced_code_block.rs)进一步处理了代码块出现在列表、引用块中时的前缀对齐问题,同样不触及代码内容本身。

六、对使用者的实践启示

  1. 代码块内容与格式化配置解耦:Biome Markdown 的MdFormatOptions主要包含indent_styleline_width(见 context.rs),它们影响的是列表、段落、嵌套结构等 Markdown 排版,而围栏代码块内容不参与这些规则。也就是说,无论你把缩进风格设为空格还是 Tab、行宽设为多少,mdn-background-6中的 CSS 输出都保持逐字节不变——这对在文档中内嵌对空白敏感的代码(CSS 缩进、Python、YAML、shell 脚本)至关重要。
  2. 书写 Markdown 时的注意事项:由于围栏内容被原样保留,代码块内若出现与围栏同字符的连续序列(如内容中有而外层也是 3 个反引号),CommonMark 会提前结束代码块。格式化器会按 §4.5 自动把外层围栏加长来修复(源码见 [fenced_code_block.rs](https://link.gitcode.com/i/858b4ddde48fb83ff0a7ac2224c9fea8#L27-L34)),但最稳妥的做法仍是使用 4 个反引号包裹含的示例代码。
  3. 如何复现与验证:在本仓库中运行cargo test -p biome_markdown_formatter即可执行该 crate 的测试套件,gen_tests!宏会依据tests/specs/prettier/markdown/**/*.{md}自动生成包括本用例在内的全部测试;修改输入或期望输出文件后,测试会立即暴露输出差异,保证 Prettier 一致性不被破坏。

结语

mdn-background-6.md表面上只是一段 25 行的 CSS 示例,但它承载了 Biome Markdown 格式化器一条重要的设计约定:格式化器只负责 Markdown 语法层的规范化(围栏长度、前导空行、信息字符串),而把围栏内的内容视为不可侵犯的原文字面量逐行保留。这一约定在FormatMdCodeContent的逐行字面输出与FormatMdFencedCodeBlock的围栏归一化中落地,并由prettier_tests.rs驱动的.prettier-snap黄金输出锁定为不可回退的测试契约。理解这一机制,你就掌握了 Biome 处理"Markdown 中嵌入代码"这一高频场景的核心行为边界。

【免费下载链接】biomeA toolchain for web projects, aimed to provide functionalities to maintain them. Biome offers formatter and linter, usable via CLI and LSP.项目地址: https://gitcode.com/gh_mirrors/bi/biome

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询