#!/usr/bin/env python # encoding: utf-8 import urllib import re from xml.etree import ElementTree from xml.parsers.expat import ExpatError import htmlentitydefs import os import glob _UNESCAPE = re.compile(ur'&\w+?;', re.UNICODE) def unescape(s): if s is None: return '' def fixup(m): ent = m.group(0)[1:-1] return unichr(htmlentitydefs.name2codepoint[ent]) try: return _UNESCAPE.sub(fixup, s) except: print 'unescape failed: %s' % repr(s) raise class UnknownVariable(Exception): pass class UnsupportedVariableExpression(Exception): pass def replace_vars(m): """Replace vars in 'content' portion. :m: match object :returns: string """ var = m.group(1) default = m.group(2) if not re.match(r'\w+$', var): raise UnsupportedVariableExpression(var) translate_vars = { 'TM_PHP_OPEN_TAG_WITH_ECHO': 'g:UltiSnipsOpenTagWithEcho', 'TM_PHP_OPEN_TAG': 'g:UltiSnipsOpenTag', 'PHPDOC_AUTHOR': 'g:snips_author', } # TODO: TM_SELECTED_TEXT/([\t ]*).*/$1/m if var in translate_vars: newvar = translate_vars[var] else: # TODO: this could be autogenerated raise UnknownVariable(var) return "`!v exists('%s') ? %s : '%s'`" % (newvar, newvar, default) def parse_content(c): try: data = ElementTree.fromstring(c)[0] rv = {} for k, v in zip(data[::2], data[1::2]): rv[k.text] = unescape(v.text) if re.search(r'\$\{\D', rv['content']): rv['content'] = re.sub( r'\$\{([^\d}][^}:]*)(?::([^}]*))?\}', replace_vars, rv['content']) return rv except (ExpatError, ElementTree.ParseError) as detail: print ' Syntax Error: %s' % (detail,) print c return None except UnknownVariable as detail: print ' Unknown variable: %s' % (detail,) return None except UnsupportedVariableExpression as detail: print ' Unsupported variable expression: %s' % (detail,) return None def fetch_snippets_from_svn(name): base_url = 'http://svn.textmate.org/trunk/Bundles/' + name + '.tmbundle/' snippet_idx = base_url + 'Snippets/' idx_list = urllib.urlopen(snippet_idx).read() rv = [] for link in re.findall('
  • (.*?)
  • ', idx_list): m = re.match(r'(.*)', link) link, name = m.groups() if name == '..': continue name = unescape(name.rsplit('.', 1)[0]) # remove Extension print "Fetching data for Snippet '%s'" % name content = urllib.urlopen(snippet_idx + link).read() cont = parse_content(content) if cont: rv.append((name, cont)) return rv def fetch_snippets_from_dir(path): """Fetch snippets from a given path.""" rv = [] for filename in glob.glob(os.path.join(path, '*.tmSnippet')): print 'Reading file %s' % filename f = open(filename) content = f.read() cont = parse_content(content) if cont: name = os.path.splitext(os.path.basename(filename))[0] rv.append((name, cont)) return rv def write_snippets(snip_descr, f): for name, d in snip_descr: if 'tabTrigger' not in d: continue if 'content' not in d or d['content'] is None: print 'SKIP: %s (no content)' % (d,) continue f.write('snippet %s "%s"\n' % (d['tabTrigger'], name)) f.write(d['content'].encode('utf-8') + '\n') f.write('endsnippet\n\n') if __name__ == '__main__': import sys bundle = sys.argv[1] if os.path.isdir(bundle): name = sys.argv[2] rv = fetch_snippets_from_dir(bundle) else: rv = fetch_snippets_from_svn(bundle) name = bundle.lower() write_snippets(rv, open('tm_' + name + '.snippets', 'w'))