├── emmet ├── __init__.py ├── reloader.py ├── python-wrapper.js ├── semver.py ├── file.py ├── context.py ├── pyv8loader.py └── snippets.json ├── messages.json ├── .gitignore ├── messages ├── official1.0.txt ├── 1.0.1.txt └── install.txt ├── Preferences.sublime-settings ├── emmet_completions ├── __init__.py └── trackers.py ├── Main.sublime-menu ├── Default.sublime-commands ├── Emmet.sublime-settings ├── misc └── generate-keymap.py ├── README.md ├── editor.js ├── Emmet.tmLanguage ├── Default (Linux).sublime-keymap ├── Default (OSX).sublime-keymap ├── Default (Windows).sublime-keymap └── emmet-plugin.py /emmet/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "install": "messages/install.txt", 3 | "2013.02.27.00.00.00": "messages/official1.0.txt" 4 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[co] 2 | *.tmLanguage.cache 3 | 4 | # Packages 5 | *.egg 6 | *.egg-info 7 | dist 8 | build 9 | eggs 10 | parts 11 | bin 12 | var 13 | sdist 14 | develop-eggs 15 | .installed.cfg 16 | 17 | # Installer logs 18 | pip-log.txt 19 | 20 | # Unit test / coverage reports 21 | .coverage 22 | .tox 23 | 24 | #Translations 25 | *.mo 26 | 27 | #Mr Developer 28 | .mr.developer.cfg 29 | 30 | .DS_Store -------------------------------------------------------------------------------- /messages/official1.0.txt: -------------------------------------------------------------------------------- 1 | 2 | Emmet v1.0 is out! 3 | ================== 4 | 5 | Check out features you might miss since beta release: 6 | 7 | http://emmet.io/blog/emmet-v1/ 8 | 9 | 10 | If you like Emmet and wish to support further development, 11 | any donations are highly appreciated: 12 | http://emmet.io/donate/ 13 | 14 | ------------------------------ 15 | Follow me on Twitter: @emmetio 16 | ------------------------------ -------------------------------------------------------------------------------- /emmet/reloader.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import imp 3 | 4 | # Dependecy reloader for Emmet plugin 5 | # The original idea is borrowed from 6 | # https://github.com/wbond/sublime_package_control/blob/master/package_control/reloader.py 7 | 8 | reload_mods = [] 9 | for mod in sys.modules: 10 | if mod.startswith('emmet') and sys.modules[mod] != None: 11 | reload_mods.append(mod) 12 | 13 | mods_load_order = [ 14 | 'emmet.semver', 15 | 'emmet.pyv8loader', 16 | 'emmet_completions.trackers', 17 | 'emmet_completions.meta', 18 | 'emmet_completions', 19 | 'emmet.file', 20 | 'emmet.context' 21 | ] 22 | 23 | for mod in mods_load_order: 24 | if mod in reload_mods: 25 | m = sys.modules[mod] 26 | if 'on_module_reload' in m.__dict__: 27 | m.on_module_reload() 28 | imp.reload(sys.modules[mod]) -------------------------------------------------------------------------------- /messages/1.0.1.txt: -------------------------------------------------------------------------------- 1 | New features: 2 | 3 | Fuzzy search for CSS abbreviations 4 | ================================== 5 | 6 | Emmet now supports fuzzy searching of unknowns CSS abbreviations. 7 | For example, instead of using ov:h abbreviation to get overflow: hidden, you can write, ov-h, ovh or even oh. 8 | 9 | Read more about fuzzy search feature: 10 | http://docs.emmet.io/css-abbreviations/fuzzy-search/ 11 | 12 | Emmet CSS snippets in auto-complete popup 13 | ========================================= 14 | 15 | All Emmet snippets are displayed in standard auto-complete popup, 16 | so you don't need to remember them all: just start typing first 17 | letters of required CSS snippet and let editor do the rest 18 | (works with fuzzy search too). 19 | 20 | To disable Emmet completions, set 21 | 22 | "show_css_completions": false 23 | 24 | preference in user's Emmet.sublime-settings 25 | 26 | ------------------------------ 27 | Follow me on Twitter: @emmetio 28 | ------------------------------ -------------------------------------------------------------------------------- /Preferences.sublime-settings: -------------------------------------------------------------------------------- 1 | { 2 | // --------------------------- README ----------------------------------- 3 | // Copy these key/values to User/Preferences.sublime-settings 4 | // They have NO effect here 5 | 6 | // If `true` will disable bindings like ctrl+alt+n etc 7 | // Handy for our italian friends :) 8 | "alt_gr": false, 9 | 10 | // If `true` will insert id="$1" snippet on pressing '#', similar on '.' 11 | // Useful for `raw html`, but annoying for some templating langs. 12 | "auto_id_class": false, 13 | 14 | // disable expanding abbreviation by Tab key 15 | "disable_tab_abbreviations": false, 16 | 17 | // disable insertion of formatted linebreak when 18 | // Enter key is pressed between opening and closing HTML tags 19 | "disable_formatted_linebreak": false, 20 | 21 | // Enables default Emmet keymap. Many users complain that Emmet actions 22 | // (especially ones that bound to Alt key) behave incorrectly in 23 | // non-English keyboard layouts. Set this setting to `false` in 24 | // Users’s Preferences.sublime-settings to disable all default 25 | // keybindings at once 26 | "enable_emmet_keymap": true, 27 | 28 | // disable expanding abbreviation by Tab key when autocomplete popup is visible 29 | "disable_tab_abbreviations_on_auto_complete": true 30 | } -------------------------------------------------------------------------------- /messages/install.txt: -------------------------------------------------------------------------------- 1 | Thank you for installing Emmet -- a toolkit that can greatly improve your workflow. Note that this plugin automatically downloads and installs PyV8 binary (see status bar message). 2 | 3 | ****************************** 4 | Please restart editor 5 | to finish installation process 6 | after PyV8 was downloaded. 7 | ****************************** 8 | 9 | Tab key handler 10 | ========================== 11 | 12 | By default, Emmet allows you to expand abbreviations with Tab key in HTML, XML, HAML and CSS/SASS/LESS/Stylus documents. As a side effect, you can’t use some of your ST2 snippets. 13 | 14 | Please read https://github.com/sergeche/emmet-sublime#tab-key-handler about how Tab handler works and how to tweak its behavior to match your needs. 15 | 16 | Enter key 17 | ========================== 18 | 19 | In HTML and XML documents, Emmet overrides Enter key to insert formatted line breaks between opening and closing tags. In some cases it will break character input (for example, in Japanese language). 20 | 21 | To disable Enter key handler, simply add the following option in your user's Preferences file: 22 | 23 | "disable_formatted_linebreak": true 24 | 25 | Actions shortcuts 26 | ========================== 27 | 28 | Emmet has a number of actions with keyboard shortcuts that may override ones you're using commonly (for example, Ctrl-E or Ctrl-Down). Please read the project main page to get list of available actions and keyboard shortcuts and how to disable them: 29 | https://github.com/sergeche/emmet-sublime 30 | 31 | Documentation and examples: 32 | http://emmet.io 33 | 34 | ------------------------------ 35 | Follow me on Twitter: @emmetio 36 | ------------------------------ 37 | -------------------------------------------------------------------------------- /emmet_completions/__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import sublime 3 | import sublime_plugin 4 | 5 | from trackers import back_track, track_regex, track_scope 6 | 7 | __authors__ = ['"Sergey Chikuyonok" ' 8 | '"Nicholas Dudfield" '] 9 | 10 | HTML = 'text.html - source' 11 | XML = 'text.xml' 12 | 13 | HTML_INSIDE_TAG_ANYWHERE = 'text.html meta.tag' 14 | HTML_INSIDE_TAG = ( 'text.html meta.tag - string - ' 15 | 'meta.scope.between-tag-pair.html ' 16 | '-punctuation.definition.tag.begin.html') 17 | 18 | HTML_INSIDE_TAG_ATTRIBUTE = 'text.html meta.tag string' 19 | 20 | HTML_NOT_INSIDE_TAG = 'text.html - meta.tag' 21 | 22 | NO_PLUG = sublime.INHIBIT_EXPLICIT_COMPLETIONS 23 | NO_BUF = sublime.INHIBIT_WORD_COMPLETIONS 24 | 25 | EMMET_SCOPE = ', '.join([HTML, XML]) 26 | 27 | def find_tag_start(view, start_pt): 28 | regions = back_track(view, start_pt, track_regex('<', False) ) 29 | return regions[-1].begin() 30 | 31 | def find_tag_name(view, start_pt): 32 | tag_region = view.find('[a-zA-Z:]+', find_tag_start(view, start_pt)) 33 | name = view.substr( tag_region ) 34 | return name 35 | 36 | def find_attribute_name(view, start_pt): 37 | conds = track_scope('string'), track_regex('\s|='), track_regex('\S') 38 | regions = back_track(view, start_pt, *conds) 39 | return view.substr(regions[-1]) 40 | 41 | def remove_html_completions(): 42 | for completer in "TagCompletions", "HtmlCompletions": 43 | try: 44 | import html_completions 45 | cm = getattr(html_completions, completer) 46 | except (ImportError, AttributeError): 47 | continue 48 | 49 | completions = sublime_plugin.all_callbacks['on_query_completions'] 50 | for i, instance in enumerate (completions): 51 | if isinstance(instance, cm): 52 | del completions[i] 53 | -------------------------------------------------------------------------------- /emmet_completions/trackers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #coding: utf8 3 | #################################### IMPORTS ################################### 4 | 5 | # Std Libs 6 | import re 7 | 8 | # Sublime Libs 9 | import sublime 10 | import sublime_plugin 11 | 12 | from collections import defaultdict 13 | 14 | ################################### CONSTANTS ################################## 15 | 16 | # Directions for tracker 17 | BACK = -1 18 | FORWARD = 1 19 | 20 | ###################### VIEW TRACKERS ( CONTEXT SCANNERS ) ###################### 21 | 22 | def pt_range(view, start_pt, direction): 23 | end_pt = direction 24 | if end_pt != -1: end_pt = view.size() 25 | return xrange(start_pt, end_pt, direction) 26 | 27 | def region_from_pt_list(l): 28 | if l: 29 | l = sorted(l) 30 | return sublime.Region(l[0], l[-1]+1) 31 | 32 | def view_tracker(view, start_pt, *conds): 33 | pts = defaultdict(list) 34 | failed = False 35 | 36 | for i, (direction, condition) in enumerate(conds): 37 | for pt in pt_range(view, start_pt, direction): 38 | if failed: break 39 | 40 | if not condition(view, pt): 41 | if not pts[i]: failed = True 42 | start_pt = pt 43 | break 44 | 45 | if len(pts[i]) < 2: 46 | pts[i].append(pt) 47 | else: 48 | pts[i][-1] = pt 49 | 50 | return [ region_from_pt_list(pt_list) for pt_list in pts.values() ] 51 | 52 | def tracker_success(regions): 53 | return all(r is not None for r in regions) 54 | 55 | def back_track(view, start_pt, *conds): 56 | return view_tracker(view, start_pt -1, *((BACK, c) for c in conds)) 57 | 58 | ################################### TRACKERS ################################### 59 | 60 | def track_regex(r, cond=True): 61 | return lambda v, p: bool(re.match(r, v.substr(p))) is cond 62 | 63 | def track_scope(s, cond=True): 64 | return lambda v, p: bool(v.match_selector(p, s)) is cond 65 | 66 | ################################################################################ -------------------------------------------------------------------------------- /emmet/python-wrapper.js: -------------------------------------------------------------------------------- 1 | var console = { 2 | log: function(msg) { 3 | log(msg); 4 | } 5 | }; 6 | 7 | /** 8 | * Simple function alias to run Emmet action. 9 | * editorProxy object should be defined 10 | * in concrete plugin implementation. 11 | */ 12 | function pyRunAction(name) { 13 | return emmet.require('actions').run(name, editorProxy); 14 | } 15 | 16 | function pyLoadSystemSnippets(data) { 17 | emmet.require('bootstrap').loadSystemSnippets(data); 18 | } 19 | 20 | function pyLoadUserData(data) { 21 | emmet.require('bootstrap').loadUserData(data); 22 | } 23 | 24 | function pyLoadExtensions(fileList) { 25 | fileList = _.toArray(fileList); 26 | emmet.require('bootstrap').loadExtensions(fileList); 27 | } 28 | 29 | function pyResetUserData() { 30 | emmet.require('bootstrap').resetUserData(); 31 | } 32 | 33 | emmet.define('file', function(require, _) { 34 | return { 35 | _parseParams: function(args) { 36 | var params = { 37 | path: args[0], 38 | size: -1 39 | }; 40 | 41 | args = _.rest(args); 42 | params.callback = _.last(args); 43 | args = _.initial(args); 44 | if (args.length) { 45 | params.size = args[0]; 46 | } 47 | 48 | return params; 49 | }, 50 | 51 | read: function(path, size, callback) { 52 | var params = this._parseParams(arguments); 53 | 54 | try { 55 | pyFile.read(params.path, params.size, function(err, content) { 56 | if (err) { 57 | return params.callback(err, content); 58 | } 59 | 60 | content = _.map(content || [], function(b) { 61 | return String.fromCharCode(b); 62 | }).join(''); 63 | params.callback(null, content); 64 | }); 65 | } catch(e) { 66 | params.callback(e); 67 | } 68 | }, 69 | 70 | readText: function() { 71 | var params = this._parseParams(arguments); 72 | try { 73 | pyFile.read_text(params.path, params.size, params.callback); 74 | } catch(e) { 75 | params.callback(e); 76 | } 77 | 78 | }, 79 | 80 | locateFile: function(editorFile, fileName) { 81 | return pyFile.locate_file(editorFile, fileName); 82 | }, 83 | 84 | createPath: function(parent, fileName) { 85 | return pyFile.create_path(parent, fileName); 86 | }, 87 | 88 | save: function(file, content) { 89 | return pyFile.save(file, content); 90 | }, 91 | 92 | getExt: function(file) { 93 | var m = (file || '').match(/\.([\w\-]+)$/); 94 | return m ? m[1].toLowerCase() : ''; 95 | } 96 | }; 97 | }); -------------------------------------------------------------------------------- /emmet/semver.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import re 4 | 5 | _REGEX = re.compile('^(?P[0-9]+)' 6 | '\.(?P[0-9]+)' 7 | '(\.(?P[0-9]+))?' 8 | '(\-(?P[0-9A-Za-z]+(\.[0-9A-Za-z]+)*))?' 9 | '(\+(?P[0-9A-Za-z]+(\.[0-9A-Za-z]+)*))?$') 10 | 11 | if 'cmp' not in __builtins__: 12 | cmp = lambda a,b: (a > b) - (a < b) 13 | 14 | def parse(version): 15 | """ 16 | Parse version to major, minor, patch, pre-release, build parts. 17 | """ 18 | match = _REGEX.match(version) 19 | if match is None: 20 | raise ValueError('%s is not valid SemVer string' % version) 21 | 22 | verinfo = match.groupdict() 23 | 24 | verinfo['major'] = int(verinfo['major']) 25 | verinfo['minor'] = int(verinfo['minor']) 26 | verinfo['patch'] = int(verinfo['patch'] or '0') 27 | 28 | return verinfo 29 | 30 | 31 | def compare(ver1, ver2): 32 | def nat_cmp(a, b): 33 | a, b = a or '', b or '' 34 | convert = lambda text: text.isdigit() and int(text) or text.lower() 35 | alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)] 36 | return cmp(alphanum_key(a), alphanum_key(b)) 37 | 38 | def compare_by_keys(d1, d2): 39 | for key in ['major', 'minor', 'patch']: 40 | v = cmp(d1.get(key), d2.get(key)) 41 | if v: 42 | return v 43 | rc1, rc2 = d1.get('prerelease'), d2.get('prerelease') 44 | build1, build2 = d1.get('build'), d2.get('build') 45 | rccmp = nat_cmp(rc1, rc2) 46 | buildcmp = nat_cmp(build1, build2) 47 | if not (rc1 or rc2): 48 | return buildcmp 49 | elif not rc1: 50 | return 1 51 | elif not rc2: 52 | return -1 53 | return rccmp or buildcmp or 0 54 | 55 | v1, v2 = parse(ver1), parse(ver2) 56 | 57 | return compare_by_keys(v1, v2) 58 | 59 | 60 | def match(version, match_expr): 61 | prefix = match_expr[:2] 62 | if prefix in ('>=', '<=', '=='): 63 | match_version = match_expr[2:] 64 | elif prefix and prefix[0] in ('>', '<', '='): 65 | prefix = prefix[0] 66 | match_version = match_expr[1:] 67 | else: 68 | raise ValueError("match_expr parameter should be in format , " 69 | "where is one of ['<', '>', '==', '<=', '>=']. " 70 | "You provided: %r" % match_expr) 71 | 72 | possibilities_dict = { 73 | '>': (1,), 74 | '<': (-1,), 75 | '==': (0,), 76 | '>=': (0, 1), 77 | '<=': (-1, 0) 78 | } 79 | 80 | possibilities = possibilities_dict[prefix] 81 | cmp_res = compare(version, match_version) 82 | 83 | return cmp_res in possibilities 84 | -------------------------------------------------------------------------------- /Main.sublime-menu: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "Preferences", 4 | "mnemonic": "n", 5 | "id": "preferences", 6 | "children": 7 | [ 8 | { 9 | "caption": "Package Settings", 10 | "mnemonic": "P", 11 | "id": "package-settings", 12 | "children": 13 | [ 14 | { 15 | "caption": "Emmet", 16 | "children": 17 | [ 18 | { 19 | "command": "open_file", "args": 20 | { 21 | "file": "${packages}/Emmet/Emmet.sublime-settings" 22 | }, 23 | "caption": "Settings – Default" 24 | }, 25 | { 26 | "command": "open_file", "args": 27 | { 28 | "file": "${packages}/User/Emmet.sublime-settings" 29 | }, 30 | "caption": "Settings – User" 31 | }, 32 | { "caption": "-" }, 33 | { "caption": "-" }, 34 | { 35 | "command": "open_file", 36 | "args": { 37 | "file": "${packages}/Emmet/Default (OSX).sublime-keymap", 38 | "platform": "OSX" 39 | }, 40 | "caption": "Key Bindings – Default" 41 | }, 42 | { 43 | "command": "open_file", 44 | "args": { 45 | "file": "${packages}/Emmet/Default (Linux).sublime-keymap", 46 | "platform": "Linux" 47 | }, 48 | "caption": "Key Bindings – Default" 49 | }, 50 | { 51 | "command": "open_file", 52 | "args": { 53 | "file": "${packages}/Emmet/Default (Windows).sublime-keymap", 54 | "platform": "Windows" 55 | }, 56 | "caption": "Key Bindings – Default" 57 | }, 58 | { 59 | "command": "open_file", 60 | "args": { 61 | "file": "${packages}/User/Default (OSX).sublime-keymap", 62 | "platform": "OSX" 63 | }, 64 | "caption": "Key Bindings – User" 65 | }, 66 | { 67 | "command": "open_file", 68 | "args": { 69 | "file": "${packages}/User/Default (Linux).sublime-keymap", 70 | "platform": "Linux" 71 | }, 72 | "caption": "Key Bindings – User" 73 | }, 74 | { 75 | "command": "open_file", 76 | "args": { 77 | "file": "${packages}/User/Default (Windows).sublime-keymap", 78 | "platform": "Windows" 79 | }, 80 | "caption": "Key Bindings – User" 81 | }, 82 | { "caption": "-" } 83 | ] 84 | } 85 | ] 86 | } 87 | ] 88 | } 89 | ] 90 | -------------------------------------------------------------------------------- /Default.sublime-commands: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "Emmet: Expand Abbreviation", 4 | "command": "run_emmet_action", 5 | "args": { 6 | "action": "expand_abbreviation" 7 | } 8 | }, 9 | 10 | { 11 | "caption": "Emmet: Wrap With Abbreviation", 12 | "command": "wrap_as_you_type" 13 | }, 14 | 15 | { 16 | "caption": "Emmet: Match Pair (outward)", 17 | "command": "run_emmet_action", 18 | "args": { 19 | "action": "match_pair_outward" 20 | } 21 | }, 22 | 23 | { 24 | "caption": "Emmet: Match Pair (inward)", 25 | "command": "run_emmet_action", 26 | "args": { 27 | "action": "match_pair_inward" 28 | } 29 | }, 30 | 31 | { 32 | "caption": "Emmet: Go to Matching Pair", 33 | "command": "run_emmet_action", 34 | "args": { 35 | "action": "matching_pair" 36 | } 37 | }, 38 | 39 | { 40 | "caption": "Emmet: Next Edit Point", 41 | "command": "run_emmet_action", 42 | "args": { 43 | "action": "next_edit_point" 44 | } 45 | }, 46 | 47 | { 48 | "caption": "Emmet: Previous Edit Point", 49 | "command": "run_emmet_action", 50 | "args": { 51 | "action": "prev_edit_point" 52 | } 53 | }, 54 | 55 | { 56 | "caption": "Emmet: Merge Lines", 57 | "command": "run_emmet_action", 58 | "args": { 59 | "action": "merge_lines" 60 | } 61 | }, 62 | 63 | { 64 | "caption": "Emmet: Toggle Comment", 65 | "command": "run_emmet_action", 66 | "args": { 67 | "action": "toggle_comment" 68 | } 69 | }, 70 | 71 | { 72 | "caption": "Emmet: Split\\Join Tag", 73 | "command": "run_emmet_action", 74 | "args": { 75 | "action": "split_join_tag" 76 | } 77 | }, 78 | 79 | { 80 | "caption": "Emmet: Remove Tag", 81 | "command": "run_emmet_action", 82 | "args": { 83 | "action": "remove_tag" 84 | } 85 | }, 86 | 87 | { 88 | "caption": "Emmet: Evaluate Math Expression", 89 | "command": "run_emmet_action", 90 | "args": { 91 | "action": "evaluate_math_expression" 92 | } 93 | }, 94 | 95 | { 96 | "caption": "Emmet: Increment Number by 1", 97 | "command": "run_emmet_action", 98 | "args": { 99 | "action": "increment_number_by_1" 100 | } 101 | }, 102 | 103 | { 104 | "caption": "Emmet: Decrement Number by 1", 105 | "command": "run_emmet_action", 106 | "args": { 107 | "action": "decrement_number_by_1" 108 | } 109 | }, 110 | 111 | { 112 | "caption": "Emmet: Increment Number by 0.1", 113 | "command": "run_emmet_action", 114 | "args": { 115 | "action": "increment_number_by_01" 116 | } 117 | }, 118 | 119 | { 120 | "caption": "Emmet: Decrement Number by 0.1", 121 | "command": "run_emmet_action", 122 | "args": { 123 | "action": "decrement_number_by_01" 124 | } 125 | }, 126 | 127 | { 128 | "caption": "Emmet: Increment Number by 10", 129 | "command": "run_emmet_action", 130 | "args": { 131 | "action": "increment_number_by_10" 132 | } 133 | }, 134 | 135 | { 136 | "caption": "Emmet: Decrement Number by 10", 137 | "command": "run_emmet_action", 138 | "args": { 139 | "action": "decrement_number_by_10" 140 | } 141 | }, 142 | 143 | { 144 | "caption": "Emmet: Select Next Item", 145 | "command": "run_emmet_action", 146 | "args": { 147 | "action": "select_next_item" 148 | } 149 | }, 150 | 151 | { 152 | "caption": "Emmet: Select Previous Item", 153 | "command": "run_emmet_action", 154 | "args": { 155 | "action": "select_previous_item" 156 | } 157 | }, 158 | 159 | { 160 | "caption": "Emmet: Reflect CSS Value", 161 | "command": "run_emmet_action", 162 | "args": { 163 | "action": "reflect_css_value" 164 | } 165 | }, 166 | 167 | { 168 | "caption": "Emmet: Encode\\Decode Image to data:URL", 169 | "command": "run_emmet_action", 170 | "args": { 171 | "action": "encode_decode_data_url" 172 | } 173 | }, 174 | 175 | { 176 | "caption": "Emmet: Update Image Size", 177 | "command": "run_emmet_action", 178 | "args": { 179 | "action": "update_image_size" 180 | } 181 | }, 182 | 183 | { 184 | "caption": "Emmet: Rename Tag", 185 | "command": "rename_tag" 186 | }, 187 | 188 | { 189 | "caption": "Emmet: Reload Extensions", 190 | "command": "emmet_reset_context" 191 | } 192 | ] -------------------------------------------------------------------------------- /emmet/file.py: -------------------------------------------------------------------------------- 1 | ''' 2 | @author Sergey Chikuyonok (serge.che@gmail.com) 3 | @link http://chikuyonok.ru 4 | ''' 5 | import sys 6 | import os.path 7 | import re 8 | 9 | is_python3 = sys.version_info[0] > 2 10 | 11 | try: 12 | if is_python3: 13 | import urllib.request as urllib2 14 | else: 15 | import urllib2 16 | except Exception as e: 17 | pass 18 | 19 | def is_url(path): 20 | return re.match(r'^https?://', path, re.IGNORECASE) 21 | 22 | def read_http(url, size=-1, mode=None): 23 | response = urllib2.urlopen(url, timeout=5) 24 | return response.read(size) 25 | 26 | def read_file(path, size=-1, mode='rb'): 27 | kwargs = {} 28 | if is_python3 and 'b' not in mode: 29 | kwargs['encoding'] = 'utf-8' 30 | 31 | with open(path, mode, **kwargs) as fp: 32 | return fp.read(size) 33 | 34 | class File(): 35 | def __init__(self): 36 | pass 37 | 38 | def _read(self, path, size, mode='rb'): 39 | reader = is_url(path) and read_http or read_file 40 | return reader(path, size, mode) 41 | 42 | def read(self, path, size, callback=None): 43 | """ 44 | Read file content and return it 45 | @param path: File's relative or absolute path 46 | @type path: str 47 | @return: str 48 | """ 49 | 50 | try: 51 | content = self._read(path, size) 52 | 53 | # return as array of character codes since PyV8 may corrupt 54 | # binary data when python string is translated into JS string 55 | if is_python3: 56 | content = [ch for ch in content] 57 | else: 58 | content = [ord(ch) for ch in content] 59 | 60 | except Exception as e: 61 | return callback(str(e), None) 62 | 63 | callback(None, content) 64 | 65 | def read_text(self, path, size, callback=None): 66 | """ 67 | Read file content and return it 68 | @param path: File's relative or absolute path 69 | @type path: str 70 | @return: str 71 | """ 72 | 73 | try: 74 | content = self._read(path, size, 'r') 75 | if not is_python3: 76 | content = content.decode('utf-8') 77 | except Exception as e: 78 | return callback(str(e), None) 79 | 80 | callback(None, content) 81 | 82 | def locate_file(self, editor_file, file_name): 83 | """ 84 | Locate file_name file that relates to editor_file. 85 | File name may be absolute or relative path 86 | 87 | @type editor_file: str 88 | @type file_name: str 89 | @return String or None if file_name cannot be located 90 | """ 91 | if is_url(file_name): 92 | return file_name 93 | 94 | result = None 95 | 96 | previous_parent = '' 97 | parent = os.path.dirname(editor_file) 98 | while parent and os.path.exists(parent) and parent != previous_parent: 99 | tmp = self.create_path(parent, file_name) 100 | if os.path.exists(tmp): 101 | result = tmp 102 | break 103 | 104 | previous_parent = parent 105 | parent = os.path.dirname(parent) 106 | 107 | return result 108 | 109 | def create_path(self, parent, file_name): 110 | """ 111 | Creates absolute path by concatenating parent and file_name. 112 | If parent points to file, its parent directory is used 113 | 114 | @type parent: str 115 | @type file_name: str 116 | @return: str 117 | """ 118 | result = '' 119 | file_name = file_name.lstrip('/') 120 | 121 | if os.path.exists(parent): 122 | if os.path.isfile(parent): 123 | parent = os.path.dirname(parent) 124 | 125 | result = os.path.normpath(os.path.join(parent, file_name)) 126 | 127 | return result 128 | 129 | def save(self, file, content): 130 | """ 131 | Saves content as file 132 | 133 | @param file: File's asolute path 134 | @type file: str 135 | @param content: File content 136 | @type content: str 137 | """ 138 | try: 139 | fp = open(file, 'wb') 140 | except: 141 | fdirs, fname = os.path.split(file) 142 | if fdirs: 143 | os.makedirs(fdirs) 144 | fp = open(file, 'wb') 145 | 146 | fp.write(content) 147 | fp.close() 148 | 149 | def get_ext(self, file): 150 | """ 151 | Returns file extention in lower case 152 | @type file: str 153 | @return: str 154 | """ 155 | ext = os.path.splitext(file)[1] 156 | if ext: 157 | ext = ext[1:] 158 | 159 | return ext.lower() 160 | -------------------------------------------------------------------------------- /Emmet.sublime-settings: -------------------------------------------------------------------------------- 1 | { 2 | // Copy any modified settings to `User/Emmet.sublime-settings` 3 | // otherwise modifications will not survive updates. 4 | 5 | // Path to folder where Emmet should look for extensions 6 | // http://docs.emmet.io/customization/ 7 | "extensions_path": "~/emmet", 8 | 9 | // Disable completions of HTML attributes 10 | // with this option disabled, you can get attribute list completions 11 | // inside opening HTML tags. 12 | // WARNING: with this option disabled, Tab key expander will not 13 | // work inside opening HTML attributes 14 | "disable_completions": false, 15 | 16 | // With this option enabled, all Emmet's CSS snippets 17 | // will be available in standard auto-complete popup 18 | "show_css_completions": true, 19 | 20 | // List of scopes where Emmet CSS completions should be available 21 | "css_completions_scope": "source.css - meta.selector.css - meta.property-value.css, source.scss - meta.selector.scss - meta.property-value.scss, source.less - meta.selector.css - meta.property-value.css", 22 | 23 | // Remove default HTML tag completions on plugin start 24 | // You should restart editor after changing this option 25 | "remove_html_completions": false, 26 | 27 | // A comma-separated list of scopes where Emmet’s Tab key 28 | // abbreviation expander should be disabled 29 | "disable_tab_abbreviations_for_scopes": "", 30 | 31 | // A regexp for scope name: if it matches, Tab handler won’t work 32 | // The reason to use this preference is that ST2 has buggy scope matcher 33 | // which may still trigger Tab handler even if it's restricted by context 34 | "disable_tab_abbreviations_for_regexp": "source\\.(?!css).+?\\stext\\.html", 35 | 36 | // Exit tabstop mode when enter key is pressed 37 | "clear_fields_on_enter_key": true, 38 | 39 | // A comma-separated list of disabled action names. 40 | // Listed action will not be triggered by default keyboard 41 | // shortcut. 42 | // Use "all" value to disable all shortcuts at once 43 | "disabled_keymap_actions": "", 44 | 45 | // By default, Emmet overrides Tab key to effectively expand abbreviations. 46 | // The downside of this approach is that you can’t expand regular ST2 47 | // snippets (like `php`). Since it’s not currently possible to get a list 48 | // of ST2 snippets via API, you can provide a list of scopes where Emmet’s 49 | // Tab trigger should be disabled when expanding simple abbreviation. 50 | // If entered abbreviation (like `php`) wasn’t found in Emmet snippets list 51 | // or "known_html_tags" preference, Tab handler will not be triggered. 52 | // Leave this setting blank to disable this feature 53 | "disabled_single_snippet_for_scopes": "text.html", 54 | 55 | // A space-separated list of single snippets that should be 56 | // forcilbly disabled (not handled) for Emmet even if it 57 | // has such abbreviation. 58 | // This option is useful if you wish the enumerated snippets 59 | // should be handled by Sublime Text. 60 | // Example value: "script style html" 61 | "disabled_single_snippets": "", 62 | 63 | // A space separated list of all known HTML tags, 64 | // used together with "disabled_on_single_snippets" option 65 | "known_html_tags": "html head title base link meta style script noscript body section nav article aside h1 h2 h3 h4 h5 h6 hgroup header footer address p hr pre blockquote ol ul li dl dt dd figure figcaption div a em strong small s cite q dfn abbr data time code var samp kbd sub sup i b u mark ruby rt rp bdi bdo span br wbr ins del img iframe embed object param video audio source track canvas map area svg math table caption colgroup col tbody thead tfoot tr td th form fieldset legend label input button select datalist optgroup option textarea keygen output progress meter details summary command menu", 66 | 67 | "empty_elements": "area base basefont br col frame hr img input isindex link meta param embed", 68 | 69 | // If set to `true`, Emmet will automatically insert final tabstop 70 | // at the end of expanded abbreviation 71 | "insert_final_tabstop": false, 72 | 73 | // Try to automatically detect XHTML dialect in HTML documents. 74 | // With this option enabled, your custom profile for HTML documents may not work. 75 | "autodetect_xhtml": true, 76 | 77 | // Use old Tab handler to expand abbreviations. 78 | // With this option enabled, editor may better handle Tab key 79 | // (especially with other plugins that overrides Tab key), 80 | // but will spit "slow plugin" message 81 | "use_old_tab_handler": false, 82 | 83 | /////////////////////////////// 84 | // Emmet customization 85 | // Each section has the same meaning as the same-named JSON file 86 | // described here: 87 | // http://docs.emmet.io/customization/ 88 | /////////////////////////////// 89 | 90 | 91 | // Custom snippets definitions, as per https://github.com/emmetio/emmet/blob/master/snippets.json 92 | "snippets": { 93 | // "html": { 94 | // "abbreviations": { 95 | // "example": "
" 96 | // } 97 | // } 98 | }, 99 | 100 | // Emmet preferences 101 | // List of all available preferences: 102 | // http://docs.emmet.io/customization/preferences/ 103 | "preferences": { 104 | // "css.valueSeparator": ": ", 105 | // "css.propertyEnd": ";" 106 | }, 107 | 108 | // Output profiles for syntaxes 109 | // http://docs.emmet.io/customization/syntax-profiles/ 110 | "syntaxProfiles": { 111 | // Enable XHTML dialect for HTML syntax 112 | // "html": "xhtml" 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /misc/generate-keymap.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import json 3 | import copy 4 | 5 | keymap = { 6 | "expand_abbreviation": "ctrl+e", 7 | "match_pair_outward": {"mac": "ctrl+d", "pc": "ctrl+,"}, 8 | "match_pair_inward": {"mac": "ctrl+j", "pc": "ctrl+shift+0"}, 9 | "matching_pair": {"mac": "ctrl+shift+t", "pc": "ctrl+alt+j"}, 10 | "next_edit_point": "ctrl+alt+right", 11 | "prev_edit_point": "ctrl+alt+left", 12 | "toggle_comment": { 13 | "mac": "alt+shift+forward_slash", 14 | "pc": "ctrl+shift+forward_slash", 15 | "context": [{ 16 | "key": "selector", 17 | "operand": "source.css - source.css.less, text.xml, text.html - source", 18 | "operator": "equal" 19 | }] 20 | }, 21 | "split_join_tag": {"mac": "shift+super+'", "pc": "shift+ctrl+`"}, 22 | "remove_tag": {"mac": "super+'", "pc": "shift+ctrl+;"}, 23 | "evaluate_math_expression": {"mac": "shift+super+y", "pc": "shift+ctrl+y"}, 24 | "increment_number_by_1": "ctrl+up", 25 | "decrement_number_by_1": "ctrl+down", 26 | "increment_number_by_01": "alt+up", 27 | "decrement_number_by_01": "alt+down", 28 | "increment_number_by_10": {"mac": "alt+super+up", "pc": "shift+alt+up"}, 29 | "decrement_number_by_10": {"mac": "alt+super+down", "pc": "shift+alt+down"}, 30 | "select_next_item": {"mac": "shift+super+.", "pc": "shift+ctrl+."}, 31 | "select_previous_item": {"mac": "shift+super+,", "pc": "shift+ctrl+,"}, 32 | "reflect_css_value": {"mac": "shift+super+r", "pc": "shift+ctrl+r"}, 33 | "rename_tag": {"mac": "super+shift+k", "pc": "shift+ctrl+'"}, 34 | "encode_decode_data_url": {"mac": "shift+ctrl+d", "pc": "ctrl+'"}, 35 | "update_image_size": {"mac": "shift+ctrl+i", "pc": "ctrl+u"}, 36 | 37 | "expand_as_you_type": { 38 | "keys": ["ctrl+alt+enter"], 39 | "context": [{ 40 | "key": "setting.is_widget", 41 | "operand": False, 42 | "operator": "equal" 43 | }] 44 | }, 45 | 46 | "wrap_as_you_type": { 47 | "mac": "ctrl+w", 48 | "pc": "shift+ctrl+g", 49 | "context": [{ 50 | "key": "setting.is_widget", 51 | "operand": False, 52 | "operator": "equal" 53 | }] 54 | } 55 | } 56 | 57 | # additional "raw" ST2 actions definition 58 | addon = [ 59 | { 60 | "keys": ["tab"], 61 | "command": "expand_abbreviation_by_tab", 62 | "context": [ 63 | { 64 | "key": "selector", 65 | "match_all": True, 66 | "operand": "source.css, source.sass, source.less, source.scss, source.stylus, text.xml, text.html - source, text.haml, text.scala.html, source string", 67 | "operator": "equal" 68 | }, { 69 | "key": "selector", 70 | "operand": "storage.type.templatetag.django", 71 | "operator": "not_equal", 72 | "match_all": True 73 | }, { 74 | "key": "selection_empty", 75 | "match_all": True 76 | }, { 77 | "key": "has_next_field", 78 | "operator": "equal", 79 | "operand": False, 80 | "match_all": True 81 | }, { 82 | "key": "setting.disable_tab_abbreviations", 83 | "operator": "equal", 84 | "operand": False, 85 | "match_all": True 86 | }, { 87 | "key": "auto_complete_visible", 88 | "operand": False, 89 | "operator": "equal", 90 | "match_all": True 91 | }, { 92 | "key": "is_abbreviation", 93 | "match_all": True 94 | } 95 | ] 96 | }, 97 | 98 | # behaviour of tab key when autocomplete popup is visible 99 | { 100 | "keys": ["tab"], 101 | "command": "expand_abbreviation_by_tab", 102 | "context": [ 103 | { 104 | "key": "selector", 105 | "match_all": True, 106 | "operand": "source.css, source.sass, source.less, source.scss, source.stylus, text.xml, text.html - source, text.haml, text.scala.html, source string", 107 | "operator": "equal" 108 | }, { 109 | "key": "selector", 110 | "operand": "storage.type.templatetag.django", 111 | "operator": "not_equal", 112 | "match_all": True 113 | }, { 114 | "key": "selection_empty", 115 | "match_all": True 116 | }, { 117 | "key": "has_next_field", 118 | "operator": "equal", 119 | "operand": False, 120 | "match_all": True 121 | }, { 122 | "key": "auto_complete_visible", 123 | "operator": "equal", 124 | "operand": True, 125 | "match_all": True 126 | }, { 127 | "key": "setting.disable_tab_abbreviations_on_auto_complete", 128 | "operator": "equal", 129 | "operand": False, 130 | "match_all": True 131 | }, { 132 | "key": "is_abbreviation", 133 | "match_all": True 134 | } 135 | ] 136 | }, 137 | 138 | # insert linebreak with formatting 139 | { 140 | "keys": ["enter"], 141 | "command": "insert_snippet", 142 | "args": {"contents": "\n\t${0}\n"}, 143 | "context": [ 144 | { 145 | "key": "selector", 146 | "operand": "meta.scope.between-tag-pair.html, meta.scope.between-tag-pair.xml", 147 | "match_all": True 148 | }, { 149 | "key": "auto_complete_visible", 150 | "operand": False, 151 | "match_all": True 152 | }, { 153 | "key": "clear_fields_on_enter_key", 154 | "match_all": True 155 | }, { 156 | "key": "setting.disable_formatted_linebreak", 157 | "operand": False, 158 | "match_all": True 159 | } 160 | ] 161 | }, 162 | 163 | { 164 | "keys": ["#"], 165 | "command": "emmet_insert_attribute", 166 | "args": {"attribute": "id"}, 167 | "context": [ 168 | { 169 | "key": "selector", 170 | "match_all": True, 171 | "operand": "text.html meta.tag -string -punctuation.definition.tag.begin.html -meta.scope.between-tag-pair.html -source -meta.tag.template.value.twig", 172 | "operator": "equal" 173 | }, { 174 | "key": "setting.auto_id_class", 175 | "operator": "equal", 176 | "operand": True 177 | } 178 | ] 179 | }, 180 | 181 | { 182 | "keys": ["."], 183 | "command": "emmet_insert_attribute", 184 | "args": {"attribute": "class"}, 185 | "context": [ 186 | { 187 | "key": "selector", 188 | "match_all": True, 189 | "operand": "text.html meta.tag -string -punctuation.definition.tag.begin.html -meta.scope.between-tag-pair.html -source -meta.tag.template.value.twig", 190 | "operator": "equal" 191 | }, { 192 | "key": "setting.auto_id_class", 193 | "operator": "equal", 194 | "operand": True 195 | } 196 | ] 197 | } 198 | ] 199 | 200 | # header of generated file 201 | header = "// This file is automatically generated with misc/generate-keymap.py script\n\n" 202 | 203 | _dir = os.path.dirname(os.path.abspath(__file__)) 204 | 205 | standalone_actions = ["wrap_as_you_type", "expand_as_you_type", "rename_tag"] 206 | 207 | def create_record(k, v, os_type): 208 | if isinstance(v, basestring): 209 | v = {"keys": [v]} 210 | else: 211 | v = copy.deepcopy(v) 212 | 213 | if os_type in v: 214 | v['keys'] = [v[os_type]] 215 | 216 | if 'pc' in v: 217 | del v['pc'] 218 | 219 | if 'mac' in v: 220 | del v['mac'] 221 | 222 | if k in standalone_actions: 223 | v['command'] = k 224 | else: 225 | v['command'] = 'run_emmet_action' 226 | v['args'] = {"action": k} 227 | 228 | if 'context' not in v: 229 | v['context'] = [] 230 | 231 | v['context'].append({'key': 'emmet_action_enabled.%s' % k}) 232 | 233 | if len(v['context']) > 1: 234 | for ctx in v['context']: 235 | ctx['match_all'] = True 236 | 237 | return v 238 | 239 | def generate_keymap_file(path): 240 | os_type = 'mac' if '(OSX)' in path else 'pc' 241 | path = os.path.abspath(os.path.join(_dir, path)) 242 | print('Generate %s (%s)' % (path, os_type)) 243 | 244 | editor_keymap = [create_record(k, v, os_type) for k, v in keymap.items()] + addon 245 | content = json.dumps(editor_keymap, indent=4) 246 | f = open(path, 'w') 247 | f.write(header + content) 248 | f.close() 249 | 250 | for path in ['../Default (OSX).sublime-keymap', '../Default (Windows).sublime-keymap', '../Default (Linux).sublime-keymap']: 251 | generate_keymap_file(path) 252 | 253 | 254 | -------------------------------------------------------------------------------- /emmet/context.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | import sys 3 | import os 4 | import os.path 5 | import codecs 6 | import json 7 | import gc 8 | import imp 9 | import re 10 | from file import File 11 | 12 | BASE_PATH = os.path.abspath(os.path.dirname(__file__)) 13 | is_python3 = sys.version_info[0] > 2 14 | 15 | ctx_info = { 16 | 'context': None, 17 | 'callbacks': [], 18 | 'reload_callbacks': [] 19 | } 20 | 21 | # Module callbacks and global JS context sharing 22 | def on_context_created(cb): 23 | if ctx_info['context']: 24 | cb(ctx_info['context']) 25 | else: 26 | ctx_info['callbacks'].append(cb) 27 | 28 | def on_context_reload(cb): 29 | ctx_info['reload_callbacks'].append(cb) 30 | 31 | def on_module_reload(): 32 | for c in ctx_info['reload_callbacks']: 33 | c() 34 | 35 | ctx_info['reload_callbacks'] = ctx_info['callbacks'] = [] 36 | 37 | def remove_reload_callback(cb): 38 | if cb in ctx_info['reload_callbacks']: 39 | ctx_info['reload_callbacks'].remove() 40 | 41 | def set_global_context(ctx): 42 | ctx_info['context'] = ctx 43 | for c in ctx_info['callbacks']: 44 | c(ctx) 45 | 46 | ctx_info['callbacks'] = [] 47 | 48 | ################################################ 49 | 50 | core_files = ['emmet-app.js', 'python-wrapper.js'] 51 | 52 | def should_use_unicode(): 53 | """ 54 | WinXP unable to eval JS in unicode object (while other OSes requires it) 55 | This function checks if we have to use unicode when reading files 56 | """ 57 | ctx = PyV8.JSContext() 58 | ctx.enter() 59 | use_unicode = True 60 | try: 61 | ctx.eval(u'(function(){return;})()') 62 | except: 63 | use_unicode = False 64 | 65 | ctx.leave() 66 | 67 | return use_unicode 68 | 69 | def make_path(filename): 70 | return os.path.normpath(os.path.join(BASE_PATH, filename)) 71 | 72 | def js_log(message): 73 | print(message) 74 | 75 | def js_file_reader(file_path, use_unicode=True): 76 | if use_unicode: 77 | f = codecs.open(file_path, 'r', 'utf-8') 78 | else: 79 | f = open(file_path, 'r') 80 | 81 | content = f.read() 82 | f.close() 83 | 84 | return content 85 | 86 | def import_pyv8(): 87 | # Importing non-existing modules is a bit tricky in Python: 88 | # if we simply call `import PyV8` and module doesn't exists, 89 | # Python will cache this failed import and will always 90 | # throw exception even if this module appear in PYTHONPATH. 91 | # To prevent this, we have to manually test if 92 | # PyV8.py(c) exists in PYTHONPATH before importing PyV8 93 | if 'PyV8' in sys.modules: 94 | # PyV8 was loaded by ST2, create global alias 95 | if 'PyV8' not in globals(): 96 | globals()['PyV8'] = __import__('PyV8') 97 | 98 | return 99 | 100 | loaded = False 101 | f, pathname, description = imp.find_module('PyV8') 102 | bin_f, bin_pathname, bin_description = imp.find_module('_PyV8') 103 | if f: 104 | try: 105 | imp.acquire_lock() 106 | globals()['_PyV8'] = imp.load_module('_PyV8', bin_f, bin_pathname, bin_description) 107 | globals()['PyV8'] = imp.load_module('PyV8', f, pathname, description) 108 | imp.release_lock() 109 | loaded = True 110 | finally: 111 | # Since we may exit via an exception, close fp explicitly. 112 | if f: 113 | f.close() 114 | if bin_f: 115 | bin_f.close() 116 | 117 | if not loaded: 118 | raise ImportError('No PyV8 module found') 119 | 120 | class Context(): 121 | """ 122 | Creates Emmet JS core context. 123 | Before instantiating this class, make sure PyV8 124 | is available in `sys.path` 125 | 126 | @param files: Additional files to load with JS core 127 | @param path: Path to Emmet extensions 128 | @param contrib: Python objects to contribute to JS execution context 129 | @param pyv8_path: Location of PyV8 binaries 130 | """ 131 | def __init__(self, files=[], ext_path=None, contrib=None, logger=None, reader=js_file_reader): 132 | self.logger = logger 133 | self.reader = reader 134 | 135 | try: 136 | import_pyv8() 137 | except ImportError as e: 138 | pass 139 | 140 | self._ctx = None 141 | self._ctx_inited = False 142 | self._contrib = contrib 143 | self._should_load_extension = True 144 | 145 | # detect reader encoding 146 | self._use_unicode = None 147 | self._core_files = [] + core_files + files 148 | 149 | self._ext_path = None 150 | self.set_ext_path(ext_path) 151 | self._user_data = None 152 | 153 | set_global_context(self) 154 | 155 | def log(self, message): 156 | if self.logger: 157 | self.logger(message) 158 | 159 | def get_ext_path(self): 160 | return self._ext_path 161 | 162 | def set_ext_path(self, val): 163 | try: 164 | if val and val[:1] == '~': 165 | val = os.path.expanduser(val) 166 | 167 | val = os.path.abspath(val) 168 | except Exception as e: 169 | return 170 | 171 | if val == self._ext_path: 172 | return 173 | 174 | self._ext_path = val 175 | self.reset() 176 | 177 | def load_extensions(self, path=None): 178 | if path is None: 179 | path = self._ext_path; 180 | 181 | if path and os.path.isdir(path): 182 | ext_files = [] 183 | self.log('Loading Emmet extensions from %s' % self._ext_path) 184 | for dirname, dirnames, filenames in os.walk(self._ext_path): 185 | for filename in filenames: 186 | if filename[0] != '.': 187 | ext_files.append(os.path.join(dirname, filename)) 188 | 189 | self.js().locals.pyLoadExtensions(ext_files) 190 | 191 | def js(self): 192 | "Returns JS context" 193 | if not self._ctx: 194 | try: 195 | import_pyv8() 196 | except ImportError as e: 197 | return None 198 | 199 | if 'PyV8' not in sys.modules: 200 | # Binary is not available yet 201 | return None 202 | 203 | if self._use_unicode is None: 204 | self._use_unicode = should_use_unicode() 205 | 206 | self._ctx_inited = False 207 | 208 | class JSContext(PyV8.JSContext): 209 | def __enter__(self): 210 | if not hasattr(self, '_counter'): 211 | self._counter = 0 212 | if not self._counter: 213 | self.lock = PyV8.JSLocker() 214 | self.lock.enter() 215 | self.enter() 216 | # print('Enter JS context') 217 | 218 | self._counter += 1 219 | return self 220 | 221 | def __exit__(self, exc_type, exc_value, traceback): 222 | self._counter -= 1 223 | if self._counter < 1 or exc_type is not None: 224 | # print('Exit JS context') 225 | self._counter = 0 226 | if self: 227 | self.leave() 228 | if self.lock: 229 | self.lock.leave() 230 | self.lock = None 231 | 232 | self._ctx = JSContext() 233 | 234 | for f in self._core_files: 235 | self.eval_js_file(f) 236 | 237 | with self._ctx as ctx: 238 | # load default snippets 239 | ctx.locals.pyLoadSystemSnippets(self.read_js_file(make_path('snippets.json'))) 240 | 241 | # expose some methods 242 | ctx.locals.log = js_log 243 | ctx.locals.pyFile = File() 244 | 245 | if self._contrib: 246 | for k in self._contrib: 247 | ctx.locals[k] = self._contrib[k] 248 | 249 | self._ctx_inited = True 250 | 251 | # if not hasattr(PyV8.JSContext.current.locals, 'isEmmet'): 252 | # print('Enter Emmet context') 253 | # self._ctx.enter() 254 | 255 | if self._ctx_inited: 256 | with self._ctx as ctx: 257 | if self._should_load_extension: 258 | ctx.locals.pyResetUserData() 259 | self._should_load_extension = False 260 | self.load_extensions() 261 | 262 | if self._user_data: 263 | ctx.locals.pyLoadUserData(self._user_data) 264 | self._user_data = None 265 | 266 | return self._ctx 267 | 268 | def load_user_data(self, data): 269 | "Loads user data payload from JSON" 270 | self._user_data = data 271 | # self.js().locals.pyLoadUserData(data) 272 | 273 | def reset(self): 274 | "Resets JS execution context" 275 | if self._ctx: 276 | # self._ctx.leave() 277 | self._ctx = None 278 | try: 279 | PyV8.JSEngine.collect() 280 | gc.collect() 281 | except: 282 | pass 283 | 284 | self._should_load_extension = True 285 | 286 | def read_js_file(self, file_path, resolve_path=False): 287 | full_path = make_path(file_path) if resolve_path else file_path 288 | return self.reader(full_path, self._use_unicode) 289 | 290 | def eval(self, source): 291 | with self.js() as ctx: 292 | ctx.eval(source) 293 | 294 | def eval_js_file(self, file_path, resolve_path=True): 295 | with self.js() as ctx: 296 | ctx.eval(self.read_js_file(file_path, resolve_path), name=file_path, line=0, col=0) 297 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Emmet for Sublime Text 2 | 3 | [![Get Support](http://codersclan.net/graphics/getSupport_github4.png)](http://codersclan.net/support/step1.php?repo_id=4) 4 | 5 | Official [Emmet](http://emmet.io) plugin (previously called _Zen Coding_) for Sublime Text. 6 | 7 | * [How to install](#how-to-install) 8 | * [Available actions](#available-actions) 9 | * [Extensions support](#extensions-support) 10 | * [Overriding keyboard shortcuts](#overriding-keyboard-shortcuts) 11 | * [Tab key handler](#tab-key-handler) 12 | 13 | ## How to install 14 | 15 | *Warning:* this plugin may not work at all in some OSes since it written in JavaScript and uses [PyV8](http://code.google.com/p/pyv8/) and [Google V8](https://developers.google.com/v8/) binaries to run. If you experience problems or editor crashes please [fill an issue](https://github.com/sergeche/emmet-sublime/issues). 16 | 17 | With [Package Control](http://wbond.net/sublime_packages/package_control): 18 | 19 | 1. Run “Package Control: Install Package” command, find and install `Emmet` plugin. 20 | 2. Restart ST editor (if required) 21 | 22 | Manually: 23 | 24 | 1. Clone or [download](https://github.com/sergeche/emmet-sublime/archive/master.zip) git repo into your packages folder (in ST, find Browse Packages... menu item to open this folder) 25 | 2. Restart ST editor (if required) 26 | 27 | -------------- 28 | 29 | **WARNING**: When plugin is installed, it will automatically download required PyV8 binary so you have to wait a bit (see _Loading PyV8 binary_ message on status bar). If you experience issues with automatic PyV8 loader, try to [install it manually](https://github.com/emmetio/pyv8-binaries). 30 | 31 | ## New features of Emmet (compared with old Zen Coding) 32 | 33 | * [Dynamic CSS abbreviations](http://docs.emmet.io/css-abbreviations/), automatic [vendor prefixes](http://docs.emmet.io/css-abbreviations/vendor-prefixes/) and [gradient generator](http://docs.emmet.io/css-abbreviations/gradients/). 34 | * [“Lorem Ipsum” generator](http://docs.emmet.io/abbreviations/lorem-ipsum/) 35 | * [Implicit tag names](http://docs.emmet.io/abbreviations/implicit-names/) 36 | * New [Yandex’s BEM filter](http://docs.emmet.io/filters/bem/) 37 | * [Extensions support](http://docs.emmet.io/customization/) 38 | * New [^ operator](http://docs.emmet.io/abbreviations/syntax/) 39 | * Various fixes and improvements 40 | 41 | ## Available actions ## 42 | 43 | * [Expand Abbreviation](http://docs.emmet.io/actions/expand-abbreviation/) – Tab or Ctrl+E 44 | * Interactive “Expand Abbreviation” — Ctrl+Alt+Enter 45 | * [Match Tag Pair Outward](http://docs.emmet.io/actions/match-pair/) – ⌃D (Mac) / Ctrl+, (PC) 46 | * [Match Tag Pair Inward](http://docs.emmet.io/actions/match-pair/) – ⌃J / Shift+Ctrl+0 47 | * [Go to Matching Pair](http://docs.emmet.io/actions/go-to-pair/) – ⇧⌃T / Ctrl+Alt+J 48 | * [Wrap With Abbreviation](http://docs.emmet.io/actions/wrap-with-abbreviation/) — ⌃W / Shift+Ctrl+G 49 | * [Go to Edit Point](http://docs.emmet.io/actions/go-to-edit-point/) — Ctrl+Alt+→ or Ctrl+Alt+← 50 | * [Select Item](http://docs.emmet.io/actions/select-item/) – ⇧⌘. or ⇧⌘, / Shift+Ctrl+. or Shift+Ctrl+, 51 | * [Toggle Comment](http://docs.emmet.io/actions/toggle-comment/) — ⇧⌥/ / Shift+Ctrl+/ 52 | * [Split/Join Tag](http://docs.emmet.io/actions/split-join-tag/) — ⇧⌘' / Shift+Ctrl+` 53 | * [Remove Tag](http://docs.emmet.io/actions/remove-tag/) – ⌘' / Shift+Ctrl+; 54 | * [Update Image Size](http://docs.emmet.io/actions/update-image-size/) — ⇧⌃I / Ctrl+U 55 | * [Evaluate Math Expression](http://docs.emmet.io/actions/evaluate-math/) — ⇧⌘Y / Shift+Ctrl+Y 56 | * [Reflect CSS Value](http://docs.emmet.io/actions/reflect-css-value/) – ⇧⌘R / Shift+Ctrl+R 57 | * [Encode/Decode Image to data:URL](http://docs.emmet.io/actions/base64/) – ⇧⌃D / Ctrl+' 58 | * Rename Tag – ⇧⌘K / Shift+Ctrl+' 59 | 60 | [Increment/Decrement Number](http://docs.emmet.io/actions/inc-dec-number/) actions: 61 | 62 | * Increment by 1: Ctrl+↑ 63 | * Decrement by 1: Ctrl+↓ 64 | * Increment by 0.1: Alt+↑ 65 | * Decrement by 0.1: Alt+↓ 66 | * Increment by 10: ⌥⌘↑ / Shift+Alt+↑ 67 | * Decrement by 10: ⌥⌘↓ / Shift+Alt+↓ 68 | 69 | ## Extensions support ## 70 | 71 | You can easily [extend](http://docs.emmet.io/customization/) Emmet with new actions and filters or customize existing ones. In `Emmet.sublime-settings`, define `extensions_path` setting and Emmet will load all `.js` and `.json` files in specified folder at startup. 72 | 73 | The default value of `extensions_path` is `~/emmet`, which points to _emmet_ folder inside your OS user’s home folder. 74 | 75 | Also, you can create sections named as extension files (e.g. `snippets`, `preferences` and `syntaxProfiles`) inside user’s `Emmet.sublime-settings` file and write your customizations there. See [original settings file](https://github.com/sergeche/emmet-sublime/blob/master/Emmet.sublime-settings#L61) for examples. 76 | 77 | ## Overriding keyboard shortcuts ## 78 | 79 | Sublime Text is a great text editor with lots of features and actions. Most of these actions are bound to keyboard shortcuts so it’s nearly impossible to provide convenient plugin shortcuts for third-party plugins. 80 | 81 | If you’re unhappy with default keymap, you can disable individual keyboard shortcuts with `disabled_keymap_actions` preference of `Emmet.sublime-settings` file. 82 | 83 | Use a comma-separated list of action names which default keyboard shortcuts should be disabled. For example, if you want to release Ctrl+E (“Expand Abbreviation”) and Ctrl+U (“Update Image Size”) shortcuts, your must set the following value: 84 | 85 | "disabled_keymap_actions": "expand_abbreviation, update_image_size" 86 | 87 | You should refer `Default (Your-OS-Name).sublime-keymap` file to get action ids (look for `args/action` key). 88 | 89 | To disable all default shortcuts, set value to `all`: 90 | 91 | "disabled_keymap_actions": "all" 92 | 93 | Not that if you disabled any action like so and you’re create your own keyboard shortcut, you **should not** use `emmet_action_enabled.ACTION_NAME` context since this is the key that disables action. 94 | 95 | ### Tab key handler ### 96 | 97 | Emmet plugin allows you to expand abbreviations with Tab key, just like regular snippets. On the other hand, due to dynamic nature and extensive syntax, sometimes you may get unexpected results. This section describes how Tab handler works and how you can fine-tune it. 98 | 99 | By default, Tab handler works in a limited _syntax scopes_: HTML, XML, HAML, CSS, SASS/SCSS, LESS and _strings in programming languages_ (like JavaScript, Python, Ruby etc.). It means: 100 | 101 | * You have to switch your document to one of the syntaxes listed above to expand abbreviations by Tab key. 102 | * With Ctrl-E shortcut, you can expand abbreviations everywhere, its scope is not limited. 103 | * When you expand abbreviation inside strings of programming languages, the output is generated with special [output profile](http://docs.emmet.io/customization/syntax-profiles/) named `line` that generates output as a single line. 104 | 105 | To fine-tune Tab key handler, you can use the following settings in user’s `Emmet.sublime-settings` file: 106 | 107 | * `disable_tab_abbreviations_for_scopes` — a comma-separated list of syntax scopes where Tab key handler should be disabled. For example, if you want disable handler inside strings of programming languages and HAML syntax, your setting will look like this: 108 | 109 | ```json 110 | "disable_tab_abbreviations_for_scopes": "text.haml, string" 111 | ``` 112 | 113 | * `disabled_single_snippet_for_scopes` — a comma-separated list of syntax scopes where Tab handler should be disabled when expanding a single abbreviation. Currently, ST doesn’t provide API for getting list of native snippets. So, for example, if you try to expand a `php` abbreviation, it will be passed to Emmet which outputs `` instead of PHP block as defined in native ST snippets. As a workaround, if you’re trying to expand a single abbreviation inside scope defined in `disabled_single_snippet_for_scopes` setting Emmet will look for its name inside its own [snippets catalog](http://docs.emmet.io/cheat-sheet/) first, inside `known_html_tags` setting second and if it’s not found, it allows ST to handle it and expand native abbreviation, if matched. 114 | * `known_html_tags` — a space-separated list of all known HTML tags used for lookup as described above. 115 | 116 | If you’re unhappy with Emmet tab handler behavior, you can disable it: just add `"disable_tab_abbreviations": true` into user’s `Preferences.sublime-settings` file. 117 | -------------------------------------------------------------------------------- /editor.js: -------------------------------------------------------------------------------- 1 | function activeView() { 2 | return sublime.active_window().active_view(); 3 | } 4 | 5 | var editorProxy = emmet.exec(function(require, _) { 6 | return { 7 | getSelectionRange: function() { 8 | var view = activeView(); 9 | var sel = view.sel()[0]; 10 | return { 11 | start: sel.begin(), 12 | end: sel.end() 13 | }; 14 | }, 15 | 16 | createSelection: function(start, end) { 17 | var view = activeView(); 18 | view.sel().clear(); 19 | 20 | view.sel().add(new sublime.Region(start, end || start)); 21 | view.show(view.sel()); 22 | }, 23 | 24 | getCurrentLineRange: function() { 25 | var view = activeView(); 26 | var selection = view.sel()[0]; 27 | var line = view.line(selection); 28 | return { 29 | start: line.begin(), 30 | end: line.end() 31 | }; 32 | }, 33 | 34 | getCaretPos: function() { 35 | var view = activeView(); 36 | var sel = view.sel(); 37 | return sel && sel[0] ? sel[0].begin() : 0; 38 | }, 39 | 40 | setCaretPos: function(pos){ 41 | this.createSelection(pos, pos); 42 | }, 43 | 44 | getCurrentLine: function() { 45 | var view = activeView(); 46 | return view.substr(view.line(view.sel()[0])); 47 | }, 48 | 49 | replaceContent: function(value, start, end, noIndent) { 50 | if (_.isUndefined(end)) 51 | end = _.isUndefined(start) ? this.getContent().length : start; 52 | if (_.isUndefined(start)) start = 0; 53 | 54 | // update tabstops: make sure all caret placeholder are unique 55 | // by default, abbreviation parser generates all unlinked (un-mirrored) 56 | // tabstops as ${0}, so we have upgrade all caret tabstops with unique 57 | // positions but make sure that all other tabstops are not linked accidentally 58 | value = pyPreprocessText(value); 59 | sublimeReplaceSubstring(start, end, value, !!noIndent); 60 | }, 61 | 62 | getContent: function() { 63 | var view = activeView(); 64 | return view.substr(new sublime.Region(0, view.size())); 65 | }, 66 | 67 | getSyntax: function() { 68 | return pyGetSyntax(); 69 | }, 70 | 71 | getProfileName: function() { 72 | var view = activeView(); 73 | var pos = this.getCaretPos(); 74 | 75 | if (view.match_selector(pos, 'text.html') 76 | && sublimeGetOption('autodetect_xhtml', false) 77 | && require('actionUtils').isXHTML(this)) { 78 | return 'xhtml'; 79 | } 80 | 81 | if (view.match_selector(pos, 'string.quoted.double.block.python') 82 | || view.match_selector(pos, 'source.coffee string') 83 | || view.match_selector(pos, 'string.unquoted.heredoc')) { 84 | // use html's default profile for: 85 | // * Python's multiline block 86 | // * CoffeeScript string 87 | // * PHP heredoc 88 | return pyDetectProfile(); 89 | } 90 | 91 | if (view.score_selector(pos, 'source string')) { 92 | return 'line'; 93 | } 94 | 95 | return pyDetectProfile(); 96 | }, 97 | 98 | prompt: function(title) { 99 | return pyEditor.prompt(); 100 | }, 101 | 102 | getSelection: function() { 103 | var view = activeView(); 104 | return view.sel() ? view.substr(view.sel()[0]) : ''; 105 | }, 106 | 107 | getFilePath: function() { 108 | return activeView().file_name(); 109 | } 110 | }; 111 | }); 112 | 113 | var _completions = {}; 114 | 115 | function require(name) { 116 | return emmet.require(name); 117 | } 118 | 119 | function pyPreprocessText(value) { 120 | var base = 1000; 121 | var zeroBase = 0; 122 | var lastZero = null; 123 | var range = require('range'); 124 | var ts = require('tabStops'); 125 | 126 | var tabstopOptions = { 127 | tabstop: function(data) { 128 | var group = parseInt(data.group, 10); 129 | var isZero = group === 0; 130 | if (isZero) 131 | group = ++zeroBase; 132 | else 133 | group += base; 134 | 135 | var placeholder = data.placeholder; 136 | if (placeholder) { 137 | // recursively update nested tabstops 138 | placeholder = ts.processText(placeholder, tabstopOptions); 139 | } 140 | 141 | var result = '${' + group + (placeholder ? ':' + placeholder : '') + '}'; 142 | 143 | if (isZero) { 144 | lastZero = range.create(data.start, result); 145 | } 146 | 147 | return result 148 | }, 149 | escape: function(ch) { 150 | if (ch == '$') { 151 | return '\\$'; 152 | } 153 | 154 | if (ch == '\\') { 155 | return '\\\\'; 156 | } 157 | 158 | return ch; 159 | } 160 | }; 161 | 162 | value = ts.processText(value, tabstopOptions); 163 | 164 | if (sublimeGetOption('insert_final_tabstop', false) && !/\$\{0\}$/.test(value)) { 165 | value += '${0}'; 166 | } else if (lastZero) { 167 | value = require('utils').replaceSubstring(value, '${0}', lastZero); 168 | } 169 | 170 | return value; 171 | } 172 | 173 | function pyExpandAbbreviationAsYouType(abbr) { 174 | var info = require('editorUtils').outputInfo(editorProxy); 175 | try { 176 | var result = emmet.expandAbbreviation(abbr, info.syntax, info.profile, 177 | require('actionUtils').captureContext(editorProxy)); 178 | return pyPreprocessText(result); 179 | } catch (e) { 180 | return ''; 181 | } 182 | 183 | } 184 | 185 | function pyWrapAsYouType(abbr, content) { 186 | var info = require('editorUtils').outputInfo(editorProxy); 187 | content = require('utils').escapeText(content); 188 | var ctx = require('actionUtils').captureContext(editorProxy); 189 | try { 190 | var result = require('wrapWithAbbreviation').wrap(abbr, content, info.syntax, info.profile, ctx); 191 | return pyPreprocessText(result); 192 | } catch(e) { 193 | return ''; 194 | } 195 | } 196 | 197 | function pyCaptureWrappingRange() { 198 | var info = require('editorUtils').outputInfo(editorProxy); 199 | var range = editorProxy.getSelectionRange(); 200 | var startOffset = range.start; 201 | var endOffset = range.end; 202 | 203 | if (startOffset == endOffset) { 204 | // no selection, find tag pair 205 | var match = require('htmlMatcher').find(info.content, startOffset); 206 | if (!match) { 207 | // nothing to wrap 208 | return null; 209 | } 210 | 211 | /** @type Range */ 212 | var utils = require('utils'); 213 | var narrowedSel = utils.narrowToNonSpace(info.content, match.range); 214 | startOffset = narrowedSel.start; 215 | endOffset = narrowedSel.end; 216 | } 217 | 218 | return [startOffset, endOffset]; 219 | } 220 | 221 | function pyGetTagNameRanges(pos) { 222 | var ranges = []; 223 | var info = require('editorUtils').outputInfo(editorProxy); 224 | 225 | // search for tag 226 | try { 227 | var tag = require('htmlMatcher').tag(info.content, pos); 228 | if (tag) { 229 | var open = tag.open.range; 230 | var tagName = /^<([\w\-\:]+)/i.exec(open.substring(info.content))[1]; 231 | ranges.push([open.start + 1, open.start + 1 + tagName.length]); 232 | 233 | if (tag.close) { 234 | ranges.push([tag.close.range.start + 2, tag.close.range.start + 2 + tagName.length]); 235 | } 236 | } 237 | } catch (e) {} 238 | 239 | return ranges; 240 | } 241 | 242 | function pyGetTagRanges() { 243 | var ranges = []; 244 | var info = require('editorUtils').outputInfo(editorProxy); 245 | 246 | // search for tag 247 | try { 248 | var tag = require('htmlMatcher').tag(info.content, editorProxy.getCaretPos()); 249 | if (tag) { 250 | ranges.push(tag.open.range.toArray()); 251 | if (tag.close) { 252 | ranges.push(tag.close.range.toArray()); 253 | } 254 | } 255 | } catch (e) {} 256 | 257 | return ranges; 258 | } 259 | 260 | function pyExtractAbbreviation() { 261 | return require('expandAbbreviation').findAbbreviation(editorProxy); 262 | } 263 | 264 | function pyHasSnippet(name) { 265 | return !!emmet.require('resources').findSnippet(editorProxy.getSyntax(), name); 266 | } 267 | 268 | /** 269 | * Get all available CSS completions. This method is optimized for CSS 270 | * only since it should contain snippets only so it's not required 271 | * to do extra parsing 272 | */ 273 | function pyGetCSSCompletions(dialect) { 274 | dialect = dialect || pyGetSyntax(); 275 | 276 | if (!_completions[dialect]) { 277 | var all = require('resources').getAllSnippets(dialect); 278 | var css = require('cssResolver'); 279 | _completions[dialect] = _.map(all, function(v, k) { 280 | var snippetValue = typeof v.parsedValue == 'object' 281 | ? v.parsedValue.data 282 | : v.value; 283 | var snippet = css.transformSnippet(snippetValue, false, dialect); 284 | return { 285 | k: v.nk, 286 | label: snippet.replace(/\:\s*\$\{0\}\s*;?$/, ''), 287 | v: css.expandToSnippet(v.nk, dialect) 288 | }; 289 | }); 290 | } 291 | 292 | return _completions[dialect]; 293 | } 294 | 295 | /** 296 | * Returns current syntax name 297 | * @return {String} 298 | */ 299 | function pyGetSyntax() { 300 | var view = activeView(); 301 | var pt = view.sel()[0].begin(); 302 | var scope = 'scope_name' in view ? view.scope_name(pt) : view.syntax_name(pt); 303 | 304 | if (~scope.indexOf('xsl')) { 305 | return 'xsl'; 306 | } 307 | 308 | var syntax = 'html'; 309 | 310 | if (!/\bstring\b/.test(scope) && /\bsource\.([\w\-]+)/.test(scope) && require('resources').hasSyntax(RegExp.$1)) { 311 | syntax = RegExp.$1; 312 | } else if (/\b(less|scss|sass|css|stylus)\b/.test(scope)) { 313 | // detect CSS-like syntaxes independently, 314 | // since it may cause collisions with some highlighters 315 | syntax = RegExp.$1; 316 | } else if (/\b(html|xml|haml)\b/.test(scope)) { 317 | syntax = RegExp.$1; 318 | } 319 | 320 | return require('actionUtils').detectSyntax(editorProxy, syntax); 321 | } 322 | 323 | function pyDetectProfile(argument) { 324 | return require('actionUtils').detectProfile(editorProxy); 325 | } -------------------------------------------------------------------------------- /Emmet.tmLanguage: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | comment 6 | Emmet abbreviation syntax by sublimator|castle_made_of_sand 7 | fileTypes 8 | 9 | em 10 | 11 | name 12 | Emmet 13 | patterns 14 | 15 | 16 | include 17 | #css_snippets 18 | 19 | 20 | include 21 | #html_abbrevs 22 | 23 | 24 | include 25 | #expandos 26 | 27 | 28 | include 29 | #html_elements 30 | 31 | 32 | include 33 | #filter 34 | 35 | 36 | include 37 | #element 38 | 39 | 40 | include 41 | #class 42 | 43 | 44 | include 45 | #operator 46 | 47 | 48 | include 49 | #id 50 | 51 | 52 | include 53 | #repeater 54 | 55 | 56 | include 57 | #repeat_quantifier 58 | 59 | 60 | begin 61 | \[ 62 | beginCaptures 63 | 64 | 0 65 | 66 | name 67 | punctuation.definition.begin.#30d556d0518a11e1bd8ab482fe507f0e 68 | 69 | 70 | end 71 | \] 72 | endCaptures 73 | 74 | 0 75 | 76 | name 77 | punctuation.definition.end.#30d556d1518a11e1ab2fb482fe507f0e 78 | 79 | 80 | name 81 | meta.tag.attributes.#30d556cf518a11e1afd8b482fe507f0e 82 | patterns 83 | 84 | 85 | include 86 | #attribute-value 87 | 88 | 89 | include 90 | #attribute-name 91 | 92 | 93 | include 94 | #double-string 95 | 96 | 97 | include 98 | #single-string 99 | 100 | 101 | 102 | 103 | begin 104 | \{ 105 | beginCaptures 106 | 107 | 0 108 | 109 | name 110 | punctuation.#30d556d4518a11e1baa6b482fe507f0e 111 | 112 | 113 | contentName 114 | string.#30d556d3518a11e1babab482fe507f0e 115 | end 116 | \} 117 | endCaptures 118 | 119 | 0 120 | 121 | name 122 | punctuation.#30d556d5518a11e1860bb482fe507f0e 123 | 124 | 125 | name 126 | string.#30d556d2518a11e1bccfb482fe507f0e 127 | patterns 128 | 129 | 130 | 131 | 132 | repository 133 | 134 | attribute-name 135 | 136 | match 137 | [a-zA-Z0-9:]+ 138 | name 139 | meta.tag entity.other.attribute-name.#30d556dd518a11e19355b482fe507f0e 140 | 141 | attribute-value 142 | 143 | captures 144 | 145 | 1 146 | 147 | name 148 | meta.tag entity.other.attribute-name.#30d556df518a11e18855b482fe507f0e 149 | 150 | 2 151 | 152 | name 153 | 154 | 155 | 3 156 | 157 | name 158 | string.#30d556e0518a11e1949eb482fe507f0e 159 | 160 | 161 | match 162 | ([a-zA-Z0-9:]+)(=)([^"'\] ]+) 163 | name 164 | meta.tag.#30d556de518a11e195ecb482fe507f0e 165 | 166 | class 167 | 168 | match 169 | \.[0-9a-zA-Z-_-]+ 170 | name 171 | entity.class.#30d556e4518a11e1a731b482fe507f0e 172 | 173 | css_snippets 174 | 175 | captures 176 | 177 | 1 178 | 179 | name 180 | 181 | 182 | 183 | match 184 | (d:tbc|wow:n|wow:u|wow:s|d:tbr|list:lr|bdl\+|bdf:c|bgbk:bb|bdbi:n|bdf:r|to:n|fst:ee|fst:ec|bgcp:cb|lisp:o|lisp:i|d:tbclg|@f|d:rbt|@i|@m|fef:eb|fef:eg|te:c|te:b|q:n|te:n|bdbs:n|bg:ie|d:li|tj:k|bd\+|fems:ac|fst:n|fst:c|fst:e|cur:t|cur:p|cur:m|maw:n|fw:n|bdtri:n|fw:b|bdtri:c|cur:a|oc:i|cur:c|bdls:n|tw:u|va:sub|tw:s|pgbi:av|tj:t|pgbi:au|va:sup|va:bl|tw:n|va:t|va:m|bds:dt|va:b|tsh\+|cps:t|cps:b|list:dclz|f\+|bd:n|!|bdl:n|bdl:a|td:u|tw:no|whs:nw|va:tb|h:a|d:ib|bgo:cb|d:i|va:tt|fef:o|wow:nm|ct:noq|pgba:al|bdli:n|d:tbhg|bga:s|fst:se|fst:sc|fsm:a|bga:f|fsm:n|tbl:f|tbl:a|bdci:n|whsc:k|bdci:c|whsc:l|bgi:n|bdb:n|bdbk:c|pgba:r|te:ac|l:a|bdr:n|whsc:ba|whsc:bs|b:a|bdt\+|ta:c|bdf:sp|ovy:a|bdb\+|d:tb|bdf:st|ti:-|ovy:h|ta:l|ovy:s|ta:r|bdf:sc|ovy:v|bdts:n|bdf:of|fl:l|fl:n|bdf:ow|fl:r|tt:c|tt:n|ec:s|tt:l|ec:h|tt:u|bdti:n|bgz:cv|bgz:ct|d:cp|fef:n|bdt:n|to\+|bdbri:n|bdbri:c|tsh:n|fv:sc|r:a|fems:ds|fems:dt|op:ms|bds:dtds|bgcp:nc|ct:ncq|bg:n|bdrs:n|ml:a|bds:dtdtds|ff:s|d:rbb|tj:d|tj:a|ff:f|fems:c|ff:c|d:itb|fems:n|ff:m|pgba:au|bdri:n|mt:a|wob:l|td:n|td:o|td:l|bxz:bb|bxsh:n|bxsh:m|bxsh:w|fw:br|ovs:p|ovs:s|w:a|fv:n|ovs:a|m:a|bgcp:pb|ovs:m|fsm:aw|pgbb:au|m:4|m:0|m:2|m:3|op:ie|fst:ue|fst:uc|pgbb:al|bdtli:n|bdtli:c|list:ur|tr:n|bgbk:c|ov:a|te:a|ov:h|ov:v|ov:s|cp:r|cp:a|lisi:n|rz:v|rz:n|rz:h|mb:a|rz:b|d:rb|d:ri|bds:db|d:tbfg|bds:ds|lis:n|d:b|tal:a|tal:c|tal:l|bgbk:eb|d:n|tal:r|tj:iw|bgr:x|bgr:y|bgr:n|list:c|list:d|bdbli:c|bdbli:n|list:n|list:s|mah:n|th:t|d:rbtg|th:f|th:a|ct:cq|ct:cs|th:m|bxz:cb|whs:p|wob:bs|fw:lr|whsc:n|te:ds|te:dt|whs:n|wob:ba|list:dc|d:tbrg|mr:a|q:en|ovs:mq|p:4|bgz:a|p:0|p:3|p:2|whs:pw|bg\+|bdcl:s|bdcl:c|wob:n|wob:k|tj:ic|tj:ii|t:a|bgcp:bb|ff:ss|cl:r|q:ru|cl:n|cl:l|cl:b|fs:o|fs:n|bds:h|bds:i|bds:n|bds:o|fs:i|ct:oq|bds:g|bds:r|bds:s|bds:w|z:a|ct:c|ct:a|pgbb:r|bgo:bb|ct:n|bdr\+|pgbb:l|d:tbcl|ovx:a|whs:pl|ovx:h|ovx:v|ovx:s|cur:d|d:tbcp|d:rbbg|femp:a|femp:b|o:n|pgba:l|pos:s|pos:r|bdi:m|bdi:n|pos:f|pos:a|bdi:w|fza:n|v:h|v:c|cur:he|cur:ha|bgo:pb|v:v) 185 | name 186 | support.zen.css.snippet.#30d556d9518a11e18531b482fe507f0e 187 | 188 | double-string 189 | 190 | begin 191 | " 192 | beginCaptures 193 | 194 | 0 195 | 196 | name 197 | punctuation.definition.begin.#30d556e7518a11e18d8eb482fe507f0e 198 | 199 | 200 | end 201 | " 202 | endCaptures 203 | 204 | 0 205 | 206 | name 207 | punctuation.definition.end.#30d556e8518a11e19ddeb482fe507f0e 208 | 209 | 210 | name 211 | string.quoted.double.#30d556e6518a11e1aa37b482fe507f0e 212 | patterns 213 | 214 | 215 | 216 | element 217 | 218 | match 219 | [a-zA-Z-:]+ 220 | name 221 | meta.tag.#30d556dc518a11e1833ab482fe507f0e 222 | 223 | expandos 224 | 225 | match 226 | (colgroup\+|optg\+|table\+|ol\+|tr\+|optgroup\+|dl\+|ul\+|select\+|colg\+|map\+)$ 227 | name 228 | support.zen.html.expandos.#30d556d7518a11e18b1ab482fe507f0e 229 | 230 | filter 231 | 232 | captures 233 | 234 | 1 235 | 236 | name 237 | keyword.pipe.#30d556da518a11e18e9cb482fe507f0e 238 | 239 | 2 240 | 241 | name 242 | entity.filter.#30d556db518a11e1947fb482fe507f0e 243 | 244 | 245 | match 246 | (\|)([a-zA-Z]+) 247 | 248 | html_abbrevs 249 | 250 | match 251 | (?:a:link|input:datetime-local|input:reset|colg|style|adr|img|bdo:l|param|form:post|bdo:r|fig|input:radio|link:print|opt|input:i|input:h|input:f|input:c|input:b|abbr|input:t|input:p|input:s|input:r|ifr|emb|cmd|link:atom|art|input:search|area:r|area:p|input:date|video|input:button|area:d|area:c|out|ftr|dlg|script:src|form:get|meta:utf|label|input:time|link:favicon|menu:toolbar|prog|input:email|str|leg|acronym|base|bq|src|obj|script|acr|input:password|input:file|tarea|select|input:number|input:range|area|input:image|input:month|fset|meta:win|menu:t|form|menu:c|link|input|link:rss|hdr|cap|det|link:touch|iframe|link:css|input:week|embed|optg|input:datetime|datag|option|html:xml|btn|input:url|menu:context|map|input:color|meta:compat|input:hidden|object|a:mail|a|datal|kg|textarea|input:submit|input:text|input:checkbox|fst|sect|audio|bdo) 252 | name 253 | entity.name.tag support.zen.html.abbrev.#30d556d6518a11e18a88b482fe507f0e 254 | patterns 255 | 256 | 257 | match 258 | : 259 | name 260 | fuck 261 | 262 | 263 | 264 | html_elements 265 | 266 | match 267 | \b(comment|bgsound|code|h2|h3|h1|h6|ilayer|h4|blink|header|table|font|u|select|noframes|noscript|style|span|img|area|mark|tt|var|tr|param|legend|source|dfn|tfoot|th|time|strike|input|td|xmp|cite|thead|dl|blockquote|fieldset|option|form|hr|big|dd|nobr|link|abbr|optgroup|li|dt|h5|ruby|noembed|pre|b|wbr|colgroup|button|isindex|keygen|p|applet|del|iframe|section|small|output|div|dir|em|frameset|layer|figure|datalist|frame|head|hgroup|meta|video|meter|summary|!DOCTYPE|rt|kbd|canvas|rp|sub|ul|tbody|bdo|aside|label|basefont|html|nav|details|sup|progress|samp|math|body|map|object|ins|acronym|marquee|figcaption|xml|base|br|address|article|strong|embed|a|ol|center|textarea|footer|i|svg|script|q|caption|s|command|menu|title|audio|col)\b 268 | name 269 | entity.name.tag support.zen.html.element.#30d556d8518a11e195e7b482fe507f0e 270 | 271 | id 272 | 273 | match 274 | #[a-zA-Z-_]+ 275 | name 276 | entity.id.#30d556e5518a11e1a0d0b482fe507f0e 277 | 278 | operator 279 | 280 | match 281 | >|\*|\+|:|\^ 282 | name 283 | keyword.#30d556e1518a11e1983ab482fe507f0e 284 | 285 | repeat_quantifier 286 | 287 | match 288 | \d 289 | name 290 | keyword.quantifier.#30d556e3518a11e18aefb482fe507f0e 291 | 292 | repeater 293 | 294 | match 295 | \$ 296 | name 297 | keyword.repeater.#30d556e2518a11e18320b482fe507f0e 298 | 299 | single-string 300 | 301 | begin 302 | ' 303 | beginCaptures 304 | 305 | 0 306 | 307 | name 308 | punctuation.definition.begin.#30d556ea518a11e1baedb482fe507f0e 309 | 310 | 311 | end 312 | ' 313 | endCaptures 314 | 315 | 0 316 | 317 | name 318 | punctuation.definition.end.#30d556eb518a11e19d9ab482fe507f0e 319 | 320 | 321 | name 322 | string.quoted.single.#30d556e9518a11e190e7b482fe507f0e 323 | patterns 324 | 325 | 326 | 327 | 328 | scopeName 329 | source.zen.5a454e6772616d6d6172 330 | uuid 331 | ffb80ea1-4cf0-11e1-b0dc-b482fe507f0e 332 | 333 | -------------------------------------------------------------------------------- /Default (Linux).sublime-keymap: -------------------------------------------------------------------------------- 1 | // This file is automatically generated with misc/generate-keymap.py script 2 | 3 | [ 4 | { 5 | "keys": [ 6 | "shift+ctrl+y" 7 | ], 8 | "args": { 9 | "action": "evaluate_math_expression" 10 | }, 11 | "command": "run_emmet_action", 12 | "context": [ 13 | { 14 | "key": "emmet_action_enabled.evaluate_math_expression" 15 | } 16 | ] 17 | }, 18 | { 19 | "keys": [ 20 | "shift+ctrl+;" 21 | ], 22 | "args": { 23 | "action": "remove_tag" 24 | }, 25 | "command": "run_emmet_action", 26 | "context": [ 27 | { 28 | "key": "emmet_action_enabled.remove_tag" 29 | } 30 | ] 31 | }, 32 | { 33 | "keys": [ 34 | "ctrl+up" 35 | ], 36 | "args": { 37 | "action": "increment_number_by_1" 38 | }, 39 | "command": "run_emmet_action", 40 | "context": [ 41 | { 42 | "key": "emmet_action_enabled.increment_number_by_1" 43 | } 44 | ] 45 | }, 46 | { 47 | "keys": [ 48 | "ctrl+shift+forward_slash" 49 | ], 50 | "args": { 51 | "action": "toggle_comment" 52 | }, 53 | "command": "run_emmet_action", 54 | "context": [ 55 | { 56 | "operand": "source.css - source.css.less, text.xml, text.html - source", 57 | "operator": "equal", 58 | "match_all": true, 59 | "key": "selector" 60 | }, 61 | { 62 | "match_all": true, 63 | "key": "emmet_action_enabled.toggle_comment" 64 | } 65 | ] 66 | }, 67 | { 68 | "keys": [ 69 | "shift+ctrl+." 70 | ], 71 | "args": { 72 | "action": "select_next_item" 73 | }, 74 | "command": "run_emmet_action", 75 | "context": [ 76 | { 77 | "key": "emmet_action_enabled.select_next_item" 78 | } 79 | ] 80 | }, 81 | { 82 | "keys": [ 83 | "ctrl+alt+enter" 84 | ], 85 | "command": "expand_as_you_type", 86 | "context": [ 87 | { 88 | "operand": false, 89 | "operator": "equal", 90 | "match_all": true, 91 | "key": "setting.is_widget" 92 | }, 93 | { 94 | "match_all": true, 95 | "key": "emmet_action_enabled.expand_as_you_type" 96 | } 97 | ] 98 | }, 99 | { 100 | "keys": [ 101 | "alt+down" 102 | ], 103 | "args": { 104 | "action": "decrement_number_by_01" 105 | }, 106 | "command": "run_emmet_action", 107 | "context": [ 108 | { 109 | "key": "emmet_action_enabled.decrement_number_by_01" 110 | } 111 | ] 112 | }, 113 | { 114 | "keys": [ 115 | "ctrl+'" 116 | ], 117 | "args": { 118 | "action": "encode_decode_data_url" 119 | }, 120 | "command": "run_emmet_action", 121 | "context": [ 122 | { 123 | "key": "emmet_action_enabled.encode_decode_data_url" 124 | } 125 | ] 126 | }, 127 | { 128 | "keys": [ 129 | "ctrl+shift+0" 130 | ], 131 | "args": { 132 | "action": "match_pair_inward" 133 | }, 134 | "command": "run_emmet_action", 135 | "context": [ 136 | { 137 | "key": "emmet_action_enabled.match_pair_inward" 138 | } 139 | ] 140 | }, 141 | { 142 | "keys": [ 143 | "shift+alt+up" 144 | ], 145 | "args": { 146 | "action": "increment_number_by_10" 147 | }, 148 | "command": "run_emmet_action", 149 | "context": [ 150 | { 151 | "key": "emmet_action_enabled.increment_number_by_10" 152 | } 153 | ] 154 | }, 155 | { 156 | "keys": [ 157 | "shift+ctrl+r" 158 | ], 159 | "args": { 160 | "action": "reflect_css_value" 161 | }, 162 | "command": "run_emmet_action", 163 | "context": [ 164 | { 165 | "key": "emmet_action_enabled.reflect_css_value" 166 | } 167 | ] 168 | }, 169 | { 170 | "keys": [ 171 | "ctrl+alt+left" 172 | ], 173 | "args": { 174 | "action": "prev_edit_point" 175 | }, 176 | "command": "run_emmet_action", 177 | "context": [ 178 | { 179 | "key": "emmet_action_enabled.prev_edit_point" 180 | } 181 | ] 182 | }, 183 | { 184 | "keys": [ 185 | "shift+ctrl+," 186 | ], 187 | "args": { 188 | "action": "select_previous_item" 189 | }, 190 | "command": "run_emmet_action", 191 | "context": [ 192 | { 193 | "key": "emmet_action_enabled.select_previous_item" 194 | } 195 | ] 196 | }, 197 | { 198 | "keys": [ 199 | "ctrl+," 200 | ], 201 | "args": { 202 | "action": "match_pair_outward" 203 | }, 204 | "command": "run_emmet_action", 205 | "context": [ 206 | { 207 | "key": "emmet_action_enabled.match_pair_outward" 208 | } 209 | ] 210 | }, 211 | { 212 | "keys": [ 213 | "ctrl+u" 214 | ], 215 | "args": { 216 | "action": "update_image_size" 217 | }, 218 | "command": "run_emmet_action", 219 | "context": [ 220 | { 221 | "key": "emmet_action_enabled.update_image_size" 222 | } 223 | ] 224 | }, 225 | { 226 | "keys": [ 227 | "ctrl+alt+right" 228 | ], 229 | "args": { 230 | "action": "next_edit_point" 231 | }, 232 | "command": "run_emmet_action", 233 | "context": [ 234 | { 235 | "key": "emmet_action_enabled.next_edit_point" 236 | } 237 | ] 238 | }, 239 | { 240 | "keys": [ 241 | "shift+ctrl+`" 242 | ], 243 | "args": { 244 | "action": "split_join_tag" 245 | }, 246 | "command": "run_emmet_action", 247 | "context": [ 248 | { 249 | "key": "emmet_action_enabled.split_join_tag" 250 | } 251 | ] 252 | }, 253 | { 254 | "keys": [ 255 | "shift+alt+down" 256 | ], 257 | "args": { 258 | "action": "decrement_number_by_10" 259 | }, 260 | "command": "run_emmet_action", 261 | "context": [ 262 | { 263 | "key": "emmet_action_enabled.decrement_number_by_10" 264 | } 265 | ] 266 | }, 267 | { 268 | "keys": [ 269 | "shift+ctrl+g" 270 | ], 271 | "command": "wrap_as_you_type", 272 | "context": [ 273 | { 274 | "operand": false, 275 | "operator": "equal", 276 | "match_all": true, 277 | "key": "setting.is_widget" 278 | }, 279 | { 280 | "match_all": true, 281 | "key": "emmet_action_enabled.wrap_as_you_type" 282 | } 283 | ] 284 | }, 285 | { 286 | "keys": [ 287 | "shift+ctrl+'" 288 | ], 289 | "command": "rename_tag", 290 | "context": [ 291 | { 292 | "key": "emmet_action_enabled.rename_tag" 293 | } 294 | ] 295 | }, 296 | { 297 | "keys": [ 298 | "alt+up" 299 | ], 300 | "args": { 301 | "action": "increment_number_by_01" 302 | }, 303 | "command": "run_emmet_action", 304 | "context": [ 305 | { 306 | "key": "emmet_action_enabled.increment_number_by_01" 307 | } 308 | ] 309 | }, 310 | { 311 | "keys": [ 312 | "ctrl+alt+j" 313 | ], 314 | "args": { 315 | "action": "matching_pair" 316 | }, 317 | "command": "run_emmet_action", 318 | "context": [ 319 | { 320 | "key": "emmet_action_enabled.matching_pair" 321 | } 322 | ] 323 | }, 324 | { 325 | "keys": [ 326 | "ctrl+down" 327 | ], 328 | "args": { 329 | "action": "decrement_number_by_1" 330 | }, 331 | "command": "run_emmet_action", 332 | "context": [ 333 | { 334 | "key": "emmet_action_enabled.decrement_number_by_1" 335 | } 336 | ] 337 | }, 338 | { 339 | "keys": [ 340 | "ctrl+e" 341 | ], 342 | "args": { 343 | "action": "expand_abbreviation" 344 | }, 345 | "command": "run_emmet_action", 346 | "context": [ 347 | { 348 | "key": "emmet_action_enabled.expand_abbreviation" 349 | } 350 | ] 351 | }, 352 | { 353 | "keys": [ 354 | "tab" 355 | ], 356 | "command": "expand_abbreviation_by_tab", 357 | "context": [ 358 | { 359 | "operand": "source.css, source.sass, source.less, source.scss, source.stylus, text.xml, text.html - source, text.haml, text.scala.html, source string", 360 | "operator": "equal", 361 | "match_all": true, 362 | "key": "selector" 363 | }, 364 | { 365 | "operand": "storage.type.templatetag.django", 366 | "operator": "not_equal", 367 | "match_all": true, 368 | "key": "selector" 369 | }, 370 | { 371 | "match_all": true, 372 | "key": "selection_empty" 373 | }, 374 | { 375 | "operator": "equal", 376 | "operand": false, 377 | "match_all": true, 378 | "key": "has_next_field" 379 | }, 380 | { 381 | "operator": "equal", 382 | "operand": false, 383 | "match_all": true, 384 | "key": "setting.disable_tab_abbreviations" 385 | }, 386 | { 387 | "operand": false, 388 | "operator": "equal", 389 | "match_all": true, 390 | "key": "auto_complete_visible" 391 | }, 392 | { 393 | "match_all": true, 394 | "key": "is_abbreviation" 395 | } 396 | ] 397 | }, 398 | { 399 | "keys": [ 400 | "tab" 401 | ], 402 | "command": "expand_abbreviation_by_tab", 403 | "context": [ 404 | { 405 | "operand": "source.css, source.sass, source.less, source.scss, source.stylus, text.xml, text.html - source, text.haml, text.scala.html, source string", 406 | "operator": "equal", 407 | "match_all": true, 408 | "key": "selector" 409 | }, 410 | { 411 | "operand": "storage.type.templatetag.django", 412 | "operator": "not_equal", 413 | "match_all": true, 414 | "key": "selector" 415 | }, 416 | { 417 | "match_all": true, 418 | "key": "selection_empty" 419 | }, 420 | { 421 | "operator": "equal", 422 | "operand": false, 423 | "match_all": true, 424 | "key": "has_next_field" 425 | }, 426 | { 427 | "operator": "equal", 428 | "operand": true, 429 | "match_all": true, 430 | "key": "auto_complete_visible" 431 | }, 432 | { 433 | "operator": "equal", 434 | "operand": false, 435 | "match_all": true, 436 | "key": "setting.disable_tab_abbreviations_on_auto_complete" 437 | }, 438 | { 439 | "match_all": true, 440 | "key": "is_abbreviation" 441 | } 442 | ] 443 | }, 444 | { 445 | "keys": [ 446 | "enter" 447 | ], 448 | "args": { 449 | "contents": "\n\t${0}\n" 450 | }, 451 | "command": "insert_snippet", 452 | "context": [ 453 | { 454 | "operand": "meta.scope.between-tag-pair.html, meta.scope.between-tag-pair.xml", 455 | "match_all": true, 456 | "key": "selector" 457 | }, 458 | { 459 | "operand": false, 460 | "match_all": true, 461 | "key": "auto_complete_visible" 462 | }, 463 | { 464 | "match_all": true, 465 | "key": "clear_fields_on_enter_key" 466 | }, 467 | { 468 | "operand": false, 469 | "match_all": true, 470 | "key": "setting.disable_formatted_linebreak" 471 | } 472 | ] 473 | }, 474 | { 475 | "keys": [ 476 | "#" 477 | ], 478 | "args": { 479 | "attribute": "id" 480 | }, 481 | "command": "emmet_insert_attribute", 482 | "context": [ 483 | { 484 | "operand": "text.html meta.tag -string -punctuation.definition.tag.begin.html -meta.scope.between-tag-pair.html -source -meta.tag.template.value.twig", 485 | "operator": "equal", 486 | "match_all": true, 487 | "key": "selector" 488 | }, 489 | { 490 | "operator": "equal", 491 | "operand": true, 492 | "key": "setting.auto_id_class" 493 | } 494 | ] 495 | }, 496 | { 497 | "keys": [ 498 | "." 499 | ], 500 | "args": { 501 | "attribute": "class" 502 | }, 503 | "command": "emmet_insert_attribute", 504 | "context": [ 505 | { 506 | "operand": "text.html meta.tag -string -punctuation.definition.tag.begin.html -meta.scope.between-tag-pair.html -source -meta.tag.template.value.twig", 507 | "operator": "equal", 508 | "match_all": true, 509 | "key": "selector" 510 | }, 511 | { 512 | "operator": "equal", 513 | "operand": true, 514 | "key": "setting.auto_id_class" 515 | } 516 | ] 517 | } 518 | ] -------------------------------------------------------------------------------- /Default (OSX).sublime-keymap: -------------------------------------------------------------------------------- 1 | // This file is automatically generated with misc/generate-keymap.py script 2 | 3 | [ 4 | { 5 | "keys": [ 6 | "shift+super+y" 7 | ], 8 | "args": { 9 | "action": "evaluate_math_expression" 10 | }, 11 | "command": "run_emmet_action", 12 | "context": [ 13 | { 14 | "key": "emmet_action_enabled.evaluate_math_expression" 15 | } 16 | ] 17 | }, 18 | { 19 | "keys": [ 20 | "super+'" 21 | ], 22 | "args": { 23 | "action": "remove_tag" 24 | }, 25 | "command": "run_emmet_action", 26 | "context": [ 27 | { 28 | "key": "emmet_action_enabled.remove_tag" 29 | } 30 | ] 31 | }, 32 | { 33 | "keys": [ 34 | "ctrl+up" 35 | ], 36 | "args": { 37 | "action": "increment_number_by_1" 38 | }, 39 | "command": "run_emmet_action", 40 | "context": [ 41 | { 42 | "key": "emmet_action_enabled.increment_number_by_1" 43 | } 44 | ] 45 | }, 46 | { 47 | "keys": [ 48 | "alt+shift+forward_slash" 49 | ], 50 | "args": { 51 | "action": "toggle_comment" 52 | }, 53 | "command": "run_emmet_action", 54 | "context": [ 55 | { 56 | "operand": "source.css - source.css.less, text.xml, text.html - source", 57 | "operator": "equal", 58 | "match_all": true, 59 | "key": "selector" 60 | }, 61 | { 62 | "match_all": true, 63 | "key": "emmet_action_enabled.toggle_comment" 64 | } 65 | ] 66 | }, 67 | { 68 | "keys": [ 69 | "shift+super+." 70 | ], 71 | "args": { 72 | "action": "select_next_item" 73 | }, 74 | "command": "run_emmet_action", 75 | "context": [ 76 | { 77 | "key": "emmet_action_enabled.select_next_item" 78 | } 79 | ] 80 | }, 81 | { 82 | "keys": [ 83 | "ctrl+alt+enter" 84 | ], 85 | "command": "expand_as_you_type", 86 | "context": [ 87 | { 88 | "operand": false, 89 | "operator": "equal", 90 | "match_all": true, 91 | "key": "setting.is_widget" 92 | }, 93 | { 94 | "match_all": true, 95 | "key": "emmet_action_enabled.expand_as_you_type" 96 | } 97 | ] 98 | }, 99 | { 100 | "keys": [ 101 | "alt+down" 102 | ], 103 | "args": { 104 | "action": "decrement_number_by_01" 105 | }, 106 | "command": "run_emmet_action", 107 | "context": [ 108 | { 109 | "key": "emmet_action_enabled.decrement_number_by_01" 110 | } 111 | ] 112 | }, 113 | { 114 | "keys": [ 115 | "shift+ctrl+d" 116 | ], 117 | "args": { 118 | "action": "encode_decode_data_url" 119 | }, 120 | "command": "run_emmet_action", 121 | "context": [ 122 | { 123 | "key": "emmet_action_enabled.encode_decode_data_url" 124 | } 125 | ] 126 | }, 127 | { 128 | "keys": [ 129 | "ctrl+j" 130 | ], 131 | "args": { 132 | "action": "match_pair_inward" 133 | }, 134 | "command": "run_emmet_action", 135 | "context": [ 136 | { 137 | "key": "emmet_action_enabled.match_pair_inward" 138 | } 139 | ] 140 | }, 141 | { 142 | "keys": [ 143 | "alt+super+up" 144 | ], 145 | "args": { 146 | "action": "increment_number_by_10" 147 | }, 148 | "command": "run_emmet_action", 149 | "context": [ 150 | { 151 | "key": "emmet_action_enabled.increment_number_by_10" 152 | } 153 | ] 154 | }, 155 | { 156 | "keys": [ 157 | "shift+super+r" 158 | ], 159 | "args": { 160 | "action": "reflect_css_value" 161 | }, 162 | "command": "run_emmet_action", 163 | "context": [ 164 | { 165 | "key": "emmet_action_enabled.reflect_css_value" 166 | } 167 | ] 168 | }, 169 | { 170 | "keys": [ 171 | "ctrl+alt+left" 172 | ], 173 | "args": { 174 | "action": "prev_edit_point" 175 | }, 176 | "command": "run_emmet_action", 177 | "context": [ 178 | { 179 | "key": "emmet_action_enabled.prev_edit_point" 180 | } 181 | ] 182 | }, 183 | { 184 | "keys": [ 185 | "shift+super+," 186 | ], 187 | "args": { 188 | "action": "select_previous_item" 189 | }, 190 | "command": "run_emmet_action", 191 | "context": [ 192 | { 193 | "key": "emmet_action_enabled.select_previous_item" 194 | } 195 | ] 196 | }, 197 | { 198 | "keys": [ 199 | "ctrl+d" 200 | ], 201 | "args": { 202 | "action": "match_pair_outward" 203 | }, 204 | "command": "run_emmet_action", 205 | "context": [ 206 | { 207 | "key": "emmet_action_enabled.match_pair_outward" 208 | } 209 | ] 210 | }, 211 | { 212 | "keys": [ 213 | "shift+ctrl+i" 214 | ], 215 | "args": { 216 | "action": "update_image_size" 217 | }, 218 | "command": "run_emmet_action", 219 | "context": [ 220 | { 221 | "key": "emmet_action_enabled.update_image_size" 222 | } 223 | ] 224 | }, 225 | { 226 | "keys": [ 227 | "ctrl+alt+right" 228 | ], 229 | "args": { 230 | "action": "next_edit_point" 231 | }, 232 | "command": "run_emmet_action", 233 | "context": [ 234 | { 235 | "key": "emmet_action_enabled.next_edit_point" 236 | } 237 | ] 238 | }, 239 | { 240 | "keys": [ 241 | "shift+super+'" 242 | ], 243 | "args": { 244 | "action": "split_join_tag" 245 | }, 246 | "command": "run_emmet_action", 247 | "context": [ 248 | { 249 | "key": "emmet_action_enabled.split_join_tag" 250 | } 251 | ] 252 | }, 253 | { 254 | "keys": [ 255 | "alt+super+down" 256 | ], 257 | "args": { 258 | "action": "decrement_number_by_10" 259 | }, 260 | "command": "run_emmet_action", 261 | "context": [ 262 | { 263 | "key": "emmet_action_enabled.decrement_number_by_10" 264 | } 265 | ] 266 | }, 267 | { 268 | "keys": [ 269 | "ctrl+w" 270 | ], 271 | "command": "wrap_as_you_type", 272 | "context": [ 273 | { 274 | "operand": false, 275 | "operator": "equal", 276 | "match_all": true, 277 | "key": "setting.is_widget" 278 | }, 279 | { 280 | "match_all": true, 281 | "key": "emmet_action_enabled.wrap_as_you_type" 282 | } 283 | ] 284 | }, 285 | { 286 | "keys": [ 287 | "super+shift+k" 288 | ], 289 | "command": "rename_tag", 290 | "context": [ 291 | { 292 | "key": "emmet_action_enabled.rename_tag" 293 | } 294 | ] 295 | }, 296 | { 297 | "keys": [ 298 | "alt+up" 299 | ], 300 | "args": { 301 | "action": "increment_number_by_01" 302 | }, 303 | "command": "run_emmet_action", 304 | "context": [ 305 | { 306 | "key": "emmet_action_enabled.increment_number_by_01" 307 | } 308 | ] 309 | }, 310 | { 311 | "keys": [ 312 | "ctrl+shift+t" 313 | ], 314 | "args": { 315 | "action": "matching_pair" 316 | }, 317 | "command": "run_emmet_action", 318 | "context": [ 319 | { 320 | "key": "emmet_action_enabled.matching_pair" 321 | } 322 | ] 323 | }, 324 | { 325 | "keys": [ 326 | "ctrl+down" 327 | ], 328 | "args": { 329 | "action": "decrement_number_by_1" 330 | }, 331 | "command": "run_emmet_action", 332 | "context": [ 333 | { 334 | "key": "emmet_action_enabled.decrement_number_by_1" 335 | } 336 | ] 337 | }, 338 | { 339 | "keys": [ 340 | "ctrl+e" 341 | ], 342 | "args": { 343 | "action": "expand_abbreviation" 344 | }, 345 | "command": "run_emmet_action", 346 | "context": [ 347 | { 348 | "key": "emmet_action_enabled.expand_abbreviation" 349 | } 350 | ] 351 | }, 352 | { 353 | "keys": [ 354 | "tab" 355 | ], 356 | "command": "expand_abbreviation_by_tab", 357 | "context": [ 358 | { 359 | "operand": "source.css, source.sass, source.less, source.scss, source.stylus, text.xml, text.html - source, text.haml, text.scala.html, source string", 360 | "operator": "equal", 361 | "match_all": true, 362 | "key": "selector" 363 | }, 364 | { 365 | "operand": "storage.type.templatetag.django", 366 | "operator": "not_equal", 367 | "match_all": true, 368 | "key": "selector" 369 | }, 370 | { 371 | "match_all": true, 372 | "key": "selection_empty" 373 | }, 374 | { 375 | "operator": "equal", 376 | "operand": false, 377 | "match_all": true, 378 | "key": "has_next_field" 379 | }, 380 | { 381 | "operator": "equal", 382 | "operand": false, 383 | "match_all": true, 384 | "key": "setting.disable_tab_abbreviations" 385 | }, 386 | { 387 | "operand": false, 388 | "operator": "equal", 389 | "match_all": true, 390 | "key": "auto_complete_visible" 391 | }, 392 | { 393 | "match_all": true, 394 | "key": "is_abbreviation" 395 | } 396 | ] 397 | }, 398 | { 399 | "keys": [ 400 | "tab" 401 | ], 402 | "command": "expand_abbreviation_by_tab", 403 | "context": [ 404 | { 405 | "operand": "source.css, source.sass, source.less, source.scss, source.stylus, text.xml, text.html - source, text.haml, text.scala.html, source string", 406 | "operator": "equal", 407 | "match_all": true, 408 | "key": "selector" 409 | }, 410 | { 411 | "operand": "storage.type.templatetag.django", 412 | "operator": "not_equal", 413 | "match_all": true, 414 | "key": "selector" 415 | }, 416 | { 417 | "match_all": true, 418 | "key": "selection_empty" 419 | }, 420 | { 421 | "operator": "equal", 422 | "operand": false, 423 | "match_all": true, 424 | "key": "has_next_field" 425 | }, 426 | { 427 | "operator": "equal", 428 | "operand": true, 429 | "match_all": true, 430 | "key": "auto_complete_visible" 431 | }, 432 | { 433 | "operator": "equal", 434 | "operand": false, 435 | "match_all": true, 436 | "key": "setting.disable_tab_abbreviations_on_auto_complete" 437 | }, 438 | { 439 | "match_all": true, 440 | "key": "is_abbreviation" 441 | } 442 | ] 443 | }, 444 | { 445 | "keys": [ 446 | "enter" 447 | ], 448 | "args": { 449 | "contents": "\n\t${0}\n" 450 | }, 451 | "command": "insert_snippet", 452 | "context": [ 453 | { 454 | "operand": "meta.scope.between-tag-pair.html, meta.scope.between-tag-pair.xml", 455 | "match_all": true, 456 | "key": "selector" 457 | }, 458 | { 459 | "operand": false, 460 | "match_all": true, 461 | "key": "auto_complete_visible" 462 | }, 463 | { 464 | "match_all": true, 465 | "key": "clear_fields_on_enter_key" 466 | }, 467 | { 468 | "operand": false, 469 | "match_all": true, 470 | "key": "setting.disable_formatted_linebreak" 471 | } 472 | ] 473 | }, 474 | { 475 | "keys": [ 476 | "#" 477 | ], 478 | "args": { 479 | "attribute": "id" 480 | }, 481 | "command": "emmet_insert_attribute", 482 | "context": [ 483 | { 484 | "operand": "text.html meta.tag -string -punctuation.definition.tag.begin.html -meta.scope.between-tag-pair.html -source -meta.tag.template.value.twig", 485 | "operator": "equal", 486 | "match_all": true, 487 | "key": "selector" 488 | }, 489 | { 490 | "operator": "equal", 491 | "operand": true, 492 | "key": "setting.auto_id_class" 493 | } 494 | ] 495 | }, 496 | { 497 | "keys": [ 498 | "." 499 | ], 500 | "args": { 501 | "attribute": "class" 502 | }, 503 | "command": "emmet_insert_attribute", 504 | "context": [ 505 | { 506 | "operand": "text.html meta.tag -string -punctuation.definition.tag.begin.html -meta.scope.between-tag-pair.html -source -meta.tag.template.value.twig", 507 | "operator": "equal", 508 | "match_all": true, 509 | "key": "selector" 510 | }, 511 | { 512 | "operator": "equal", 513 | "operand": true, 514 | "key": "setting.auto_id_class" 515 | } 516 | ] 517 | } 518 | ] -------------------------------------------------------------------------------- /Default (Windows).sublime-keymap: -------------------------------------------------------------------------------- 1 | // This file is automatically generated with misc/generate-keymap.py script 2 | 3 | [ 4 | { 5 | "keys": [ 6 | "shift+ctrl+y" 7 | ], 8 | "args": { 9 | "action": "evaluate_math_expression" 10 | }, 11 | "command": "run_emmet_action", 12 | "context": [ 13 | { 14 | "key": "emmet_action_enabled.evaluate_math_expression" 15 | } 16 | ] 17 | }, 18 | { 19 | "keys": [ 20 | "shift+ctrl+;" 21 | ], 22 | "args": { 23 | "action": "remove_tag" 24 | }, 25 | "command": "run_emmet_action", 26 | "context": [ 27 | { 28 | "key": "emmet_action_enabled.remove_tag" 29 | } 30 | ] 31 | }, 32 | { 33 | "keys": [ 34 | "ctrl+up" 35 | ], 36 | "args": { 37 | "action": "increment_number_by_1" 38 | }, 39 | "command": "run_emmet_action", 40 | "context": [ 41 | { 42 | "key": "emmet_action_enabled.increment_number_by_1" 43 | } 44 | ] 45 | }, 46 | { 47 | "keys": [ 48 | "ctrl+shift+forward_slash" 49 | ], 50 | "args": { 51 | "action": "toggle_comment" 52 | }, 53 | "command": "run_emmet_action", 54 | "context": [ 55 | { 56 | "operand": "source.css - source.css.less, text.xml, text.html - source", 57 | "operator": "equal", 58 | "match_all": true, 59 | "key": "selector" 60 | }, 61 | { 62 | "match_all": true, 63 | "key": "emmet_action_enabled.toggle_comment" 64 | } 65 | ] 66 | }, 67 | { 68 | "keys": [ 69 | "shift+ctrl+." 70 | ], 71 | "args": { 72 | "action": "select_next_item" 73 | }, 74 | "command": "run_emmet_action", 75 | "context": [ 76 | { 77 | "key": "emmet_action_enabled.select_next_item" 78 | } 79 | ] 80 | }, 81 | { 82 | "keys": [ 83 | "ctrl+alt+enter" 84 | ], 85 | "command": "expand_as_you_type", 86 | "context": [ 87 | { 88 | "operand": false, 89 | "operator": "equal", 90 | "match_all": true, 91 | "key": "setting.is_widget" 92 | }, 93 | { 94 | "match_all": true, 95 | "key": "emmet_action_enabled.expand_as_you_type" 96 | } 97 | ] 98 | }, 99 | { 100 | "keys": [ 101 | "alt+down" 102 | ], 103 | "args": { 104 | "action": "decrement_number_by_01" 105 | }, 106 | "command": "run_emmet_action", 107 | "context": [ 108 | { 109 | "key": "emmet_action_enabled.decrement_number_by_01" 110 | } 111 | ] 112 | }, 113 | { 114 | "keys": [ 115 | "ctrl+'" 116 | ], 117 | "args": { 118 | "action": "encode_decode_data_url" 119 | }, 120 | "command": "run_emmet_action", 121 | "context": [ 122 | { 123 | "key": "emmet_action_enabled.encode_decode_data_url" 124 | } 125 | ] 126 | }, 127 | { 128 | "keys": [ 129 | "ctrl+shift+0" 130 | ], 131 | "args": { 132 | "action": "match_pair_inward" 133 | }, 134 | "command": "run_emmet_action", 135 | "context": [ 136 | { 137 | "key": "emmet_action_enabled.match_pair_inward" 138 | } 139 | ] 140 | }, 141 | { 142 | "keys": [ 143 | "shift+alt+up" 144 | ], 145 | "args": { 146 | "action": "increment_number_by_10" 147 | }, 148 | "command": "run_emmet_action", 149 | "context": [ 150 | { 151 | "key": "emmet_action_enabled.increment_number_by_10" 152 | } 153 | ] 154 | }, 155 | { 156 | "keys": [ 157 | "shift+ctrl+r" 158 | ], 159 | "args": { 160 | "action": "reflect_css_value" 161 | }, 162 | "command": "run_emmet_action", 163 | "context": [ 164 | { 165 | "key": "emmet_action_enabled.reflect_css_value" 166 | } 167 | ] 168 | }, 169 | { 170 | "keys": [ 171 | "ctrl+alt+left" 172 | ], 173 | "args": { 174 | "action": "prev_edit_point" 175 | }, 176 | "command": "run_emmet_action", 177 | "context": [ 178 | { 179 | "key": "emmet_action_enabled.prev_edit_point" 180 | } 181 | ] 182 | }, 183 | { 184 | "keys": [ 185 | "shift+ctrl+," 186 | ], 187 | "args": { 188 | "action": "select_previous_item" 189 | }, 190 | "command": "run_emmet_action", 191 | "context": [ 192 | { 193 | "key": "emmet_action_enabled.select_previous_item" 194 | } 195 | ] 196 | }, 197 | { 198 | "keys": [ 199 | "ctrl+," 200 | ], 201 | "args": { 202 | "action": "match_pair_outward" 203 | }, 204 | "command": "run_emmet_action", 205 | "context": [ 206 | { 207 | "key": "emmet_action_enabled.match_pair_outward" 208 | } 209 | ] 210 | }, 211 | { 212 | "keys": [ 213 | "ctrl+u" 214 | ], 215 | "args": { 216 | "action": "update_image_size" 217 | }, 218 | "command": "run_emmet_action", 219 | "context": [ 220 | { 221 | "key": "emmet_action_enabled.update_image_size" 222 | } 223 | ] 224 | }, 225 | { 226 | "keys": [ 227 | "ctrl+alt+right" 228 | ], 229 | "args": { 230 | "action": "next_edit_point" 231 | }, 232 | "command": "run_emmet_action", 233 | "context": [ 234 | { 235 | "key": "emmet_action_enabled.next_edit_point" 236 | } 237 | ] 238 | }, 239 | { 240 | "keys": [ 241 | "shift+ctrl+`" 242 | ], 243 | "args": { 244 | "action": "split_join_tag" 245 | }, 246 | "command": "run_emmet_action", 247 | "context": [ 248 | { 249 | "key": "emmet_action_enabled.split_join_tag" 250 | } 251 | ] 252 | }, 253 | { 254 | "keys": [ 255 | "shift+alt+down" 256 | ], 257 | "args": { 258 | "action": "decrement_number_by_10" 259 | }, 260 | "command": "run_emmet_action", 261 | "context": [ 262 | { 263 | "key": "emmet_action_enabled.decrement_number_by_10" 264 | } 265 | ] 266 | }, 267 | { 268 | "keys": [ 269 | "shift+ctrl+g" 270 | ], 271 | "command": "wrap_as_you_type", 272 | "context": [ 273 | { 274 | "operand": false, 275 | "operator": "equal", 276 | "match_all": true, 277 | "key": "setting.is_widget" 278 | }, 279 | { 280 | "match_all": true, 281 | "key": "emmet_action_enabled.wrap_as_you_type" 282 | } 283 | ] 284 | }, 285 | { 286 | "keys": [ 287 | "shift+ctrl+'" 288 | ], 289 | "command": "rename_tag", 290 | "context": [ 291 | { 292 | "key": "emmet_action_enabled.rename_tag" 293 | } 294 | ] 295 | }, 296 | { 297 | "keys": [ 298 | "alt+up" 299 | ], 300 | "args": { 301 | "action": "increment_number_by_01" 302 | }, 303 | "command": "run_emmet_action", 304 | "context": [ 305 | { 306 | "key": "emmet_action_enabled.increment_number_by_01" 307 | } 308 | ] 309 | }, 310 | { 311 | "keys": [ 312 | "ctrl+alt+j" 313 | ], 314 | "args": { 315 | "action": "matching_pair" 316 | }, 317 | "command": "run_emmet_action", 318 | "context": [ 319 | { 320 | "key": "emmet_action_enabled.matching_pair" 321 | } 322 | ] 323 | }, 324 | { 325 | "keys": [ 326 | "ctrl+down" 327 | ], 328 | "args": { 329 | "action": "decrement_number_by_1" 330 | }, 331 | "command": "run_emmet_action", 332 | "context": [ 333 | { 334 | "key": "emmet_action_enabled.decrement_number_by_1" 335 | } 336 | ] 337 | }, 338 | { 339 | "keys": [ 340 | "ctrl+e" 341 | ], 342 | "args": { 343 | "action": "expand_abbreviation" 344 | }, 345 | "command": "run_emmet_action", 346 | "context": [ 347 | { 348 | "key": "emmet_action_enabled.expand_abbreviation" 349 | } 350 | ] 351 | }, 352 | { 353 | "keys": [ 354 | "tab" 355 | ], 356 | "command": "expand_abbreviation_by_tab", 357 | "context": [ 358 | { 359 | "operand": "source.css, source.sass, source.less, source.scss, source.stylus, text.xml, text.html - source, text.haml, text.scala.html, source string", 360 | "operator": "equal", 361 | "match_all": true, 362 | "key": "selector" 363 | }, 364 | { 365 | "operand": "storage.type.templatetag.django", 366 | "operator": "not_equal", 367 | "match_all": true, 368 | "key": "selector" 369 | }, 370 | { 371 | "match_all": true, 372 | "key": "selection_empty" 373 | }, 374 | { 375 | "operator": "equal", 376 | "operand": false, 377 | "match_all": true, 378 | "key": "has_next_field" 379 | }, 380 | { 381 | "operator": "equal", 382 | "operand": false, 383 | "match_all": true, 384 | "key": "setting.disable_tab_abbreviations" 385 | }, 386 | { 387 | "operand": false, 388 | "operator": "equal", 389 | "match_all": true, 390 | "key": "auto_complete_visible" 391 | }, 392 | { 393 | "match_all": true, 394 | "key": "is_abbreviation" 395 | } 396 | ] 397 | }, 398 | { 399 | "keys": [ 400 | "tab" 401 | ], 402 | "command": "expand_abbreviation_by_tab", 403 | "context": [ 404 | { 405 | "operand": "source.css, source.sass, source.less, source.scss, source.stylus, text.xml, text.html - source, text.haml, text.scala.html, source string", 406 | "operator": "equal", 407 | "match_all": true, 408 | "key": "selector" 409 | }, 410 | { 411 | "operand": "storage.type.templatetag.django", 412 | "operator": "not_equal", 413 | "match_all": true, 414 | "key": "selector" 415 | }, 416 | { 417 | "match_all": true, 418 | "key": "selection_empty" 419 | }, 420 | { 421 | "operator": "equal", 422 | "operand": false, 423 | "match_all": true, 424 | "key": "has_next_field" 425 | }, 426 | { 427 | "operator": "equal", 428 | "operand": true, 429 | "match_all": true, 430 | "key": "auto_complete_visible" 431 | }, 432 | { 433 | "operator": "equal", 434 | "operand": false, 435 | "match_all": true, 436 | "key": "setting.disable_tab_abbreviations_on_auto_complete" 437 | }, 438 | { 439 | "match_all": true, 440 | "key": "is_abbreviation" 441 | } 442 | ] 443 | }, 444 | { 445 | "keys": [ 446 | "enter" 447 | ], 448 | "args": { 449 | "contents": "\n\t${0}\n" 450 | }, 451 | "command": "insert_snippet", 452 | "context": [ 453 | { 454 | "operand": "meta.scope.between-tag-pair.html, meta.scope.between-tag-pair.xml", 455 | "match_all": true, 456 | "key": "selector" 457 | }, 458 | { 459 | "operand": false, 460 | "match_all": true, 461 | "key": "auto_complete_visible" 462 | }, 463 | { 464 | "match_all": true, 465 | "key": "clear_fields_on_enter_key" 466 | }, 467 | { 468 | "operand": false, 469 | "match_all": true, 470 | "key": "setting.disable_formatted_linebreak" 471 | } 472 | ] 473 | }, 474 | { 475 | "keys": [ 476 | "#" 477 | ], 478 | "args": { 479 | "attribute": "id" 480 | }, 481 | "command": "emmet_insert_attribute", 482 | "context": [ 483 | { 484 | "operand": "text.html meta.tag -string -punctuation.definition.tag.begin.html -meta.scope.between-tag-pair.html -source -meta.tag.template.value.twig", 485 | "operator": "equal", 486 | "match_all": true, 487 | "key": "selector" 488 | }, 489 | { 490 | "operator": "equal", 491 | "operand": true, 492 | "key": "setting.auto_id_class" 493 | } 494 | ] 495 | }, 496 | { 497 | "keys": [ 498 | "." 499 | ], 500 | "args": { 501 | "attribute": "class" 502 | }, 503 | "command": "emmet_insert_attribute", 504 | "context": [ 505 | { 506 | "operand": "text.html meta.tag -string -punctuation.definition.tag.begin.html -meta.scope.between-tag-pair.html -source -meta.tag.template.value.twig", 507 | "operator": "equal", 508 | "match_all": true, 509 | "key": "selector" 510 | }, 511 | { 512 | "operator": "equal", 513 | "operand": true, 514 | "key": "setting.auto_id_class" 515 | } 516 | ] 517 | } 518 | ] -------------------------------------------------------------------------------- /emmet/pyv8loader.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | import os 3 | import os.path 4 | import sys 5 | import json 6 | import re 7 | import threading 8 | import subprocess 9 | import tempfile 10 | import collections 11 | import platform 12 | import semver 13 | import time 14 | import zipfile 15 | 16 | is_python3 = sys.version_info[0] > 2 17 | 18 | if is_python3: 19 | import urllib.request as url_req 20 | import urllib.error as url_err 21 | import urllib.parse as url_parse 22 | else: 23 | import urllib 24 | import urllib2 25 | url_req = urllib2 26 | url_err = urllib2 27 | url_parse = urllib2 28 | 29 | CHECK_INTERVAL = 60 * 60 * 24 30 | 31 | # PACKAGES_URL = 'https://api.github.com/repos/emmetio/pyv8-binaries/downloads' 32 | PACKAGES_URL = 'https://api.github.com/repos/emmetio/pyv8-binaries/contents' 33 | 34 | def load(dest_path, delegate=None): 35 | """ 36 | Main function that attempts to load or update PyV8 binary. 37 | First, it loads list of available PyV8 modules and check if 38 | PyV8 should be downloaded or updated. 39 | @param dest_path: Path where PyV8 lib should be downloaded 40 | @param delegate: instance of LoaderDelegate that will receive 41 | loader progress events 42 | @returns: `True` if download progress was initiated 43 | """ 44 | if delegate is None: 45 | delegate = LoaderDelegate() 46 | 47 | config = get_loader_config(dest_path) 48 | 49 | if 'PyV8' in sys.modules and (config['skip_update'] or time.time() < config['last_update'] + CHECK_INTERVAL): 50 | # No need to load anything: user already has PyV8 binary 51 | # or decided to disable update process 52 | delegate.log('No need to update PyV8') 53 | return False 54 | 55 | def on_complete(result, *args, **kwargs): 56 | if result is not None: 57 | # Most recent version was downloaded 58 | config['last_id'] = result 59 | if 'PyV8' not in sys.modules: 60 | # PyV8 is not loaded yet, we can safely unpack it 61 | unpack_pyv8(dest_path) 62 | 63 | config['last_update'] = time.time() 64 | save_loader_config(dest_path, config) 65 | delegate.on_complete(*args, **kwargs) 66 | 67 | # try to download most recent version of PyV8 68 | # As PyV8 for Sublime Text spreads the world, it's possible 69 | # that multiple distinct PyV8Loader's may start doing the same 70 | # job at the same time. In this case, we should check if there's 71 | # already a thread that load PyV8 and hook on existing thread 72 | # rather that creating a new one 73 | thread = None 74 | thread_exists = False 75 | for t in threading.enumerate(): 76 | if hasattr(t, 'is_pyv8_thread'): 77 | print('PyV8: Reusing thread') 78 | thread = t 79 | thread_exists = True 80 | break 81 | 82 | if not thread: 83 | print('PyV8: Creating new thread') 84 | thread = PyV8Loader(get_arch(), dest_path, config, delegate=delegate) 85 | thread.start() 86 | 87 | delegate.on_start() 88 | 89 | # watch on download progress 90 | prog = ThreadProgress(thread, delegate, thread_exists) 91 | prog.on('complete', on_complete if not thread_exists else delegate.on_complete) 92 | prog.on('error', delegate.on_error) 93 | 94 | def get_arch(): 95 | "Returns architecture name for PyV8 binary" 96 | suffix = is_python3 and '-p3' or '' 97 | p = lambda a: '%s%s' % (a, suffix) 98 | is_64bit = sys.maxsize > 2**32 99 | system_name = platform.system() 100 | if system_name == 'Darwin': 101 | try: 102 | if semver.match(platform.mac_ver()[0], '<10.7.0'): 103 | return p('mac106') 104 | except: 105 | pass 106 | 107 | return p('osx') 108 | if system_name == 'Windows': 109 | return p('win64') if is_64bit else p('win32') 110 | if system_name == 'Linux': 111 | return p('linux64') if is_64bit else p('linux32') 112 | 113 | def get_loader_config(path): 114 | config = { 115 | "last_id": 0, 116 | "last_update": 0, 117 | "skip_update": False 118 | } 119 | 120 | config_path = os.path.join(path, 'config.json') 121 | if os.path.exists(config_path): 122 | with open(config_path) as fd: 123 | for k,v in json.load(fd).items(): 124 | config[k] = v 125 | 126 | return config 127 | 128 | def save_loader_config(path, data): 129 | config_path = os.path.join(path, 'config.json') 130 | 131 | if not os.path.exists(path): 132 | os.makedirs(path) 133 | fp = open(config_path, 'w') 134 | fp.write(json.dumps(data)) 135 | fp.close() 136 | 137 | def clean_old_data(): 138 | for f in os.listdir('.'): 139 | if f.lower() != 'config.json' and f.lower() != 'pack.zip': 140 | try: 141 | os.remove(f) 142 | except Exception as e: 143 | pass 144 | 145 | def unpack_pyv8(package_dir): 146 | f = os.path.join(package_dir, 'pack.zip') 147 | if not os.path.exists(f): 148 | return 149 | 150 | package_zip = zipfile.ZipFile(f, 'r') 151 | 152 | root_level_paths = [] 153 | last_path = None 154 | for path in package_zip.namelist(): 155 | last_path = path 156 | if path.find('/') in [len(path) - 1, -1]: 157 | root_level_paths.append(path) 158 | if path[0] == '/' or path.find('../') != -1 or path.find('..\\') != -1: 159 | raise 'The PyV8 package contains files outside of the package dir and cannot be safely installed.' 160 | 161 | if last_path and len(root_level_paths) == 0: 162 | root_level_paths.append(last_path[0:last_path.find('/') + 1]) 163 | 164 | prev_dir = os.getcwd() 165 | os.chdir(package_dir) 166 | 167 | clean_old_data() 168 | 169 | # Here we don't use .extractall() since it was having issues on OS X 170 | skip_root_dir = len(root_level_paths) == 1 and \ 171 | root_level_paths[0].endswith('/') 172 | extracted_paths = [] 173 | for path in package_zip.namelist(): 174 | dest = path 175 | 176 | if not is_python3: 177 | try: 178 | if not isinstance(dest, unicode): 179 | dest = unicode(dest, 'utf-8', 'strict') 180 | except UnicodeDecodeError: 181 | dest = unicode(dest, 'cp1252', 'replace') 182 | 183 | if os.name == 'nt': 184 | regex = ':|\*|\?|"|<|>|\|' 185 | if re.search(regex, dest) != None: 186 | print ('%s: Skipping file from package named %s due to ' + 187 | 'an invalid filename') % (__name__, path) 188 | continue 189 | 190 | # If there was only a single directory in the package, we remove 191 | # that folder name from the paths as we extract entries 192 | if skip_root_dir: 193 | dest = dest[len(root_level_paths[0]):] 194 | 195 | if os.name == 'nt': 196 | dest = dest.replace('/', '\\') 197 | else: 198 | dest = dest.replace('\\', '/') 199 | 200 | dest = os.path.join(package_dir, dest) 201 | 202 | def add_extracted_dirs(dir): 203 | while dir not in extracted_paths: 204 | extracted_paths.append(dir) 205 | dir = os.path.dirname(dir) 206 | if dir == package_dir: 207 | break 208 | 209 | if path.endswith('/'): 210 | if not os.path.exists(dest): 211 | os.makedirs(dest) 212 | add_extracted_dirs(dest) 213 | else: 214 | dest_dir = os.path.dirname(dest) 215 | if not os.path.exists(dest_dir): 216 | os.makedirs(dest_dir) 217 | add_extracted_dirs(dest_dir) 218 | extracted_paths.append(dest) 219 | try: 220 | open(dest, 'wb').write(package_zip.read(path)) 221 | except (IOError, UnicodeDecodeError): 222 | print ('%s: Skipping file from package named %s due to ' + 223 | 'an invalid filename') % (__name__, path) 224 | package_zip.close() 225 | 226 | os.chdir(prev_dir) 227 | os.remove(f) 228 | 229 | class LoaderDelegate(): 230 | """ 231 | Abstract class used to display PyV8 binary download progress, 232 | and provide some settings for downloader 233 | """ 234 | def __init__(self, settings={}): 235 | self.settings = settings 236 | 237 | def on_start(self, *args, **kwargs): 238 | "Invoked when download process is initiated" 239 | pass 240 | 241 | def on_progress(self, *args, **kwargs): 242 | "Invoked on download progress" 243 | pass 244 | 245 | def on_complete(self, *args, **kwargs): 246 | "Invoked when download process was finished successfully" 247 | pass 248 | 249 | def on_error(self, *args, **kwargs): 250 | "Invoked when error occured during download process" 251 | pass 252 | 253 | def setting(self, name, default=None): 254 | "Returns specified setting name" 255 | return self.settings[name] if name in self.settings else default 256 | 257 | def log(self, message): 258 | pass 259 | 260 | class ThreadProgress(): 261 | def __init__(self, thread, delegate, is_background=False): 262 | self.thread = thread 263 | self.delegate = delegate 264 | self.is_background = is_background 265 | self._callbacks = {} 266 | threading.Timer(0, self.run).start() 267 | 268 | def run(self): 269 | if not self.thread.is_alive(): 270 | if self.thread.exit_code != 0: 271 | return self.trigger('error', exit_code=self.thread.exit_code, progress=self) 272 | 273 | return self.trigger('complete', result=self.thread.result, progress=self) 274 | 275 | self.trigger('progress', progress=self) 276 | threading.Timer(0.1, self.run).start() 277 | 278 | def on(self, event_name, callback): 279 | if event_name not in self._callbacks: 280 | self._callbacks[event_name] = [] 281 | 282 | if isinstance(callback, collections.Callable): 283 | self._callbacks[event_name].append(callback) 284 | 285 | return self 286 | 287 | def trigger(self, event_name, *args, **kwargs): 288 | if event_name in self._callbacks: 289 | for c in self._callbacks[event_name]: 290 | c(*args, **kwargs) 291 | 292 | if self.delegate and hasattr(self.delegate, 'on_%s' % event_name): 293 | getattr(self.delegate, 'on_%s' % event_name)(*args, **kwargs) 294 | 295 | return self 296 | 297 | class BinaryNotFoundError(Exception): 298 | pass 299 | 300 | 301 | class NonCleanExitError(Exception): 302 | def __init__(self, returncode): 303 | self.returncode = returncode 304 | 305 | def __str__(self): 306 | return repr(self.returncode) 307 | 308 | 309 | class CliDownloader(): 310 | def __init__(self, settings): 311 | self.settings = settings 312 | 313 | def find_binary(self, name): 314 | for dir in os.environ['PATH'].split(os.pathsep): 315 | path = os.path.join(dir, name) 316 | if os.path.exists(path): 317 | return path 318 | 319 | raise BinaryNotFoundError('The binary %s could not be located' % name) 320 | 321 | def execute(self, args): 322 | proc = subprocess.Popen(args, stdin=subprocess.PIPE, 323 | stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 324 | 325 | output = proc.stdout.read() 326 | returncode = proc.wait() 327 | if returncode != 0: 328 | error = NonCleanExitError(returncode) 329 | error.output = output 330 | raise error 331 | return output 332 | 333 | class WgetDownloader(CliDownloader): 334 | def __init__(self, settings): 335 | self.settings = settings 336 | self.wget = self.find_binary('wget') 337 | 338 | def clean_tmp_file(self): 339 | os.remove(self.tmp_file) 340 | 341 | def download(self, url, error_message, timeout, tries): 342 | if not self.wget: 343 | return False 344 | 345 | self.tmp_file = tempfile.NamedTemporaryFile().name 346 | command = [self.wget, '--connect-timeout=' + str(int(timeout)), '-o', 347 | self.tmp_file, '-O', '-', '-U', 'Emmet PyV8 Loader', 348 | '--no-check-certificate'] 349 | 350 | command.append(url) 351 | 352 | if self.settings.get('http_proxy'): 353 | os.putenv('http_proxy', self.settings.get('http_proxy')) 354 | if not self.settings.get('https_proxy'): 355 | os.putenv('https_proxy', self.settings.get('http_proxy')) 356 | if self.settings.get('https_proxy'): 357 | os.putenv('https_proxy', self.settings.get('https_proxy')) 358 | 359 | while tries > 0: 360 | tries -= 1 361 | try: 362 | result = self.execute(command) 363 | self.clean_tmp_file() 364 | return result 365 | except NonCleanExitError as e: 366 | error_line = '' 367 | with open(self.tmp_file) as f: 368 | for line in list(f): 369 | if re.search('ERROR[: ]|failed: ', line): 370 | error_line = line 371 | break 372 | 373 | if e.returncode == 8: 374 | regex = re.compile('^.*ERROR (\d+):.*', re.S) 375 | if re.sub(regex, '\\1', error_line) == '503': 376 | # GitHub and BitBucket seem to rate limit via 503 377 | print('%s: Downloading %s was rate limited, trying again' % (__name__, url)) 378 | continue 379 | error_string = 'HTTP error ' + re.sub('^.*? ERROR ', '', 380 | error_line) 381 | 382 | elif e.returncode == 4: 383 | error_string = re.sub('^.*?failed: ', '', error_line) 384 | # GitHub and BitBucket seem to time out a lot 385 | if error_string.find('timed out') != -1: 386 | print('%s: Downloading %s timed out, trying again' % (__name__, url)) 387 | continue 388 | 389 | else: 390 | error_string = re.sub('^.*?(ERROR[: ]|failed: )', '\\1', 391 | error_line) 392 | 393 | error_string = re.sub('\\.?\s*\n\s*$', '', error_string) 394 | print('%s: %s %s downloading %s.' % (__name__, error_message, 395 | error_string, url)) 396 | self.clean_tmp_file() 397 | break 398 | return False 399 | 400 | 401 | class CurlDownloader(CliDownloader): 402 | def __init__(self, settings): 403 | self.settings = settings 404 | self.curl = self.find_binary('curl') 405 | 406 | def download(self, url, error_message, timeout, tries): 407 | if not self.curl: 408 | return False 409 | command = [self.curl, '-f', '--user-agent', 'Emmet PyV8 Loader', 410 | '--connect-timeout', str(int(timeout)), '-sS'] 411 | 412 | command.append(url) 413 | 414 | if self.settings.get('http_proxy'): 415 | os.putenv('http_proxy', self.settings.get('http_proxy')) 416 | if not self.settings.get('https_proxy'): 417 | os.putenv('HTTPS_PROXY', self.settings.get('http_proxy')) 418 | if self.settings.get('https_proxy'): 419 | os.putenv('HTTPS_PROXY', self.settings.get('https_proxy')) 420 | 421 | while tries > 0: 422 | tries -= 1 423 | try: 424 | return self.execute(command) 425 | except NonCleanExitError as e: 426 | if e.returncode == 22: 427 | code = re.sub('^.*?(\d+)\s*$', '\\1', e.output) 428 | if code == '503': 429 | # GitHub and BitBucket seem to rate limit via 503 430 | print('%s: Downloading %s was rate limited, trying again' % (__name__, url)) 431 | continue 432 | error_string = 'HTTP error ' + code 433 | elif e.returncode == 6: 434 | error_string = 'URL error host not found' 435 | elif e.returncode == 28: 436 | # GitHub and BitBucket seem to time out a lot 437 | print('%s: Downloading %s timed out, trying again' % (__name__, url)) 438 | continue 439 | else: 440 | error_string = e.output.rstrip() 441 | 442 | print('%s: %s %s downloading %s.' % (__name__, error_message, error_string, url)) 443 | break 444 | return False 445 | 446 | 447 | class UrlLib2Downloader(): 448 | def __init__(self, settings): 449 | self.settings = settings 450 | 451 | def download(self, url, error_message, timeout, tries): 452 | http_proxy = self.settings.get('http_proxy') 453 | https_proxy = self.settings.get('https_proxy') 454 | if http_proxy or https_proxy: 455 | proxies = {} 456 | if http_proxy: 457 | proxies['http'] = http_proxy 458 | if not https_proxy: 459 | proxies['https'] = http_proxy 460 | if https_proxy: 461 | proxies['https'] = https_proxy 462 | proxy_handler = url_req.ProxyHandler(proxies) 463 | else: 464 | proxy_handler = url_req.ProxyHandler() 465 | handlers = [proxy_handler] 466 | 467 | # secure_url_match = re.match('^https://([^/]+)', url) 468 | # if secure_url_match != None: 469 | # secure_domain = secure_url_match.group(1) 470 | # bundle_path = self.check_certs(secure_domain, timeout) 471 | # if not bundle_path: 472 | # return False 473 | # handlers.append(VerifiedHTTPSHandler(ca_certs=bundle_path)) 474 | url_req.install_opener(url_req.build_opener(*handlers)) 475 | 476 | while tries > 0: 477 | tries -= 1 478 | try: 479 | request = url_req.Request(url, headers={"User-Agent": 480 | "Emmet PyV8 Loader"}) 481 | http_file = url_req.urlopen(request, timeout=timeout) 482 | return http_file.read() 483 | 484 | except url_err.HTTPError as e: 485 | # Bitbucket and Github ratelimit using 503 a decent amount 486 | if str(e.code) == '503': 487 | print('%s: Downloading %s was rate limited, trying again' % (__name__, url)) 488 | continue 489 | print('%s: %s HTTP error %s downloading %s.' % (__name__, error_message, str(e.code), url)) 490 | 491 | except url_err.URLError as e: 492 | # Bitbucket and Github timeout a decent amount 493 | if str(e.reason) == 'The read operation timed out' or \ 494 | str(e.reason) == 'timed out': 495 | print('%s: Downloading %s timed out, trying again' % (__name__, url)) 496 | continue 497 | print('%s: %s URL error %s downloading %s.' % (__name__, error_message, str(e.reason), url)) 498 | break 499 | return False 500 | 501 | class PyV8Loader(threading.Thread): 502 | def __init__(self, arch, download_path, config, delegate=None): 503 | self.arch = arch 504 | self.config = config 505 | self.download_path = download_path 506 | self.exit_code = 0 507 | self.result = None 508 | self.delegate = delegate or LoaderDelegate() 509 | self.is_pyv8_thread = True 510 | 511 | threading.Thread.__init__(self) 512 | self.delegate.log('Creating thread') 513 | 514 | def download_url(self, url, error_message): 515 | # TODO add settings 516 | has_ssl = 'ssl' in sys.modules and hasattr(url_req, 'HTTPSHandler') 517 | is_ssl = re.search('^https://', url) != None 518 | 519 | if (is_ssl and has_ssl) or not is_ssl: 520 | downloader = UrlLib2Downloader(self.delegate.settings) 521 | else: 522 | for downloader_class in [CurlDownloader, WgetDownloader]: 523 | try: 524 | downloader = downloader_class(self.delegate.settings) 525 | break 526 | except BinaryNotFoundError: 527 | pass 528 | 529 | if not downloader: 530 | self.delegate.log('Unable to download PyV8 binary due to invalid downloader') 531 | return False 532 | 533 | timeout = self.delegate.settings.get('timeout', 60) 534 | # timeout = 3 535 | return downloader.download(url.replace(' ', '%20'), error_message, timeout, 3) 536 | 537 | def run(self): 538 | # get list of available packages first 539 | self.delegate.log('Loading %s' % PACKAGES_URL) 540 | try: 541 | packages = self.download_url(PACKAGES_URL, 'Unable to download packages list.') 542 | except Exception as e: 543 | self.delegate.log('Unable to download file: %s' % e) 544 | self.exit_code = 4 545 | return 546 | 547 | if not packages: 548 | self.exit_code = 1 549 | return 550 | 551 | if isinstance(packages, bytes): 552 | packages = packages.decode('utf-8') 553 | 554 | files = json.loads(packages) 555 | 556 | # find package for current architecture 557 | cur_item = None 558 | bundle_name = 'pyv8-%s.zip' % self.arch 559 | for item in files: 560 | if bundle_name == item['name']: 561 | cur_item = item 562 | break 563 | 564 | if not cur_item: 565 | self.delegate.log('Unable to find binary for %s architecture' % self.arch) 566 | self.exit_code = 2 567 | return 568 | 569 | if cur_item['sha'] == self.config['last_id']: 570 | self.delegate.log('You have the most recent PyV8 binary') 571 | return 572 | 573 | url = 'https://raw.github.com/emmetio/pyv8-binaries/master/%s' % cur_item['name'] 574 | self.delegate.log('Loading PyV8 binary from %s' % url) 575 | package = self.download_url(url, 'Unable to download package from %s' % url) 576 | if not package: 577 | self.exit_code = 3 578 | return 579 | 580 | # we should only save downloaded package and delegate module 581 | # loading/unloading to main thread since improper PyV8 unload 582 | # may cause editor crash 583 | try: 584 | os.makedirs(self.download_path) 585 | except Exception as e: 586 | pass 587 | 588 | fp = open(os.path.join(self.download_path, 'pack.zip'), 'wb') 589 | fp.write(package) 590 | fp.close() 591 | 592 | self.result = cur_item['sha'] 593 | # Done! 594 | 595 | -------------------------------------------------------------------------------- /emmet-plugin.py: -------------------------------------------------------------------------------- 1 | import sublime 2 | import sublime_plugin 3 | 4 | import re 5 | import imp 6 | import json 7 | import sys 8 | import os.path 9 | import traceback 10 | 11 | BASE_PATH = os.path.abspath(os.path.dirname(__file__)) 12 | PACKAGES_PATH = sublime.packages_path() or os.path.dirname(BASE_PATH) 13 | # EMMET_GRAMMAR = os.path.join(BASE_PATH, 'Emmet.tmLanguage') 14 | EMMET_GRAMMAR = 'Packages/%s/Emmet.tmLanguage' % os.path.basename(BASE_PATH).replace('.sublime-package', '') 15 | sys.path += [BASE_PATH] + [os.path.join(BASE_PATH, f) for f in ['emmet_completions', 'emmet']] 16 | 17 | 18 | # Make sure all dependencies are reloaded on upgrade 19 | if 'emmet.reloader' in sys.modules: 20 | imp.reload(sys.modules['emmet.reloader']) 21 | import emmet.reloader 22 | 23 | # import completions as cmpl 24 | import emmet.pyv8loader as pyv8loader 25 | import emmet_completions as cmpl 26 | from emmet_completions.meta import HTML_ELEMENTS_ATTRIBUTES, HTML_ATTRIBUTES_VALUES 27 | from emmet.context import Context 28 | from emmet.context import js_file_reader as _js_file_reader 29 | from emmet.pyv8loader import LoaderDelegate 30 | 31 | __version__ = '1.1' 32 | __core_version__ = '1.0' 33 | __authors__ = ['"Sergey Chikuyonok" ' 34 | '"Nicholas Dudfield" '] 35 | 36 | is_python3 = sys.version_info[0] > 2 37 | 38 | # JS context 39 | ctx = None 40 | # Emmet Settings 41 | settings = None 42 | 43 | # Default ST settings 44 | user_settings = None 45 | 46 | def is_st3(): 47 | return sublime.version()[0] == '3' 48 | 49 | def js_file_reader(file_path, use_unicode=True): 50 | if hasattr(sublime, 'load_resource'): 51 | rel_path = file_path 52 | for prefix in [sublime.packages_path(), sublime.installed_packages_path()]: 53 | if rel_path.startswith(prefix): 54 | rel_path = os.path.join('Packages', rel_path[len(prefix) + 1:]) 55 | break 56 | 57 | rel_path = rel_path.replace('.sublime-package', '') 58 | # for Windows we have to replace slashes 59 | rel_path = rel_path.replace('\\', '/') 60 | return sublime.load_resource(rel_path) 61 | 62 | return _js_file_reader(file_path, use_unicode) 63 | 64 | def init(): 65 | "Init Emmet plugin" 66 | # load settings 67 | globals()['user_settings'] = sublime.load_settings('Preferences.sublime-settings') 68 | globals()['settings'] = sublime.load_settings('Emmet.sublime-settings') 69 | settings.add_on_change('extensions_path', update_settings) 70 | 71 | # setup environment for PyV8 loading 72 | pyv8_paths = [ 73 | os.path.join(PACKAGES_PATH, 'PyV8'), 74 | os.path.join(PACKAGES_PATH, 'PyV8', pyv8loader.get_arch()), 75 | os.path.join(PACKAGES_PATH, 'PyV8', 'pyv8-%s' % pyv8loader.get_arch()) 76 | ] 77 | 78 | sys.path += pyv8_paths 79 | 80 | # unpack recently loaded binary, is exists 81 | for p in pyv8_paths: 82 | pyv8loader.unpack_pyv8(p) 83 | 84 | # provide some contributions to JS 85 | contrib = { 86 | 'sublime': sublime, 87 | 'sublimeReplaceSubstring': replace_substring, 88 | 'sublimeGetOption': settings.get 89 | } 90 | 91 | # create JS environment 92 | delegate = SublimeLoaderDelegate() 93 | globals()['ctx'] = Context( 94 | files=['../editor.js'], 95 | ext_path=settings.get('extensions_path', None), 96 | contrib=contrib, 97 | logger=delegate.log, 98 | reader=js_file_reader 99 | ) 100 | 101 | update_settings() 102 | 103 | pyv8loader.load(pyv8_paths[1], delegate) 104 | 105 | if settings.get('remove_html_completions', False): 106 | sublime.set_timeout(cmpl.remove_html_completions, 2000) 107 | 108 | class SublimeLoaderDelegate(LoaderDelegate): 109 | def __init__(self, settings=None): 110 | 111 | if settings is None: 112 | settings = {} 113 | for k in ['http_proxy', 'https_proxy', 'timeout']: 114 | if user_settings.has(k): 115 | settings[k] = user_settings.get(k, None) 116 | 117 | LoaderDelegate.__init__(self, settings) 118 | self.state = None 119 | self.message = 'Loading PyV8 binary, please wait' 120 | self.i = 0 121 | self.addend = 1 122 | self.size = 8 123 | 124 | def on_start(self, *args, **kwargs): 125 | self.state = 'loading' 126 | 127 | def on_progress(self, *args, **kwargs): 128 | if kwargs['progress'].is_background: 129 | return 130 | 131 | before = self.i % self.size 132 | after = (self.size - 1) - before 133 | msg = '%s [%s=%s]' % (self.message, ' ' * before, ' ' * after) 134 | if not after: 135 | self.addend = -1 136 | if not before: 137 | self.addend = 1 138 | self.i += self.addend 139 | 140 | sublime.set_timeout(lambda: sublime.status_message(msg), 0) 141 | 142 | def on_complete(self, *args, **kwargs): 143 | self.state = 'complete' 144 | 145 | if kwargs['progress'].is_background: 146 | return 147 | 148 | sublime.set_timeout(lambda: sublime.status_message('PyV8 binary successfully loaded'), 0) 149 | 150 | def on_error(self, exit_code=-1, thread=None): 151 | self.state = 'error' 152 | sublime.set_timeout(lambda: show_pyv8_error(exit_code), 0) 153 | 154 | def setting(self, name, default=None): 155 | "Returns specified setting name" 156 | return self.settings.get(name, default) 157 | 158 | def log(self, message): 159 | print('Emmet: %s' % message) 160 | 161 | def show_pyv8_error(exit_code): 162 | if 'PyV8' not in sys.modules: 163 | sublime.error_message('Error while loading PyV8 binary: exit code %s \nTry to manually install PyV8 from\nhttps://github.com/emmetio/pyv8-binaries' % exit_code) 164 | 165 | def active_view(): 166 | return sublime.active_window().active_view() 167 | 168 | def check_context(verbose=False): 169 | "Checks if JS context is completely available" 170 | if not ctx.js(): 171 | if verbose: 172 | sublime.message_dialog('Please wait a bit while PyV8 binary is being downloaded') 173 | return False 174 | 175 | return True 176 | 177 | 178 | def replace_substring(start, end, value, no_indent=False): 179 | view = active_view() 180 | 181 | view.sel().clear() 182 | view.sel().add(sublime.Region(start, end or start)) 183 | 184 | if not is_python3: 185 | value = value.decode('utf-8') 186 | 187 | # XXX a bit naive indentation control. It handles most common 188 | # `no_indent` usages like replacing CSS rule content, but may not 189 | # produce expected result in all possible situations 190 | 191 | if no_indent: 192 | line = view.substr(view.line(view.sel()[0])) 193 | value = unindent_text(value, get_line_padding(line)) 194 | 195 | view.run_command('insert_snippet', {'contents': value}) 196 | 197 | def unindent_text(text, pad): 198 | """ 199 | Removes padding at the beginning of each text's line 200 | @type text: str 201 | @type pad: str 202 | """ 203 | lines = text.splitlines() 204 | 205 | for i,line in enumerate(lines): 206 | if line.startswith(pad): 207 | lines[i] = line[len(pad):] 208 | 209 | return '\n'.join(lines) 210 | 211 | def get_line_padding(line): 212 | """ 213 | Returns padding of current editor's line 214 | @return str 215 | """ 216 | m = re.match(r'^(\s+)', line) 217 | return m and m.group(0) or '' 218 | 219 | def update_settings(): 220 | ctx.set_ext_path(settings.get('extensions_path', None)) 221 | 222 | keys = ['snippets', 'preferences', 'syntaxProfiles', 'profiles'] 223 | payload = {} 224 | for k in keys: 225 | data = settings.get(k, None) 226 | if data: 227 | payload[k] = data 228 | 229 | ctx.reset() 230 | ctx.load_user_data(json.dumps(payload)) 231 | ctx.js() 232 | 233 | def get_scope(view, pt=-1): 234 | if pt == -1: 235 | # use current caret position 236 | pt = view.sel()[0].begin() 237 | 238 | if hasattr(view, 'scope_name'): 239 | return view.scope_name(pt) 240 | 241 | return view.syntax_name(pt) 242 | 243 | def should_perform_action(name, view=None): 244 | if not view: 245 | view = active_view() 246 | 247 | # fallback to old check 248 | if not view.settings().get('enable_emmet_keymap', True): 249 | return False 250 | 251 | disabled_actions = settings.get('disabled_keymap_actions', '') 252 | 253 | if not disabled_actions: # no disabled actions 254 | return True 255 | 256 | if disabled_actions == 'all': # disable all actions 257 | return False 258 | 259 | return name not in re.split(r'\s*,\s*', disabled_actions.strip()) 260 | 261 | def should_handle_tab_key(syntax=None): 262 | view = active_view() 263 | scopes = settings.get('disabled_single_snippet_for_scopes', None) 264 | cur_scope = get_scope(view) 265 | 266 | if sublime.score_selector(cur_scope, 'source.css'): 267 | return True 268 | 269 | if not scopes or not sublime.score_selector(cur_scope, scopes): 270 | return True 271 | 272 | with ctx.js() as c: 273 | abbr = c.locals.pyExtractAbbreviation() 274 | 275 | disabled_snippets = settings.get('disabled_single_snippets', '').split() 276 | if disabled_snippets and abbr in disabled_snippets: 277 | return False 278 | 279 | if not re.match(r'^[\w\-\:%]+$', abbr): 280 | # it's a complex expression 281 | return True 282 | 283 | if re.match(r'^(lorem|lipsum)([a-z]{2})?\d*$', abbr, re.I): 284 | # hardcoded Lorem Ipsum generator 285 | return True 286 | 287 | # detect inline CSS 288 | if syntax is None: 289 | syntax = c.locals.pyGetSyntax(); 290 | 291 | if syntax == 'css': 292 | return True 293 | 294 | known_tags = settings.get('known_html_tags', '').split() 295 | if abbr in known_tags or c.locals.pyHasSnippet(abbr): 296 | return True 297 | 298 | return False 299 | 300 | def log(message): 301 | if settings.get('debug', False): 302 | print('Emmet: %s' % message) 303 | 304 | def action_factory(name): 305 | def _action(i, sel): 306 | with ctx.js() as c: 307 | return c.locals.pyRunAction(name) 308 | return _action 309 | 310 | class RunEmmetAction(sublime_plugin.TextCommand): 311 | def run(self, edit, action=None, **kw): 312 | run_action(action_factory(action)) 313 | 314 | class ActionContextHandler(sublime_plugin.EventListener): 315 | def on_query_context(self, view, key, op, operand, match_all): 316 | if not key.startswith('emmet_action_enabled.'): 317 | return None 318 | 319 | prefix, name = key.split('.') 320 | return should_perform_action(name, view) 321 | 322 | def get_edit(view, edit_token=None): 323 | edit = None 324 | try: 325 | edit = view.begin_edit() 326 | except: 327 | pass 328 | 329 | if not edit and edit_token: 330 | try: 331 | edit = view.begin_edit(edit_token, 'Emmet') 332 | except Exception as e: 333 | pass 334 | 335 | return edit 336 | 337 | def run_action(action, view=None): 338 | if not check_context(True): 339 | return 340 | 341 | "Runs Emmet action in multiselection mode" 342 | if not view: 343 | view = active_view() 344 | 345 | region_key = '__emmet__' 346 | sels = list(view.sel()) 347 | result = False 348 | 349 | # edit = get_edit(view, edit_token) 350 | max_sel_ix = len(sels) - 1 351 | 352 | try: 353 | for i, sel in enumerate(reversed(sels)): 354 | view.sel().clear() 355 | view.sel().add(sel) 356 | # run action 357 | # result = r(name) or result 358 | result = action(max_sel_ix - i, sel) or result 359 | 360 | # remember resulting selections 361 | view.add_regions(region_key, 362 | (view.get_regions(region_key) + list(view.sel())) , '') 363 | except Exception as e: 364 | view.erase_regions(region_key) 365 | print(traceback.format_exc()) 366 | return 367 | 368 | 369 | # output all saved regions as selection 370 | view.sel().clear() 371 | for sel in view.get_regions(region_key): 372 | view.sel().add(sel) 373 | 374 | view.erase_regions(region_key) 375 | 376 | # if edit: 377 | # view.end_edit(edit) 378 | return result 379 | 380 | class TabAndCompletionsHandler(): 381 | def correct_syntax(self, view, syntax='html'): 382 | return syntax == 'html' and view.match_selector( view.sel()[0].b, cmpl.EMMET_SCOPE ) 383 | 384 | def completion_handler(self, view): 385 | "Returns completions handler fo current caret position" 386 | black_list = settings.get('completions_blacklist', []) 387 | 388 | # A mapping of scopes, sub scopes and handlers, first matching of which 389 | # is used. 390 | COMPLETIONS = ( 391 | (cmpl.HTML_INSIDE_TAG, self.html_elements_attributes), 392 | (cmpl.HTML_INSIDE_TAG_ATTRIBUTE, self.html_attributes_values) 393 | ) 394 | 395 | pos = view.sel()[0].b 396 | 397 | # Try to find some more specific contextual abbreviation 398 | for sub_selector, handler in COMPLETIONS: 399 | h_name = handler.__name__ 400 | if not black_list or h_name in black_list: continue 401 | if (view.match_selector(pos, sub_selector) or 402 | view.match_selector(pos - 1, sub_selector)): 403 | return handler 404 | 405 | return None 406 | 407 | def html_elements_attributes(self, view, prefix, pos): 408 | tag = cmpl.find_tag_name(view, pos) 409 | values = HTML_ELEMENTS_ATTRIBUTES.get(tag, []) 410 | return [(v, '%s\t@%s' % (v,v), '%s="$1"' % v) for v in values] 411 | 412 | def html_attributes_values(self, view, prefix, pos): 413 | attr = cmpl.find_attribute_name(view, pos) 414 | values = HTML_ATTRIBUTES_VALUES.get(attr, []) 415 | return [(v, '%s\t@=%s' % (v,v), v) for v in values] 416 | 417 | def expand_by_tab(self, view): 418 | if not check_context(): 419 | return False; 420 | 421 | with ctx.js() as c: 422 | syntax = str(c.locals.pyGetSyntax()); 423 | 424 | if not should_handle_tab_key(syntax): 425 | return False 426 | 427 | # we need to filter out attribute completions if 428 | # 'disable_completions' option is not active 429 | if (not settings.get('disable_completions', False) and 430 | self.correct_syntax(view, syntax) and 431 | self.completion_handler(view)): 432 | return None 433 | 434 | caret_pos = view.sel()[0].begin() 435 | cur_scope = get_scope(view) 436 | 437 | # let's see if Tab key expander should be disabled for current scope 438 | banned_scopes = settings.get('disable_tab_abbreviations_for_scopes', '') 439 | if banned_scopes and view.score_selector(caret_pos, banned_scopes): 440 | return None 441 | 442 | # Sometimes ST2 matcher may incorrectly filter scope context, 443 | # check it against special regexp 444 | banned_regexp = settings.get('disable_tab_abbreviations_for_regexp', None) 445 | if banned_regexp and re.search(banned_regexp, cur_scope): 446 | return None 447 | 448 | return run_action(action_factory('expand_abbreviation')) 449 | # view.run_command('run_emmet_action', 450 | # {'action':'expand_abbreviation'}) 451 | 452 | class ExpandAbbreviationByTab(sublime_plugin.TextCommand): 453 | def run(self, edit, **kw): 454 | if settings.get('use_old_tab_handler', False): 455 | return 456 | 457 | view = active_view() 458 | h = TabAndCompletionsHandler() 459 | if not h.expand_by_tab(view): 460 | # try to mimic default Tab behaviour of Sublime Text 461 | view.run_command('insert_best_completion', { 462 | 'default': '\t', 463 | 'exact': user_settings.get('tab_completion', True) 464 | }) 465 | 466 | 467 | class TabExpandHandler(sublime_plugin.EventListener): 468 | def on_query_context(self, view, key, op, operand, match_all): 469 | if key != 'is_abbreviation': 470 | return None 471 | 472 | if settings.get('use_old_tab_handler', False): 473 | h = TabAndCompletionsHandler() 474 | return h.expand_by_tab(view) 475 | 476 | return check_context() 477 | 478 | def on_query_completions(self, view, prefix, locations): 479 | h = TabAndCompletionsHandler() 480 | if view.match_selector(locations[0], settings.get('css_completions_scope', '')) and check_context(): 481 | l = [] 482 | if settings.get('show_css_completions', False): 483 | with ctx.js() as c: 484 | completions = c.locals.pyGetCSSCompletions() 485 | if completions: 486 | for p in completions: 487 | l.append(('%s\t%s' % (p['k'], p['label']), p['v'])) 488 | 489 | if not l: 490 | return [] 491 | 492 | return (l, sublime.INHIBIT_WORD_COMPLETIONS | sublime.INHIBIT_EXPLICIT_COMPLETIONS) 493 | 494 | if not h.correct_syntax(view) or settings.get('disable_completions', False): 495 | return [] 496 | 497 | handler = h.completion_handler(view) 498 | if handler: 499 | pos = view.sel()[0].b 500 | completions = handler(view, prefix, pos) 501 | return completions 502 | 503 | return [] 504 | 505 | 506 | class CommandsAsYouTypeBase(sublime_plugin.TextCommand): 507 | input_message = "Enter Input" 508 | default_input = "" 509 | process_panel_input = lambda s, i: i.title() 510 | 511 | # Note that this must be of form `Packages/$Package/Emmet.tmLanguage` on ST3 512 | # NOT an absolute path! 513 | panel_grammar = EMMET_GRAMMAR 514 | 515 | def is_enabled(self): 516 | return True 517 | 518 | def run_command(self, edit, view, processed_input): 519 | if '\n' in processed_input: 520 | for sel in view.sel(): 521 | trailing = sublime.Region(sel.end(), view.line(sel).end()) 522 | if view.substr(trailing).isspace(): 523 | view.erase(edit, trailing) 524 | 525 | if not is_python3: 526 | processed_input = processed_input.decode('utf-8') 527 | view.run_command('insert_snippet', { 'contents': processed_input }) 528 | 529 | def on_panel_change(self, abbr): 530 | if not abbr and self.erase: 531 | self.undo() 532 | self.erase = False 533 | return 534 | 535 | def inner_insert(): 536 | self.view.run_command(self.name(), dict(panel_input=abbr)) 537 | # self.view.run_command('hide_auto_complete') 538 | 539 | self.undo() 540 | sublime.set_timeout(inner_insert, 0) 541 | 542 | def undo(self): 543 | if self.erase: 544 | sublime.set_timeout(lambda: self.view.run_command('undo'), 0) 545 | 546 | def remember_sels(self, view): 547 | self._sels = list(view.sel()) 548 | self._sel_items = [] 549 | 550 | for sel in self._sels: 551 | # selection should be unindented in order to get desired result 552 | line = view.substr(view.line(sel)) 553 | s = view.substr(sel) 554 | self._sel_items.append(unindent_text(s, get_line_padding(line))) 555 | 556 | def on_panel_done(self, abbr): 557 | pass 558 | 559 | def run(self, edit, panel_input=None, **kwargs): 560 | 561 | if panel_input is None: 562 | self.setup(edit, self.view, **kwargs) 563 | self.erase = False 564 | 565 | panel = self.view.window().show_input_panel ( 566 | self.input_message, 567 | self.default_input, 568 | self.on_panel_done, # on_done 569 | self.on_panel_change, # on_change 570 | self.undo) # on_cancel 571 | 572 | panel.sel().clear() 573 | panel.sel().add(sublime.Region(0, panel.size())) 574 | 575 | if self.panel_grammar: 576 | panel.set_syntax_file(self.panel_grammar) 577 | panel_setting = panel.settings().set 578 | 579 | panel_setting('line_numbers', False) 580 | panel_setting('gutter', False) 581 | panel_setting('auto_complete', False) 582 | panel_setting('tab_completion', False) 583 | else: 584 | self.run_on_input(edit, self.view, panel_input) 585 | 586 | def setup(self, edit, view, **kwargs): 587 | pass 588 | 589 | def run_on_input(self, edit, view, panel_input): 590 | view = self.view 591 | cmd_input = self.process_panel_input(panel_input) or '' 592 | try: 593 | self.erase = self.run_command(edit, view, cmd_input) is not False 594 | except: 595 | pass 596 | 597 | class WrapAsYouType(CommandsAsYouTypeBase): 598 | default_input = 'div' 599 | _prev_output = '' 600 | input_message = "Enter Wrap Abbreviation: " 601 | 602 | def setup(self, edit, view, **kwargs): 603 | self._prev_output = '' 604 | 605 | if len(view.sel()) == 1: 606 | # capture wrapping context (parent HTML element) 607 | # if there is only one selection 608 | with ctx.js() as c: 609 | r = c.locals.pyCaptureWrappingRange() 610 | if r: 611 | view.sel().clear() 612 | view.sel().add(sublime.Region(r[0], r[1])) 613 | view.show(view.sel()) 614 | 615 | self.remember_sels(view) 616 | 617 | # override method to correctly wrap abbreviations 618 | def run_on_input(self, edit, view, abbr): 619 | # def _real_insert(self, abbr): 620 | # view = self.view 621 | # self.edit = get_edit(view, self.edit_token) 622 | 623 | self.erase = True 624 | 625 | # restore selections 626 | view.sel().clear() 627 | for sel in self._sels: 628 | view.sel().add(sel) 629 | 630 | def ins(i, sel): 631 | try: 632 | with ctx.js() as c: 633 | self._prev_output = c.locals.pyWrapAsYouType(abbr, self._sel_items[i]) 634 | # self.run_command(view, output) 635 | except Exception: 636 | "dont litter the console" 637 | 638 | self.run_command(edit, view, self._prev_output) 639 | 640 | run_action(ins, view) 641 | # if self.edit: 642 | # view.end_edit(self.edit) 643 | 644 | class ExpandAsYouType(WrapAsYouType): 645 | default_input = 'div' 646 | input_message = "Enter Abbreviation: " 647 | 648 | def setup(self, edit, view, **kwargs): 649 | # adjust selection to non-space bounds 650 | sels = [] 651 | for s in view.sel(): 652 | text = view.substr(s) 653 | a = s.a + len(text) - len(text.lstrip()) 654 | b = s.b - len(text) + len(text.rstrip()) 655 | 656 | sels.append(sublime.Region(a, b)) 657 | 658 | view.sel().clear() 659 | for s in sels: 660 | view.sel().add(s) 661 | 662 | self.remember_sels(active_view()) 663 | 664 | 665 | class EnterKeyHandler(sublime_plugin.EventListener): 666 | def on_query_context(self, view, key, op, operand, match_all): 667 | if key != 'clear_fields_on_enter_key': 668 | return None 669 | 670 | if settings.get('clear_fields_on_enter_key', False): 671 | view.run_command('clear_fields') 672 | 673 | return True 674 | 675 | 676 | class RenameTag(sublime_plugin.TextCommand): 677 | def run(self, edit, **kw): 678 | if not check_context(True): 679 | return 680 | 681 | view = active_view() 682 | sels = list(view.sel()) 683 | sel_cleared = False 684 | with ctx.js() as c: 685 | for s in sels: 686 | ranges = c.locals.pyGetTagNameRanges(s.begin()) 687 | if ranges: 688 | if not sel_cleared: 689 | view.sel().clear() 690 | sel_cleared = True 691 | 692 | for r in ranges: 693 | view.sel().add(sublime.Region(r[0], r[1])) 694 | view.show(view.sel()) 695 | 696 | class EmmetInsertAttribute(sublime_plugin.TextCommand): 697 | def run(self, edit, attribute=None, **kw): 698 | if not attribute: 699 | return 700 | 701 | view = active_view() 702 | prefix = '' 703 | if view.sel(): 704 | sel = view.sel()[0] 705 | if not view.substr(sublime.Region(sel.begin() - 1, sel.begin())).isspace(): 706 | prefix = ' ' 707 | 708 | view.run_command('insert_snippet', {'contents': '%s%s="$1"' % (prefix, attribute)}) 709 | 710 | class EmmetResetContext(sublime_plugin.TextCommand): 711 | def run(self, edit, **kw): 712 | update_settings() 713 | 714 | def plugin_loaded(): 715 | sublime.set_timeout(init, 200) 716 | 717 | ################## 718 | # Init plugin 719 | if not is_python3: 720 | init() 721 | 722 | -------------------------------------------------------------------------------- /emmet/snippets.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "lang": "en", 4 | "locale": "en-US", 5 | "charset": "UTF-8", 6 | "indentation": "\t", 7 | "newline": "\n" 8 | }, 9 | 10 | "css": { 11 | "filters": "html", 12 | "snippets": { 13 | "@i": "@import url(|);", 14 | "@import": "@import url(|);", 15 | "@m": "@media ${1:screen} {\n\t|\n}", 16 | "@media": "@media ${1:screen} {\n\t|\n}", 17 | "@f": "@font-face {\n\tfont-family:|;\n\tsrc:url(|);\n}", 18 | "@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}", 19 | 20 | "@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}", 21 | 22 | 23 | "anim": "animation:|;", 24 | "anim-": "animation:${1:name} ${2:duration} ${3:timing-function} ${4:delay} ${5:iteration-count} ${6:direction} ${7:fill-mode};", 25 | "animdel": "animation-delay:${1:time};", 26 | 27 | "animdir": "animation-direction:${1:normal};", 28 | "animdir:n": "animation-direction:normal;", 29 | "animdir:r": "animation-direction:reverse;", 30 | "animdir:a": "animation-direction:alternate;", 31 | "animdir:ar": "animation-direction:alternate-reverse;", 32 | 33 | "animdur": "animation-duration:${1:0}s;", 34 | 35 | "animfm": "animation-fill-mode:${1:both};", 36 | "animfm:f": "animation-fill-mode:forwards;", 37 | "animfm:b": "animation-fill-mode:backwards;", 38 | "animfm:bt": "animation-fill-mode:both;", 39 | "animfm:bh": "animation-fill-mode:both;", 40 | 41 | "animic": "animation-iteration-count:${1:1};", 42 | "animic:i": "animation-iteration-count:infinite;", 43 | 44 | "animn": "animation-name:${1:none};", 45 | 46 | "animps": "animation-play-state:${1:running};", 47 | "animps:p": "animation-play-state:paused;", 48 | "animps:r": "animation-play-state:running;", 49 | 50 | "animtf": "animation-timing-function:${1:linear};", 51 | "animtf:e": "animation-timing-function:ease;", 52 | "animtf:ei": "animation-timing-function:ease-in;", 53 | "animtf:eo": "animation-timing-function:ease-out;", 54 | "animtf:eio": "animation-timing-function:ease-in-out;", 55 | "animtf:l": "animation-timing-function:linear;", 56 | "animtf:cb": "animation-timing-function:cubic-bezier(${1:0.1}, ${2:0.7}, ${3:1.0}, ${3:0.1});", 57 | 58 | "ap": "appearance:${none};", 59 | 60 | "!": "!important", 61 | "pos": "position:${1:relative};", 62 | "pos:s": "position:static;", 63 | "pos:a": "position:absolute;", 64 | "pos:r": "position:relative;", 65 | "pos:f": "position:fixed;", 66 | "t": "top:|;", 67 | "t:a": "top:auto;", 68 | "r": "right:|;", 69 | "r:a": "right:auto;", 70 | "b": "bottom:|;", 71 | "b:a": "bottom:auto;", 72 | "l": "left:|;", 73 | "l:a": "left:auto;", 74 | "z": "z-index:|;", 75 | "z:a": "z-index:auto;", 76 | "fl": "float:${1:left};", 77 | "fl:n": "float:none;", 78 | "fl:l": "float:left;", 79 | "fl:r": "float:right;", 80 | "cl": "clear:${1:both};", 81 | "cl:n": "clear:none;", 82 | "cl:l": "clear:left;", 83 | "cl:r": "clear:right;", 84 | "cl:b": "clear:both;", 85 | 86 | "colm": "columns:|;", 87 | "colmc": "column-count:|;", 88 | "colmf": "column-fill:|;", 89 | "colmg": "column-gap:|;", 90 | "colmr": "column-rule:|;", 91 | "colmrc": "column-rule-color:|;", 92 | "colmrs": "column-rule-style:|;", 93 | "colmrw": "column-rule-width:|;", 94 | "colms": "column-span:|;", 95 | "colmw": "column-width:|;", 96 | 97 | "d": "display:${1:block};", 98 | "d:n": "display:none;", 99 | "d:b": "display:block;", 100 | "d:i": "display:inline;", 101 | "d:ib": "display:inline-block;", 102 | "d:ib+": "display: inline-block;\n*display: inline;\n*zoom: 1;", 103 | "d:li": "display:list-item;", 104 | "d:ri": "display:run-in;", 105 | "d:cp": "display:compact;", 106 | "d:tb": "display:table;", 107 | "d:itb": "display:inline-table;", 108 | "d:tbcp": "display:table-caption;", 109 | "d:tbcl": "display:table-column;", 110 | "d:tbclg": "display:table-column-group;", 111 | "d:tbhg": "display:table-header-group;", 112 | "d:tbfg": "display:table-footer-group;", 113 | "d:tbr": "display:table-row;", 114 | "d:tbrg": "display:table-row-group;", 115 | "d:tbc": "display:table-cell;", 116 | "d:rb": "display:ruby;", 117 | "d:rbb": "display:ruby-base;", 118 | "d:rbbg": "display:ruby-base-group;", 119 | "d:rbt": "display:ruby-text;", 120 | "d:rbtg": "display:ruby-text-group;", 121 | "v": "visibility:${1:hidden};", 122 | "v:v": "visibility:visible;", 123 | "v:h": "visibility:hidden;", 124 | "v:c": "visibility:collapse;", 125 | "ov": "overflow:${1:hidden};", 126 | "ov:v": "overflow:visible;", 127 | "ov:h": "overflow:hidden;", 128 | "ov:s": "overflow:scroll;", 129 | "ov:a": "overflow:auto;", 130 | "ovx": "overflow-x:${1:hidden};", 131 | "ovx:v": "overflow-x:visible;", 132 | "ovx:h": "overflow-x:hidden;", 133 | "ovx:s": "overflow-x:scroll;", 134 | "ovx:a": "overflow-x:auto;", 135 | "ovy": "overflow-y:${1:hidden};", 136 | "ovy:v": "overflow-y:visible;", 137 | "ovy:h": "overflow-y:hidden;", 138 | "ovy:s": "overflow-y:scroll;", 139 | "ovy:a": "overflow-y:auto;", 140 | "ovs": "overflow-style:${1:scrollbar};", 141 | "ovs:a": "overflow-style:auto;", 142 | "ovs:s": "overflow-style:scrollbar;", 143 | "ovs:p": "overflow-style:panner;", 144 | "ovs:m": "overflow-style:move;", 145 | "ovs:mq": "overflow-style:marquee;", 146 | "zoo": "zoom:1;", 147 | "zm": "zoom:1;", 148 | "cp": "clip:|;", 149 | "cp:a": "clip:auto;", 150 | "cp:r": "clip:rect(${1:top} ${2:right} ${3:bottom} ${4:left});", 151 | "bxz": "box-sizing:${1:border-box};", 152 | "bxz:cb": "box-sizing:content-box;", 153 | "bxz:bb": "box-sizing:border-box;", 154 | "bxsh": "box-shadow:${1:inset }${2:hoff} ${3:voff} ${4:blur} ${5:color};", 155 | "bxsh:r": "box-shadow:${1:inset }${2:hoff} ${3:voff} ${4:blur} ${5:spread }rgb(${6:0}, ${7:0}, ${8:0});", 156 | "bxsh:ra": "box-shadow:${1:inset }${2:h} ${3:v} ${4:blur} ${5:spread }rgba(${6:0}, ${7:0}, ${8:0}, .${9:5});", 157 | "bxsh:n": "box-shadow:none;", 158 | "m": "margin:|;", 159 | "m:a": "margin:auto;", 160 | "mt": "margin-top:|;", 161 | "mt:a": "margin-top:auto;", 162 | "mr": "margin-right:|;", 163 | "mr:a": "margin-right:auto;", 164 | "mb": "margin-bottom:|;", 165 | "mb:a": "margin-bottom:auto;", 166 | "ml": "margin-left:|;", 167 | "ml:a": "margin-left:auto;", 168 | "p": "padding:|;", 169 | "pt": "padding-top:|;", 170 | "pr": "padding-right:|;", 171 | "pb": "padding-bottom:|;", 172 | "pl": "padding-left:|;", 173 | "w": "width:|;", 174 | "w:a": "width:auto;", 175 | "h": "height:|;", 176 | "h:a": "height:auto;", 177 | "maw": "max-width:|;", 178 | "maw:n": "max-width:none;", 179 | "mah": "max-height:|;", 180 | "mah:n": "max-height:none;", 181 | "miw": "min-width:|;", 182 | "mih": "min-height:|;", 183 | "mar": "max-resolution:${1:res};", 184 | "mir": "min-resolution:${1:res};", 185 | "ori": "orientation:|;", 186 | "ori:l": "orientation:landscape;", 187 | "ori:p": "orientation:portrait;", 188 | "ol": "outline:|;", 189 | "ol:n": "outline:none;", 190 | "olo": "outline-offset:|;", 191 | "olw": "outline-width:|;", 192 | "olw:tn": "outline-width:thin;", 193 | "olw:m": "outline-width:medium;", 194 | "olw:tc": "outline-width:thick;", 195 | "ols": "outline-style:|;", 196 | "ols:n": "outline-style:none;", 197 | "ols:dt": "outline-style:dotted;", 198 | "ols:ds": "outline-style:dashed;", 199 | "ols:s": "outline-style:solid;", 200 | "ols:db": "outline-style:double;", 201 | "ols:g": "outline-style:groove;", 202 | "ols:r": "outline-style:ridge;", 203 | "ols:i": "outline-style:inset;", 204 | "ols:o": "outline-style:outset;", 205 | "olc": "outline-color:#${1:000};", 206 | "olc:i": "outline-color:invert;", 207 | "bd": "border:|;", 208 | "bd+": "border:${1:1px} ${2:solid} ${3:#000};", 209 | "bd:n": "border:none;", 210 | "bdbk": "border-break:${1:close};", 211 | "bdbk:c": "border-break:close;", 212 | "bdcl": "border-collapse:|;", 213 | "bdcl:c": "border-collapse:collapse;", 214 | "bdcl:s": "border-collapse:separate;", 215 | "bdc": "border-color:#${1:000};", 216 | "bdc:t": "border-color:transparent;", 217 | "bdi": "border-image:url(|);", 218 | "bdi:n": "border-image:none;", 219 | "bdti": "border-top-image:url(|);", 220 | "bdti:n": "border-top-image:none;", 221 | "bdri": "border-right-image:url(|);", 222 | "bdri:n": "border-right-image:none;", 223 | "bdbi": "border-bottom-image:url(|);", 224 | "bdbi:n": "border-bottom-image:none;", 225 | "bdli": "border-left-image:url(|);", 226 | "bdli:n": "border-left-image:none;", 227 | "bdci": "border-corner-image:url(|);", 228 | "bdci:n": "border-corner-image:none;", 229 | "bdci:c": "border-corner-image:continue;", 230 | "bdtli": "border-top-left-image:url(|);", 231 | "bdtli:n": "border-top-left-image:none;", 232 | "bdtli:c": "border-top-left-image:continue;", 233 | "bdtri": "border-top-right-image:url(|);", 234 | "bdtri:n": "border-top-right-image:none;", 235 | "bdtri:c": "border-top-right-image:continue;", 236 | "bdbri": "border-bottom-right-image:url(|);", 237 | "bdbri:n": "border-bottom-right-image:none;", 238 | "bdbri:c": "border-bottom-right-image:continue;", 239 | "bdbli": "border-bottom-left-image:url(|);", 240 | "bdbli:n": "border-bottom-left-image:none;", 241 | "bdbli:c": "border-bottom-left-image:continue;", 242 | "bdf": "border-fit:${1:repeat};", 243 | "bdf:c": "border-fit:clip;", 244 | "bdf:r": "border-fit:repeat;", 245 | "bdf:sc": "border-fit:scale;", 246 | "bdf:st": "border-fit:stretch;", 247 | "bdf:ow": "border-fit:overwrite;", 248 | "bdf:of": "border-fit:overflow;", 249 | "bdf:sp": "border-fit:space;", 250 | "bdlen": "border-length:|;", 251 | "bdlen:a": "border-length:auto;", 252 | "bdsp": "border-spacing:|;", 253 | "bds": "border-style:|;", 254 | "bds:n": "border-style:none;", 255 | "bds:h": "border-style:hidden;", 256 | "bds:dt": "border-style:dotted;", 257 | "bds:ds": "border-style:dashed;", 258 | "bds:s": "border-style:solid;", 259 | "bds:db": "border-style:double;", 260 | "bds:dtds": "border-style:dot-dash;", 261 | "bds:dtdtds": "border-style:dot-dot-dash;", 262 | "bds:w": "border-style:wave;", 263 | "bds:g": "border-style:groove;", 264 | "bds:r": "border-style:ridge;", 265 | "bds:i": "border-style:inset;", 266 | "bds:o": "border-style:outset;", 267 | "bdw": "border-width:|;", 268 | "bdtw": "border-top-width:|;", 269 | "bdrw": "border-right-width:|;", 270 | "bdbw": "border-bottom-width:|;", 271 | "bdlw": "border-left-width:|;", 272 | "bdt": "border-top:|;", 273 | "bt": "border-top:|;", 274 | "bdt+": "border-top:${1:1px} ${2:solid} ${3:#000};", 275 | "bdt:n": "border-top:none;", 276 | "bdts": "border-top-style:|;", 277 | "bdts:n": "border-top-style:none;", 278 | "bdtc": "border-top-color:#${1:000};", 279 | "bdtc:t": "border-top-color:transparent;", 280 | "bdr": "border-right:|;", 281 | "br": "border-right:|;", 282 | "bdr+": "border-right:${1:1px} ${2:solid} ${3:#000};", 283 | "bdr:n": "border-right:none;", 284 | "bdrst": "border-right-style:|;", 285 | "bdrst:n": "border-right-style:none;", 286 | "bdrc": "border-right-color:#${1:000};", 287 | "bdrc:t": "border-right-color:transparent;", 288 | "bdb": "border-bottom:|;", 289 | "bb": "border-bottom:|;", 290 | "bdb+": "border-bottom:${1:1px} ${2:solid} ${3:#000};", 291 | "bdb:n": "border-bottom:none;", 292 | "bdbs": "border-bottom-style:|;", 293 | "bdbs:n": "border-bottom-style:none;", 294 | "bdbc": "border-bottom-color:#${1:000};", 295 | "bdbc:t": "border-bottom-color:transparent;", 296 | "bdl": "border-left:|;", 297 | "bl": "border-left:|;", 298 | "bdl+": "border-left:${1:1px} ${2:solid} ${3:#000};", 299 | "bdl:n": "border-left:none;", 300 | "bdls": "border-left-style:|;", 301 | "bdls:n": "border-left-style:none;", 302 | "bdlc": "border-left-color:#${1:000};", 303 | "bdlc:t": "border-left-color:transparent;", 304 | "bdrs": "border-radius:|;", 305 | "bdtrrs": "border-top-right-radius:|;", 306 | "bdtlrs": "border-top-left-radius:|;", 307 | "bdbrrs": "border-bottom-right-radius:|;", 308 | "bdblrs": "border-bottom-left-radius:|;", 309 | "bg": "background:#${1:000};", 310 | "bg+": "background:${1:#fff} url(${2}) ${3:0} ${4:0} ${5:no-repeat};", 311 | "bg:n": "background:none;", 312 | "bg:ie": "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='${1:x}.png',sizingMethod='${2:crop}');", 313 | "bgc": "background-color:#${1:fff};", 314 | "bgc:t": "background-color:transparent;", 315 | "bgi": "background-image:url(|);", 316 | "bgi:n": "background-image:none;", 317 | "bgr": "background-repeat:|;", 318 | "bgr:n": "background-repeat:no-repeat;", 319 | "bgr:x": "background-repeat:repeat-x;", 320 | "bgr:y": "background-repeat:repeat-y;", 321 | "bgr:sp": "background-repeat:space;", 322 | "bgr:rd": "background-repeat:round;", 323 | "bga": "background-attachment:|;", 324 | "bga:f": "background-attachment:fixed;", 325 | "bga:s": "background-attachment:scroll;", 326 | "bgp": "background-position:${1:0} ${2:0};", 327 | "bgpx": "background-position-x:|;", 328 | "bgpy": "background-position-y:|;", 329 | "bgbk": "background-break:|;", 330 | "bgbk:bb": "background-break:bounding-box;", 331 | "bgbk:eb": "background-break:each-box;", 332 | "bgbk:c": "background-break:continuous;", 333 | "bgcp": "background-clip:${1:padding-box};", 334 | "bgcp:bb": "background-clip:border-box;", 335 | "bgcp:pb": "background-clip:padding-box;", 336 | "bgcp:cb": "background-clip:content-box;", 337 | "bgcp:nc": "background-clip:no-clip;", 338 | "bgo": "background-origin:|;", 339 | "bgo:pb": "background-origin:padding-box;", 340 | "bgo:bb": "background-origin:border-box;", 341 | "bgo:cb": "background-origin:content-box;", 342 | "bgsz": "background-size:|;", 343 | "bgsz:a": "background-size:auto;", 344 | "bgsz:ct": "background-size:contain;", 345 | "bgsz:cv": "background-size:cover;", 346 | "c": "color:#${1:000};", 347 | "c:r": "color:rgb(${1:0}, ${2:0}, ${3:0});", 348 | "c:ra": "color:rgba(${1:0}, ${2:0}, ${3:0}, .${4:5});", 349 | "cm": "/* |${child} */", 350 | "cnt": "content:'|';", 351 | "cnt:n": "content:normal;", 352 | "cnt:oq": "content:open-quote;", 353 | "cnt:noq": "content:no-open-quote;", 354 | "cnt:cq": "content:close-quote;", 355 | "cnt:ncq": "content:no-close-quote;", 356 | "cnt:a": "content:attr(|);", 357 | "cnt:c": "content:counter(|);", 358 | "cnt:cs": "content:counters(|);", 359 | 360 | 361 | "tbl": "table-layout:|;", 362 | "tbl:a": "table-layout:auto;", 363 | "tbl:f": "table-layout:fixed;", 364 | "cps": "caption-side:|;", 365 | "cps:t": "caption-side:top;", 366 | "cps:b": "caption-side:bottom;", 367 | "ec": "empty-cells:|;", 368 | "ec:s": "empty-cells:show;", 369 | "ec:h": "empty-cells:hide;", 370 | "lis": "list-style:|;", 371 | "lis:n": "list-style:none;", 372 | "lisp": "list-style-position:|;", 373 | "lisp:i": "list-style-position:inside;", 374 | "lisp:o": "list-style-position:outside;", 375 | "list": "list-style-type:|;", 376 | "list:n": "list-style-type:none;", 377 | "list:d": "list-style-type:disc;", 378 | "list:c": "list-style-type:circle;", 379 | "list:s": "list-style-type:square;", 380 | "list:dc": "list-style-type:decimal;", 381 | "list:dclz": "list-style-type:decimal-leading-zero;", 382 | "list:lr": "list-style-type:lower-roman;", 383 | "list:ur": "list-style-type:upper-roman;", 384 | "lisi": "list-style-image:|;", 385 | "lisi:n": "list-style-image:none;", 386 | "q": "quotes:|;", 387 | "q:n": "quotes:none;", 388 | "q:ru": "quotes:'\\00AB' '\\00BB' '\\201E' '\\201C';", 389 | "q:en": "quotes:'\\201C' '\\201D' '\\2018' '\\2019';", 390 | "ct": "content:|;", 391 | "ct:n": "content:normal;", 392 | "ct:oq": "content:open-quote;", 393 | "ct:noq": "content:no-open-quote;", 394 | "ct:cq": "content:close-quote;", 395 | "ct:ncq": "content:no-close-quote;", 396 | "ct:a": "content:attr(|);", 397 | "ct:c": "content:counter(|);", 398 | "ct:cs": "content:counters(|);", 399 | "coi": "counter-increment:|;", 400 | "cor": "counter-reset:|;", 401 | "va": "vertical-align:${1:top};", 402 | "va:sup": "vertical-align:super;", 403 | "va:t": "vertical-align:top;", 404 | "va:tt": "vertical-align:text-top;", 405 | "va:m": "vertical-align:middle;", 406 | "va:bl": "vertical-align:baseline;", 407 | "va:b": "vertical-align:bottom;", 408 | "va:tb": "vertical-align:text-bottom;", 409 | "va:sub": "vertical-align:sub;", 410 | "ta": "text-align:${1:left};", 411 | "ta:l": "text-align:left;", 412 | "ta:c": "text-align:center;", 413 | "ta:r": "text-align:right;", 414 | "ta:j": "text-align:justify;", 415 | "ta-lst": "text-align-last:|;", 416 | "tal:a": "text-align-last:auto;", 417 | "tal:l": "text-align-last:left;", 418 | "tal:c": "text-align-last:center;", 419 | "tal:r": "text-align-last:right;", 420 | "td": "text-decoration:${1:none};", 421 | "td:n": "text-decoration:none;", 422 | "td:u": "text-decoration:underline;", 423 | "td:o": "text-decoration:overline;", 424 | "td:l": "text-decoration:line-through;", 425 | "te": "text-emphasis:|;", 426 | "te:n": "text-emphasis:none;", 427 | "te:ac": "text-emphasis:accent;", 428 | "te:dt": "text-emphasis:dot;", 429 | "te:c": "text-emphasis:circle;", 430 | "te:ds": "text-emphasis:disc;", 431 | "te:b": "text-emphasis:before;", 432 | "te:a": "text-emphasis:after;", 433 | "th": "text-height:|;", 434 | "th:a": "text-height:auto;", 435 | "th:f": "text-height:font-size;", 436 | "th:t": "text-height:text-size;", 437 | "th:m": "text-height:max-size;", 438 | "ti": "text-indent:|;", 439 | "ti:-": "text-indent:-9999px;", 440 | "tj": "text-justify:|;", 441 | "tj:a": "text-justify:auto;", 442 | "tj:iw": "text-justify:inter-word;", 443 | "tj:ii": "text-justify:inter-ideograph;", 444 | "tj:ic": "text-justify:inter-cluster;", 445 | "tj:d": "text-justify:distribute;", 446 | "tj:k": "text-justify:kashida;", 447 | "tj:t": "text-justify:tibetan;", 448 | "tov": "text-overflow:${ellipsis};", 449 | "tov:e": "text-overflow:ellipsis;", 450 | "tov:c": "text-overflow:clip;", 451 | "to": "text-outline:|;", 452 | "to+": "text-outline:${1:0} ${2:0} ${3:#000};", 453 | "to:n": "text-outline:none;", 454 | "tr": "text-replace:|;", 455 | "tr:n": "text-replace:none;", 456 | "tt": "text-transform:${1:uppercase};", 457 | "tt:n": "text-transform:none;", 458 | "tt:c": "text-transform:capitalize;", 459 | "tt:u": "text-transform:uppercase;", 460 | "tt:l": "text-transform:lowercase;", 461 | "tw": "text-wrap:|;", 462 | "tw:n": "text-wrap:normal;", 463 | "tw:no": "text-wrap:none;", 464 | "tw:u": "text-wrap:unrestricted;", 465 | "tw:s": "text-wrap:suppress;", 466 | "tsh": "text-shadow:${1:hoff} ${2:voff} ${3:blur} ${4:#000};", 467 | "tsh:r": "text-shadow:${1:h} ${2:v} ${3:blur} rgb(${4:0}, ${5:0}, ${6:0});", 468 | "tsh:ra": "text-shadow:${1:h} ${2:v} ${3:blur} rgba(${4:0}, ${5:0}, ${6:0}, .${7:5});", 469 | "tsh+": "text-shadow:${1:0} ${2:0} ${3:0} ${4:#000};", 470 | "tsh:n": "text-shadow:none;", 471 | "trf": "transform:|;", 472 | "trf:skx": "transform: skewX(${1:angle});", 473 | "trf:sky": "transform: skewY(${1:angle});", 474 | "trf:sc": "transform: scale(${1:x}, ${2:y});", 475 | "trf:scx": "transform: scaleX(${1:x});", 476 | "trf:scy": "transform: scaleY(${1:y});", 477 | "trf:r": "transform: rotate(${1:angle});", 478 | "trf:t": "transform: translate(${1:x}, ${2:y});", 479 | "trf:tx": "transform: translateX(${1:x});", 480 | "trf:ty": "transform: translateY(${1:y});", 481 | "trfo": "transform-origin:|;", 482 | "trfs": "transform-style:${1:preserve-3d};", 483 | "trs": "transition:${1:prop} ${2:time};", 484 | "trsde": "transition-delay:${1:time};", 485 | "trsdu": "transition-duration:${1:time};", 486 | "trsp": "transition-property:${1:prop};", 487 | "trstf": "transition-timing-function:${1:tfunc};", 488 | "lh": "line-height:|;", 489 | "whs": "white-space:|;", 490 | "whs:n": "white-space:normal;", 491 | "whs:p": "white-space:pre;", 492 | "whs:nw": "white-space:nowrap;", 493 | "whs:pw": "white-space:pre-wrap;", 494 | "whs:pl": "white-space:pre-line;", 495 | "whsc": "white-space-collapse:|;", 496 | "whsc:n": "white-space-collapse:normal;", 497 | "whsc:k": "white-space-collapse:keep-all;", 498 | "whsc:l": "white-space-collapse:loose;", 499 | "whsc:bs": "white-space-collapse:break-strict;", 500 | "whsc:ba": "white-space-collapse:break-all;", 501 | "wob": "word-break:|;", 502 | "wob:n": "word-break:normal;", 503 | "wob:k": "word-break:keep-all;", 504 | "wob:ba": "word-break:break-all;", 505 | "wos": "word-spacing:|;", 506 | "wow": "word-wrap:|;", 507 | "wow:nm": "word-wrap:normal;", 508 | "wow:n": "word-wrap:none;", 509 | "wow:u": "word-wrap:unrestricted;", 510 | "wow:s": "word-wrap:suppress;", 511 | "wow:b": "word-wrap:break-word;", 512 | "wm": "writing-mode:${1:lr-tb};", 513 | "wm:lrt": "writing-mode:lr-tb;", 514 | "wm:lrb": "writing-mode:lr-bt;", 515 | "wm:rlt": "writing-mode:rl-tb;", 516 | "wm:rlb": "writing-mode:rl-bt;", 517 | "wm:tbr": "writing-mode:tb-rl;", 518 | "wm:tbl": "writing-mode:tb-lr;", 519 | "wm:btl": "writing-mode:bt-lr;", 520 | "wm:btr": "writing-mode:bt-rl;", 521 | "lts": "letter-spacing:|;", 522 | "lts-n": "letter-spacing:normal;", 523 | "f": "font:|;", 524 | "f+": "font:${1:1em} ${2:Arial,sans-serif};", 525 | "fw": "font-weight:|;", 526 | "fw:n": "font-weight:normal;", 527 | "fw:b": "font-weight:bold;", 528 | "fw:br": "font-weight:bolder;", 529 | "fw:lr": "font-weight:lighter;", 530 | "fs": "font-style:${italic};", 531 | "fs:n": "font-style:normal;", 532 | "fs:i": "font-style:italic;", 533 | "fs:o": "font-style:oblique;", 534 | "fv": "font-variant:|;", 535 | "fv:n": "font-variant:normal;", 536 | "fv:sc": "font-variant:small-caps;", 537 | "fz": "font-size:|;", 538 | "fza": "font-size-adjust:|;", 539 | "fza:n": "font-size-adjust:none;", 540 | "ff": "font-family:|;", 541 | "ff:s": "font-family:serif;", 542 | "ff:ss": "font-family:sans-serif;", 543 | "ff:c": "font-family:cursive;", 544 | "ff:f": "font-family:fantasy;", 545 | "ff:m": "font-family:monospace;", 546 | "ff:a": "font-family: Arial, \"Helvetica Neue\", Helvetica, sans-serif;", 547 | "ff:t": "font-family: \"Times New Roman\", Times, Baskerville, Georgia, serif;", 548 | "ff:v": "font-family: Verdana, Geneva, sans-serif;", 549 | "fef": "font-effect:|;", 550 | "fef:n": "font-effect:none;", 551 | "fef:eg": "font-effect:engrave;", 552 | "fef:eb": "font-effect:emboss;", 553 | "fef:o": "font-effect:outline;", 554 | "fem": "font-emphasize:|;", 555 | "femp": "font-emphasize-position:|;", 556 | "femp:b": "font-emphasize-position:before;", 557 | "femp:a": "font-emphasize-position:after;", 558 | "fems": "font-emphasize-style:|;", 559 | "fems:n": "font-emphasize-style:none;", 560 | "fems:ac": "font-emphasize-style:accent;", 561 | "fems:dt": "font-emphasize-style:dot;", 562 | "fems:c": "font-emphasize-style:circle;", 563 | "fems:ds": "font-emphasize-style:disc;", 564 | "fsm": "font-smooth:|;", 565 | "fsm:a": "font-smooth:auto;", 566 | "fsm:n": "font-smooth:never;", 567 | "fsm:aw": "font-smooth:always;", 568 | "fst": "font-stretch:|;", 569 | "fst:n": "font-stretch:normal;", 570 | "fst:uc": "font-stretch:ultra-condensed;", 571 | "fst:ec": "font-stretch:extra-condensed;", 572 | "fst:c": "font-stretch:condensed;", 573 | "fst:sc": "font-stretch:semi-condensed;", 574 | "fst:se": "font-stretch:semi-expanded;", 575 | "fst:e": "font-stretch:expanded;", 576 | "fst:ee": "font-stretch:extra-expanded;", 577 | "fst:ue": "font-stretch:ultra-expanded;", 578 | "op": "opacity:|;", 579 | "op+": "opacity: $1;\nfilter: alpha(opacity=$2);", 580 | "op:ie": "filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100);", 581 | "op:ms": "-ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)';", 582 | "rsz": "resize:|;", 583 | "rsz:n": "resize:none;", 584 | "rsz:b": "resize:both;", 585 | "rsz:h": "resize:horizontal;", 586 | "rsz:v": "resize:vertical;", 587 | "cur": "cursor:${pointer};", 588 | "cur:a": "cursor:auto;", 589 | "cur:d": "cursor:default;", 590 | "cur:c": "cursor:crosshair;", 591 | "cur:ha": "cursor:hand;", 592 | "cur:he": "cursor:help;", 593 | "cur:m": "cursor:move;", 594 | "cur:p": "cursor:pointer;", 595 | "cur:t": "cursor:text;", 596 | "pgbb": "page-break-before:|;", 597 | "pgbb:au": "page-break-before:auto;", 598 | "pgbb:al": "page-break-before:always;", 599 | "pgbb:l": "page-break-before:left;", 600 | "pgbb:r": "page-break-before:right;", 601 | "pgbi": "page-break-inside:|;", 602 | "pgbi:au": "page-break-inside:auto;", 603 | "pgbi:av": "page-break-inside:avoid;", 604 | "pgba": "page-break-after:|;", 605 | "pgba:au": "page-break-after:auto;", 606 | "pgba:al": "page-break-after:always;", 607 | "pgba:l": "page-break-after:left;", 608 | "pgba:r": "page-break-after:right;", 609 | "orp": "orphans:|;", 610 | "us": "user-select:${none};", 611 | "wid": "widows:|;", 612 | "wfsm": "-webkit-font-smoothing:${antialiased};", 613 | "wfsm:a": "-webkit-font-smoothing:antialiased;", 614 | "wfsm:s": "-webkit-font-smoothing:subpixel-antialiased;", 615 | "wfsm:sa": "-webkit-font-smoothing:subpixel-antialiased;", 616 | "wfsm:n": "-webkit-font-smoothing:none;" 617 | } 618 | }, 619 | 620 | "html": { 621 | "filters": "html", 622 | "profile": "html", 623 | "snippets": { 624 | "!!!": "", 625 | "!!!4t": "", 626 | "!!!4s": "", 627 | "!!!xt": "", 628 | "!!!xs": "", 629 | "!!!xxs": "", 630 | 631 | "c": "", 632 | "cc:ie6": "", 633 | "cc:ie": "", 634 | "cc:noie": "\n\t${child}|\n" 635 | }, 636 | 637 | "abbreviations": { 638 | "!": "html:5", 639 | "a": "", 640 | "a:link": "", 641 | "a:mail": "", 642 | "abbr": "", 643 | "acronym": "", 644 | "base": "", 645 | "basefont": "", 646 | "br": "
", 647 | "frame": "", 648 | "hr": "
", 649 | "bdo": "", 650 | "bdo:r": "", 651 | "bdo:l": "", 652 | "col": "", 653 | "link": "", 654 | "link:css": "", 655 | "link:print": "", 656 | "link:favicon": "", 657 | "link:touch": "", 658 | "link:rss": "", 659 | "link:atom": "", 660 | "meta": "", 661 | "meta:utf": "", 662 | "meta:win": "", 663 | "meta:vp": "", 664 | "meta:compat": "", 665 | "style": "