Python Markdown的拓展
通过对 Python Markdown的拓展来获得类似 django 官方文档的阅读体验,发现一些很细节的文档内容展现形式,能够极大地提高文档的阅读体验。阅读其他技术文档时也会经常发现类似的内容展现形式。
确定需求
阅读技术类文档经常会看到这么几种内容: Code block 、 Admonition 、 Command tab 。中文不太好翻译,来看一下实际的效果就知道了,下面是 django 中这几种内容的展现形式。
Code block
代码块的上方有一个 header,左边显示代码块所在文件路径,这样示例代码应该放在哪个文件就一目了然;右边是一个按钮,点击即可复制整个代码块中的内容。
Admonition
admonition 用来展现一些提示、警告等内容,文档中经常见到的有危险(danger)、警告(warning)、注意(attention)、重要(important)、提示(hint)等内容,不同类型的内容通常会以不同的背景和字体颜色区分。
Command tab
技术类文档中少不了系统命令,很多相同效果的命令在不同操作系统中的字符内容是有一定差异的。写的不太好的文档通常只给出 Linux 下的执行命令;好点的文档则将执行命令分别列出;而 django 文档的处理就非常细节,以 tab 切换的形式给出不同系统下的命令执行方式,这样既能够列出不同系统下的执行命令,又不会重复占用文档的内容空间,提高了文档的紧凑感和阅读时的流畅性。
我的需求就是要在自己博客文章中实现以上三种内容展现效果。
方案研究
博客文章的标记语言采用的是 Markdown,具体的实现采用的是 Python-Markdown/markdown 这个开源库。这个库不仅实现了 Markdown 标准语法的解析,还提供了很多丰富的拓展语法。
例如需求中提到的 admonition 功能,通过添加 markdown.extensions.admonition 拓展就可以直接实现(具体的实现原理和使用方式下面会介绍)。
Code block的功能也有相应的拓展来实现的,但是调研发现官方自带拓展的功能弱了一点,无法通过拓展的语法在代码块的上方添加 header,只能部分满足需求。开源的第三方拓展中也没有找到可满足需求的实现,所以这里可能需要自己拓展实现。
Command tab功能的实现在 markdown 的第三方拓展库 facelessuser/pymdown-extensions中找到了一个 tabbed 拓展,提供的标记语法可被解析生成一个 tab 选项卡,完美满足需求。
至此,实现方案基本就可以确定了:
- admonition 功能,直接使用 markdown 库的官方 admonition 拓展就可以;
- Code block在 pymdown-extensions 中有一个更好的拓展实现,叫做 SuperFences,但是还是无法满足生成代码块 header 的需求,因此我们考虑对 SuperFences 再做进一步拓展;
- Command tab使用 pymdown-extensions 的 tabbed 拓展可完美满足需求。
具体实现
Admonition
admonition的实现最为简单,只需引入官方 markdown.extensions.admonition 拓展就可以了。它的实现原理是通过下面的语法标记 admonition 的内容:
!!! note "注意"请注意这段内容!
markdown 会把标记内容解析为下面的 HTML 文本:
<div class="admonition note"> <p class="admonition-title">注意</p> <p>请注意这段内容!</p> </div>
编写适当的 CSS 样式,就可以达到类似 django 文档中那样的展示效果了。
参考资料
markdown.extensions.admonition 拓展的使用可参考官方文档 Admonition 。
拓展的引入方式可参考博客项目的源码 blogproject/core/utils.py#L57 。
admonition 的 CSS 样式可参考博客中的源码 frontend/src/style/_admonition.scss 。
Code Block
code block的实现使用 pymdown-extensions 中 SuperFences 拓展,不过遗憾的是,SuperFences 没有在代码块头部添加 header 内容的功能,这样就无法展示代码块所在的文件路径等信息了。花了不少时间读了一下 SuperFences 的源码,遗憾地发现 SuperFences 并没有暴露什么便捷的接口用于对已解析后的内容做进一步加工,如果通过继承等方式进行拓展的话可能需要覆盖重写大量方法,最后决定用一种 monkey patch 的方式进行拓展,以便使需要改动的代码量最小。
首先来看看 SuperFences 提供的代码块标记语法:
``python linenums="1" def print_hello_world():print("hello world") ```
注意到高亮的第一行代码,python 指定代码块中代码属于何种编程语言,其后紧跟的 key=value 形式的键值对是拓展选项(linenums 是代码行号拓展,指定后解析的代码块中的代码将包含代码行号)。
解析后的 HTML 文档大致如下:
<pre class="highlight"><code>...</code></pre>
可惜 SuperFences 原生只提供 linenums、hl_lines 两个拓展选项,我们希望能够添加一个拓展选项 filename,用于指定代码块所属文件路径,并将其值添加到解析后的代码块头部。标记语法如下:
``python linenums="1" filename="pyproject/hello_world.py" def print_hello_world():print("hello world") ```
预期的解析效果:
<div class="literal-block"><div class="code-block-caption">pyproject/hello_world.py</div><pre class="highlight"><code>...</code></pre> </div>
不过想基于 SuperFences 实现以上拓展并不容易,难点主要在以下两处:
- SuperFences 在解析内容时会校验拓展选项,默认的校验器(validator)只接受 linenums、hl_lines 两个拓展选项,任何多余的选项都无法通过校验,所以我们添加的 filename 拓展选项就无法通过校验,而 SuperFences 并未暴露任何接口可以替换掉默认的校验器。
- SuperFences 最终会调用 SuperFencesBlockPreprocessor.highlight 实例方法对代码块做代码高亮处理,然后返回 <pre>...</pre> 预排版内容,这是我们期望的。理想的拓展方法是对 highlight 方法返回的内容再进行包装,即在外层再包上 filename 选项的内容,但是 SuperFences 并未暴露任何接口可以替换 SuperFencesBlockPreprocessor 类,这样就无法通过继承覆盖重写 highlight 方法的方式增强 SuperFencesBlockPreprocessor 。
好在 Python 语言足够灵活,我们可以通过 monkey patch 的方式以最小代码 kill 掉上述两个难点。
对于难点 1,SuperFences 使用的默认校验器 highlight_validator 是定义在 pymdownx.superfences 模块中的顶层函数,因此这里采用的方式就是在 SuperFences 调用这个函数之前,将 highlight_validator 替换为我们自定义的函数,这在 Python 中实现非常简单:
import pymdownx.superfencespymdownx.superfences.highlight_validator = _highlight_validator
_highlight_validator 是我们自定义的函数,放宽了原校验函数的校验逻辑,具体的实现代码可参考本博客的源码 blogproject/core/utils.py#L18 。
对于难点 2,想要对一个类方法返回的结果进一步包装,自然想到类方法装饰器。首先实现一个装饰器,对 highlight 方法返回的结果进行进一步的处理,然后再用 monkey patch 的方式将 SuperFencesBlockPreprocessor.highlight 方法替换为装饰后的方法。具体的实现代码请参考博客的源码 blogproject/core/utils.py#L26 。
最后编写适当的 CSS 样式,就可以达到类似 django 文档中代码块那样的展示效果了。相关的样式代码可参考博客的源码 frontend/src/style/_literal.scss 。
参考资料
SuperFences 拓展还提供了很多丰富的功能,具体使用方式可参考其官方文档 SuperFences。
Command Tab
Command tab借助 pymdown-extensions 的 tabbed 拓展实现,标记语法如下:
=== "Linux/macOS"```bash$ pipenv install django```=== "Windows"```shell...\> pipenv install django```
这段内容将被解析为一段具有 tab 选项卡结构的 HTML 代码段,编写相应的 CSS 样式就可以实现类似 django 文档中那样的命令切换选项卡效果,相关的样式代码可参考博客的源码 frontend/src/style/_tabbed.scss 。
效果演示
来看看最终的实现效果。
Admonition
危险
千万不要进行这样的操作:sudo rm -rf /*。
错误
如果这样做,你将造成不可修复的错误。
警告
如果执行了 sudo rm -rf /* 导致系统无法恢复,后果自负。
当心
千万当心在搜索历史命令时不经意间导致 sudo rm -rf /* 命令的执行。
注意
千万注意你的猫在键盘上乱踩时敲出 sudo rm -rf /* 命令。
重要
最好不要在系统中留下 sudo rm -rf /* 的历史记录。
备注
以上内容请切记。
提示
注意 sudo rm -rf /* 后也是可能被恢复的,所以如果你是删库跑路,一定要采取其他措施掩盖你的行径。
小贴士
物理删除不如心理删除。
Code Block
core/utils.py
def caption_fence_code_format(source, language, css_class, options, md):code = fence_code_format(source, language, css_class, options, md)caption = options.get("filename", "")if caption == "":return codereturn '<div><div class="code-caption">{}</div>{}</div>'.format(caption, code)
Command Tab
Linux/macOS
$ export ENV_VAR=test
Windows
...\> set ENV_VAR=test
Python Markdown的拓展相关推荐
- python粘性拓展_拓展Python Markdown
通过拓展 Python Markdown 来获得类似 django 官方文档的阅读体验. 最近阅读 django 的官方文档,发现一些很细节的文档内容展现形式,能够极大地提高文档的阅读体验.阅读其他技 ...
- 让小乌龟可以唱歌——对Python turtle进行拓展
在Scratch中,小猫是可以唱歌的,而且Scratch的声音木块有着丰富的功能,在这方面Python turtle略有欠缺,今天我们就来完善一下. Python声音模块 Python处理声音的模块很 ...
- Python的unittest拓展和HTMLReport SKIP报表扩展
C:\Python27\Lib中修改unittest内容 unittest 在init中添加Myskip代码: __all__ = ['TestResult', 'TestCase', 'TestSu ...
- Markdown语法拓展 vscode
VSCode 默认是支持 Markdown 的,但还是有必装下面 3 个插件提高写作效率. Markdown All in One mardownlint Markdown Preview Merma ...
- python os.environ_Python拓展11(os.environ获取与设置系统变量)
os.environ获取与设置系统变量 1.简介 对于官方的解释,environ是一个字符串所对应环境的映像对象.这是什么意思呢?举个例子来说,environ['HOME']就代表了当前这个用户的主目 ...
- Python markdown转epub (格式间转换)
import pypandocdef readme():"""转化文件的格式.convert(source, to, format=None, extra_args=() ...
- 关于Python字典的拓展使用
上一篇中,使用了词典来存储单词,但是词典的功能远远不止于此. 词典可以用在人工对话,伪智能,记录日志,甚至诗词接龙. 词典的应用之对对子: dict = {} digits = '0123456789 ...
- 飞象求职学python_用Python制作markdown编辑器
还记得在上篇提到的rest-framework,文档中提到了markdown也是可选应用. 那么这篇我们就来尝试使用markdown来制作一个在线的可以预览的editor. 安装 Python Mar ...
- activiti高亮显示图片_第 09 篇:让博客支持 Markdown 语法和代码高亮
作者:HelloGitHub-追梦人物 文中涉及的示例代码,已同步更新到HelloGitHub-Team 仓库[1] 为了让博客文章具有良好的排版,显示更加丰富的格式,我们使用 Markdown 语法 ...
最新文章
- vivo自带便签新版_原来vivo手机点击这个按钮,还能变成扫描仪,纸质文档一键电子化...
- 虚拟化何以四两拨千斤
- MySQL-索引优化篇(3)_利用索引优化锁
- Dubbo负载均衡与集群容错
- 在控制台中录入一个字符串 , 打印这个字符串中的字符以及出现的次数(Python)
- 108. Convert Sorted Array to Binary Search Tree
- java有趣项目_有趣的java小项目------猜拳游戏
- mysql导出如何不区分大小写_mysql 不区分大小写
- 基于深度学习的IRS辅助MIMO通信系统的CSI压缩及恢复研究
- 微信小程序进度条详解 progress 自定圆形进度条
- 门禁系统产品选择与施工要点
- Python_今天是今年第几天
- python 类 探索
- 浅析IRF虚拟化技术增强企业网络架构的弹性
- R中的 url编码 和 解码
- office communications server 2007 标准版部署详细步骤及错误分析
- cenos各个版本下载地址
- CNDS博客,文章发布时提示“请勿使用默认标题”
- PHP 身份证验证方法
- android输入法剪切板历史记录,Android Q 获取剪切板内容
热门文章
- 如何成为别人嘴里的高级前端工程师?
- 锐起无盘2046 XP 教程(适合新手)
- html插音乐的步骤,HTML插入背景音乐方法【全】
- TensorFlow-gpuCould not load dynamic library ‘cudart64_102.dll‘; dlerror: cudart64_102.dll not found
- NVIDIA Tesla® K80 加速 数字全息实时重建
- ORC 2V2 详细心得
- MapboxMap 之设置 Style
- 数据库系统知识总结(一):数据库系统基础知识
- 【工控老马】欧姆龙PLC Socket发送Fins/TCP命令解析
- iPhone4s降级ios6.1.3流程总结