Qiita における Fenced code block の解釈 あるいは Fenced code block の歴史

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">ちなみに qiita は fenced code block のネスト? ができないので、僕の中では非常に評価が下がっております。</p>&mdash; tk0miya (@tk0miya) <a href="https://twitter.com/tk0miya/status/1225978480921235456?ref_src=twsrc%5Etfw">February 8, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

揚げ足取りをしても仕方がないのですが、気づいてしまったので書き残しておきます。


## Fenced code blocks って?

プログラマがよく使うであろう Markdown 記法のひとつで、 backtick 3つ以上でテキストを囲う記法です。囲われたテキストはコードブロックとして解釈されます。

    ```
    ここはコードブロックです。
    ```

この記法、日常的に読み書きする人が多いのではないかと思います。でも、実はこの記法はオリジナルの Markdown には含まれていません。Markdown 派生言語の [Markdown Extra](https://michelf.ca/projects/php-markdown/extra/) (PHP Markdown Extra とも呼ばれる)で追加されたサードパーティの文法です。

しかし、CommonMark や GFM、Qiita Markdown など、最近の Markdown 派生言語にはほぼ採用されており、いまや Markdown の顔とも言える文法のひとつです。

オリジナルの Markdown では、コードブロックはインデントで表現します。

```
ここでは Hello world の方法を紹介します。

   print("Hello world")

と書いたプログラムを実行してみましょう。
```

こんな感じで、コードを書くときにインデントするという記法です。
この記法はテキストのままでも読みやすいのですが、一方で字下げする必要があるためちょっと書きづらい面があります。エディタから貼り付けたコードを字下げする一手間がかかったりするわけですね[^1]。

[^1]: 他にも、コードブロックをふたつ連続して書けない(くっついてしまう)、箇条書きの直後に配置できない(誤った解釈となる)、コードブロックの先頭、末尾に空行を含められないといった問題もあります。

これに対して、Markdown Extra では、コードの前後にマーカーを記述する新しい文法を追加しました。

    ここでは Hello world の方法を紹介します。

    ~~~
    print("Hello world")
    ~~~

    と書いたプログラムを実行してみましょう。


こちらもテキストのままで読みやすいですよね。

Markdown Extra では以下のルールが定義されています[^2]。

* コードブロックの開始行は 3つ以上のチルダ(`~`)を含んでいること
* コードブロックの終了行は開始行と同数のチルダであること
* チルダの代わりにバックチック(`)を使ってもよいこと

[^2]: コードブロックに Element ID や CSS のクラスを適用する Special Attributes という記法もあるのですが、ここでは説明を省略します。興味がある方は[こちら](https://michelf.ca/projects/php-markdown/extra/#spe-attr) からどうぞ。

## CommonMark における Fenced code block

CommonMark ではこの Fenced code block により細かいルールを規定しています。

* コードブロックの開始行は 3つ以上の **連続する** バックチックもしくはチルダで始まること[^3]
* コードブロックの終了行は開始行と同数 **以上** の **同じ記号** が並んでいること
* バックチックとチルダが混在している行は開始行/終了行とみなさないこと

[^3]: 厳密には 3つまでの先頭スペースを許容します

このルールによってコードブロック記法を CommonMark 文書の中で記述しやすくなっています。

```
    ~~~
    チルダで始まったコードブロックは
    ```
    バックチックを書いても閉じられません。
    ~~~
```
```
    ``````
    また、開始行のマーカーを長めに書いておくと
    ```
    それよい短い行はマーカーとみなされません。
    ``````
```

## Qiita での Fenced code block の解釈は?

どうも Markdown Extra ベースの実装になっているようです。

* バックチックで開始したコードブロックをチルダで閉じることができる
* マーカーの長さによらず、3つ以上のチルダもしくはバックチックで閉じることができる

ということで、CommonMark のところで挙げた例は Qiita では有効ではありません。
Fenced code block 記法について説明するときは 4つ以上インデントしてマーカーとして判別させないというハックが必要なのでした[^4]。ちょっぴりつらいですね。

```
Markdown ではコードブロックを書くときに

    ```
    print("Hello world")
    ```

と書きます。4文字以上インデントすると終了行と解釈されません。
```

[^4]: 全角スペースを挟み込むという悪行がちらっと頭によぎりましたが、おすすめしません。

## インライン Code 要素 (Code spans) について

関連する要素としてインラインコード要素があります。
CommonMark では Fenced code block と同様に開始マーカーに複数個のバックチックが使えます。

```
バックチックを使うとインラインに ```コードを埋め込む``` ことができます
```

さらに、コードブロックの前後のスペースはトリムされることになっているため、次のように書くとチルダをインラインコード要素として表現できます。

```
複数個のバックチックとスペースを使うと ``` ` ``` というマークアップでチルダが表現できます。
```

一方、Qiita では複数個のチルダに対応していないので、チルダを含んだコードをインラインで表示できないように見えます。チルダを書きたい場合はコードブロックを使うと良いでしょう。もしくは、エスケープすることで回避する手もあります。

```
エスケープするとチルダ \` を記述できます。
残念ながらコード要素内ではありませんが…
```

## まとめ

* Fenced code block の出自
* Qiita の Fenced code block は Markdown Extra ベースっぽいこと
* その回避方法