Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
#!/usr/bin/env ruby
#
# This script is an astonishing feat of top notch
# rockstar craftsmanship. It totally uses artificial
# intelligence to extract colors out of tmTheme and
# build an itermcolors scheme file for iTerm2.
#
# I know this sounds crazy, but it actually knows
# approximately what colors should be used in the
# ANSI list, and tries to find nearest colors from
# the given tmTheme. This level of intelligent guessing
# has never been achieved before in the field of
# converting TextMate themes into iTerm color schemes.
# Never.
#
#
# USAGE
#
# Step 1. Make sure you have plist gem installed
# gem install plist
#
# Step 2. Make it executable
# chmod +x tm2iterm.rb
#
# Step 3. Execute it
# ./tm2iterm.rb /path/to/some.tmTheme /path/to/output.itermcolors
#
# Step 4. Double click the itermcolors file
# iTerm2 will pick up the scheme and add it to the list
require 'rubygems'
require 'plist'
BRIGHTEN_AMOUNT = 50
class Color
attr_reader :hex, :rgb, :coefficients, :coef_hash
def initialize(hex = nil)
hex ||= '000000'
if hex =~ /\A#/
hex.slice!(0)
end
@hex = hex[0..5]
@rgb = @hex.scan(/../).map do |hex_component|
cap_between_0_and_255(hex_component.to_i(16))
end
@coefficients = @rgb.map do |value|
value / 255.0
end
@coef_hash = {
'Red Component' => @coefficients[0],
'Green Component' => @coefficients[1],
'Blue Component' => @coefficients[2]
}
end
def brighten(value = BRIGHTEN_AMOUNT)
hex_string = @rgb.map do |component|
cap_between_0_and_255(component + value).to_s(16)
end.join
self.class.new(hex_string)
end
alias to_s hex
def inspect
"<Color:##{self}>"
end
def sum_distance(color)
[0, 1, 2].map{ |i| (color.rgb[i] - rgb[i]).abs }.inject(0){ |sum, v| sum += v }
end
def find_nearest(colors)
colors.min do |a, b|
sum_distance(a) <=> sum_distance(b)
end
end
def ==(color)
color.rgb == rgb
end
private
def cap_between_0_and_255(num)
num = [num, 0].max
num = [num, 255].min
num
end
end
# Shortcut
def c(hex)
Color.new(hex)
end
input, output = ARGV
if input.nil? || output.nil?
puts 'FAIL. OMG.'
puts 'Usage: tm2iterm /path/to/file.tmTheme /path/to/file.itermcolors'
exit(1)
elsif File.directory?(output)
output = File.absolute_path(output) + '/' + File.basename(input,'.tmTheme') + '.itermcolors'
end
tm_theme = Plist::parse_xml(input)
head = tm_theme['settings'].shift['settings']
tail = Hash[tm_theme['settings'].map do |s|
[ s['name'].downcase, s['settings']['foreground'] ]
end]
background = c head['background']
bold = c head['foreground']
cursor = c head['caret']
cursor_text = c head['foreground']
foreground = c head['foreground']
selected_text = c head['foreground']
selection = c head['selection']
# black #000000
# red #ff0000
# green #00ff00
# yellow #ffff00
# blue #0000ff
# magenta #ff00ff
# cyan #00ffff
# white #ffffff
ref_color_list = %w[ #000000
#ff0000
#00ff00
#ffff00
#0000ff
#ff00ff
#00ffff
#ffffff ].map{ |color| c(color) }
src_color_list = tail.values.uniq.map{ |color| c(color) }
ansi = []
8.times do |i|
if matching_color = src_color_list.find{|color| color == ref_color_list[i]}
ansi[i] = matching_color
src_color_list.delete(matching_color)
end
end
8.times do |i|
ansi[i] = ansi[i] || begin
nearest = ref_color_list[i].find_nearest(src_color_list)
src_color_list.delete(nearest)
nearest
end
end
8.times do |i|
ansi[i + 8] = ansi[i].brighten
end
scheme = {
'Background Color' => background.coef_hash,
'Bold Color' => bold.coef_hash,
'Cursor Color' => cursor.coef_hash,
'Cursor Text Color' => cursor_text.coef_hash,
'Foreground Color' => foreground.coef_hash,
'Selected Text Color' => selected_text.coef_hash,
'Selection Color' => selection.coef_hash,
}
16.times do |i|
scheme.merge!("Ansi #{i} Color" => ansi[i].coef_hash)
end
File.open(output, 'w') do |file|
file.write(Plist::Emit.dump(scheme))
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.