通过对 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 选项卡,完美满足需求。

至此,实现方案基本就可以确定了:

  1. admonition 功能,直接使用 markdown 库的官方 admonition 拓展就可以;
  2. Code block在 pymdown-extensions 中有一个更好的拓展实现,叫做 SuperFences,但是还是无法满足生成代码块 header 的需求,因此我们考虑对 SuperFences 再做进一步拓展;
  3. 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 实现以上拓展并不容易,难点主要在以下两处:

  1. SuperFences 在解析内容时会校验拓展选项,默认的校验器(validator)只接受 linenums、hl_lines 两个拓展选项,任何多余的选项都无法通过校验,所以我们添加的 filename 拓展选项就无法通过校验,而 SuperFences 并未暴露任何接口可以替换掉默认的校验器。
  2. 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的拓展相关推荐

  1. python粘性拓展_拓展Python Markdown

    通过拓展 Python Markdown 来获得类似 django 官方文档的阅读体验. 最近阅读 django 的官方文档,发现一些很细节的文档内容展现形式,能够极大地提高文档的阅读体验.阅读其他技 ...

  2. 让小乌龟可以唱歌——对Python turtle进行拓展

    在Scratch中,小猫是可以唱歌的,而且Scratch的声音木块有着丰富的功能,在这方面Python turtle略有欠缺,今天我们就来完善一下. Python声音模块 Python处理声音的模块很 ...

  3. Python的unittest拓展和HTMLReport SKIP报表扩展

    C:\Python27\Lib中修改unittest内容 unittest 在init中添加Myskip代码: __all__ = ['TestResult', 'TestCase', 'TestSu ...

  4. Markdown语法拓展 vscode

    VSCode 默认是支持 Markdown 的,但还是有必装下面 3 个插件提高写作效率. Markdown All in One mardownlint Markdown Preview Merma ...

  5. python os.environ_Python拓展11(os.environ获取与设置系统变量)

    os.environ获取与设置系统变量 1.简介 对于官方的解释,environ是一个字符串所对应环境的映像对象.这是什么意思呢?举个例子来说,environ['HOME']就代表了当前这个用户的主目 ...

  6. Python markdown转epub (格式间转换)

    import pypandocdef readme():"""转化文件的格式.convert(source, to, format=None, extra_args=() ...

  7. 关于Python字典的拓展使用

    上一篇中,使用了词典来存储单词,但是词典的功能远远不止于此. 词典可以用在人工对话,伪智能,记录日志,甚至诗词接龙. 词典的应用之对对子: dict = {} digits = '0123456789 ...

  8. 飞象求职学python_用Python制作markdown编辑器

    还记得在上篇提到的rest-framework,文档中提到了markdown也是可选应用. 那么这篇我们就来尝试使用markdown来制作一个在线的可以预览的editor. 安装 Python Mar ...

  9. activiti高亮显示图片_第 09 篇:让博客支持 Markdown 语法和代码高亮

    作者:HelloGitHub-追梦人物 文中涉及的示例代码,已同步更新到HelloGitHub-Team 仓库[1] 为了让博客文章具有良好的排版,显示更加丰富的格式,我们使用 Markdown 语法 ...

最新文章

  1. vivo自带便签新版_原来vivo手机点击这个按钮,还能变成扫描仪,纸质文档一键电子化...
  2. 虚拟化何以四两拨千斤
  3. MySQL-索引优化篇(3)_利用索引优化锁
  4. Dubbo负载均衡与集群容错
  5. 在控制台中录入一个字符串 , 打印这个字符串中的字符以及出现的次数(Python)
  6. 108. Convert Sorted Array to Binary Search Tree
  7. java有趣项目_有趣的java小项目------猜拳游戏
  8. mysql导出如何不区分大小写_mysql 不区分大小写
  9. 基于深度学习的IRS辅助MIMO通信系统的CSI压缩及恢复研究
  10. 微信小程序进度条详解 progress 自定圆形进度条
  11. 门禁系统产品选择与施工要点
  12. Python_今天是今年第几天
  13. python 类 探索
  14. 浅析IRF虚拟化技术增强企业网络架构的弹性
  15. R中的 url编码 和 解码
  16. office communications server 2007 标准版部署详细步骤及错误分析
  17. cenos各个版本下载地址
  18. CNDS博客,文章发布时提示“请勿使用默认标题”
  19. PHP 身份证验证方法
  20. android输入法剪切板历史记录,Android Q 获取剪切板内容

热门文章

  1. 如何成为别人嘴里的高级前端工程师?
  2. 锐起无盘2046 XP 教程(适合新手)
  3. html插音乐的步骤,HTML插入背景音乐方法【全】
  4. TensorFlow-gpuCould not load dynamic library ‘cudart64_102.dll‘; dlerror: cudart64_102.dll not found
  5. NVIDIA Tesla® K80 加速 数字全息实时重建
  6. ORC 2V2 详细心得
  7. MapboxMap 之设置 Style
  8. 数据库系统知识总结(一):数据库系统基础知识
  9. 【工控老马】欧姆龙PLC Socket发送Fins/TCP命令解析
  10. iPhone4s降级ios6.1.3流程总结