资讯详情

mdBook 隐藏代码行(Hide Lines)机制详解:`` 前缀、自定义前缀与 `.boring` 渲染原理

📅 2026/10/5 7:58:18 | 华诺云谱 👁 阅读
mdBook 隐藏代码行(Hide Lines)机制详解:`` 前缀、自定义前缀与 `.boring` 渲染原理
开发工具文档【免费下载链接】mdBookCreate book from markdown files. Like Gitbook but implemented in Rust项目地址https://gitcode.com/gh_mirrors/md/mdBook点击查看免费下载mdBook 内置了一套隐藏代码行Hiding Code Lines功能允许作者在代码块中保留完整示例的同时用特定前缀把一部分行标记为默认隐藏读者可通过代码块上的眼睛图标一键展开。本文以仓库测试用例 hide-lines.md 为主线结合 hide_lines.rs 渲染实现、config.rs 配置结构与期望输出 hide-lines.html完整讲解 Rust 专属的#语法、其他语言自定义前缀、代码块级覆盖hidelines属性以及隐藏行在 HTML 中的真实形态。读完本文你将能够在自己的书籍中精准控制代码示例的默认可见行并理解其背后的逐行解析逻辑。功能概述mdBook 隐藏代码行的核心思路是代码块内的每一行在渲染前都会经过一次前缀扫描以行首允许前置空白是否出现约定前缀来决定该行是否被包裹进隐藏标记。该功能有两个使用层次Rust 代码块开箱即用采用与 Rustdoc 相同的#前缀约定参见官方指南 mdbook.md 的 Hiding code lines 一节其他语言需在book.toml中为每种语言声明一个自定义前缀通过[output.html.code.hidelines]配置项指定单个代码块还可用hidelines属性临时覆盖全局前缀。隐藏的行在最终 HTML 中被包裹为span classboring.../span。boring这一命名直接来自源码 hide_lines.rs前端样式会将这类行默认折叠并在鼠标悬停或点击时显示眼睛图标fa-eye供读者切换显示。Rust 代码块#前缀与 Rustdoc 兼容语法对于标注为rust的代码块mdBook 默认启用隐藏行解析无需任何配置。语法约定如下以##加一个空格开头的行会被隐藏且前缀本身含空格一并从显示文本中剔除以##开头的行用于转义它不会被隐藏渲染时只剥掉一个#保留字面#内容以#直接紧跟非空格字符如#hidden();、#[not_hidden]、#![...]的行不属于隐藏规则按原样显示。这一行为与 Rustdoc 的文档测试隐藏约定一致目的是让作者写出既能被mdbook test完整编译运行、又能让读者只看到重点行的示例。一个完整的 Rust 示例剖析测试用例 hide-lines.md 中的 Rust 代码块原文为#![allow(something)] # #hidden(); # hidden(); ## not_hidden(); #[not_hidden] not_hidden();注意这段代码没有fn main。mdBook 的 HTML 渲染器会先调用 wrap_rust_main当检测到文本中既没有fn main也没有quick_main!时会仿照 rustdoc 的做法把 Rust 内层属性inner attribute如#![...]用 partition_rust_source 单独提取出来然后自动补上隐藏的fn main外壳# #![allow(unused)] #![allow(something)] # fn main() { # #hidden(); # hidden(); ## not_hidden(); #[not_hidden] not_hidden(); # }再经隐藏行扫描后最终渲染结果见 hide-lines.html为span classboring#![allow(unused)] /span#![allow(something)] span classboringfn main() { /spanspan classboring /span#hidden(); span classboringhidden(); /span# not_hidden(); #[not_hidden] not_hidden(); span classboring}/span逐行对照可以看出# #![allow(unused)]、# fn main() {、# }是自动补全的样板#后跟空格全部被隐藏原文的#单独一行被隐藏只留下空行# hidden();被隐藏#与空格被剥掉读者看到的是hidden();## not_hidden();被转义保留渲染为# not_hidden();#hidden();因#后紧跟字符h而不在隐藏规则内原样显示#[not_hidden]、#![allow(something)]同样因#后不是空格而原样显示——这正是内层属性被提取到fn main外壳之外的原因否则它们会被吞进隐藏区。源码中的解析规则上述行为由 hide_lines_rust 实现。它使用正则^(\s*)#(.?)(.*)$对每一行做分组匹配然后按#后的第二个字符分派捕获组(.?)的取值处理方式示例#不隐藏输出# 该行剩余内容转义## not_hidden();→# not_hidden();空串或空格隐藏包裹进span classboring仅保留缩进与#之后的内容# hidden();→hidden();其他任意字符不隐藏原样输出#hidden();、#[not_hidden]、#![...]其中隐藏分支在构造 span 文本时故意丢弃#与随后的空格format!({}{}, caps[1], caps[3])这与读者看到的行不含前缀的直觉一致。注意正则中的(\s*)捕获的是#前的空白缩进隐藏后仍会保留从而维持代码块的视觉对齐。其他语言在book.toml中自定义前缀#隐藏规则默认只对rust代码块生效。要隐藏其他语言的代码行需要在book.toml中为每种语言声明一个前缀字符串配置项位于[output.html.code.hidelines]表内。测试书籍 book.toml 中的真实写法[book] title hidelines [output.html.code.hidelines] python ~配置项说明从 config.rs 的Code结构体可以看到/// A prefix string to hide lines per language (one or more chars). pub hidelines: HashMapString, String,键代码块的语言标识即围栏代码块的语法标注如python、js、bash值一个或多个字符组成的前缀字符串配置归属[output.html.code.hidelines]属于 HTML 渲染器的output.html.code配置族官方文档 renderers.md 也列出了同样的示例hidelines { python ~ }。配置后以该前缀开头的行行首允许有空白缩进即被隐藏。官方指南 mdbook.md 给出了直观对比例子~hidden() nothidden(): ~ hidden() ~hidden() nothidden()渲染后读者默认看到的是hidden() nothidden(): hidden() hidden() nothidden()其中~前缀连同它本身被剥掉~前的缩进保留第三行~ hidden()中~后的缩进、第四行~hidden()中~前的缩进都被保留未加前缀的nothidden():与nothidden()原样显示。前缀匹配规则前缀匹配由 hide_lines_with_prefix 实现关键逻辑为line.trim_start().starts_with(prefix)前缀不必是行的第一个字符前面允许任意空白随后通过line.find(prefix)定位前缀位置将前缀之前的空白与之后的内容共同放入span classboring从而保证隐藏行展开后缩进仍然对齐。代码块级局部覆盖hidelines属性全局配置对整本书生效但单个代码块可以临时换用不同的前缀。做法是在代码块围栏的语言标注后追加hidelines前缀属性逗号或空格分隔与其他代码块属性同格式。测试用例 hide-lines.md 中的第二个 Python 代码块就是这一用法python,hidelines!!! !!!hidden() nothidden(): !!! hidden() !!!hidden() nothidden() 该块完全绕开book.toml中python ~的全局配置改用!!!作为前缀。其期望渲染结果与第一块使用~在隐藏的行集合上完全一致均输出为span classboringhidden() /spannothidden(): span classboring hidden() /spanspan classboring hidden() /span nothidden()从 hide_lines 的入口逻辑可见覆盖的优先级代码块 class 中若存在hidelines前缀信息则优先使用否则才回退到按语言查表hidelines.get(language)。换句话说属性 全局配置 默认关闭非 Rust。渲染原理从解析到span classboring隐藏行处理发生在 HTML 渲染管线中调用链为 tree.rs 中的hide_lines(mut self.tree, code_id, self.options.config.code.hidelines)。整段流程如下HTML 解析阶段将代码块解析为ego_tree::TreeNode语法树代码文本作为该节点的首个文本子节点hide_lines 从代码块元素的class属性中同时提取两样信息language-*语言标识与可选的hidelinesprefix前缀若语言为rust则走 hide_lines_rust 的正则解析路径否则查hidelines配置表拿到前缀或使用属性覆盖值走 hide_lines_with_prefix 的前缀匹配路径匹配到的行被替换为span classboring.../span元素重新挂回语法树后序列化为 HTML。因此隐藏与否在服务端渲染阶段就已定型前端只需处理boring类的显隐切换悬停/点击代码块时显示眼睛图标。这一设计使得隐藏行依然真实存在于 HTML 中而非被删除配合搜索功能时被隐藏的代码仍可被检索到。测试用例验证从hide-lines.md到期望输出仓库中tests/testsuite/rendering/hidelines/目录构成一个完整的端到端测试书籍hide-lines.md测试源文档包含三个代码块覆盖三种场景book.toml声明[output.html.code.hidelines] python ~验证全局自定义前缀hide-lines.html期望渲染结果供渲染测试逐字节比对。三个代码块分别验证了代码块场景关键断言python无属性全局配置前缀~~hidden()、~ hidden()、~hidden()均被隐藏python,hidelines!!!局部覆盖前缀与上一块隐藏结果完全一致证明覆盖生效rust无属性Rust 内建#语法#隐藏、##转义、#hidden();与#[not_hidden]保留、自动补全fn main外壳期望输出还揭示了一个容易忽略的细节Rust 代码块即使没有任何#隐藏标记只要缺少fn mainmdBook 也会自动补上隐藏的fn main包裹并追加#![allow(unused)]——这正是 wrap_rust_main 与partition_rust_source的职责也是 Rust 代码块渲染结果中总能看到fn main() {、}两行boringspan 的原因。常见边界情况与注意事项转义需求如果某行本来就以#开头且希望它可见例如注释掉的代码请用##前缀渲染时只剥掉一个#前缀后必须有空格或行尾#紧跟非空格字符#hidden();、#[derive(...)]、#![feature(...)]不会被隐藏这是与 Rustdoc 一致的刻意设计前缀可含多个字符hidelines配置值与hidelines属性都支持多字符前缀如!!!匹配按字符串整体比较缩进保留前缀前的空白与前缀后的内容都会保留在隐藏行中展开后不影响代码对齐非 Rust 语言默认关闭若不配置[output.html.code.hidelines]Python、JavaScript 等语言代码块不会做任何隐藏处理只有 Rust 块内置该能力优先级代码块hidelines属性优先于book.toml全局配置可用来在个别示例中微调前缀而不影响全书与rustdoc_include的区别{{#rustdoc_include}}是包含外部文件并自动为未选中的行加#前缀而本文所述隐藏行机制作用于普通代码块内部两者最终都依赖同一套#隐藏规则渲染。小结隐藏代码行是 mdBook 提升代码示例可读性的核心特性之一Rust 块零配置使用#前缀并兼容 Rustdoc 约定其他语言通过[output.html.code.hidelines]配置自定义前缀单块可用hidelines覆盖。其实现集中在 hide_lines.rs 的三条处理路径Rust 正则、通用前缀、fn main自动包裹中渲染产物是带boring类的span配合前端眼睛图标实现显隐切换。若要在自己的书籍中验证这些行为直接参考 hide-lines.md 及其期望输出 hide-lines.html 即可。赞分享开发工具文档【免费下载链接】mdBookCreate book from markdown files. Like Gitbook but implemented in Rust项目地址https://gitcode.com/gh_mirrors/md/mdBook点击查看免费下载相关推荐mdBook 全类型 SUMMARY.md 实战解析前缀章节、Part 分篇、分隔符、草稿与后缀章节的解析与渲染mdBook 全类型 SUMMARY.md 实战解析前缀章节、Part 分篇、分隔符、草稿与后缀章节的解析与渲染 本篇技术指南以 mdBook 仓库中用于 G开发工具文档Slidev 代码块行号详解lineNumbers、lines 与 startLine 配置及渲染原理Slidev 代码块行号详解lineNumbers、lines 与 startLine 配置及渲染原理 本文基于 Slidev 官方文档 code block前端开发工具Bourbon前缀处理prefixer自动添加浏览器前缀Bourbon前缀处理prefixer自动添加浏览器前缀 什么是prefixer工具 prefixer是Bourbon提供的核心Sass工具用于自动为CSS前端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑