nikkie-ftnextの日記

イベントレポートや読書メモを発信

Sphinxには、シンタックスハイライトしたコードの表示の仕方が「4つ」ある

はじめに

Sphinx、全然理解せずに使ってたな… nikkieです。

ドキュメント変換ツールSphinx、私は執筆や発表資料作成でもヘビーユースしています。
シンタックスハイライトしたコードの表示はcode-blockディレクティブ1literalincludeディレクティブを使ってきたのですが、このたびあと2つあることを知りました。
このエントリの流れとしては知った順に見ていきますが、「終わりに」に4つをまとめています。

目次

前提

Sphinxのドキュメントのユーザガイドの中に、reStructuredTextについてのページ2があります。
その中の「ディレクティブ」というドキュメントの「コードサンプルの表示」セクションを参照しました。
ここに4つ紹介されています

There are multiple ways to show syntax-highlighted literal code blocks in Sphinx:

動作環境

Sphinx 6.1.3で検証しました。

今回素振りしたreSTファイルはこちらにあります:

すでに知っていたcode-blockディレクティブ

https://www.sphinx-doc.org/ja/master/usage/restructuredtext/directives.html#directive-code-block

これまでずっと、以下のような形でコードを書いていました3

.. code-block:: python

    >>> print("spam")
    spam

このディレクティブはSphinxディレクティブです。
ドキュメント中のoptions部分をぜひ見ていただきたいのですが、コードのスタイルをoptionできめ細かに指定できます。
行番号を表示したり、指定した行を強調したり、クロスリファレンスできるようにしたりといろいろできます。

すでに知っていたliteralincludeディレクティブ

https://www.sphinx-doc.org/ja/master/usage/restructuredtext/directives.html#directive-literalinclude

ドキュメントに含めたいソースコードがファイル中にある場合literalincludeディレクティブを使っています。

example.pyのコードをドキュメントに含める例です:

.. literalinclude:: example.py

このディレクティブもSphinxディレクティブです。
code-blockディレクティブと同様のoptionに加えて、指定した行の範囲や関数のコードだけを含めることもできます!4
私の中では大変重宝しています。

この2つの私の中での使い分けですが、

  • ファイルがあるならliteralinclude
    • 最初は手元にファイルがなかったとしても、コード量が多かったり何度も使ったりするならファイルにしてliteralincludeに指定します
  • ファイルが必要なければcode-blockに書く

としています。

存在を初めて知ったdoctest block

https://www.sphinx-doc.org/ja/master/usage/restructuredtext/basics.html#rst-doctest-blocks

なんとPython対話モードからreSTにコピー&ペーストするだけで、doctest blockとして扱われます!

>>> 1 + 1
2

いままで対話モードからcode-blockディレクティブの中にコピペしていましたが、もっと少ない手順で済んだなんて!
これからは非常にお世話になりそうです。

doctest blockは(Sphinxが依存している)docutilsのディレクティブです。
https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#doctest-blocks

存在を初めて知ったliteral block

https://www.sphinx-doc.org/ja/master/usage/restructuredtext/basics.html#literal-blocks

2重のコロン(::)で終えた行に続くインデントされた行がliteral blockです。

::

    >>> 1 + 1
    2

前の段落から続けることもできる::

    >>> print("this is a Literal block")
    this is a Literal block

doctest blockに書けるのは対話モードのPythonだけですが、literal block(や他の2つのディレクティブ)にはPython以外のコードも書けます。
literal blockではコードのスタイルはきめ細かには指定できないため、きめ細かく指定したければcode-blockディレクティブの出番ですね。

literal blockもdocutilsのディレクティブです。
https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#literal-blocks

highlightディレクティブというものがある!

https://www.sphinx-doc.org/ja/master/usage/restructuredtext/directives.html#directive-highlight

シンタックスハイライトに使う言語(正確にはPygmentsのlexer5)を指定するディレクティブです。

あるhighlightディレクティブの後は(次のhighlightディレクティブまで)指定した言語でシンタックスハイライトされます。
literal blockも言語指定をしないcode-blockも、highlightディレクティブでの指定が適用されました。
code-blockディレクティブしか知らなかったので、毎回言語を指定してきましたが、highlightディレクティブを知ったおかげで解放されそうです。

また、highlightディレクティブのoption linenothresholdは興味深いです。
「N行以上のコードには一律で行番号表示する」と指定できるoptionと理解しました。
私は行番号を頻繁に使うので、highlightディレクティブのおかげでますます楽ができそうです。

.. highlight:: rest
    :linenothreshold: 1

reSTのハイライトを指定(行番号付きで指定)

::

    Doctest blockの例
    ====================

    >>> 1 + 1
    2

.. code-block::

    Literal blockの例です::

        ここがLiteral block

終わりに

シンタックスハイライトされたコードをSphinxで表示する方法を、ようやく完全に理解しました。

  • Pythonの対話モードを貼り付けるだけで済む、doctest block
  • 末尾::の行の後にインデントしてコードを書く、literal block
  • code-blockディレクティブに続けてコードを書く(きめ細かくスタイル指定できる)
  • ファイルに書いたコードをliteralincludeディレクティブで含める(code-block同様きめ細かくスタイル指定できる)

手持ちの武器が増えた感覚です。
いままではcode-blockディレクティブ一本槍で結構無茶をしていたんだな〜

今後作るSphinx製ドキュメントで使っていって、4つを使い分けられる状態を目指します。
よろしくね、私の味方!

P.S. parsed-literalディレクティブ

シンタックスハイライトはされないのですが、コード中にインラインマークアップやリンクを入れられると知りました6

.. parsed-literal::

    >>> # コメントの一部を **強調**
    >>> 1 + 1
    2
    >>> # `doctestのドキュメント <https://docs.python.org/ja/3/library/doctest.html>`__
    >>> print("parsed-literalディレクティブの練習です")
    parsed-literalディレクティブの練習です

parsed-literalディレクティブというそうで、docutilsのディレクティブです。
https://docutils.sourceforge.io/docs/ref/rst/directives.html#parsed-literal


  1. 「ディレクティブ」は.. <ディレクティブ>::と書いて、インデントした行に内容を書くものというざっくり理解です(精緻にする伸びしろはあると思います)
  2. https://www.sphinx-doc.org/ja/master/usage/restructuredtext/index.html
  3. 先日のdoctestの記事でも使っています。
  4. literalincludeディレクティブについてだけで1エントリ書けそうな気がしますね(書けたら書きます)
  5. https://pygments.org/docs/lexers/
  6. Sphinxをはじめよう』付録A.30より。ドキュメントと見比べる中で気付きました