対話型コンソールアプリ制作初級者に教えたい「CSI」

#はじめに
わたしはコンソールアプリ上級者**ではありません**
ただ色々制作していた中でたまたまCSIを知って感動したので書きました。

#CSIとは
**エスケープシーケンス**の一種です。
エスケープと聞いて真っ先に思い浮かぶのは、`\n`でしょう。
`\n`はUnix系において改行を意味する非常に簡単なエスケープですが、今回はもう少し込み入った特殊表現を行います。

ちなみに、CSIは*Control Sequence Introducer*の頭文字です。

#なにができるか
代表的なものは
- カーソルの移動
- 画面の消去（カーソル以前の全消去、カーソル以後の全消去も可）
- 行の消去
- 文字の消去
- 矩形単位での文字消去
- 文字スタイル（太字、文字色、背景色など）の設定
- ウィンドウ情報（サイズなど）の制御
などなど…

[**こちら**](https://ttssh2.osdn.jp/manual/ja/about/ctrlseq.html#CSI)に制御の種類と方法が全て掲載されています。

#動作について
~~**Bashは簡単にエスケープさせてくれないようです**（[参考](http://qiita.com/kawaz/items/f8d68f11d31aa3ea3d1c)）。~~
［追記：コメント情報］
Bashでもエスケープできるようです。`echo -e`のように`-e`オプションをつけると、エスケープが適用されます。
また、上記リンクは別の議論でした。訂正させていただきます。

#CSIを使ってみる
さて、先の例では改行をするための`\n`を示しました。
CSIは複数の制御ができるので制御コードは一通りではありませんが、CSIであることを認識させるための接頭辞があります。
それが**`\e[`**または**`\033[`**ないしは**`\0x1b[`**です。

[追記]
bashがエスケープできないと思っていた件、`\e[`が対応していないだけでした。`\033[`なら可能です。

eは文字型、033は8進数、0x1bは16進数なので本質的には変わりませんが、eの方が楽でしょう。
CSIは狭義の[^1]エスケープシーケンスの一種です。始めに`\e`を挿入することでエスケープシーケンスの始まりを伝え、さらに`[`でCSIであることを伝えます。

この後に何を書くかによってCSIの制御が変わってきます。
##CSI制御一覧（一部）

|制御文字（スペースは不要）|動作|
|:--:|:--|
|*n* A|n行(指定がなければ1行)カーソルを上げる|
|*n* B|n行(指定がなければ1行)カーソルを下げる|
|*n* C|n文字(指定がなければ1文字)カーソルを進める|
|*n* D|n文字(指定がなければ1文字)カーソルを戻す|
|*n* E|カーソルをn行(指定がなければ1行)下の先頭に合わせる|
|*n* F|カーソルをn行(指定がなければ1行)上の先頭に合わせる|
|*n* G|カーソルを現在の行のn文字目(指定がなければ1文字目)に合わせる|
|*n*; *m* H|カーソルをn行目のm文字目(指定がなければ1行1文字:左上)に合わせる|
|*n* J|画面消去（詳細は後述）|
|*n* K|行消去（詳細は後述）|
|*n* S|n行（指定がなければ1行）画面をスクロールして進める|
|*n* T|n行（指定がなければ1行）画面をスクロールして戻す|
|*n* L|カーソルのある行の前にn行（指定がなければ1行）挿入|
|*n* M|カーソルのある行からn行（指定がなければ1行）削除|
|*n* P|カーソルの位置からn文字（指定がなければ1文字）削除|
|*n* P|カーソルの位置からn文字（指定がなければ1文字）を空白置換|
|*n* `|カーソルを現在の行のn文字目（指定がなければ1文字目）に移動|
|*n* a|カーソルをn文字（指定がなければ1文字）進める|
|*n* d|カーソルを現在の位置のn行（指定がなければ1行）上げる|
|*n* m|SGR(Select Graphic Rendition)パラメータを指定する（詳細は後述）|
|s|現在のカーソル位置を記憶する|
|u|記憶したカーソル位置を呼び出す|
|> 3; *a*; *b*; *c*; *d* J|(b, a)から(d, c)まで矩形消去|
|> 3; *a*; *b*; K|現在の行のa文字目からb文字目まで消去|

###画面消去（*CSI* *n* J）と行消去（*CSI* *n* K）の挙動

|n|挙動|
|:--:|:--|
|無指定|カーソルより後ろを画面（行）先頭まで消去|
|0|カーソルより後ろを画面（行）先頭まで消去|
|1|カーソルより手前を画面（行）末尾まで消去|
|2|画面（行）全体を消去|

###SGRパラメータ（代表的なもの）

|n|意味|
|:--:|:--|
|なし|パラメータリセット|
|0|パラメータリセット|
|1|太字|
|2|薄字（広くはサポートされていない）|
|3|斜体（広くはサポートされていない、反転表示(n=7)になることも）|
|4|下線|
|5|点滅：150回/分以内|
|6|高速点滅：150回/分以上（広くはサポートされていない）|
|7|反転表示：前景と背景の色が入れ替わる|
|8|文字を隠す（広くはサポートされていない）|
|9|取り消し線（広くはサポートされていない）|
|10|デフォルトのフォント|
|11–19|(n-10)番目の代替フォントを使用|
|20|[フラクトゥールフォント](https://ja.wikipedia.org/wiki/%E3%83%95%E3%83%A9%E3%82%AF%E3%83%88%E3%82%A5%E3%83%BC%E3%83%AB)（ほとんどサポートされていない）
|21|太字OFF（広くはサポートされていない）, 二重下線（ほとんどサポートされていない）|
|22|色と強調（太字・薄字）を元に戻す|
|23|斜体とフラクトゥールを元に戻す	
|24|下線を取り消す|
|25|点滅を取り消す|
|28|文字の隠蔽を取り消す|
|29|取り消し線を消す|	
|30–37|前景色（要は文字色）を(n-30)番に変更させる（色番号は後述）|
|38|前景色設定拡張。`\e[38;5;n`でカラーインデックスを指定できる(0...255)。`\e[38;2;r;g;b`でRGBを設定できる(各0...255)。|
|39|前景色をリセット|
|40–47|背景色を(n-40)番に変更させる（色番号は後述）|
|48|背景景色設定拡張。`\e[48;5;n`でカラーインデックスを指定できる(0...255)。`\e[48;2;r;g;b`でRGBを設定できる(各0...255)。|
|49|背景色をリセット|
なんだこのサポートされてない嵐…

###色番号

|番号|色|
|:--:|:--:|
|0|黒|
|1|赤|
|2|緑|
|3|黄|
|4|青|
|5|マゼンタ|
|6|シアン|
|7|白|

ざっと各色を表示するとこのような感じです。
![スクリーンショット 2016-08-05 10.39.55.png](https://qiita-image-store.s3.amazonaws.com/0/94904/1f70d50b-9465-addf-93df-764f6dab76b8.png)


##SGRの使い方
実際にSGR設定を行うときは

```zsh
echo "\e[31mred\e[m"
```
とやれば赤文字で`red`と表示してくれます。

また、

```zsh
echo "\e[31;43mred&yellow\e[m"
```
とやれば、赤文字黄背景で`red&yellow`と表示してくれます。
このようにオプションを`;`区切りで打っていけば複数のオプションを一度に設定できます。
CSI終了後に予想外の挙動をしないように、**必ず最後に`\e[m`または`\e[0m`でオプションを切りましょう。**

[^1]: 厳密には`\n`はエスケープシーケンスではありません。狭義には`ESC(0x1B)`から始まる制御バイト列のことを指すそうです。