Pandoc--strip-comments实战:彻底清除 Markdown/Textile 源文件中的 HTML 注释
【免费下载链接】pandocUniversal markup converter项目地址: https://gitcode.com/gh_mirrors/pa/pandoc
导读
--strip-comments是 Pandoc 提供的一个布尔型选项,用于在读取 Markdown 或 Textile 源文件时剥离其中的 HTML 注释(<!-- ... -->),而不是像默认行为那样把它们当作 raw HTML 透传到输出文档中。本文以 test/command/2552.md 中的官方命令测试为骨架,完整讲解该选项的语法、默认行为、生效范围、源码实现原理与边界场景,并给出可直接复制的实战示例。读完本文,你将能够准确判断何时该用--strip-comments,以及它在不同输入格式(Markdown / CommonMark / HTML / Textile)和不同扩展组合下的实际表现。
一、命令测试:一条最能说明问题的基线用例
Pandoc 仓库中的test/command/2552.md是一个典型的 golden test(命令测试):它描述了一次完整的命令行执行过程及期望输出,由测试框架读取后运行并比对结果。其内容如下:
% pandoc --strip-comments Foo bar <!-- comment --> baz<!-- bim -->boop ^D <p>Foo</p> <p>bar</p> <p>bazboop</p>测试文件的核心信息:
- 命令行仅带
--strip-comments,不指定输入输出格式,因此按 Pandoc 惯例自动选择 markdown 输入、HTML 输出; - 输入文本包含三种注释形态:
- 独立成段的块级注释
<!-- comment -->; - 内联注释
<!-- bim -->,它把一行文本baz<!-- bim -->boop从中间"切开";
- 独立成段的块级注释
- 期望输出中,注释被完全删除:
- 块级注释连同其所在段落一起消失,
bar前后两个段落仍然各自独立; - 内联注释被移除后,
baz与boop合并成同一个段落<p>bazboop</p>,中间没有残留空格或其他字符。
- 块级注释连同其所在段落一起消失,
注意baz<!-- bim -->boop的合并行为:注释被剥离后两段文本直接拼接为bazboop,这与"注释相当于占位文本、替换为空字符串"的实现一致。仓库中另有一个用例 test/command/7521.md 验证了列表场景:
% pandoc --strip-comments - one <!-- with comm --> - two ^D <ul> <li>one</li> <li>two</li> </ul>它说明:位于列表项之间的块级注释同样会被剥除,且不会在<li>之间留下空项,输出列表保持干净。
二、选项语法与默认行为
2.1 命令行写法
--strip-comments是一个带可选布尔参数的选项,完整的语法为:
--strip-comments[=true|false]- 直接写
--strip-comments,等价于--strip-comments=true; - 显式传
false可关闭:--strip-comments=false; - 若想覆盖配置文件中的设置,可传入
--strip-comments=true显式开启。
2.2 官方手册中的权威定义
MANUAL.txt(Pandoc 官方手册)对该选项的定义如下:
Strip out HTML comments in the Markdown or Textile source, rather than passing them on to Markdown, Textile or HTML output as raw HTML. This does not apply to HTML comments inside raw HTML blocks when the
markdown_in_html_blocksextension is not set.
翻译并拆解为三点关键约束:
- 适用输入格式:Markdown 或 Textile 源文件;
- 默认行为:不开启时,HTML 注释会被当作 raw HTML 原样透传到 Markdown、Textile 或 HTML 输出中;
- 边界条件:当
markdown_in_html_blocks扩展未启用时,位于"raw HTML 块内部的 HTML 注释"不受本选项影响。
2.3 默认值与配置映射
在 Pandoc 的读取器选项中,readerStripComments的默认值为False(见 src/Text/Pandoc/Options.hs 与 src/Text/Pandoc/Options.hs),即默认保留注释。该选项同样暴露在 YAML 元数据与 Lua 读取器参数中:
- YAML 前端数据(
standalone模式):字段名为strip-comments(见 MANUAL.txt 的选项—变量对照表); - Lua API:
ReaderOptions.strip_comments(见 pandoc-lua-engine/src/Text/Pandoc/Lua/Marshal/ReaderOptions.hs)。
也就是说,在文档头部写入strip-comments: true或在 Lua 过滤器里修改读取器选项,可以达到与命令行相同的目的。
三、命令行参数解析源码
命令行选项在 src/Text/Pandoc/App/CommandLineOptions.hs 中定义:
, option "" ["strip-comments"] (OptArg (\arg opt -> do boolValue <- readBoolFromOptArg "--strip-comments" arg return opt { optStripComments = boolValue }) "true|false") OptFlag (T.pack "Strip HTML comments")实现要点:
- 使用
OptArg声明参数为"可选参数"类型,参数取值true|false,这正是"带参数可写可不写"语法(--strip-comments[=true|false])的来源; - 通过
readBoolFromOptArg解析布尔值,未提供参数时视为true; - 解析结果存入
optStripComments字段(src/Text/Pandoc/App/Opt.hs),随后在组装ReaderOptions时映射为readerStripComments。
因此从源码可以确认:这是一个纯粹的读取端(reader)选项,只影响输入解析阶段,与输出格式无关。
四、源码级实现:注释在哪里、如何被剥除
readerStripComments的消费点主要有两处,分别对应 Markdown/CommonMark 读取器和 HTML 读取器。
4.1 CommonMark/Markdown 读取器:解析后遍历剥离
在 src/Text/Pandoc/Readers/CommonMark.hs 中,readCommonMarkBody在解析完成后对 AST 做一次遍历:
(if readerStripComments opts then walk stripBlockComments . walk stripInlineComments else id) <$>对应的剥离函数(src/Text/Pandoc/Readers/CommonMark.hs):
stripBlockComments :: Block -> Block stripBlockComments (RawBlock (B.Format "html") s) = RawBlock (B.Format "html") (removeComments s) stripBlockComments x = x stripInlineComments :: Inline -> Inline stripInlineComments (RawInline (B.Format "html") s) = RawInline (B.Format "html") (removeComments s) stripInlineComments x = x原理拆解:
- Markdown 解析器本身会把 HTML 注释识别为
RawBlock (Format "html")(块级)或RawInline (Format "html")(行内)AST 节点; - 开启选项后,解析完成后用
walk遍历整棵 AST,只对这两类 raw HTML 节点调用removeComments; removeComments(src/Text/Pandoc/Readers/CommonMark.hs)使用 Attoparsec 解析并删除其中的<!-- ... -->片段,解析失败则原样返回;- 剥离后的空字符串节点在后续写出阶段自然消失。
这解释了 2552 测试中的行为:baz<!-- bim -->boop中的注释被解析为 raw inline HTML,剥除后剩bazboop;<!-- comment -->被解析为 raw block,剥除后该块为空,bar两侧的段落边界保持不变。
4.2 HTML 读取器:解析期就地替换
在 src/Text/Pandoc/Readers/HTML.hs 中,HTML 读取器在词法扫描阶段处理TagComment:
TagComment s | "<!--" `T.isPrefixOf` inp -> do string "<!--" count (T.length s) anyChar string "-->" stripComments <- getOption readerStripComments if stripComments then return (next, "") else return (next, "<!--" <> s <> "-->")也就是说,HTML 读取器在识别注释 token 的当下即决定保留还是替换为空字符串,属于"解析期就地处理",与 CommonMark 读取器的"解析后遍历"是两条不同实现路径,但对外行为一致。
五、可复制的实战示例
5.1 独立段落注释(对应 2552 测试)
printf 'Foo\n\nbar\n\n<!-- comment -->\n\nbaz\n' | pandoc --strip-comments输出:
<p>Foo</p> <p>bar</p> <p>baz</p>5.2 行内注释合并文本
printf 'baz<!-- bim -->boop\n' | pandoc --strip-comments输出:
<p>bazboop</p>5.3 列表项之间的注释
printf -- '- one\n <!-- with comm -->\n- two\n' | pandoc --strip-comments输出:
<ul> <li>one</li> <li>two</li> </ul>5.4 对比:不开启选项时注释被透传
printf 'baz<!-- bim -->boop\n' | pandoc输出(Markdown 读取器将注释作为 raw HTML 透传):
<p>baz<!-- bim -->boop</p>这正是--strip-comments要改变的默认行为。需要说明的是,HTML 读取器在解析 HTML 输入时同样受该选项控制:开启后在 token 解析期直接丢弃注释,而不开启时注释会保留在输出中。
5.5 通过 YAML 元数据开启
在standalone文档头部写入(字段名与命令行对应,见 MANUAL.txt 的对照表):
--- strip-comments: true ---六、边界与注意事项
markdown_in_html_blocks扩展:当该扩展未启用、且注释位于 raw HTML 块内部时,--strip-comments不生效(见 MANUAL.txt 的说明)。这是官方文档明确划出的边界,涉及多格式组合时应特别留意。- 仅影响读取端:选项写入
ReaderOptions(readerStripComments默认False,见 src/Text/Pandoc/Options.hs),因此无论输出为 HTML、LaTeX 还是其他格式,只要输入是受支持的格式,剥除行为都发生在解析阶段。 - 只针对 HTML 注释:该选项只处理
<!-- ... -->形式的 HTML 注释,不影响 Lua 注释、其他语言的注释语法,也不涉及按行注释剥离。 - 多格式输入差异:Markdown/CommonMark 走"AST 遍历剥离",HTML 走"token 解析期替换",二者实现位置不同(分别为 src/Text/Pandoc/Readers/CommonMark.hs 与 src/Text/Pandoc/Readers/HTML.hs),但对外行为一致。
- 验证手段:仓库中 test/command/2552.md 与 test/command/7521.md 是官方回归测试,修改相关代码后运行这些测试即可验证行为是否被破坏。
七、小结
--strip-comments是一个实现简洁、边界清晰的读取端选项:它通过修改ReaderOptions.readerStripComments,在 Markdown/CommonMark 读取器中以 AST 遍历的方式、在 HTML 读取器中以 token 替换的方式,将<!-- ... -->注释安全地剥离,避免其以 raw HTML 形式泄漏到最终文档。无论是清理导出文档中的敏感批注、去除模板注释,还是在批量转换流程中统一净化源文件,都可以把pandoc --strip-comments作为标准前置处理步骤。
【免费下载链接】pandocUniversal markup converter项目地址: https://gitcode.com/gh_mirrors/pa/pandoc
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考