GitHub Flavored Markdown は何であって何でないか

のんびりしていたらこんなメンションをもらっていたので、ちょっとまとめてみようと思います。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">そろそろ <a href="https://twitter.com/tk0miya?ref_src=twsrc%5Etfw">@tk0miya</a> さんがアップしてくる頃。GFMはspecかっちりしてるんでしたっけ(markdown全く詳しくない</p>&mdash; Aki Ariga (@chezou) <a href="https://twitter.com/chezou/status/1223581964818452480?ref_src=twsrc%5Etfw">February 1, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## かっちりしている?

この記事を読んでいる皆さんは Markdown の歴史に精通していると思うので、古い部分はざっくり割愛してしまいますが、オリジナルの Markdown は **かっちりしていない** ことで有名なマークアップ言語です。

必要最低限のマークアップ要素は規定されていて HTML への変換ツールも完成していた Markdown ですが、マークアップ言語の言語仕様としては貧弱で、インデントのルールやインライン要素をネストしたときの挙動、空行の有無による解釈の違い、などなど、細かい部分のルールについては一切定義されていなかったのでした。

これが原因で、さまざまな言語やツールに移植される際にそれぞれが独自の解釈をすることになってしまったため、処理系によって処理結果が変わってしまうという[バベルの塔](https://babelmark.github.io/)を生み出してしまったあの Markdown です。

これに対して、なんとか統一変換ルールを作らねば、と立ち上がったのが [CommonMark プロジェクト](https://commonmark.org/)のひとたちでした。彼らはさまざまな荒波を乗り越えながら Markdown の各要素をどのように解釈するとよいのかを仕様書に取りまとめたのでした。[CommonMark spec](https://spec.commonmark.org/current/) の各サンプルを見ていくと、異常系のサンプルの多さが際立ちます。Markdown の歴史は分化の歴史であったため、それをなくすためにエッジケースにエッジケースを重ねて、解釈の余地を減らしていったのが CommonMark spec なのです[^1]。

[^1]: なぜか独自の記法として [Fenced code blocks](https://spec.commonmark.org/0.29/#fenced-code-blocks) が追加されているので単なるパースルールブックと認識するのは正確ではないのですが、ここでは些細な話なので割愛します。

最初に出てきた **かっちりしている** というのは CommonMark spec を念頭においての発言なんですね。

## じゃあ GFM はかっちりしているの?

それでは最初の質問にもどると、GFM はかっちりしているのでしょうか。
GFM とは皆さんおなじみの GitHub Flavored Markdown というやつで、GitHub 上で利用できるあの Markdown ですね。みなさんが思い浮かべる **マークダウン** というのはだいたいこれを指していると思って間違いはないと思います。

以前の GFM はマークアップ仕様などは一切公開されていない、単なる Markdown の亜種のひとつだったのですが、CommonMark プロジェクトが進むうちに GFM 自身が [CommonMark の派生実装のひとつとして再定義されました](https://github.blog/2017-03-14-a-formal-spec-for-github-markdown/)。

いまでは [GFM spec が公開されており](https://github.github.com/gfm/)、だれでも GFM の定義を読むことができるようになっています。GFM spec の目次に目を通すと **(extension)** と書かれたセクションがあります。GFM は CommonMark をベースにしているため、GFM の独自仕様はこの部分だと明示されているのです。

extension と記載されている要素は以下のものです。

* [4.10 Tables (extension)](https://github.github.com/gfm/#tables-extension-)
* [5.3 Task list items (extension)](https://github.github.com/gfm/#task-list-items-extension-)
* [6.5 Strikethrough (extension)](https://github.github.com/gfm/#strikethrough-extension-)
* [6.9 Autolinks (extension)](https://github.github.com/gfm/#autolinks-extension-)
* [6.11 Disallowed Raw HTML (extension)](https://github.github.com/gfm/#disallowed-raw-html-extension-)

表やタスクリスト、打ち消し線など、GitHub のイシューなどでよく使う要素が追加されていますね。
ベースとなる CommonMark の厳密な仕様記述に、追加された要素が差分で記載されているのですから、これはかっちりしていると言ってもよいのではないでしょうか。

**いいえ、よくありません**。

## GFM はかっちりしてない

### spec の中で矛盾を起こしている

CommonMark で定義された仕様と GFM で追加された仕様が矛盾している箇所がいくつかあります。

たとえば [6.8 Autolinks](https://github.github.com/gfm/#autolinks) では

```
http://example.com
```
という URL が書いてある場合は変換されずに

```
<p>http://example.com</p>
```
と出力されると定義されているのですが(Example 619)、GFM で追加された [6.9 Autolinks (extension)](https://github.github.com/gfm/#autolinks-extension-) では

>An extended url autolink will be recognised when one of the schemes http://, or https://, followed by a valid domain, then zero or more non-space non-< characters according to extended autolink path validation:

と規定されていて、リンクになると書かれています (Example 628)。
順序や成り立ちを考えると、前者の仕様は破棄され GFM としては後者の仕様を採用したという理解となるのですが、仕様書の中で矛盾が起きているのは非常に厄介です。

同様の衝突が [4.6 HTML blocks](https://github.github.com/gfm/#html-blocks) と [6.11 Disallowed Raw HTML (extension)](https://github.github.com/gfm/#disallowed-raw-html-extension-) にもあります。

### 追加された要素の仕様記述レベルがゆるい

残念ながら GFM で追加された要素の仕様記述レベルは、CommonMark 由来のパートと比べると非常にゆるいと言わざるを得ません。
たとえば、CommonMark では、ブロックレベルの要素が段落に割り込むことができるかどうか(can interrupt)が非常に重要な概念となっていて、各ブロック要素の説明には必ず interrupt についての説明があります。

>Thematic breaks can interrupt a paragraph:

>ATX headings need not be separated from surrounding content by blank lines, and they can interrupt paragraphs:

>An indented code block cannot interrupt a paragraph. (This allows hanging indents and the like.)

残念ながら追加された [4.10 Tables (extension)](https://github.github.com/gfm/#tables-extension-) では、一切の言及がありません。

また、前述の通り CommonMark ではその歴史的背景から、神経質すぎると感じるほどのエッジケースのサンプルを掲載していますが、GFM の追加要素にはそれがありません。正常系とちょっとしたイレギュラーケースについて記述があるのみです。

たとえば、[5.3 Task list items (extension)](https://github.github.com/gfm/#task-list-items-extension-) では

>A task list item marker consists of an optional number of spaces, a left bracket ([), either a whitespace character or the letter x in either lowercase or uppercase, and then a right bracket (]).

と定義されているのですが、"an optional number of spaces" について、スペースが多い場合、少ない場合については特に言及がありません。たとえば、スペースが多い場合は [4.4 Indented code blocks](https://github.github.com/gfm/#indented-code-blocks) との棲み分けについて考える必要があります。

CommonMark と同等のレベルで仕様を書くのは非常に難しいだろうとは思うのですが、オリジナルの Markdown が引き起こしたトラブルを考えると、もう少し詳細な仕様を記述しても良いのではないかと思います。いまのところ、エッジケースについては github.com の挙動を信じるしかなさそうです[^2]。

[^2]: 過去に[テーブルのエッジケースの理解を深めたことがある](https://gist.github.com/tk0miya/20692987c8d47128e75d0a59bfdda4a8)のですが、これは果たして仕様なのでしょうか。

### github.com の動作と合致していない

GFM spec の先頭では、GFM は GitHub.com で使われている Markdown のことだと宣言されています([1.1 What is GitHub Flavored Markdown?](https://github.github.com/gfm/#what-is-github-flavored-markdown-))。

>GitHub Flavored Markdown, often shortened as GFM, is the dialect of Markdown that is currently supported for user content on GitHub.com and GitHub Enterprise.

しかし、GFM spec と github.com の挙動を照らし合わせると明らかな矛盾があります。

一番わかりやすいのは [6.13 Soft line breaks](https://github.github.com/gfm/#soft-line-breaks) です。GFM では改行は soft line break とみなされ、HTML には単なる改行として変換されます。そのため、ブラウザから変換結果を見るとスペースに変換されると規定されています。しかし、github.com で Markdown を書いてみると、改行は hard line breaks として扱われ、ブラウザ上でも改行として扱われます(`<BR>` 相当)。

他にも、許可するとされる HTML タグが省略されたり書き換えられるといった動作の違いがあります。

前者に関してはさっぱり意味がわかりません。
後者については、サービスを提供するという立場上、自由に HTML タグを書けないような制約を設けるのは自然だとは理解できるのですが、それであれば [6.11 Disallowed Raw HTML (extension)](https://github.github.com/gfm/#disallowed-raw-html-extension-) という追加仕様があるのですから、そこに記述すべきだとも言えます。

### GFM に記載されていない github.com 独自のマークアップが存在する

さて、お気づきでしょうか。github.com では日常的に使われている記法が、GFM には掲載されていません。こちらで認識しているのは次のようなものです。

* 絵文字 (ex. ``:+1:``)
* メンション
* コミットの参照 (コミットID)
* イシューの参照
* リポジトリの参照

いくつかのものは github.com としての、リポジトリやイシューを管理しているからこそ実現できるマークアップですから、マークアップ言語としての仕様にはあえて記述していないのかもしれません。
しかし、絵文字が含まれていないのは理解できません。GitHub のマークダウンには絵文字が入っていない、というのはみなさん同意できますか?
(ちなみにサービスよりの記法は gist では使えません)

## まとめ(というか雑感)

* 僕は GFM はかっちりしてないと思う
* 規定されていない/非公開よりマシ
* みんなが使っているのは GitHub.com Flavored GFM であって GFM ではない
* だからといって、僕らが Markdown 記法を選択するときに GFM 以前に戻ることはできないだろう (開発者はたいてい GitHub に慣れているので)