Pandoc 缩写词与 smart 扩展:从 issue 4635 回归测试看 softbreak 场景下的不间断空格处理
2026/9/20 23:42:02 网站建设 项目流程

Pandoc 缩写词与 smart 扩展:从 issue #4635 回归测试看 softbreak 场景下的不间断空格处理

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

导读

本文围绕 pandoc 仓库中的回归测试 test/command/4635.md 展开,深入剖析 Pandoc 的 Markdown 阅读器在启用smart扩展时如何处理缩写词(如 "cf."、"Mr."):普通空格后自动插入不间断空格(non-breaking space),而换行(softbreak)之后则保持换行原样。读完本文,你将理解--abbreviations选项、默认缩写表data/abbreviations的加载机制、str解析器中\160的插入逻辑,以及 issue #4635 背后"缩写词 + softbreak"这一边界场景的完整修复链路。

测试用例 4635.md 是什么

test/command/4635.md 是 Pandoc 的命令式黄金测试(golden test)用例之一,位于 test/command 目录下,编号对应 GitHub issue #4635。该目录中的每个.md文件都遵循同一约定:% pandoc ...开头书写命令行,随后给出输入,^D表示输入结束,再给出期望的标准输出。

这类测试由 test/Command.hs 驱动:Pandoc 会以文档中指定的参数与输入实际运行一遍,并将结果与文档中记录的期望输出逐字比对。因此 4635.md 不仅是一个 bug 修复的见证,更是一个"可执行的规格说明"——任何回归(例如缩写词后误吞换行、或错误地把 softbreak 替换成空格)都会让测试直接失败。

缩写词的来源:默认数据文件与 --abbreviations 选项

测试中的 "cf." 之所以被特殊对待,是因为它出现在 Pandoc 内置的缩写词表中。

  • 默认缩写表位于 data/abbreviations,共 80 个词条,除 "cf." 外还包括 "e.g."、"i.e."、"Mr."、"Mrs."、"Dr."、"St."、"vs." 等常见英文缩写;
  • 用户可通过命令行选项--abbreviations=FILE指定自定义缩写文件,其参数解析位于 src/Text/Pandoc/App/CommandLineOptions.hs;
  • 加载逻辑位于 src/Text/Pandoc/App.hs 的readAbbreviations函数:未指定文件时读取数据文件abbreviations,指定时读取用户文件,然后按行切分、过滤空行、组装成Set Text。可以用pandoc --print-default-data-file=abbreviations随时查看默认表内容。

从源码注释可以确认:"These currently only affect the Markdown reader."(这些缩写词目前仅影响 Markdown 阅读器),而 MANUAL.txt 也说明:smart扩展会在 "Mr." 这类缩写词之后插入不间断空格(MANUAL.txt)。

smart 扩展与缩写词:源码中的插入逻辑

真正执行"缩写词后插 nbsp"的是 Markdown 阅读器的str解析器,位于 src/Text/Pandoc/Readers/Markdown.hs。其核心逻辑为:

  1. 解析出一段由字母数字与单个点号组成的词(如cf.);
  2. 仅当smart扩展开启(guardEnabled Ext_smart)时,才取出readerAbbreviations选项中的缩写词集合进行成员判断;
  3. 若当前词命中缩写表,则解析其后的空白,并分情况处理:
    • 若空白解析结果是单个Space,则用B.str "\160"(不间断空格)替换,产出Str "cf." + 不间断空格
    • 若结果是其他结构(如SoftBreak,即换行),则原样保留,不替换。

源码中紧邻的注释直接点明了本测试的意图(src/Text/Pandoc/Readers/Markdown.hs):

-- replace space after with nonbreaking space -- if softbreak, move before abbrev if possible (#4635)

也就是说:缩写词后若跟普通空格,需要插入\160防止排版换行断开缩写;但若缩写词后面本身是一个换行(softbreak),则应当尊重这个换行,而不是把它吞掉并换成不间断空格。

四个测试场景逐条解读

4635.md 用 4 个输入/输出对,覆盖了"缩写词后是 softbreak"这一问题的全部形态。以下按原文档顺序完整给出并逐条分析。

场景 1:括号包裹的缩写词后换行

% pandoc -f markdown -t native (cf. foo) ^D [ Para [ Str "(cf." , SoftBreak , Str "foo)" ] ]

输入(cf.换行foo)(cf.被解析为单个Str "(cf.",换行保留为SoftBreakfoo)成为下一个Str。要点:即使cf.是已知缩写,其后是换行而非空格,因此不插入 nbsp,softbreak 完整保留

场景 2:行首有正文、括号包裹的缩写词后换行

% pandoc -f markdown -t native a (cf. foo) ^D [ Para [ Str "a" , Space , Str "(cf." , SoftBreak , Str "foo)" ] ]

在场景 1 基础上增加前置单词a。结果中a(cf.之间是普通Space,而(cf.foo)之间依然是SoftBreak。这确认了括号包裹并不影响缩写词的识别,也不影响"换行保留"的行为。

场景 3:缩写词直接后换行

% pandoc -f markdown -t native cf. foo ^D [ Para [ Str "cf." , SoftBreak , Str "foo" ] ]

最纯粹的情形:cf.独占一行首。尽管cf.命中缩写表,其后是换行,因此产出Str "cf." + SoftBreak + Str "foo",无任何 nbsp 插入。

场景 4:行首有正文、缩写词直接后换行

% pandoc -f markdown -t native a cf. foo ^D [ Para [ Str "a" , Space , Str "cf." , SoftBreak , Str "foo" ] ]

acf.之间是普通Spacecf.foo之间是SoftBreak。四个场景共同验证了同一条不变式:缩写词后的 softbreak 始终原样保留,只有空格才被替换为 nbsp

与普通空格行为的对比:为什么需要 #4635 修复

为了理解上述行为的价值,可以对照smart扩展在"缩写词 + 空格"下的表现:输入cf. foo,输出应为[Para [Str "cf.", Str "\160", Str "foo"]](缩写词与下一词之间是不间断空格\160)。这正是排版上的预期——避免 "cf." 出现在行尾而 "foo" 被挤到下一行,造成视觉断裂。

issue #4635 暴露的问题则在于 softbreak 路径:在没有修复前,缩写词后的换行若被当作普通空白处理,要么会被替换成 nbsp 从而吞掉换行(改变段落换行语义),要么换行被错误地折叠进缩写词处理流程导致与str合并逻辑冲突。修复后的str解析器通过case B.toList ils'对空白结果做模式匹配(src/Text/Pandoc/Readers/Markdown.hs):

case B.toList ils' of [Space] -> return $! (B.str result <> B.str "\160") _ -> return $! (B.str result <> ils')

只有结果恰好是单个Space时才替换为\160;出现SoftBreak等任何其他结构时都原样拼接。这样既保留了 nbsp 的排版价值,又不会破坏 Markdown 源文本中的显式换行。

如何复现与验证

无需搭建复杂环境,只需本仓库源码构建出的 pandoc 可执行文件(或系统安装的 pandoc),即可复现全部四个场景:

# 场景 3:缩写词后换行 printf 'cf.\nfoo\n' | pandoc -f markdown -t native # 场景 4:正文 + 缩写词后换行 printf 'a cf.\nfoo\n' | pandoc -f markdown -t native # 对照:缩写词后普通空格 → 输出中会出现 \160 printf 'cf. foo\n' | pandoc -f markdown -t native

前两条命令的输出应与 4635.md 中记录的期望输出逐字一致。注意该行为依赖smart扩展开启(pandoc 的markdown格式默认启用smart,可用-f markdown-smart关闭验证差异),且与--abbreviations自定义文件配合时会按新表判定命中。

小结

test/command/4635.md以 4 组最小化的输入输出对,精确锁定了 Pandoc 在"缩写词 + softbreak"这一边界场景下的行为契约:缩写词只对紧随其后的普通空格触发 nbsp 替换,对换行(SoftBreak)一律放行。配合 data/abbreviations 默认缩写表、src/Text/Pandoc/App.hs 的加载逻辑、以及 src/Text/Pandoc/Readers/Markdown.hs 的str解析器,读者既能从命令行层面复现现象,也能从源码层面理解机制,是一份兼具"测试规格"与"实现原理"双重价值的参考资料。

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

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

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

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

立即咨询