hp12c このページをアンテナに追加 RSSフィード Twitter

2010-11-07

RubyANSIカラーシーケンスを学ぼう!

ブログを下記に移転しました。デザイン変更により移転先では記事が一層読みやすくなっていますので、よろしければ移動をお願い致します。

RubyでANSIカラーシーケンスを学ぼう! : melborne.github.com


----------------------------------------


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

Wirbleirb(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}")

f:id:keyesberry:20101112083741p:image

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

f:id:keyesberry:20101112083742p:image


ファイルから読込んでHTML出力するようなこともできます

puts CodeRay.scan_file("code.rb").div(:line_numbers => :table)

f:id:keyesberry:20101112083743p:image


素晴らしいですね!

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"

f:id:keyesberry:20110721203131p:image


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モジュールの内部は

メタプログラミングばりばりです:)


IRC(Interactive Colors)

最後に上のライブラリを使って

ターミナル上でインタラクティブにカラーチェックができる

irc.rbという簡単なツールを作りました

Term-ANSIColorライブラリを使っています


ruby irc.rbで立上げると

welcomeメッセージに続きirbのようにpromptモードになります

red on_greenなどの色属性をタイプすると

その属性に制御された文字列が表示されます

f:id:keyesberry:20101107152015p:image


文字列をリセットしたい場合は

'='に続いて文字列をタイプします

入力できる属性はhelp(h, colors, attrs)で確認できます

終了はexit(q, quit, bye)です

f:id:keyesberry:20101107152016p:image


使ってくれる人がいたらうれしいです


(追記:2010-11-8) rainbow commandを追加しました。GNU Readlineに対応しました。

f:id:keyesberry:20101108075820p:image


(追記:2010-11-12) WirbleとCodeRayライブラリの記載を追加しました。

(追記:2011-7-20) Paintライブラリの記載を追加しました。


melborne/irc - GitHub

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証