Basic colors (often, the color differs when using the bright effect) Final color will be 30 + value for foreground and 40 + value for background
cache
cache
Terminal effects - most of them are not supported ;) See en.wikipedia.org/wiki/ANSI_escape_code
Important purpose
A list of color names, based on X11's rgb.txt Can be used with Paint.[] by passing a string containing the color name See Paint::Util::update_rgb_colors for generating
A list of color names for standard ansi colors, needed for 16/8 color fallback mode See en.wikipedia.org/wiki/ANSI_escape_code#Colors
A list of color names for standard bright ansi colors, needed for 16 color fallback mode See en.wikipedia.org/wiki/ANSI_escape_code#Colors
Hash for defining color/effect shortcuts See README for details
Takes a string and color options and colorizes the string See README.rdoc for details
# File lib/paint.rb, line 86 def [](string, *options) return string.to_s if mode.zero? || options.empty? if options.size == 1 && !options.first.respond_to?(:to_ary) options = options.first end cache[options] + string.to_s + NOTHING end
Sometimes, you only need the color Used by []
# File lib/paint.rb, line 98 def color(*options) return '' if mode.zero? || options.empty? mix = [] color_seen = false colors = ANSI_COLORS_FOREGROUND options.each{ |option| case option when Symbol if color = colors[option] mix << color color_seen = :set elsif ANSI_EFFECTS.key?(option) mix << effect(option) elsif option == :random mix << random(color_seen) color_seen = :set else raise ArgumentError, "Unknown color or effect: #{ option }" end when Array if option.size == 3 && option.all?{ |n| n.is_a? Numeric } mix << rgb(*(option + [color_seen])) # 1.8 workaround color_seen = :set else raise ArgumentError, "Array argument must contain 3 numerals" end when ::String if option =~ /^#?(?:[0-9a-f]{3}){1,2}$/ mix << hex(option, color_seen) color_seen = :set else mix << rgb_name(option, color_seen) color_seen = :set end when Numeric integer = option.to_i color_seen = :set if (30..49).include?(integer) mix << integer when nil color_seen = :set else raise ArgumentError, "Invalid argument: #{ option.inspect }" end if color_seen == :set colors = ANSI_COLORS_BACKGROUND color_seen = true end } wrap(*mix) end
Paint::SomeModule –> Paint::SHORTCUTS
# File lib/paint/shortcuts.rb, line 13 def const_missing(mod_name) # get shortcuts shortcuts = SHORTCUTS[mod_name.to_s.gsub(/[A-Z]/,'_\0').downcase[1..-1].to_sym] || [] # create module class_eval "module #{mod_name}; end" mod = const_get(mod_name) eigen_mod = class << mod; self; end # 1.8 # define direct behaviour, class methods # mod.define_singleton_method :method_missing do |color_name, *args| eigen_mod.send:define_method, :method_missing do |color_name, *args| if color_code = shortcuts[color_name] if args.empty? then color_code else color_code + Array(args).join + NOTHING end else nil end end eigen_mod.send:define_method, :respond_to? do |color_name, *args| shortcuts.include?(color_name) || super(color_name, *args) end # define include behaviour, instance methods eigen_mod.send:define_method, :included do |_| shortcuts.each{ |color_name, color_code| define_method color_name do |*args| if args.empty? then color_code else color_code + Array(args).join + NOTHING end end } private(*shortcuts.keys) unless shortcuts.empty? end # include variations, defined in child modules mod.class_eval "module String; end" string = mod.const_get(:String) eigen_string = class << string; self; end # 1.8 eigen_string.send:define_method, :included do |_| shortcuts.each{ |color_name, color_code| define_method color_name do color_code + to_s + NOTHING end } end # OK, let's take it one level further ;) mod.class_eval "module Prefix; end" prefix_prefix = mod.const_get(:Prefix) eigen_prefix_prefix = class << prefix_prefix; self; end # 1.8 eigen_prefix_prefix.send:define_method, :const_missing do |prefix_name| class_eval "module #{prefix_name}; end" prefix = const_get(prefix_name) eigen_prefix = class << prefix; self; end # 1.8 eigen_prefix.send:define_method, :included do |_| define_method prefix_name.to_s.gsub(/[A-Z]/,'_\0').downcase[1..-1].to_sym do |color_name| shortcuts[color_name] && shortcuts[color_name] + to_s + NOTHING end end prefix end # :) mod end
Determine supported colors This is just a naive approach, based on some things I could test Please open issues if it does not work correctly for you
# File lib/paint/util.rb, line 38 def detect_mode if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ # windows if ENV['ANSICON'] 16 elsif ENV['ConEmuANSI'] == 'ON' 256 else 0 end else # case ENV['COLORTERM'] # when 'gnome-terminal' # 256 # else case ENV['TERM'] when /-256color$/, 'xterm' 256 when /-color$/, 'rxvt' 16 else # optimistic default 256 end # end end end
Creates the specified effect by looking it up in Paint::ANSI_EFFECTS
# File lib/paint.rb, line 212 def effect(effect_name) ANSI_EFFECTS[effect_name] end
Creates 256-compatible color from a html-like color string
# File lib/paint.rb, line 187 def hex(string, background = false) string.tr! '#','' rgb( *(if string.size == 6 # string.chars.each_cons(2).map{ |hex_color| hex_color.join.to_i(16) } [string[0,2].to_i(16), string[2,2].to_i(16), string[4,2].to_i(16)] else string.chars.map{ |hex_color_half| (hex_color_half*2).to_i(16) } end + [background]) # 1.8 workaround ) end
This variable influences the color code generation Currently supported values:
256 - 256 colors
16 - only ansi colors and bright effect
8 - only ansi colors
0 - no colorization!
# File lib/paint.rb, line 164 def mode() @mode ||= detect_mode end
# File lib/paint.rb, line 165 def mode=(val) cache.clear; @mode = val end
Tries to print all 256 colors
# File lib/paint/util.rb, line 12 def rainbow (0...256).each{ |color| print Paint[' ', 48, 5, color] # print empty bg color field } puts end
Creates a random ansi color
# File lib/paint.rb, line 207 def random(background = false) (background ? 40 : 30) + rand(8) end
Creates a 256-compatible color from rgb values
# File lib/paint.rb, line 178 def rgb(red, green, blue, background = false) if mode == 8 || mode == 16 "#{background ? 4 : 3}#{rgb_like_value(red, green, blue, mode == 16)}" else "#{background ? 48 : 38}#{rgb_value(red, green, blue)}" end end
Creates a 256-color from a name found in Paint::RGB_COLORS (based on rgb.txt)
# File lib/paint.rb, line 200 def rgb_name(color_name, background = false) if color_code = RGB_COLORS[color_name] rgb(*(color_code + [background] )) # 1.8 workaround end end
Creates simple ansi color by looking it up on Paint::ANSI_COLORS
# File lib/paint.rb, line 173 def simple(color_name, background = false) (background ? 40 : 30) + ANSI_COLORS[color_name] end
Removes any color and effect strings
# File lib/paint/util.rb, line 7 def unpaint(string) string.gsub(/\e\[(?:[0-9];?)+m/, '') end
Updates color names
# File lib/paint/util.rb, line 20 def update_rgb_colors(path = '/etc/X11/rgb.txt') if File.file?(path) Paint::RGB_COLORS.clear File.open(path, 'r') do |file| file.each_line{ |line| line.chomp =~ /(\d+)\s+(\d+)\s+(\d+)\s+(.*)$/ Paint::RGB_COLORS[$4] = [$1.to_i, $2.to_i, $3.to_i] if $4 } end else raise ArgumentError, "Not a valid file: #{path}" end end
Adds ansi sequence
# File lib/paint.rb, line 168 def wrap(*ansi_codes) "\0033[" + ansi_codes*";" + "m" end
# File lib/paint.rb, line 218 def cache @cache ||= Hash.new { |h, k| h[k] = color(*k) } end
# File lib/paint.rb, line 261 def distance(rgb1, rgb2) rgb1.zip(rgb2).inject(0){ |acc, (cur1, cur2)| acc + (cur1 - cur2)**2 } end
Returns ansi color matching an rgb value, without fore-/background information See mail.python.org/pipermail/python-list/2008-December/1150496.html
# File lib/paint.rb, line 247 def rgb_like_value(red, green, blue, use_bright = false) color_pool = RGB_COLORS_ANSI.values color_pool += RGB_COLORS_ANSI_BRIGHT.values if use_bright ansi_color_rgb = color_pool.min_by{ |col| distance([red, green, blue],col) } key_method = RUBY_VERSION < "1.9" ? :index : :key if ansi_color = RGB_COLORS_ANSI.send(key_method, ansi_color_rgb) ANSI_COLORS[ansi_color] else ansi_color = RGB_COLORS_ANSI_BRIGHT.send(key_method, ansi_color_rgb) "#{ANSI_COLORS[ansi_color]};1" end end
Returns nearest supported 256-color an rgb value, without fore-/background information Inspired by the rainbow gem
# File lib/paint.rb, line 224 def rgb_value(red, green, blue) gray_possible = true sep = 42.5 while gray_possible if red < sep || green < sep || blue < sep gray = red < sep && green < sep && blue < sep gray_possible = false end sep += 42.5 end if gray ";5;#{ 232 + ((red.to_f + green.to_f + blue.to_f)/33).round }" else # rgb ";5;#{ [16, *[red, green, blue].zip([36, 6, 1]).map{ |color, mod| (6 * (color.to_f / 256)).to_i * mod }].inject(:+) }" end end