ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Biome Markdown 格式化器对 blockquote(引用块)边界的处理:从 notext-end 测试用例看源码实现

2026/9/20 15:35:29 拓冰建站 浏览量
Biome Markdown 格式化器对 blockquote(引用块)边界的处理:从 notext-end 测试用例看源码实现 Biome Markdown 格式化器对 blockquote引用块边界的处理从 notext-end 测试用例看源码实现【免费下载链接】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[!NOTE] 导读 本文以 Biome 仓库中的 Markdown 格式化测试用例crates/biome_markdown_formatter/tests/specs/prettier/markdown/blockquote/notext-end.md为切入点剖析 Biome 的 Markdown 格式化器在遇到「以空引用行结尾」或「引用块内嵌套引用」时如何规范化前缀与空行。读完本文你将掌握该测试用例的输入/输出差异、QuoteBoundaryTrim边界裁剪算法、MdQuotePrefix前缀重建逻辑以及这些行为与proseWrap配置项的联动关系。1. 测试用例背景它测的是什么在 Biome 的 Markdown 格式化测试体系中blockquote/目录存放着一组专门针对引用块blockquote的回归与兼容性用例包含simple.md、nested.md、code.md、list.md、paragraph.md、interrupt-others.md、issue-14228.md以及本文主角notext-end.md等。命名中的notext-end意为「结尾处没有文本」——即引用块最后一行是仅含标记的空引用行而非实际内容行。该文件同时存在三个变体构成完整的「输入—预期—实测」闭环文件作用notext-end.md原始输入未格式化notext-end.md.prettier-snapPrettier 的格式化输出用于兼容性对比notext-end.md.snapBiome 的快照记录输入、与 Prettier 的 diff、Biome 实际输出快照头部信息表明它由crates/biome_formatter_test/src/snapshot_builder.rs生成info字段标注markdown/blockquote/notext-end.md说明该用例会同时跑 Prettier 对比和 Biome 自身输出两条验证链路。1.1 输入内容拆解原始输入由五组引用块组成覆盖了五类典型场景 [!NOTE] DOOM _b_ A B *a* # foo a b This is a quote with an italic _across multuple lines which should just work_. So make sure there is no if we set proseWrap to never This is a quote with a link [across multuple lines which should just work](). So make sure there is no if we set proseWrap to never第一组GitHub 风格的[!NOTE]提示块Alert 语法 内联代码第二组外层引用内嵌双层引用与混排内容为行内代码A、B第三组外层引用内嵌「标题 行内代码比较表达式」ab且引用块之后紧接两个连续空行第四组跨行斜体italic文本在换行处跨过引用前缀第五组跨行链接link同样在换行处跨过引用前缀。其中第四、五组的注释明确提示当proseWrap设为never时换行处不应被插入前缀——这正是本用例要守护的行为边界。2. Biome 输出与 Prettier 的差异快照中的 diff 在说什么Biome 对notext-end.md的实际输出为 [!NOTE] DOOM _b_ A B _a_ # foo a b This is a quote with an italic _across multuple lines which should just work_. So make sure there is no if we set proseWrap to never This is a quote with a link [across multuple lines which should just work](). So make sure there is no if we set proseWrap to never对照notext-end.md.snap中记录的「Prettier differences」diff可以精确还原 Biome 与 Prettier 的两处分歧嵌套引用之间的空引用行被移除Prettier 在 _b_与 A之间保留了一行 仅一个引用标记的空行Biome 则将其删除直接让 _b_ 与下一层引用 A相邻嵌套引用内的标题前空行被移除Prettier 在 # foo之前保留了 空引用行Biome 同样删除并将 ab 与标题直接衔接引用块之间的空行数量被归一化第三组引用块与第四组之间原本有两个空行Prettier 输出中空行被压缩Biome 则保留了两个空行即输入中的空行结构并在 diff 中体现为新增的空行。这三处差异共同指向 Biome 的一个明确设计取向默认非 Preserve 模式下对引用边界仅含前缀、不含任何内容的行做「瘦身」处理但保留引用块之间真实的空行分隔。3. 源码实现边界裁剪从何而来行为背后的核心逻辑位于crates/biome_markdown_formatter/src/markdown/lists/block_list.rs其中定义了三种边界裁剪模式pub(crate) enum QuoteBoundaryTrim { /// Preserve quote-only boundary lines. #[default] None, /// Remove quote-only lines before blockquote content. Leading, /// Remove quote-only lines before and after blockquote content. LeadingAndTrailing, }选择哪种模式由 FormatMdQuote 在格式化每个MdQuote节点时决定let quote_boundary_trim if node.syntax().next_sibling().is_none() { QuoteBoundaryTrim::LeadingAndTrailing } else { QuoteBoundaryTrim::Leading };即若该引用块没有后续兄弟节点是最后一个块则同时裁剪首尾边界否则只裁剪头部边界。这解释了为什么输入中位于文件中间、后面还有其他引用块的组别只处理前置空引用行而不会误删其后的空行。3.1 头部边界扫描算法quote_boundary_trim_start展示了实现细节的微妙之处纯引用行的 CST 形状并不统一——引用块自身前缀对应的首个空行表现为内容列表里的MdNewline而后续的纯引用行则表现为MdQuotePrefixMdNewline成对出现。算法因此只精确匹配这两种形态一旦遇到「前缀后跟真实内容」就立即停止// The first empty line of a blockquote is represented by the quote nodes // own prefix plus a leading newline in the content list. if iter.peek().is_some_and(|(_, block)| block.is_newline()) { iter.next(); start 1; } // Additional quote-only leading lines are represented as // MdQuotePrefix MdNewline pairs. while let Some((prefix_index, AnyMdBlock::MdQuotePrefix(_))) iter.next() { if iter.peek().is_some_and(|(_, block)| block.is_newline()) { iter.next(); start prefix_index 2; } else { break; } }3.2 尾部边界扫描算法quote_boundary_trim_end采用反向扫描只把「末尾的MdQuotePrefix」或「MdQuotePrefixMdNewline」识别为尾部纯引用行。注释特别强调单独的MdNewline不足以判定为边界它只有在前一个条目是MdQuotePrefix时才属于纯引用行——避免误删真实内容行的换行。被判定为边界的条目在FormatMdBlockList和QuoteBlockList中通过format_removed_quote_boundary输出为空从而在最终渲染中消失见 shared.rs 的对应辅助函数。4. 前缀重建MdQuotePrefix的「补空格」行为当引用内容被重新排版后Biome 需要逐行重建前缀。这一逻辑在 quote_prefix.rsif let Some(post_marker_space_token) post_marker_space_token { write!(f, [post_marker_space_token.format()])?; } else { let marker marker_token?; let next_has_text marker .next_token() .is_some_and(|t| t.text().starts_with(|c: char| !c.is_whitespace())); if next_has_text { write!(f, [space()])?; } }若 CST 中保留了后的空格 token原样输出若没有该 token则检查紧随其后的 token 是否以非空白字符开头——是则补一个空格保证 text的规范形态否则不加保持空行形态。此外该文件还支持should_remove选项当引用块以空行开头starts_with_blank_line且裁剪范围非空时FormatMdQuote会让整个前缀以format_removed输出从而把开头的空引用行整体抹掉quote.rs。5. 与proseWrap的联动跨行内容为什么不被插入输入第四、五组的注释点明了关键约束跨行斜体与跨行链接在proseWrap: never下不应在续行补。这与FormatMdQuote的分支结构对应let prose_wrap f.options().prose_wrap(); if prose_wrap ProseWrap::Preserve should_format_quote_structurally(node)? { return Quote::new(node.clone()).fmt(f); }proseWrap: preserve且引用内含「结构性续行」跨行的段落文本等时走Quote::new(...)的专门实现quote.rs其中QuoteParagraph会针对跨行段落判断should_format_quote_continuation_after_newline并配合QuoteLinePrefix按需重建行前缀其余情况含never/always走通用路径用align( , content)对齐内容——该模式只会按块级结构输出前缀不会在段落内部的软换行处硬塞。ProseWrap枚举定义在 context.rs取值preserve/always/never并通过with_prose_wrap注入格式化上下文。因此本用例实际上覆盖了两种渲染策略下的边界稳定性既验证默认对齐模式对纯引用行的裁剪也验证 preserve 模式下跨行文本的前缀重建不会破坏语义。6. 测试如何运行与验证blockquote/用例属于 Prettier 兼容性测试套件。在crates/biome_markdown_formatter/tests/spec_tests.rs中tests_macros::gen_tests!会扫描tests/specs/markdown/**/*.md生成测试函数而prettier/子目录的用例由 prettier_tests.rs 驱动逻辑上与crates/biome_formatter_test的快照构建器协作读取输入 → 分别调用 Biome 与 Prettier 格式化 → 生成.snap快照记录 diff → 断言 Biome 输出与 Prettier 输出的一致性策略。因此notext-end.md的价值是双重的对格式化器开发者它是边界行为的回归守卫任何对QuoteBoundaryTrim、MdQuotePrefix重建逻辑的改动都会在此用例的快照 diff 中原形毕露对使用者它直观展示了 Biome 在 blockquote 嵌套、空引用行、跨行行内元素等复杂组合下的输出约定——嵌套引用的空行会被收敛引用块间的空行保留跨行内容不补前缀。7. 小结通过notext-end.md这一个用例可以串起 Biome Markdown 格式化器关于引用块的三条核心规则边界瘦身默认模式下仅含的纯引用行在块首被裁剪最后一块还会裁剪块尾由QuoteBoundaryTrim与quote_boundary_trim_range实现前缀重建后的空格按「后随 token 是否为文本」动态决定保证输出规范且稳定quote_prefix.rsproseWrap 联动preserve模式下跨行段落走专门的结构化路径quote.rsnever/always下走对齐路径跨行处均不额外插入。如需进一步研究可对比同目录下的 nested.md嵌套引用、code.md代码块边界与 issue-14228.md历史回归问题它们共同刻画了 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),仅供参考