[分享] (某种程度上)解决了 markdown-toc 不支持中文锚点生成的问题,已被合并至上游

我在尝试使用 markdown-mode + markdown-toc 配合 Pandoc 导出 HTML 时,发现所有含中文的标题全部不能正常从目录跳转,与 Issue #65 一致。追溯提交历史,我注意到 PR #59 新加的条件筛掉了所有非 ASCII 字符,与 Pandoc 的行为不一致,导致生成的锚点与标题的 HTML id 不对应。

阅读 markdown-mode 源码发现,导出 HTML 的命令可能是 pandoc,也可能不是,因为不能保证所有命令生成 id 的逻辑都一致,加上我自己是 Pandoc 用户,所以我打算仅针对 Pandoc 的默认行为在 markdown-toc 包里添加一个选项,当开启时让锚点对齐 Pandoc 的 id 生成算法,默认不开启,不打破原有的逻辑。

我提了一个 PR #70,尝试用 ELisp 复刻 pandoc/src/Text/Pandoc/Shared.hs 中的 textToIdentifieruniqueIdent 函数,当看起来似乎可以解决当前问题的时候提交了这套代码。

但是我很快发现,有各种各样的情况没有正确处理,比如说带变体选择符的 Emoji、粗体 斜体 链接 等 Markdown 格式等。为了彻底理清,我阅读了 pandoc/src/Text/Pandoc/Readers/Markdown.hs 等 Pandoc 源代码,这才发现情况远远比我一开始想的要复杂,比如说代码块 ` ` 中的反斜杠 \ 没有转义作用,比如说区分尖括号 < > 里面是 HTML 标签还是自动链接,再比如说连续 3 个句点 . 会被合并为省略号 (Pandoc “smart punct” 智能标点),等等。

考虑到正则语言不可能完美复刻 Pandoc 成熟的语法树解析器,实现一个完整的解析器过于复杂、有杀鸡用牛刀之嫌,我决定用 ELisp 实现一个基于正则的简化版,能够滤除 Emoji 变体选择符,滤除多数 Markdown 格式,区分代码块内外的反斜杠,处理与句点与中划线有关的智能标点等等,没有外部二进制依赖、效率较高、处理绝大部分情况;再实现一个调用 Pandoc CLI 的完全兼容版,处理一切情况。每一个函数的已知限制,我都已经在其 doc string 中注明。

这几天,我学习了 rx 宏(会说人话的正则,不愧是 Lisp)、dash 箭头宏,接触了 provide、前向声明、Eask,在多轮测试打磨过后,终于做出了一套自己感到满意的方案。特别感谢维护者 @jcs090218 耐心且专业的指导,如果没有 JCS 的帮助,这个 PR 不可能完成

现在更新至最新 markdown-toc 后,(setq markdown-toc-preset 'pandoc) 即可启用 ELisp 版锚点生成算法,设置为 'pandoc-cli 则启用 Pandoc CLI 版。欢迎有需求的道友试用,如有任何改进建议,欢迎回帖讨论!

8 个赞

markdown-toc 實現上一直有些許問題. 很感謝你的協助讓這個包變得更完美, 也讓 Emacs 生態變得更加美好! :smiley:

1 个赞