\[\newcommand{\ud}{\mathop{}\negthinspace\mathrm{d}} \newcommand{\pfrac}[2][x]{\frac{\partial #2}{\partial #1}}\]

高级功能

本章介绍 reStructuredText 的高级语法功能。

代码输出

行内代码我们在之前已经介绍过,使用一对双反引号 ``text`` 或者 literal 角色 :literal:`text`

本节主要介绍代码块的使用。

文本代码块

备注

reStructuredText 原生的代码块指令是 codeDirective: code ),本文不做介绍。

Sphinx 中独立的代码块输出,使用 code-block 指令(或其别名 sourcecode 指令)。

  • 在该指令的同一行,可选指定一种编程语言的语法进行高亮。restructuredText 使用 Pygments 进行语法高亮,它支持的编程语言的简要列表可以参考 Supported languages ;这些编程语言除了正式名,也有一些别名可以使用,具体可以参考本地安装的 pygments 库下的这个文件中的 LEXERS 变量:

    site-packages\pygments\lexers\_mapping.py
    
    • 例如,在 LEXERS 变量中,我们可以找到关于 Python 语法高亮的那一行:

      'PythonLexer': ('pygments.lexers.python', 'Python', ('python', 'py', 'sage', 'python3', 'py3'), ...
      

      注意到此行内侧嵌套的那组圆括号,表示 python, py, sage, python3, py3 这些名称都可以用来指定 Python 语法高亮(但我一般推荐最靠前的那个名称)。

    • 如果不需要在该代码块中启用语法高亮(纯文本),可以指定一个不被 Pygments 支持的名称,比如 text 或者 none。

  • 代码块的行号选项

    • 启用行号 :linenos:

    • 指定第一行行号从何数字开始 :lineno-start: NUM 。该选项会自动启用行号选项

    • 强调某一行或几行 :emphasize-lines: NUM1,NUM2,...

  • 添加代码块标题 :caption: TEXT

  • 代码块被以 :ref: 引用时显示的文本 :name: TEXT

  • 反向缩进代码块 :dedent: NUM

文本代码块的例子。注意,所有的选项都不是必须的。

.. code-block:: python
   :linenos:
   :emphasize-lines: 1,4-5
   :caption: A highlighted code-block example
   :name: 一个代码块示例

   def foo():
       x = 3
       y = 4
       z = x ** 2 + y ** 2
       print(f"Val = {z}")
       return z

该例将会输出下述的代码块。它在被引用时会显示为 A highlighted code-block example 这个链接。

列表 1 A highlighted code-block example
1def foo():
2    x = 3
3    y = 4
4    z = x ** 2 + y ** 2
5    print(f"Val = {z}")
6    return z

导入代码文件

备注

reStructuredText 原生的导入代码文件指令是 includeDirective: including-an-external-document-fragment ),本文不做介绍。

Sphinx 支持从外部文件导入代码文本,使用 literalinclude 指令。该指令支持的选项有:

  • 显示行号 :linenos:

  • 语法高亮 :language: LANG

  • 行包括,即有选择地导入哪些行: :lines: NUM1,NUM2,...

  • 行强调 :emphasize-lines: NUM1,NUM2 。注意,如果有选择地导入代码行,行号将与文件原本的行号不同。

.. literalinclude:: conf.py
   :linenos:
   :language: python
   :emphasize-lines: 3-6
   :lines: 1, 17-21, 23-

以上导入代码的例子将导入与当前 rst 文档同目录下的 conf.py 文件,并输出以下结果:

 1author = "wklchris"
 2
 3# [sphinx.ext.extlinks]
 4extlinks = {
 5   'docutils': ("https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#%s", 'Docutils: %s'),
 6   'docutils-directive': ("https://docutils.sourceforge.io/docs/ref/rst/directives.html#%s", 'Directive: %s')
 7
 8# Others
 9highlight_language = 'none'
10numfig = True
11
12mathjax3_config = {
13   'tex': {'tags': 'ams'}
14}
15
16
17rst_prolog = """
18.. math::
19
20   \\newcommand{\\ud}{\\mathop{}\\negthinspace\mathrm{d}}
21   \\newcommand{\\pfrac}[2][x]{\\frac{\\partial #2}{\\partial #1}}
22
23.. |section-symbols| replace:: 
24   ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~
25"""

数学公式

备注

reStructuredText 中支持的数学公式使用 LaTeX 语法。对使用 LaTeX 书写数学公式不熟悉的读者,可以在网络上找到许多入门语法;或者,也可以参考我撰写的 《简单粗暴LaTeX》一书 中的数学公式章节。

行内数学公式直接使用角色 :math: ,例如 \(\sin\alpha\) ,这里主要讲行间公式的处理。

reStructuredText 语法中使用 math 指令( Directive: math )来插入行间数学公式。

  • 数学公式语法上与 LaTeX 的原生命令一致。

    • 单行公式可以直接写在指令的同一行,比如 .. math:: a + b = c

    • 多行公式需要在公式间插入一个空白行。

  • 数学公式中可以使用与符号 & 来对齐列,使用双反斜线 \\ 来换行。Sphinx 会自动将整个块放在一个 LaTeX 语法的 split 环境中。

关于 Sphinx 中的数学公式使用:

备注

在 Sphinx 输出 HTML 时,建议在 conf.py 中加载 sphinx.ext.mathjax 插件。本文档开启了该插件,且**配置了 MathJax 的自动编号**。下文中所有右侧的编号都由 MathJax 提供,而左侧的、位于公式前一行的编号是 Sphinx 构建的结果。当然,这两者的格式都可以在配置(或 CSS 文件)中进行变更。

Sphinx 用户可以参考下文的 MathJax 支持 一节来获取更多关于 MathJax 的信息。

  • Sphinx 支持以 :label: TEXT 选项来给公式添加编号,并使其能够被 :eq: 角色交叉引用。更复杂的引用可以使用 MathJax 中的功能支持,见 MathJax 支持 一节。

  • Sphinx 支持以 :nowrap: 选项来取消自动公式环境(equation, align, split),而显式地由读者自行指定。

示例

单行公式的例子如下。使用 :eq:`math-single` 引用时会显示 (1) 这个链接。

.. math::
    :label: math-single

    \int_1^\infty \frac{1}{x^2} dx = 1
(1)\[\int_1^\infty \frac{1}{x^2} dx = 1\]

使用 &\\ 进行标记的对齐多行公式,引用如 (2)

.. math::
    :label: math-multi

    (a + b)^2 &= (a + b)(a + b) \\
              &= a^2 + 2ab + b^2
(2)\[\begin{split}(a + b)^2 &= (a + b)(a + b) \\ &= a^2 + 2ab + b^2\end{split}\]

用空白行分隔的多个公式,在 MathJax 支持下的 HTML 输出会默认向右对齐。如果开启了 MathJax 的自动编号,那么右侧会对整体附加一个序号(而不是给每行编号);这类似于 LaTeX 中的 split 环境。

.. math::

    f(x) = x^2 + \sin x

    S(r) = \{ x \in \mathbb{R}^n \,|\, \|x\| \leq r \}
\[ \begin{align}\begin{aligned}f(x) = x^2 + \sin x\\S(r) = \{ x \in \mathbb{R}^n \,|\, \|x\| \leq r \}\end{aligned}\end{align} \]

如果要居中对齐,使用 :nowrap: 选项配合 LaTeX 语法中的 gather(*) 环境:

无编号多行居中:

.. math::
    :nowrap:

    \begin{gather*}
    f(x) = x^2 + \sin x \\
    S(r) = \{ x \in \mathbb{R}^n \,|\, \|x\| \leq r \}
    \end{gather*}

无编号多行居中:

\begin{gather*} f(x) = x^2 + \sin x \\ S(r) = \{ x \in \mathbb{R}^n \,|\, \|x\| \leq r \} \end{gather*}

要给多行公式中的每一行进行自动编号(并使它们在文中能够被交叉引用),参考 多行公式引用 一节。

MathJax 支持

MathJax ( 官方文档 )是一个 JavaScript 数学公式渲染引擎。在使用 Sphinx 将 reStructuredText 文档转换为 HTML 后,往往需要用 MathJax 来支持其中的数学公式输出。

Sphinx 中启用 MathJax 的方式是在 conf.py 中加载 sphinx.ext.mathjax 插件:

# 向 extensions 变量中添加一项 'sphinx.ext.mathjax'
extensions = ['sphinx.ext.mathjax', ...]

在 conf.py 中,我们还可能需要配置 mathjax_config 变量来设置 MathJax,比如启用 MathJax 支持的 公式自动编号

警告

由于 reStructuredText 与 MathJax 语法的兼容性,Sphinx 并非接受 MathJax 的所有功能。MathJax 的自动编号在 Sphinx 文档中很难进行交叉引用,因此仍然推荐使用 Sphinx 的 :label: 角色。

同理,在 reStructuredText 文档中,应当使用 :math: 行内角色,而不是 MathJax 支持的 $..$ 语法。

# 如果是 MathJax v2,使用:
# mathjax2_config = {
#    'TeX': {
#        'equationNumbers': { 'autoNumber': "AMS" }
#    }
# }
mathjax3_config = {
    'tex': {'tags': 'ams'}
}

单行公式引用

单行公式引用需要启用 :nowrap: 选项,并以 MathJax 语法(LaTeX 语法)中的 \label 显式地声明标签。在需要引用时,使用 \ref 引用公式序号,或者使用 \eqref 引用由圆括号括起的公式序号。

LaTeX 中支持的单行公式有两种:

  • 带编号的单行公式: \begin{equation}..\end{equation}

  • 无编号的单行公式: \begin{equation*}..\end{equation*}

由于需引用的公式一般都含编号,下面以带编号公式为例。

引用 MathJax 单行公式: :math:`\ref{mathjax-single}`:math:`\eqref{mathjax-single}`.. math::
    :nowrap:

    \begin{equation}
    \label{mathjax-single}
    \alpha + \beta = \gamma
    \end{equation}

引用 MathJax 单行公式: \(\ref{mathjax-single}\)\(\eqref{mathjax-single}\)

\begin{equation} \label{mathjax-single} \alpha + \beta = \gamma \end{equation}

多行公式引用

多行公式需要配合 math 指令的 :nowrap: 选项,并以 MathJax 语法(LaTeX 语法)中的 \label 显式地声明标签。

LaTeX 语法中常用的多行公式环境有(不带星号环境将给每行编号,带星号的不做任何编号):

  • align(*):支持以 & 对齐各列,每行独立编号。列对齐方式在居右与居左之间轮替,即各列的对齐依次是:居右、居左、居右、居左、……

  • split(*):与 align(*) 类似,但所有行共享一个编号。与 equation-aligned 嵌套环境的效果类似。

  • gather(*):每行内容居中且独立编号。

  • gathered:在 equation 内部嵌套 gathered 环境,可以达成居中且共享编号的输出效果。

先看一个 gather 环境的例子,每行独立编号(align 环境也是如此)。要引用的行需要用 \label 语法声明一个标签。

类似单行公式引用,引用时使用 :math:`\ref{mj-gather-a}` 或者 :math:`\eqref{mj-gather-a}`.. math::
    :nowrap:

    \begin{gather}
    f(x) = x^2 + \sin x \label{mj-gather-a} \\
    S(r) = \{ x \in \mathbb{R}^n \,|\, \|x\| \leq r \}
    \end{gather}

类似单行公式引用,引用时使用 \(\ref{mj-gather-a}\) 或者 \(\eqref{mj-gather-a}\)

\begin{gather} f(x) = x^2 + \sin x \label{mj-gather-a} \\ S(r) = \{ x \in \mathbb{R}^n \,|\, \|x\| \leq r \} \end{gather}

如果将上例中的 gather 环境改为带星号的 gather 环境,将不进行任何公式编号。此时,标签命令 \label 应该写在数学环境内部、第一行公式之前。引用公式 \(\ref{eq:gatherstar-a}\) 的语法同上。

\begin{gather*} \label{eq:gatherstar-a} f(x) = x^2 + \sin x \\ S(r) = \{ x \in \mathbb{R}^n \,|\, \|x\| \leq r \} \end{gather*}

最后,看一个 split 环境的例子。split 环境中所有行共享同一个编号,引用为 \(\ref{eq:split}\) 。标签命令同样写在第一行公式之前。

.. math::
    :nowrap:

    \begin{split}
    \label{eq:split}
    a &> b & c &\geq d \\
    e &\neq f & \lim_\infty g &= h
    \end{split}
\begin{split} \label{eq:split} a &> b & c &\geq d \\ e &\neq f & \lim_\infty g &= h \end{split}

关于 MathJax 支持的全部 LaTeX 命令,参考 MathJax: Supported TeX/LaTeX commands 文档。

自定义数学命令

MathJax 允许自定义简单的 LaTeX 命令,即 LaTeX 中的 \newcommand 命令。参考 MathJax: Defining TeX Macros 页面。

在 Sphinx 中,由于 rst_prolog 变量的存在,我们可以直接在 conf.py 文件中更改该变量值,来在每一个文档文件的头部添加一个额外的数学环境,用于自定义数学命令:

rst_prolog = """
.. math::

   \\newcommand{\\ud}{\\mathop{}\\negthinspace\mathrm{d}}
   \\newcommand{\\pfrac}[2][x]{\\frac{\\partial #2}{\\partial #1}}

.. |section-symbols| replace:: 
   ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~
"""

上例是本文档使用的 conf.py 文件的一部分,定义了两个数学命令:

  • 一个是 \ud 命令,无输入参数,用来打印一个竖直的积分符号。

  • 另一个是 \pfrac[#1]{#2} 命令,有分子分母两个参数,用来打印偏导分式。参数1是分母,默认值是x;参数2是分子。

下面是一个使用了上述两个自定义符号的例子:

.. math::

    \pfrac{f(x,y)} &= y \\
    \pfrac[y]{f(x,y)} &= \int_0^\pi \sin x \ud x
\[\begin{split}\pfrac{f(x,y)} &= y \\ \pfrac[y]{f(x,y)} &= \int_0^\pi \sin x \ud x\end{split}\]

警示标记

警示标记( Directive: admonitions )是我个人最喜欢的 reStructuredText 功能之一。它将绘制一个能够快速引起读者注意的消息框。比如 warning 警示标记:

.. warning:: This is an optional title

    Warning mesage.

    Warning message paragraph 2.

警告

This is an optional title

Warning mesage.

Warning message paragraph 2.

在 Sphinx 中正确显示警示标题

在 Sphinx 中,如果要正常地在警示框顶部显示标题(而不是将标题显示为框内的一个段落),你可能需要这样书写:

.. admonition:: 在 Sphinx 中正确显示警示标题
   :class: hint

而常规的写法 .. hint:: Sphinx 中正确显示警示标题 在 Sphinx 中可能不会被识别。

reStructuredText 提供特殊支持的警示标记有以下 9 种:

  • attention 注意

  • caution 警告。Sphinx 中,caution 的默认的 HTML (中文)输出样式与 warning 相同。

  • danger 危险

  • error 错误

  • hint 提示

  • important 重要

  • note 注解

  • tip 小技巧

  • warning 警告。Sphinx 中,warning 默认的 HTML (中文)输出样式与 caution 相同。

它们的样式如下:

注意

attention

小心

caution

危险

danger

错误

error

提示

hint

重要

important

备注

note

小技巧

tip

警告

warning

除了上述几种警示以外,reStructuredText 允许用户使用 admonition 指令来自定义警示。比如,用户可以创建一个不在上述 9 类中的一类新警示(比如叫 critical),然后再在 CSS 文件中对 critical 类进行样式定义。

.. admonition::
   :class: critical

   Admonition message.

上例在 Sphinx 构建后,会输出一个具有 admonition 与 critical 类别的 div 容器。

替换文本

reStructuredText 支持的替换功能( Docutils: substitution-definitions )有许多应用场景。一个是用来替代比较长的一个短语或称谓,减少键盘输入的痛苦。

  • 以双侧竖线 |text| 来标记目标文本。在生成文档时,它会被替换文本所代替。

  • 以含下划线后缀的语法 |text|_ 将让目标文本在被代替的同时,还成为一个指向 _text 地址的超链接。

  • 替换文本的写法类似于脚注,不过还需要 replace:: 关键字。

  • 替换文本中可以包含行内标记,比如强调、斜体等。

本文将介绍文本标记语言 |rst| (reST)。由于 |rst-plain| 这个词太长了,很多人喜欢在输入时简化它。

前往文档页面: |rst|_

.. |rst-plain| replace:: reStructuredText
.. |rst| replace:: **reS**\ tructured\ **T**\ ext
.. _rst: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html

本文将介绍 reStructuredText (reST),一种文本标记语言。由于 reStructuredText 这个词太长了,很多人喜欢在输入时简化它。


除了文本,替换对象也可以是图片。事实上它还可以是一些奇奇怪怪的东西,但用的比较少,这里就不介绍了。感兴趣的读者可以参考 Docutils 文档。

本段中的这个目标对象 |kitty| 将会被替换为一个宽、高均为 70 px 的竖直居中图片。

.. |kitty| image:: eg.png
    :height: 70
    :width: 70
    :align: middle

本段中的这个目标对象 kitty 将会被替换为一个宽、高均为 70 px 的竖直居中图片。

最后,Sphinx 还提供了几个默认的替换文本:

  • |today| :文档构建当天的日期。需要在 conf.py 文件中声明 today_fmt 变量来指定日期的格式,比如:

    today_fmt = "%Y-%m-%d"
    

    这会打印如 2000-01-31 这样的日期格式。定义日期格式的语法与 Python 中 datetime 格式的使用相同。

  • |release| :文档的发布信息,需要在 conf.py 中声明 release 变量。

  • |version| :文档的版本信息,需要在 conf.py 中声明 version 变量。

术语表*

术语表是由 Sphinx 支持的一个指令,本质上是一个位于 glossary 指令块内部的定义列表:

  • 术语表中的每一组术语可以是单个(例中 a1),也可以是多个(例中 f2 与 c3)

  • 术语表支持一个可选的选项 :sorted: ,它将按(每组的第一个术语的)首字母顺序排列该表所有术语组。

  • 术语表中的术语可以用 :term: 角色引用(引用时忽略行内标记格式),比如 a1d4 term

.. glossary::
    :sorted:

    **a1**
        A1 is a term.

    f2
    c3
        F2 and C3 are two closely related terms, or they are identical.

    d4 term
    e4 term
        D4/E4 are two keys to be sorted.

上例将给出以下的输出结果:

a1

A1 is a term.

d4 term
e4 term

D4/E4 are two keys to be sorted.

f2
c3

F2 and C3 are two closely related terms, or they are identical.