- 开发工具
- Lint
- 格式化
- 静态分析
- 代码质量
- 前端
【免费下载链接】biome
A toolchain for web projects, aimed to provide functionalities to maintain them. Biome offers formatter and linter, usable via CLI and LSP.
本文以 Biome 仓库中的 Markdown 格式化器测试用例 mdn-background-8.md 为解剖样本,深入讲解 Biome 的 Markdown 格式化器(biome_markdown_formatter)在处理围栏代码块(fenced code block)时的核心策略:代码块内部内容原样保留、绝不重排,同时规范化围栏本身(fence)的长度与收尾换行。读完本文,你将掌握 Biome 对 Markdown 代码块的格式化语义、围栏长度计算规则(CommonMark §4.5)、逐行内容复写(verbatim)的实现原理,以及对应的测试组织方式,能够据此预判任何 Markdown 文档中代码块的格式化结果。
一、测试用例背后的真实场景:MDN 文档中的复杂 CSS 代码块
mdn-background-8.md并非手写的最小化示例,而是取自 MDN(Mozilla Developer Network)文档中介绍background属性的真实片段,文件内容是一个被 ```css 围栏包裹的、排版相当“混乱”的 CSS 规则:
.plaid-gradient { background: repeating-linear-gradient( 90deg, transparent, transparent 50px, rgb(255 127 0 / 25%) 50px, ... rgb(255 206 0 / 25%) 166px ), repeating-linear-gradient( 0deg, transparent, transparent 50px, ... ), repeating-linear-gradient( -45deg, ... ), repeating-linear-gradient(45deg, transparent, transparent 5px, rgb( 143 77 63 / 25% ) 5px, rgb(143 77 63 / 25%) 10px); background: repeating-linear-gradient( 90deg, transparent 0 50px, ... ); }可以看到,这份代码里存在多种“坏味道”:transparent 69px,一行完全没有缩进、transparent前有多余空格(transparent 50px)、rgb(与参数之间随意换行等。任何对 CSS 敏感的工具看到这样的输入都会“手痒”。这正是该测试用例存在的意义:验证 Biome 的 Markdown 格式化器在遇到这类“格式糟糕但语义合法”的嵌入式代码时,是否会越界去重排 CSS 代码。
期望输出:内容被完整保留
与输入文件配套的期望快照 mdn-background-8.md.prettier-snap 给出了答案:代码块内部的每一行、每一个空格都与输入完全一致,包括:
- 未缩进的
transparent 69px,行; - 行内的多余空格(
transparent 50px); rgb(\n 143 77 63 / 25%\n) 5px这种奇怪的换行。
也就是说,Biome 对待 Markdown 围栏代码块内部内容的态度是verbatim(原样保留):Markdown 格式化器只负责 Markdown 语法层面的排版,不会对围栏内的 CSS/JS 等嵌入式代码做任何重排。这与 Prettier 的行为一致(该测试位于tests/specs/prettier/目录下,属于 Prettier 兼容性测试集),也是 Biome 对齐社区格式化生态的重要一环。
二、源码层面:围栏代码块的格式化实现
2.1 格式化入口与分发
在biome_markdown_formatter中,围栏代码块由FormatMdFencedCodeBlock规则处理,实现在 fenced_code_block.rs。其分发入口是生成的 code_block.rs,根据 AST 节点类型将MdFencedCodeBlock(围栏代码块)与MdIndentCodeBlock(缩进代码块)分别路由:
match node { AnyMdCodeBlock::MdFencedCodeBlock(node) => node.format().fmt(f), AnyMdCodeBlock::MdIndentCodeBlock(node) => node.format().fmt(f), }2.2 围栏长度计算:遵循 CommonMark §4.5
围栏代码块的开闭围栏由反引号`组成。一个容易被忽略的规则是:开闭围栏必须严格长于内容中任何连续同字符序列,否则内容中更长的反引号串会被误解析为结束围栏。FormatMdFencedCodeBlock通过longest_fence_char_sequence函数扫描内容中的连续反引号,并计算规范化后的围栏长度:
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();即:围栏长度取“内容中最长连续反引号数 + 1”与 3 的较大值。例如内容中出现了 ```(3 个反引号),外层围栏就会被规范化为至少 4 个反引号。源码注释明确引用了 CommonMark §4.5 作为依据。随后,开闭围栏都会被替换为这个规范化后的normalized_fence(见format_replaced的使用),从而保证开闭围栏长度一致且不会与内容冲突。
2.3 内容复写:逐行保留,仅处理换行与围栏缩进
真正体现“内容原样保留”的是 code_content.rs 中的FormatMdCodeContent。它对MdCodeContent的value_token做如下处理:
- 跳过 token 两侧的 trivia(避免把开围栏行信息串的空格误当成内容行输出);
- 跳过每行开头、长度不超过
opening_fence_indent的缩进空格(对应“代码块整体缩进对齐”场景,即本文件fenced_code_block.rs中收集的indent长度); - 以
literal_line_breaks逐行输出,不改动行内任何字符; - 对
\r\n、\r、\n三种换行风格分别处理,统一按行结构输出。
换言之,FormatMdCodeContent只做“换行归一化 + 行首围栏缩进剥离”,绝不触碰代码本身的空白与排版。这正是mdn-background-8.md中那些“畸形缩进”得以完整保留的底层原因。
三、测试组织:Prettier 兼容性测试如何驱动该用例
该用例位于tests/specs/prettier/markdown/code/,由 prettier_tests.rs 中的宏自动收集并执行:
tests_macros::gen_tests! {"tests/specs/prettier/markdown/**/*.{md}", crate::test_snapshot, ""}test_snapshot为该类测试固定了格式化选项:
let options = MdFormatOptions::default() .with_indent_style(IndentStyle::Space) .with_indent_width(IndentWidth::default()); let language = language::MarkdownTestFormatLanguage::gfm();即以 GFM(GitHub Flavored Markdown)方言、空格缩进、默认缩进宽度运行格式化,并将结果与同目录下的.prettier-snap期望文件比对。凡是目录下成对出现的*.md与*.md.prettier-snap(如 simple.md、format.md、以及mdn-background-1.md到mdn-background-9.md这一系列 MDN 取材用例),都遵循同一套“输入 + 期望快照”的测试模式。
此外,crates/biome_markdown_formatter/tests/spec_tests.rs中还另有一套 Markdown 规范测试(tests/specs/markdown/**/*.md),由 spec_test.rs 通过Configuration显式开启markdown.formatter.enabled后运行,覆盖更广的 Markdown 语法面。两套测试体系共同守护 Markdown 格式化行为的稳定性。
四、行为总结与实用结论
结合测试输入、期望输出与源码实现,可以总结出 Biome Markdown 格式化器对围栏代码块的确定行为:
| 关注点 | Biome 行为 | 依据 |
|---|---|---|
| 内容行内空白 | 原样保留,不重排嵌入式代码 | code_content.rs、mdn-background-8 快照 |
| 内容换行风格 | 归一化为统一行结构(兼容 LF/CRLF/CR) | 同上 |
| 开闭围栏长度 | 规范化为“内容最长连续反引号 + 1”,最小 3(CommonMark §4.5) | fenced_code_block.rs |
| 代码块整体缩进 | 剥离开围栏行对应的缩进,内容按对齐位置输出 | 同上opening_fence_indent逻辑 |
| 与 Prettier 兼容性 | 由prettier_tests.rs快照测试持续保障 | prettier_tests.rs |
实用结论:如果你使用 Biome 的 Markdown 格式化功能(例如biome format处理.md文件,或在编辑器中通过 LSP 触发格式化),可以放心地预期——文档中围栏代码块内部的 CSS、JavaScript 等代码不会被强行重排;格式化器只调整围栏本身(必要时加长以避免与内容冲突)、修正行尾换行并处理外层缩进。这意味着:想借助 Markdown 格式化来“顺手美化”代码块内代码的做法在 Biome 中不成立,代码块内部排版仍需交给对应语言的格式化器(如 Biome 的biome format对独立 CSS/JS 文件)单独处理。
若想进一步验证或复现,可运行该 crate 的快照测试(对应tests/specs/prettier/markdown/code/mdn-background-8.md),并在修改格式化逻辑后观察.prettier-snap的差异——这正是 Biome 团队维护 Markdown 格式化器回归质量的标准流程。
- 开发工具
- Lint
- 格式化
- 静态分析
- 代码质量
- 前端
【免费下载链接】biome
A toolchain for web projects, aimed to provide functionalities to maintain them. Biome offers formatter and linter, usable via CLI and LSP.
相关推荐
Prettier 如何格式化 Markdown 内嵌 CSS 代码块:以 mdn-background-3 测试用例为引
Prettier 如何格式化 Markdown 内嵌 CSS 代码块:以 mdn background 3 测试用例为引 Markdown 文档中的 CSS 代
开发工具格式化CLIBiome Markdown 格式化器如何保护围栏代码块中的 CSS 内容:以 mdn-font-face-1 测试用例为线索的源码级解析
Biome Markdown 格式化器如何保护围栏代码块中的 CSS 内容:以 mdn font face 1 测试用例为线索的源码级解析 本篇文章以 Biom
开发工具Lint格式化静态分析代码质量前端Prettier 如何格式化 Markdown 代码围栏内的 CSS:以 grid-auto-columns 测试用例为例
Prettier 如何格式化 Markdown 代码围栏内的 CSS:以 grid auto columns 测试用例为例 本篇技术指南以 Prettier 仓
开发工具格式化CLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考