markdownlint MD009 规则进阶:用 `code_blocks: true` 检查代码块内的行尾空白
开发工具代码质量Lint静态分析【免费下载链接】markdownlintA Node.js style checker and lint tool for Markdown/CommonMark files.项目地址https://gitcode.com/gh_mirrors/ma/markdownlint点击查看免费下载本文以 markdownlint 仓库中的场景测试文档 test/trailing-spaces-in-code-true.md 为切入点深入讲解 MD009no-trailing-spaces规则在开启code_blocks参数后如何把检查范围从普通正文扩展到缩进代码块与围栏代码块并结合规则源码 lib/md009.mjs、官方规则说明 doc/md009.md 与 JSON Schema 配置定义说明每个参数的默认值、取值语义和自动修复行为。读完本文你将能准确判断自己的 Markdown 文档中哪些行尾空白会被 MD009 报告、如何通过配置文件精确控制检查边界以及如何利用测试标记语法验证规则行为。一、测试文档是什么MD009 行为的三段式实验样本markdownlint 仓库把test/目录下的.md文件当作“场景测试样本”每个文件就是一个真实的 Markdown 文档其中嵌入了{MD009:-5}之类的预期错误标记并由测试驱动 test/markdownlint-test-scenarios.mjs 自动 lint 该文件、比对标记行号与实际报告行号最后对结果生成快照 test/markdownlint-test-scenarios.mjs.snapshot。trailing-spaces-in-code-true.md正是这一机制下的一个典型样本。它的文件名即测试意图“代码中的行尾空白Code True”即把no-trailing-spaces规则的code_blocks参数显式配置为true验证代码块内的行尾空白此时也会被报告。文档整体分为三个结构完全相同的段落分别对应 Markdown 的三种代码形态普通段落代码行第 1 段未经缩进、无围栏会被解析为普通文本缩进代码块第 2 段每行以 4 个空格开头构成codeIndented围栏代码块第 3 段以js围栏包裹构成codeFenced。三种形态内部都是同一份 5 行 JavaScript 代码const a 10;到const e 10;但行尾空白数量从 0 递增到 4 个空格形成一组梯度样本随后用一行预期标记注释声明“应当报告哪些行”。二、逐行解读测试样本标记语法与内联配置用sed ... | cat -A查看该文件可看到行尾空白的确切字节分布每行末尾的$表示行结束之前的多余空格即为“行尾空白”# Trailing Spaces in Code - True$ $ const a 10;$ const b 10; $ ← 行尾 1 个空格 const c 10; $ ← 行尾 2 个空格 const d 10; $ ← 行尾 3 个空格 const e 10; $ ← 行尾 4 个空格 $ {MD009:-5} {MD009:-3} {MD009:-2}$ $ const a 10;$ const b 10; $ const c 10; $ const d 10; $ const e 10; $ $ {MD009:-5} {MD009:-3} {MD009:-2}$ $ js$ const a 10;$ const b 10; $ const c 10; $ const d 10; $ const e 10; $ $ $ {MD009:-6} {MD009:-4} {MD009:-3}$文件末尾通过 HTML 注释形式的内联配置inline configuration开启检查!-- markdownlint-configure-file { code-block-style: false, no-trailing-spaces: { code_blocks: true } } --code-block-style: false是为了屏蔽 MD046 对代码块风格一致性consistent的干扰让本样本只聚焦 MD009no-trailing-spaces.code_blocks: true则是本次实验的“自变量”——把检查范围扩大到代码块。预期标记{MD009:-5}的含义测试驱动 test/markdownlint-test-scenarios.mjs 中的正则/\{(MD\d)(?::([-]?)(\d))?\}/g解析这些标记MD009表示期望触发 MD009 规则-5表示“该标记所在行的上数 5 行”为预期错误行-为负向偏移为正向偏移无符号则为绝对行号。因此第一组标记{MD009:-5} {MD009:-3} {MD009:-2}位于第 9 行分别指向上数第 5、3、2 行即第 4、6、7 行——对应行尾为 1、3、4 个空格的三行。注意恰好 2 个空格的const c 10;第 5 行没有被标记这正是 MD009 默认允许 2 个空格作为硬换行hard break的设计。缩进代码块段第 11~15 行同理标记指向第 12、14、15 行。围栏代码块段第 19~25 行因围栏占去一行标记偏移变为-6 -4 -3指向第 21、23、24 行。快照中的完整验证结果在快照 test/markdownlint-test-scenarios.mjs.snapshot 中trailing-spaces-in-code-true.md的用例报告了全部 9 个错误每段 3 个例如{ lineNumber: 4, ruleNames: [MD009, no-trailing-spaces], ruleDescription: Trailing spaces, errorDetail: Expected: 0 or 2; Actual: 1, errorRange: [14, 1], fixInfo: { editColumn: 14, deleteCount: 1 }, severity: error }errorDetail的 “Expected: 0 or 2” 呼应了规则参数br_spaces默认值为 2即允许 0 个或恰好 2 个行尾空格fixInfo则给出可自动修复的列位置与删除数量证明 MD009 属于“Fixable”可修复规则doc/md009.md 顶部亦有此说明。三、规则源码解析code_blocks到底改了什么要理解为什么true与默认行为不同需要看规则实现 lib/md009.mjs。核心逻辑如下let brSpaces params.config.br_spaces; brSpaces Number((brSpaces undefined) ? 2 : brSpaces); const codeBlocks params.config.code_blocks; const includeCode (codeBlocks undefined) ? false : !!codeBlocks; const listItemEmptyLines !!params.config.list_item_empty_lines; const strict !!params.config.strict; const codeBlockLineNumbers new Set(); if (!includeCode) { for (const codeBlock of filterByTypesCached([ codeFenced ])) { addRangeToSet(codeBlockLineNumbers, codeBlock.startLine 1, codeBlock.endLine - 1); } for (const codeBlock of filterByTypesCached([ codeIndented ])) { addRangeToSet(codeBlockLineNumbers, codeBlock.startLine, codeBlock.endLine); } }这段代码揭示了code_blocks的真实机制它不是“开启后新增一类检查”而是“关闭时预先豁免代码块”。默认情况下includeCode false规则通过 micromark 解析出的codeFenced围栏代码块与codeIndented缩进代码块两类 token把它们的行号收集进codeBlockLineNumbers集合随后逐行检查时跳过这些行——这正是 doc/md009.md 所说明的默认允许代码块内的行尾空白因为“某些编程语言要求行尾空白有意义”。当code_blocks: true时includeCode为真codeBlockLineNumbers保持为空集合豁免逻辑失效代码块行与普通文本行一视同仁地接受行尾空白检查。围栏代码块的豁免区间是startLine 1到endLine - 1围栏行本身不豁免围栏后的行尾空白照常检查而缩进代码块是整个startLine到endLine区间。addRangeToSet来自 helpers/micromark-helpers.cjs负责把闭区间内的行号批量加入集合。逐行判定与自动修复随后的主循环逐行计算行尾空白数量并依据参数组合决定是否报告const expected (brSpaces 2) ? 0 : brSpaces; for (let lineIndex 0; lineIndex params.lines.length; lineIndex) { const line params.lines[lineIndex]; const lineNumber lineIndex 1; const trailingSpaces line.length - line.trimEnd().length; if ( trailingSpaces !codeBlockLineNumbers.has(lineNumber) !listItemLineNumbers.has(lineNumber) ( (expected ! trailingSpaces) || (strict (!paragraphLineNumbers.has(lineNumber) || codeInlineLineNumbers.has(lineNumber))) ) ) { const column line.length - trailingSpaces 1; addError( onError, lineNumber, Expected: (expected 0 ? : 0 or ) expected ; Actual: trailingSpaces, undefined, [ column, trailingSpaces ], { editColumn: column, deleteCount: trailingSpaces } ); } }关键点expected (brSpaces 2) ? 0 : brSpacesbr_spaces只有设为2 或更大才生效设为 1 与 0 行为相同即完全禁止行尾空白doc/md009.md 明确强调“必须将br_spaces设为 2该参数才会生效”。判定条件为“有行尾空白”且“不在豁免集合中”且数量不等于expected或 strict 模式下的特殊场景。报告错误时附带fixInfo: { editColumn, deleteCount }供applyFixes自动删除多余空格——这正是快照中fixed字段内容变化的原因。其他三个参数对检查范围的影响br_spacesinteger默认2允许的行尾空格数用于显式硬换行。list_item_empty_linesboolean默认false设为true时允许列表项内的空行保留空格部分解析器有此要求即使在strict: true下也生效doc/md009.md。源码中通过listItemLineNumbers集合对listOrdered/listUnordered的listItemPrefix、listItemIndent、content子 token 进行行号收集与回收。strictboolean默认false默认情况下即使行尾空格数量恰好等于允许值只要不构成硬换行例如出现在段落末尾也不会报告strict: true会把这类“不必要的换行”也揪出来。源码中表现为把paragraph与codeText行内代码的行号纳入专门集合后参与条件判断。四、三份对照样本默认、false与true的行为差异仓库中还有两份同系列对照样本构成完整的行为矩阵样本文件配置报告数量报告范围test/trailing-spaces-in-code-default.md仅code-block-style: false未设置code_blocks3 个仅普通段落第 4、6、7 行test/trailing-spaces-in-code-false.mdcode_blocks: false显式关闭3 个仅普通段落第 4、6、7 行test/trailing-spaces-in-code-true.mdcode_blocks: true显式开启9 个普通段落 缩进代码块 围栏代码块从快照 test/markdownlint-test-scenarios.mjs.snapshot 可以确认default与false两份样本的errors数组都只有 3 项只覆盖普通段落只有true样本是完整的 9 项。这验证了规则源码中“codeBlocks undefined与显式false等价”的行为——undefined与false都会让includeCode为假。自动修复结果的对比三份样本快照中的fixed字段同样值得对比。以true样本为例修复后的内容为换行符已归一化# Trailing Spaces in Code - True const a 10; const b 10; const c 10; const d 10; const e 10; ...可见修复策略是删除数量不为 2 的行尾空格1、3、4 个被清除但保留恰好 2 个空格的行因为 2 个空格是合法的硬换行。default/false样本中代码块内的行尾空白因被豁免而原样保留在fixed输出里这从侧面印证了“只有code_blocks: true时修复才会触及代码块行”。快照之外测试驱动还会对修复结果再次执行 lint 并断言“不存在仍有fixInfo的错误”保证自动修复一次到位test/markdownlint-test-scenarios.mjs 中 “Fixable error(s) not fixed.” 断言。五、在实际项目中如何配置与使用配置文件写法在项目根目录创建.markdownlint.json或.markdownlint.jsonc、.markdownlint.yaml即可全局开启对代码块行尾空白的检查{ no-trailing-spaces: { code_blocks: true, br_spaces: 2, list_item_empty_lines: false, strict: false } }对应的 YAML 写法no-trailing-spaces: code_blocks: true br_spaces: 2 list_item_empty_lines: false strict: false如果只想对某个文件临时启用可以在该文件内使用与测试样本相同的内联配置注释!-- markdownlint-configure-file { no-trailing-spaces: { code_blocks: true } } --运行检查的命令行工具为 markdownlint-cli / markdownlint-cli2见 README.md示例npx markdownlint-cli2 **/*.md参数取值速查表依据 JSON Schema规则在配置 schema schema/markdownlint-config-schema.json 中定义如下参数参数类型默认值说明enabledbooleantrue是否启用该规则severitystringerror报告级别可选error/warningbr_spacesinteger最小值 02允许用于换行的行尾空格数必须 2 才生效code_blocksbooleanfalse是否检查代码块内的行尾空白list_item_empty_linesbooleanfalse是否允许列表项内空行保留空格strictbooleanfalse是否报告未构成换行的多余空格规则整体还支持直接写布尔值no-trailing-spaces: false或error/warning字符串来快速启停。一条实用的决策建议是否开启code_blocks: true取决于你的代码块内容形态如果代码块内主要是 JavaScript、Python、Go 等对行尾空白不敏感、编辑器通常不会留尾随空格的代码开启后可以帮你保持仓库“零行尾空白”的整洁状态也便于 CI 统一校验如果文档中混有对行尾空格敏感或需要保留尾随空白的内容例如部分 shell 脚本、格式文件、逐字复制的日志片段建议保持默认false或用strictlist_item_empty_lines组合做更细粒度的控制可参考 test/trailing-spaces-in-lists-allowed-strict.md 的用法。六、小结从测试样本到规则的完整闭环test/trailing-spaces-in-code-true.md虽然看起来只是一个 34 行的测试数据文件但它完整覆盖了 MD009 规则的关键行为面三种代码形态普通段落、缩进代码块、围栏代码块在同一配置下的统一表现行尾空格数量的梯度0/1/2/3/4验证了br_spaces 2的“0 或 2 个允许”语义标记语法{MD009:-N}精确描述了预期行号与规则实现、快照三方互相印证内联配置注释展示了无需全局配置文件即可局部调整规则参数的官方用法。当你理解了code_blocks: true在 lib/md009.mjs 中“取消豁免”的本质后就可以举一反三同样的 token 豁免思路也适用于 MD010制表符、MD040 等与代码块相关的规则。测试文档既是回归测试的资产也是理解规则语义最直接、最权威的“活文档”。赞分享开发工具代码质量Lint静态分析【免费下载链接】markdownlintA Node.js style checker and lint tool for Markdown/CommonMark files.项目地址https://gitcode.com/gh_mirrors/ma/markdownlint点击查看免费下载相关推荐markdownlint MD009 代码块尾随空格豁免code_blocks 参数与内联配置实战markdownlint MD009 代码块尾随空格豁免code_blocks 参数与内联配置实战 本文围绕 markdownlint 的 MD009no开发工具代码质量Lint静态分析markdownlint MD009 尾随空格规则代码块与严格模式的完整配置实战markdownlint MD009 尾随空格规则代码块与严格模式的完整配置实战 导读 本文以 markdownlint 仓库中的 code block tr开发工具代码质量Lint静态分析markdownlint 规则 MD009no-trailing-spaces深入解析尾随空白的检测、配置与自动修复markdownlint 规则 MD009no trailing spaces深入解析尾随空白的检测、配置与自动修复 本篇文章围绕 markdownlin开发工具代码质量Lint静态分析上一篇Decepticon 文件媒介式多 Agent 协作契约Workspace 目录布局、终端状态交接与编排器读取协议下一篇大模型下载太慢3 条镜像路线 1 份避坑清单3 分钟跑通创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

AI安全网关怎么选?2026六大能力模块拆解与13家国内外厂商图谱

AI安全网关怎么选?2026六大能力模块拆解与13家国内外厂商图谱

厂商信息为公开资料整理,截至 2026 年 10 月;能力描述以各厂商公开资料为准,排列不构成排名。法规条款以官方发布文本为准。2023 年春天,三星半导体部门的工程师为了排查代码问题,把一段源代码粘贴进了 ChatGPT。几周之…

2026/10/17 10:56:35 阅读更多 →
MimiClaw:在ESP32-S3上跑通MCU级AI智能体的感知决策闭环

MimiClaw:在ESP32-S3上跑通MCU级AI智能体的感知决策闭环

“MimiClaw”这名字一出来,我第一反应是玩具出新款了,或者某个智能音箱换了壳。直到把项目仓库拉下来、把固件烧进一块 ESP32-S3 开发板,我才意识到自己最开始的理解偏了方向——它不是“在 MCU 上跑了个能聊天的模型”,而是把一整…

2026/10/16 8:48:49 阅读更多 →
我的Java面试复习路线与踩坑记录

我的Java面试复习路线与踩坑记录

最近在准备 Java 秋招,把面试八股重新过了一遍,顺手记录一下我的复习路线和几个印象最深的坑关于复习顺序:我试过上来就背 JVM,直接劝退;后来老老实实从 JavaSE 和集合重走一遍,反而顺了几个印象深的坑&…

2026/10/16 8:48:49 阅读更多 →

最新新闻

从RNN到Transformer:Attention机制原理与工程实践

从RNN到Transformer:Attention机制原理与工程实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 13:18:27 阅读更多 →
AIGC检测时代,本科生论文如何用降AI率工具与WPS AI写出自然人风格?

AIGC检测时代,本科生论文如何用降AI率工具与WPS AI写出自然人风格?

2. 写在最前面:这个标题,戳中了多少本科生的痛点 “导师又让重写”——这几个字,我估计在座各位本科阶段都见过。尤其是理工科写课程报告、文科写结课论文的时候,辛苦攒出来的几千字,导师一句“语言不够凝练”“表达太…

2026/10/17 13:18:27 阅读更多 →
感知机原理与三种Python实现:NumPy、scikit-learn与PyTorch可视化对比

感知机原理与三种Python实现:NumPy、scikit-learn与PyTorch可视化对比

说到感知机算法原理,我见过很多开发者的第一反应是:这算法不是已经被深度学习淘汰了吗?实际情况恰恰相反。感知机虽然简单到只有一行决策规则,但它几乎是理解一切神经网络的基础原型。我第一次认真手写感知机代码时,最…

2026/10/17 13:18:27 阅读更多 →
Java集合框架核心解析:从ArrayList到HashMap的底层原理与习题自证法

Java集合框架核心解析:从ArrayList到HashMap的底层原理与习题自证法

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 13:18:27 阅读更多 →
Claude Code 自定义模型切换痛点与 CC Switch 配置管理实践

Claude Code 自定义模型切换痛点与 CC Switch 配置管理实践

很多人一开始接触 Claude Code 的时候都会问:这玩意儿是不是只能用自带的模型?实际上完全可以把它接成自定义模型,只要你愿意处理那一堆破配置。我之前就干过这种蠢事:为了在家里的开发机上把 Claude Code 接到某个支持自定义API的…

2026/10/17 13:18:27 阅读更多 →
基于MP2642GR-Z与STM32F446RE的两串电池被动均衡方案设计与实现

基于MP2642GR-Z与STM32F446RE的两串电池被动均衡方案设计与实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 13:17:26 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/16 21:35:51 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/17 1:44:59 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 21:54:38 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →