如何编写Lua版Pandoc过滤器 为非标题文本添加术语鼠标悬停提示
问题解决方案
一、报错问题根因与修复
你的代码报错是三个低级错误共同导致的:
- 变量名不匹配:Str回调的入参名是
el,但代码里写的是elem.text,取值失败导致返回nil - 返回值类型错误:Pandoc级过滤器必须返回完整的
pandoc.Pandoc文档对象,原代码遍历到第一个非Header块就直接返回单块处理结果,既丢失了剩余内容,返回值类型也不符合要求 - 遍历逻辑错误:原代码没有将修改后的块写回文档的blocks数组,修改没有生效
二、带标点术语匹配问题修复
原有逻辑是判断整个Str节点的文本完全等于术语key才替换,自然无法匹配CO,/SBP;这类附带标点的场景。需要改用正则拆分Str节点内容,提取所有独立存在的术语,将术语部分替换为带提示的Span,其余文本保留为普通Str节点即可。
完整可运行代码
-- 术语表定义 local glossary = {CO = "Cardiac Output", DBP = "Diastolic Blood Pressure", SBP = "Systolic Blood Pressure"} -- 预编译带单词边界的术语匹配正则,避免误匹配长单词中的片段 local pattern = '%f[%w](' .. table.concat(pandoc.utils.keys(glossary), '|') .. ')%f[%W]' function Pandoc(doc) -- 遍历所有块,仅处理非Header块 for i, block in ipairs(doc.blocks) do if block.t ~= 'Header' then -- 将修改后的块写回文档 doc.blocks[i] = pandoc.walk_block(block, { Str = function(el) local res = {} local last_end = 1 -- 遍历当前Str中所有匹配到的术语 for start, match, end_pos in el.text:gmatch('()' .. pattern .. '()') do -- 插入术语前的剩余文本 if start > last_end then table.insert(res, pandoc.Str(el.text:sub(last_end, start - 1))) end -- 插入带提示的术语Span table.insert(res, pandoc.Span(match, { title = glossary[match], class = 'glossary' })) last_end = end_pos end -- 插入最后一个术语之后的剩余文本 if last_end <= #el.text then table.insert(res, pandoc.Str(el.text:sub(last_end))) end -- 无匹配时返回nil保留原节点,有匹配时返回替换后的节点列表 return #res > 0 and res or nil end }) end end -- 返回修改后的完整文档对象 return doc end
上述代码完全符合你的需求:标题中的术语不会被处理,正文里无论术语是单独出现、紧跟标点、还是在列表中都会被正确识别替换。
内容的提问来源于stack exchange,提问作者cjdbarlow
相关产品推荐
相关产品推荐

