ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Biome Markdown 格式化器代码块缩进规范化解析:从 prettier 兼容测试用例到源码实现

Biome Markdown 格式化器代码块缩进规范化解析:从 prettier 兼容测试用例到源码实现 Biome Markdown 格式化器代码块缩进规范化解析从 prettier 兼容测试用例到源码实现【免费下载链接】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本文以 Biome 仓库中的 markdown 格式化测试规格文件 indent.md 为主线深入讲解 Biome Markdown 格式化器biome_markdown_formatter如何规范化缩进代码块Indented Code Block与围栏代码块Fenced Code Block的缩进包括列表嵌套场景的处理、围栏长度归一化、Tab 转空格等核心行为。读完本文你将掌握该测试用例所覆盖的全部格式化规则并能对照源码理解其底层实现机制与测试驱动方式。一、测试用例全貌输入与期望输出该用例位于crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/目录下属于prettier/兼容性测试子集专门验证代码块缩进场景。目录下包含两个配套文件indent.md测试输入同时是断言源文件indent.md.prettier-snap与 Prettier 输出对齐的期望快照。输入内容indent.mdIndented Code Block Indented Code Block Indented Code Block Indented Code Block Indented Code Block - Fenced Code Block Fenced Code Block Fenced Code Block Fenced Code Block Fenced Code BlockChange to your home directory:cdList the contents:ls -l**期望输出indent.md.prettier-snap** markdown Indented Code Block Indented Code Block Indented Code Block Indented Code Block Indented Code Block - Fenced Code Block Fenced Code Block Fenced Code Block Fenced Code Block Fenced Code BlockChange to your home directory:cdList the contents:ls -l对比输入与快照可以提炼出该用例验证的四个关键行为 1. **顶部缩进代码块保持原样**5 行各以 4 个空格缩进的 Indented Code Block格式化后逐字保留不做任何重排 2. **列表内的围栏代码块Fenced Code Block保持嵌套缩进**- 列表项下的围栏代码块其内容行保留列表语义所需的缩进 3. **HTML 注释原样保留**!-- prettier/prettier#3459 -- 用于标注该用例的来源背景格式化器不删除、不挪动它 4. **有序列表标记后的空白被规范化**1. Change 被输出为 1. Change列表标记后补齐为两个空格同时列表项内的缩进代码块cd、ls -l 前 8 个空格原样保留——这正是 Prettier 在 [prettier/prettier#3459](https://link.gitcode.com/i/b56cdcff38c79f3071b000fb2a530b69) 相关场景中的行为约定。 ## 二、缩进代码块Indented Code Block的格式化实现 缩进代码块是 CommonMark 中以 4 个空格或一个 Tab开头的代码块。Biome 在 [indent_code_block.rs](https://link.gitcode.com/i/6b8b75f899e219ac20c3b47cbcb21297) 中实现了 FormatMdIndentCodeBlock 规则其核心设计是**区分“顶层”与“列表内”两条路径**通过 FormatMdIndentCodeBlockOptions 中的 in_list: bool 字段切换见该文件 L8-L25。 ### 2.1 顶层缩进代码块原样透传 当 in_list 为 false 时L31-L57格式化器逐项遍历 content 中的内联节点并直接写出。这里最关键的是对 MdIndentToken 的处理——它调用 FormatMdIndentTokenOptions { replace_tabs_with_spaces: at_line_start, should_remove: false } - 在**行首**位置的缩进 token若内容为 \t 会被替换为 4 个空格 - 非行首位置或已是空格的缩进 token 则原样输出。 Tab 转空格的实现在 [indent_token.rs](https://link.gitcode.com/i/a073bc2e22545dc1b6405ae1bddaec11) 的 L34-L38 rust if self.replace_tabs_with_spaces token.text() \t { format_replaced(token, text( , Some(token.text_range().start()))).fmt(f) } else { token.format().fmt(f) }这就是顶部 5 行 4 空格缩进代码块保持不变的直接原因源文件中每个缩进 token 已是空格且位于行首格式化器按原样写出不增删任何空白。2.2 列表内的缩进代码块最小缩进剥离 对齐当in_list为true时L59-L104情况复杂得多。缩进代码块出现在列表项中时源文件通常携带列表语义所需的前缀缩进格式化器需要计算最小前导空白minimum_leading_whitespaceL108-L120通过LeadingWhitespaceCounterL122-L181逐行统计“包含内容”的行首缩进量取所有行中的最小值剥离多余缩进LinePrefixStripperL183-L232负责从每行行首剥掉与“最小前导空白”等量的空白字符。对MdIndentToken直接删除strip_indent_token对MdTextual文本则用strip_text逐字符处理——注意L213-L215的注释强调原始代码块内的换行不会获得结构性的align()缩进因此保留后续源码缩进确保多行代码仍停留在列表项内部统一对齐到 4 空格外层用align( , ...)包裹L62-L63并显式输出 4 个空格 token使所有代码行对齐到统一的缩进基准。在indent.md用例中有序列表1.下的cd、ls -l各行均以 8 个空格开头列表标记 2 个字符 4 空格缩进 2 个内容的延续缩进最小前导空白剥离后仍保持 8 空格对齐输出快照中原样保留。三、围栏代码块Fenced Code Block围栏长度归一化与列表嵌套用例中- 列表项下嵌入了围栏代码块。Biome 在 fenced_code_block.rs 中实现了FormatMdFencedCodeBlock规则包含两大核心逻辑。3.1 围栏长度自适应CommonMark §4.5L27-L34计算围栏长度先通过longest_fence_char_sequenceL177-L205扫描代码内容中反引号的最长连续序列然后取(max_inner 1).max(3)作为围栏长度。依据 CommonMark 规范外层围栏必须严格长于内容中的同字符最长连续序列否则内容里的会被解析为闭合围栏。例如内容中含 3 个反引号时外层围栏自动升级为 4 个。归一化后的围栏会通过format_replaced替换原围栏 tokenL63-L69、L135-L141确保开闭围栏长度一致。3.2 列表/引用前缀与内容缩进处理L36-L58先探测三种情况是否在列表内text_context.is_list()、内容是否含引用前缀MdQuotePrefix、是否含代码内容MdCodeContent引用前缀存在时前缀行原样输出L49-L51避免结构align()把推进列表内容L73-L88的注释解释了这一约束此时围栏内容整体使用dedent_to_root剥离到根层级再打印无代码内容时列表内采用TextPrintMode::Fill、顶层采用TextPrintMode::Clean打印内容L89-L101有代码内容时逐项处理MdCodeContentL102-L115把opening_fence_indent开围栏所在行的缩进宽度L43-L46累加计算传给代码内容规则。其中开围栏前的indenttoken 与闭围栏前的r_fence_indenttoken在没有引用前缀时会被整体移除L53-L57、L125-L129因为开围栏行的缩进由上层结构列表项统一负责避免重复缩进。3.3 代码内容行首缩进的剥离围栏代码块的内容由 code_content.rs 的FormatMdCodeContent处理。L42-L50的关键逻辑是每一行的行首最多剥离opening_fence_indent个空格——这个宽度正是开围栏行自身的缩进量。这样设计的意图很清晰当围栏代码块嵌套在列表项中时源文件通常会对所有代码行整体缩进以匹配开围栏的缩进级别格式化时这些“结构性”缩进被剥离而代码本身真实的缩进超过开围栏缩进量的部分得到保留。同时L57-L74还专门处理了\r\n与\r换行符保证跨平台换行风格的稳定输出。四、测试驱动spec 测试机制与 Prettier 兼容性验证indent.md之所以能成为“事实标准”依赖 Biome markdown 格式化器的快照测试框架。4.1 测试入口与扫描规则spec_tests.rs 通过tests_macros::gen_tests!宏生成测试扫描模式为tests/specs/markdown/**/*.md即tests/specs/markdown目录下所有.md文件都会自动成为一个快照测试用例。该用例位于prettier/markdown/code/子目录同样被纳入扫描范围。4.2 测试配置与快照断言spec_test.rs 的run函数构造了启用 Markdown 格式化器的Configurationlet config Configuration { markdown: Some(MarkdownConfiguration { formatter: Some(MarkdownFormatterConfiguration { enabled: Some(true.into()), ..Default::default() }), ..Default::default() }), ..Default::default() };随后通过SpecSnapshot::new(...).test()执行格式化并生成快照。.prettier-snap后缀表明该快照同时被用于与 Prettier 输出做一致性对照——这正是prettier/目录的语义凡是此类用例Biome 的输出必须与 Prettier 完全一致。indent.md恰好覆盖了 Prettier 与 Biome 都极其敏感的“列表内代码块缩进”边界场景。4.3 本地复现方式在仓库根目录运行以下命令即可执行 markdown 格式化测试并查看该用例结果cargo test -p biome_markdown_formatter若需精确匹配该用例可结合cargo test -p biome_markdown_formatter indent过滤。测试框架会依据 indent.md 与 indent.md.prettier-snap 断言输出任何格式化行为的回归都会在快照对比中暴露。五、总结缩进规范化的整体心智模型综合indent.md用例与相关源码Biome Markdown 格式化器对代码块缩进的处理遵循如下原则场景处理策略实现位置顶层缩进代码块原样透传仅行首 Tab 转 4 空格indent_code_block.rsL31-L57、indent_token.rs列表内缩进代码块计算最小前导空白并剥离align( )统一对齐indent_code_block.rsL59-L104围栏代码块围栏长度按内容自适应归一化至少 3严格大于内容最长序列fenced_code_block.rsL27-L34列表/引用内围栏开围栏行缩进剥离代码行仅剥离开围栏缩进量引用前缀特殊处理fenced_code_block.rs、code_content.rs从该测试用例可以得出一个明确结论Biome 对代码块缩进的策略是“结构归一、内容保真”——与代码块语义相关的结构性缩进列表嵌套、开围栏缩进由格式化器统一计算与剥离而代码内容本身的缩进绝对不被破坏。这正是indent.md中cd、ls -l前的 8 个空格在格式化后原样保留同时列表标记空白又被规范化的根本原因。理解这条边界也就理解了 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),仅供参考
返回列表