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


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

はじめに

「見た目は JavaScript、頭脳(中身)は Ruby、(安定感は AC/DC)」 でお届けしているスクリプト言語 Kinx。言語はライブラリが命。ということでライブラリの使い方編。

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

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

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

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

どうやって実現?

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

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

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

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

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

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

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

サンプル

サンプル 1

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

ちなみに、このソースのオリジナルは ここ です。これを Kinx 用に書き換えています。

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");
  }
}

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

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

サンプル 2

256色指定での文字色。

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 色に変換しているので、微妙に色の入れ替わりがスムーズでもない気もするが、こんなものか...。

サンプル 3

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

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");
}

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

サンプル 4

RGB 指定での文字色。

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");
}

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

サンプル 5

RGB 指定での背景色。

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");
}

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

サンプル 6

基本的なところに戻って、テキストベースでのプログレスバーなんかも出せる。元ネタは ここ の「エスケープシーケンスの利用」です。

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");

おわりに

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

ではまた、次回。