Kinx ライブラリ - エスケープ・シーケンス

# Kinx ライブラリ - エスケープ・シーケンス

## はじめに

**「見た目は JavaScript、頭脳(中身)は Ruby、（安定感は AC/DC）」** でお届けしているスクリプト言語 [Kinx](https://github.com/Kray-G/kinx)。言語はライブラリが命。ということでライブラリの使い方編。 

今回はエスケープ・シーケンスです。なんのこっちゃと思うかもしれませんが、Windows でも VT100 のエスケープ・シーケンスを使えるようにしたので、この部分でも Windows/Linux で共通化が（一部）可能に。

* 参考
    * 最初の動機 ... [スクリプト言語 KINX（ご紹介）](https://qiita.com/Kray-G/items/ca08b6fb40d15dd0ec76)
        * 個別記事へのリンクは全てここに集約してあります。
    * リポジトリ ... [https://github.com/Kray-G/kinx](https://github.com/Kray-G/kinx)
        * Pull Request 等お待ちしております。 

将来的に curses を使う話もなくはない。ただ大掛かりで高機能なので、まずは簡単にエスケープ・シーケンスが使える程度でも役に立つかと。

## エスケープ・シーケンスって何?

制御端末って味気ないですよね。エスケープ・シーケンスを使うとホラ、今すぐあなたもカラフルに色をつけたり、ページングしたり、ちょっとした見栄えの良い操作性を提供できます。

## どうやって実現?

ます、レキサーのほうでは `\e` を `\x1b` の代わりとして使えるようにした。短く書けて便利です。

Linux 端末は VT100 系統のエスケープ・シーケンスは最初から使える想定。Windows は以下を利用させていただきました。

* [ansicolor-w32.c](https://github.com/mattn/ansicolor-w32.c)

これで Windows のエスケープ・シーケンスもサポート完了。なんて素晴らしいライブラリなんだ。MIT ライセンス。しかし、いくつか修正した。

* `ESC [ 38;2;r;g;b m`、`ESC [ 38;5;d m` に対応してなかったので対応。
* `ESC [ 48;2;r;g;b m`、`ESC [ 48;5;d m` に対応してなかったので対応。
* アンダーラインがボールドと同じ対応で違和感があったので削除。~~アンダーラインは未サポート~~ アンダーラインを正式にサポート（0.9.2リリースには入ってません）。

> この記事を投稿した直後、ほんとにアンダーラインできんものなのか？と調べたらやり方が分かった。。。なので、たった今修正してコミットした。アンダーライン、サポートしたよ！（最新ソースでは）

Windows のコマンドプロンプトはかなり制限がきつく、16色しかサポートできない。なので、上記 `38`、`48` では近い色になるように結構細工している。しかしこんなに頑張る必要あったのだろうか、という気が若干しないでもない。しかもそんなにうまくいってる気がしない。まあいいか。

では、いくつかサンプルを見てみよう。

## サンプル

### サンプル 1

まずは色サンプル。これは [ansicolor-w32.c](https://github.com/mattn/ansicolor-w32.c) でサポートしている範囲。ただし、アンダーラインだけは ~~未サポートにした~~ 下記の結果に出ていないが自分で実装して最新ソースではサポートされている。尚、左側がコマンドプロンプト。右側の黄色い枠線内は WSL 上で動かしている ubuntu での結果。

ちなみに、このソースのオリジナルは [ここ](https://qiita.com/hidai@github/items/1704bf2926ab8b157a4f) です。これを Kinx 用に書き換えています。

```javascript
System.print("  --   ");
for (b in 40..47) {
  System.print("\e[%{b}m    %{b}   \e[0m ");
}
System.print("\n");
for (c in [ 30, 31, 32, 33, 34, 35, 36, 37, 90, 91, 92, 93, 94, 95, 96, 97 ]) {
  s = (""+c);
  System.print("\e[%{c}m %{c}   \e[0m ");
  for (b in 40..47) {
    s = "%{c};%{b}";
    System.print("\e[%{s}m %{s}   \e[0m ");
  }
  System.print("\n");
  for (a in [ 1, 4 ]) {
    s = "%{c};%{a}";
    System.print("\e[%{s}m %{s} \e[0m ");
    for (b in 40..47) {
      s = "%{c};%{b};%{a}";
      System.print("\e[%{s}m %{s} \e[0m ");
    }
    System.print("\n");
  }
}
```

この範囲ならばっちり使えると思う。アンダーラインは ~~無いけど~~ 最新ソースでは追加した。

![sample5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/579271/e2bab53e-0a87-1991-a87d-2c0a983f2a6c.png)

ここからが自分で追加実装した部分。

### サンプル 2

256色指定での文字色。

```javascript
for (i = 0; i < 16; i++) {
    for (j = 0; j < 16; j++) {
        const v = i*16+j;
        System.print("\e[38;5;%dm%02X\e[0m " % v % v);
    }
    System.print("\n");
}
```

こんな感じになる。16色しかないのでこんな感じか。個別に色を設定してはおらず RGB 情報から計算して 16 色に変換しているので、微妙に色の入れ替わりがスムーズでもない気もするが、こんなものか...。

![sample1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/579271/0da26384-d065-4790-15d5-a2feeb6d1a41.png)

### サンプル 3

続いて256色指定での背景色。

```javascript
for (i = 0; i < 16; i++) {
    for (j = 0; j < 16; j++) {
        const v = (i<<4) | j;
        System.print(" \e[48;5;%dm%02X" % v % v);
    }
    System.print("\e[0m\n");
}
```

出力結果。微妙に濃い感じに見えるな。

![sample2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/579271/66d82b27-292e-c76f-1d7b-d8b184b5e633.png)

### サンプル 4

RGB 指定での文字色。

```javascript
for (i = 0; i < 16; i++) {
    for (j = 0; j < 32; j++) {
        System.print("\e[38;2;%d;%d;255mX" % (i<<4) % (j<<3));
    }
    System.print("\e[0m\n");
}
```

この辺から怪しさが増す。が、それっぽく...

![sample3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/579271/48f4b0b8-aafc-0eb1-d1ef-5bad9c414b03.png)

### サンプル 5

RGB 指定での背景色。

```javascript
for (i = 0; i < 16; i++) {
    for (j = 0; j < 32; j++) {
        System.print("\e[48;2;%d;%d;255m " % (i<<4) % (j<<3));
    }
    System.print("\e[0m\n");
}
```

こうすると違いが目立つ。

![sample4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/579271/ddb54ff9-3e54-ca4a-6f03-0b338bdf39e3.png)

### サンプル 6

基本的なところに戻って、テキストベースでのプログレスバーなんかも出せる。元ネタは [ここ](https://www.mm2d.net/main/prog/c/console-04.html) の「エスケープシーケンスの利用」です。

```javascript
System.print("0%       50%       100%\n");
System.print("+---------+---------+\n");
for (i = 0; i <= 100; i++) {
    for (j = 0; j < i / 5 + 1; j++) {
        System.print("#");
    }
    System.print("\n");
    System.print("%3d%%\n" % i);
    System.sleep(100);
    System.print("\e[2A");
}
System.print("\n\nfinish!\n");
```

![sample7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/579271/58eded23-6699-2a01-5112-5cba40ae4204.png)

## おわりに

エスケープ・シーケンスは今時そんなに重要ではないとは思うが、ちょっとしたコマンドを見栄え良くするのに便利なので、個人的には好き。**Windows で使える**って、そこそこ嬉しい人いるんじゃなかろうか。

ではまた、次回。