RubyでANSIカラーシーケンスを学ぼう!
CUIの世界は地味な世界です。真っ黒なターミナル画面に単一色の文字列。それが却って落ち着くという向きもありますが、今となってはその地味さは際立っています。
ターミナルで色を使う方法を学ぶことで、新しい世界が開けるかも知れません。
ようこそANSIカラーの世界へ!
エスケープシーケンス
ターミナルで色を使うためにはエスケープシーケンスというものを利用します。エスケープシーケンスはターミナル上で色を含む特定の制御を実現するための特殊な文字列です。
print "\e[31mhello\e[0m"
これによりターミナル上に赤文字で「hello」と出力されます。この”\e[31m” “\e[0m”の部分がエスケープシーケンスです。”\e[31m”はそれ以降を赤文字で出力する制御命令、”\e[0m”はそれ以降を初期状態へリセットする制御命令です。
シーケンス中の数字において、30~37は文字色、40~47は背景色、0~9は文字装飾に割り振られています。
Text attributes
0 All attributes off
1 Bold on
4 Underscore (on monochrome display adapter only)
5 Blink on
7 Reverse video on
8 Concealed on
Foreground colors
30 Black
31 Red
32 Green
33 Yellow
34 Blue
35 Magenta
36 Cyan
37 White
Background colors
40 Black
41 Red
42 Green
43 Yellow
44 Blue
45 Magenta
46 Cyan
47 White
ASCII Table - ANSI Escape sequences
1つの文字列に複数のシーケンスを適用することもできます。
print "\e[31m\e[43m\e[5mhello\e[0m"
これで黄色の背景色上で赤いhelloが点滅します。セミコロンで区切って、複数のシーケンスコードを適用することもできます。これは上のシーケンスと等価です。
print "\e[31;43;5mhello\e[0m"
エスケープシーケンスは端末依存で、端末によって使えないものもあります。
ターミナルカラー・ライブラリ
Rubyでカラーシーケンスを使う場合、そのままでは扱いづらいので定数などへのマッピングが必要になるでしょう。また文字列終端での復帰にも対応しなければいけません。自作する手もありますが、Rubyには既に便利なライブラリがいくつかあります。
HighLine
HighLineはターミナルの入出力を支援するツール群です。その中にカラーシーケンスを扱うHighLine#colorがあります。gem install highline
でインストールして、以下のように使います。
require 'highline'
h = HighLine.new
puts h.color("hello", :green)
色属性として複数の引数を取ることもできます。
puts h.color("Ruby World", :red, :on_cyan, :underline)
TermColor
TermColorは文字列中のカラータグをエスケープシーケンスに変換するライブラリです。gem install termcolor
でインストールして、以下のように使います。
require 'termcolor'
puts TermColor.parse("<green>hello</green>")
puts TermColor.parse("<red><on_white><blink>ruby is fun!</blink></on_white></red>")
色指定は1つしかできませんが以下のように使うこともできます。
puts TermColor.colorize("hello", :green)
またString#termcolorが用意されているので、以下のようにもできます。
puts "<red><bold>Ruby is red!</bold></red>".termcolor
TermColorはTermtterのjugyoさん作で、Termtterでのカラー表示に使われています。
Rainbow
RainbowはStringクラスにcolor(foreground), background,bright, blink, inverseなどのエスケープシーケンスに対応したメソッドを追加します。gem install rainbow
でインストールして、以下のように使います。
require 'rainbow'
puts "hello".color(:red)
puts "hello".color(255, 0, 0)
puts "hello".color(:red).background(:green)
puts "hello".bright.background(:blue).blink
Term-ANSIColor
Term-ANSIColorは多様な使い方ができるライブラリで、エスケープシーケンスをモジュール関数化したり、Stringクラスのインスタンスメソッドにしたりできます。gem install term-ansicolor
でインストールして、以下のように使います。
まずはクラスメソッドとして。
require 'term/ansicolor'
class Color
extend Term::ANSIColor
end
print Color.red, Color.bold, "No Namespace cluttering:", Color.clear, "\n"
print Color.green + "green" + Color.clear, "\n"
print Color.on_red(Color.green("green")), "\n"
print Color.yellow { Color.on_black { "yellow on_black" } }, "\n\n"
次にTerm::ANSIColorのインスタンスメソッドとして。
c = Term::ANSIColor
print c.red, c.bold, "No Namespace cluttering (alternative):", c.clear, "\n"
print c.green + "green" + c.clear, "\n"
print c.on_red(c.green("green")), "\n"
print c.yellow { c.on_black { "yellow on_black" } }, "\n\n"
関数的に。
include Term::ANSIColor
print red, bold, "Usage as constants:", reset, "\n"
print red("red"), on_red("on_red"), "\n"
print red { bold { "Usage as block forms:" } }, "\n"
Stringのメソッドとして。
class String
include Term::ANSIColor
end
print "Usage as String Mixins:".red.bold, "\n"
print "Hello, World\n".blue.on_yellow.blink
Wirble
Wirbleはirb(Interactive Ruby)の支援ツールで、Rubyのシンタックスに合わせたカラーリングすなわち、Syntax Highlightingを実現します。ライブラリとして使う場合は以下のようにします。
require "wirble"
w = Wirble::Colorize
puts w.colorize_string("Hello, Ruby", :light_cyan)
puts w.colorize("hello, :hello, [1, 2, 3], {:a => 1, :b => 2}")
colorizeメソッドでRubyのオブジェクトに応じたカラーリングが実現できているのが分かります。
なおirbで使う場合は.irbrcで以下のように指定します。
require 'wirble'
Wirble.init
Wirble.colorize
Coderay
CodeRayは複数の言語に対応したSyntax Highlightingライブラリです。Rubyのコードをターミナルでカラーリングする場合は、以下のようにします。
require "coderay"
puts CodeRay.scan("5.times do\n puts 'hello, ruby'\nend", :ruby).term
ファイルから読込んでHTML出力するようなこともできます。
puts CodeRay.scan_file("code.rb").div(:line_numbers => :table)
素晴らしいですね!
Paint
Paintはライブラリでの使い勝手と拡張性を売りにした、非常にユニークな使い方ができるライブラリです。gem install paint
でインストールして、以下のように使います。
まずはPaint.[]クラスメソッドを使う例です。
require 'paint'
Paint['Ruby', :red] #=> "\e[31mRuby\e[0m"
Paint['Ruby', :red, :bright, :underline] #=> "\e[31;1;4mRuby\e[0m"
Paint['Ruby', :red, :blue] #=> "\e[31;44mRuby\e[0m"
第3引数以降に現れるカラーは背景色用です。
色の指定は多様な方法でできます。
Paint['Ruby', [100, 255, 5]] #=> "\e[38;5;118mRuby\e[0m"
Paint['Ruby', "gold", "snow"] #=> "\e[38;5;226;48;5;231mRuby\e[0m"
Paint['Ruby', "#123456"] #=> "\e[38;5;24mRuby\e[0m"
Paint['Ruby', "fff"] #=> "\e[38;5;231mRuby\e[0m"
第2引数以下を省略すると色指定がランダムになり、:inverseをしていすると前景色と背景色を入れ替えます。
それが削除されたということを示すために Paint['Ruby'] #=> "\e[37mRuby\e[0m"
Paint['Ruby', :inverse] #=> "\e[7mRuby\e[0m"
PaintがユニークなのはPaint::SHORTCUTSを使ったユーザカラー定義です。
Paint::SHORTCUTS[:mycolor] = {
:white => Paint.color(:black),
:red => Paint.color(:red, :bright),
:title => Paint.color(:underline)
} #=> {:white=>"\e[30m", :red=>"\e[31;1m", :title=>"\e[4m"}
上のように登録することでPaint::Mycolorの名前空間で、white red titleの各クラスメソッドが有効になります。
Paint::Mycolor.red "Ruby" #=> "\e[31;1mRuby\e[0m"
Paint::Mycolor.white #=> "\e[30m"
Paint::Mycolor.title "Ruby" #=> "\e[4mRuby\e[0m"
include Paint::Mycolor #=> Object
red "Ruby" #=> "\e[31;1mRuby\e[0m"
white "Ruby" #=> "\e[30mRuby\e[0m"
それだけでなく、Paint::Mycolor::Stringモジュールをincludeすることにより、あらゆるオブジェクトをユーザ定義に従い、色つき文字列化することもできます。
include Paint::Mycolor::String #=> Object
"Ruby".red #=> "\e[1;31mRuby\e[0m"
"Ruby".title #=> "\e[4mRuby\e[0m"
5.red #=> "\e[31;1m5\e[0m"
[1, 2].red #=> "\e[31;1m[1, 2]\e[0m"
ちょっとやり過ぎな気もしますが..
グローバルな名前空間が汚染されることを避けたいなら、プレフィクスとして任意のメソッド名を指定することもできます。
include Paint::Mycolor::Prefix::C #=> Object
"Ruby".c(:red) #=> "\e[31;1mRuby\e[0m"
"Ruby".c(:title) #=> "\e[4mRuby\e[0m"
[1, 2].c(:red) #=> "\e[31;1m[1, 2]\e[0m"
なんかスゴイですね!
ご想像のとおりPaintモジュールの内部はメタプログラミングばりばりです:)
Colcolor
Colcolorはカラム指向の拙作カラーライブラリです。この使い方については2014-7-14に別途記事にしていますが、その一部をここに転載します。
colcolor
が特徴的なのは、他のライブラリが行指向のものであるのに対して、列(カラム)指向である点です。つまり一行におけるホワイトスペースで区切られたカラムを認識し、それ毎に異なるカラーを適用できるのです。
"Charlie 21 programmer".colco(:red, :yellow, :cyan) # => "\e[31mCharlie\e[0m \e[33m21\e[0m \e[36mprogrammer\e[0m"
例えば、タブ区切りのリストにおいて、そのカラムごとに色を変えたい場合は、次のように簡単にできます。
require "colcolor"
list = <<-EOS
Charlie\t21\tprogrammer
Bill\t43\tdoctor
Liz\t18\tstudent
EOS
list.each_line do |line|
puts line.colco(:green, :yellow, :blue)
end
出力です。
背景色を作りたいときは、:bg_green
などとします。
複数属性の適用
一つのカラムに対して、前景色と背景色など複数の属性を適用したい場合は、アンダースコア(_
)で属性をつなぎます。
list.each_line do |line|
puts line.colco(:green, :red_yellow, :blue_underline)
end
最初の色が前景色、2つ目以降が背景色になります。出力です。
緑背景に赤文字を点滅させたいときは、:red_green_blink
などとします。
カスタムカラムパターン
デフォルトではホワイトスペースをカラムの区切りと認識して各色を適用しますが、regexp
オプションでこれをカスタマイズできます。
例えば、リストにスペースで区切られた名前と苗字が含まれていて、そこは連続した背景色にしたい場合、次のようにします。
list = <<-EOS
Charlie Brown\t21\tprogrammer
Bill Clinton\t43\tdoctor
Liz Taylor\t18\tstudent
EOS
# 不適切な例:
list.each_line do |line|
puts line.colco(:bg_green, :bg_green, :yellow, :blue_underline)
end
puts
# regexpオプションを使った例:
re = /^.*?(?=\t)|\S+/ # 最初のタブの前、または空白以外にマッチ
list.each_line do |line|
puts line.colco(:bg_green, :yellow, :blue_underline, regexp:re)
end
出力です。
IRC(Interactive Colors)
最後に上のライブラリを使って、ターミナル上でインタラクティブにカラーチェックができるirc.rbという簡単なツールを作りました。Term-ANSIColorライブラリを使っています。
ruby irc.rb
で立上げると、welcomeメッセージに続きirbのようにpromptモードになります。red on_greenなどの色属性をタイプすると、その属性に制御された文字列が表示されます。
文字列をリセットしたい場合は、’=’に続いて文字列をタイプします。入力できる属性はhelp(h, colors, attrs)で確認できます。終了はexit(q, quit, bye)です。
使ってくれる人がいたらうれしいです。
(追記:2010-11-8) rainbow commandを追加しました。GNU Readlineに対応しました。
(追記:2010-11-12) WirbleとCodeRayライブラリの記載を追加しました。 (追記:2011-7-20) Paintライブラリの記載を追加しました。 (追記:2014-7-23) Colcolrライブラリの記載を追加しました。
blog comments powered by Disqus