Module: SimpleColor::Colorer
- Extended by:
- Colorer
- Included in:
- Colorer
- Defined in:
- lib/simplecolor/colorer.rb,
lib/simplecolor/colors.rb
Overview
The Colorer module handle all color outputs
Constant Summary collapse
- CLEAR =
Regular expression to scan if there is a clear ANSI effect
"\e\[0m"
- CLEAR_REGEXP =
/\e\[0m/
- ANSICOLOR_REGEXP =
Regular expression that is used to scan for ANSI-sequences
/\e\[(?:[\d;]*)m/
- COLOR_REGEXP =
/#{ANSICOLOR_REGEXP}+/
- COLORMATCH_REGEXP =
/#{ANSICOLOR_REGEXP}*/
- ANSI_COLORS =
Basic colors (often, the color differs when using the bright effect) Final color will be 30 + value for foreground and 40 + value for background 90+value for intense foreground, 100+value for intense background
{ :black => 0, :red => 1, :green => 2, :yellow => 3, :blue => 4, :magenta => 5, :cyan => 6, :white => 7, :default => 9, }
- ANSI_COLORS_FOREGROUND =
Hash[ANSI_COLORS.map {|k,v| [k, 30+v]
- ANSI_COLORS_BACKGROUND =
- ANSI_COLORS_INTENSE_FOREGROUND =
aixterm (not standard)
- ANSI_COLORS_INTENSE_BACKGROUND =
- ANSI_EFFECTS =
{ :reset => 0, :nothing => 0, # usually supported :clear => 0, :normal => 0, # usually supported :bright => 1, :bold => 1, # usually supported :faint => 2, :italic => 3, :underline => 4, # usually supported :blink => 5, :slow_blink => 5, :rapid_blink => 6, :inverse => 7, :reverse => 7, :swap => 7, # usually supported :conceal => 8, :hide => 8, :crossed => 9, :crossed_out => 9, :default_font => 10, :font0 => 10, :font1 => 11, :font2 => 12, :font3 => 13, :font4 => 14, :font5 => 15, :font6 => 16, :font7 => 17, :font8 => 18, :font9 => 19, :fraktur => 20, :bright_off => 21, :bold_off => 21, :double_underline => 21, :clean => 22, :regular => 22, #neither bold or faint :italic_off => 23, :fraktur_off => 23, :underline_off => 24, :blink_off => 25, :inverse_off => 26, :positive => 26, :conceal_off => 27, :show => 27, :reveal => 27, :crossed_off => 29, :crossed_out_off => 29, :frame => 51, :framed => 51, :encircle => 52, :encircled => 52, :overline => 53, :overlined => 53, :frame_off => 54, :encircle_off => 54, :framed_off => 54, :encircled_off => 54, :overline_off => 55, :overlined_off => 55, }
- COLORS =
attributes that can be specified to the color method
[ANSI_EFFECTS,ANSI_COLORS_FOREGROUND, ANSI_COLORS_BACKGROUND, ANSI_COLORS_INTENSE_FOREGROUND, ANSI_COLORS_INTENSE_BACKGROUND].inject({}){ |a,b| a.merge(b) }
Instance Method Summary collapse
-
#color_attributes(*args, mode: :text, color_mode: :truecolor, abbreviations: {}, rgb_class: RGB, **rgb_parse_opts) ⇒ Object
A color name can be: - an array of rgb data (truecolor) (start with :on to specify background) - a String: rgb:10-20-30 (foreground truecolor) on_rgb:10-20-30 (background truecolor) t:rgb...
- #colored?(s, **kwds) ⇒ Boolean
-
#colorer(s, *attributes, global_color: :after, local_color: :before, **kwds) ⇒ Object
Returns a colored version of the string (modified in place), according to attributes.
- #colors ⇒ Object
- #regexp(type = :color, mode: :text, **_rest) ⇒ Object
-
#uncolorer(s, **kwds) ⇒ Object
Returns an uncolored version of the string, that is all ANSI-sequences are stripped from the string.
Instance Method Details
#color_attributes(*args, mode: :text, color_mode: :truecolor, abbreviations: {}, rgb_class: RGB, **rgb_parse_opts) ⇒ Object
A color name can be:
- an array of rgb data (truecolor) (start with :on to specify background)
- a String: rgb:10-20-30 (foreground truecolor) on_rgb:10-20-30 (background truecolor) t:rgb... (don't fallback to lower color mode) (on_)rgb256:r:g:b (force 256 color mode) (on_)rgb256:grey3 (256 grey scale) (on_)rgb256:5 (direct color code) (t:)(on_)#AABBCC (hex code, truecolor) (t:)(on_)#ABC (reduced hex code, truecolor) (t:)(on_)name (X11 color name, truecolor) A color attribute can be:
- a symbol (looked in at COLORS)
- an integer (direct color code)
- a color escape sequence
- a String
30 31 32 33 34 35 36 37 38 39 40 41 42 43 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 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/simplecolor/colorer.rb', line 30 def color_attributes(*args, mode: :text, color_mode: :truecolor, abbreviations: {}, rgb_class: RGB, **rgb_parse_opts) return "" if mode==:disabled or mode==false #early abort abbreviations={} if abbreviations.nil? colors=self.colors accu=[] buffer="" flush=lambda {r=accu.join(";"); accu=[]; r.empty? || r="\e["+r+"m"; buffer<<r} #Note: "\e"="\x1b" parse_rgb=lambda do |s| symbol=s.is_a?(Symbol) truecol=/(?<truecol>(?:truecolor|true|t):)?/ on=/(?<on>on_)?/ # the to_s is because of a bug in truffleruby where # `s.match(...) do end` does not work with symbols s.to_s.match(/\A#{truecol}#{on}(?<rest>.*)\z/) do |m| tcol=m[:truecol]; on=m[:on]; string=m[:rest] lcolormode = tcol ? :truecolor : color_mode string=string.to_sym if symbol accu << rgb_class.parse(string, **rgb_parse_opts).ansi(background: !!on, convert: lcolormode) end end parse=lambda do |*cols, abbrevs: abbreviations| cols.each do |col| if (scol=abbrevs[col]) # Array are special, in a non abbreviation they mean an rgb mode but for abbreviations it just combine several color attributes parse.call(*scol, abbrevs: {}) #we erase abbreviations so :red = :red do not get an infinite loop next end case col when Proc scol=col.call(buffer, accu) parse.call(*scol) when Symbol if colors.key?(col) #ANSI effects accu << colors[col] else parse_rgb.call(col) end when Integer #direct ansi code accu << col.to_s when Array background=false if col.first == :on background=true; col.shift end accu << rgb_class.new(col).ansi(convert: color_mode, background: background) when rgb_class accu << col.ansi(convert: color_mode) when COLOR_REGEXP flush.call buffer<<col when String parse_rgb.call(col) when nil # skip else raise WrongColor.new(col) end end end parse.call(*args) flush.call case mode when :shell "%{"+buffer+"%}" when :disabled "" # already handled above when :text, :enabled, true buffer else raise WrongParameter.new(mode) end end |
#colored?(s, **kwds) ⇒ Boolean
226 227 228 |
# File 'lib/simplecolor/colorer.rb', line 226 def colored?(s, **kwds) !! (s =~ regexp(:color, **kwds)) end |
#colorer(s, *attributes, global_color: :after, local_color: :before, **kwds) ⇒ Object
Returns a colored version of the string (modified in place), according to attributes
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/simplecolor/colorer.rb', line 139 def colorer(s, *attributes, global_color: :after, local_color: :before, **kwds) if s.nil? color_attributes(*attributes,**kwds) elsif s.empty? # We don't color an empty string; use nil to get color attributes s else match_reg=regexp(:match, **kwds) color_reg=regexp(:color, **kwds) clear_reg=regexp(:clear, **kwds) colors=color_attributes(*attributes,**kwds) clear=color_attributes(:clear,**kwds) split=SimpleColor.color_strings(s, color_regexp: color_reg) sp, i=split.each_with_index.find do |sp, i| i>=1 && sp.match(/#{color_reg}/) end global=true unless sp and (i<split.length-1 || !sp.match(/#{clear_reg}$/)) if global case global_color when :keep matched = s.match(match_reg) s.insert(0, colors) unless matched.end(0)>0 when :before s.insert(0, colors) when :after # we need to insert the ANSI sequences after existing ones so that # the new colors have precedence matched = s.match(match_reg) #since this has a '*' it matches at the beginning s.insert(matched.end(0), colors) end else pos=0 split.each_with_index do |sp,i| if sp.match(/#{clear_reg}$/) #don't reinsert ourselves at end unless i==split.length-1 pos+=sp.length s.insert(pos, colors) pos+=colors.length end elsif sp.match(/#{color_reg}/) case local_color when :keep if i!=0 # we need to clear ourselves s.insert(pos, clear) pos+=clear.length end pos+=sp.length when :before #we already inserted ourselves except if we are on the #first block if i==0 s.insert(pos, colors) pos+=colors.length end pos+=sp.length when :after pos+=sp.length s.insert(pos, colors) pos+=colors.length end else if i==0 s.insert(pos, colors) pos+=colors.length end pos+=sp.length end end end s.concat(clear) unless s =~ /#{clear_reg}$/ or colors.empty? s end end |
#colors ⇒ Object
9 10 11 |
# File 'lib/simplecolor/colorer.rb', line 9 def colors COLORS end |
#regexp(type = :color, mode: :text, **_rest) ⇒ Object
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/simplecolor/colorer.rb', line 106 def regexp(type=:color, mode: :text, **_rest) case type when :color if mode == :shell m=regexp(:ansi, mode: mode) /#{m}+/ else COLOR_REGEXP end when :match if mode == :shell m=regexp(:ansi, mode: mode) /#{m}*/ else COLORMATCH_REGEXP end when :ansi if mode == :shell /%{#{ANSICOLOR_REGEXP}%}/ else ANSICOLOR_REGEXP end when :clear if mode == :shell /%{#{CLEAR_REGEXP}%}/ else CLEAR_REGEXP end end end |
#uncolorer(s, **kwds) ⇒ Object
Returns an uncolored version of the string, that is all ANSI-sequences are stripped from the string. @see: colorer
220 221 222 223 224 |
# File 'lib/simplecolor/colorer.rb', line 220 def uncolorer(s, **kwds) s.to_str.gsub!(regexp(:color, **kwds), '') || s.to_str rescue ArgumentError #rescue from "invalid byte sequence in UTF-8" s.to_str end |