feat(SublimeText2.WebPackages): cache packages

This commit is contained in:
Iristyle
2013-04-04 08:54:25 -04:00
parent 590d7a44f9
commit 1e6f643a1b
1026 changed files with 79077 additions and 0 deletions

View File

@@ -0,0 +1,228 @@
# coding=utf-8
import sys
import os
import os.path
import codecs
import json
import gc
import imp
import re
from file import File
BASE_PATH = os.path.abspath(os.path.dirname(__file__))
is_python3 = sys.version_info[0] > 2
core_files = ['emmet-app.js', 'python-wrapper.js']
def should_use_unicode():
"""
WinXP unable to eval JS in unicode object (while other OSes requires it)
This function checks if we have to use unicode when reading files
"""
ctx = PyV8.JSContext()
ctx.enter()
use_unicode = True
try:
ctx.eval(u'(function(){return;})()')
except:
use_unicode = False
ctx.leave()
return use_unicode
def make_path(filename):
return os.path.normpath(os.path.join(BASE_PATH, filename))
def js_log(message):
print(message)
def js_file_reader(file_path, use_unicode=True):
if use_unicode:
f = codecs.open(file_path, 'r', 'utf-8')
else:
f = open(file_path, 'r')
content = f.read()
f.close()
return content
def import_pyv8():
# Importing non-existing modules is a bit tricky in Python:
# if we simply call `import PyV8` and module doesn't exists,
# Python will cache this failed import and will always
# throw exception even if this module appear in PYTHONPATH.
# To prevent this, we have to manually test if
# PyV8.py(c) exists in PYTHONPATH before importing PyV8
if 'PyV8' in sys.modules:
# PyV8 was loaded by ST2, create global alias
if 'PyV8' not in globals():
globals()['PyV8'] = __import__('PyV8')
return
loaded = False
f, pathname, description = imp.find_module('PyV8')
bin_f, bin_pathname, bin_description = imp.find_module('_PyV8')
if f:
try:
imp.acquire_lock()
globals()['_PyV8'] = imp.load_module('_PyV8', bin_f, bin_pathname, bin_description)
globals()['PyV8'] = imp.load_module('PyV8', f, pathname, description)
imp.release_lock()
loaded = True
finally:
# Since we may exit via an exception, close fp explicitly.
if f:
f.close()
if bin_f:
bin_f.close()
if not loaded:
raise ImportError('No PyV8 module found')
class Context():
"""
Creates Emmet JS core context.
Before instantiating this class, make sure PyV8
is available in `sys.path`
@param files: Additional files to load with JS core
@param path: Path to Emmet extensions
@param contrib: Python objects to contribute to JS execution context
@param pyv8_path: Location of PyV8 binaries
"""
def __init__(self, files=[], ext_path=None, contrib=None, logger=None, reader=js_file_reader):
self.logger = logger
self.reader = reader
try:
import_pyv8()
except ImportError as e:
pass
self._ctx = None
self._contrib = contrib
self._should_load_extension = True
# detect reader encoding
self._use_unicode = None
self._core_files = [] + core_files + files
self._ext_path = None
self.set_ext_path(ext_path)
self._user_data = None
def log(self, message):
if self.logger:
self.logger(message)
def get_ext_path(self):
return self._ext_path
def set_ext_path(self, val):
try:
if val and val[:1] == '~':
val = os.path.expanduser(val)
val = os.path.abspath(val)
except Exception as e:
return
if val == self._ext_path:
return
self._ext_path = val
self.reset()
def load_extensions(self, path=None):
if path is None:
path = self._ext_path;
if path and os.path.isdir(path):
ext_files = []
self.log('Loading Emmet extensions from %s' % self._ext_path)
for dirname, dirnames, filenames in os.walk(self._ext_path):
for filename in filenames:
if filename[0] != '.':
ext_files.append(os.path.join(dirname, filename))
self.js().locals.pyLoadExtensions(ext_files)
def js(self):
"Returns JS context"
if not self._ctx:
try:
import_pyv8()
except ImportError as e:
return None
if 'PyV8' not in sys.modules:
# Binary is not available yet
return None
if self._use_unicode is None:
self._use_unicode = should_use_unicode()
glue = u'\n' if self._use_unicode else '\n'
core_src = [self.read_js_file(make_path(f)) for f in self._core_files]
self._ctx = PyV8.JSContext()
self._ctx.enter()
self._ctx.eval(glue.join(core_src))
# for f in self._core_files:
# self._ctx.eval(self.read_js_file(make_path(f)), name=f, line=0, col=0)
# load default snippets
self._ctx.locals.pyLoadSystemSnippets(self.read_js_file(make_path('snippets.json')))
# expose some methods
self._ctx.locals.log = js_log
self._ctx.locals.pyFile = File()
if self._contrib:
for k in self._contrib:
self._ctx.locals[k] = self._contrib[k]
else:
self._ctx.enter()
if self._should_load_extension:
self._ctx.locals.pyResetUserData()
self._should_load_extension = False
self.load_extensions()
if self._user_data:
self._ctx.locals.pyLoadUserData(self._user_data)
self._user_data = None
return self._ctx
def load_user_data(self, data):
"Loads user data payload from JSON"
self._user_data = data
# self.js().locals.pyLoadUserData(data)
def reset(self):
"Resets JS execution context"
if self._ctx:
self._ctx.leave()
self._ctx = None
try:
PyV8.JSEngine.collect()
gc.collect()
except:
pass
self._should_load_extension = True
def read_js_file(self, file_path):
return self.reader(file_path, self._use_unicode)
def eval(self, source):
self.js().eval(source)
def eval_js_file(self, file_path):
self.eval(self.read_js_file(file_path))

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,159 @@
'''
@author Sergey Chikuyonok (serge.che@gmail.com)
@link http://chikuyonok.ru
'''
import sys
import os.path
import re
is_python3 = sys.version_info[0] > 2
try:
if is_python3:
import urllib.request as urllib2
else:
import urllib2
except Exception as e:
pass
def is_url(path):
return re.match(r'^https?://', path, re.IGNORECASE)
def read_http(url, size=-1, mode=None):
response = urllib2.urlopen(url, timeout=5)
return response.read(size)
def read_file(path, size=-1, mode='rb'):
kwargs = {}
if is_python3 and 'b' not in mode:
kwargs['encoding'] = 'utf-8'
with open(path, mode, **kwargs) as fp:
return fp.read(size)
class File():
def __init__(self):
pass
def _read(self, path, size, mode='rb'):
reader = is_url(path) and read_http or read_file
return reader(path, size, mode)
def read(self, path, size, callback=None):
"""
Read file content and return it
@param path: File's relative or absolute path
@type path: str
@return: str
"""
try:
content = self._read(path, size)
# return as array of character codes since PyV8 may corrupt
# binary data when python string is translated into JS string
if is_python3:
content = [ch for ch in content]
else:
content = [ord(ch) for ch in content]
except Exception as e:
return callback(str(e), None)
callback(None, content)
def read_text(self, path, size, callback=None):
"""
Read file content and return it
@param path: File's relative or absolute path
@type path: str
@return: str
"""
try:
content = self._read(path, size, 'r')
if not is_python3:
content = content.decode('utf-8')
except Exception as e:
return callback(str(e), None)
callback(None, content)
def locate_file(self, editor_file, file_name):
"""
Locate <code>file_name</code> file that relates to <code>editor_file</code>.
File name may be absolute or relative path
@type editor_file: str
@type file_name: str
@return String or None if <code>file_name</code> cannot be located
"""
if is_url(file_name):
return file_name
result = None
previous_parent = ''
parent = os.path.dirname(editor_file)
while parent and os.path.exists(parent) and parent != previous_parent:
tmp = self.create_path(parent, file_name)
if os.path.exists(tmp):
result = tmp
break
previous_parent = parent
parent = os.path.dirname(parent)
return result
def create_path(self, parent, file_name):
"""
Creates absolute path by concatenating <code>parent</code> and <code>file_name</code>.
If <code>parent</code> points to file, its parent directory is used
@type parent: str
@type file_name: str
@return: str
"""
result = ''
file_name = file_name.lstrip('/')
if os.path.exists(parent):
if os.path.isfile(parent):
parent = os.path.dirname(parent)
result = os.path.normpath(os.path.join(parent, file_name))
return result
def save(self, file, content):
"""
Saves <code>content</code> as <code>file</code>
@param file: File's asolute path
@type file: str
@param content: File content
@type content: str
"""
try:
fp = open(file, 'wb')
except:
fdirs, fname = os.path.split(file)
if fdirs:
os.makedirs(fdirs)
fp = open(file, 'wb')
fp.write(content)
fp.close()
def get_ext(self, file):
"""
Returns file extention in lower case
@type file: str
@return: str
"""
ext = os.path.splitext(file)[1]
if ext:
ext = ext[1:]
return ext.lower()

View File

@@ -0,0 +1,97 @@
var console = {
log: function(msg) {
log(msg);
}
};
/**
* Simple function alias to run Emmet action.
* <code>editorProxy</code> object should be defined
* in concrete plugin implementation.
*/
function pyRunAction(name) {
return emmet.require('actions').run(name, editorProxy);
}
function pyLoadSystemSnippets(data) {
emmet.require('bootstrap').loadSystemSnippets(data);
}
function pyLoadUserData(data) {
emmet.require('bootstrap').loadUserData(data);
}
function pyLoadExtensions(fileList) {
fileList = _.toArray(fileList);
emmet.require('bootstrap').loadExtensions(fileList);
}
function pyResetUserData() {
emmet.require('bootstrap').resetUserData();
}
emmet.define('file', function(require, _) {
return {
_parseParams: function(args) {
var params = {
path: args[0],
size: -1
};
args = _.rest(args);
params.callback = _.last(args);
args = _.initial(args);
if (args.length) {
params.size = args[0];
}
return params;
},
read: function(path, size, callback) {
var params = this._parseParams(arguments);
try {
pyFile.read(params.path, params.size, function(err, content) {
if (err) {
return params.callback(err, content);
}
content = _.map(content || [], function(b) {
return String.fromCharCode(b);
}).join('');
params.callback(null, content);
});
} catch(e) {
params.callback(e);
}
},
readText: function() {
var params = this._parseParams(arguments);
try {
pyFile.read_text(params.path, params.size, params.callback);
} catch(e) {
params.callback(e);
}
},
locateFile: function(editorFile, fileName) {
return pyFile.locate_file(editorFile, fileName);
},
createPath: function(parent, fileName) {
return pyFile.create_path(parent, fileName);
},
save: function(file, content) {
return pyFile.save(file, content);
},
getExt: function(file) {
var m = (file || '').match(/\.([\w\-]+)$/);
return m ? m[1].toLowerCase() : '';
}
};
});

View File

@@ -0,0 +1,591 @@
# coding=utf-8
import os
import os.path
import sys
import json
import re
import threading
import subprocess
import tempfile
import collections
import platform
import semver
import time
import zipfile
is_python3 = sys.version_info[0] > 2
if is_python3:
import urllib.request as url_req
import urllib.error as url_err
import urllib.parse as url_parse
else:
import urllib
import urllib2
url_req = urllib2
url_err = urllib2
url_parse = urllib2
CHECK_INTERVAL = 60 * 60 * 24
# PACKAGES_URL = 'https://api.github.com/repos/emmetio/pyv8-binaries/downloads'
PACKAGES_URL = 'https://api.github.com/repos/emmetio/pyv8-binaries/contents'
def load(dest_path, delegate=None):
"""
Main function that attempts to load or update PyV8 binary.
First, it loads list of available PyV8 modules and check if
PyV8 should be downloaded or updated.
@param dest_path: Path where PyV8 lib should be downloaded
@param delegate: instance of LoaderDelegate that will receive
loader progress events
@returns: `True` if download progress was initiated
"""
if delegate is None:
delegate = LoaderDelegate()
config = get_loader_config(dest_path)
if 'PyV8' in sys.modules and (config['skip_update'] or time.time() < config['last_update'] + CHECK_INTERVAL):
# No need to load anything: user already has PyV8 binary
# or decided to disable update process
delegate.log('No need to update PyV8')
return False
def on_complete(result, *args, **kwargs):
if result is not None:
# Most recent version was downloaded
config['last_id'] = result
if 'PyV8' not in sys.modules:
# PyV8 is not loaded yet, we can safely unpack it
unpack_pyv8(dest_path)
config['last_update'] = time.time()
save_loader_config(dest_path, config)
delegate.on_complete(*args, **kwargs)
# try to download most recent version of PyV8
# As PyV8 for Sublime Text spreads the world, it's possible
# that multiple distinct PyV8Loader's may start doing the same
# job at the same time. In this case, we should check if there's
# already a thread that load PyV8 and hook on existing thread
# rather that creating a new one
thread = None
thread_exists = False
for t in threading.enumerate():
if hasattr(t, 'is_pyv8_thread'):
print('PyV8: Reusing thread')
thread = t
thread_exists = True
break
if not thread:
print('PyV8: Creating new thread')
thread = PyV8Loader(get_arch(), dest_path, config, delegate=delegate)
thread.start()
delegate.on_start()
# watch on download progress
prog = ThreadProgress(thread, delegate, thread_exists)
prog.on('complete', on_complete if not thread_exists else delegate.on_complete)
prog.on('error', delegate.on_error)
def get_arch():
"Returns architecture name for PyV8 binary"
suffix = is_python3 and '-p3' or ''
p = lambda a: '%s%s' % (a, suffix)
is_64bit = sys.maxsize > 2**32
system_name = platform.system()
if system_name == 'Darwin':
if semver.match(platform.mac_ver()[0], '<10.7.0'):
return p('mac106')
return p('osx')
if system_name == 'Windows':
return p('win64') if is_64bit else p('win32')
if system_name == 'Linux':
return p('linux64') if is_64bit else p('linux32')
def get_loader_config(path):
config = {
"last_id": 0,
"last_update": 0,
"skip_update": False
}
config_path = os.path.join(path, 'config.json')
if os.path.exists(config_path):
with open(config_path) as fd:
for k,v in json.load(fd).items():
config[k] = v
return config
def save_loader_config(path, data):
config_path = os.path.join(path, 'config.json')
if not os.path.exists(path):
os.makedirs(path)
fp = open(config_path, 'w')
fp.write(json.dumps(data))
fp.close()
def clean_old_data():
for f in os.listdir('.'):
if f.lower() != 'config.json' and f.lower() != 'pack.zip':
try:
os.remove(f)
except Exception as e:
pass
def unpack_pyv8(package_dir):
f = os.path.join(package_dir, 'pack.zip')
if not os.path.exists(f):
return
package_zip = zipfile.ZipFile(f, 'r')
root_level_paths = []
last_path = None
for path in package_zip.namelist():
last_path = path
if path.find('/') in [len(path) - 1, -1]:
root_level_paths.append(path)
if path[0] == '/' or path.find('../') != -1 or path.find('..\\') != -1:
raise 'The PyV8 package contains files outside of the package dir and cannot be safely installed.'
if last_path and len(root_level_paths) == 0:
root_level_paths.append(last_path[0:last_path.find('/') + 1])
prev_dir = os.getcwd()
os.chdir(package_dir)
clean_old_data()
# Here we don't use .extractall() since it was having issues on OS X
skip_root_dir = len(root_level_paths) == 1 and \
root_level_paths[0].endswith('/')
extracted_paths = []
for path in package_zip.namelist():
dest = path
if not is_python3:
try:
if not isinstance(dest, unicode):
dest = unicode(dest, 'utf-8', 'strict')
except UnicodeDecodeError:
dest = unicode(dest, 'cp1252', 'replace')
if os.name == 'nt':
regex = ':|\*|\?|"|<|>|\|'
if re.search(regex, dest) != None:
print ('%s: Skipping file from package named %s due to ' +
'an invalid filename') % (__name__, path)
continue
# If there was only a single directory in the package, we remove
# that folder name from the paths as we extract entries
if skip_root_dir:
dest = dest[len(root_level_paths[0]):]
if os.name == 'nt':
dest = dest.replace('/', '\\')
else:
dest = dest.replace('\\', '/')
dest = os.path.join(package_dir, dest)
def add_extracted_dirs(dir):
while dir not in extracted_paths:
extracted_paths.append(dir)
dir = os.path.dirname(dir)
if dir == package_dir:
break
if path.endswith('/'):
if not os.path.exists(dest):
os.makedirs(dest)
add_extracted_dirs(dest)
else:
dest_dir = os.path.dirname(dest)
if not os.path.exists(dest_dir):
os.makedirs(dest_dir)
add_extracted_dirs(dest_dir)
extracted_paths.append(dest)
try:
open(dest, 'wb').write(package_zip.read(path))
except (IOError, UnicodeDecodeError):
print ('%s: Skipping file from package named %s due to ' +
'an invalid filename') % (__name__, path)
package_zip.close()
os.chdir(prev_dir)
os.remove(f)
class LoaderDelegate():
"""
Abstract class used to display PyV8 binary download progress,
and provide some settings for downloader
"""
def __init__(self, settings={}):
self.settings = settings
def on_start(self, *args, **kwargs):
"Invoked when download process is initiated"
pass
def on_progress(self, *args, **kwargs):
"Invoked on download progress"
pass
def on_complete(self, *args, **kwargs):
"Invoked when download process was finished successfully"
pass
def on_error(self, *args, **kwargs):
"Invoked when error occured during download process"
pass
def setting(self, name, default=None):
"Returns specified setting name"
return self.settings[name] if name in self.settings else default
def log(self, message):
pass
class ThreadProgress():
def __init__(self, thread, delegate, is_background=False):
self.thread = thread
self.delegate = delegate
self.is_background = is_background
self._callbacks = {}
threading.Timer(0, self.run).start()
def run(self):
if not self.thread.is_alive():
if self.thread.exit_code != 0:
return self.trigger('error', exit_code=self.thread.exit_code, progress=self)
return self.trigger('complete', result=self.thread.result, progress=self)
self.trigger('progress', progress=self)
threading.Timer(0.1, self.run).start()
def on(self, event_name, callback):
if event_name not in self._callbacks:
self._callbacks[event_name] = []
if isinstance(callback, collections.Callable):
self._callbacks[event_name].append(callback)
return self
def trigger(self, event_name, *args, **kwargs):
if event_name in self._callbacks:
for c in self._callbacks[event_name]:
c(*args, **kwargs)
if self.delegate and hasattr(self.delegate, 'on_%s' % event_name):
getattr(self.delegate, 'on_%s' % event_name)(*args, **kwargs)
return self
class BinaryNotFoundError(Exception):
pass
class NonCleanExitError(Exception):
def __init__(self, returncode):
self.returncode = returncode
def __str__(self):
return repr(self.returncode)
class CliDownloader():
def __init__(self, settings):
self.settings = settings
def find_binary(self, name):
for dir in os.environ['PATH'].split(os.pathsep):
path = os.path.join(dir, name)
if os.path.exists(path):
return path
raise BinaryNotFoundError('The binary %s could not be located' % name)
def execute(self, args):
proc = subprocess.Popen(args, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
output = proc.stdout.read()
returncode = proc.wait()
if returncode != 0:
error = NonCleanExitError(returncode)
error.output = output
raise error
return output
class WgetDownloader(CliDownloader):
def __init__(self, settings):
self.settings = settings
self.wget = self.find_binary('wget')
def clean_tmp_file(self):
os.remove(self.tmp_file)
def download(self, url, error_message, timeout, tries):
if not self.wget:
return False
self.tmp_file = tempfile.NamedTemporaryFile().name
command = [self.wget, '--connect-timeout=' + str(int(timeout)), '-o',
self.tmp_file, '-O', '-', '-U', 'Emmet PyV8 Loader',
'--no-check-certificate']
command.append(url)
if self.settings.get('http_proxy'):
os.putenv('http_proxy', self.settings.get('http_proxy'))
if not self.settings.get('https_proxy'):
os.putenv('https_proxy', self.settings.get('http_proxy'))
if self.settings.get('https_proxy'):
os.putenv('https_proxy', self.settings.get('https_proxy'))
while tries > 0:
tries -= 1
try:
result = self.execute(command)
self.clean_tmp_file()
return result
except NonCleanExitError as e:
error_line = ''
with open(self.tmp_file) as f:
for line in list(f):
if re.search('ERROR[: ]|failed: ', line):
error_line = line
break
if e.returncode == 8:
regex = re.compile('^.*ERROR (\d+):.*', re.S)
if re.sub(regex, '\\1', error_line) == '503':
# GitHub and BitBucket seem to rate limit via 503
print('%s: Downloading %s was rate limited, trying again' % (__name__, url))
continue
error_string = 'HTTP error ' + re.sub('^.*? ERROR ', '',
error_line)
elif e.returncode == 4:
error_string = re.sub('^.*?failed: ', '', error_line)
# GitHub and BitBucket seem to time out a lot
if error_string.find('timed out') != -1:
print('%s: Downloading %s timed out, trying again' % (__name__, url))
continue
else:
error_string = re.sub('^.*?(ERROR[: ]|failed: )', '\\1',
error_line)
error_string = re.sub('\\.?\s*\n\s*$', '', error_string)
print('%s: %s %s downloading %s.' % (__name__, error_message,
error_string, url))
self.clean_tmp_file()
break
return False
class CurlDownloader(CliDownloader):
def __init__(self, settings):
self.settings = settings
self.curl = self.find_binary('curl')
def download(self, url, error_message, timeout, tries):
if not self.curl:
return False
command = [self.curl, '-f', '--user-agent', 'Emmet PyV8 Loader',
'--connect-timeout', str(int(timeout)), '-sS']
command.append(url)
if self.settings.get('http_proxy'):
os.putenv('http_proxy', self.settings.get('http_proxy'))
if not self.settings.get('https_proxy'):
os.putenv('HTTPS_PROXY', self.settings.get('http_proxy'))
if self.settings.get('https_proxy'):
os.putenv('HTTPS_PROXY', self.settings.get('https_proxy'))
while tries > 0:
tries -= 1
try:
return self.execute(command)
except NonCleanExitError as e:
if e.returncode == 22:
code = re.sub('^.*?(\d+)\s*$', '\\1', e.output)
if code == '503':
# GitHub and BitBucket seem to rate limit via 503
print('%s: Downloading %s was rate limited, trying again' % (__name__, url))
continue
error_string = 'HTTP error ' + code
elif e.returncode == 6:
error_string = 'URL error host not found'
elif e.returncode == 28:
# GitHub and BitBucket seem to time out a lot
print('%s: Downloading %s timed out, trying again' % (__name__, url))
continue
else:
error_string = e.output.rstrip()
print('%s: %s %s downloading %s.' % (__name__, error_message, error_string, url))
break
return False
class UrlLib2Downloader():
def __init__(self, settings):
self.settings = settings
def download(self, url, error_message, timeout, tries):
http_proxy = self.settings.get('http_proxy')
https_proxy = self.settings.get('https_proxy')
if http_proxy or https_proxy:
proxies = {}
if http_proxy:
proxies['http'] = http_proxy
if not https_proxy:
proxies['https'] = http_proxy
if https_proxy:
proxies['https'] = https_proxy
proxy_handler = url_req.ProxyHandler(proxies)
else:
proxy_handler = url_req.ProxyHandler()
handlers = [proxy_handler]
# secure_url_match = re.match('^https://([^/]+)', url)
# if secure_url_match != None:
# secure_domain = secure_url_match.group(1)
# bundle_path = self.check_certs(secure_domain, timeout)
# if not bundle_path:
# return False
# handlers.append(VerifiedHTTPSHandler(ca_certs=bundle_path))
url_req.install_opener(url_req.build_opener(*handlers))
while tries > 0:
tries -= 1
try:
request = url_req.Request(url, headers={"User-Agent":
"Emmet PyV8 Loader"})
http_file = url_req.urlopen(request, timeout=timeout)
return http_file.read()
except url_err.HTTPError as e:
# Bitbucket and Github ratelimit using 503 a decent amount
if str(e.code) == '503':
print('%s: Downloading %s was rate limited, trying again' % (__name__, url))
continue
print('%s: %s HTTP error %s downloading %s.' % (__name__, error_message, str(e.code), url))
except url_err.URLError as e:
# Bitbucket and Github timeout a decent amount
if str(e.reason) == 'The read operation timed out' or \
str(e.reason) == 'timed out':
print('%s: Downloading %s timed out, trying again' % (__name__, url))
continue
print('%s: %s URL error %s downloading %s.' % (__name__, error_message, str(e.reason), url))
break
return False
class PyV8Loader(threading.Thread):
def __init__(self, arch, download_path, config, delegate=None):
self.arch = arch
self.config = config
self.download_path = download_path
self.exit_code = 0
self.result = None
self.delegate = delegate or LoaderDelegate()
self.is_pyv8_thread = True
threading.Thread.__init__(self)
self.delegate.log('Creating thread')
def download_url(self, url, error_message):
# TODO add settings
has_ssl = 'ssl' in sys.modules and hasattr(url_req, 'HTTPSHandler')
is_ssl = re.search('^https://', url) != None
if (is_ssl and has_ssl) or not is_ssl:
downloader = UrlLib2Downloader(self.delegate.settings)
else:
for downloader_class in [CurlDownloader, WgetDownloader]:
try:
downloader = downloader_class(self.delegate.settings)
break
except BinaryNotFoundError:
pass
if not downloader:
self.delegate.log('Unable to download PyV8 binary due to invalid downloader')
return False
timeout = self.delegate.settings.get('timeout', 60)
# timeout = 3
return downloader.download(url.replace(' ', '%20'), error_message, timeout, 3)
def run(self):
# get list of available packages first
self.delegate.log('Loading %s' % PACKAGES_URL)
try:
packages = self.download_url(PACKAGES_URL, 'Unable to download packages list.')
except Exception as e:
self.delegate.log('Unable to download file: %s' % e)
self.exit_code = 4
return
if not packages:
self.exit_code = 1
return
if isinstance(packages, bytes):
packages = packages.decode('utf-8')
files = json.loads(packages)
# find package for current architecture
cur_item = None
bundle_name = 'pyv8-%s.zip' % self.arch
for item in files:
if bundle_name == item['name']:
cur_item = item
break
if not cur_item:
self.delegate.log('Unable to find binary for %s architecture' % self.arch)
self.exit_code = 2
return
if cur_item['sha'] == self.config['last_id']:
self.delegate.log('You have the most recent PyV8 binary')
return
url = 'https://raw.github.com/emmetio/pyv8-binaries/master/%s' % cur_item['name']
self.delegate.log('Loading PyV8 binary from %s' % url)
package = self.download_url(url, 'Unable to download package from %s' % url)
if not package:
self.exit_code = 3
return
# we should only save downloaded package and delegate module
# loading/unloading to main thread since improper PyV8 unload
# may cause editor crash
try:
os.makedirs(self.download_path)
except Exception as e:
pass
fp = open(os.path.join(self.download_path, 'pack.zip'), 'wb')
fp.write(package)
fp.close()
self.result = cur_item['sha']
# Done!

View File

@@ -0,0 +1,25 @@
import sys
import imp
# Dependecy reloader for Emmet plugin
# The original idea is borrowed from
# https://github.com/wbond/sublime_package_control/blob/master/package_control/reloader.py
reload_mods = []
for mod in sys.modules:
if mod.startswith('emmet') and sys.modules[mod] != None:
reload_mods.append(mod)
mods_load_order = [
'emmet.semver',
'emmet.pyv8loader',
'emmet_completions.trackers',
'emmet_completions.meta',
'emmet_completions',
'emmet.file',
'emmet.context'
]
for mod in mods_load_order:
if mod in reload_mods:
imp.reload(sys.modules[mod])

View File

@@ -0,0 +1,83 @@
# -*- coding: utf-8 -*-
import re
_REGEX = re.compile('^(?P<major>[0-9]+)'
'\.(?P<minor>[0-9]+)'
'\.(?P<patch>[0-9]+)'
'(\-(?P<prerelease>[0-9A-Za-z]+(\.[0-9A-Za-z]+)*))?'
'(\+(?P<build>[0-9A-Za-z]+(\.[0-9A-Za-z]+)*))?$')
if 'cmp' not in __builtins__:
cmp = lambda a,b: (a > b) - (a < b)
def parse(version):
"""
Parse version to major, minor, patch, pre-release, build parts.
"""
match = _REGEX.match(version)
if match is None:
raise ValueError('%s is not valid SemVer string' % version)
verinfo = match.groupdict()
verinfo['major'] = int(verinfo['major'])
verinfo['minor'] = int(verinfo['minor'])
verinfo['patch'] = int(verinfo['patch'])
return verinfo
def compare(ver1, ver2):
def nat_cmp(a, b):
a, b = a or '', b or ''
convert = lambda text: text.isdigit() and int(text) or text.lower()
alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)]
return cmp(alphanum_key(a), alphanum_key(b))
def compare_by_keys(d1, d2):
for key in ['major', 'minor', 'patch']:
v = cmp(d1.get(key), d2.get(key))
if v:
return v
rc1, rc2 = d1.get('prerelease'), d2.get('prerelease')
build1, build2 = d1.get('build'), d2.get('build')
rccmp = nat_cmp(rc1, rc2)
buildcmp = nat_cmp(build1, build2)
if not (rc1 or rc2):
return buildcmp
elif not rc1:
return 1
elif not rc2:
return -1
return rccmp or buildcmp or 0
v1, v2 = parse(ver1), parse(ver2)
return compare_by_keys(v1, v2)
def match(version, match_expr):
prefix = match_expr[:2]
if prefix in ('>=', '<=', '=='):
match_version = match_expr[2:]
elif prefix and prefix[0] in ('>', '<', '='):
prefix = prefix[0]
match_version = match_expr[1:]
else:
raise ValueError("match_expr parameter should be in format <op><ver>, "
"where <op> is one of ['<', '>', '==', '<=', '>=']. "
"You provided: %r" % match_expr)
possibilities_dict = {
'>': (1,),
'<': (-1,),
'==': (0,),
'>=': (0, 1),
'<=': (-1, 0)
}
possibilities = possibilities_dict[prefix]
cmp_res = compare(version, match_version)
return cmp_res in possibilities

View File

@@ -0,0 +1,846 @@
{
"variables": {
"lang": "en",
"locale": "en-US",
"charset": "UTF-8",
"indentation": "\t",
"newline": "\n"
},
"css": {
"filters": "html",
"snippets": {
"@i": "@import url(|);",
"@import": "@import url(|);",
"@m": "@media ${1:screen} {\n\t|\n}",
"@media": "@media ${1:screen} {\n\t|\n}",
"@f": "@font-face {\n\tfont-family:|;\n\tsrc:url(|);\n}",
"@f+": "@font-face {\n\tfont-family: '${1:FontName}';\n\tsrc: url('${2:FileName}.eot');\n\tsrc: url('${2:FileName}.eot?#iefix') format('embedded-opentype'),\n\t\t url('${2:FileName}.woff') format('woff'),\n\t\t url('${2:FileName}.ttf') format('truetype'),\n\t\t url('${2:FileName}.svg#${1:FontName}') format('svg');\n\tfont-style: ${3:normal};\n\tfont-weight: ${4:normal};\n}",
"@kf": "@-webkit-keyframes ${1:identifier} {\n\t${2:from} { ${3} }${6}\n\t${4:to} { ${5} }\n}\n@-o-keyframes ${1:identifier} {\n\t${2:from} { ${3} }${6}\n\t${4:to} { ${5} }\n}\n@-moz-keyframes ${1:identifier} {\n\t${2:from} { ${3} }${6}\n\t${4:to} { ${5} }\n}\n@keyframes ${1:identifier} {\n\t${2:from} { ${3} }${6}\n\t${4:to} { ${5} }\n}",
"anim": "animation:|;",
"anim-": "animation:${1:name} ${2:duration} ${3:timing-function} ${4:delay} ${5:iteration-count} ${6:direction} ${7:fill-mode};",
"animdel": "animation-delay:${1:time};",
"animdir": "animation-direction:${1:normal};",
"animdir:n": "animation-direction:normal;",
"animdir:r": "animation-direction:reverse;",
"animdir:a": "animation-direction:alternate;",
"animdir:ar": "animation-direction:alternate-reverse;",
"animdur": "animation-duration:${1:0}s;",
"animfm": "animation-fill-mode:${1:both};",
"animfm:f": "animation-fill-mode:forwards;",
"animfm:b": "animation-fill-mode:backwards;",
"animfm:bt": "animation-fill-mode:both;",
"animfm:bh": "animation-fill-mode:both;",
"animic": "animation-iteration-count:${1:1};",
"animic:i": "animation-iteration-count:infinite;",
"animn": "animation-name:${1:none};",
"animps": "animation-play-state:${1:running};",
"animps:p": "animation-play-state:paused;",
"animps:r": "animation-play-state:running;",
"animtf": "animation-timing-function:${1:linear};",
"animtf:e": "animation-timing-function:ease;",
"animtf:ei": "animation-timing-function:ease-in;",
"animtf:eo": "animation-timing-function:ease-out;",
"animtf:eio": "animation-timing-function:ease-in-out;",
"animtf:l": "animation-timing-function:linear;",
"animtf:cb": "animation-timing-function:cubic-bezier(${1:0.1}, ${2:0.7}, ${3:1.0}, ${3:0.1});",
"ap": "appearance:${none};",
"!": "!important",
"pos": "position:${1:relative};",
"pos:s": "position:static;",
"pos:a": "position:absolute;",
"pos:r": "position:relative;",
"pos:f": "position:fixed;",
"t": "top:|;",
"t:a": "top:auto;",
"r": "right:|;",
"r:a": "right:auto;",
"b": "bottom:|;",
"b:a": "bottom:auto;",
"l": "left:|;",
"l:a": "left:auto;",
"z": "z-index:|;",
"z:a": "z-index:auto;",
"fl": "float:${1:left};",
"fl:n": "float:none;",
"fl:l": "float:left;",
"fl:r": "float:right;",
"cl": "clear:${1:both};",
"cl:n": "clear:none;",
"cl:l": "clear:left;",
"cl:r": "clear:right;",
"cl:b": "clear:both;",
"colm": "columns:|;",
"colmc": "column-count:|;",
"colmf": "column-fill:|;",
"colmg": "column-gap:|;",
"colmr": "column-rule:|;",
"colmrc": "column-rule-color:|;",
"colmrs": "column-rule-style:|;",
"colmrw": "column-rule-width:|;",
"colms": "column-span:|;",
"colmw": "column-width:|;",
"d": "display:${1:block};",
"d:n": "display:none;",
"d:b": "display:block;",
"d:i": "display:inline;",
"d:ib": "display:inline-block;",
"d:li": "display:list-item;",
"d:ri": "display:run-in;",
"d:cp": "display:compact;",
"d:tb": "display:table;",
"d:itb": "display:inline-table;",
"d:tbcp": "display:table-caption;",
"d:tbcl": "display:table-column;",
"d:tbclg": "display:table-column-group;",
"d:tbhg": "display:table-header-group;",
"d:tbfg": "display:table-footer-group;",
"d:tbr": "display:table-row;",
"d:tbrg": "display:table-row-group;",
"d:tbc": "display:table-cell;",
"d:rb": "display:ruby;",
"d:rbb": "display:ruby-base;",
"d:rbbg": "display:ruby-base-group;",
"d:rbt": "display:ruby-text;",
"d:rbtg": "display:ruby-text-group;",
"v": "visibility:${1:hidden};",
"v:v": "visibility:visible;",
"v:h": "visibility:hidden;",
"v:c": "visibility:collapse;",
"ov": "overflow:${1:hidden};",
"ov:v": "overflow:visible;",
"ov:h": "overflow:hidden;",
"ov:s": "overflow:scroll;",
"ov:a": "overflow:auto;",
"ovx": "overflow-x:${1:hidden};",
"ovx:v": "overflow-x:visible;",
"ovx:h": "overflow-x:hidden;",
"ovx:s": "overflow-x:scroll;",
"ovx:a": "overflow-x:auto;",
"ovy": "overflow-y:${1:hidden};",
"ovy:v": "overflow-y:visible;",
"ovy:h": "overflow-y:hidden;",
"ovy:s": "overflow-y:scroll;",
"ovy:a": "overflow-y:auto;",
"ovs": "overflow-style:${1:scrollbar};",
"ovs:a": "overflow-style:auto;",
"ovs:s": "overflow-style:scrollbar;",
"ovs:p": "overflow-style:panner;",
"ovs:m": "overflow-style:move;",
"ovs:mq": "overflow-style:marquee;",
"zoo": "zoom:1;",
"zm": "zoom:1;",
"cp": "clip:|;",
"cp:a": "clip:auto;",
"cp:r": "clip:rect(${1:top} ${2:right} ${3:bottom} ${4:left});",
"bxz": "box-sizing:${1:border-box};",
"bxz:cb": "box-sizing:content-box;",
"bxz:bb": "box-sizing:border-box;",
"bxsh": "box-shadow:${1:inset }${2:hoff} ${3:voff} ${4:blur} ${5:color};",
"bxsh:r": "box-shadow:${1:inset }${2:hoff} ${3:voff} ${4:blur} ${5:spread }rgb(${6:0}, ${7:0}, ${8:0});",
"bxsh:ra": "box-shadow:${1:inset }${2:h} ${3:v} ${4:blur} ${5:spread }rgba(${6:0}, ${7:0}, ${8:0}, .${9:5});",
"bxsh:n": "box-shadow:none;",
"m": "margin:|;",
"m:a": "margin:auto;",
"mt": "margin-top:|;",
"mt:a": "margin-top:auto;",
"mr": "margin-right:|;",
"mr:a": "margin-right:auto;",
"mb": "margin-bottom:|;",
"mb:a": "margin-bottom:auto;",
"ml": "margin-left:|;",
"ml:a": "margin-left:auto;",
"p": "padding:|;",
"pt": "padding-top:|;",
"pr": "padding-right:|;",
"pb": "padding-bottom:|;",
"pl": "padding-left:|;",
"w": "width:|;",
"w:a": "width:auto;",
"h": "height:|;",
"h:a": "height:auto;",
"maw": "max-width:|;",
"maw:n": "max-width:none;",
"mah": "max-height:|;",
"mah:n": "max-height:none;",
"miw": "min-width:|;",
"mih": "min-height:|;",
"mar": "max-resolution:${1:res};",
"mir": "min-resolution:${1:res};",
"ori": "orientation:|;",
"ori:l": "orientation:landscape;",
"ori:p": "orientation:portrait;",
"ol": "outline:|;",
"ol:n": "outline:none;",
"olo": "outline-offset:|;",
"olw": "outline-width:|;",
"ols": "outline-style:|;",
"olc": "outline-color:#${1:000};",
"olc:i": "outline-color:invert;",
"bd": "border:|;",
"bd+": "border:${1:1px} ${2:solid} ${3:#000};",
"bd:n": "border:none;",
"bdbk": "border-break:${1:close};",
"bdbk:c": "border-break:close;",
"bdcl": "border-collapse:|;",
"bdcl:c": "border-collapse:collapse;",
"bdcl:s": "border-collapse:separate;",
"bdc": "border-color:#${1:000};",
"bdc:t": "border-color:transparent;",
"bdi": "border-image:url(|);",
"bdi:n": "border-image:none;",
"bdti": "border-top-image:url(|);",
"bdti:n": "border-top-image:none;",
"bdri": "border-right-image:url(|);",
"bdri:n": "border-right-image:none;",
"bdbi": "border-bottom-image:url(|);",
"bdbi:n": "border-bottom-image:none;",
"bdli": "border-left-image:url(|);",
"bdli:n": "border-left-image:none;",
"bdci": "border-corner-image:url(|);",
"bdci:n": "border-corner-image:none;",
"bdci:c": "border-corner-image:continue;",
"bdtli": "border-top-left-image:url(|);",
"bdtli:n": "border-top-left-image:none;",
"bdtli:c": "border-top-left-image:continue;",
"bdtri": "border-top-right-image:url(|);",
"bdtri:n": "border-top-right-image:none;",
"bdtri:c": "border-top-right-image:continue;",
"bdbri": "border-bottom-right-image:url(|);",
"bdbri:n": "border-bottom-right-image:none;",
"bdbri:c": "border-bottom-right-image:continue;",
"bdbli": "border-bottom-left-image:url(|);",
"bdbli:n": "border-bottom-left-image:none;",
"bdbli:c": "border-bottom-left-image:continue;",
"bdf": "border-fit:${1:repeat};",
"bdf:c": "border-fit:clip;",
"bdf:r": "border-fit:repeat;",
"bdf:sc": "border-fit:scale;",
"bdf:st": "border-fit:stretch;",
"bdf:ow": "border-fit:overwrite;",
"bdf:of": "border-fit:overflow;",
"bdf:sp": "border-fit:space;",
"bdlen": "border-length:|;",
"bdlen:a": "border-length:auto;",
"bdsp": "border-spacing:|;",
"bds": "border-style:|;",
"bds:n": "border-style:none;",
"bds:h": "border-style:hidden;",
"bds:dt": "border-style:dotted;",
"bds:ds": "border-style:dashed;",
"bds:s": "border-style:solid;",
"bds:db": "border-style:double;",
"bds:dtds": "border-style:dot-dash;",
"bds:dtdtds": "border-style:dot-dot-dash;",
"bds:w": "border-style:wave;",
"bds:g": "border-style:groove;",
"bds:r": "border-style:ridge;",
"bds:i": "border-style:inset;",
"bds:o": "border-style:outset;",
"bdw": "border-width:|;",
"bdtw": "border-top-width:|;",
"bdrw": "border-right-width:|;",
"bdbw": "border-bottom-width:|;",
"bdlw": "border-left-width:|;",
"bdt": "border-top:|;",
"bt": "border-top:|;",
"bdt+": "border-top:${1:1px} ${2:solid} ${3:#000};",
"bdt:n": "border-top:none;",
"bdts": "border-top-style:|;",
"bdts:n": "border-top-style:none;",
"bdtc": "border-top-color:#${1:000};",
"bdtc:t": "border-top-color:transparent;",
"bdr": "border-right:|;",
"br": "border-right:|;",
"bdr+": "border-right:${1:1px} ${2:solid} ${3:#000};",
"bdr:n": "border-right:none;",
"bdrst": "border-right-style:|;",
"bdrst:n": "border-right-style:none;",
"bdrc": "border-right-color:#${1:000};",
"bdrc:t": "border-right-color:transparent;",
"bdb": "border-bottom:|;",
"bb": "border-bottom:|;",
"bdb+": "border-bottom:${1:1px} ${2:solid} ${3:#000};",
"bdb:n": "border-bottom:none;",
"bdbs": "border-bottom-style:|;",
"bdbs:n": "border-bottom-style:none;",
"bdbc": "border-bottom-color:#${1:000};",
"bdbc:t": "border-bottom-color:transparent;",
"bdl": "border-left:|;",
"bl": "border-left:|;",
"bdl+": "border-left:${1:1px} ${2:solid} ${3:#000};",
"bdl:n": "border-left:none;",
"bdls": "border-left-style:|;",
"bdls:n": "border-left-style:none;",
"bdlc": "border-left-color:#${1:000};",
"bdlc:t": "border-left-color:transparent;",
"bdrs": "border-radius:|;",
"bdtrrs": "border-top-right-radius:|;",
"bdtlrs": "border-top-left-radius:|;",
"bdbrrs": "border-bottom-right-radius:|;",
"bdblrs": "border-bottom-left-radius:|;",
"bg": "background:|;",
"bg+": "background:${1:#fff} url(${2}) ${3:0} ${4:0} ${5:no-repeat};",
"bg:n": "background:none;",
"bg:ie": "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='${1:x}.png',sizingMethod='${2:crop}');",
"bgc": "background-color:#${1:fff};",
"bgc:t": "background-color:transparent;",
"bgi": "background-image:url(|);",
"bgi:n": "background-image:none;",
"bgr": "background-repeat:|;",
"bgr:n": "background-repeat:no-repeat;",
"bgr:x": "background-repeat:repeat-x;",
"bgr:y": "background-repeat:repeat-y;",
"bgr:sp": "background-repeat:space;",
"bgr:rd": "background-repeat:round;",
"bga": "background-attachment:|;",
"bga:f": "background-attachment:fixed;",
"bga:s": "background-attachment:scroll;",
"bgp": "background-position:${1:0} ${2:0};",
"bgpx": "background-position-x:|;",
"bgpy": "background-position-y:|;",
"bgbk": "background-break:|;",
"bgbk:bb": "background-break:bounding-box;",
"bgbk:eb": "background-break:each-box;",
"bgbk:c": "background-break:continuous;",
"bgcp": "background-clip:${1:padding-box};",
"bgcp:bb": "background-clip:border-box;",
"bgcp:pb": "background-clip:padding-box;",
"bgcp:cb": "background-clip:content-box;",
"bgcp:nc": "background-clip:no-clip;",
"bgo": "background-origin:|;",
"bgo:pb": "background-origin:padding-box;",
"bgo:bb": "background-origin:border-box;",
"bgo:cb": "background-origin:content-box;",
"bgsz": "background-size:|;",
"bgsz:a": "background-size:auto;",
"bgsz:ct": "background-size:contain;",
"bgsz:cv": "background-size:cover;",
"c": "color:#${1:000};",
"c:r": "color:rgb(${1:0}, ${2:0}, ${3:0});",
"c:ra": "color:rgba(${1:0}, ${2:0}, ${3:0}, .${4:5});",
"cm": "/* |${child} */",
"cnt": "content:'|';",
"cnt:n": "content:normal;",
"cnt:oq": "content:open-quote;",
"cnt:noq": "content:no-open-quote;",
"cnt:cq": "content:close-quote;",
"cnt:ncq": "content:no-close-quote;",
"cnt:a": "content:attr(|);",
"cnt:c": "content:counter(|);",
"cnt:cs": "content:counters(|);",
"tbl": "table-layout:|;",
"tbl:a": "table-layout:auto;",
"tbl:f": "table-layout:fixed;",
"cps": "caption-side:|;",
"cps:t": "caption-side:top;",
"cps:b": "caption-side:bottom;",
"ec": "empty-cells:|;",
"ec:s": "empty-cells:show;",
"ec:h": "empty-cells:hide;",
"lis": "list-style:|;",
"lis:n": "list-style:none;",
"lisp": "list-style-position:|;",
"lisp:i": "list-style-position:inside;",
"lisp:o": "list-style-position:outside;",
"list": "list-style-type:|;",
"list:n": "list-style-type:none;",
"list:d": "list-style-type:disc;",
"list:c": "list-style-type:circle;",
"list:s": "list-style-type:square;",
"list:dc": "list-style-type:decimal;",
"list:dclz": "list-style-type:decimal-leading-zero;",
"list:lr": "list-style-type:lower-roman;",
"list:ur": "list-style-type:upper-roman;",
"lisi": "list-style-image:|;",
"lisi:n": "list-style-image:none;",
"q": "quotes:|;",
"q:n": "quotes:none;",
"q:ru": "quotes:'\\00AB' '\\00BB' '\\201E' '\\201C';",
"q:en": "quotes:'\\201C' '\\201D' '\\2018' '\\2019';",
"ct": "content:|;",
"ct:n": "content:normal;",
"ct:oq": "content:open-quote;",
"ct:noq": "content:no-open-quote;",
"ct:cq": "content:close-quote;",
"ct:ncq": "content:no-close-quote;",
"ct:a": "content:attr(|);",
"ct:c": "content:counter(|);",
"ct:cs": "content:counters(|);",
"coi": "counter-increment:|;",
"cor": "counter-reset:|;",
"va": "vertical-align:${1:top};",
"va:sup": "vertical-align:super;",
"va:t": "vertical-align:top;",
"va:tt": "vertical-align:text-top;",
"va:m": "vertical-align:middle;",
"va:bl": "vertical-align:baseline;",
"va:b": "vertical-align:bottom;",
"va:tb": "vertical-align:text-bottom;",
"va:sub": "vertical-align:sub;",
"ta": "text-align:${1:left};",
"ta:l": "text-align:left;",
"ta:c": "text-align:center;",
"ta:r": "text-align:right;",
"ta:j": "text-align:justify;",
"ta-lst": "text-align-last:|;",
"tal:a": "text-align-last:auto;",
"tal:l": "text-align-last:left;",
"tal:c": "text-align-last:center;",
"tal:r": "text-align-last:right;",
"td": "text-decoration:${1:none};",
"td:n": "text-decoration:none;",
"td:u": "text-decoration:underline;",
"td:o": "text-decoration:overline;",
"td:l": "text-decoration:line-through;",
"te": "text-emphasis:|;",
"te:n": "text-emphasis:none;",
"te:ac": "text-emphasis:accent;",
"te:dt": "text-emphasis:dot;",
"te:c": "text-emphasis:circle;",
"te:ds": "text-emphasis:disc;",
"te:b": "text-emphasis:before;",
"te:a": "text-emphasis:after;",
"th": "text-height:|;",
"th:a": "text-height:auto;",
"th:f": "text-height:font-size;",
"th:t": "text-height:text-size;",
"th:m": "text-height:max-size;",
"ti": "text-indent:|;",
"ti:-": "text-indent:-9999px;",
"tj": "text-justify:|;",
"tj:a": "text-justify:auto;",
"tj:iw": "text-justify:inter-word;",
"tj:ii": "text-justify:inter-ideograph;",
"tj:ic": "text-justify:inter-cluster;",
"tj:d": "text-justify:distribute;",
"tj:k": "text-justify:kashida;",
"tj:t": "text-justify:tibetan;",
"tov": "text-overflow:${ellipsis};",
"tov:e": "text-overflow:ellipsis;",
"tov:c": "text-overflow:clip;",
"to": "text-outline:|;",
"to+": "text-outline:${1:0} ${2:0} ${3:#000};",
"to:n": "text-outline:none;",
"tr": "text-replace:|;",
"tr:n": "text-replace:none;",
"tt": "text-transform:${1:uppercase};",
"tt:n": "text-transform:none;",
"tt:c": "text-transform:capitalize;",
"tt:u": "text-transform:uppercase;",
"tt:l": "text-transform:lowercase;",
"tw": "text-wrap:|;",
"tw:n": "text-wrap:normal;",
"tw:no": "text-wrap:none;",
"tw:u": "text-wrap:unrestricted;",
"tw:s": "text-wrap:suppress;",
"tsh": "text-shadow:${1:hoff} ${2:voff} ${3:blur} ${4:#000};",
"tsh:r": "text-shadow:${1:h} ${2:v} ${3:blur} rgb(${4:0}, ${5:0}, ${6:0});",
"tsh:ra": "text-shadow:${1:h} ${2:v} ${3:blur} rgba(${4:0}, ${5:0}, ${6:0}, .${7:5});",
"tsh+": "text-shadow:${1:0} ${2:0} ${3:0} ${4:#000};",
"tsh:n": "text-shadow:none;",
"trf": "transform:|;",
"trf:skx": "transform: skewX(${1:angle});",
"trf:sky": "transform: skewY(${1:angle});",
"trf:sc": "transform: scale(${1:x}, ${2:y});",
"trf:scx": "transform: scaleX(${1:x});",
"trf:scy": "transform: scaleY(${1:y});",
"trf:r": "transform: rotate(${1:angle});",
"trf:t": "transform: translate(${1:x}, ${2:y});",
"trf:tx": "transform: translateX(${1:x});",
"trf:ty": "transform: translateY(${1:y});",
"trfo": "transform-origin:|;",
"trfs": "transform-style:${1:preserve-3d};",
"trs": "transition:${1:prop} ${2:time};",
"trsde": "transition-delay:${1:time};",
"trsdu": "transition-duration:${1:time};",
"trsp": "transition-property:${1:prop};",
"trstf": "transition-timing-function:${1:tfunc};",
"lh": "line-height:|;",
"whs": "white-space:|;",
"whs:n": "white-space:normal;",
"whs:p": "white-space:pre;",
"whs:nw": "white-space:nowrap;",
"whs:pw": "white-space:pre-wrap;",
"whs:pl": "white-space:pre-line;",
"whsc": "white-space-collapse:|;",
"whsc:n": "white-space-collapse:normal;",
"whsc:k": "white-space-collapse:keep-all;",
"whsc:l": "white-space-collapse:loose;",
"whsc:bs": "white-space-collapse:break-strict;",
"whsc:ba": "white-space-collapse:break-all;",
"wob": "word-break:|;",
"wob:n": "word-break:normal;",
"wob:k": "word-break:keep-all;",
"wob:l": "word-break:loose;",
"wob:bs": "word-break:break-strict;",
"wob:ba": "word-break:break-all;",
"wos": "word-spacing:|;",
"wow": "word-wrap:|;",
"wow:nm": "word-wrap:normal;",
"wow:n": "word-wrap:none;",
"wow:u": "word-wrap:unrestricted;",
"wow:s": "word-wrap:suppress;",
"lts": "letter-spacing:|;",
"f": "font:|;",
"f+": "font:${1:1em} ${2:Arial,sans-serif};",
"fw": "font-weight:|;",
"fw:n": "font-weight:normal;",
"fw:b": "font-weight:bold;",
"fw:br": "font-weight:bolder;",
"fw:lr": "font-weight:lighter;",
"fs": "font-style:${italic};",
"fs:n": "font-style:normal;",
"fs:i": "font-style:italic;",
"fs:o": "font-style:oblique;",
"fv": "font-variant:|;",
"fv:n": "font-variant:normal;",
"fv:sc": "font-variant:small-caps;",
"fz": "font-size:|;",
"fza": "font-size-adjust:|;",
"fza:n": "font-size-adjust:none;",
"ff": "font-family:|;",
"ff:s": "font-family:serif;",
"ff:ss": "font-family:sans-serif;",
"ff:c": "font-family:cursive;",
"ff:f": "font-family:fantasy;",
"ff:m": "font-family:monospace;",
"fef": "font-effect:|;",
"fef:n": "font-effect:none;",
"fef:eg": "font-effect:engrave;",
"fef:eb": "font-effect:emboss;",
"fef:o": "font-effect:outline;",
"fem": "font-emphasize:|;",
"femp": "font-emphasize-position:|;",
"femp:b": "font-emphasize-position:before;",
"femp:a": "font-emphasize-position:after;",
"fems": "font-emphasize-style:|;",
"fems:n": "font-emphasize-style:none;",
"fems:ac": "font-emphasize-style:accent;",
"fems:dt": "font-emphasize-style:dot;",
"fems:c": "font-emphasize-style:circle;",
"fems:ds": "font-emphasize-style:disc;",
"fsm": "font-smooth:|;",
"fsm:a": "font-smooth:auto;",
"fsm:n": "font-smooth:never;",
"fsm:aw": "font-smooth:always;",
"fst": "font-stretch:|;",
"fst:n": "font-stretch:normal;",
"fst:uc": "font-stretch:ultra-condensed;",
"fst:ec": "font-stretch:extra-condensed;",
"fst:c": "font-stretch:condensed;",
"fst:sc": "font-stretch:semi-condensed;",
"fst:se": "font-stretch:semi-expanded;",
"fst:e": "font-stretch:expanded;",
"fst:ee": "font-stretch:extra-expanded;",
"fst:ue": "font-stretch:ultra-expanded;",
"op": "opacity:|;",
"op:ie": "filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100);",
"op:ms": "-ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)';",
"rsz": "resize:|;",
"rsz:n": "resize:none;",
"rsz:b": "resize:both;",
"rsz:h": "resize:horizontal;",
"rsz:v": "resize:vertical;",
"cur": "cursor:${pointer};",
"cur:a": "cursor:auto;",
"cur:d": "cursor:default;",
"cur:c": "cursor:crosshair;",
"cur:ha": "cursor:hand;",
"cur:he": "cursor:help;",
"cur:m": "cursor:move;",
"cur:p": "cursor:pointer;",
"cur:t": "cursor:text;",
"pgbb": "page-break-before:|;",
"pgbb:au": "page-break-before:auto;",
"pgbb:al": "page-break-before:always;",
"pgbb:l": "page-break-before:left;",
"pgbb:r": "page-break-before:right;",
"pgbi": "page-break-inside:|;",
"pgbi:au": "page-break-inside:auto;",
"pgbi:av": "page-break-inside:avoid;",
"pgba": "page-break-after:|;",
"pgba:au": "page-break-after:auto;",
"pgba:al": "page-break-after:always;",
"pgba:l": "page-break-after:left;",
"pgba:r": "page-break-after:right;",
"orp": "orphans:|;",
"us": "user-select:${none};",
"wid": "widows:|;",
"wfsm": "-webkit-font-smoothing:${antialiased};",
"wfsm:a": "-webkit-font-smoothing:antialiased;",
"wfsm:s": "-webkit-font-smoothing:subpixel-antialiased;",
"wfsm:sa": "-webkit-font-smoothing:subpixel-antialiased;",
"wfsm:n": "-webkit-font-smoothing:none;"
}
},
"html": {
"filters": "html",
"profile": "html",
"snippets": {
"!!!": "<!doctype html>",
"!!!4t": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">",
"!!!4s": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">",
"!!!xt": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">",
"!!!xs": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">",
"!!!xxs": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">",
"c": "<!-- |${child} -->",
"cc:ie6": "<!--[if lte IE 6]>\n\t${child}|\n<![endif]-->",
"cc:ie": "<!--[if IE]>\n\t${child}|\n<![endif]-->",
"cc:noie": "<!--[if !IE]><!-->\n\t${child}|\n<!--<![endif]-->"
},
"abbreviations": {
"!": "html:5",
"a": "<a href=\"\">",
"a:link": "<a href=\"http://|\">",
"a:mail": "<a href=\"mailto:|\">",
"abbr": "<abbr title=\"\">",
"acronym": "<acronym title=\"\">",
"base": "<base href=\"\" />",
"basefont": "<basefont/>",
"br": "<br/>",
"frame": "<frame/>",
"hr": "<hr/>",
"bdo": "<bdo dir=\"\">",
"bdo:r": "<bdo dir=\"rtl\">",
"bdo:l": "<bdo dir=\"ltr\">",
"col": "<col/>",
"link": "<link rel=\"stylesheet\" href=\"\" />",
"link:css": "<link rel=\"stylesheet\" href=\"${1:style}.css\" />",
"link:print": "<link rel=\"stylesheet\" href=\"${1:print}.css\" media=\"print\" />",
"link:favicon": "<link rel=\"shortcut icon\" type=\"image/x-icon\" href=\"${1:favicon.ico}\" />",
"link:touch": "<link rel=\"apple-touch-icon\" href=\"${1:favicon.png}\" />",
"link:rss": "<link rel=\"alternate\" type=\"application/rss+xml\" title=\"RSS\" href=\"${1:rss.xml}\" />",
"link:atom": "<link rel=\"alternate\" type=\"application/atom+xml\" title=\"Atom\" href=\"${1:atom.xml}\" />",
"meta": "<meta/>",
"meta:utf": "<meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\" />",
"meta:win": "<meta http-equiv=\"Content-Type\" content=\"text/html;charset=windows-1251\" />",
"meta:vp": "<meta name=\"viewport\" content=\"width=${1:device-width}, user-scalable=${2:no}, initial-scale=${3:1.0}, maximum-scale=${4:1.0}, minimum-scale=${5:1.0}\" />",
"meta:compat": "<meta http-equiv=\"X-UA-Compatible\" content=\"${1:IE=7}\" />",
"style": "<style>",
"script": "<script>",
"script:src": "<script src=\"\">",
"img": "<img src=\"\" alt=\"\" />",
"iframe": "<iframe src=\"\" frameborder=\"0\">",
"embed": "<embed src=\"\" type=\"\" />",
"object": "<object data=\"\" type=\"\">",
"param": "<param name=\"\" value=\"\" />",
"map": "<map name=\"\">",
"area": "<area shape=\"\" coords=\"\" href=\"\" alt=\"\" />",
"area:d": "<area shape=\"default\" href=\"\" alt=\"\" />",
"area:c": "<area shape=\"circle\" coords=\"\" href=\"\" alt=\"\" />",
"area:r": "<area shape=\"rect\" coords=\"\" href=\"\" alt=\"\" />",
"area:p": "<area shape=\"poly\" coords=\"\" href=\"\" alt=\"\" />",
"form": "<form action=\"\">",
"form:get": "<form action=\"\" method=\"get\">",
"form:post": "<form action=\"\" method=\"post\">",
"label": "<label for=\"\">",
"input": "<input type=\"${1:text}\" />",
"inp": "<input type=\"${1:text}\" name=\"\" id=\"\" />",
"input:hidden": "input[type=hidden name]",
"input:h": "input:hidden",
"input:text": "inp",
"input:t": "inp",
"input:search": "inp[type=search]",
"input:email": "inp[type=email]",
"input:url": "inp[type=url]",
"input:password": "inp[type=password]",
"input:p": "input:password",
"input:datetime": "inp[type=datetime]",
"input:date": "inp[type=date]",
"input:datetime-local": "inp[type=datetime-local]",
"input:month": "inp[type=month]",
"input:week": "inp[type=week]",
"input:time": "inp[type=time]",
"input:number": "inp[type=number]",
"input:color": "inp[type=color]",
"input:checkbox": "inp[type=checkbox]",
"input:c": "input:checkbox",
"input:radio": "inp[type=radio]",
"input:r": "input:radio",
"input:range": "inp[type=range]",
"input:file": "inp[type=file]",
"input:f": "input:file",
"input:submit": "<input type=\"submit\" value=\"\" />",
"input:s": "input:submit",
"input:image": "<input type=\"image\" src=\"\" alt=\"\" />",
"input:i": "input:image",
"input:button": "<input type=\"button\" value=\"\" />",
"input:b": "input:button",
"isindex": "<isindex/>",
"input:reset": "input:button[type=reset]",
"select": "<select name=\"\" id=\"\">",
"option": "<option value=\"\">",
"textarea": "<textarea name=\"\" id=\"\" cols=\"${1:30}\" rows=\"${2:10}\">",
"menu:context": "menu[type=context]>",
"menu:c": "menu:context",
"menu:toolbar": "menu[type=toolbar]>",
"menu:t": "menu:toolbar",
"video": "<video src=\"\">",
"audio": "<audio src=\"\">",
"html:xml": "<html xmlns=\"http://www.w3.org/1999/xhtml\">",
"keygen": "<keygen/>",
"command": "<command/>",
"bq": "blockquote",
"acr": "acronym",
"fig": "figure",
"figc": "figcaption",
"ifr": "iframe",
"emb": "embed",
"obj": "object",
"src": "source",
"cap": "caption",
"colg": "colgroup",
"fst": "fieldset",
"btn": "button",
"btn:b": "button[type=button]",
"btn:r": "button[type=reset]",
"btn:s": "button[type=submit]",
"optg": "optgroup",
"opt": "option",
"tarea": "textarea",
"leg": "legend",
"sect": "section",
"art": "article",
"hdr": "header",
"ftr": "footer",
"adr": "address",
"dlg": "dialog",
"str": "strong",
"prog": "progress",
"fset": "fieldset",
"datag": "datagrid",
"datal": "datalist",
"kg": "keygen",
"out": "output",
"det": "details",
"cmd": "command",
"doc": "html>(head>meta[charset=UTF-8]+title{${1:Document}})+body",
"doc4": "html>(head>meta[http-equiv=\"Content-Type\" content=\"text/html;charset=${charset}\"]+title{${1:Document}})+body",
"html:4t": "!!!4t+doc4[lang=${lang}]",
"html:4s": "!!!4s+doc4[lang=${lang}]",
"html:xt": "!!!xt+doc4[xmlns=http://www.w3.org/1999/xhtml xml:lang=${lang}]",
"html:xs": "!!!xs+doc4[xmlns=http://www.w3.org/1999/xhtml xml:lang=${lang}]",
"html:xxs": "!!!xxs+doc4[xmlns=http://www.w3.org/1999/xhtml xml:lang=${lang}]",
"html:5": "!!!+doc[lang=${lang}]",
"ol+": "ol>li",
"ul+": "ul>li",
"dl+": "dl>dt+dd",
"map+": "map>area",
"table+": "table>tr>td",
"colgroup+": "colgroup>col",
"colg+": "colgroup>col",
"tr+": "tr>td",
"select+": "select>option",
"optgroup+": "optgroup>option",
"optg+": "optgroup>option"
}
},
"xml": {
"extends": "html",
"profile": "xml",
"filters": "html"
},
"xsl": {
"extends": "html",
"profile": "xml",
"filters": "html, xsl",
"abbreviations": {
"tm": "<xsl:template match=\"\" mode=\"\">",
"tmatch": "tm",
"tn": "<xsl:template name=\"\">",
"tname": "tn",
"call": "<xsl:call-template name=\"\"/>",
"ap": "<xsl:apply-templates select=\"\" mode=\"\"/>",
"api": "<xsl:apply-imports/>",
"imp": "<xsl:import href=\"\"/>",
"inc": "<xsl:include href=\"\"/>",
"ch": "<xsl:choose>",
"xsl:when": "<xsl:when test=\"\">",
"wh": "xsl:when",
"ot": "<xsl:otherwise>",
"if": "<xsl:if test=\"\">",
"par": "<xsl:param name=\"\">",
"pare": "<xsl:param name=\"\" select=\"\"/>",
"var": "<xsl:variable name=\"\">",
"vare": "<xsl:variable name=\"\" select=\"\"/>",
"wp": "<xsl:with-param name=\"\" select=\"\"/>",
"key": "<xsl:key name=\"\" match=\"\" use=\"\"/>",
"elem": "<xsl:element name=\"\">",
"attr": "<xsl:attribute name=\"\">",
"attrs": "<xsl:attribute-set name=\"\">",
"cp": "<xsl:copy select=\"\"/>",
"co": "<xsl:copy-of select=\"\"/>",
"val": "<xsl:value-of select=\"\"/>",
"each": "<xsl:for-each select=\"\">",
"for": "each",
"tex": "<xsl:text></xsl:text>",
"com": "<xsl:comment>",
"msg": "<xsl:message terminate=\"no\">",
"fall": "<xsl:fallback>",
"num": "<xsl:number value=\"\"/>",
"nam": "<namespace-alias stylesheet-prefix=\"\" result-prefix=\"\"/>",
"pres": "<xsl:preserve-space elements=\"\"/>",
"strip": "<xsl:strip-space elements=\"\"/>",
"proc": "<xsl:processing-instruction name=\"\">",
"sort": "<xsl:sort select=\"\" order=\"\"/>",
"choose+": "xsl:choose>xsl:when+xsl:otherwise",
"xsl": "!!!+xsl:stylesheet[version=1.0 xmlns:xsl=http://www.w3.org/1999/XSL/Transform]>{\n|}"
},
"snippets": {
"!!!": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
}
},
"haml": {
"filters": "haml",
"extends": "html",
"profile": "xml"
},
"scss": {
"extends": "css"
},
"sass": {
"extends": "css"
},
"less": {
"extends": "css"
},
"stylus": {
"extends": "css"
}
}