Permalink
Browse files

Better handling of unknown colours

In case the user doesn't configure the ANSI palette properly, the colours
are unknown to tco and they're cannot be used for matching. However, they
should still be accessible through the '@x' notation.

This functionality was broken due to improper handling of them. This
commit fixes that. As a result, it also resolves #3.
  • Loading branch information...
pazdera committed Apr 24, 2014
1 parent ce942ee commit a3bff189762bef7784fa1452bde792fb88df6ed7
Showing with 85 additions and 43 deletions.
  1. +1 −1 bin/tco
  2. +4 −5 lib/tco.rb
  3. +43 −18 lib/tco/colouring.rb
  4. +37 −19 lib/tco/palette.rb
View
@@ -120,7 +120,7 @@ begin
end
if ARGV.length > 0
input = ARGV[0].gsub /\\[nt]/, "\\n" => "\n", "\\t" => "\t"
input = ARGV[0].gsub(/\\[nt]/, "\\n" => "\n", "\\t" => "\t")
else
input = ""
while input_line = gets
View
@@ -120,11 +120,10 @@ def self.display_palette
black = Tco::Colour.new([0,0,0])
white = Tco::Colour.new([255,255,255])
font_colour = if (colours[c] - black).abs > (colours[c] - white).abs
black
else
white
end
font_colour = black
if colours[c].is_a?(Colour) && (colours[c] - black).abs < (colours[c] - white).abs
font_colour = white
end
square_styles.push [c, font_colour, colours[c]]
c += 1
View
@@ -59,8 +59,8 @@ def initialize(configuration)
def decorate(string, (fg, bg, bright, underline))
return string unless STDOUT.isatty || @output_type == :raw
fg = to_colour fg
bg = to_colour bg
fg = get_colour_instance fg
bg = get_colour_instance bg
output = []
lines = string.lines.map(&:chomp)
@@ -116,12 +116,20 @@ def e(seq)
def colour_ansi(string, fg=nil, bg=nil)
unless fg == nil
colour_id = @palette.match_colour(fg)
colour_id = if fg.is_a? Unknown
fg.id
else
@palette.match_colour(fg)
end
string = e(colour_id + 30) + string
end
unless bg == nil
colour_id = @palette.match_colour(bg)
colour_id = if bg.is_a? Unknown
bg.id
else
@palette.match_colour(bg)
end
string = e(colour_id + 40) + string
end
@@ -134,12 +142,20 @@ def colour_ansi(string, fg=nil, bg=nil)
def colour_extended(string, fg=nil, bg=nil)
unless fg == nil
colour_id = @palette.match_colour(fg)
colour_id = if fg.is_a? Unknown
fg.id
else
@palette.match_colour(fg)
end
string = e("38;5;#{colour_id}") + string
end
unless bg == nil
colour_id = @palette.match_colour(bg)
colour_id = if bg.is_a? Unknown
bg.id
else
@palette.match_colour(bg)
end
string = e("48;5;#{colour_id}") + string
end
@@ -192,11 +208,17 @@ def resolve_colour_name(name)
def resolve_colour_def(colour_def)
return nil if colour_def == "" || colour_def == "default"
begin
@palette.get_colour_value parse_colour_id colour_def
id = parse_colour_id colour_def
if @palette.is_known? id
Colour.new @palette.get_colour_value id
else
Unknown.new id
end
rescue RuntimeError
begin
parse_rgb_value colour_def
Colour.new parse_rgb_value colour_def
rescue RuntimeError
begin
colour_def = resolve_colour_name colour_def
@@ -212,16 +234,19 @@ def resolve_colour_def(colour_def)
end
end
def to_colour(value)
rgb = case
when value.is_a?(String) then resolve_colour_def value
when value.is_a?(Array) then value
when value.is_a?(Colour) then value.rgb
when value == nil then return nil
else raise "Colour value type '#{value.class}' not supported."
end
Colour.new rgb
def get_colour_instance(value)
case
when value.is_a?(String)
resolve_colour_def value
when value.is_a?(Array)
Colour.new value
when value.is_a?(Colour) || value.is_a?(Unknown)
value
when value == nil
nil
else
raise "Colour value type '#{value.class}' not supported."
end
end
end
end
View
@@ -22,6 +22,18 @@
# THE SOFTWARE.
module Tco
class Unknown
attr_reader :id
def initialize(id)
@id = id
end
def to_s
"@#{@id}"
end
end
class Colour
attr_reader :rgb, :lab
@@ -243,7 +255,8 @@ def delta_e_2000(lab1, lab2)
xDL = xDL / (kl * xSL)
xDC = xDC / (kc * xSC)
xDH = xDH / (kh * xSH)
de = Math.sqrt(xDL**2 + xDC**2 + xDH**2 + xRT * xDC * xDH)
Math.sqrt(xDL**2 + xDC**2 + xDH**2 + xRT * xDC * xDH)
end
end
@@ -260,22 +273,22 @@ def initialize(type)
# they were explicitly configured in tco.conf.
#
# The colour values in comments are the defaults for xterm.
nil, # [0, 0, 0]
nil, # [205, 0, 0]
nil, # [0, 205, 0]
nil, # [205, 205, 0]
nil, # [0, 0, 238]
nil, # [205, 0, 205]
nil, # [0, 205, 205]
nil, # [229, 229, 229]
nil, # [127, 127, 127]
nil, # [255, 0, 0]
nil, # [0, 255, 0]
nil, # [255, 255, 0]
nil, # [92, 92, 255]
nil, # [255, 0, 255]
nil, # [0, 255, 255]
nil, # [255, 255, 255]
Unknown.new(0), # [0, 0, 0]
Unknown.new(1), # [205, 0, 0]
Unknown.new(2), # [0, 205, 0]
Unknown.new(3), # [205, 205, 0]
Unknown.new(4), # [0, 0, 238]
Unknown.new(5), # [205, 0, 205]
Unknown.new(6), # [0, 205, 205]
Unknown.new(7), # [229, 229, 229]
Unknown.new(8), # [127, 127, 127]
Unknown.new(9), # [255, 0, 0]
Unknown.new(10), # [0, 255, 0]
Unknown.new(11), # [255, 255, 0]
Unknown.new(12), # [92, 92, 255]
Unknown.new(13), # [255, 0, 255]
Unknown.new(14), # [0, 255, 255]
Unknown.new(15), # [255, 255, 255]
# The colours bellow are the definitions from xterm extended
# colour palette. They should be the same across terminals.
@@ -529,10 +542,15 @@ def set_colour_value(id, rgb_colour)
def get_colour_value(id)
raise "Id '#{id}' out of range." unless id.between?(0, @palette.length-1)
raise "Value of colour '#{id}' is unknown" if @palette[id].is_a? Unknown
@palette[id].rgb if @palette[id]
end
# Returns an index of the closest colour in the palette
def is_known?(id)
raise "Id '#{id}' out of range." unless id.between?(0, @palette.length-1)
!@palette[id].is_a? Unknown
end
def match_colour(colour)
unless colour.is_a? Colour
msg = "Unsupported argument type '#{colour.class}', must be 'Colour'."
@@ -547,7 +565,7 @@ def match_colour(colour)
if @cache.has_key? colour.to_s
@cache[colour.to_s]
else
distances = colours.map { |c| c ? c - colour : Float::INFINITY }
distances = colours.map { |c| c.is_a?(Colour) ? c - colour : Float::INFINITY }
colour_index = distances.each_with_index.min[1]
# TODO: No cache eviction is currently in place

0 comments on commit a3bff18

Please sign in to comment.