module Ansi256

Constants

CODE
EMPTY_TRIPLE
MULTI_PATTERN
NAMED_COLORS
PATTERN
VERSION

Public Class Methods

bg(code, str = nil) click to toggle source
# File lib/ansi256.rb, line 22
def bg code, str = nil
  if str
    wrap str, Ansi256.bg(code)
  elsif NAMED_COLORS.include?(code)
    "\e[#{CODE[code] + 10}m"
  elsif code.is_a?(Fixnum) && (0..255).include?(code)
    "\e[48;5;#{code}m"
  elsif ansirgb = ansicode_for_rgb(code)
    "\e[48;5;#{ansirgb}m"
  else
    raise ArgumentError, "Invalid color code: #{code}"
  end
end
enabled=(bool) click to toggle source
# File lib/ansi256/mixin.rb, line 3
def enabled= bool
  if bool
    String.class_eval do
      [:fg, :bg, :plain].each do |name|
        undef_method(name) if method_defined?(name)
      end

      def fg code
        Ansi256.fg(code, self)
      end

      def bg code
        Ansi256.bg(code, self)
      end

      def plain
        Ansi256.plain self
      end

      Ansi256::CODE.each do |name, code|
        undef_method(name) if method_defined?(name)
        define_method name do
          Ansi256.send name, self
        end
      end
    end

    Ansi256.class_eval do
      class << self
        undef_method(:enabled?) if method_defined?(:enabled?)
        def enabled?
          true
        end
      end
    end
  else
    String.class_eval do
      [:fg, :bg, :plain].each do |name|
        undef_method(name) if method_defined?(name)
      end

      def fg code
        self
      end
      alias bg fg

      def plain
        Ansi256.plain self
      end

      Ansi256::CODE.each do |name, code|
        undef_method(name) if method_defined?(name)
        define_method name do
          self
        end
      end
    end

    Ansi256.class_eval do
      class << self
        undef_method(:enabled?) if method_defined?(:enabled?)
        def enabled?
          false
        end
      end
    end
  end

  bool
end
enabled?() click to toggle source
# File lib/ansi256/mixin.rb, line 33
def enabled?
  true
end
fg(code, str = nil) click to toggle source
# File lib/ansi256.rb, line 8
def fg code, str = nil
  if str
    wrap str, Ansi256.fg(code)
  elsif NAMED_COLORS.include?(code)
    "\e[#{CODE[code]}m"
  elsif code.is_a?(Fixnum) && (0..255).include?(code)
    "\e[38;5;#{code}m"
  elsif ansirgb = ansicode_for_rgb(code)
    "\e[38;5;#{ansirgb}m"
  else
    raise ArgumentError, "Invalid color code: #{code}"
  end
end
plain(str) click to toggle source
# File lib/ansi256.rb, line 46
def plain str
  str.gsub(PATTERN, '')
end

Private Class Methods

ansicode_for_rgb(rgb) click to toggle source
# File lib/ansi256.rb, line 63
def ansicode_for_rgb rgb
  return unless rgb.is_a?(String) &&
                rgb =~ /^#?([0-9a-f]+)$/i
  rgb = $1

  case rgb.length
  when 2
    m = (256 - 231) * rgb.to_i(16) / 256
    return m == 0 ? 16 : (231 + m)
  when 3
    r, g, b = rgb.each_char.map { |e| (e * 2).to_i(16) }
  when 6
    r, g, b = rgb.each_char.each_slice(2).map { |e| e.join.to_i(16) }
  else
    return
  end

  r, g, b = [r, g, b].map { |e| 6 * e / 256 }
  r * 36 + g * 6 + b + 16
end
ansify(prev, curr) click to toggle source
# File lib/ansi256.rb, line 55
def ansify prev, curr
  nums = []
  nums << curr[0] if prev[0] != curr[0]
  nums << curr[1] if prev[1] != curr[1]
  nums.concat curr[2].to_a if prev[2] != curr[2]
  "\e[#{nums.compact.join ';'}m"
end
wrap(str, color) click to toggle source
# File lib/ansi256.rb, line 84
def wrap str, color
  current = [nil, nil, Set.new]

  (color + str.gsub(PATTERN) { |m|
    if m =~ /\e\[[^m]*\b0m/
      m + color
    else
      m
    end
  } << reset).gsub(MULTI_PATTERN) { |ansi|
    prev = current.dup
    prev[2] = prev[2].dup
    codes = ansi.scan(/\d+/).map(&:to_i)

    idx = -1
    while (idx += 1) < codes.length
      case code = codes[idx]
      when 38
        current[0] = codes[idx, 3].join ';'
        idx += 2 # 38;5;11
      when 48
        current[1] = codes[idx, 3].join ';'
        idx += 2 # 38;5;11
      when 30..37
        current[0] = codes[idx]
      when 40..47
        current[1] = codes[idx]
      when 0
        current[0] = current[1] = nil
        current[2].clear
      else
        current[2] << code
      end
    end

    if current == prev
      ''
    elsif current == EMPTY_TRIPLE
      reset
    else
      if (0..1).any? { |i| prev[i] && !current[i] } || current[2].proper_subset?(prev[2])
        prev = EMPTY_TRIPLE
        reset
      else
        ''
      end + ansify(prev, current)
    end
  }
end