Skip to content

Instantly share code, notes, and snippets.

@maxim
Created June 10, 2012 04:04
Show Gist options
  • Save maxim/2903788 to your computer and use it in GitHub Desktop.
Save maxim/2903788 to your computer and use it in GitHub Desktop.
Convert TextMate themes into iTerm 2 color schemes.
#!/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)
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
@mrmurphy
Copy link

Hey! Thanks for putting this up!

I get this error when I try to run it, and I'm not Ruby enough to solve it:

tm2iterm.rb:78:in block in sum_distance': undefined method-' for nil:NilClass (NoMethodError)
from tm2iterm.rb:78:in map' from tm2iterm.rb:78:insum_distance'
from tm2iterm.rb:83:in block in find_nearest' from tm2iterm.rb:82:ineach'
from tm2iterm.rb:82:in min' from tm2iterm.rb:82:infind_nearest'
from tm2iterm.rb:157:in block in <main>' from tm2iterm.rb:155:intimes'
from tm2iterm.rb:155:in `

'

Any ideas?

Thanks!

@mixn
Copy link

mixn commented Oct 31, 2013

Worked like a charm — thank you! :)

@travisnewman
Copy link

This worked but I'm noticing the colors aren't quite the same. I had a theme from the same creator for each but they looked different. I was hoping converting one would fix that but unfortunately it hasn't. Is this common?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment