如何在pandoc-filter的stdio模式下补充文档末尾的闭合details标签?
我写了一个pandoc-filter,它利用HTML的details和summary元素(配合下方的CSS),在pandoc把Markdown转成HTML时生成可折叠区块。
区块以Markdown标题行(比如### Section title)开头,假设级别为N的标题之后,下一个标题的级别不会超过N+2。
Javascript代码
let level = 0; filter.stdio(function ({ t, c }, format, meta) { if (t === "Header") { const content = [ filter.RawBlock("html", `<details open><summary>`), filter.Header(...c), filter.RawBlock("html", `</summary>`) ]; for (let i = c[0]; i <= level; i++) content.unshift(filter.RawBlock("html", `</details>`)); level = c[0]; return content; } });
CSS代码
details:not([open]) > summary { list-style: disclosure-closed; } details[open] > summary { list-style: disclosure-open; }
这个过滤器能正常运行,但会生成无效HTML(浏览器会自动修正):文档末尾缺少</details>闭合标签。因为filter.stdio传入的处理函数不会在文档结束时被调用,我找不到添加这些闭合标签的方法。
比如,把它应用到本文的Markdown源时,会生成如下details和summary标签(省略其他标签):
示例CSS:
summary { margin-left: 1em; } details:not([open])>summary { list-style: disclosure-closed; } details[open]>summary { list-style: disclosure-open; }
示例HTML:
<details open> <summary> <h1>The context</h1> </summary> ... <details open> <summary> <h2>Javascript</h2> </summary> ... </details> <details open> <summary> <h2>CSS</h2> </summary> ... </details> </details> <details open> <summary> <h1>The problem</h1> </summary> ... <!-- The </details> end tag is missing. -->
我可以通过编写解析整个Markdown文档JSON表示的pandoc过滤器来解决此问题,但希望继续使用filter.stdio这种逐节点处理的方式。请问如何在filter.stdio模式下添加缺失的</details>闭合标签?
要在filter.stdio模式下补全末尾缺失的</details>,可以监听输入流的结束事件,在文档处理完成后追加需要的闭合标签。具体修改如下:
修改后的Javascript代码:
let level = 0; const filter = require('pandoc-filter'); filter.stdio(function ({ t, c }, format, meta) { if (t === "Header") { const content = [ filter.RawBlock("html", `<details open><summary>`), filter.Header(...c), filter.RawBlock("html", `</summary>`) ]; for (let i = c[0]; i <= level; i++) content.unshift(filter.RawBlock("html", `</details>`)); level = c[0]; return content; } }); // 监听输入流结束,补全剩余的</details>标签 process.stdin.on('end', () => { let closeTags = ''; // 根据当前层级生成对应数量的闭合标签 for (let i = 1; i <= level; i++) { closeTags += '</details>\n'; } if (closeTags) { // 输出符合pandoc AST格式的RawBlock节点 process.stdout.write(JSON.stringify([ { t: 'RawBlock', c: ['html', closeTags] } ])); } });
原理说明
pandoc过滤器通过stdin接收文档的AST(JSON格式),处理后通过stdout输出修改后的AST。当输入流结束时,手动生成包含闭合标签的RawBlock节点并输出,pandoc会自动将这些节点追加到文档末尾,从而补全所有未闭合的details标签。
注意事项
- 确保代码正确引入了
pandoc-filter模块 - 输出的JSON必须符合pandoc AST的格式,这里生成单个
RawBlock节点的数组即可
内容的提问来源于stack exchange,提问作者Heiko Theißen

