Vale 的 blockquote 作用域机制:从 RST 引用块到 text.blockquote 的识别链路
开发工具代码质量CLI【免费下载链接】vale:pencil: A markup-aware linter for prose built with speed and extensibility in mind.项目地址https://gitcode.com/gh_mirrors/va/vale点击查看免费下载reStructuredTextRST文档中的引用块block quote在 Vale 的检查体系里有着独特地位它既不是普通段落也不是代码块而是拥有独立 scopeblockquote的一类文本。在 Vale 仓库中testdata/fixtures/scopes/blockquote/test.rst 就是为验证这一机制而设计的极简夹具——3 行文本里包含引用块外与引用块内两个对照区域用于确认基于 scope 的规则只命中引用块内部。本文将以该夹具为切入点结合 testdata/e2e/scopes.yaml 中的端到端用例、internal/lint/ast.go 与 internal/check/scope.go 的源码实现完整还原RST 引用块 → HTML 标签 →text.blockquote作用域 → 规则命中的整条链路并给出可复制的规则编写与调试方法。一块测试夹具背后的核心问题先看这个夹具文件的完整内容共 3 行We can say here outside of a quote. But we cant say here inside of one!第 1 行是普通段落第 3 行以 4 个空格缩进。在 reStructuredText 语法中缩进块就是引用块block quote不同于 Markdown 的前缀标记RST 用统一缩进表达这段文字是被引用的内容。Docutils 的rst2html在转换时会把它输出为blockquotep.../p/blockquote。这个夹具要回答的问题非常明确当一条规则声明scope: blockquote时它应该命中第 3 行的 here而不是第 1 行的 here。两个 here 在源码中是同一单词唯一的差异就是所在区域的作用域不同——这正是 scope 机制存在的意义同一份文本因为承载它的结构化位置不同可以被不同的规则差异化地检查。同一目录下的三个格式夹具描述了同一语义在不同标记语言中的写法test.rst缩进 4 空格RST 引用块语法test.md But we cant say here inside of one!Markdown 引用语法test.adoc[quote]属性块 ____分隔线AsciiDoc 引用块语法。三个文件最终都要在 Vale 内部被转换为统一的 HTML 表示进而统一为同一个 scope。e2e 测试跨格式验证 blockquote 识别夹具本身不产出任何结论它由端到端测试驱动。在 testdata/e2e/scopes.yaml 的blockquote用例第 105–112 行中- name: blockquote args: . requires: asciidoctor, rst2html exit: 1 want: | test.adoc:5:12:rules.Quote:Dont use here in a blockquote. test.md:3:20:rules.Quote:Dont use here in a blockquote. test.rst:3:22:rules.Quote:Dont use here in a blockquote.注意其中几个关键细节requires: asciidoctor, rst2html——AsciiDoc 和 RST 都不是 Vale 原生解析的格式需要外部转换器参与见下文RST 解析管线一节。测试运行环境缺少这些命令时会自动跳过。期望输出test.rst:3:22——3:22表示告警位于第 3 行第 22 列即引用块内的 here 处第 1 行的 here 没有产生任何告警。这从端到端层面固化了缩进块 引用块的语义。三个格式输出一致——无论 RST、Markdown 还是 AsciiDocscope: blockquote的规则都只命中引用块内部且行列定位都精确指向源文件中的 here 单词。实际驱动测试的规则位于 testdata/fixtures/scopes/rules/Quote.ymlmessage: Dont use %s in a blockquote. extends: existence ignorecase: true scope: blockquote level: error tokens: - here这是典型的existence类规则在blockquote作用域内检查 here 是否出现。另一个更直观的例子是 testdata/fixtures/scopes/matrix/styles/M/Blockquote.yml它用raw字段配合nonword: true直接对引用块文本做子串匹配extends: existence message: blockquote level: error nonword: true scope: blockquote raw: - QBQT规则编写把 scope 写进你的样式要在自己的 Vale 样式中针对引用块定制检查只需在规则定义中加入scope: blockquote例如extends: existence message: 不要在以引号开头的内容中使用 %s。 level: error scope: blockquote tokens: - quotescope 支持多种组合语法它们由 internal/check/scope.go 的NewScope/Scope.Matches实现单独使用scope: blockquote只作用于引用块取反scope: ~blockquote作用于除引用块以外的所有文本。例如普通正文里禁止 maybe但引用中允许AND 链scope: sentence ~blockquote表示作用于句子作用域、但排除引用块内的句子。这在 internal/check/manager_test.go 的TestSplitScope中有直接用例{sentence ~blockquote ~list, []string{sentence}}多值并集scope: [heading, blockquote]任一匹配即生效。注意 scope 名blockquote是 Vale 的保留字internal/core/scopes.go 中reservedScopes明确将其登记在案用户自定义命名 scope 时不能覆盖它。此外Vale 还支持doc(...)选择器直接按 HTML 元素定位如scope: doc(blockquote, ul)见 scopes.yaml 的doc-group用例把引用块与列表统一当作目标块。源码级原理HTML 标签如何变成作用域Vale 的 lint 流水线对所有格式统一收敛为 HTML 后再做作用域标注。关键映射表位于 internal/lint/ast.go 的tagToScopevar tagToScope map[string]string{ th: text.table.header, td: text.table.cell, caption: text.table.caption, li: text.list, blockquote: text.blockquote, figcaption: text.figure.caption, }lintHTMLTokens在逐个消费 HTML 标签时维护tagHistory当某个非内联block 级标签闭合、文本缓冲区被 flush 时调用 lintScope它遍历tagHistory命中blockquote即把当前文本块的作用域标注为text.blockquote再加上文件扩展名等后缀如text.blockquote.rst。这段逻辑里有两个容易被忽略的细节summarized : scope text.blockquote || scope text.list——引用块和列表的文本会被计入文档摘要summary参与 readability 等基于整篇文本的指标计算不会因为不是普通段落而丢失。作用域是按包含关系匹配的规则scope: blockquote匹配块text.blockquote.rst而更细的text.blockquote也能被blockquote前缀包含。这保证了规则可以只写blockquote不必关心后缀细节。Scope.Matches的匹配语义在 internal/check/scope_test.go 中被逐一钉死{[]string{blockquote}, text.blockquote.md, true}, {[]string{blockquote}, text.md, false}, {[]string{~blockquote}, text.md, true}, {[]string{~blockquote}, text.blockquote.md, false}, {[]string{~blockquote ~heading}, text.blockquote.md, false},这些断言直接对应文章开头那条测试意图blockquote只能触达text.blockquote文本块普通段落text与引用块严格隔离取反~blockquote则相反。此外internal/check/manager_test.go 还验证了sentence ~blockquote这类链式 scope 的拆分行为。值得说明的是Vale 的作用域解析对大规模样式有专门的性能优化internal/check/scope.go 注释记载scope 与选择器在历史上被反复解析占整个运行分配量的约 70%650 KB 文件上 18 GB 中的 13 GB因此现在用sync.Map缓存解析结果一次解析、处处复用。RST 解析管线rst2html 与常驻解释器池RST 是 Vale 中少数需要外部工具参与的格式。完整管线位于 internal/lint/rst.golintRST首先在 PATH 上寻找rst2html或rst2html.py、rst2html-3等变体见rstExe找不到即返回E100错误rst2html not found; its needed to read reStructuredTextprepareRST对源文本做预处理把 Sphinx 风格的.. code-block::、.. raw::指令统一改写为::把.. contents::目录改写为.. code::避免目录重复扫描文档标题callRst调起 Docutils 完成rst2html转换得到与 Markdown 同构的 HTML转换产物进入lintRSTHTML最终调用lintHTMLTokens走统一的 HTML 标注路径。性能上有一个值得了解的优化Docutils 单次转换只需几毫秒但启动 Python 解释器并导入 Docutils 需要约 70 毫秒。Vale 因此维护了一个常驻解释器进程池rstServer一个 Python 进程持续接收待转换文档批量场景下还把多个文档拼成一个批次rstSplitMark分隔每块 32 KB并发转换充分利用多核见convertRSTBatch。对 Sphinx 用户internal/lint/rst.go 还展示了未知指令的兜底策略Docutils 不认识的自定义指令如.. versionadded::默认按正文处理代码类指令toctree、math、graphviz等按字面量块跳过未知角色文本按代码处理。项目可在配置的[sphinx]段通过CodeDirectives与ProseRoles扩展这两份名单。跨格式对照RST / Markdown / AsciiDoc 的引用块格式引用块语法转换产物定位示例reStructuredText统一缩进如 4 空格blockquotep…/p/blockquotetest.rst:3:22Markdown行前缀同上test.md:3:20AsciiDoc[quote]____分隔同上test.adoc:5:12三种语法最终都归一为blockquote标签进而归一为text.blockquote作用域。Markdown 路径由 goldmark 渲染见 internal/lint/md.go 的goldMd配置AsciiDoc 路径依赖asciidoctor命令RST 路径依赖rst2html。还有一个与 Markdown 引用相关的实现细节值得注意当告警需要跨行匹配例如一个短语被换行截断时Vale 在 internal/core/location.go 中把引用块的行首标记纳入换行匹配模式\n(?:[ \t]|)*因此引用块内跨行的长短语也能被正确定位到源文件而不会因为干扰而失配。对应回归测试见 internal/core/location_test.go 的TestLocateMatchWrappedLine引用块跨行场景的回归。调试建议与验证方法用 e2e 用例自测本仓库的 testdata/e2e/scopes.yaml 提供了完整的blockquote回归用例可直接作为行为基准。运行前请确认rst2html、asciidoctor已在 PATH 中对应requires字段。从最小夹具起步仿照 testdata/fixtures/scopes/blockquote/test.rst 的结构——引用外一句 引用内一句——验证新规则的命中边界。注意 scope 的包含匹配blockquote会命中所有以text.blockquote开头的块若想更精确可以结合链如sentence blockquote或改用doc(blockquote)选择器粒度从整块到块内元素均可控。留意取反语义~blockquote排除的是引用块本身及其内部一切文本它不会影响text块见TestScopeMatches中~blockquotevstext.md为 true 的断言。小结一块只有 3 行的测试夹具背后是 Vale 一整套格式无关的 scope 基础设施RST 缩进经 Docutils 转成blockquote标签internal/lint/ast.go 的tagToScope把它标注为text.blockquoteinternal/check/scope.go 的包含匹配与取反/AND 链让规则得以精确触达引用块testdata/e2e/scopes.yaml 则用跨三个格式的期望输出把这条链路固化为可回归的行为契约。理解了这条链路你在编写针对引用内容的样式规则时就能准确预判规则会命中哪些文本、跳过哪些文本并利用sentence ~blockquote这类组合精确控制检查范围。赞分享开发工具代码质量CLI【免费下载链接】vale:pencil: A markup-aware linter for prose built with speed and extensibility in mind.项目地址https://gitcode.com/gh_mirrors/va/vale点击查看免费下载相关推荐Vale blockquote 作用域scope完全指南让引文检查既精准又可预期Vale blockquote 作用域scope完全指南让引文检查既精准又可预期 本指南以 Vale 项目测试夹具 testdata/fixtures/s开发工具代码质量CLIVale 的 paragraph 作用域实战解析从 scope 语义到分段Splitting底层机制Vale 的 paragraph 作用域实战解析从 scope 语义到分段Splitting底层机制 导读 本文围绕 Vale 仓库中 paragraph开发工具代码质量CLI终极JavaScript引擎工作机制解析从作用域到原型链的完整指南终极JavaScript引擎工作机制解析从作用域到原型链的完整指南 JavaScript作为Web开发的基石其引擎工作机制直接影响代码执行效率和正确性。本文文档教程上一篇Llama-3.2-1B_rai_1.7.1_npu_4K边缘AI计算的终极解决方案下一篇为什么选择A-Frame Inspector5大理由让3D开发效率提升10倍创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考