* still buggy, but closer to much better conversion

This commit is contained in:
capitaomorte 2010-03-31 14:55:47 +00:00
parent 8696e15e32
commit 83a81b2a9c

View File

@ -1,31 +1,21 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# -*- coding: utf-8 -*-
# textmate_import.rb --- import textmate snippets
#
# Copyright (C) 2009 Rob Christie, 2010 João Távora
#
# This is a quick script to generate YASnippets from TextMate Snippets. # This is a quick script to generate YASnippets from TextMate Snippets.
# #
# I based the script off of a python script of a similar nature by # I based the script off of a python script of a similar nature by
# Jeff Wheeler: http://nokrev.com # Jeff Wheeler: http://nokrev.com
# http://code.nokrev.com/?p=snippet-copier.git;a=blob_plain;f=snippet_copier.py # http://code.nokrev.com/?p=snippet-copier.git;a=blob_plain;f=snippet_copier.py
# #
# Usage # Use textmate_import.rb --help to get usage information.
#
# Make sure you have the plist and the choice gem installed
# $ sudo gem install plist
# $ sudo gem install choice
#
# Usage: snippet_copier.rb [-dofp]
#
# Standard Options:
# -d, --snippet-dir=PATH Tells the program the directory to find the TextMate Snippets
# -o, --output-dir=PATH What directory to write the new YASnippets to
# -f, --file=SNIPPET FILE NAME A specific snippet that you want to copy or a glob for various files
# -p, --print-pretty Pretty prints multiple snippets when printing to standard out
# -b, --convert-bindings TextMate "keyEquivalent" keys are translated to YASnippet "# binding :" directives
# -g, --info-plist=INFO Attempt to derive group information from "info.plist" type-file PLIST
#
# Common options:
# --help Show this message
require 'rubygems' require 'rubygems'
require 'plist' require 'plist'
require 'choice' require 'choice'
require 'FileUtils'
require 'ruby-debug' if $DEBUG require 'ruby-debug' if $DEBUG
Choice.options do Choice.options do
@ -58,6 +48,12 @@ Choice.options do
desc 'Pretty prints multiple snippets when printing to standard out' desc 'Pretty prints multiple snippets when printing to standard out'
end end
option :quiet do
short '-v'
long '--quiet'
desc 'Be quiet.'
end
option :convert_bindings do option :convert_bindings do
short '-b' short '-b'
long '--convert-bindings' long '--convert-bindings'
@ -67,7 +63,7 @@ Choice.options do
option :info_plist do option :info_plist do
short '-g' short '-g'
long '--info-plist' long '--info-plist'
desc "Attempt to derive group information from \"info.plist\" type-file PLIST" desc "Attempt to derive menu information from \"info.plist\" type-file PLIST"
end end
separator '' separator ''
@ -79,7 +75,84 @@ Choice.options do
end end
end end
# Represents and is capable of outputting the representation of a
# TextMate menu in terms of `yas/define-menu'
#
class TmSubmenu
attr_reader :items, :name
def initialize(name, hash)
@items = hash["items"]
@name = name
end
def to_lisp(allsubmenus,
indent = 0,
thingy = ["(", ")"])
first = true;
string = ""
items.each do |uuid|
string += "\n"
string += " " * indent
string += (first ? thingy[0] : (" " * thingy[0].length))
submenu = allsubmenus[uuid]
if submenu
str = "(yas/submenu "
string += str + "\"" + submenu.name + "\""
string += submenu.to_lisp(allsubmenus,
indent + str.length + thingy[0].length)
else
snippet = TmSnippet::snippets_by_uid[uuid]
sname = snippet ? snippet.name : uuid
if (sname !~ /---------------------/)
string += "(yas/item \"" + sname + "\")"
else
string += "(yas/separator)"
end
end
first = false;
end
string += ")"
string += thingy[1]
return string
end
def self.main_menu_to_lisp (hash)
mainmenu = TmSubmenu.new("__main_menu__", hash)
all = {}
hash["submenus"].each_pair do |k,v|
all[k] = TmSubmenu.new(v["name"], v)
end
closing = "\n '("
closing+= hash["excludedItems"].collect do |uuid|
snippet = TmSnippet::snippets_by_uid[uuid]
"\"" + (snippet ? snippet.name : uuid) + "\""
end.join( "\n ") + "))"
str = "(yas/define-menu "
return str + "'major-mode-name" + mainmenu.to_lisp(all,
str.length,
["'(" , closing])
end
end
# Represents a textmate snippet
#
# - @file is the .tmsnippet/.plist file path relative to cwd
#
# - optional @info is a Plist.parsed info.plist found in the bundle dir
#
# - @@snippets_by_uid is where one can find all the snippets parsed so
# far.
#
#
class TmSnippet class TmSnippet
@@known_substitutions=[ @@known_substitutions=[
{ {
@ -92,18 +165,15 @@ class TmSnippet
}, },
{ "$TM_SELECTED_TEXT" => "`yas/selected-text`" } { "$TM_SELECTED_TEXT" => "`yas/selected-text`" }
] ]
attr_reader :file
# Makes a TmSnippet @@snippets_by_uid={}
# def self.snippets_by_uid; @@snippets_by_uid; end
# * file is the .tmsnippet/.plist file path relative to cwd
# * optional info is a Plist.parsed info.plist found in the bundle dir
#
def initialize(file,info=nil) def initialize(file,info=nil)
@file = file @file = file
@info = info @info = info
@snippet = TmSnippet::read_plist(file) @snippet = TmSnippet::read_plist(file)
@@snippets_by_uid[self.uuid] = self;
raise RuntimeError.new("Cannot convert this snippet #{file}!") unless @snippet; raise RuntimeError.new("Cannot convert this snippet #{file}!") unless @snippet;
end end
@ -111,15 +181,15 @@ class TmSnippet
@snippet["name"] @snippet["name"]
end end
def group # def subdir
if @info # if @info
submenus = @info["mainMenu"]["submenus"] # submenus = @info["mainMenu"]["submenus"]
container = submenus.keys.find do |submenu| # container = submenus.keys.find do |submenu|
submenus[submenu]["items"].member?(uuid) # submenus[submenu]["items"].member?(uuid)
end # end
submenus[container]["name"] if container; # submenus[container]["name"] if container;
end # end
end # end
def uuid def uuid
@snippet["uuid"] @snippet["uuid"]
@ -144,6 +214,7 @@ class TmSnippet
def to_yasnippet def to_yasnippet
doc = "# -*- mode: snippet -*-\n" doc = "# -*- mode: snippet -*-\n"
doc << "# type: command\n" unless self.content doc << "# type: command\n" unless self.content
doc << "# uuid: #{self.uuid}\n"
doc << "# key: #{self.tab_trigger}\n" if self.tab_trigger doc << "# key: #{self.tab_trigger}\n" if self.tab_trigger
doc << "# contributor: Translated from TextMate Snippet\n" doc << "# contributor: Translated from TextMate Snippet\n"
doc << "# name: #{self.name}\n" doc << "# name: #{self.name}\n"
@ -164,7 +235,7 @@ class TmSnippet
end end
def canonicalize(filename) def canonicalize(filename)
invalid_char = /[^ a-z_0-9.+=~(){}'"'"'`&#,-]/i invalid_char = /[^ a-z_0-9.+=~(){}\/'"'"'`&#,-]/i
filename. filename.
gsub(invalid_char, ''). # remove invalid characters gsub(invalid_char, ''). # remove invalid characters
@ -174,14 +245,13 @@ class TmSnippet
def yasnippet_file(basedir) def yasnippet_file(basedir)
# files cannot end with dots (followed by spaces) on windows # files cannot end with dots (followed by spaces) on windows
basedir = File.join(basedir,group.gsub(/[.]+ *$/,'')) if group
File.join(basedir,canonicalize(@file[0, @file.length-File.extname(@file).length]) + ".yasnippet") File.join(basedir,canonicalize(@file[0, @file.length-File.extname(@file).length]) + ".yasnippet")
end end
def self.read_plist(xml_or_binary) def self.read_plist(xml_or_binary)
begin begin
parsed = Plist::parse_xml(xml_or_binary) parsed = Plist::parse_xml(xml_or_binary)
return parsed if parsed; return parsed if parsed
raise RuntimeError.new "Probably in binary format and parse_xml is very quiet..." raise RuntimeError.new "Probably in binary format and parse_xml is very quiet..."
rescue RuntimeError => e rescue RuntimeError => e
if (system "plutil -convert xml1 '#{xml_or_binary}' -o /tmp/textmate_import") if (system "plutil -convert xml1 '#{xml_or_binary}' -o /tmp/textmate_import")
@ -195,27 +265,25 @@ class TmSnippet
end end
if $0 == __FILE__ if $0 == __FILE__
info_plist = TmSnippet::read_plist(Choice.choices.info_plist) if Choice.choices.info_plist; info_plist = TmSnippet::read_plist(Choice.choices.info_plist) if Choice.choices.info_plist;
if Choice.choices.output_dir # Glob snippets into snippet_files, going into subdirs
FileUtils.mkdir_p Choice.choices.output_dir #
FileUtils.touch File.join(Choice.choices.output_dir, ".yas-make-groups")
FileUtils.touch File.join(Choice.choices.output_dir, ".yas-ignore-filenames-as-triggers")
end
original_dir = Dir.pwd original_dir = Dir.pwd
Dir.chdir Choice.choices.snippet_dir Dir.chdir Choice.choices.snippet_dir
snippet_files_glob = File.join("**", Choice.choices.snippet) snippet_files_glob = File.join("**", Choice.choices.snippet)
snippet_files = Dir.glob(snippet_files_glob) snippet_files = Dir.glob(snippet_files_glob)
puts "Will try to convert #{snippet_files.length} snippets...\n" # Attempt to convert each snippet files in snippet_files
#
puts "Will try to convert #{snippet_files.length} snippets...\n" unless Choice.choices.quiet
snippet_files.each do |file| snippet_files.each do |file|
begin begin
puts "Processing \"#{File.join(Choice.choices.snippet_dir,file)}\"\n" puts "Processing \"#{File.join(Choice.choices.snippet_dir,file)}\"\n" unless Choice.choices.quiet
snippet = TmSnippet.new(file,info_plist) snippet = TmSnippet.new(file,info_plist)
if Choice.choices.output_dir if Choice.choices.output_dir
file_to_create = snippet.yasnippet_file(File.join(original_dir, Choice.choices.output_dir)) file_to_create = snippet.yasnippet_file(File.join(original_dir, Choice.choices.output_dir))
FileUtils.mkdir_p(File.dirname(file_to_create)) FileUtils.mkdir_p(File.dirname(file_to_create))
@ -226,14 +294,35 @@ if $0 == __FILE__
if Choice.choices.print_pretty if Choice.choices.print_pretty
puts "--------------------------------------------" puts "--------------------------------------------"
end end
puts snippet.to_yasnippet puts snippet.to_yasnippet if Choice.choices.print_pretty or not Choice.choices.info_plist
if Choice.choices.print_pretty if Choice.choices.print_pretty
puts "--------------------------------------------" puts "--------------------------------------------\n\n"
end end
puts "\n\n"
end end
rescue Exception => e rescue Exception => e
$stderr.puts "Oops... #{e.class}:#{e.message}\n#{e.backtrace.join("\n")}" $stderr.puts "Oops... #{e.class}:#{e.message}\n#{e.backtrace.join("\n")}"
end end
end end
# Attempt to decypher the menu
#
modename = Choice.choices.output_dir or "major-mode-name"
str = TmSubmenu::main_menu_to_lisp(info_plist["mainMenu"]) if info_plist
puts str unless !str or Choice.choices.quiet
# Write some basic .yas-* files
#
if Choice.choices.output_dir
FileUtils.mkdir_p Choice.choices.output_dir
FileUtils.touch File.join(Choice.choices.output_dir, ".yas-make-groups") unless str
FileUtils.touch File.join(Choice.choices.output_dir, ".yas-ignore-filenames-as-triggers")
File.open(File.join(Choice.choices.output_dir, ".yas-setup.el"), 'w') do |file|
file.write ";; .yas-setup.el for #{modename}\n"
file.write ";;\n"
file.write ";;\n"
file.write(str)
file.write "\n;;\n"
file.write ";; .yas-setup.el for #{modename} ends here\n"
end
end
end end