begin require 'Plist' rescue LoadError raise 'You must "gem install plist" to get plist parser' end ROOT = File.expand_path('..') SRC = ROOT DST = File.join(ROOT, 'build') DST_THEMES = File.join(DST, 'themes') TMP = File.join(ROOT, 'tmp') THEMES_DIR = File.expand_path("~/Library/Application\\ Support/TextMate/Themes") # http://kpumuk.info/ruby-on-rails/colorizing-console-ruby-script-output/ begin require 'Win32/Console/ANSI' if PLATFORM =~ /win32/ rescue LoadError raise 'You must "gem install win32console" to use terminal colors on Windows' end def colorize(text, color_code) "#{color_code}#{text}\e[0m" end def red(text); colorize(text, "\e[31m"); end def green(text); colorize(text, "\e[32m"); end def yellow(text); colorize(text, "\e[33m"); end def blue(text); colorize(text, "\e[34m"); end def magenta(text); colorize(text, "\e[35m"); end def azure(text); colorize(text, "\e[36m"); end def white(text); colorize(text, "\e[37m"); end def black(text); colorize(text, "\e[30m"); end def file_color(text); yellow(text); end def dir_color(text); blue(text); end def cmd_color(text); azure(text); end def detect_selector_from_scope(scope) return ".panelNode-script" unless scope parts = scope.split(',') parts.each do |part| case part when "comment" return ".js-comment, .xml-comment, .css-comment" when "keyword" return ".js-keyword" when "variable", "constant.language" return ".js-variable" when "variable.language", "variable.other" return ".js-variabledef" when "string", "css.string" return ".js-string" when "string.regexp" return ".js-regexp" when "constant", "constant.numeric" return ".js-atom" when "keyword.operator.js" return ".js-operator" when "string.quoted.docinfo.doctype.DTD", "meta.tag.preprocessor.xml", "meta.tag.sgml.doctype", "meta.tag.sgml.doctype entity", "meta.tag.sgml.doctype string", "meta.tag.preprocessor.xml", "meta.tag.preprocessor.xml entity", "meta.tag.preprocessor.xml string" return ".xml-processing" when "entity.name.tag", "meta.tag", "declaration.tag" return ".xml-tagname" when /attribute-name/ return ".xml-attname" when "???" return ".xml-text" when "???" return ".xml-entity" when "???" return ".xml-cdata" when /property-value.css/ return ".css-value" when "meta.selector.css entity.name.tag" return ".css-identifier" when /property-name.css/ return ".css-colorcode" when "???" return ".css-string" when "???" return ".css-unit" when "???" return ".css-important" when "???" return ".css-select-op" when "???" return ".css-punctuation" when "???" return ".css-compare" when /at-rule/ return ".css-at" end end nil end def color(val) return "???" unless val =~ /#([0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])/ "\##{$1}" end def font_style(val) return nil unless val=="italic" "italic" end def font_weight(val) return nil unless val=="bold" "bold" end KNOWN_ATTRIBUTES = { "foreground" => ["color", method(:color)], "background" => ["background-color", method(:color)], "fontStyle" => ["font-style", method(:font_style)], "fontStyle" => ["font-weight", method(:font_weight)], } def gen_rule(dict) scope = dict["scope"] selector = detect_selector_from_scope(scope) return "" unless selector settings = dict["settings"] return "" unless settings rule = {} KNOWN_ATTRIBUTES.each do |attr, spec| next unless settings[attr] spec = [spec] unless spec.is_a?(Array) val = settings[attr] val = spec[1].call(val) if spec[1] rule[spec[0]] = val if val end return "" unless rule.keys.size>0 if (selector==".panelNode-script") then rule["font-family"] = "Monaco, Courier New" rule["font-size"] = "11px" additional = "" highlight = color(settings["lineHighlight"]) if settings["lineHighlight"] additional += ".sourceRow.hovered { background-color: #{highlight}; }\n\n" if highlight selection = color(settings["selection"]) if settings["selection"] additional += ".sourceRow[exeLine=\"true\"] { background-color: #{selection}; }\n\n" if selection end res = "#{selector} {\n" rule.each do |key, value| res += " #{key}: #{value};\n" end res += "}\n\n" res += additional if additional res end def gen_css(data, source) desc = "#{data["name"]}" desc += " by #{data["author"]}" if data["author"] desc += ", converted from TextMate theme (#{source})" res = "/* #{desc} */\n\n" data["settings"].each do |dict| res += gen_rule(dict) end { "css" => res, "name" => data["name"], "author" => data["author"], "description" => desc } end def my_mkdir(dir) puts "#{cmd_color('creating directory')} #{dir_color(dir)}" mkdir dir end def process(dir) files = Dir.glob(File.join(dir, "**", "*.tmTheme")) files.each do |filename| basename = File.basename(filename) puts " Converting #{file_color(basename)}" data = Plist::parse_xml(filename) preset = gen_css(data, basename) preset["basename"] = basename yield preset end end task :convert do my_mkdir(DST_THEMES) unless File.exist?(DST_THEMES) puts "Scanning #{dir_color(THEMES_DIR)} ..." process(THEMES_DIR) do |preset| res = File.join(DST_THEMES, preset["basename"] + ".css") File.open(res, "w") do |file| file.write preset["css"] end end end task :sql do puts "Generating sql ..." res = "" process(THEMES_DIR) do |preset| name = preset["name"].downcase.gsub(/[\(\)&]/, "").gsub(/[ \t]/, "_").gsub(/'/, "''") title = preset["name"].gsub(/'/, "''") desc = preset["description"].gsub(/'/, "''") code = preset["css"].gsub(/\n/, "\\r\\n").gsub(/'/, "''").gsub(/"/, """) res += "('#{name}', '#{title}', '#{desc}', '#{code}', '2008-06-14 18:19:36', '2008-06-14 18:24:04'),\n" end puts res end task :default => :convert