nikkie-ftnextの日記

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

Sphinxのliteralincludeディレクティブのオプションを完全理解! シンタックスハイライトしたコードを別ファイルから柔軟に含められます

はじめに

よろしくね、私の味方! nikkieです。

Sphinxシンタックスハイライトしたコードを表示する1際、コードが別のファイルにあるときは literalinclude ディレクティブを使っています。
このディレクティブ、ドキュメントを読んだところ知らなかったオプションを知り、もっと便利に使えそうでした。
バックアップとして現時点の理解をアウトプットします。

目次

Sphinxliteralincludeディレクティブ

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

.. literalinclude:: example.py

のようにして、example.pyにあるコードをドキュメントに含められます。
Sphinxプロジェクトで書いているreSTとは別のファイルにあるソースコードを、コピペすることなくliteralincludeドキュメントに含められるので、私には大変重宝しています。

例えばsphinx-revealjsで発表資料を作るとき、サンプルコード群はリポジトリルートでディレクトリを分けてliteralincludeしています2
reSTの中の、例えばcode-blockディレクティブのコードはフォーマットが難しいですが、サンプルコードをreSTから切り離すとフォーマットしやすくなります。

ドキュメントでは、以下のような使用例(オプション指定例)が示されています。

.. literalinclude:: example.rb
   :language: ruby
   :emphasize-lines: 12,15-18
   :linenos:

オプションはたくさんあり、オススメのものを以下に述べていきます。

literalincludeディレクティブとcode-blockディレクティブで共通のオプション

行番号表示

linenos

linenosはフラグで、指定すると行番号が表示されます。

lineno-start

lineno-startは整数と一緒に指定します。
行番号は指定した整数から始まります。

languageシンタックスハイライト指定

シンタックスハイライトに使うPygmentsのlexerのshortnameを指定します。
いろいろ見た中では、以下が参照しやすいように思われます。

強調したい行を指定 emphasize-lines

コードのうち強調したい行の行番号を指定します。
:emphasize-lines: 12,15-18のように、カンマ区切り、また範囲はハイフンで表せます。
lineno-startを指定したり、後述する一部だけ含めたりしても、含めるコードの1行目を強調するなら:emphasize-lines: 1です。
ドキュメントに含める部分を一番上から1,2,...行目と数えるようです。

その他(未検証含む)

caption

指定したキャプションを付けられます。
code-blockディレクトリとの差分は、:caption:とだけ指定した場合、literalincludeディレクティブに指定したファイル名がキャプションとして使われる3ようです。

name(素振り材料)

implicit target nameを指定し、refロールなどでコードを参照できるようです。

コードの一部だけをinclude(code-blockディレクティブにはないオプション)

クラス・関数・メソッド単位 pyobject

もしPythonモジュールの場合には、 pyobject オプションを使用してクラス、関数、メソッドの単位でインクルードすることもできます:

.. literalinclude:: example.py
   :pyobject: awesome_function

literalincludeに指定したファイルがPythonモジュールのとき限定ですが、クラス・関数・メソッド単位で一部だけ含められます!
pyobjectオプションはカンマ区切りをサポートしていないので、複数のクラス・関数・メソッドを含めたいときは、literalincludeディレクティブを複数指定して使っています。

Pythonモジュールをフォーマットしても、pyobjectオプションなら含める箇所がズレる(後述)ようなこともありません

行数指定 lines

lines オプションを使って行番号を正確に指定することでも部分的なインクルードを行えます:

.. literalinclude:: example.py
   :lines: 1,3,5-10,20-

1行目、3行目、5〜10行目、20行目以降が含められます。
行番号を表示したとき、1行目、3行目、5〜10行目の部分の行番号は飛び飛びにはならず、1〜7と連番になります。

linesオプションはPythonモジュール以外を部分的に含める際に重宝します。
ですが、含めるファイルをフォーマットして空行が入った/除かれた場合にズレてしまうので、注意が必要です。

特定の文字列の後から/前までを含められる

今回ドキュメントを読んで一番の驚きはこちら。
注釈の例ですが、以下のPythonモジュールについて

if __name__ == "__main__":
    # [initialize]
    app.start(":8000")
    # [initialize]

:start-after: [initialize]:end-before: [initialize]と指定すると、コメントの間の行が含められます

  • :start-after: pattern:patternの行の次から
  • :start-at: pattern:patternの行から(patternの行を含む)
  • :end-before: pattern:patternの行の前まで
  • :end-at: pattern:patternの行まで(patternの行を含む)

linesオプションの指定と違って、フォーマットが変わってもincludeされる部分は影響を受けなさそうで、結構使いやすそうな印象です。
これは使っていきたい!

素振りしたファイルはこちら:
https://raw.githubusercontent.com/ftnext/sphinx-playground/17821e32ac7fc3f1232d339f0d4de2bf37a1430e/code-samples/source/literalinclude-practice.rst

その他(未検証含む)

素振り材料も含めて記載します。

差分表示 diff
.. literalinclude:: example.py
   :diff: example.py.orig

unified diff formatで差分が表示されます。

前後にコード行を追加 prepend,append(素振り材料)

例えば、 <?php/?> マーカーを含まないPHPコードをハイライトする際などに役立ちます。

lineno-match

ファイルの一部を表示するように指定した場合、ファイルの行番号をそのまま表示するように指定することもできます。その場合は、 lineno-match オプションを設定して下さい。

(上の素振りで試しましたが)ファイルの行番号がそのまま表示されました。
以下のただし書きも納得です。

ただし、このオプションは選択された行が切れ目なくつながっている場合でのみ有効です。

終わりに

存在を知ってから積極的に利用しているliteralincludeディレクティブ、ドキュメントにあたったことで知識の整理と新たな発見がありました。
start-afterend-beforeが特にヤバいです!
今まで使っていて感じていた、かゆいところにきれいに手が届きそう!

reSTにソースコードをコピペせずDRYを保って扱えるliteralincludeディレクティブはオススメです。
たくさんのオプションによって非常に柔軟に使えることが分かりました。


  1. 先日、全部で4つあるとアウトプットしました。
  2. https://github.com/ftnext/2022_slides sourceにreSTを置き、samplecodeにPythonモジュールを置いています
  3. In addition, it supports the caption option; however, this can be provided with no argument to use the filename as the caption. (「追加のオプション」の直後のパラグラフより)