- 文档
- 开发工具
- CLI
【免费下载链接】pandoc
Universal markup converter
本篇技术指南以 pandoc 仓库中的命令测试用例 test/command/5072.md 为切入点,深入解析 pandoc 将 Markdown 紧凑列表转换为 LaTeXenumerate环境时的完整渲染链路:\tightlist命令的定义出处、LaTeX 写入器的判断逻辑、紧凑/宽松列表的判定算法,以及该机制在其他输出格式中的对应实现。读完本文,你将能够准确预测任意 Markdown 列表在pandoc -t latex下的输出形态,并理解--incremental、beamer 等选项与列表紧凑性的相互作用。
一、测试用例全景:一次最小的紧凑列表转换
test/command/5072.md全文是一个标准的 pandoc 命令测试(golden test),这类测试的约定是:代码块内第一行为要执行的 pandoc 命令,随后的行是标准输入,^D表示输入结束,之后是期望的输出。该用例完整内容如下:
% pandoc -t latex -i 1. one 2. two ^D \begin{enumerate} \def\labelenumi{\arabic{enumi}.} \tightlist \item one \item two \end{enumerate}这个用例验证的核心事实是:一个仅由两行1. one、2. two组成的紧凑有序列表,经 pandoc 的 LaTeX 写入器处理后,会生成一个带\tightlist的enumerate环境。命令行中的-t latex指定输出格式为 LaTeX,-i是--incremental的简写(其完整影响见本文第五节)。测试由 test/command/Command.hs 驱动的命令测试框架执行,同目录下的 test/command/1710.md、test/command/4016.md 等用例也包含\tightlist输出,共同覆盖了该行为的各种变体。
二、\tightlist 是什么:模板中的命令定义
\tightlist并非 LaTeX 内建命令,而是 pandoc 在默认 LaTeX 模板中预定义的一个辅助宏。其定义位于模板 data/templates/common.latex 的 "tight lists" 段落:
$-- tight lists $-- \providecommand{\tightlist}{% \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}\providecommand的语义是:仅当该命令尚未被定义时才定义它。这意味着用户可以在自己的 LaTeX 序言(preamble)中预先重定义\tightlist,模板中的定义会被自动跳过,从而实现紧凑间距的个性化定制。
该宏的内容十分直白:将\itemsep(列表项之间的垂直间距)和\parskip(段落间距)均设为0pt,从而把列表项紧凑地排在一起。反过来,若某个列表没有输出\tightlist,enumerate/itemize环境会沿用文档默认的\itemsep与\parskip,列表项之间就会出现明显的段落间隔——这正是"宽松列表"的 LaTeX 表现形态。
三、何时输出 \tightlist:LaTeX 写入器的判断逻辑
\tightlist由 LaTeX 写入器在渲染列表块时按需插入。写入器主文件为 src/Text/Pandoc/Writers/LaTeX.hs,其中blockToLaTeX函数分别处理三类列表:
有序列表(LaTeX.hs#L606-L658):
blockToLaTeX (OrderedList (start, numstyle, numdelim) lst) = do ... let spacing = if isTightList lst then text "\\tightlist" else empty return $ ... $$ text ("\\begin{enumerate}" <> inc) $$ stylecommand $$ resetcounter $$ spacing $$ vcat items $$ "\\end{enumerate}"无序列表(LaTeX.hs#L589-L605)与定义列表(LaTeX.hs#L659-L669)采用同样的模式,唯一的差别是定义列表要求所有条目的内容均为紧凑列表(all (isTightList . snd) lst)时才输出\tightlist。
判定结果直接由isTightList lst决定:列表紧凑则输出\tightlist,否则输出空(empty)。从代码结构可以推断,写入器对"是否紧凑"的判定完全交给共享工具函数,自身不重新实现列表间距的解析逻辑。
四、紧凑/宽松列表的判定算法:isTightList
isTightList定义于共享工具模块 src/Text/Pandoc/Shared.hs#L714-L722:
-- | Detect if a list is tight. isTightList :: [[Block]] -> Bool isTightList = all isPlainItem where isPlainItem [] = True isPlainItem (Plain _ : _) = True isPlainItem [BulletList xs] = isTightList xs isPlainItem [OrderedList _ xs] = isTightList xs isPlainItem _ = False算法要点:
- 列表的每一项都是
[Block](块序列)。当每一项都满足isPlainItem时,整个列表被视为紧凑列表; - 空项(
[])和以Plain(无段落间距的纯文本块)开头的项都算紧凑;而包含Para(有段落间距的段落块)的项会返回False,使整个列表判定为宽松列表; - 递归场景:若某项仅由一个子列表构成,则递归判定该子列表,实现嵌套列表的紧凑性传递。
该判定与 MANUAL.txt 中描述的用户侧规则完全对应:列表项之间无空行 → 紧凑列表;列表项之间有空行 → 宽松列表(宽松列表中的每项会作为独立段落渲染)。测试用例 5072.md 的两行列表项之间没有空行,因此被判为紧凑列表,从而输出\tightlist。
五、命令选项的相互作用:-i / --incremental 与 beamer
5072.md 的命令使用了-i选项,但观察输出可知:在普通 LaTeX 输出中,-i并不会改变列表结构。查看 LaTeX.hs#L594 与 LaTeX.hs#L609 的代码:
let inc = if beamer && incremental then "[<+->]" else ""--incremental(writerIncremental)只有在beamer 输出(stBeamer为真)时才会生效,给itemize/enumerate环境附加[<+->]覆盖参数,实现幻灯片逐条渐显效果。因此:
pandoc -t latex -i:-i被接受但不影响输出,测试用例 5072.md 的输出与不带-i完全一致;pandoc -t beamer -i:列表环境会变为\begin{enumerate}[<+->],配合\tightlist共同控制列表的紧凑布局与逐条展示。
这也解释了 changelog 中 changelog.md#L15508 记录的#5072相关改动:writerIncremental与 beamer 增量展示行为的关联。
六、紧凑列表机制在其他输出格式中的对应实现
isTightList是一个跨格式共享的判定函数,被多个写入器复用,紧凑性并非 LaTeX 独有:
- ConTeXt:src/Text/Pandoc/Writers/ConTeXt.hs#L271 中紧凑列表输出为
\startitemize,宽松列表则附加,packed]参数; - DocBook:src/Text/Pandoc/Writers/DocBook.hs#L265 为紧凑列表的
itemizedlist/orderedlist元素添加spacing="compact"属性; - Djot:src/Text/Pandoc/Writers/Djot.hs#L130-L134 在紧凑/宽松之间切换
D.Tight/D.Loose间距标记; - OpenDocument:src/Text/Pandoc/Writers/OpenDocument.hs#L422-L458 根据紧凑性选择不同的段落样式(如
numberItemTightStyleName等),并包含一个本地的isTightList辅助实现。
可见,"紧凑列表输出为更密排的列表结构"是 pandoc 所有结构化输出格式的共同语义,\tightlist只是该语义在 LaTeX 中的落地形式。
七、动手验证:复现与扩展实验
在当前仓库目录下,可以直接复现 5072.md 的验证过程(需本机已安装 pandoc 与 LaTeX 工具链):
# 复现测试用例的原始命令 printf '1. one\n2. two\n' | pandoc -t latex -i # 验证宽松列表不输出 \tightlist printf '1. one\n\n2. two\n' | pandoc -t latex # 验证无序列表同样受紧凑性控制 printf '* one\n* two\n' | pandoc -t latex # 验证 beamer 下 --incremental 的作用 printf '1. one\n2. two\n' | pandoc -t beamer -i对于宽松列表的输出,可以观察到\tightlist消失、列表项被渲染为带\item的独立段落。若需要自定义紧凑间距,可在生成的.tex文件序言中预先定义自己的\tightlist(由于模板使用\providecommand,自定义定义会优先生效),例如加大\itemsep至2pt以获得视觉上更舒展的列表。
总结
通过 test/command/5072.md 这一个最小测试用例,可以完整还原 pandoc 处理紧凑列表的技术链路:Markdown 源文本的"项间无空行"在解析阶段形成紧凑列表,isTightList(Shared.hs)负责统一判定,LaTeX 写入器(LaTeX.hs)据此决定是否输出模板中预定义的\tightlist(common.latex),并叠加--incremental等选项产生 beamer 特有的增量展示效果。理解这一链路,不仅有助于排查 LaTeX 列表排版差异,也能举一反三地掌握 ConTeXt、DocBook、Djot 等其他格式中紧凑列表的等价实现。
- 文档
- 开发工具
- CLI
【免费下载链接】pandoc
Universal markup converter
相关推荐
Pandoc 命令行黄金测试实战:从 test/command/5857.md 看空列表项的 Markdown 往返输出
Pandoc 命令行黄金测试实战:从 test/command/5857.md 看空列表项的 Markdown 往返输出 本文以 test/command/58
文档开发工具CLIOpCore-Simplify:智能EFI配置引擎,开启Hackintosh自动化新时代
OpCore Simplify:智能EFI配置引擎,开启Hackintosh自动化新时代 在Hackintosh的世界里,每一次成功的安装都像是一次精密的航天任
文档开发工具CLIPandoc 换行语义与 LaTeX 输出深入解析:从 `test/command/3324.md` 看 `\hfill\break` 的实现原理
Pandoc 换行语义与 LaTeX 输出深入解析:从 test/command/3324.md 看 \hfill\break 的实现原理 导读 本文以 pan
文档开发工具CLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考