markdown-preview-enhancedのマークダウンを取り込むSphinxフィルタ

みなさん、ドキュメントを書いていますでしょうか？
私は最近は[Atom](https://atom.io/)の[markdown-preview-enhanced](https://atom.io/packages/markdown-preview-enhanced)を使ってドキュメントを書いています。

[markdown-preview-enhanced](https://atom.io/packages/markdown-preview-enhanced)の記事は以下のものがわかり易いと思います。

- [【ドキュメントが書きたくなる】Markdownライブプレビュー + インライン数式/UML/図表 + 綺麗にPDF/Wordエクスポートまで](http://qiita.com/tomo_makes/items/da4e8fe7d8cf168b545f)
- [Atom Markdown Preview Enhancedで業務に関する全てのドキュメントを書くためのTips](http://qiita.com/cha2maru/items/9ba4413bd91083e7c984)

[mermaid.js](https://knsv.github.io/mermaid/)や[PlantUML](http://plantuml.com/)をリアルタイムに確認しながらドキュメント書けるので、とにかくドキュメントを早く書けます。

ただ辛い部分もあって、ある程度長いドキュメントになってくるとページ分けをしたくなってきます。
やはりある程度、分量があるドキュメントは[Sphinx](http://sphinx-users.jp/)のように章分けしてくれるツールのほうが読みやすいです。

ですので、`markdown-preview-enhanced`で書いたマークダウンを`Sphinx`にそのまま取り込める[sphinxcontrib-pandoc-markdown](https://github.com/pashango2/sphinxcontrib-pandoc-markdown)というSphinxフィルターを作りました。

## sphinxcontrib-pandoc-markdown

インストールはpipで簡単に行なえます。

```
$ pip install -U sphinxcontrib-pandoc-markdown
```

`conf.py`に以下の記述を追加します。

```python
from sphinxcontrib.pandoc_markdown import MarkdownParser

source_suffix = ['.rst', '.md']
source_parsers = {
   '.md': MarkdownParser,
}
```

これでSphinxで`.md`ファイルを読み込み可能になります。

### 依存ソフトウェア

`markdown-preview-enhanced`という名前の通り[pandoc](http://pandoc.org/)に依存しています、依存ソフトウェアは以下の通りです。

- [pandoc](http://pandoc.org/)
- [Graphviz](http://www.graphviz.org/) (viz機能を使う場合)
- [PlantUML](http://plantuml.com/) (UML機能を使う場合)
- [mermaid-cli](https://knsv.github.io/mermaid/) (JavaScript以外の出力を場合)

### 実装済みの`markdown-preview-enhanced`機能

- @import記法
- [Graphviz](http://www.graphviz.org/)
- [PlantUML](http://plantuml.com/)
- [mermaid](http://knsv.github.io/mermaid/)
- [WaveDrom](http://wavedrom.com/)

CodeChunkやスライド能は実装していません。

## Sphinx独自の機能

`markdown-preview-enhanced`に実装されている機能の他に`Sphinx`固有の記法も実装しています。

### reStructuredTextの埋め込み

`recommonmark`と同様に`eval_rst`で`reStructuredText`を埋め込むことができます。

```
`` ` eval_rst
* This is a bulleted list.
* It has two items, the second
  item uses two lines.
`` `
```

これでマークダウンで書きながら、`reStructuredText`できめ細かい制御を行うという書き方もできます。

### ノート、警告、参照

````
`` `note
This is note.
`` `
````

![](https://qiita-image-store.s3.amazonaws.com/0/140075/4a166ca2-a3af-281f-d8f5-cab3092eddef.png)

````
`` `warning
This is warning.
`` `
````

![](https://qiita-image-store.s3.amazonaws.com/0/140075/8cc7b6eb-2a48-be37-b225-3f394b3d411c.png)

````
`` `seealso
This is seealso
`` `
````

![](https://qiita-image-store.s3.amazonaws.com/0/140075/cf122d1c-f2fe-ddd1-1130-3e0bbeb503d0.png)


### TODO

````
`` `todo
This is todo.
`` `
````

![](https://qiita-image-store.s3.amazonaws.com/0/140075/efb7fe95-c093-d320-3a73-155c8288888f.png)


### TODOリスト

````
`` `todolist
`` `
````

TODOを使用するには`conf.py`に以下の記述を追加してください。

```python
extensions += ['sphinx.ext.todo']

todo_include_todos=True
```

### pull-quote、highlights

````
`` `pull-quote
This is pull-quote
`` `
````

````
`` `highlights
This is highlights
`` `
````

### epigraph
````
`` `epigraph
考えるな、感じろ ”Don't think. Feel!”

-- 映画「ドラゴン怒りの鉄拳」より
`` `
````

## mermaid.js


![](https://qiita-image-store.s3.amazonaws.com/0/140075/e73ef6bc-ac65-e4ec-7fa5-8af5c24411a6.png)


````
`` `mermaid
graph TD;
    A-->B;
    A-->C;
    B-->D;
    C-->D;
`` `
````

#### 注意

mermaid.jsの現バージョン(7.0.0)ではCssの設定の関係で画面が半透明になる問題があります。
すでに[イシュー](https://github.com/knsv/mermaid/issues/527)は投げられていますが、次バージョンあたりで修正されると思います。
現状ではpng形式で出力した方がよいでしょう。


mermaid.jsを使用するには、Sphinx拡張の[sphinxcontrib-mermaid](https://github.com/mgaitan/sphinxcontrib-mermaid)をインストールする必要があります。

```
$ pip install sphinxcontrib-mermaid
```

また、pngやsvgなどの出力形式に対応したい場合は[mermaid-cli](https://www.npmjs.com/package/mermaid-cli)が必要です。

```
$ npm install -g mermaid-cli
```

`conf.py`に以下の記述を追加してください。

```python
extensions += ['sphinxcontrib.mermaid']
mermaid_output_format = "png"
```


## Graphviz

Graphvizはグラフ構造をテキストベースで記述できるツールです。

````
`` `viz
digraph G {

	subgraph cluster_0 {
		style=filled;
		color=lightgrey;
		node [style=filled,color=white];
		a0 -> a1 -> a2 -> a3;
		label = "process #1";
	}

	subgraph cluster_1 {
		node [style=filled];
		b0 -> b1 -> b2 -> b3;
		label = "process #2";
		color=blue
	}
	start -> a0;
	start -> b0;
	a1 -> b3;
	b2 -> a3;
	a3 -> a0;
	a3 -> end;
	b3 -> end;

	start [shape=Mdiamond];
	end [shape=Msquare];
}
`` `
````

[Graphviz](http://www.graphviz.org/)を使用するには、`conf.py`に以下のコードを追加する必要があります。

``` python
extensions += ['sphinx.ext.graphviz']
```

## Wavedrom

````
`` `wavedrom
{ assign:[
  ["out",
    ["|",
      ["&", ["~", "a"], "b"],
      ["&", ["~", "b"], "a"]
    ]
  ]
]}
`` `
````

[Wavedrom](http://wavedrom.com/)を使用するには、Sphinx拡張の[sphinxcontrib-wavedrom](https://github.com/bavovanachte/sphinx-wavedrom)をインストールする必要があります。

```
$ pip install sphinxcontrib-wavedrom
```

`conf.py`に以下の記述を追加してください。

```python
extensions += ['sphinxcontrib.wavedrom']
```

## 技術解説

ここはQiitaなので技術解説をします。
とはいえ、`pandoc`が難しい事をほとんどやってくれているので難しい事はほとんどありません。

### 処理の流れ

```
マークダウン -> プリプロセス -> pandocフィルタ -> reStructuredText -> ポストプロセス
```

#### プリプロセス

`@import`記法をプリプロセス部分で置き換えします、多分、本家の処理もプリプロセスで処理していると思います。

#### pandocフィルタ

pandocでマークダウンから`reStructuredText`に変換しているのですが、フィルタを作成して画像のキャプションを除去しています。
フィルタは以下の通りです。

```python
from pandocfilters import toJSONFilter, Image


def remove_caption_filter(key, value, format_, meta):
    if key == 'Image':
        """
        markdown:
            ![alt](path "title")

        json:
            [[u'', [], []], [{u'c': u'alt', u't': u'Str'}], [u'path', u'fig:title']]
        """
        try:
            alt = value[1][0].get("c")
        except IndexError:
            alt = None

        value[1] = []

        if alt:
            value[2][1] = u"fig:{}".format(alt)
        return Image(*value)


if __name__ == "__main__":
    toJSONFilter(remove_caption_filter)
```


#### ポストプロセス

変換された`reStructuredText`をパースしてコードブロックの置換処理をします。

こんなコードを

```
.. codeblock:: note

    This is Note.
```

こんなふうに変換しているだけです。

```
.. note::

    This is Note.
```

`reStructuredText`はルールが単純なのでパースしやすいですね。

## まとめ

`markdown-preview-enhanced`は`マークダウン`->`HTML`と直接変換なのに対して、こちらは`マークダウン`->`reStructuredText`->`HTML`としているので、どうしても`docutils`の仕様にひっぱられたりします。

何か問題や欲しい機能がありましたら、GitHubのイシューに投げてください。

https://github.com/pashango2/sphinxcontrib-pandoc-markdown