Ruby を使っているとき、Linux でのターミナル表示の処理の仕方をすぐ忘れてしまうので、簡単なライブラリを作った。
require_relative 'es' res = ES.reset print ES.clear puts ES.color(:style, :blink) + ES.color(:green, :bright) + "blink" + res puts ES.color(:style, :underline) + ES.color(:blue, :background) + "undlerline" print ES.down print res 16.times do |i| puts (0...16).map {|j| ES.color(:ex_b, n = 16 * i + j) + "%02X" % n + res + " "}.join end print res + ES.push print ES.cursor_r(5, 2) print ES.color(:cyan) + ES.color(:style, :reverse) + "cursor move" + ES.pop
凡例。
ES.color(:blue)
で文字色を青にする。背景はES.color(:red, :backgroud)
という風に指定する。- 拡張文字色(256色)は
ES.color(:ex_c, n)
、拡張背景色はES.color(:ex_b, n)
。 - 文字の点滅、下線、反転、太字は
ES.color(:style, :blink)
,ES.color(:style, :underline)
,ES.color(:style, :reverse)
,ES.color(:style, :bold)
のように。 - 指定を解除するには
ES.reset
とする。
- 画面クリアは
ES.clear
、行頭へのカーソル移動はES.top
。 - カーソルの一行上、一行下はそれぞれ
ES.up
,ES.down
。 - カーソルの絶対位置の指定は
ES.cursor(x, y)
(x, y は自然数)、相対的な移動はES.cursor_r(x, y)
(x, y は 0 や負数も許す)。 - 状態の保存は
ES.push
、復帰はES.pop
。
その他、ここのサイトの記述を取り入れております。
ライブラリのコード
es.rb
require 'io/console' module ES S = "\e[" Col = %i(black red green yellow blue magenta cyan white ex_c ex_b reset style code) Cmd1 = %i(CUU CUD CUF CUB CNL CPL CHA CHT CBT ECH DCH IL DL SU SD REP DSR) Do1 = %w(A B C D E F G I Z X P L M S T b n) Cmd2 = %i(DECSC DECRC RIS DECALN IND NEL HTS VTS PLD PLU RI DSC SOS ST) Do2 = %w(7 8 c #8 D E H J K L M P X /) def color(col, opt = nil) ch = Col.map.with_index {|c, i| [c, i + 30]}.to_h[col] case ch when 38 return S + "38;5;#{opt.to_i}m" when 39 return S + "48;5;#{opt.to_i}m" when 40 return S + "39;40m" when 41 n = case opt when :bold then 1 when :italic then 3 when :blink then 5 when :reverse then 7 when :blink_stop then 25 when :underline then 4 when :bold_stop then 22 when :underline_stop then 24 else opt end return S + "#{n}m" when 42 return S + "#{opt}m" end raise "Undefind color name: #{col}" unless ch m = case opt when :background then 10 when :bright then 60 when :bright_background then 70 else 0 end S + (ch + m).to_s + "m" end def csi(*args) cm = Cmd1.zip(Do1).to_h if (a = cm[args[0]]) S + args[1].to_s + a else case args[0] when :CUP then S + "#{args[1]};#{args[2]}H" when :ED then S + (args[1] ? args[1].to_s : "") + "J" when :EL then S + (args[1] ? args[1].to_s : "") + "K" when :TBC then S + (args[1] ? args[1].to_s : "") + "g" when :DECSTBM S + (args[1] ? "#{args[1]};#{args[2]}" : "") + "r" when :DECTCEM S + "?25" + args[1] else raise "#{args[0]} is undefined CSI." end end end def cmd(given) cm = Cmd2.zip(Do2).to_h[given] cm ? "\e" + cm : raise("#{given} is undefined command.") end def esc(str) "\e" + str end def clear() ES.csi(:ED, 2) + ES.csi(:CUP, 1, 1) end def down() ES.cmd(:NEL) end def up() ES.cmd(:RI) end def reset() S + "0m" end def top() ES.csi(:CHA, 1) end def home() ES.cursor(0, 0) end def push() ES.cmd(:DECSC) end def pop() ES.cmd(:DECRC) end # カーソルの移動 def cursor(x, y = nil) y ? ES.csi(:CUP, y, x) : ES.csi(:CHA, x) end # 相対的なカーソルの移動 def cursor_r(x, y) st = "" st += if x > 0 ES.csi(:CUF, x) elsif x < 0 ES.csi(:CUB, -x) else "" end st += if y > 0 ES.csi(:CUD, y) elsif y < 0 ES.csi(:CUU, -y) else "" end end def console_size [`tput cols`, `tput lines`].map(&:to_i) end # スクロールする行の範囲を指定する(引数がなければ範囲の解除) def scroll(rn = nil) return case rn when Range then ES.csi(:DECSTBM, rn.first, rn.max) when 0 then ES.csi(:DECSTBM) else ES.push + ES.csi(:DECSTBM) + ES.pop end end def cursor_position puts "\x1B[6n" res = "" STDIN.raw do |io| until (c = io.getc) == 'R' res << c if c end end m = /(\d+);(\d+)/.match(res) [m[2], m[1]].map(&:to_i) end # 下にn行開けてカーソルを始めの位置にもってくる # (既に下にn行以上開いていたら何もしない) def safe_scroll(n) return "" if n <= 0 str = "" y = ES.cursor_position[1] if (h = ES.console_size[1]) < y + n str = ES.scroll_up(n - 1) y = h - n end str + ES.cursor(1, y) end def scroll_up(n) "\n" * n end def clear_below() ES.csi(:ED) end module_function :color, :csi, :cmd, :clear, :down, :up, :reset, :top, :cursor, :cursor_r, :home, :push, :pop, :esc, :console_size, :scroll, :scroll_up, :cursor_position, :clear_below, :safe_scroll end
使用例1
これのコードは以下です。
ANSIエスケープシーケンスで遊ぶ · GitHub
使用例2
require_relative 'es' y = ES.console_size[1] print ES.safe_scroll(1) print ES.push print ES.scroll(1..y - 1) + ES.cursor(1, y) + ES.color(:green, :bright) + "FIXED" print ES.reset + ES.pop 10.times do |i| puts i sleep(0.1) end print ES.scroll + ES.clear_below + ES.reset
※参考
ANSIエスケープコード - コンソール制御 - 碧色工房
VT100のエスケープシーケンス - BK class