Folio Writer 的编译流水线覆盖了 99% 的需求——引用、交叉引用、模板、格式。Lua 过滤器则是应对最后 1% 的后门:期刊古怪的内部规定、你自己的简写、任何复选框都没料到的情况。过滤器就是几行 Lua 代码,在编译过程中改写文档,pandoc 原生就能运行它们——无需安装任何东西。
pandoc 编译手稿时,会先把 Markdown 解析成一棵文档树——段落、标题、span、引用。Lua 过滤器就是一组以这些元素命名的函数;每个函数会被每一个匹配的元素调用,并可以返回替换内容。什么都不返回就保持原样,返回 {} 就删除它,返回新元素就变换它。整个模型就是这样。
.lua 文件——放在项目里(例如 Research 中的 Filters 子文件夹),它就会随手稿一起同步和移动。在任意位置输入 @@today;编译后的文档就会带上编译日期——适用于投稿信和草稿水印:
-- today.lua
function Str(el)
if el.text == "@@today" then
return pandoc.Str(os.date("%B %e, %Y"))
end
end
把写给自己的笔记放在围栏 div 里,起草时可见,保证不会出现在输出中:
::: draft Ask Wei whether the 2019 calibration applies here. :::
-- strip-draft.lua
function Div(el)
if el.classes:includes("draft") then
return {} -- deleted from every compiled format
end
end
只把它挂载到投稿配置上,笔记在给导师的草稿配置中仍会出现——按配置区分的过滤器正是为此而设。
让 [key result]{.highlight} 在不同格式中各有真正的含义——在 HTML 中是黄色标记,在 LaTeX PDF 中是高亮:
-- highlight.lua
function Span(el)
if not el.classes:includes("highlight") then return nil end
if FORMAT:match("latex") then
local out = pandoc.Inlines(pandoc.RawInline("latex", "\\hl{"))
out:extend(el.content)
out:insert(pandoc.RawInline("latex", "}"))
return out
elseif FORMAT:match("html") then
local out = pandoc.Inlines(pandoc.RawInline("html", ""))
out:extend(el.content)
out:insert(pandoc.RawInline("html", ""))
return out
end
end
对于 LaTeX 分支,在格式元数据中一次性加入高亮宏包:header-includes: \usepackage{soul}。
两个习惯能让编写过滤器毫不费力。想看清你要匹配的文档树,就在终端里对一段代码运行 pandoc -t native——它会准确打印出你的函数将收到的元素。当编译出了问题时,编译与预览日志会显示完整的 pandoc 命令行及其输出,包括过滤器错误。还有一点 Folio Writer 特有的注意事项:如果过滤器文件被移动了,它会被跳过,而不会导致编译失败——如果某个过滤器似乎悄无声息地失效了,请查看日志。
pandoc Lua 过滤器文档列出了所有元素和辅助函数,社区的过滤器合集里有大量现成的过滤器——缩写处理、表格调整、图表渲染。那里的任何过滤器都可以直接放进编译面板。