MkDocs でスペース2個のインデントをリストのネストとして認識させたい場合

Markdown を HTML 化する手段として MkDocs は有用な選択肢だ。Sphinx の Markdown 版みたいな使い心地である。

しかし唯一残念なのが (リスト等をネストさせる時の)インデントがスペース4個強制 であること。これがスペース2個だと認識されない。

そこでスペース2個で認識させるにはどうしたらいいか調べてみた。

結論

Python-Markdown の Extension である Mdx Truly Sane Lists を取り入れる。

適用手順

まずはインストール。

$ pip install mdx_truly_sane_lists

インストールされてるか確認。

$ pip freeze | grep mdx
mdx-truly-sane-lists==1.1.1

お使いの MkDocs プロジェクトに組み込む。具体的には mkdocs.yml に以下を記述。

site_name: 2space indentation test
markdown_extensions:
    - mdx_truly_sane_lists:
        nested_indent: 2

nested_indent の値を 2 にする。

詳しく

関連情報や背景などを雑多に取り上げておく。

MkDocs が持つ Markdown to HTML 変換の仕組み

Python-Markdown を使用して変換している。

つまり Markdown 変換の挙動を制御するためには、この Python-Markdown に与える設定値を変える必要があるのだけど、MkDocs ではこの変更を設定ファイル mkdocs.yml 経由で行えるようになっている。それが上記の markdown_extensions の記述。

Python-Markdown の拡張機能については以下を参照。

mdx_truly_sane_lists はサードパーティー製の拡張機能にあたる。

mdx_truly_sane_lists のソースはどうなってんの?

mdx_truly_sane_lists/mdx_truly_sane_lists.py を見るといい。

20行目 あたりだろうか、

    TrulySaneBlockProcessorMixin.truly_sane_tab_length = self.getConfigs()['nested_indent']

mkdocs.yml にて指定した nested_indent の値を読み込み、Python-Markdown に与えているという感じ。(詳しく読んでないけどたぶん)Python-Markdown 側で「Extension クラスを継承してパーサーを与えたら変換の挙動をカスタムできるよ」みたいになっていて、そのお作法に従って書いている&インデント数の指定を(nested_indent として指定した)2にしている、のだと思う。

スペース2個インデントの是非

Python-Markdown では スペース4個が正義 だと捉えている。

markdown/index.md から引用しておくと、

The syntax rules clearly state that when a list item consists of multiple paragraphs, "each subsequent paragraph in a list item must be indented by either 4 spaces or one tab" (emphasis added). However, many implementations do not enforce this rule and allow less than 4 spaces of indentation. The implementers of Python-Markdown consider it a bug to not enforce this rule.

とある。Markdown の仕様としてはスペース4個 or タブ1個が正しいので、それ以外のインデントはバグですよ というスタンス。

ただ、それじゃ困る(既にスペース2個の Markdown 実装も流通している)というわけで、一応回避策が用意してある。

In the event that one would prefer different behavior, tab_length can be set to whatever length is desired. Be warned however, as this will affect indentation for all aspects of the syntax (including root level code blocks).

tab_length というプロパティを使えばいい。もっというと、以下のような感じでレンダークラス markdown.Markdown を使う時に指定する。

        md = markdown.Markdown(
            tab_length=2,
            ...
        )

ただし、ここを変更しちゃうと、リストに限らず(Markdown 文法中で登場する)インデントのスペース個数が全部変わってしまうので注意。