ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Pandoc 列表内行内代码的急切解析与 HTML 安全转义:5627 号命令测试深度解析

Pandoc 列表内行内代码的急切解析与 HTML 安全转义:5627 号命令测试深度解析 文档开发工具CLI【免费下载链接】pandocUniversal markup converter项目地址https://gitcode.com/gh_mirrors/pa/pandoc点击查看免费下载导读本文以 Pandoc 仓库中的命令测试 test/command/5627.md 为切入点深入解析 Markdown 读取器在列表项内“急切”解析行内代码的行为以及 HTML 输出中对--、!--等注释定界符的安全转义机制。读完本文你将掌握 Pandoc 命令行测试文件的格式约定、--something!--这类特殊文本在行内代码与围栏代码块中的处理差异以及有序列表start属性的生成规则并能基于仓库源码解释这些行为背后的实现原理。测试文件背景5627 号变更与 command 测试格式test/command/5627.md是一个典型的 Pandoc “命令测试”command test文件输入为一段 pandoc CLI 调用^D之后为期望的标准输出两者共同构成 golden 测试用例。这类测试由 test/Tests/Command.hs 驱动的测试套件统一执行。该测试对应的功能变更记录在 changelog.md 中Markdown reader: Handle inline code more eagerly within lists (Brian Leung, #5627)。也就是说这个测试验证的是 Markdown 读取器对“列表内行内代码”的解析策略调整当行内代码反引号包裹的 span出现在列表项中时解析器应更积极地完成闭合与配对同时保证代码内容不会意外吞掉后续的列表结构。场景一Example 1——行内代码与围栏代码块中的注释定界符测试的第一段用例完整输入如下% pandoc -t html ## Example 1. One 2. Two --something!-- 3. Three ~~~html --!--scriptalert(Escaped!)/script ~~~ ~~~html Something ~~~ ^D对应的期望输出节选关键部分为h2 idexampleExample/h2 ol type1 liOne/li liTwo code--gt;somethinglt;!--/code/li liThree/li /ol div classsourceCode idcb1pre classsourceCode htmlcode classsourceCode htmlspan idcb1-1a href#cb1-1 aria-hiddentrue tabindex-1/a--gt;span classcolt;!--lt;scriptgt;alert(#39;Escaped!#39;)lt;/scriptgt;/span/span/code/pre/div div classsourceCode idcb2pre classsourceCode htmlcode classsourceCode htmlspan idcb2-1a href#cb2-1 aria-hiddentrue tabindex-1/aSomething/span/code/pre/div这里有两类值得注意的输出行内代码中的转义--something!--被输出为code--gt;somethinglt;!--/code。反引号包裹的内容原样保留但其中的被转义为gt;、被转义为lt;。这意味着无论 Markdown 原文中写的是--HTML 注释的结束定界符还是!--注释的开始定界符进入code元素后都只是普通文本绝不会被浏览器或后续 HTML 解析当作真实注释结构。围栏代码块中的高亮与转义~~~html围栏代码块被渲染为带语法高亮的div classsourceCode结构HTML writer 对内联代码与源码块分别处理源码块输出包含cb1之类的行号锚点。其中--被转义为--gt;而!--scriptalert(Escaped!)/script整体被识别为 HTML 注释并包裹在span classcocomment高亮 span 中script中的单引号也被转义为#39;。从输出可以推断代码块内容经过了两层处理先按 HTML 语法做注释识别与着色再对整个内容做实体转义因此源码块中出现的任何--都不会“逃逸”出precode结构。场景二Example 2——无序列表中的重复行内代码第二段用例换成了无序列表并让多个列表项包含相同的--something!--% pandoc -t html ## Example 2 - --something!-- - --something!-- - bye --something else!-- ~~~html --!--scriptalert(Escaped!)/script ~~~ ~~~html Something ~~~ ^D期望输出中三个列表项都被正确解析code内容全部转义ul licode--gt;somethinglt;!--/code/li licode--gt;somethinglt;!--/code/li libye code--gt;something elselt;!--/code/li /ul这个用例的意义在于重复出现的相同行内代码不会干扰列表结构解析。即使列表项的内容以开头如- --something!--解析器也不会把反引号误判为列表标记的一部分更不会因为代码中含有关似注释定界符的文本而提前结束列表。围栏代码块部分与 Example 1 完全一致验证了输出行为的稳定性。场景三Example 3——编号列表、跨行代码与 start 属性第三段用例最具信息量它同时覆盖了编号列表语义、跨行行内代码的边界行为以及start属性的生成% pandoc -t html ## Example 3 1. --one!-- 5. bye --two !-- 3. three, not in block 1. four, not in block 2. five 5. six 6. seven - separate unordered list 42. forty-two, separate ordered list ^D期望输出h2 idexample-3Example 3/h2 ol type1 licode--gt;onelt;!--/code/li libye code--gt;two lt;!--/code/li li three, not in block/li lifour, not in block /li lifive/li lisix/li liseven /li /ol ul liseparate unordered list /li /ul ol start42 type1 liforty-two, separate ordered list/li /ol逐点解读编号语义列表项的书写编号1.、5.、3.、1.……并不影响输出它们被合并为同一个有序列表输出编号连续为 17。这正是 Markdown 有序列表的标准语义只有第一个列表项的编号决定ol的start属性此处第一项为1.故无start属性仅输出type1。从 Markdown 读取器的列表解析代码 可以印证orderedListStart解析列表项标记后后续项的编号在listStart判定中仅作为“是不是列表项”的判据不再影响列表自身编号。行内代码的急切闭合1.与5.两项中的行内代码在同一行内完成配对--one!--、--two !--。而第 3、4 项中开启反引号位于3.之后潜在的闭合反引号却出现在下一行1. four, not in block的末尾——这一行本身是列表起始。由于行内代码解析不允许跨过列表起始行见下文源码分析配对失败整个行内代码解析回退为字面文本于是输出中保留了字符lithree, not in block、four, not in block/li。第 6 项行尾未配对的反引号同样按字面输出。列表类型切换- separate unordered list 开启了一个新的无序列表其后的42. forty-two又是一个新的有序列表且因为首项编号为 42输出。这说明一旦出现不同标记符- 与数字加句点前一个列表即告终结列表间不存在“跨类型延续”。底层原理Markdown 读取器中的 code 解析器上述行为可以在 src/Text/Pandoc/Readers/Markdown.hs 中找到直接依据。行内代码的解析函数位于该文件第 1644 行附近注释明确写道“parses inline code, between ns and ns”解析器先读取一个或多个反引号作为定界符然后用manyTill循环收集内容内容收集的每一段要么是“非反引号、非换行”的字符要么是反引号串要么是换行符关键在于换行处理遇到换行时解析器执行notFollowedBy (inList listStart)与notFollowedBy blankline两个前瞻检查见 inList 与 listStart。这两个前瞻检查正是“eager within lists”的核心实现当反引号尚未闭合而遇到换行时只要下一行不是空行、也不是新的列表起始行解析器就允许继续跨行收集反之若下一行是空行或列表起始如 Example 3 中的1. four, not in block解析即告失败并整体回退为普通文本。这种“急切”策略让合法的跨行行内代码能尽早闭合同时把列表结构从代码内容中保护出来。另外行内代码的解析结果最终通过B.codeWith attr或B.rawInline syn构造取决于是否启用了raw_attribute/inline_code_attributes扩展随后由 HTML writer 输出时对内容做实体转义从而得到--gt;、lt;!--这样的安全文本。安全视角为什么要转义--与!--HTML 注释以!--开始、以--结束。若文档正文中的代码直接输出未转义的--在特定上下文中可能提前闭合某个注释或干扰 HTML 结构解析而!--scriptalert(...)/script这类文本若不被转义其中的尖括号就可能被浏览器解释为真实标签。从 HTML 读取器的注释处理逻辑 可以看到Pandoc 自身在解析 HTML 注释时也是严格匹配!--与--定界符的TagComment分支要求以!--开头并以--结束。这反过来解释了测试为何刻意构造--!--scriptalert(Escaped!)/script它验证的是写入端的转义能力——无论源码块内容多么像注释或脚本输出端都会把、、等字符实体化使script永远停留在“被高亮的文本”层面而非可执行标签。此外rawHtmlInline 表明原始 HTML 行内标签的解析受raw_html扩展控制。默认 Markdown 输入开启该扩展时正文中的真实标签会被原样保留而测试中的--、!--出现在反引号代码内因此不会被当作标签只作为普通字符流入转义流程。若在解析 HTML 时启用了readerStripComments选项注释则会被剥离见 HTML.hs 中stripComments分支与本文测试的输出行为互为补充。如何复现与验证在本地具备 pandoc 构建环境的前提下可按以下方式复现该测试手动复现将测试文件中^D之前的内容作为输入执行pandoc -t html比对输出是否与^D之后的内容一致。运行整个命令测试套件该文件位于 test/command/5627.md由 test/Tests/Command.hs 统一加载执行可通过项目的测试入口如cabal test或 Makefile 中的测试目标运行。修改验证将--something!--从反引号内移到正文中观察输出变化——正文中的--不再被转义而是原样输出反衬出“代码上下文转义”这一行为边界。小结test/command/5627.md以三个递进的用例锁定了一个小而重要的行为契约列表项内的行内代码会被“急切”解析跨行时以空行和列表起始行为边界代码无论行内code还是围栏precode中的--、!--一律实体转义杜绝注释定界符逃逸与脚本注入有序列表按首项编号生成start属性后续书写编号不影响输出序列。结合 changelog.md 中 #5627 的变更记录与 Markdown 读取器 的code、inList、listStart实现读者既能理解该测试的验收标准也能在自己的 Markdown 工具链或 Pandoc 过滤器开发中复用它作为“列表与行内代码边界行为”的回归参考。赞分享文档开发工具CLI【免费下载链接】pandocUniversal markup converter项目地址https://gitcode.com/gh_mirrors/pa/pandoc点击查看免费下载相关推荐Pandoc 命令测试实战HTML 列表内嵌表格转换为 reStructuredText 的黄金用例解析Pandoc 命令测试实战HTML 列表内嵌表格转换为 reStructuredText 的黄金用例解析 本篇技术指南以 test/command/5898.文档开发工具CLIPandoc 命令行回归测试解析HTML 空内联元素如何被安全转换为 MarkdownPandoc 命令行回归测试解析HTML 空内联元素如何被安全转换为 Markdown 本篇文章围绕 Pandoc 仓库中的命令行回归测试用例 test/co文档开发工具CLIPandoc 定义列表中的行内代码转 LaTeX\item[...] 上下文的 verbatim 安全处理机制解析Pandoc 定义列表中的行内代码转 LaTeX \item ... 上下文的 verbatim 安全处理机制解析 导读 在 Pandoc 中Markdow文档开发工具CLI创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表