Module: Rufio::AnsiLineParser
- Defined in:
- lib/rufio/ansi_line_parser.rb
Overview
ANSI エスケープコード付きの行をトークン列に分解するモジュール。bat –color=always の出力をシンタックスハイライト表示するために使用する。
Constant Summary collapse
- ANSI_SGR_PATTERN =
SGR (Select Graphic Rendition) ANSI エスケープシーケンスにマッチするパターン
/\e\[[0-9;]*m/
Class Method Summary collapse
-
.display_width(tokens) ⇒ Integer
トークン配列の表示幅(ANSI コードを除く)を計算する。.
-
.parse(line) ⇒ Array<Hash>
ANSI 付き行を String, fg: String|nil のトークン配列に分解する。.
-
.wrap(tokens, max_width) ⇒ Array<Array<Hash>>
トークン配列を max_width で折り返し、行ごとのトークン配列の配列を返す。 全角文字(日本語等)は幅2として扱う。.
Class Method Details
.display_width(tokens) ⇒ Integer
トークン配列の表示幅(ANSI コードを除く)を計算する。
34 35 36 |
# File 'lib/rufio/ansi_line_parser.rb', line 34 def self.display_width(tokens) tokens.sum { |t| TextUtils.display_width(t[:text]) } end |
.parse(line) ⇒ Array<Hash>
ANSI 付き行を String, fg: String|nil のトークン配列に分解する。
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# File 'lib/rufio/ansi_line_parser.rb', line 14 def self.parse(line) tokens = [] current_fg = nil # ANSI SGR シーケンス、非エスケープ文字列、孤立エスケープの順にスキャン line.scan(/#{ANSI_SGR_PATTERN}|[^\e]+|\e(?!\[)/) do |part| if part.start_with?("\e[") current_fg = apply_ansi_sequence(current_fg, part) else tokens << { text: part, fg: current_fg } end end tokens end |
.wrap(tokens, max_width) ⇒ Array<Array<Hash>>
トークン配列を max_width で折り返し、行ごとのトークン配列の配列を返す。全角文字(日本語等)は幅2として扱う。
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/rufio/ansi_line_parser.rb', line 44 def self.wrap(tokens, max_width) return [] if tokens.empty? || max_width <= 0 lines = [] current_line = [] current_width = 0 tokens.each do |token| fg = token[:fg] current_text = String.new token[:text].each_char do |char| char_w = TextUtils.char_width(char) if current_width + char_w > max_width # 折り返し: 現在のテキストをトークンとして確定 current_line << { text: current_text, fg: fg } unless current_text.empty? lines << current_line current_line = [] current_text = String.new current_width = 0 end current_text << char current_width += char_w end current_line << { text: current_text, fg: fg } unless current_text.empty? end lines << current_line unless current_line.empty? lines end |