Module: Howzit::Color

Defined in:
lib/howzit/colors.rb

Overview

Terminal output color functions.

Constant Summary collapse

ESCAPE_REGEX =

Regexp to match excape sequences

/(?<=\[)(?:(?:(?:[349]|10)[0-9]|[0-9])?;?)+(?=m)/.freeze
ATTRIBUTES =

All available color names. Available as methods and string extensions.

Examples:

Use a color as a method. Color reset will be added to end of string.

Color.yellow('This text is yellow') => "\e[33mThis text is yellow\e[0m"

Use a color as a string extension. Color reset added automatically.

'This text is green'.green => "\e[1;32mThis text is green\e[0m"

Send a text string as a color

Color.send('red') => "\e[31m"
[
  [:clear,               0], # String#clear is already used to empty string in Ruby 1.9
  [:reset,               0], # synonym for :clear
  [:bold,                1],
  [:dark,                2],
  [:italic,              3], # not widely implemented
  [:underline,           4],
  [:underscore,          4], # synonym for :underline
  [:blink,               5],
  [:rapid_blink,         6], # not widely implemented
  [:negative,            7], # no reverse because of String#reverse
  [:concealed,           8],
  [:strikethrough,       9], # not widely implemented
  [:strike,              9], # not widely implemented
  [:black,              30],
  [:red,                31],
  [:green,              32],
  [:yellow,             33],
  [:blue,               34],
  [:magenta,            35],
  [:purple,             35],
  [:cyan,               36],
  [:white,              37],
  [:bgblack,            40],
  [:bgred,              41],
  [:bggreen,            42],
  [:bgyellow,           43],
  [:bgblue,             44],
  [:bgmagenta,          45],
  [:bgpurple,           45],
  [:bgcyan,             46],
  [:bgwhite,            47],
  [:boldblack,          90],
  [:boldred,            91],
  [:boldgreen,          92],
  [:boldyellow,         93],
  [:boldblue,           94],
  [:boldmagenta,        95],
  [:boldpurple,         95],
  [:boldcyan,           96],
  [:boldwhite,          97],
  [:boldbgblack,       100],
  [:boldbgred,         101],
  [:boldbggreen,       102],
  [:boldbgyellow,      103],
  [:boldbgblue,        104],
  [:boldbgmagenta,     105],
  [:boldbgpurple,      105],
  [:boldbgcyan,        106],
  [:boldbgwhite,       107],
  [:softpurple,  '0;35;40'],
  [:hotpants,    '7;34;40'],
  [:knightrider, '7;30;40'],
  [:flamingo,    '7;31;47'],
  [:yeller,      '1;37;43'],
  [:whiteboard,  '1;30;47'],
  [:chalkboard,  '1;37;40'],
  [:led,         '0;32;40'],
  [:redacted,    '0;30;40'],
  [:alert,       '1;31;43'],
  [:error,       '1;37;41'],
  [:default, '0;39']
].map(&:freeze).freeze
ATTRIBUTE_NAMES =

Array of attribute keys only

ATTRIBUTES.transpose.first
COLORED_REGEXP =

Regular expression that is used to scan for ANSI-sequences while uncoloring strings.

/\e\[(?:(?:[349]|10)[0-7]|[0-9])?m/.freeze

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Class Attribute Details

.coloringObject

Enables colored output

Examples:

Turn color on or off based on TTY

Howzit::Color.coloring = STDOUT.isatty


206
207
208
# File 'lib/howzit/colors.rb', line 206

def coloring
  @coloring ||= true
end

Class Method Details

.coloring?Boolean

Returns true if the coloring function of this module is switched on, false otherwise.



195
196
197
# File 'lib/howzit/colors.rb', line 195

def coloring?
  @coloring
end

.configured_colorsObject

Merge config file colors into attributes



237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/howzit/colors.rb', line 237

def configured_colors
  color_file = File.join(File.expand_path(CONFIG_DIR), COLOR_FILE)
  if File.exist?(color_file)
    colors = YAML.load(Util.read_file(color_file))
    return ATTRIBUTES unless !colors.nil? && colors.is_a?(Hash)

    attrs = ATTRIBUTES.to_h
    attrs = attrs.merge(colors.symbolize_keys)
    new_colors = {}
    attrs.each { |k, v| new_colors[k] = translate_rgb(v) }
    new_colors.to_a
  else
    ATTRIBUTES
  end
end

.rgb(hex) ⇒ String

Generate escape codes for hex colors



223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/howzit/colors.rb', line 223

def rgb(hex)
  is_bg = hex.match(/^bg?#/) ? true : false
  hex_string = hex.sub(/^([fb]g?)?#/, '')

  parts = hex_string.match(/(?<r>..)(?<g>..)(?<b>..)/)
  t = []
  %w[r g b].each do |e|
    t << parts[e].hex
  end

  "\e[#{is_bg ? '48' : '38'};2;#{t.join(';')}"
end

.template(input) ⇒ String

Convert a template string to a colored string. Colors are specified with single letters inside curly braces. Uppercase changes background color.

w: white, k: black, g: green, l: blue, y: yellow, c: cyan, m: magenta, r: red, b: bold, u: underline, i: italic, x: reset (remove background, color, emphasis)

Examples:

Convert a templated string

Color.template('{Rwb}Warning:{x} {w}you look a little {g}ill{x}')


272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
# File 'lib/howzit/colors.rb', line 272

def template(input)
  input = input.join(' ') if input.is_a? Array
  fmt = input.gsub(/%/, '%%')
  fmt = fmt.gsub(/(?<!\\u|\$|\\\\)\{(\w+)\}/i) do
    m = Regexp.last_match(1)
    if m =~ /^[wkglycmrWKGLYCMRdbuix]+$/
      m.split('').map { |c| "%<#{c}>s" }.join('')
    else
      Regexp.last_match(0)
    end
  end

  # Build colors hash from configured_colors, generating escape codes directly
  color_map = configured_colors.to_h
  colors = if coloring?
             {
               w: "\e[#{color_map[:white]}m",
               k: "\e[#{color_map[:black]}m",
               g: "\e[#{color_map[:green]}m",
               l: "\e[#{color_map[:blue]}m",
               y: "\e[#{color_map[:yellow]}m",
               c: "\e[#{color_map[:cyan]}m",
               m: "\e[#{color_map[:magenta]}m",
               r: "\e[#{color_map[:red]}m",
               W: "\e[#{color_map[:bgwhite]}m",
               K: "\e[#{color_map[:bgblack]}m",
               G: "\e[#{color_map[:bggreen]}m",
               L: "\e[#{color_map[:bgblue]}m",
               Y: "\e[#{color_map[:bgyellow]}m",
               C: "\e[#{color_map[:bgcyan]}m",
               M: "\e[#{color_map[:bgmagenta]}m",
               R: "\e[#{color_map[:bgred]}m",
               d: "\e[#{color_map[:dark]}m",
               b: "\e[#{color_map[:bold]}m",
               u: "\e[#{color_map[:underline]}m",
               i: "\e[#{color_map[:italic]}m",
               x: "\e[#{color_map[:reset]}m"
             }
           else
             # When coloring is disabled, return empty strings
             {
               w: '', k: '', g: '', l: '', y: '', c: '', m: '', r: '',
               W: '', K: '', G: '', L: '', Y: '', C: '', M: '', R: '',
               d: '', b: '', u: '', i: '', x: ''
             }
           end

  result = fmt.empty? ? input : format(fmt, colors)
  # Unescape braces and dollar signs that were escaped to prevent color code interpretation
  result.gsub(/\\\{/, '{').gsub(/\\\}/, '}').gsub(/\\\$/, '$')
end

.translate_rgb(code) ⇒ Object



210
211
212
213
214
# File 'lib/howzit/colors.rb', line 210

def translate_rgb(code)
  return code if code.to_s !~ /#[A-Z0-9]{3,6}/i

  rgb(code)
end

Instance Method Details

#attributesObject

Returns an array of all Howzit::Color attributes as symbols.



412
413
414
# File 'lib/howzit/colors.rb', line 412

def attributes
  ATTRIBUTE_NAMES
end

#rgb(hex) ⇒ String

Generate escape codes for hex colors



381
382
383
384
385
386
387
388
389
390
391
# File 'lib/howzit/colors.rb', line 381

def rgb(hex)
  is_bg = hex.match(/^bg?#/) ? true : false
  hex_string = hex.sub(/^([fb]g?)?#/, '')

  parts = hex_string.match(/(?<r>..)(?<g>..)(?<b>..)/)
  t = []
  %w[r g b].each do |e|
    t << parts[e].hex
  end
  "\e[#{is_bg ? '48' : '38'};2;#{t.join(';')}m"
end

#support?(feature) ⇒ Boolean

Returns true if Howzit::Color supports the feature.

The feature :clear, that is mixing the clear color attribute into String, is only supported on ruby implementations, that do not already implement the String#clear method. It’s better to use the reset color attribute instead.



93
94
95
96
97
98
# File 'lib/howzit/colors.rb', line 93

def support?(feature)
  case feature
  when :clear
    !String.instance_methods(false).map(&:to_sym).include?(:clear)
  end
end

#uncolor(string = nil) ⇒ Object

Returns an uncolored version of the string, that is all ANSI-sequences are stripped from the string.



399
400
401
402
403
404
405
406
407
408
409
# File 'lib/howzit/colors.rb', line 399

def uncolor(string = nil) # :yields:
  if block_given?
    yield.to_str.gsub(COLORED_REGEXP, '')
  elsif string.respond_to?(:to_str)
    string.to_str.gsub(COLORED_REGEXP, '')
  elsif respond_to?(:to_str)
    to_str.gsub(COLORED_REGEXP, '')
  else
    ''
  end
end