高级功能 ============= 本章介绍 reStructuredText 的高级语法功能。 代码输出 ----------- 行内代码我们在之前已经介绍过,使用一对双反引号 ````text```` 或者 literal 角色 ``:literal:`text``` 。 本节主要介绍代码块的使用。 文本代码块 ^^^^^^^^^^^^^ .. note:: reStructuredText 原生的代码块指令是 ``code`` ( :docutils-directive:`code` ),本文不做介绍。 Sphinx 中独立的代码块输出,使用 ``code-block`` 指令(或其别名 ``sourcecode`` 指令)。 - 在该指令的同一行,可选指定一种编程语言的语法进行高亮。restructuredText 使用 Pygments 进行语法高亮,它支持的编程语言的简要列表可以参考 `Supported languages `_ ;这些编程语言除了正式名,也有一些别名可以使用,具体可以参考本地安装的 pygments 库下的这个文件中的 ``LEXERS`` 变量: .. code-block:: none site-packages\pygments\lexers\_mapping.py * 例如,在 ``LEXERS`` 变量中,我们可以找到关于 Python 语法高亮的那一行: .. code-block:: 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:: reST .. 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 该例将会输出下述的代码块。它在被引用时会显示为 :ref:`code-block-eg` 这个链接。 .. _code-block-eg: .. 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 导入代码文件 ^^^^^^^^^^^^^^^ .. note:: reStructuredText 原生的导入代码文件指令是 ``include`` ( :docutils-directive:`including-an-external-document-fragment` ),本文不做介绍。 Sphinx 支持从外部文件导入代码文本,使用 ``literalinclude`` 指令。该指令支持的选项有: * 显示行号 ``:linenos:`` * 语法高亮 ``:language: LANG`` * 行包括,即有选择地导入哪些行: ``:lines: NUM1,NUM2,...`` * 行强调 ``:emphasize-lines: NUM1,NUM2`` 。注意,如果有选择地导入代码行,行号将与文件原本的行号不同。 .. code-block:: reST .. literalinclude:: conf.py :linenos: :language: python :emphasize-lines: 3-6 :lines: 1, 17-21, 23- 以上导入代码的例子将导入与当前 rst 文档同目录下的 conf.py 文件,并输出以下结果: .. literalinclude:: conf.py :linenos: :language: python :emphasize-lines: 3-6 :lines: 1, 17-21, 23- .. _display-math: 数学公式 ----------- .. note:: reStructuredText 中支持的数学公式使用 LaTeX 语法。对使用 LaTeX 书写数学公式不熟悉的读者,可以在网络上找到许多入门语法;或者,也可以参考我撰写的 `《简单粗暴LaTeX》一书 `_ 中的数学公式章节。 行内数学公式直接使用角色 ``:math:`` ,例如 :math:`\sin\alpha` ,这里主要讲行间公式的处理。 reStructuredText 语法中使用 ``math`` 指令( :docutils-directive:`math` )来插入行间数学公式。 * 数学公式语法上与 LaTeX 的原生命令一致。 - 单行公式可以直接写在指令的同一行,比如 ``.. math:: a + b = c`` 。 - 多行公式需要在公式间插入一个空白行。 * 数学公式中可以使用与符号 ``&`` 来对齐列,使用双反斜线 ``\\`` 来换行。Sphinx 会自动将整个块放在一个 LaTeX 语法的 split 环境中。 关于 Sphinx 中的数学公式使用: .. note:: 在 Sphinx 输出 HTML 时,建议在 conf.py 中加载 `sphinx.ext.mathjax`_ 插件。本文档开启了该插件,且**配置了 MathJax 的自动编号**。下文中所有右侧的编号都由 MathJax 提供,而左侧的、位于公式前一行的编号是 Sphinx 构建的结果。当然,这两者的格式都可以在配置(或 CSS 文件)中进行变更。 Sphinx 用户可以参考下文的 :ref:`mathjax-support` 一节来获取更多关于 MathJax 的信息。 * Sphinx 支持以 ``:label: TEXT`` 选项来给公式添加编号,并使其能够被 ``:eq:`` 角色交叉引用。更复杂的引用可以使用 MathJax 中的功能支持,见 :ref:`mathjax-support` 一节。 * Sphinx 支持以 ``:nowrap:`` 选项来取消自动公式环境(equation, align, split),而显式地由读者自行指定。 示例 ^^^^^^^^^^^^ 单行公式的例子如下。使用 ``:eq:`math-single``` 引用时会显示 :eq:`math-single` 这个链接。 .. code-block:: reST .. math:: :label: math-single \int_1^\infty \frac{1}{x^2} dx = 1 .. math:: :label: math-single \int_1^\infty \frac{1}{x^2} dx = 1 使用 ``&`` 与 ``\\`` 进行标记的对齐多行公式,引用如 :eq:`math-multi` 。 .. code-block:: reST .. math:: :label: math-multi (a + b)^2 &= (a + b)(a + b) \\ &= a^2 + 2ab + b^2 .. math:: :label: math-multi (a + b)^2 &= (a + b)(a + b) \\ &= a^2 + 2ab + b^2 用空白行分隔的多个公式,在 MathJax 支持下的 HTML 输出会默认向右对齐。如果开启了 MathJax 的自动编号,那么右侧会对整体附加一个序号(而不是给每行编号);这类似于 LaTeX 中的 split 环境。 .. code-block:: reST .. math:: f(x) = x^2 + \sin x S(r) = \{ x \in \mathbb{R}^n \,|\, \|x\| \leq r \} .. math:: f(x) = x^2 + \sin x S(r) = \{ x \in \mathbb{R}^n \,|\, \|x\| \leq r \} 如果要居中对齐,使用 ``:nowrap:`` 选项配合 LaTeX 语法中的 gather(\*) 环境: .. code-block:: reST 无编号多行居中: .. math:: :nowrap: \begin{gather*} f(x) = x^2 + \sin x \\ S(r) = \{ x \in \mathbb{R}^n \,|\, \|x\| \leq r \} \end{gather*} 无编号多行居中: .. math:: :nowrap: \begin{gather*} f(x) = x^2 + \sin x \\ S(r) = \{ x \in \mathbb{R}^n \,|\, \|x\| \leq r \} \end{gather*} 要给多行公式中的每一行进行自动编号(并使它们在文中能够被交叉引用),参考 :ref:`mathjax-multi-ref` 一节。 .. _mathjax-support: MathJax 支持 ^^^^^^^^^^^^^^^^^ MathJax ( `官方文档 `_ )是一个 JavaScript 数学公式渲染引擎。在使用 Sphinx 将 reStructuredText 文档转换为 HTML 后,往往需要用 MathJax 来支持其中的数学公式输出。 Sphinx 中启用 MathJax 的方式是在 conf.py 中加载 `sphinx.ext.mathjax`_ 插件: .. code-block:: python # 向 extensions 变量中添加一项 'sphinx.ext.mathjax' extensions = ['sphinx.ext.mathjax', ...] 在 conf.py 中,我们还可能需要配置 ``mathjax_config`` 变量来设置 MathJax,比如启用 MathJax 支持的 `公式自动编号 `_ : .. warning:: 由于 reStructuredText 与 MathJax 语法的兼容性,Sphinx 并非接受 MathJax 的所有功能。MathJax 的自动编号在 Sphinx 文档中很难进行交叉引用,因此仍然推荐使用 Sphinx 的 ``:label:`` 角色。 同理,在 reStructuredText 文档中,应当使用 ``:math:`` 行内角色,而不是 MathJax 支持的 ``$..$`` 语法。 .. code-block:: python # 如果是 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*}`` 由于需引用的公式一般都含编号,下面以带编号公式为例。 .. code-block:: reST 引用 MathJax 单行公式: :math:`\ref{mathjax-single}` 或 :math:`\eqref{mathjax-single}` 。 .. math:: :nowrap: \begin{equation} \label{mathjax-single} \alpha + \beta = \gamma \end{equation} 引用 MathJax 单行公式: :math:`\ref{mathjax-single}` 或 :math:`\eqref{mathjax-single}` 。 .. math:: :nowrap: \begin{equation} \label{mathjax-single} \alpha + \beta = \gamma \end{equation} .. _mathjax-multi-ref: 多行公式引用 ~~~~~~~~~~~~~~~~~~~~~~~~~ 多行公式需要配合 ``math`` 指令的 ``:nowrap:`` 选项,并以 MathJax 语法(LaTeX 语法)中的 ``\label`` 显式地声明标签。 LaTeX 语法中常用的多行公式环境有(不带星号环境将给每行编号,带星号的不做任何编号): * align(*):支持以 ``&`` 对齐各列,每行独立编号。列对齐方式在居右与居左之间轮替,即各列的对齐依次是:居右、居左、居右、居左、…… * split(*):与 align(*) 类似,但所有行共享一个编号。与 equation-aligned 嵌套环境的效果类似。 * gather(*):每行内容居中且独立编号。 * gathered:在 equation 内部嵌套 gathered 环境,可以达成居中且共享编号的输出效果。 先看一个 gather 环境的例子,每行独立编号(align 环境也是如此)。要引用的行需要用 ``\label`` 语法声明一个标签。 .. code-block:: reST 类似单行公式引用,引用时使用 :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} 类似单行公式引用,引用时使用 :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} 如果将上例中的 gather 环境改为带星号的 gather 环境,将不进行任何公式编号。此时,标签命令 ``\label`` 应该写在数学环境内部、第一行公式之前。引用公式 :math:`\ref{eq:gatherstar-a}` 的语法同上。 .. math:: :nowrap: \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 环境中所有行共享同一个编号,引用为 :math:`\ref{eq:split}` 。标签命令同样写在第一行公式之前。 .. code-block:: reST .. math:: :nowrap: \begin{split} \label{eq:split} a &> b & c &\geq d \\ e &\neq f & \lim_\infty g &= h \end{split} .. math:: :nowrap: \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 文件中更改该变量值,来在每一个文档文件的头部添加一个额外的数学环境,用于自定义数学命令: .. literalinclude:: conf.py :language: python :lines: 33- :emphasize-lines: 2-5 上例是本文档使用的 conf.py 文件的一部分,定义了两个数学命令: * 一个是 ``\ud`` 命令,无输入参数,用来打印一个竖直的积分符号。 * 另一个是 ``\pfrac[#1]{#2}`` 命令,有分子分母两个参数,用来打印偏导分式。参数1是分母,默认值是x;参数2是分子。 下面是一个使用了上述两个自定义符号的例子: .. code-block:: reST .. math:: \pfrac{f(x,y)} &= y \\ \pfrac[y]{f(x,y)} &= \int_0^\pi \sin x \ud x .. math:: \pfrac{f(x,y)} &= y \\ \pfrac[y]{f(x,y)} &= \int_0^\pi \sin x \ud x 警示标记 ----------- 警示标记( :docutils-directive:`admonitions` )是我个人最喜欢的 reStructuredText 功能之一。它将绘制一个能够快速引起读者注意的消息框。比如 warning 警示标记: .. code-block:: reST .. warning:: This is an optional title Warning mesage. Warning message paragraph 2. .. warning:: This is an optional title Warning mesage. Warning message paragraph 2. .. admonition:: 在 Sphinx 中正确显示警示标题 :class: hint 在 Sphinx 中,如果要正常地在警示框顶部显示标题(而不是将标题显示为框内的一个段落),你可能需要这样书写: .. code-block:: reST .. 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:: attention .. caution:: caution .. danger:: danger .. error:: error .. hint:: hint .. important:: important .. note:: note .. tip:: tip .. warning:: warning 除了上述几种警示以外,reStructuredText 允许用户使用 ``admonition`` 指令来自定义警示。比如,用户可以创建一个不在上述 9 类中的一类新警示(比如叫 critical),然后再在 CSS 文件中对 critical 类进行样式定义。 .. code-block:: reST .. admonition:: :class: critical Admonition message. 上例在 Sphinx 构建后,会输出一个具有 admonition 与 critical 类别的 div 容器。 .. _sec-substitution-text: 替换文本 ----------- reStructuredText 支持的替换功能( :docutils:`substitution-definitions` )有许多应用场景。一个是用来替代比较长的一个短语或称谓,减少键盘输入的痛苦。 * 以双侧竖线 ``|text|`` 来标记目标文本。在生成文档时,它会被替换文本所代替。 * 以含下划线后缀的语法 ``|text|_`` 将让目标文本在被代替的同时,还成为一个指向 ``_text`` 地址的超链接。 * 替换文本的写法类似于脚注,不过还需要 ``replace::`` 关键字。 * 替换文本中可以包含行内标记,比如强调、斜体等。 .. code-block:: reST 本文将介绍文本标记语言 |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 本文将介绍 |rst| (reST),一种文本标记语言。由于 |rst-plain| 这个词太长了,很多人喜欢在输入时简化它。 .. |rst-plain| replace:: reStructuredText .. |rst| replace:: **reS**\ tructured\ **T**\ ext .. _rst: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html ---------- 除了文本,替换对象也可以是图片。事实上它还可以是一些奇奇怪怪的东西,但用的比较少,这里就不介绍了。感兴趣的读者可以参考 Docutils 文档。 .. code-block:: reST 本段中的这个目标对象 |kitty| 将会被替换为一个宽、高均为 70 px 的竖直居中图片。 .. |kitty| image:: eg.png :height: 70 :width: 70 :align: middle 本段中的这个目标对象 |kitty| 将会被替换为一个宽、高均为 70 px 的竖直居中图片。 .. |kitty| image:: eg.png :height: 70 :width: 70 :align: middle 最后,Sphinx 还提供了几个默认的替换文本: * ``|today|`` :文档构建当天的日期。需要在 ``conf.py`` 文件中声明 ``today_fmt`` 变量来指定日期的格式,比如: .. code-block:: python today_fmt = "%Y-%m-%d" 这会打印如 2000-01-31 这样的日期格式。定义日期格式的语法与 Python 中 datetime 格式的使用相同。 * ``|release|`` :文档的发布信息,需要在 ``conf.py`` 中声明 ``release`` 变量。 * ``|version|`` :文档的版本信息,需要在 ``conf.py`` 中声明 ``version`` 变量。 .. _directive-glossary: 术语表\* ----------- 术语表是由 Sphinx 支持的一个指令,本质上是一个位于 glossary 指令块内部的定义列表: * 术语表中的每一组术语可以是单个(例中 a1),也可以是多个(例中 f2 与 c3) * 术语表支持一个可选的选项 ``:sorted:`` ,它将按(每组的第一个术语的)首字母顺序排列该表所有术语组。 * 术语表中的术语可以用 ``:term:`` 角色引用(引用时忽略行内标记格式),比如 :term:`a1` 与 :term:`d4 term` 。 .. code-block:: reST .. 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. 上例将给出以下的输出结果: .. 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. .. _sphinx.ext.mathjax: https://www.sphinx-doc.org/en/master/usage/extensions/math.html#module-sphinx.ext.mathjax