Markdown 解析库
起因是本人喜欢在写博客的时候尽量带上目录,可以让整体的结构和思路更加清晰。
但是博客自带的 Markdown 解析库并不是那么好用,虽然能满足基本功能,但是在拓展性上不是很友好。
曾经自己 hook 源代码来实现 TOC
语法,无奈很不优雅,每次升级做 merge 也是很麻烦的事情。
所以干脆整个插件,自己动手,丰衣足食。
自己再去造一个 Markdown 解析器的轮子很显然不合适。于是网上 github 搜索一波,其实 PHP 版本的 Markdown 解析器还真不多。
根据 star 数、活跃度、issue 情况,综合评定下来选择了 erusev/parsedown。
关于TOC
的支持,目前并没有解决方案。作者 erusev 在这个 issues 上回答了,其并没有打算支持,所以我只能自己来了。
Markdown 简明语法手册
源自本插件 Test Case 编辑优化而成。
里面列出的每一项 Markdown 功能点都有示例源码和渲染结果,渲染结果由本插件强力驱动。
1.0.1
然后有了第一版的 Markdown 解析插件:mrgeneralgoo/typecho-markdown(1.0.1)
这版本主要是干了俩件事:
1.更换 Markdown 解析库,在性能和可拓展性上有较大的提升。
2.新增TOC
语法支持。
TOC
的支持实现上是通过 DOMDocument
来操作解析完后的 DOM 元素,然后添加目录元素到 HTML 里面。这个操作会把 HTML 源码转成 NCR 格式,不方便阅读,但也是 HTML 规范之一,不影响使用。
经过自己使用一个月后很稳定,于是开源出来了。但是其他人使用会出现一些问题,比如在某些场景下会出现乱码。
这个可能是DOMDocument
处理内容的时候遇到了特殊的字符串处理出现了问题。
虽然采用了处理 DOM 这种通用且暴力的方案,但是后来我发现就 Markdown 生成目录来说,并不需要这种通用的操作方式,只针对 Markdown 处理就好了。
1.1.0
所以这次有了第二版 Markdown 解析插件:mrgeneralgoo/typecho-markdown(1.1.0)
这次主要变更如下:
1.精简结构,移除暂时无用的 erusev/parsedown-extra 。
2.优化性能,解决乱码问题,使用原生 Markdown 语法来实现目录生成。
3.把TOC
解析的逻辑拆分出来,做成了一个单独的 parsedown-extension 库。
新版本,性能提升近 20%,并且源码不会再被转成 NCR 格式,乱码问题也被彻底解决。
当然过程中也比较坎坷曲折。
第一就是 erusev/parsedown 很不规范,而且文档也少,大部分情况下只能通过作者自己开发的 erusev/parsedown-extra 来学习,推导如何接入,学习成本很高。
第二是 erusev/parsedown 在代码设计上对拓展并不开放,只适合用来做基类。比如我这里实现了TOC
语法,你实现了LaTeX
语法,但是我们俩的代码是不能直接一起使用的。得通过 merge 代码的方式,把功能都集成到一个子类当中才行,要是想像一个插件一样安装即用是不可能的。
我也明白,为什么 joyqi 要自己写一个 Markdown 解析器 SegmentFault/HyperDown 了。
Markdown已经面世许多年了,国内外许多大大小小的网站都在用它,但是它的解析器却依然混乱不堪。SegmentFault 是中国较大规模使用 Markdown 语法的网站,我们一直在使用一些开源类库,包括但不限于
他们都有或多或少的毛病,有的性能较差,有的代码比较业余,更多的情况是由于Markdown本身解析比较复杂,因此我们几乎无法去维护另外一个人写的代码。基于这个原因,我为 SegmentFault 专门编写了这么一个Markdown解析器。
当然,其实有很多设计模式解决第二点,不过鉴于目前还不需要这样做,如果有需要,我还是会尝试一下的😏。
1.1.1
感谢 @fzhihua 反馈的问题,本版本修复多文章列表情况下目录重复问题。
原因在于为了提升性能,解析器使用了单例模式,但是没有做好数据收尾工作,导致了前一篇文章的目录会继续出现在后一篇文章里面,这种 case 只会在多文章情况下会出现,欢迎大家继续反馈 🙇。
1.2.0
本版新增了对 MathJax 语法的支持。
果然,在 1.1.0 版本提到TOC
和LaTeX
语法,这次就有 @yujincheng08 提了 PR 想要支持 MathJax 语法。
这位同学的解决方案没有问题,看得出来还对解析库有一定的了解,所以我测试了下,没问题就给 merge 了。但是从接到 PR 到发布 1.2.0 版本的时候我都在考虑一个问题:如何更方便的拓展解析器的功能。
于是把我对解析库的了解,抽象了 2 个方法来,方便大家理解和接入,这俩个方法分别是:addInlineElements
和addBlockElements
对应的就是相应行元素和块元素的渲染。
然后继续基于元素来分析对应的功能,这次支持 MathJax 语法,关键在于 MathJax 的语法是通过$
和$$
来作为标记符的,标记符包裹的东西会有一些特殊符号,正常情况下会被转义,所以导致公式无法解析成功。
基于此抽象之后本版本要实现的功能其实是:某些标记符包裹的内容不处理。
目标清楚之后,对这位同学的 PR 做了一些细微的处理😂,就是这版的更新由来了。
本版本更新的其实是:
1.新增对特定符号包裹的内容不处理 (顺便支持下 MathJax)
2.新增函数使得拓展其功能更加简单 (之所以不叫接口是因为目前的复杂度还没高到要拆分的地步,如果大家踊跃 PR 壮大起来后就可以拆分了)
show 一下 MathJax 的解析效果(需要引入 MathJax 脚本或者使用 MathJax 插件):
$f(x,y,z) = 3y^2z \left( 3+\frac{7x+5}{1+y^2} \right)$
$f(x,y,z) = 3y^2z \left( 3+\frac{7x+5}{1+y^2} \right)$
$$
F^{HLLC}=\left\{
\begin{array}{rcl}
F_L & & {0 < S_L}\\
F^*_L & & {S_L \leq 0 < S_M}\\
F^*_R & & {S_M \leq 0 < S_R}\\
F_R & & {S_R \leq 0}
\end{array} \right.
$$
$$ F^{HLLC}=\left\{
\begin{array}{rcl}
F_L & & {0 < S_L}\\
F^*_L & & {S_L \leq 0 < S_M}\\
F^*_R & & {S_M \leq 0 < S_R}\\
F_R & & {S_R \leq 0}
\end{array} \right. $$
1.2.1
支持原文输出,当没有检测到 [TOC]
语法的时候不再对标题级别元素添加id
属性 (#9)
1.2.2
[TOC]
语法解析功能支持配置化(默认开启),兼容一些自带目录解析功能的主题。
1.2.3
修复 $$
和 markdown 语法共存在一段有时会导致 markdown 不解析问题 (#13)
More
移步 CHANGELOG
本作品由 程小白 创作,采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可,可自由转载、引用但需署名作者且注明文章出处。
原文地址:https://www.chengxiaobai.cn/php/markdown-parser-library.html
很好用过来感谢下😁
大佬大佬!
我这边电信宽带,发现每次按Ctrl+F5刷新的加载时间都要 10~40 秒 , 时间主要消耗在了mermaid.min这个js文件上, 我尝试把mermaid的CDN地址改到了unpkg.com,只用一两秒就可以完成加载!
感谢反馈,国内网络环境比较复杂,我找个合适的 CDN 替换上。
插件非常棒,满足了我对TeX和mermaid语法的需求。有两个问题请教下:1、
- [ ]
typora中选择框好像没法支持;2、我在使用行间公式时,公式下方总是会渲染出<br>
,导致和下方文字的间隙变大。请问这两个问题有什么解决方式吗?