List continuationでAsciidoctorの順序付きリストの採番を崩さないようにする

# はじめに
マークアップ言語で手順を記述する際に順序付きリストは便利ですよね。
しかし、代表的なマークアップ言語であるMarkdownでは、順序付きリストの間にコードブロックやテーブルを挿入すると採番が1から振り直されてしまうという悲しみを背負っています。
一方で、[Asciidoctor](https://asciidoctor.org)というマークアップ言語にはそのような悲しみを克服するList continuationという機能が用意されています。
本記事では、List continuation機能で順序付きリストの採番を崩さないようにする例を紹介します。

# 環境情報

```bash
$ asciidoctor --version
Asciidoctor 1.5.6.2 [http://asciidoctor.org]
Runtime Environment (ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17]) (lc:UTF-8 fs:UTF-8 in:- ex:UTF-8)
```

# List continuation とは
まず、公式の説明を引用させていただきます。

> To add additional paragraphs or other block elements to a list item, you must “attach” them (in a series) using a list continuation. A list continuation is a + symbol on a line by itself, immediately adjacent to the block being attached.

block とは Asciidoctor の文書を構成する要素です。例えば、以下は block です。

* 表
* 図
* コードブロック
* パラグラフ

つまり、表やコードブロックなどを含めてリストの1項目とする機能です。

# NG例
最初に、順序付きリストの採番が崩れてしまう例を示します。
コードは以下です。

```
. foo
|===
||0|1
|0|0|1
|1|1|0
|===

. bar

. baz
```

表示は以下です。

![スクリーンショット 2018-06-17 14.12.40.png](https://qiita-image-store.s3.amazonaws.com/0/260145/6c82eb40-547d-6c8a-8137-9ecc8fd04ae1.png)

barに1が採番されてしまいました。

# List continuationで採番を崩さない例
次に、順序付きリストの採番を崩さない例を示します。
コードは以下です（2行目に+が追加されただけです）。

```
. foo
+
|===
||0|1
|0|0|1
|1|1|0
|===

. bar

. baz
```

表示は以下です。

![スクリーンショット 2018-06-17 14.08.00.png](https://qiita-image-store.s3.amazonaws.com/0/260145/b4a75ab1-31ac-095e-53dd-c7da2ba3d593.png)

barにきちんと2が採番されました。
図、コードブロック、パラグラフも同様に採番を崩さないようにできます。

# 少しマニアックなおまけ
Asciidoctorは文書に属性（環境変数のようなもの）を定義する機能があります。
例えば、以下のコードは `pi` という属性に `3.14` をセットし、それを参照した文を表示させています。

```
:pi: 3.14
. 円の半径 `r` を測ります。
. 円の面積 `S = {pi} * r * r` を計算します。
. 円の円周 `L = 2 * {pi} * r` を求めます。
```

表示は以下です。

![スクリーンショット 2018-06-17 14.28.14.png](https://qiita-image-store.s3.amazonaws.com/0/260145/e026a174-58ee-446c-5de5-4f501bde8ec9.png)

上記のように記述することで、円周率を `3` として計算させるように変更する際、1行目の属性 `pi` の値を変更するだけでよくなります。

さて、稀なケースではありますが、属性の定義を順序付きリストの中に組み込みたい場合があります。
あまりうまい例ではないですが、以下のコードを考えます。

```
. 小数部分 `r` と 次数 `n` を標準入力で受け取ります。

ifeval::[{precise} == true]
. 2次の項まで近似します、というメッセージを標準出力します。
:expr: 1 + nr + n(n-1)/2*r*r
endif::[]
ifeval::[{precise} == false]
. 1次の項まで近似します、というメッセージを標準出力します。
:expr: 1 + nr
endif::[]

. `y = {expr}` を求めます。

. `y` を標準出力します。
```

上記は属性 `precise` が `true` ならば2次の項まで近似する項目を、 `false` ならば1次の項まで近似する項目を表示させたかったコードです（ちなみに、 `ifeval` は想像の通り、条件分岐文です）。
しかし、以下のように属性の定義式がそのまま表示され、属性の定義もされていません（ `precise` は `true` とします）。

![スクリーンショット 2018-06-17 14.59.12.png](https://qiita-image-store.s3.amazonaws.com/0/260145/84932e1f-3988-90d2-3d00-b76779f17b70.png)

これもList continuationすれば解決するのか？と思い、以下のコードにしてみます（属性の定義式の前の行に+を追加した）。

```
. 小数部分 `r` と 次数 `n` を標準入力で受け取ります。

ifeval::[{precise} == true]
. 2次の項まで近似します、というメッセージを標準出力します。
+
:expr: 1 + nr + n(n-1)/2*r*r
endif::[]
ifeval::[{precise} == false]
. 1次の項まで近似します、というメッセージを標準出力します。
+
:expr: 1 + nr
endif::[]

. `y = {expr}` を求めます。

. `y` を標準出力します。
```

以下のように属性の定義式は表示されなくなりましたが、属性は定義されていないままです。

![スクリーンショット 2018-06-17 15.04.21.png](https://qiita-image-store.s3.amazonaws.com/0/260145/de6c15ee-d6a6-855a-ae9b-c4318bb5d35d.png)

リストの中で属性の定義はできないのでしょうか。
実は、以下のようにインラインで属性を定義することで実現できます。

```
. 小数部分 `r` と 次数 `n` を標準入力で受け取ります。

ifeval::[{precise} == true]
. 2次の項まで近似します、というメッセージを標準出力します。
{set:expr:1 + nr + n(n-1)/2 r*r}
endif::[]
ifeval::[{precise} == false]
. 1次の項まで近似します、というメッセージを標準出力します。
{set:expr:1 + nr}
endif::[]

. `y = {expr}` を求めます。

. `y` を標準出力します。
```

出力は以下のとおりです。

![スクリーンショット 2018-06-17 15.06.29.png](https://qiita-image-store.s3.amazonaws.com/0/260145/e7c83868-e98a-27a4-9d21-9c4e09612743.png)

期待通りの結果が得られました。
これは、属性の定義式がブロックでないことに起因すると思われます。

# 参考ページ
* [Asciidoctor User Manual](https://asciidoctor.org/docs/user-manual/)