使用 GNU m4 为 Markdown 添加目录支持

garfileo

我的大部分文档都是先在我机器上的 Nikola(静态网站生成器)环境中撰写,然后发表于『段错误』网站。在本地存有备份总比没有备份要好,云时代也不例外。

Nikola 支持多种标记语言,我只用过 RST 与 Markdown。RST 用起来要比 Markdown 繁琐一些,但它的标记也更丰富一些,譬如 RST 提供了产生文章目录的标记(Table of Contents, TOC),而 Markdown 没有这一标记。不过,由于 Markdown 支持 HTML 的直接嵌入,它所缺乏的功能,我们可以从其他途径予以补充。

本文讲述的是如何基于 GNU m4 为 Markdown 增加几个标记使之支持文章目录的生成。当然,你可以不必了解 GNU m4,只需按照本文的方法逐步进行即可获得这一能力。

section.m4 脚本

假设所有的 Markdown 文件都存放在 nikola/posts 目录。可使用以下命令,在 nikola 中建立 m4 目录,然后在 m4 目录中建立空文件 section.m4:

$ cd nikola ; mkdir m4 ; cd m4
$ touch section.m4

然后将以下内容写入 section.m4 文件:

m4_divert(-1)
m4_define(`_SEC_', `m4_divert(1)* [$1](#$1)
m4_divert(2)<span id="$1"></span>
# $1')

m4_define(`_TOC_',
`m4_divert(1)**目录**

m4_divert(2)m4_dnl')

m4_changequote(`@!--', `--!@')
m4_divert(0)m4_dnl

这份 m4 脚本主要定义了两个宏: _TOC__SEC_。在用 Markdown 标记撰写文档时,可以将这两个宏作为我们『扩展』的标记来使用。

Markdown 文档的 m4 化

下面来看如何在 Markdown 文档中使用 section.m4 脚本。

假设我们在 nikola/posts 目写了一份 m4_demo.markdown 文档,其内容如下:

m4_include(`section.m4')m4_dnl
<!-- 
.. title: 如何写一个 GNU 风格的命令行程序
.. slug: how-to-write-a-gnu-cmd-program
.. date: 2016-01-16 09:00:41 UTC+08:00
.. tags: C 语言, GNU Autotools
.. category: 0. 编程基础
.. link: 
.. description: 
.. type: text
-->

_TOC_

_SEC_(GNU Autotools 不黑)

我见过很多人抱怨 GNU Autotools 太复杂。事实上它比求解一元二次方程简单多了……

_SEC_(月亮)

我要构建的这个程序名曰 **moon**,它属于 **zero** 项目。不要问为什么,因为它就应该叫 moon……

_SEC_(造月亮的原料)

现在,我执行以下命令……

m4_demo.markdown 文件首部出现的

m4_include(`section.m4')m4_dnl

表示加载上一节给出的 section.m4 脚本(请注意 section.m4 两侧所用的引号形式),接下来的 <!-- ... ... --> 文本是 Nikola 自动生成的文件元信息。再接下来是 _TOC_ 标记,表示将在此处插入文件目录。之后,只需将每一节的标题由原来的 # 标题名 换成 _SEC_(标题名) 即可。

生成目录

在 nikola/posts 目录下执行以下命令:

$ m4 -P -I ../m4 m4_demo.markdown > demo.markdown

结果得到的 demo.markdown 内容如下:

<!-- 
.. title: 如何写一个 GNU 风格的命令行程序
.. slug: how-to-write-a-gnu-cmd-program
.. date: 2016-01-16 09:00:41 UTC+08:00
.. tags: C 语言, GNU Autotools
.. category: 0. 编程基础
.. link: 
.. description: 
.. type: text
-->

**目录**

* [GNU Autotools 不黑](#GNU Autotools 不黑)
* [月亮](#月亮)
* [造月亮的原料](#造月亮的原料)

<span id="GNU Autotools 不黑"></span>
# GNU Autotools 不黑

我见过很多人抱怨 GNU Autotools 太复杂。事实上它比求解一元二次方程简单多了……

<span id="月亮"></span>
# 月亮

我要构建的这个程序名曰 **moon**,它属于 **zero** 项目。不要问为什么,因为它就应该叫 moon……

<span id="造月亮的原料"></span>
# 造月亮的原料

现在,我执行以下命令……

原来在 m4_demo.markdown 文档中的 _TOC_ 标记被替换为:

**目录**

* [GNU Autotools 不黑](#GNU Autotools 不黑)
* [月亮](#月亮)
* [造月亮的原料](#造月亮的原料)

而每个形如 _SEC_(标题) 的标记被替换为下面的形式:

<span id="标题"></span>
# 标题

*

由 m4_demo.markdown 经 m4 命令转换而成的 demo.markdown 就是含有文章目录信息的 Markdown 文档。如果你用的 Markdown 没有自作主张的去篡改 Markdown 文档中嵌入的 HTML 标记,那么它就可以将这份 demo.markdown 处理为带有文章目录信息的 HTML 文档。在这方面,Nikola 很良心。不过,很多号称支持 Markdown 标记的博客类网站却往往会对 Markdown 文档中嵌入的 HTML 标记进行了可耻的过滤……这个世界从未让人省心过。

在我的机器上,这篇文档会被 Nikola 渲染为下图所示的结果。

markdown目录

Markdown 元文档

本节内容为 Nikola 用户而设。

上一节所用的 m4_demo.markdown 文档是放在 nikoila/posts 目录中的,而生成的 demo.markdown 文档也在这一目录。当 Nikola 扫描 nikola/posts 目录生成静态网页时,它对这两份 Markdown 文档都进行了处理,这显然不是我们所期望的。

要规避 Nikola 的扫描,可在 nikola/posts 目录中在创建一个 metadoc 目录,然后将 m4_demo.markdown 文档以 demo.m4 的形式命名,并将其存放在 metadoc 目录,然后在 nikola/posts 目录中执行下面的命令来生成含有目录信息的 Markdown 文档:

$ m4 -P -I ../m4 metadoc/demo.m4 > demo.markdown

demo.m4 就是 demo.markdown 的元文档。

若想知道更多

如果想知道 section.m4 都干了些什么,可以趁机学习一下 GNU m4。我写了一份 GNU m4 的教程,见『让这世界再多一份 GNU m4 教程』。

阅读 3.8k

5.8k 声望
1.9k 粉丝
0 条评论
5.8k 声望
1.9k 粉丝
文章目录
宣传栏