Pandoc 命令测试 11046:修复 citeproc 脚注围绕破折号的移动行为
2026/9/19 13:24:01 网站建设 项目流程

Pandoc 命令测试 11046:修复 citeproc 脚注围绕破折号的移动行为

【免费下载链接】pandocUniversal markup converter项目地址: https://gitcode.com/gh_mirrors/pa/pandoc

导读

本篇文章围绕 Pandoc 仓库中编号为11046的命令测试文件(test/command/11046.md)展开,该测试对应一次真实缺陷修复:使用--citeproc处理引用时,生成的脚注不应围绕 em-dash(——)移动位置。读者将通过本文理解 Pandoc 命令测试的编写与执行机制、--citeproc结合 CSL note 风格的工作流程,以及mvPunct在 Text.Pandoc.Citeproc 中如何将"破折号"排除在标点移动逻辑之外。

测试文件是什么

命令测试的通用格式

Pandoc 仓库使用test/command/目录下的 Markdown 文件作为**命令测试(command test)**用例。每个文件可以包含一个或多个以% pandoc ...开头的代码块,格式由 test/Tests/Command.hs 定义:

  • 第一行以%开头,后面是要执行的完整命令行;
  • 接下来若干行作为命令的标准输入(stdin);
  • 输入以单独一行^D结束;
  • ^D之后的若干行是期望的标准输出(stdout);
  • 若期望有标准错误输出,各行需以2>前缀标记;
  • 若期望非零退出码,最后一行需以=>开头并给出退出码。

执行时,Tests.Command 会读取command目录下所有.md文件,把每个代码块当作一个独立用例,通过test-pandoc --emulate运行并比对实际输出与期望输出(golden test)。11046.md正是这样一个符合上述格式的测试文件。

11046.md 的完整内容

test/command/11046.md 全文如下:

% pandoc --citeproc -t plain+smart --csl command/chicago-note-bibliography.csl --- references: - id: doe title: Title type: book date: 2006 author: John Doe ... blah blah [@doe]---blah blah. ^D blah blah[1]---blah blah. John Doe. Title, n.d. [1] John Doe, Title.

它由两部分构成:命令 + stdin% pandoc ...^D)与期望输出^D之后)。

命令解析:--citeproc 与 CSL note 风格

命令行各参数的作用

参数含义
--citeproc启用 Pandoc 内建的引用处理引擎(citeproc),解析[@doe]形式的引用并将脚注/参考文献渲染进文档
-t plain+smart输出 plain 格式并开启smart扩展,使破折号、引号等得到智能排版
--csl command/chicago-note-bibliography.csl指定 CSL 样式文件,该文件位于 test/command/chicago-note-bibliography.csl,是 "Chicago Manual of Style (note)" 的 note 类样式

YAML 元数据中的参考文献

stdin 部分的 YAML 块定义了一个 id 为doe的文献条目:

  • title: Title— 书名;
  • type: book— 文献类型为图书;
  • date: 2006— 出版年份 2006;
  • author: John Doe— 作者。

该条目通过references字段直接内嵌在文档元数据中,这也是--citeproc支持的标准文献数据来源之一(另一个常见来源是通过--bibliography指定外部.bib文件)。

note 类 CSL 的渲染特点

chicago-note-bibliography.csl的根元素声明了class="note"(第 2 行),属于脚注式引用样式:正文中的引用被渲染成上标序号脚注,文末另附完整参考文献列表。

从 citation 布局 可以看到:

  • 首次引用渲染为"短注释 + 短标题"的格式;
  • 相同文献再次出现时使用ibid(同上)等缩略处理(position="ibid"分支);
  • 参考文献部分(bibliography)使用悬挂缩进(hanging-indent="true"),并按contributors-sort、标题、体裁、出版年份排序(第 956-962 行)。

输入与期望输出逐行对照

输入行

正文只有一行:blah blah [@doe]---blah blah.

  • [@doe]是 citeproc 的引用语法,指向 id 为doe的文献;
  • ---smart扩展开启时会排版为 em-dash(——);
  • 句末有英文句点。

期望输出

blah blah[1]---blah blah. John Doe. Title, n.d. [1] John Doe, Title.

三段分别对应:

  1. 正文行blah blah[1]---blah blah.— 引用[@doe]被替换为上标脚注序号[1],且位置紧跟在blah之后、em-dash 之前
  2. 参考文献条目John Doe. Title, n.d.— 其中n.d.是"no date(无日期)"的缩写。注意 CSL 样式在 issued 宏 中,当文献缺少可用的issued日期字段时会输出<text term="no date" form="short"/>,即n.d.
  3. 脚注内容[1] John Doe, Title.— 脚注采用短格式(短注释 + 短标题),并保留句点结尾。

源码原理:为什么脚注不围绕 em-dash 移动

问题背景

该测试对应的缺陷记录在 changelog.md:

Text.Pandoc.Citeproc:Don't move footnotes around em-dashes (#11046).

即旧版 Pandoc 在移动标点(punctuation moving)时会错误地把脚注/引号挪到 em-dash 的另一侧,导致blah blah---[1]blah blah.这样不自然的结果。11046.md用 golden test 锁定了修复后的期望行为:脚注序号必须保持在 em-dash之前

mvPunct 与 isPunct 的实现

修复位于 Text.Pandoc.Citeproc 的标点移动逻辑中。核心注释(第 459-462 行)写得很明确:

-- We don't treat an em-dash or en-dash as punctuation here, because we don't -- want notes and quotes to move around them. isPunct :: Char -> Bool isPunct c = isPunctuation c && c /= '\x2014' && c /= '\x2013'
  • \x2014是 em-dash(——)的 Unicode 码点,\x2013是 en-dash(–);
  • isPunct在判断"标点"时显式排除这两种破折号,尽管它们在 Unicode 分类中属于isPunctuation
  • 这样,mvPunct在执行脚注/引号移动时就不会把 em-dash / en-dash 当作可跨越的标点,从而保持脚注序号停留在破折号之前。

从源码结构可以推断,mvPunct负责在引用(Cite)与其相邻标点之间重排顺序(如将句点移动到脚注之后),而将破折号排除后,---两侧的内联元素顺序保持不变,正是11046.md期望输出所验证的行为。

测试如何运行与验证

运行命令测试

Pandoc 的命令测试由 test/test-pandoc.hs 驱动。构建完成后,在仓库根目录执行:

cabal test

或单独运行命令测试子集:

cabal test pandoc-tests --test-options='-p "11046"'

-p使用 tasty 的 pattern 匹配,"11046"会匹配名为Command:11046.md的测试组(每个文件在 Tests.Command 中生成一个同名 test group,文件内的多个代码块依次编号为#1#2…)。

对比测试的实际执行

execTest(test/Tests/Command.hs#L56-L70)会把%后的命令改写为test-pandoc --emulate ...,将^D之前的内容作为 stdin 传入,并比较实际 stdout 与^D之后的期望输出。若两者不一致,会输出带+++/---标记的 diff。因此11046.md不仅是文档,更是一个可持续回归的自动化测试:任何将来重新引入"脚注围绕破折号移动"问题的改动都会立刻导致该用例失败。

延伸:同类 citeproc 测试用例

11046.md只是 Pandoc 庞大 citeproc 测试集的一例。test/command/下还有大量相关用例,例如:

  • test/command/pandoc-citeproc-13.md — 同样使用chicago-note-bibliography.csl,但以-t markdown-citations输出,展示脚注式引用在 Markdown 中的表示:正文为Foo.[^1],参考文献放入{#refs .references .csl-bib-body .hanging-indent}的 Div 容器中,脚注为[^1]: Author, "Title."
  • 其余大量--citeproc用例覆盖 BibTeX 导入、ibid缩略、多作者et al.规则、locator 定位符等场景。

这些用例与11046.md共同构成 citeproc 功能的回归防线,也从侧面说明:--csl指定的 CSL 样式对输出形态(note 式脚注 vs author-date 式括注)起着决定性作用,而mvPunct的标点移动规则则是保证最终排版符合出版习惯的最后一环。

小结

11046.md以最小复现的形式记录了一次精确的排版修复:

  • 输入只需一行包含[@doe]引用与---em-dash 的正文;
  • 期望输出严格约定脚注序号位于 em-dash 之前;
  • 底层由 Text.Pandoc.Citeproc 中isPunct\x2014(em-dash)与\x2013(en-dash)的排除逻辑保证。

无论是想要理解 Pandoc 命令测试的编写范式,还是希望探究 citeproc 标点移动的边界行为,11046.md都是一个足够小而完整的切入点。

【免费下载链接】pandocUniversal markup converter项目地址: https://gitcode.com/gh_mirrors/pa/pandoc

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询