├── .gitignore
├── CONTRIBUTING.md
├── Default (Linux).sublime-keymap
├── Default (OSX).sublime-keymap
├── Default (Windows).sublime-keymap
├── Default.sublime-commands
├── Emmet.sublime-settings
├── Emmet.tmLanguage
├── LICENSE
├── Main.sublime-menu
├── Preferences.sublime-settings
├── README.md
├── editor.js
├── emmet-plugin.py
├── emmet
├── __init__.py
├── bootstrap.js
├── caniuse.json
├── context.py
├── emmet-app.js
├── file.py
├── python-wrapper.js
├── pyv8loader.py
├── reloader.py
├── semver.py
└── snippets.json
├── emmet_completions
├── __init__.py
├── meta.py
└── trackers.py
├── messages.json
├── messages
├── 1.0.1.txt
├── install.txt
└── official1.0.txt
└── misc
└── generate-keymap.py
/.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
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | This is a Sublime Text plugin repo so post here only ST-related issues. If you found a bug in how Emmet works (for example, invalid result after expanding abbreviation) or have a proposal for a new features, please post them on core [Emmet repo](https://github.com/emmetio/emmet).
2 |
3 | ## About keyboard shortcuts
4 |
5 | A lot of people complain about Emmet shortcuts overriding some default ST actions or actions from other plugins.
6 |
7 | As described in [README](README.md), it’s nearly impossible to provide shortcuts that will not override anything and will be convenient for everyone. So default Emmet shortcuts are ones that I’m personally happy with. If you don’t like them, please spend 2 minutes for tweaking shortcuts, as [described in README](README.md#overriding-keyboard-shortcuts).
8 |
9 | All issues about shortcuts will be rejected. If you have a better shortcut for Emmet actions, you should create a Pull Request with updated `*.sublime-keymap` and `misc/generate-keymap.py` files. Note that the last one is used to generate all `*.sublime-keymap` files.
--------------------------------------------------------------------------------
/Default (Linux).sublime-keymap:
--------------------------------------------------------------------------------
1 | // This file is automatically generated with misc/generate-keymap.py script
2 |
3 | [
4 | {
5 | "keys": [
6 | "shift+ctrl+;"
7 | ],
8 | "args": {
9 | "action": "remove_tag"
10 | },
11 | "command": "run_emmet_action",
12 | "context": [
13 | {
14 | "key": "emmet_action_enabled.remove_tag"
15 | }
16 | ]
17 | },
18 | {
19 | "keys": [
20 | "ctrl+up"
21 | ],
22 | "args": {
23 | "action": "increment_number_by_1"
24 | },
25 | "command": "run_emmet_action",
26 | "context": [
27 | {
28 | "key": "emmet_action_enabled.increment_number_by_1"
29 | }
30 | ]
31 | },
32 | {
33 | "keys": [
34 | "ctrl+shift+forward_slash"
35 | ],
36 | "args": {
37 | "action": "toggle_comment"
38 | },
39 | "command": "run_emmet_action",
40 | "context": [
41 | {
42 | "operand": "source.css, source.less, source.scss, source.postcss, text.xml, text.html - source",
43 | "operator": "equal",
44 | "match_all": true,
45 | "key": "selector"
46 | },
47 | {
48 | "match_all": true,
49 | "key": "emmet_action_enabled.toggle_comment"
50 | }
51 | ]
52 | },
53 | {
54 | "keys": [
55 | "shift+ctrl+."
56 | ],
57 | "args": {
58 | "action": "select_next_item"
59 | },
60 | "command": "run_emmet_action",
61 | "context": [
62 | {
63 | "key": "emmet_action_enabled.select_next_item"
64 | }
65 | ]
66 | },
67 | {
68 | "keys": [
69 | "ctrl+alt+enter"
70 | ],
71 | "command": "expand_as_you_type",
72 | "context": [
73 | {
74 | "operand": false,
75 | "operator": "equal",
76 | "match_all": true,
77 | "key": "setting.is_widget"
78 | },
79 | {
80 | "match_all": true,
81 | "key": "emmet_action_enabled.expand_as_you_type"
82 | }
83 | ]
84 | },
85 | {
86 | "keys": [
87 | "alt+down"
88 | ],
89 | "args": {
90 | "action": "decrement_number_by_01"
91 | },
92 | "command": "run_emmet_action",
93 | "context": [
94 | {
95 | "key": "emmet_action_enabled.decrement_number_by_01"
96 | }
97 | ]
98 | },
99 | {
100 | "keys": [
101 | "ctrl+'"
102 | ],
103 | "args": {
104 | "action": "encode_decode_data_url"
105 | },
106 | "command": "run_emmet_action",
107 | "context": [
108 | {
109 | "key": "emmet_action_enabled.encode_decode_data_url"
110 | }
111 | ]
112 | },
113 | {
114 | "keys": [
115 | "shift+ctrl+y"
116 | ],
117 | "args": {
118 | "action": "evaluate_math_expression"
119 | },
120 | "command": "run_emmet_action",
121 | "context": [
122 | {
123 | "key": "emmet_action_enabled.evaluate_math_expression"
124 | }
125 | ]
126 | },
127 | {
128 | "keys": [
129 | "ctrl+shift+0"
130 | ],
131 | "args": {
132 | "action": "balance_inward"
133 | },
134 | "command": "run_emmet_action",
135 | "context": [
136 | {
137 | "key": "emmet_action_enabled.balance_inward"
138 | }
139 | ]
140 | },
141 | {
142 | "keys": [
143 | "ctrl+,"
144 | ],
145 | "args": {
146 | "action": "balance_outward"
147 | },
148 | "command": "run_emmet_action",
149 | "context": [
150 | {
151 | "key": "emmet_action_enabled.balance_outward"
152 | }
153 | ]
154 | },
155 | {
156 | "keys": [
157 | "shift+alt+up"
158 | ],
159 | "args": {
160 | "action": "increment_number_by_10"
161 | },
162 | "command": "run_emmet_action",
163 | "context": [
164 | {
165 | "key": "emmet_action_enabled.increment_number_by_10"
166 | }
167 | ]
168 | },
169 | {
170 | "keys": [
171 | "shift+ctrl+r"
172 | ],
173 | "args": {
174 | "action": "reflect_css_value"
175 | },
176 | "command": "run_emmet_action",
177 | "context": [
178 | {
179 | "key": "emmet_action_enabled.reflect_css_value"
180 | }
181 | ]
182 | },
183 | {
184 | "keys": [
185 | "ctrl+alt+left"
186 | ],
187 | "args": {
188 | "action": "prev_edit_point"
189 | },
190 | "command": "run_emmet_action",
191 | "context": [
192 | {
193 | "key": "emmet_action_enabled.prev_edit_point"
194 | }
195 | ]
196 | },
197 | {
198 | "keys": [
199 | "shift+ctrl+,"
200 | ],
201 | "args": {
202 | "action": "select_previous_item"
203 | },
204 | "command": "run_emmet_action",
205 | "context": [
206 | {
207 | "key": "emmet_action_enabled.select_previous_item"
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+shift+u"
313 | ],
314 | "command": "update_as_you_type",
315 | "context": [
316 | {
317 | "operand": false,
318 | "operator": "equal",
319 | "match_all": true,
320 | "key": "setting.is_widget"
321 | },
322 | {
323 | "match_all": true,
324 | "key": "emmet_action_enabled.update_as_you_type"
325 | }
326 | ]
327 | },
328 | {
329 | "keys": [
330 | "ctrl+alt+j"
331 | ],
332 | "args": {
333 | "action": "matching_pair"
334 | },
335 | "command": "run_emmet_action",
336 | "context": [
337 | {
338 | "key": "emmet_action_enabled.matching_pair"
339 | }
340 | ]
341 | },
342 | {
343 | "keys": [
344 | "ctrl+down"
345 | ],
346 | "args": {
347 | "action": "decrement_number_by_1"
348 | },
349 | "command": "run_emmet_action",
350 | "context": [
351 | {
352 | "key": "emmet_action_enabled.decrement_number_by_1"
353 | }
354 | ]
355 | },
356 | {
357 | "keys": [
358 | "ctrl+e"
359 | ],
360 | "args": {
361 | "action": "expand_abbreviation"
362 | },
363 | "command": "run_emmet_action",
364 | "context": [
365 | {
366 | "key": "emmet_action_enabled.expand_abbreviation"
367 | }
368 | ]
369 | },
370 | {
371 | "keys": [
372 | "tab"
373 | ],
374 | "command": "expand_abbreviation_by_tab",
375 | "context": [
376 | {
377 | "operand": "source.css, source.sass, source.less, source.scss, source.stylus, source.postcss, source.jade, text.jade, source.pug, text.pug, text.slim, text.xml, text.html - source, text.haml, text.scala.html, source string",
378 | "operator": "equal",
379 | "match_all": true,
380 | "key": "selector"
381 | },
382 | {
383 | "operand": "storage.type.templatetag.django",
384 | "operator": "not_equal",
385 | "match_all": true,
386 | "key": "selector"
387 | },
388 | {
389 | "match_all": true,
390 | "key": "selection_empty"
391 | },
392 | {
393 | "operator": "equal",
394 | "operand": false,
395 | "match_all": true,
396 | "key": "has_next_field"
397 | },
398 | {
399 | "operator": "equal",
400 | "operand": false,
401 | "match_all": true,
402 | "key": "setting.disable_tab_abbreviations"
403 | },
404 | {
405 | "operand": false,
406 | "operator": "equal",
407 | "match_all": true,
408 | "key": "auto_complete_visible"
409 | },
410 | {
411 | "match_all": true,
412 | "key": "is_abbreviation"
413 | }
414 | ]
415 | },
416 | {
417 | "keys": [
418 | "tab"
419 | ],
420 | "command": "expand_abbreviation_by_tab",
421 | "context": [
422 | {
423 | "operand": "source.css, source.sass, source.less, source.scss, source.stylus, source.postcss, source.jade, text.jade, text.slim, text.xml, text.html - source, text.haml, text.scala.html, source string",
424 | "operator": "equal",
425 | "match_all": true,
426 | "key": "selector"
427 | },
428 | {
429 | "operand": "storage.type.templatetag.django",
430 | "operator": "not_equal",
431 | "match_all": true,
432 | "key": "selector"
433 | },
434 | {
435 | "match_all": true,
436 | "key": "selection_empty"
437 | },
438 | {
439 | "operator": "equal",
440 | "operand": false,
441 | "match_all": true,
442 | "key": "has_next_field"
443 | },
444 | {
445 | "operator": "equal",
446 | "operand": true,
447 | "match_all": true,
448 | "key": "auto_complete_visible"
449 | },
450 | {
451 | "operator": "equal",
452 | "operand": false,
453 | "match_all": true,
454 | "key": "setting.disable_tab_abbreviations_on_auto_complete"
455 | },
456 | {
457 | "match_all": true,
458 | "key": "is_abbreviation"
459 | }
460 | ]
461 | },
462 | {
463 | "keys": [
464 | "enter"
465 | ],
466 | "args": {
467 | "contents": "\n\t${0}\n"
468 | },
469 | "command": "insert_snippet",
470 | "context": [
471 | {
472 | "operand": "meta.scope.between-tag-pair.html, meta.scope.between-tag-pair.xml",
473 | "match_all": true,
474 | "key": "selector"
475 | },
476 | {
477 | "operand": false,
478 | "match_all": true,
479 | "key": "auto_complete_visible"
480 | },
481 | {
482 | "match_all": true,
483 | "key": "clear_fields_on_enter_key"
484 | },
485 | {
486 | "operand": false,
487 | "match_all": true,
488 | "key": "setting.disable_formatted_linebreak"
489 | }
490 | ]
491 | },
492 | {
493 | "keys": [
494 | "#"
495 | ],
496 | "args": {
497 | "attribute": "id"
498 | },
499 | "command": "emmet_insert_attribute",
500 | "context": [
501 | {
502 | "operand": "text.html meta.tag -string -punctuation.definition.tag.begin.html -meta.scope.between-tag-pair.html -source -meta.tag.template.value.twig",
503 | "operator": "equal",
504 | "match_all": true,
505 | "key": "selector"
506 | },
507 | {
508 | "operator": "equal",
509 | "operand": true,
510 | "key": "setting.auto_id_class"
511 | }
512 | ]
513 | },
514 | {
515 | "keys": [
516 | "."
517 | ],
518 | "args": {
519 | "attribute": "class"
520 | },
521 | "command": "emmet_insert_attribute",
522 | "context": [
523 | {
524 | "operand": "text.html meta.tag -string -punctuation.definition.tag.begin.html -meta.scope.between-tag-pair.html -source -meta.tag.template.value.twig",
525 | "operator": "equal",
526 | "match_all": true,
527 | "key": "selector"
528 | },
529 | {
530 | "operator": "equal",
531 | "operand": true,
532 | "key": "setting.auto_id_class"
533 | }
534 | ]
535 | }
536 | ]
--------------------------------------------------------------------------------
/Default (OSX).sublime-keymap:
--------------------------------------------------------------------------------
1 | // This file is automatically generated with misc/generate-keymap.py script
2 |
3 | [
4 | {
5 | "keys": [
6 | "super+'"
7 | ],
8 | "args": {
9 | "action": "remove_tag"
10 | },
11 | "command": "run_emmet_action",
12 | "context": [
13 | {
14 | "key": "emmet_action_enabled.remove_tag"
15 | }
16 | ]
17 | },
18 | {
19 | "keys": [
20 | "ctrl+up"
21 | ],
22 | "args": {
23 | "action": "increment_number_by_1"
24 | },
25 | "command": "run_emmet_action",
26 | "context": [
27 | {
28 | "key": "emmet_action_enabled.increment_number_by_1"
29 | }
30 | ]
31 | },
32 | {
33 | "keys": [
34 | "alt+shift+forward_slash"
35 | ],
36 | "args": {
37 | "action": "toggle_comment"
38 | },
39 | "command": "run_emmet_action",
40 | "context": [
41 | {
42 | "operand": "source.css, source.less, source.scss, source.postcss, text.xml, text.html - source",
43 | "operator": "equal",
44 | "match_all": true,
45 | "key": "selector"
46 | },
47 | {
48 | "match_all": true,
49 | "key": "emmet_action_enabled.toggle_comment"
50 | }
51 | ]
52 | },
53 | {
54 | "keys": [
55 | "shift+super+."
56 | ],
57 | "args": {
58 | "action": "select_next_item"
59 | },
60 | "command": "run_emmet_action",
61 | "context": [
62 | {
63 | "key": "emmet_action_enabled.select_next_item"
64 | }
65 | ]
66 | },
67 | {
68 | "keys": [
69 | "ctrl+alt+enter"
70 | ],
71 | "command": "expand_as_you_type",
72 | "context": [
73 | {
74 | "operand": false,
75 | "operator": "equal",
76 | "match_all": true,
77 | "key": "setting.is_widget"
78 | },
79 | {
80 | "match_all": true,
81 | "key": "emmet_action_enabled.expand_as_you_type"
82 | }
83 | ]
84 | },
85 | {
86 | "keys": [
87 | "alt+down"
88 | ],
89 | "args": {
90 | "action": "decrement_number_by_01"
91 | },
92 | "command": "run_emmet_action",
93 | "context": [
94 | {
95 | "key": "emmet_action_enabled.decrement_number_by_01"
96 | }
97 | ]
98 | },
99 | {
100 | "keys": [
101 | "shift+ctrl+d"
102 | ],
103 | "args": {
104 | "action": "encode_decode_data_url"
105 | },
106 | "command": "run_emmet_action",
107 | "context": [
108 | {
109 | "key": "emmet_action_enabled.encode_decode_data_url"
110 | }
111 | ]
112 | },
113 | {
114 | "keys": [
115 | "shift+super+y"
116 | ],
117 | "args": {
118 | "action": "evaluate_math_expression"
119 | },
120 | "command": "run_emmet_action",
121 | "context": [
122 | {
123 | "key": "emmet_action_enabled.evaluate_math_expression"
124 | }
125 | ]
126 | },
127 | {
128 | "keys": [
129 | "ctrl+j"
130 | ],
131 | "args": {
132 | "action": "balance_inward"
133 | },
134 | "command": "run_emmet_action",
135 | "context": [
136 | {
137 | "key": "emmet_action_enabled.balance_inward"
138 | }
139 | ]
140 | },
141 | {
142 | "keys": [
143 | "ctrl+d"
144 | ],
145 | "args": {
146 | "action": "balance_outward"
147 | },
148 | "command": "run_emmet_action",
149 | "context": [
150 | {
151 | "key": "emmet_action_enabled.balance_outward"
152 | }
153 | ]
154 | },
155 | {
156 | "keys": [
157 | "alt+super+up"
158 | ],
159 | "args": {
160 | "action": "increment_number_by_10"
161 | },
162 | "command": "run_emmet_action",
163 | "context": [
164 | {
165 | "key": "emmet_action_enabled.increment_number_by_10"
166 | }
167 | ]
168 | },
169 | {
170 | "keys": [
171 | "shift+super+r"
172 | ],
173 | "args": {
174 | "action": "reflect_css_value"
175 | },
176 | "command": "run_emmet_action",
177 | "context": [
178 | {
179 | "key": "emmet_action_enabled.reflect_css_value"
180 | }
181 | ]
182 | },
183 | {
184 | "keys": [
185 | "ctrl+alt+left"
186 | ],
187 | "args": {
188 | "action": "prev_edit_point"
189 | },
190 | "command": "run_emmet_action",
191 | "context": [
192 | {
193 | "key": "emmet_action_enabled.prev_edit_point"
194 | }
195 | ]
196 | },
197 | {
198 | "keys": [
199 | "shift+super+,"
200 | ],
201 | "args": {
202 | "action": "select_previous_item"
203 | },
204 | "command": "run_emmet_action",
205 | "context": [
206 | {
207 | "key": "emmet_action_enabled.select_previous_item"
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+u"
313 | ],
314 | "command": "update_as_you_type",
315 | "context": [
316 | {
317 | "operand": false,
318 | "operator": "equal",
319 | "match_all": true,
320 | "key": "setting.is_widget"
321 | },
322 | {
323 | "match_all": true,
324 | "key": "emmet_action_enabled.update_as_you_type"
325 | }
326 | ]
327 | },
328 | {
329 | "keys": [
330 | "ctrl+shift+t"
331 | ],
332 | "args": {
333 | "action": "matching_pair"
334 | },
335 | "command": "run_emmet_action",
336 | "context": [
337 | {
338 | "key": "emmet_action_enabled.matching_pair"
339 | }
340 | ]
341 | },
342 | {
343 | "keys": [
344 | "ctrl+down"
345 | ],
346 | "args": {
347 | "action": "decrement_number_by_1"
348 | },
349 | "command": "run_emmet_action",
350 | "context": [
351 | {
352 | "key": "emmet_action_enabled.decrement_number_by_1"
353 | }
354 | ]
355 | },
356 | {
357 | "keys": [
358 | "ctrl+e"
359 | ],
360 | "args": {
361 | "action": "expand_abbreviation"
362 | },
363 | "command": "run_emmet_action",
364 | "context": [
365 | {
366 | "key": "emmet_action_enabled.expand_abbreviation"
367 | }
368 | ]
369 | },
370 | {
371 | "keys": [
372 | "tab"
373 | ],
374 | "command": "expand_abbreviation_by_tab",
375 | "context": [
376 | {
377 | "operand": "source.css, source.sass, source.less, source.scss, source.stylus, source.postcss, source.jade, text.jade, source.pug, text.pug, text.slim, text.xml, text.html - source, text.haml, text.scala.html, source string",
378 | "operator": "equal",
379 | "match_all": true,
380 | "key": "selector"
381 | },
382 | {
383 | "operand": "storage.type.templatetag.django",
384 | "operator": "not_equal",
385 | "match_all": true,
386 | "key": "selector"
387 | },
388 | {
389 | "match_all": true,
390 | "key": "selection_empty"
391 | },
392 | {
393 | "operator": "equal",
394 | "operand": false,
395 | "match_all": true,
396 | "key": "has_next_field"
397 | },
398 | {
399 | "operator": "equal",
400 | "operand": false,
401 | "match_all": true,
402 | "key": "setting.disable_tab_abbreviations"
403 | },
404 | {
405 | "operand": false,
406 | "operator": "equal",
407 | "match_all": true,
408 | "key": "auto_complete_visible"
409 | },
410 | {
411 | "match_all": true,
412 | "key": "is_abbreviation"
413 | }
414 | ]
415 | },
416 | {
417 | "keys": [
418 | "tab"
419 | ],
420 | "command": "expand_abbreviation_by_tab",
421 | "context": [
422 | {
423 | "operand": "source.css, source.sass, source.less, source.scss, source.stylus, source.postcss, source.jade, text.jade, source.pug, text.pug, text.slim, text.xml, text.html - source, text.haml, text.scala.html, source string",
424 | "operator": "equal",
425 | "match_all": true,
426 | "key": "selector"
427 | },
428 | {
429 | "operand": "storage.type.templatetag.django",
430 | "operator": "not_equal",
431 | "match_all": true,
432 | "key": "selector"
433 | },
434 | {
435 | "match_all": true,
436 | "key": "selection_empty"
437 | },
438 | {
439 | "operator": "equal",
440 | "operand": false,
441 | "match_all": true,
442 | "key": "has_next_field"
443 | },
444 | {
445 | "operator": "equal",
446 | "operand": true,
447 | "match_all": true,
448 | "key": "auto_complete_visible"
449 | },
450 | {
451 | "operator": "equal",
452 | "operand": false,
453 | "match_all": true,
454 | "key": "setting.disable_tab_abbreviations_on_auto_complete"
455 | },
456 | {
457 | "match_all": true,
458 | "key": "is_abbreviation"
459 | }
460 | ]
461 | },
462 | {
463 | "keys": [
464 | "enter"
465 | ],
466 | "args": {
467 | "contents": "\n\t${0}\n"
468 | },
469 | "command": "insert_snippet",
470 | "context": [
471 | {
472 | "operand": "meta.scope.between-tag-pair.html, meta.scope.between-tag-pair.xml",
473 | "match_all": true,
474 | "key": "selector"
475 | },
476 | {
477 | "operand": false,
478 | "match_all": true,
479 | "key": "auto_complete_visible"
480 | },
481 | {
482 | "match_all": true,
483 | "key": "clear_fields_on_enter_key"
484 | },
485 | {
486 | "operand": false,
487 | "match_all": true,
488 | "key": "setting.disable_formatted_linebreak"
489 | }
490 | ]
491 | },
492 | {
493 | "keys": [
494 | "#"
495 | ],
496 | "args": {
497 | "attribute": "id"
498 | },
499 | "command": "emmet_insert_attribute",
500 | "context": [
501 | {
502 | "operand": "text.html meta.tag -string -punctuation.definition.tag.begin.html -meta.scope.between-tag-pair.html -source -meta.tag.template.value.twig",
503 | "operator": "equal",
504 | "match_all": true,
505 | "key": "selector"
506 | },
507 | {
508 | "operator": "equal",
509 | "operand": true,
510 | "key": "setting.auto_id_class"
511 | }
512 | ]
513 | },
514 | {
515 | "keys": [
516 | "."
517 | ],
518 | "args": {
519 | "attribute": "class"
520 | },
521 | "command": "emmet_insert_attribute",
522 | "context": [
523 | {
524 | "operand": "text.html meta.tag -string -punctuation.definition.tag.begin.html -meta.scope.between-tag-pair.html -source -meta.tag.template.value.twig",
525 | "operator": "equal",
526 | "match_all": true,
527 | "key": "selector"
528 | },
529 | {
530 | "operator": "equal",
531 | "operand": true,
532 | "key": "setting.auto_id_class"
533 | }
534 | ]
535 | }
536 | ]
--------------------------------------------------------------------------------
/Default (Windows).sublime-keymap:
--------------------------------------------------------------------------------
1 | // This file is automatically generated with misc/generate-keymap.py script
2 |
3 | [
4 | {
5 | "keys": [
6 | "shift+ctrl+;"
7 | ],
8 | "args": {
9 | "action": "remove_tag"
10 | },
11 | "command": "run_emmet_action",
12 | "context": [
13 | {
14 | "key": "emmet_action_enabled.remove_tag"
15 | }
16 | ]
17 | },
18 | {
19 | "keys": [
20 | "ctrl+up"
21 | ],
22 | "args": {
23 | "action": "increment_number_by_1"
24 | },
25 | "command": "run_emmet_action",
26 | "context": [
27 | {
28 | "key": "emmet_action_enabled.increment_number_by_1"
29 | }
30 | ]
31 | },
32 | {
33 | "keys": [
34 | "ctrl+shift+forward_slash"
35 | ],
36 | "args": {
37 | "action": "toggle_comment"
38 | },
39 | "command": "run_emmet_action",
40 | "context": [
41 | {
42 | "operand": "source.css, source.less, source.scss, source.postcss, text.xml, text.html - source",
43 | "operator": "equal",
44 | "match_all": true,
45 | "key": "selector"
46 | },
47 | {
48 | "match_all": true,
49 | "key": "emmet_action_enabled.toggle_comment"
50 | }
51 | ]
52 | },
53 | {
54 | "keys": [
55 | "shift+ctrl+."
56 | ],
57 | "args": {
58 | "action": "select_next_item"
59 | },
60 | "command": "run_emmet_action",
61 | "context": [
62 | {
63 | "key": "emmet_action_enabled.select_next_item"
64 | }
65 | ]
66 | },
67 | {
68 | "keys": [
69 | "ctrl+alt+enter"
70 | ],
71 | "command": "expand_as_you_type",
72 | "context": [
73 | {
74 | "operand": false,
75 | "operator": "equal",
76 | "match_all": true,
77 | "key": "setting.is_widget"
78 | },
79 | {
80 | "match_all": true,
81 | "key": "emmet_action_enabled.expand_as_you_type"
82 | }
83 | ]
84 | },
85 | {
86 | "keys": [
87 | "alt+down"
88 | ],
89 | "args": {
90 | "action": "decrement_number_by_01"
91 | },
92 | "command": "run_emmet_action",
93 | "context": [
94 | {
95 | "key": "emmet_action_enabled.decrement_number_by_01"
96 | }
97 | ]
98 | },
99 | {
100 | "keys": [
101 | "ctrl+'"
102 | ],
103 | "args": {
104 | "action": "encode_decode_data_url"
105 | },
106 | "command": "run_emmet_action",
107 | "context": [
108 | {
109 | "key": "emmet_action_enabled.encode_decode_data_url"
110 | }
111 | ]
112 | },
113 | {
114 | "keys": [
115 | "shift+ctrl+y"
116 | ],
117 | "args": {
118 | "action": "evaluate_math_expression"
119 | },
120 | "command": "run_emmet_action",
121 | "context": [
122 | {
123 | "key": "emmet_action_enabled.evaluate_math_expression"
124 | }
125 | ]
126 | },
127 | {
128 | "keys": [
129 | "ctrl+shift+0"
130 | ],
131 | "args": {
132 | "action": "balance_inward"
133 | },
134 | "command": "run_emmet_action",
135 | "context": [
136 | {
137 | "key": "emmet_action_enabled.balance_inward"
138 | }
139 | ]
140 | },
141 | {
142 | "keys": [
143 | "ctrl+,"
144 | ],
145 | "args": {
146 | "action": "balance_outward"
147 | },
148 | "command": "run_emmet_action",
149 | "context": [
150 | {
151 | "key": "emmet_action_enabled.balance_outward"
152 | }
153 | ]
154 | },
155 | {
156 | "keys": [
157 | "shift+alt+up"
158 | ],
159 | "args": {
160 | "action": "increment_number_by_10"
161 | },
162 | "command": "run_emmet_action",
163 | "context": [
164 | {
165 | "key": "emmet_action_enabled.increment_number_by_10"
166 | }
167 | ]
168 | },
169 | {
170 | "keys": [
171 | "shift+ctrl+r"
172 | ],
173 | "args": {
174 | "action": "reflect_css_value"
175 | },
176 | "command": "run_emmet_action",
177 | "context": [
178 | {
179 | "key": "emmet_action_enabled.reflect_css_value"
180 | }
181 | ]
182 | },
183 | {
184 | "keys": [
185 | "ctrl+alt+left"
186 | ],
187 | "args": {
188 | "action": "prev_edit_point"
189 | },
190 | "command": "run_emmet_action",
191 | "context": [
192 | {
193 | "key": "emmet_action_enabled.prev_edit_point"
194 | }
195 | ]
196 | },
197 | {
198 | "keys": [
199 | "shift+ctrl+,"
200 | ],
201 | "args": {
202 | "action": "select_previous_item"
203 | },
204 | "command": "run_emmet_action",
205 | "context": [
206 | {
207 | "key": "emmet_action_enabled.select_previous_item"
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+shift+u"
313 | ],
314 | "command": "update_as_you_type",
315 | "context": [
316 | {
317 | "operand": false,
318 | "operator": "equal",
319 | "match_all": true,
320 | "key": "setting.is_widget"
321 | },
322 | {
323 | "match_all": true,
324 | "key": "emmet_action_enabled.update_as_you_type"
325 | }
326 | ]
327 | },
328 | {
329 | "keys": [
330 | "ctrl+alt+j"
331 | ],
332 | "args": {
333 | "action": "matching_pair"
334 | },
335 | "command": "run_emmet_action",
336 | "context": [
337 | {
338 | "key": "emmet_action_enabled.matching_pair"
339 | }
340 | ]
341 | },
342 | {
343 | "keys": [
344 | "ctrl+down"
345 | ],
346 | "args": {
347 | "action": "decrement_number_by_1"
348 | },
349 | "command": "run_emmet_action",
350 | "context": [
351 | {
352 | "key": "emmet_action_enabled.decrement_number_by_1"
353 | }
354 | ]
355 | },
356 | {
357 | "keys": [
358 | "ctrl+e"
359 | ],
360 | "args": {
361 | "action": "expand_abbreviation"
362 | },
363 | "command": "run_emmet_action",
364 | "context": [
365 | {
366 | "key": "emmet_action_enabled.expand_abbreviation"
367 | }
368 | ]
369 | },
370 | {
371 | "keys": [
372 | "tab"
373 | ],
374 | "command": "expand_abbreviation_by_tab",
375 | "context": [
376 | {
377 | "operand": "source.css, source.sass, source.less, source.scss, source.stylus, source.postcss, source.jade, text.jade, source.pug, text.pug, text.slim, text.xml, text.html - source, text.haml, text.scala.html, source string",
378 | "operator": "equal",
379 | "match_all": true,
380 | "key": "selector"
381 | },
382 | {
383 | "operand": "storage.type.templatetag.django",
384 | "operator": "not_equal",
385 | "match_all": true,
386 | "key": "selector"
387 | },
388 | {
389 | "match_all": true,
390 | "key": "selection_empty"
391 | },
392 | {
393 | "operator": "equal",
394 | "operand": false,
395 | "match_all": true,
396 | "key": "has_next_field"
397 | },
398 | {
399 | "operator": "equal",
400 | "operand": false,
401 | "match_all": true,
402 | "key": "setting.disable_tab_abbreviations"
403 | },
404 | {
405 | "operand": false,
406 | "operator": "equal",
407 | "match_all": true,
408 | "key": "auto_complete_visible"
409 | },
410 | {
411 | "match_all": true,
412 | "key": "is_abbreviation"
413 | }
414 | ]
415 | },
416 | {
417 | "keys": [
418 | "tab"
419 | ],
420 | "command": "expand_abbreviation_by_tab",
421 | "context": [
422 | {
423 | "operand": "source.css, source.sass, source.less, source.scss, source.stylus, source.postcss, source.jade, text.jade, source.pug, text.pug, text.slim, text.xml, text.html - source, text.haml, text.scala.html, source string",
424 | "operator": "equal",
425 | "match_all": true,
426 | "key": "selector"
427 | },
428 | {
429 | "operand": "storage.type.templatetag.django",
430 | "operator": "not_equal",
431 | "match_all": true,
432 | "key": "selector"
433 | },
434 | {
435 | "match_all": true,
436 | "key": "selection_empty"
437 | },
438 | {
439 | "operator": "equal",
440 | "operand": false,
441 | "match_all": true,
442 | "key": "has_next_field"
443 | },
444 | {
445 | "operator": "equal",
446 | "operand": true,
447 | "match_all": true,
448 | "key": "auto_complete_visible"
449 | },
450 | {
451 | "operator": "equal",
452 | "operand": false,
453 | "match_all": true,
454 | "key": "setting.disable_tab_abbreviations_on_auto_complete"
455 | },
456 | {
457 | "match_all": true,
458 | "key": "is_abbreviation"
459 | }
460 | ]
461 | },
462 | {
463 | "keys": [
464 | "enter"
465 | ],
466 | "args": {
467 | "contents": "\n\t${0}\n"
468 | },
469 | "command": "insert_snippet",
470 | "context": [
471 | {
472 | "operand": "meta.scope.between-tag-pair.html, meta.scope.between-tag-pair.xml",
473 | "match_all": true,
474 | "key": "selector"
475 | },
476 | {
477 | "operand": false,
478 | "match_all": true,
479 | "key": "auto_complete_visible"
480 | },
481 | {
482 | "match_all": true,
483 | "key": "clear_fields_on_enter_key"
484 | },
485 | {
486 | "operand": false,
487 | "match_all": true,
488 | "key": "setting.disable_formatted_linebreak"
489 | }
490 | ]
491 | },
492 | {
493 | "keys": [
494 | "#"
495 | ],
496 | "args": {
497 | "attribute": "id"
498 | },
499 | "command": "emmet_insert_attribute",
500 | "context": [
501 | {
502 | "operand": "text.html meta.tag -string -punctuation.definition.tag.begin.html -meta.scope.between-tag-pair.html -source -meta.tag.template.value.twig",
503 | "operator": "equal",
504 | "match_all": true,
505 | "key": "selector"
506 | },
507 | {
508 | "operator": "equal",
509 | "operand": true,
510 | "key": "setting.auto_id_class"
511 | }
512 | ]
513 | },
514 | {
515 | "keys": [
516 | "."
517 | ],
518 | "args": {
519 | "attribute": "class"
520 | },
521 | "command": "emmet_insert_attribute",
522 | "context": [
523 | {
524 | "operand": "text.html meta.tag -string -punctuation.definition.tag.begin.html -meta.scope.between-tag-pair.html -source -meta.tag.template.value.twig",
525 | "operator": "equal",
526 | "match_all": true,
527 | "key": "selector"
528 | },
529 | {
530 | "operator": "equal",
531 | "operand": true,
532 | "key": "setting.auto_id_class"
533 | }
534 | ]
535 | }
536 | ]
--------------------------------------------------------------------------------
/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: Balance (outward)",
17 | "command": "run_emmet_action",
18 | "args": {
19 | "action": "balance_outward"
20 | }
21 | },
22 |
23 | {
24 | "caption": "Emmet: Balance (inward)",
25 | "command": "run_emmet_action",
26 | "args": {
27 | "action": "balance_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.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 main template",
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 | // Disables PyV8 auto-update. Useful for users who installed PyV8
84 | // manually from https://github.com/emmetio/pyv8-binaries because
85 | // of local proxy restrictions
86 | "disable_pyv8_update": false,
87 |
88 | // Expand abbreviations inside PHP strings on single line
89 | // (e.g. without formatting)
90 | "php_single_line": false,
91 |
92 | ///////////////////////////////
93 | // Emmet customization
94 | // Each section has the same meaning as the same-named JSON file
95 | // described here:
96 | // http://docs.emmet.io/customization/
97 | ///////////////////////////////
98 |
99 |
100 | // Custom snippets definitions, as per https://github.com/emmetio/emmet/blob/master/snippets.json
101 | "snippets": {
102 | // "html": {
103 | // "abbreviations": {
104 | // "example": "
"
105 | // }
106 | // }
107 | },
108 |
109 | // Emmet preferences
110 | // List of all available preferences:
111 | // http://docs.emmet.io/customization/preferences/
112 | "preferences": {
113 | // "css.valueSeparator": ": ",
114 | // "css.propertyEnd": ";"
115 | },
116 |
117 | // Output profiles for syntaxes
118 | // http://docs.emmet.io/customization/syntax-profiles/
119 | "syntaxProfiles": {
120 | // Enable XHTML dialect for HTML syntax
121 | // "html": "xhtml"
122 | //
123 | // Write chained CSS abbreviations on single line:
124 | // "css": "css_line"
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/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|main|template)\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 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2012 Sergey Chikuyonok
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in the
7 | Software without restriction, including without limitation the rights to use,
8 | copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
9 | Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19 | AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # This plugin is deprecated and no longer maintained, please use [new version](http://github.com/emmetio/sublime-text-plugin).
2 | ---
3 |
4 | # Emmet for Sublime Text
5 |
6 | Official [Emmet](http://emmet.io) plugin for Sublime Text.
7 |
8 | * [How to install](#how-to-install)
9 | * [Available actions](#available-actions)
10 | * [Extensions support](#extensions-support)
11 | * [Overriding keyboard shortcuts](#overriding-keyboard-shortcuts)
12 | * [How to expand abbreviatoins with Tab key in other syntaxes](#how-to-expand-abbreviations-with-tab-in-other-syntaxes)
13 | * [Notes about Tab key handler](#tab-key-handler)
14 |
15 | ## How to install
16 |
17 | *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).
18 |
19 | With [Package Control](http://wbond.net/sublime_packages/package_control):
20 |
21 | 1. Run “Package Control: Install Package” command, find and install `Emmet` plugin.
22 | 2. Restart ST editor (if required)
23 |
24 | Manually:
25 |
26 | 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)
27 | 2. Restart ST editor (if required)
28 |
29 | --------------
30 |
31 | **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).
32 |
33 | ## Available actions ##
34 |
35 | * [Expand Abbreviation](http://docs.emmet.io/actions/expand-abbreviation/) – Tab or Ctrl+E
36 | * Interactive “Expand Abbreviation” — Ctrl+Alt+Enter
37 | * [Match Tag Pair Outward](http://docs.emmet.io/actions/match-pair/) – ⌃D (Mac) / Ctrl+, (PC)
38 | * [Match Tag Pair Inward](http://docs.emmet.io/actions/match-pair/) – ⌃J / Shift+Ctrl+0
39 | * [Go to Matching Pair](http://docs.emmet.io/actions/go-to-pair/) – ⇧⌃T / Ctrl+Alt+J
40 | * [Wrap With Abbreviation](http://docs.emmet.io/actions/wrap-with-abbreviation/) — ⌃W / Shift+Ctrl+G
41 | * [Go to Edit Point](http://docs.emmet.io/actions/go-to-edit-point/) — Ctrl+Alt+→ or Ctrl+Alt+←
42 | * [Select Item](http://docs.emmet.io/actions/select-item/) – ⇧⌘. or ⇧⌘, / Shift+Ctrl+. or Shift+Ctrl+,
43 | * [Toggle Comment](http://docs.emmet.io/actions/toggle-comment/) — ⇧⌥/ / Shift+Ctrl+/
44 | * [Split/Join Tag](http://docs.emmet.io/actions/split-join-tag/) — ⇧⌘' / Shift+Ctrl+`
45 | * [Remove Tag](http://docs.emmet.io/actions/remove-tag/) – ⌘' / Shift+Ctrl+;
46 | * [Update Image Size](http://docs.emmet.io/actions/update-image-size/) — ⇧⌃I / Ctrl+U
47 | * [Evaluate Math Expression](http://docs.emmet.io/actions/evaluate-math/) — ⇧⌘Y / Shift+Ctrl+Y
48 | * [Reflect CSS Value](http://docs.emmet.io/actions/reflect-css-value/) – ⇧⌘R / Shift+Ctrl+R
49 | * [Encode/Decode Image to data:URL](http://docs.emmet.io/actions/base64/) – ⇧⌃D / Ctrl+'
50 | * Rename Tag – ⇧⌘K / Shift+Ctrl+'
51 |
52 | [Increment/Decrement Number](http://docs.emmet.io/actions/inc-dec-number/) actions:
53 |
54 | * Increment by 1: Ctrl+↑
55 | * Decrement by 1: Ctrl+↓
56 | * Increment by 0.1: Alt+↑
57 | * Decrement by 0.1: Alt+↓
58 | * Increment by 10: ⌥⌘↑ / Shift+Alt+↑
59 | * Decrement by 10: ⌥⌘↓ / Shift+Alt+↓
60 |
61 | ## Extensions support ##
62 |
63 | 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.
64 |
65 | The default value of `extensions_path` is `~/emmet`, which points to _emmet_ folder inside your OS user’s home folder.
66 |
67 | 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.
68 |
69 | ## Overriding keyboard shortcuts ##
70 |
71 | 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.
72 |
73 | If you’re unhappy with default keymap, you can disable individual keyboard shortcuts with `disabled_keymap_actions` preference of `Emmet.sublime-settings` file.
74 |
75 | 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:
76 |
77 | "disabled_keymap_actions": "expand_abbreviation, update_image_size"
78 |
79 | You should refer `Default (Your-OS-Name).sublime-keymap` file to get action ids (look for `args/action` key).
80 |
81 | To disable all default shortcuts, set value to `all`:
82 |
83 | "disabled_keymap_actions": "all"
84 |
85 | 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.
86 |
87 | ## How to expand abbreviations with Tab in other syntaxes
88 |
89 | Emmet expands abbreviations in limited syntaxes only: HTML, CSS, LESS, SCSS, Stylus and PostCSS. The reason to restrict Tab handler to a limited syntax list is because it breaks native Sublime Text snippets.
90 |
91 | If you want to abbreviation with Tab in other syntaxes (for example, JSX, HAML etc.) you have to tweak your [keyboard shorcuts settings](http://sublime-text-unofficial-documentation.readthedocs.org/en/sublime-text-2/reference/key_bindings.html): add `expand_abbreviation_by_tab` command for `tab` key for required syntax *scope selectors*. To get current syntax scope selector, press ⇧⌃P (OSX) or Ctrl+Alt+Shift+P, it will be displayed in editor status bar.
92 |
93 | Go to `Preferences` > `Key Bindings — User` and insert the following JSON snippet with properly configured scope selector instead of `SCOPE_SELECTOR` token:
94 |
95 | ```js
96 | {
97 | "keys": ["tab"],
98 | "command": "expand_abbreviation_by_tab",
99 |
100 | // put comma-separated syntax selectors for which
101 | // you want to expandEmmet abbreviations into "operand" key
102 | // instead of SCOPE_SELECTOR.
103 | // Examples: source.js, text.html - source
104 | "context": [
105 | {
106 | "operand": "SCOPE_SELECTOR",
107 | "operator": "equal",
108 | "match_all": true,
109 | "key": "selector"
110 | },
111 |
112 | // run only if there's no selected text
113 | {
114 | "match_all": true,
115 | "key": "selection_empty"
116 | },
117 |
118 | // don't work if there are active tabstops
119 | {
120 | "operator": "equal",
121 | "operand": false,
122 | "match_all": true,
123 | "key": "has_next_field"
124 | },
125 |
126 | // don't work if completion popup is visible and you
127 | // want to insert completion with Tab. If you want to
128 | // expand Emmet with Tab even if popup is visible --
129 | // remove this section
130 | {
131 | "operand": false,
132 | "operator": "equal",
133 | "match_all": true,
134 | "key": "auto_complete_visible"
135 | },
136 | {
137 | "match_all": true,
138 | "key": "is_abbreviation"
139 | }
140 | ]
141 | }
142 | ```
143 |
144 | ### Tab key handler ###
145 |
146 | 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.
147 |
148 | By default, Tab handler works in a limited _syntax scopes_: HTML, XML, HAML, CSS, SASS/SCSS, LESS, PostCSS and _strings in programming languages_ (like JavaScript, Python, Ruby etc.). It means:
149 |
150 | * You have to switch your document to one of the syntaxes listed above to expand abbreviations by Tab key.
151 | * With Ctrl-E shortcut, you can expand abbreviations everywhere, its scope is not limited.
152 | * 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.
153 |
154 | To fine-tune Tab key handler, you can use the following settings in user’s `Emmet.sublime-settings` file:
155 |
156 | * `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:
157 |
158 | ```json
159 | "disable_tab_abbreviations_for_scopes": "text.haml, string"
160 | ```
161 |
162 | * `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.
163 | * `known_html_tags` — a space-separated list of all known HTML tags used for lookup as described above.
164 |
165 | 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.
166 |
167 | ## Disable automatic vendor prefixes insertion
168 | If your workflow already includes an automated task for CSS vendor prefixing (such as [Autoprefixer](https://github.com/postcss/autoprefixer)), you can disable Emmet's automatic vendor prefixes insertion adding this option to your user’s `Emmet.sublime-settings` file:
169 | ```json
170 | {
171 | "preferences": {
172 | "css.autoInsertVendorPrefixes": false
173 | }
174 | }
175 | ```
176 |
--------------------------------------------------------------------------------
/editor.js:
--------------------------------------------------------------------------------
1 | var _completions = {};
2 |
3 | // some caching data used during action sessions
4 | // make sure to call pyResetCache() before each new function call
5 | var __cache = {};
6 |
7 | var editorUtils = emmet.utils.editor;
8 | var actionUtils = emmet.utils.action;
9 | var range = emmet.require('assets/range.js');
10 | var tabStops = emmet.tabStops;
11 | var utils = emmet.utils.common;
12 | var htmlMatcher = emmet.htmlMatcher;
13 | var resources = emmet.resources;
14 | var cssResolver = emmet.require('resolver/css.js');
15 | var abbreviationParser = emmet.require('parser/abbreviation.js');
16 | var expandAbbreviationAction = emmet.require('action/expandAbbreviation.js');
17 | var updateTagAction = emmet.require('action/updateTag.js');
18 |
19 | function activeView() {
20 | return sublime.active_window().active_view();
21 | }
22 |
23 | var editorProxy = {
24 | getSelectionRange: function() {
25 | var view = activeView();
26 | var sel = view.sel()[0];
27 | return {
28 | start: sel.begin(),
29 | end: sel.end()
30 | };
31 | },
32 |
33 | createSelection: function(start, end) {
34 | var view = activeView();
35 | view.sel().clear();
36 |
37 | view.sel().add(new sublime.Region(start, end || start));
38 | view.show(view.sel());
39 | },
40 |
41 | getCurrentLineRange: function() {
42 | var view = activeView();
43 | var selection = view.sel()[0];
44 | var line = view.line(selection);
45 | return {
46 | start: line.begin(),
47 | end: line.end()
48 | };
49 | },
50 |
51 | getCaretPos: function() {
52 | var view = activeView();
53 | var sel = view.sel();
54 | return sel && sel[0] ? sel[0].begin() : 0;
55 | },
56 |
57 | setCaretPos: function(pos){
58 | this.createSelection(pos, pos);
59 | },
60 |
61 | getCurrentLine: function() {
62 | var view = activeView();
63 | return view.substr(view.line(view.sel()[0]));
64 | },
65 |
66 | replaceContent: function(value, start, end, noIndent) {
67 | if (typeof end === 'undefined')
68 | end = typeof start === 'undefined' ? this.getContent().length : start;
69 | if (typeof start === 'undefined') start = 0;
70 |
71 | // update tabstops: make sure all caret placeholder are unique
72 | // by default, abbreviation parser generates all unlinked (un-mirrored)
73 | // tabstops as ${0}, so we have upgrade all caret tabstops with unique
74 | // positions but make sure that all other tabstops are not linked accidentally
75 | value = pyPreprocessText(value);
76 | value = editorUtils.normalize(value);
77 | sublimeReplaceSubstring(start, end, value, !!noIndent);
78 | },
79 |
80 | getContent: function() {
81 | var view = activeView();
82 | return view.substr(new sublime.Region(0, view.size()));
83 | },
84 |
85 | getSyntax: function() {
86 | return pyGetSyntax();
87 | },
88 |
89 | getProfileName: function() {
90 | var view = activeView();
91 | var pos = this.getCaretPos();
92 |
93 | var m = function(sel) {
94 | return view.match_selector(pos, sel);
95 | }
96 |
97 | if (m('text.html') && sublimeGetOption('autodetect_xhtml', false) && actionUtils.isXHTML(this)) {
98 | return 'xhtml';
99 | }
100 |
101 | if (m('string.quoted.double.block.python')
102 | || m('source.coffee string')
103 | || (m('source.php string') && !sublimeGetOption('php_single_line'))
104 | || m('string.unquoted.heredoc')) {
105 | // use html's default profile for:
106 | // * Python's multiline block
107 | // * CoffeeScript string
108 | // * PHP heredoc
109 | return pyDetectProfile();
110 | }
111 |
112 | if (m('source string')) {
113 | return 'line';
114 | }
115 |
116 | return pyDetectProfile();
117 | },
118 |
119 | prompt: function(title) {
120 | return pyEditor.prompt();
121 | },
122 |
123 | getSelection: function() {
124 | var view = activeView();
125 | return view.sel() ? view.substr(view.sel()[0]) : '';
126 | },
127 |
128 | getFilePath: function() {
129 | return activeView().file_name();
130 | }
131 | };
132 |
133 | function pyPreprocessText(value) {
134 | var base = 1000;
135 | var zeroBase = 0;
136 | var lastZero = null;
137 |
138 | var tabstopOptions = {
139 | tabstop: function(data) {
140 | var group = parseInt(data.group, 10);
141 | var isZero = group === 0;
142 | if (isZero)
143 | group = ++zeroBase;
144 | else
145 | group += base;
146 |
147 | var placeholder = data.placeholder;
148 | if (placeholder) {
149 | // recursively update nested tabstops
150 | placeholder = tabStops.processText(placeholder, tabstopOptions);
151 | }
152 |
153 | var result = '${' + group + (placeholder ? ':' + placeholder : '') + '}';
154 |
155 | if (isZero) {
156 | lastZero = range.create(data.start, result);
157 | }
158 |
159 | return result
160 | },
161 | escape: function(ch) {
162 | if (ch == '$') {
163 | return '\\$';
164 | }
165 |
166 | if (ch == '\\') {
167 | return '\\\\';
168 | }
169 |
170 | return ch;
171 | }
172 | };
173 |
174 | value = tabStops.processText(value, tabstopOptions);
175 |
176 | if (sublimeGetOption('insert_final_tabstop', false) && !/\$\{0\}$/.test(value)) {
177 | value += '${0}';
178 | } else if (lastZero) {
179 | value = utils.replaceSubstring(value, '${0}', lastZero);
180 | }
181 |
182 | return value;
183 | }
184 |
185 | function pyExpandAsYouType(abbr, options) {
186 | options = options || {};
187 | var ix = (options.index || 0);
188 | var cacheKey = 'expandParams' + ix;
189 | if (!(cacheKey in __cache)) {
190 | var capturePos = options.selectedRange
191 | ? options.selectedRange.begin()
192 | : editorProxy.getCaretPos();
193 |
194 | __cache[cacheKey] = {
195 | syntax: editorProxy.getSyntax(),
196 | profile: editorProxy.getProfileName() || null,
197 | counter: ix + 1,
198 | contextNode: actionUtils.captureContext(editorProxy, capturePos)
199 | };
200 |
201 | if (options.selectedContent) {
202 | __cache[cacheKey].pastedContent = utils.escapeText(options.selectedContent);
203 | }
204 | }
205 |
206 | try {
207 | var result = abbreviationParser.expand(abbr, __cache[cacheKey]);
208 | return pyPreprocessText(result);
209 | } catch(e) {
210 | return '';
211 | }
212 | }
213 |
214 | function pyUpdateAsYouType(abbr, options) {
215 | options = options || {};
216 | var ix = (options.index || 0);
217 | var cacheKey = 'updateParams' + ix;
218 | if (!(cacheKey in __cache)) {
219 | var capturePos = options.selectedRange
220 | ? options.selectedRange.begin()
221 | : editorProxy.getCaretPos();
222 |
223 | __cache[cacheKey] = {
224 | counter: ix + 1,
225 | content: editorProxy.getContent(),
226 | ctx: actionUtils.captureContext(editorProxy, capturePos)
227 | };
228 | }
229 |
230 | // try {
231 | var cache = __cache[cacheKey];
232 | if (!cache.ctx) {
233 | return null;
234 | }
235 |
236 | var tag = updateTagAction.getUpdatedTag(abbr, cache.ctx, cache.content, {
237 | counter: cache.counter
238 | });
239 |
240 | if (!tag) {
241 | return null;
242 | }
243 |
244 | var out = [{
245 | start: cache.ctx.match.open.range.start,
246 | end: cache.ctx.match.open.range.end,
247 | content: tag.source
248 | }];
249 |
250 | if (tag.name() != cache.ctx.name && cache.ctx.match.close) {
251 | out.unshift({
252 | start: cache.ctx.match.close.range.start,
253 | end: cache.ctx.match.close.range.end,
254 | content: '' + tag.name() + '>'
255 | });
256 | }
257 |
258 | return out;
259 | // } catch(e) {
260 | // console.log(e);
261 | // return null;
262 | // }
263 | }
264 |
265 | function pyCaptureWrappingRange() {
266 | var info = editorUtils.outputInfo(editorProxy);
267 | var range = editorProxy.getSelectionRange();
268 | var startOffset = range.start;
269 | var endOffset = range.end;
270 |
271 | if (startOffset == endOffset) {
272 | // no selection, find tag pair
273 | var match = htmlMatcher.find(info.content, startOffset);
274 | if (!match) {
275 | // nothing to wrap
276 | return null;
277 | }
278 |
279 | var narrowedSel = utils.narrowToNonSpace(info.content, match.range);
280 | startOffset = narrowedSel.start;
281 | endOffset = narrowedSel.end;
282 | }
283 |
284 | return [startOffset, endOffset];
285 | }
286 |
287 | function pyGetTagNameRanges(pos) {
288 | var ranges = [];
289 | var info = editorUtils.outputInfo(editorProxy);
290 |
291 | // search for tag
292 | try {
293 | var tag = htmlMatcher.tag(info.content, pos);
294 | if (tag) {
295 | var open = tag.open.range;
296 | var tagName = /^<([\w\-\:]+)/i.exec(open.substring(info.content))[1];
297 | ranges.push([open.start + 1, open.start + 1 + tagName.length]);
298 |
299 | if (tag.close) {
300 | ranges.push([tag.close.range.start + 2, tag.close.range.start + 2 + tagName.length]);
301 | }
302 | }
303 | } catch (e) {}
304 |
305 | return ranges;
306 | }
307 |
308 | function pyGetTagRanges() {
309 | var ranges = [];
310 | var info = editorUtils.outputInfo(editorProxy);
311 |
312 | // search for tag
313 | try {
314 | var tag = htmlMatcher.tag(info.content, editorProxy.getCaretPos());
315 | if (tag) {
316 | ranges.push(tag.open.range.toArray());
317 | if (tag.close) {
318 | ranges.push(tag.close.range.toArray());
319 | }
320 | }
321 | } catch (e) {}
322 |
323 | return ranges;
324 | }
325 |
326 | function pyExtractAbbreviation() {
327 | return expandAbbreviationAction.findAbbreviation(editorProxy);
328 | }
329 |
330 | function pyHasSnippet(name) {
331 | return !!resources.findSnippet(editorProxy.getSyntax(), name);
332 | }
333 |
334 | /**
335 | * Get all available CSS completions. This method is optimized for CSS
336 | * only since it should contain snippets only so it's not required
337 | * to do extra parsing
338 | */
339 | function pyGetCSSCompletions(dialect) {
340 | dialect = dialect || pyGetSyntax();
341 |
342 | if (!_completions[dialect]) {
343 | var all = resources.getAllSnippets(dialect);
344 | _completions[dialect] = Object.keys(all).map(function(k) {
345 | var v = all[k];
346 | var snippetValue = typeof v.parsedValue == 'object'
347 | ? v.parsedValue.data
348 | : v.value;
349 | var snippet = cssResolver.transformSnippet(snippetValue, false, dialect);
350 | return {
351 | k: v.nk,
352 | label: snippet.replace(/\:\s*\$\{0\}\s*;?$/, ''),
353 | v: cssResolver.expandToSnippet(v.nk, dialect)
354 | };
355 | });
356 | }
357 |
358 | return _completions[dialect];
359 | }
360 |
361 | /**
362 | * Returns current syntax name
363 | * @return {String}
364 | */
365 | function pyGetSyntax() {
366 | var view = activeView();
367 | var pt = view.sel()[0].begin();
368 | var scope = 'scope_name' in view ? view.scope_name(pt) : view.syntax_name(pt);
369 |
370 | if (~scope.indexOf('xsl')) {
371 | return 'xsl';
372 | }
373 |
374 | if (!/\bstring\b/.test(scope) && /\bsource\.jsx?\b/.test(scope)) {
375 | return 'jsx';
376 | }
377 |
378 | var syntax = 'html';
379 |
380 | if (!/\bstring\b/.test(scope) && /\bsource\.([\w\-]+)/.test(scope) && resources.hasSyntax(RegExp.$1)) {
381 | syntax = RegExp.$1;
382 | } else if (/\b(less|scss|sass|css|stylus|postcss)\b/.test(scope)) {
383 | // detect CSS-like syntaxes independently,
384 | // since it may cause collisions with some highlighters
385 | syntax = RegExp.$1;
386 |
387 | if (syntax === 'postcss') {
388 | syntax = 'css';
389 | }
390 | } else if (/\b(html|xml|haml|slim|jade|pug)\b/.test(scope)) {
391 | syntax = RegExp.$1;
392 | }
393 |
394 | return actionUtils.detectSyntax(editorProxy, syntax);
395 | }
396 |
397 | function pyDetectProfile(syntax) {
398 | return actionUtils.detectProfile(editorProxy, syntax);
399 | }
400 |
401 | function pyResetCache() {
402 | __cache = {};
403 | }
--------------------------------------------------------------------------------
/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.2'
32 | __core_version__ = '1.1'
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=get_extensions_path(),
96 | contrib=contrib,
97 | logger=delegate.log,
98 | reader=js_file_reader
99 | )
100 |
101 | update_settings()
102 |
103 | if not settings.get('disable_pyv8_update', False):
104 | pyv8loader.load(pyv8_paths[1], delegate)
105 | else:
106 | print('PyV8 auto-update is disabled')
107 |
108 | if settings.get('remove_html_completions', False):
109 | sublime.set_timeout(cmpl.remove_html_completions, 2000)
110 |
111 | def get_extensions_path():
112 | ext_path = settings.get('extensions_path', None)
113 | if ext_path:
114 | try:
115 | if not is_python3:
116 | ext_path = ext_path.decode('utf-8')
117 | ext_path = os.path.expanduser(ext_path)
118 | if not os.path.isabs(ext_path):
119 | ext_path = os.path.normpath(os.path.join(sublime.packages_path(), ext_path))
120 | except Exception as e:
121 | print('Unable to normalize extension path for Emmet: %s' % e)
122 | ext_path = None
123 | return ext_path
124 |
125 | class SublimeLoaderDelegate(LoaderDelegate):
126 | def __init__(self, settings=None):
127 |
128 | if settings is None:
129 | settings = {}
130 | for k in ['http_proxy', 'https_proxy', 'timeout']:
131 | if user_settings.has(k):
132 | settings[k] = user_settings.get(k, None)
133 |
134 | LoaderDelegate.__init__(self, settings)
135 | self.state = None
136 | self.message = 'Loading PyV8 binary, please wait'
137 | self.i = 0
138 | self.addend = 1
139 | self.size = 8
140 |
141 | def on_start(self, *args, **kwargs):
142 | self.state = 'loading'
143 |
144 | def on_progress(self, *args, **kwargs):
145 | if kwargs['progress'].is_background:
146 | return
147 |
148 | before = self.i % self.size
149 | after = (self.size - 1) - before
150 | msg = '%s [%s=%s]' % (self.message, ' ' * before, ' ' * after)
151 | if not after:
152 | self.addend = -1
153 | if not before:
154 | self.addend = 1
155 | self.i += self.addend
156 |
157 | sublime.set_timeout(lambda: sublime.status_message(msg), 0)
158 |
159 | def on_complete(self, *args, **kwargs):
160 | self.state = 'complete'
161 |
162 | if kwargs['progress'].is_background:
163 | return
164 |
165 | sublime.set_timeout(lambda: sublime.status_message('PyV8 binary successfully loaded'), 0)
166 |
167 | def on_error(self, exit_code=-1, progress=None):
168 | self.state = 'error'
169 | sublime.set_timeout(lambda: show_pyv8_error(exit_code), 0)
170 |
171 | def setting(self, name, default=None):
172 | "Returns specified setting name"
173 | return self.settings.get(name, default)
174 |
175 | def log(self, message):
176 | print('Emmet: %s' % message)
177 |
178 | def show_pyv8_error(exit_code):
179 | if 'PyV8' not in sys.modules:
180 | 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)
181 |
182 | def active_view():
183 | return sublime.active_window().active_view()
184 |
185 | def check_context(verbose=False):
186 | "Checks if JS context is completely available"
187 | if not ctx.js():
188 | if verbose:
189 | sublime.message_dialog('Please wait a bit while PyV8 binary is being downloaded')
190 | return False
191 |
192 | return True
193 |
194 |
195 | def replace_substring(start, end, value, no_indent=False):
196 | view = active_view()
197 |
198 | view.sel().clear()
199 | view.sel().add(sublime.Region(start, end or start))
200 |
201 | if not is_python3:
202 | value = value.decode('utf-8')
203 |
204 | # XXX a bit naive indentation control. It handles most common
205 | # `no_indent` usages like replacing CSS rule content, but may not
206 | # produce expected result in all possible situations
207 |
208 | if no_indent:
209 | line = view.substr(view.line(view.sel()[0]))
210 | value = unindent_text(value, get_line_padding(line))
211 |
212 | view.run_command('insert_snippet', {'contents': value})
213 |
214 | def unindent_text(text, pad):
215 | """
216 | Removes padding at the beginning of each text's line
217 | @type text: str
218 | @type pad: str
219 | """
220 | lines = text.splitlines()
221 |
222 | for i,line in enumerate(lines):
223 | if line.startswith(pad):
224 | lines[i] = line[len(pad):]
225 |
226 | return '\n'.join(lines)
227 |
228 | def get_line_padding(line):
229 | """
230 | Returns padding of current editor's line
231 | @return str
232 | """
233 | m = re.match(r'^(\s+)', line)
234 | return m and m.group(0) or ''
235 |
236 | def update_settings():
237 | ctx.set_ext_path(get_extensions_path())
238 |
239 | keys = ['snippets', 'preferences', 'syntaxProfiles', 'profiles']
240 | payload = {}
241 | for k in keys:
242 | data = settings.get(k, None)
243 | if data:
244 | payload[k] = data
245 |
246 | ctx.reset()
247 | ctx.load_user_data(json.dumps(payload))
248 | ctx.js()
249 |
250 | def get_scope(view, pt=-1):
251 | if pt == -1:
252 | # use current caret position
253 | pt = view.sel()[0].begin()
254 |
255 | if hasattr(view, 'scope_name'):
256 | return view.scope_name(pt)
257 |
258 | return view.syntax_name(pt)
259 |
260 | def should_perform_action(name, view=None):
261 | if not view:
262 | view = active_view()
263 |
264 | # fallback to old check
265 | if not view.settings().get('enable_emmet_keymap', True):
266 | return False
267 |
268 | disabled_actions = settings.get('disabled_keymap_actions', '')
269 |
270 | if not disabled_actions: # no disabled actions
271 | return True
272 |
273 | if disabled_actions == 'all': # disable all actions
274 | return False
275 |
276 | return name not in re.split(r'\s*,\s*', disabled_actions.strip())
277 |
278 | def should_handle_tab_key(syntax=None):
279 | view = active_view()
280 | scopes = settings.get('disabled_single_snippet_for_scopes', None)
281 | cur_scope = get_scope(view)
282 |
283 |
284 | if sublime.score_selector(cur_scope, 'source.css'):
285 | return True
286 |
287 | with ctx.js() as c:
288 | abbr = c.locals.pyExtractAbbreviation()
289 |
290 | disabled_snippets = settings.get('disabled_single_snippets', '').split()
291 | if disabled_snippets and abbr in disabled_snippets:
292 | return False
293 |
294 | if not re.match(r'^[\w\-\:%]+$', abbr):
295 | # it's a complex expression
296 | return True
297 |
298 | if re.match(r'^(lorem|lipsum)([a-z]{2})?\d*$', abbr, re.I):
299 | # hardcoded Lorem Ipsum generator
300 | return True
301 |
302 | # detect inline CSS
303 | if syntax is None:
304 | syntax = c.locals.pyGetSyntax();
305 |
306 | if syntax == 'css':
307 | return True
308 |
309 | known_tags = settings.get('known_html_tags', '').split()
310 | if abbr in known_tags or c.locals.pyHasSnippet(abbr):
311 | return True
312 |
313 | if not scopes or not sublime.score_selector(cur_scope, scopes):
314 | return True
315 |
316 | return False
317 |
318 | def log(message):
319 | if settings.get('debug', False):
320 | print('Emmet: %s' % message)
321 |
322 | def action_factory(name):
323 | def _action(i, sel):
324 | with ctx.js() as c:
325 | return c.locals.pyRunAction(name)
326 | return _action
327 |
328 | class RunEmmetAction(sublime_plugin.TextCommand):
329 | def run(self, edit, action=None, **kw):
330 | run_action(action_factory(action))
331 |
332 | class ActionContextHandler(sublime_plugin.EventListener):
333 | def on_query_context(self, view, key, op, operand, match_all):
334 | if not key.startswith('emmet_action_enabled.'):
335 | return None
336 |
337 | prefix, name = key.split('.')
338 | return should_perform_action(name, view)
339 |
340 | def get_edit(view, edit_token=None):
341 | edit = None
342 | try:
343 | edit = view.begin_edit()
344 | except:
345 | pass
346 |
347 | if not edit and edit_token:
348 | try:
349 | edit = view.begin_edit(edit_token, 'Emmet')
350 | except Exception as e:
351 | pass
352 |
353 | return edit
354 |
355 | def run_action(action, view=None):
356 | if not check_context(True):
357 | return
358 |
359 | "Runs Emmet action in multiselection mode"
360 | if not view:
361 | view = active_view()
362 |
363 | region_key = '__emmet__'
364 | sels = list(view.sel())
365 | result = False
366 |
367 | # edit = get_edit(view, edit_token)
368 | max_sel_ix = len(sels) - 1
369 |
370 | try:
371 | for i, sel in enumerate(reversed(sels)):
372 | view.sel().clear()
373 | view.sel().add(sel)
374 | # run action
375 | # result = r(name) or result
376 | result = action(max_sel_ix - i, sel) or result
377 |
378 | # remember resulting selections
379 | view.add_regions(region_key,
380 | (view.get_regions(region_key) + list(view.sel())) , '')
381 | except Exception as e:
382 | view.erase_regions(region_key)
383 | print(traceback.format_exc())
384 | return
385 |
386 |
387 | # output all saved regions as selection
388 | view.sel().clear()
389 | for sel in view.get_regions(region_key):
390 | view.sel().add(sel)
391 |
392 | view.erase_regions(region_key)
393 |
394 | # if edit:
395 | # view.end_edit(edit)
396 | return result
397 |
398 | class TabAndCompletionsHandler():
399 | def correct_syntax(self, view, syntax='html'):
400 | return syntax == 'html' and view.match_selector( view.sel()[0].b, cmpl.EMMET_SCOPE )
401 |
402 | def completion_handler(self, view):
403 | "Returns completions handler fo current caret position"
404 | black_list = settings.get('completions_blacklist', [])
405 |
406 | # A mapping of scopes, sub scopes and handlers, first matching of which
407 | # is used.
408 | COMPLETIONS = (
409 | (cmpl.HTML_INSIDE_TAG, self.html_elements_attributes),
410 | (cmpl.HTML_INSIDE_TAG_ATTRIBUTE, self.html_attributes_values)
411 | )
412 |
413 | pos = view.sel()[0].b
414 |
415 | # Try to find some more specific contextual abbreviation
416 | for sub_selector, handler in COMPLETIONS:
417 | h_name = handler.__name__
418 | if not black_list or h_name in black_list: continue
419 | if (view.match_selector(pos, sub_selector) or
420 | view.match_selector(pos - 1, sub_selector)):
421 | return handler
422 |
423 | return None
424 |
425 | def html_elements_attributes(self, view, prefix, pos):
426 | tag = cmpl.find_tag_name(view, pos)
427 | values = HTML_ELEMENTS_ATTRIBUTES.get(tag, [])
428 | return [(v, '%s\t@%s' % (v,v), '%s="$1"' % v) for v in values]
429 |
430 | def html_attributes_values(self, view, prefix, pos):
431 | attr = cmpl.find_attribute_name(view, pos)
432 | values = HTML_ATTRIBUTES_VALUES.get(attr, [])
433 | return [(v, '%s\t@=%s' % (v,v), v) for v in values]
434 |
435 | def expand_by_tab(self, view):
436 | if not check_context():
437 | return False;
438 |
439 | with ctx.js() as c:
440 | syntax = str(c.locals.pyGetSyntax());
441 |
442 | if not should_handle_tab_key(syntax):
443 | return False
444 |
445 | # we need to filter out attribute completions if
446 | # 'disable_completions' option is not active
447 | if (not settings.get('disable_completions', False) and
448 | self.correct_syntax(view, syntax) and
449 | self.completion_handler(view)):
450 | return None
451 |
452 | caret_pos = view.sel()[0].begin()
453 | cur_scope = get_scope(view)
454 |
455 | # let's see if Tab key expander should be disabled for current scope
456 | banned_scopes = settings.get('disable_tab_abbreviations_for_scopes', '')
457 | if banned_scopes and view.score_selector(caret_pos, banned_scopes):
458 | return None
459 |
460 | # Sometimes ST2 matcher may incorrectly filter scope context,
461 | # check it against special regexp
462 | banned_regexp = settings.get('disable_tab_abbreviations_for_regexp', None)
463 | if banned_regexp and re.search(banned_regexp, cur_scope):
464 | return None
465 |
466 | return run_action(action_factory('expand_abbreviation'))
467 | # view.run_command('run_emmet_action',
468 | # {'action':'expand_abbreviation'})
469 |
470 | class ExpandAbbreviationByTab(sublime_plugin.TextCommand):
471 | def run(self, edit, **kw):
472 | if settings.get('use_old_tab_handler', False):
473 | return
474 |
475 | view = active_view()
476 | h = TabAndCompletionsHandler()
477 | if not h.expand_by_tab(view):
478 | # try to mimic default Tab behaviour of Sublime Text
479 | view.run_command('insert_best_completion', {
480 | 'default': '\t',
481 | 'exact': user_settings.get('tab_completion', True)
482 | })
483 |
484 |
485 | class TabExpandHandler(sublime_plugin.EventListener):
486 | def on_query_context(self, view, key, op, operand, match_all):
487 | if key != 'is_abbreviation':
488 | return None
489 |
490 | if settings.get('use_old_tab_handler', False):
491 | h = TabAndCompletionsHandler()
492 | return h.expand_by_tab(view)
493 |
494 | return check_context()
495 |
496 | def on_query_completions(self, view, prefix, locations):
497 | h = TabAndCompletionsHandler()
498 | if view.match_selector(locations[0], settings.get('css_completions_scope', '')) and check_context():
499 | l = []
500 | if settings.get('show_css_completions', False):
501 | with ctx.js() as c:
502 | completions = c.locals.pyGetCSSCompletions()
503 | if completions:
504 | for p in completions:
505 | l.append(('%s\t%s' % (p['k'], p['label']), p['v']))
506 |
507 | if not l:
508 | return []
509 |
510 | return (l, sublime.INHIBIT_WORD_COMPLETIONS | sublime.INHIBIT_EXPLICIT_COMPLETIONS)
511 |
512 | if not h.correct_syntax(view) or settings.get('disable_completions', False):
513 | return []
514 |
515 | handler = h.completion_handler(view)
516 | if handler:
517 | pos = view.sel()[0].b
518 | completions = handler(view, prefix, pos)
519 | return completions
520 |
521 | return []
522 |
523 |
524 | class CommandsAsYouTypeBase(sublime_plugin.TextCommand):
525 | input_message = "Enter Input"
526 | default_input = ""
527 | process_panel_input = lambda s, i: i.title()
528 |
529 | # Note that this must be of form `Packages/$Package/Emmet.tmLanguage` on ST3
530 | # NOT an absolute path!
531 | panel_grammar = EMMET_GRAMMAR
532 |
533 | def is_enabled(self):
534 | return True
535 |
536 | def run_command(self, edit, view, processed_input):
537 | if '\n' in processed_input:
538 | for sel in view.sel():
539 | trailing = sublime.Region(sel.end(), view.line(sel).end())
540 | if view.substr(trailing).isspace():
541 | view.erase(edit, trailing)
542 |
543 | if not is_python3:
544 | processed_input = processed_input.decode('utf-8')
545 | view.run_command('insert_snippet', { 'contents': processed_input })
546 |
547 | def on_panel_change(self, abbr):
548 | if not abbr and self.erase:
549 | self.undo()
550 | self.erase = False
551 | return
552 |
553 | def inner_insert():
554 | self.view.run_command(self.name(), dict(panel_input=abbr))
555 | # self.view.run_command('hide_auto_complete')
556 |
557 | self.undo()
558 | sublime.set_timeout(inner_insert, 0)
559 |
560 | def undo(self):
561 | if self.erase:
562 | sublime.set_timeout(lambda: self.view.run_command('undo'), 0)
563 |
564 | def remember_sels(self, view):
565 | self._sels = list(view.sel())
566 | self._sel_items = []
567 |
568 | for sel in self._sels:
569 | # selection should be unindented in order to get desired result
570 | line = view.substr(view.line(sel))
571 | s = view.substr(sel)
572 | self._sel_items.append(unindent_text(s, get_line_padding(line)))
573 |
574 | def on_panel_done(self, abbr):
575 | if abbr:
576 | self.default_input = abbr
577 |
578 | def run(self, edit, panel_input=None, **kwargs):
579 |
580 | if panel_input is None:
581 | self.setup(edit, self.view, **kwargs)
582 | self.erase = False
583 |
584 | panel = self.view.window().show_input_panel (
585 | self.input_message,
586 | self.default_input,
587 | self.on_panel_done, # on_done
588 | self.on_panel_change, # on_change
589 | self.undo) # on_cancel
590 |
591 | panel.sel().clear()
592 | panel.sel().add(sublime.Region(0, panel.size()))
593 |
594 | if self.panel_grammar:
595 | panel.set_syntax_file(self.panel_grammar)
596 | panel_setting = panel.settings().set
597 |
598 | panel_setting('line_numbers', False)
599 | panel_setting('gutter', False)
600 | panel_setting('auto_complete', False)
601 | panel_setting('tab_completion', False)
602 | else:
603 | self.run_on_input(edit, self.view, panel_input)
604 |
605 | def setup(self, edit, view, **kwargs):
606 | pass
607 |
608 | def run_on_input(self, edit, view, panel_input):
609 | view = self.view
610 | cmd_input = self.process_panel_input(panel_input) or ''
611 | try:
612 | self.erase = self.run_command(edit, view, cmd_input) is not False
613 | except:
614 | pass
615 |
616 | class WrapAsYouType(CommandsAsYouTypeBase):
617 | default_input = 'div'
618 | _prev_output = ''
619 | input_message = 'Enter Wrap Abbreviation: '
620 |
621 | def setup(self, edit, view, **kwargs):
622 | self._prev_output = ''
623 |
624 | with ctx.js() as c:
625 | r = c.locals.pyResetCache()
626 | if len(view.sel()) == 1:
627 | # capture wrapping context (parent HTML element)
628 | # if there is only one selection
629 | r = c.locals.pyCaptureWrappingRange()
630 | if r:
631 | view.sel().clear()
632 | view.sel().add(sublime.Region(r[0], r[1]))
633 | view.show(view.sel())
634 |
635 | self.remember_sels(view)
636 |
637 | # override method to correctly wrap abbreviations
638 | def run_on_input(self, edit, view, abbr):
639 | self.erase = True
640 |
641 | # restore selections
642 | view.sel().clear()
643 | for sel in self._sels:
644 | view.sel().add(sel)
645 |
646 | def ins(i, sel):
647 | try:
648 | with ctx.js() as c:
649 | opt = {
650 | 'selectedContent': self._sel_items[i],
651 | 'index': i,
652 | 'selectedRange': sel
653 | }
654 | self._prev_output = c.locals.pyExpandAsYouType(abbr, opt)
655 | # self.run_command(view, output)
656 | except Exception as e:
657 | "dont litter the console"
658 |
659 | self.run_command(edit, view, self._prev_output)
660 |
661 | run_action(ins, view)
662 |
663 | class ExpandAsYouType(WrapAsYouType):
664 | default_input = 'div'
665 | input_message = 'Enter Abbreviation: '
666 |
667 | def setup(self, edit, view, **kwargs):
668 | # adjust selection to non-space bounds
669 | sels = []
670 | for s in view.sel():
671 | text = view.substr(s)
672 | a = s.a + len(text) - len(text.lstrip())
673 | b = s.b - len(text) + len(text.rstrip())
674 |
675 | sels.append(sublime.Region(a, b))
676 |
677 | view.sel().clear()
678 | for s in sels:
679 | view.sel().add(s)
680 |
681 | self.remember_sels(active_view())
682 |
683 | with ctx.js() as c:
684 | r = c.locals.pyResetCache()
685 |
686 | class UpdateAsYouType(WrapAsYouType):
687 | default_input = ''
688 | input_message = 'Enter Abbreviation: '
689 | _prev_ranges = None
690 | _first_run = False
691 |
692 | def setup(self, edit, view, **kwargs):
693 | self._first_run = not self.default_input
694 | self._prev_ranges = None
695 |
696 | with ctx.js() as c:
697 | r = c.locals.pyResetCache()
698 |
699 | self.remember_sels(view)
700 |
701 | def run_on_input(self, edit, view, abbr):
702 | self.erase = not self._first_run
703 | self._first_run = False
704 |
705 | # restore selections
706 | view.sel().clear()
707 | for sel in self._sels:
708 | view.sel().add(sel)
709 |
710 | def ins(i, sel):
711 | try:
712 | with ctx.js() as c:
713 | opt = {
714 | 'index': i,
715 | 'selectedRange': sel
716 | }
717 | ranges = c.locals.pyUpdateAsYouType(abbr, opt)
718 | if ranges:
719 | out = []
720 | for r in ranges:
721 | # transform JS object to native one
722 | out.append({
723 | 'start': r['start'],
724 | 'end': r['end'],
725 | 'content': r['content']
726 | })
727 | self._prev_ranges = out
728 | # self.run_command(view, output)
729 | except Exception as e:
730 | "dont litter the console"
731 |
732 | self.run_command(edit, view, self._prev_ranges)
733 |
734 | run_action(ins, view)
735 |
736 | def run_command(self, edit, view, ranges):
737 | if not ranges:
738 | return
739 |
740 | for r in ranges:
741 | content = r['content']
742 | region = sublime.Region(r['start'], r['end'])
743 | view.replace(edit, region, content)
744 |
745 | class EnterKeyHandler(sublime_plugin.EventListener):
746 | def on_query_context(self, view, key, op, operand, match_all):
747 | if key != 'clear_fields_on_enter_key':
748 | return None
749 |
750 | if settings.get('clear_fields_on_enter_key', False):
751 | view.run_command('clear_fields')
752 |
753 | return True
754 |
755 |
756 | class RenameTag(sublime_plugin.TextCommand):
757 | def run(self, edit, **kw):
758 | if not check_context(True):
759 | return
760 |
761 | view = active_view()
762 | sels = list(view.sel())
763 | sel_cleared = False
764 | with ctx.js() as c:
765 | for s in sels:
766 | ranges = c.locals.pyGetTagNameRanges(s.begin())
767 | if ranges:
768 | if not sel_cleared:
769 | view.sel().clear()
770 | sel_cleared = True
771 |
772 | for r in ranges:
773 | view.sel().add(sublime.Region(r[0], r[1]))
774 | view.show(view.sel())
775 |
776 | class EmmetInsertAttribute(sublime_plugin.TextCommand):
777 | def run(self, edit, attribute=None, **kw):
778 | if not attribute:
779 | return
780 |
781 | view = active_view()
782 | prefix = ''
783 | if view.sel():
784 | sel = view.sel()[0]
785 | if not view.substr(sublime.Region(sel.begin() - 1, sel.begin())).isspace():
786 | prefix = ' '
787 |
788 | view.run_command('insert_snippet', {'contents': '%s%s="$1"' % (prefix, attribute)})
789 |
790 | class EmmetResetContext(sublime_plugin.TextCommand):
791 | def run(self, edit, **kw):
792 | update_settings()
793 |
794 | def plugin_loaded():
795 | sublime.set_timeout(init, 200)
796 |
797 | ##################
798 | # Init plugin
799 | if not is_python3:
800 | init()
801 |
802 |
--------------------------------------------------------------------------------
/emmet/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sergeche/emmet-sublime/e6d2752128f8aa81c242c9640a0d40f216cf8761/emmet/__init__.py
--------------------------------------------------------------------------------
/emmet/bootstrap.js:
--------------------------------------------------------------------------------
1 | var self = this;
--------------------------------------------------------------------------------
/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 = ['bootstrap.js', '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 | if val:
164 | val = os.path.expanduser(val)
165 | val = os.path.abspath(val)
166 |
167 | if val == self._ext_path:
168 | return
169 |
170 | self._ext_path = val
171 | self.reset()
172 |
173 | def load_extensions(self, path=None):
174 | if path is None:
175 | path = self.get_ext_path();
176 |
177 | if path and os.path.isdir(path):
178 | ext_files = []
179 | self.log('Loading Emmet extensions from %s' % path)
180 | for dirname, dirnames, filenames in os.walk(path):
181 | for filename in filenames:
182 | if filename[0] != '.':
183 | ext_files.append(os.path.join(dirname, filename))
184 |
185 | self.js().locals.pyLoadExtensions(ext_files)
186 |
187 | def js(self):
188 | "Returns JS context"
189 | if not self._ctx:
190 | try:
191 | import_pyv8()
192 | except ImportError as e:
193 | return None
194 |
195 | if 'PyV8' not in sys.modules:
196 | # Binary is not available yet
197 | return None
198 |
199 | if self._use_unicode is None:
200 | self._use_unicode = should_use_unicode()
201 |
202 | self._ctx_inited = False
203 |
204 | class JSContext(PyV8.JSContext):
205 | def __enter__(self):
206 | if not hasattr(self, '_counter'):
207 | self._counter = 0
208 | if not self._counter:
209 | self.lock = PyV8.JSLocker()
210 | self.lock.enter()
211 | self.enter()
212 | # print('Enter JS context')
213 |
214 | self._counter += 1
215 | return self
216 |
217 | def __exit__(self, exc_type, exc_value, traceback):
218 | self._counter -= 1
219 | if self._counter < 1 or exc_type is not None:
220 | # print('Exit JS context')
221 | self._counter = 0
222 | if self:
223 | self.leave()
224 | if self.lock:
225 | self.lock.leave()
226 | self.lock = None
227 |
228 | self._ctx = JSContext()
229 |
230 | with self._ctx as ctx:
231 | # expose some methods
232 | ctx.locals.log = js_log
233 | ctx.locals.pyFile = File()
234 |
235 | for f in self._core_files:
236 | self.eval_js_file(f)
237 |
238 | with self._ctx as ctx:
239 | # load default snippets
240 | ctx.locals.pyLoadSystemSnippets(self.read_js_file(make_path('snippets.json')))
241 | ctx.locals.pyLoadCIU(self.read_js_file(make_path('caniuse.json')))
242 |
243 | if self._contrib:
244 | for k in self._contrib:
245 | ctx.locals[k] = self._contrib[k]
246 |
247 | self._ctx_inited = True
248 |
249 | # if not hasattr(PyV8.JSContext.current.locals, 'isEmmet'):
250 | # print('Enter Emmet context')
251 | # self._ctx.enter()
252 |
253 | if self._ctx_inited:
254 | with self._ctx as ctx:
255 | if self._should_load_extension:
256 | ctx.locals.pyResetUserData()
257 | self._should_load_extension = False
258 | self.load_extensions()
259 |
260 | if self._user_data:
261 | ctx.locals.pyLoadUserData(self._user_data)
262 | self._user_data = None
263 |
264 | return self._ctx
265 |
266 | def load_user_data(self, data):
267 | "Loads user data payload from JSON"
268 | self._user_data = data
269 | # self.js().locals.pyLoadUserData(data)
270 |
271 | def reset(self):
272 | "Resets JS execution context"
273 | if self._ctx:
274 | # self._ctx.leave()
275 | self._ctx = None
276 | try:
277 | PyV8.JSEngine.collect()
278 | gc.collect()
279 | except:
280 | pass
281 |
282 | self._should_load_extension = True
283 |
284 | def read_js_file(self, file_path, resolve_path=False):
285 | full_path = make_path(file_path) if resolve_path else file_path
286 | return self.reader(full_path, self._use_unicode)
287 |
288 | def eval(self, source):
289 | with self.js() as ctx:
290 | ctx.eval(source)
291 |
292 | def eval_js_file(self, file_path, resolve_path=True):
293 | with self.js() as ctx:
294 | ctx.eval(self.read_js_file(file_path, resolve_path), name=file_path, line=0, col=0)
295 |
--------------------------------------------------------------------------------
/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/python-wrapper.js:
--------------------------------------------------------------------------------
1 | var console = {
2 | log: function(msg) {
3 | log(msg);
4 | }
5 | };
6 |
7 | function _toArray(obj, ix) {
8 | // Some array-like objects coming from Python
9 | // cannot be converted with simple Array#slice call.
10 | ix = ix || 0;
11 | if ('length' in obj) {
12 | return Array.prototype.slice.call(obj, ix);
13 | }
14 |
15 | // Convert object manually
16 | var out = [];
17 | var keys = Object.keys(obj);
18 | for (var i = ix || 0, il = keys.length; i < il; i++) {
19 | out.push(obj[keys[i]]);
20 | }
21 | return out;
22 | }
23 |
24 | /**
25 | * Simple function alias to run Emmet action.
26 | * editorProxy
object should be defined
27 | * in concrete plugin implementation.
28 | */
29 | function pyRunAction(name) {
30 | return emmet.run(name, editorProxy);
31 | }
32 |
33 | function pyLoadSystemSnippets(data) {
34 | emmet.loadSystemSnippets(data);
35 | }
36 |
37 | function pyLoadCIU(data) {
38 | emmet.loadCIU(data);
39 | }
40 |
41 | function pyLoadUserData(data) {
42 | emmet.loadUserData(data);
43 | }
44 |
45 | function pyLoadExtensions(fileList) {
46 | emmet.loadExtensions(_toArray(fileList));
47 | }
48 |
49 | function pyResetUserData() {
50 | emmet.resetUserData();
51 | }
52 |
53 | emmet.file({
54 | _parseParams: function(args) {
55 | var params = {
56 | path: args[0],
57 | size: -1
58 | };
59 |
60 | args = _toArray(args, 1);
61 | params.callback = args.pop();
62 | if (args.length) {
63 | params.size = args[0];
64 | }
65 |
66 | return params;
67 | },
68 |
69 | read: function(path, size, callback) {
70 | var params = this._parseParams(arguments);
71 |
72 | try {
73 | pyFile.read(params.path, params.size, function(err, content) {
74 | if (err) {
75 | return params.callback(err, content);
76 | }
77 |
78 | content = _toArray(content || []).map(function(b) {
79 | return String.fromCharCode(b);
80 | }).join('');
81 | params.callback(null, content);
82 | });
83 | } catch(e) {
84 | params.callback(e);
85 | }
86 | },
87 |
88 | readText: function() {
89 | var params = this._parseParams(arguments);
90 | try {
91 | pyFile.read_text(params.path, params.size, params.callback);
92 | } catch(e) {
93 | params.callback(e);
94 | }
95 |
96 | },
97 |
98 | locateFile: function(editorFile, fileName) {
99 | return pyFile.locate_file(editorFile, fileName);
100 | },
101 |
102 | createPath: function(parent, fileName) {
103 | return pyFile.create_path(parent, fileName);
104 | },
105 |
106 | save: function(file, content) {
107 | return pyFile.save(file, content);
108 | },
109 |
110 | getExt: function(file) {
111 | var m = (file || '').match(/\.([\w\-]+)$/);
112 | return m ? m[1].toLowerCase() : '';
113 | }
114 | });
--------------------------------------------------------------------------------
/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)), '-sSL']
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/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])
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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": "css",
12 | "profile": "css",
13 | "snippets": {
14 | "@i": "@import url(|);",
15 | "@import": "@import url(|);",
16 | "@m": "@media ${1:screen} {\n\t|\n}",
17 | "@media": "@media ${1:screen} {\n\t|\n}",
18 | "@f": "@font-face {\n\tfont-family:|;\n\tsrc:url(|);\n}",
19 | "@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}",
20 |
21 | "@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}",
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:f": "display:flex;",
101 | "d:if": "display:inline-flex;",
102 | "d:i": "display:inline;",
103 | "d:ib": "display:inline-block;",
104 | "d:ib+": "display: inline-block;\n*display: inline;\n*zoom: 1;",
105 | "d:li": "display:list-item;",
106 | "d:ri": "display:run-in;",
107 | "d:cp": "display:compact;",
108 | "d:tb": "display:table;",
109 | "d:itb": "display:inline-table;",
110 | "d:tbcp": "display:table-caption;",
111 | "d:tbcl": "display:table-column;",
112 | "d:tbclg": "display:table-column-group;",
113 | "d:tbhg": "display:table-header-group;",
114 | "d:tbfg": "display:table-footer-group;",
115 | "d:tbr": "display:table-row;",
116 | "d:tbrg": "display:table-row-group;",
117 | "d:tbc": "display:table-cell;",
118 | "d:rb": "display:ruby;",
119 | "d:rbb": "display:ruby-base;",
120 | "d:rbbg": "display:ruby-base-group;",
121 | "d:rbt": "display:ruby-text;",
122 | "d:rbtg": "display:ruby-text-group;",
123 | "v": "visibility:${1:hidden};",
124 | "v:v": "visibility:visible;",
125 | "v:h": "visibility:hidden;",
126 | "v:c": "visibility:collapse;",
127 | "ov": "overflow:${1:hidden};",
128 | "ov:v": "overflow:visible;",
129 | "ov:h": "overflow:hidden;",
130 | "ov:s": "overflow:scroll;",
131 | "ov:a": "overflow:auto;",
132 | "ovx": "overflow-x:${1:hidden};",
133 | "ovx:v": "overflow-x:visible;",
134 | "ovx:h": "overflow-x:hidden;",
135 | "ovx:s": "overflow-x:scroll;",
136 | "ovx:a": "overflow-x:auto;",
137 | "ovy": "overflow-y:${1:hidden};",
138 | "ovy:v": "overflow-y:visible;",
139 | "ovy:h": "overflow-y:hidden;",
140 | "ovy:s": "overflow-y:scroll;",
141 | "ovy:a": "overflow-y:auto;",
142 | "ovs": "overflow-style:${1:scrollbar};",
143 | "ovs:a": "overflow-style:auto;",
144 | "ovs:s": "overflow-style:scrollbar;",
145 | "ovs:p": "overflow-style:panner;",
146 | "ovs:m": "overflow-style:move;",
147 | "ovs:mq": "overflow-style:marquee;",
148 | "zoo": "zoom:1;",
149 | "zm": "zoom:1;",
150 | "cp": "clip:|;",
151 | "cp:a": "clip:auto;",
152 | "cp:r": "clip:rect(${1:top} ${2:right} ${3:bottom} ${4:left});",
153 | "bxz": "box-sizing:${1:border-box};",
154 | "bxz:cb": "box-sizing:content-box;",
155 | "bxz:bb": "box-sizing:border-box;",
156 | "bxsh": "box-shadow:${1:inset }${2:hoff} ${3:voff} ${4:blur} ${5:color};",
157 | "bxsh:r": "box-shadow:${1:inset }${2:hoff} ${3:voff} ${4:blur} ${5:spread }rgb(${6:0}, ${7:0}, ${8:0});",
158 | "bxsh:ra": "box-shadow:${1:inset }${2:h} ${3:v} ${4:blur} ${5:spread }rgba(${6:0}, ${7:0}, ${8:0}, .${9:5});",
159 | "bxsh:n": "box-shadow:none;",
160 | "m": "margin:|;",
161 | "m:a": "margin:auto;",
162 | "mt": "margin-top:|;",
163 | "mt:a": "margin-top:auto;",
164 | "mr": "margin-right:|;",
165 | "mr:a": "margin-right:auto;",
166 | "mb": "margin-bottom:|;",
167 | "mb:a": "margin-bottom:auto;",
168 | "ml": "margin-left:|;",
169 | "ml:a": "margin-left:auto;",
170 | "p": "padding:|;",
171 | "pt": "padding-top:|;",
172 | "pr": "padding-right:|;",
173 | "pb": "padding-bottom:|;",
174 | "pl": "padding-left:|;",
175 | "w": "width:|;",
176 | "w:a": "width:auto;",
177 | "h": "height:|;",
178 | "h:a": "height:auto;",
179 | "maw": "max-width:|;",
180 | "maw:n": "max-width:none;",
181 | "mah": "max-height:|;",
182 | "mah:n": "max-height:none;",
183 | "miw": "min-width:|;",
184 | "mih": "min-height:|;",
185 | "mar": "max-resolution:${1:res};",
186 | "mir": "min-resolution:${1:res};",
187 | "ori": "orientation:|;",
188 | "ori:l": "orientation:landscape;",
189 | "ori:p": "orientation:portrait;",
190 | "ol": "outline:|;",
191 | "ol:n": "outline:none;",
192 | "olo": "outline-offset:|;",
193 | "olw": "outline-width:|;",
194 | "olw:tn": "outline-width:thin;",
195 | "olw:m": "outline-width:medium;",
196 | "olw:tc": "outline-width:thick;",
197 | "ols": "outline-style:|;",
198 | "ols:n": "outline-style:none;",
199 | "ols:dt": "outline-style:dotted;",
200 | "ols:ds": "outline-style:dashed;",
201 | "ols:s": "outline-style:solid;",
202 | "ols:db": "outline-style:double;",
203 | "ols:g": "outline-style:groove;",
204 | "ols:r": "outline-style:ridge;",
205 | "ols:i": "outline-style:inset;",
206 | "ols:o": "outline-style:outset;",
207 | "olc": "outline-color:#${1:000};",
208 | "olc:i": "outline-color:invert;",
209 | "bfv": "backface-visibility:|;",
210 | "bfv:h": "backface-visibility:hidden;",
211 | "bfv:v": "backface-visibility:visible;",
212 | "bd": "border:|;",
213 | "bd+": "border:${1:1px} ${2:solid} ${3:#000};",
214 | "bd:n": "border:none;",
215 | "bdbk": "border-break:${1:close};",
216 | "bdbk:c": "border-break:close;",
217 | "bdcl": "border-collapse:|;",
218 | "bdcl:c": "border-collapse:collapse;",
219 | "bdcl:s": "border-collapse:separate;",
220 | "bdc": "border-color:#${1:000};",
221 | "bdc:t": "border-color:transparent;",
222 | "bdi": "border-image:url(|);",
223 | "bdi:n": "border-image:none;",
224 | "bdti": "border-top-image:url(|);",
225 | "bdti:n": "border-top-image:none;",
226 | "bdri": "border-right-image:url(|);",
227 | "bdri:n": "border-right-image:none;",
228 | "bdbi": "border-bottom-image:url(|);",
229 | "bdbi:n": "border-bottom-image:none;",
230 | "bdli": "border-left-image:url(|);",
231 | "bdli:n": "border-left-image:none;",
232 | "bdci": "border-corner-image:url(|);",
233 | "bdci:n": "border-corner-image:none;",
234 | "bdci:c": "border-corner-image:continue;",
235 | "bdtli": "border-top-left-image:url(|);",
236 | "bdtli:n": "border-top-left-image:none;",
237 | "bdtli:c": "border-top-left-image:continue;",
238 | "bdtri": "border-top-right-image:url(|);",
239 | "bdtri:n": "border-top-right-image:none;",
240 | "bdtri:c": "border-top-right-image:continue;",
241 | "bdbri": "border-bottom-right-image:url(|);",
242 | "bdbri:n": "border-bottom-right-image:none;",
243 | "bdbri:c": "border-bottom-right-image:continue;",
244 | "bdbli": "border-bottom-left-image:url(|);",
245 | "bdbli:n": "border-bottom-left-image:none;",
246 | "bdbli:c": "border-bottom-left-image:continue;",
247 | "bdf": "border-fit:${1:repeat};",
248 | "bdf:c": "border-fit:clip;",
249 | "bdf:r": "border-fit:repeat;",
250 | "bdf:sc": "border-fit:scale;",
251 | "bdf:st": "border-fit:stretch;",
252 | "bdf:ow": "border-fit:overwrite;",
253 | "bdf:of": "border-fit:overflow;",
254 | "bdf:sp": "border-fit:space;",
255 | "bdlen": "border-length:|;",
256 | "bdlen:a": "border-length:auto;",
257 | "bdsp": "border-spacing:|;",
258 | "bds": "border-style:|;",
259 | "bds:n": "border-style:none;",
260 | "bds:h": "border-style:hidden;",
261 | "bds:dt": "border-style:dotted;",
262 | "bds:ds": "border-style:dashed;",
263 | "bds:s": "border-style:solid;",
264 | "bds:db": "border-style:double;",
265 | "bds:dtds": "border-style:dot-dash;",
266 | "bds:dtdtds": "border-style:dot-dot-dash;",
267 | "bds:w": "border-style:wave;",
268 | "bds:g": "border-style:groove;",
269 | "bds:r": "border-style:ridge;",
270 | "bds:i": "border-style:inset;",
271 | "bds:o": "border-style:outset;",
272 | "bdw": "border-width:|;",
273 | "bdtw": "border-top-width:|;",
274 | "bdrw": "border-right-width:|;",
275 | "bdbw": "border-bottom-width:|;",
276 | "bdlw": "border-left-width:|;",
277 | "bdt": "border-top:|;",
278 | "bt": "border-top:|;",
279 | "bdt+": "border-top:${1:1px} ${2:solid} ${3:#000};",
280 | "bdt:n": "border-top:none;",
281 | "bdts": "border-top-style:|;",
282 | "bdts:n": "border-top-style:none;",
283 | "bdtc": "border-top-color:#${1:000};",
284 | "bdtc:t": "border-top-color:transparent;",
285 | "bdr": "border-right:|;",
286 | "br": "border-right:|;",
287 | "bdr+": "border-right:${1:1px} ${2:solid} ${3:#000};",
288 | "bdr:n": "border-right:none;",
289 | "bdrst": "border-right-style:|;",
290 | "bdrst:n": "border-right-style:none;",
291 | "bdrc": "border-right-color:#${1:000};",
292 | "bdrc:t": "border-right-color:transparent;",
293 | "bdb": "border-bottom:|;",
294 | "bb": "border-bottom:|;",
295 | "bdb+": "border-bottom:${1:1px} ${2:solid} ${3:#000};",
296 | "bdb:n": "border-bottom:none;",
297 | "bdbs": "border-bottom-style:|;",
298 | "bdbs:n": "border-bottom-style:none;",
299 | "bdbc": "border-bottom-color:#${1:000};",
300 | "bdbc:t": "border-bottom-color:transparent;",
301 | "bdl": "border-left:|;",
302 | "bl": "border-left:|;",
303 | "bdl+": "border-left:${1:1px} ${2:solid} ${3:#000};",
304 | "bdl:n": "border-left:none;",
305 | "bdls": "border-left-style:|;",
306 | "bdls:n": "border-left-style:none;",
307 | "bdlc": "border-left-color:#${1:000};",
308 | "bdlc:t": "border-left-color:transparent;",
309 | "bdrs": "border-radius:|;",
310 | "bdtrrs": "border-top-right-radius:|;",
311 | "bdtlrs": "border-top-left-radius:|;",
312 | "bdbrrs": "border-bottom-right-radius:|;",
313 | "bdblrs": "border-bottom-left-radius:|;",
314 | "bg": "background:#${1:000};",
315 | "bg+": "background:${1:#fff} url(${2}) ${3:0} ${4:0} ${5:no-repeat};",
316 | "bg:n": "background:none;",
317 | "bg:ie": "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='${1:x}.png',sizingMethod='${2:crop}');",
318 | "bgc": "background-color:#${1:fff};",
319 | "bgc:t": "background-color:transparent;",
320 | "bgi": "background-image:url(|);",
321 | "bgi:n": "background-image:none;",
322 | "bgr": "background-repeat:|;",
323 | "bgr:n": "background-repeat:no-repeat;",
324 | "bgr:x": "background-repeat:repeat-x;",
325 | "bgr:y": "background-repeat:repeat-y;",
326 | "bgr:sp": "background-repeat:space;",
327 | "bgr:rd": "background-repeat:round;",
328 | "bga": "background-attachment:|;",
329 | "bga:f": "background-attachment:fixed;",
330 | "bga:s": "background-attachment:scroll;",
331 | "bgp": "background-position:${1:0} ${2:0};",
332 | "bgpx": "background-position-x:|;",
333 | "bgpy": "background-position-y:|;",
334 | "bgbk": "background-break:|;",
335 | "bgbk:bb": "background-break:bounding-box;",
336 | "bgbk:eb": "background-break:each-box;",
337 | "bgbk:c": "background-break:continuous;",
338 | "bgcp": "background-clip:${1:padding-box};",
339 | "bgcp:bb": "background-clip:border-box;",
340 | "bgcp:pb": "background-clip:padding-box;",
341 | "bgcp:cb": "background-clip:content-box;",
342 | "bgcp:nc": "background-clip:no-clip;",
343 | "bgo": "background-origin:|;",
344 | "bgo:pb": "background-origin:padding-box;",
345 | "bgo:bb": "background-origin:border-box;",
346 | "bgo:cb": "background-origin:content-box;",
347 | "bgsz": "background-size:|;",
348 | "bgsz:a": "background-size:auto;",
349 | "bgsz:ct": "background-size:contain;",
350 | "bgsz:cv": "background-size:cover;",
351 | "c": "color:#${1:000};",
352 | "c:r": "color:rgb(${1:0}, ${2:0}, ${3:0});",
353 | "c:ra": "color:rgba(${1:0}, ${2:0}, ${3:0}, .${4:5});",
354 | "cm": "/* |${child} */",
355 | "cnt": "content:'|';",
356 | "cnt:n": "content:normal;",
357 | "cnt:oq": "content:open-quote;",
358 | "cnt:noq": "content:no-open-quote;",
359 | "cnt:cq": "content:close-quote;",
360 | "cnt:ncq": "content:no-close-quote;",
361 | "cnt:a": "content:attr(|);",
362 | "cnt:c": "content:counter(|);",
363 | "cnt:cs": "content:counters(|);",
364 |
365 | "tbl": "table-layout:|;",
366 | "tbl:a": "table-layout:auto;",
367 | "tbl:f": "table-layout:fixed;",
368 | "cps": "caption-side:|;",
369 | "cps:t": "caption-side:top;",
370 | "cps:b": "caption-side:bottom;",
371 | "ec": "empty-cells:|;",
372 | "ec:s": "empty-cells:show;",
373 | "ec:h": "empty-cells:hide;",
374 | "lis": "list-style:|;",
375 | "lis:n": "list-style:none;",
376 | "lisp": "list-style-position:|;",
377 | "lisp:i": "list-style-position:inside;",
378 | "lisp:o": "list-style-position:outside;",
379 | "list": "list-style-type:|;",
380 | "list:n": "list-style-type:none;",
381 | "list:d": "list-style-type:disc;",
382 | "list:c": "list-style-type:circle;",
383 | "list:s": "list-style-type:square;",
384 | "list:dc": "list-style-type:decimal;",
385 | "list:dclz": "list-style-type:decimal-leading-zero;",
386 | "list:lr": "list-style-type:lower-roman;",
387 | "list:ur": "list-style-type:upper-roman;",
388 | "lisi": "list-style-image:|;",
389 | "lisi:n": "list-style-image:none;",
390 | "q": "quotes:|;",
391 | "q:n": "quotes:none;",
392 | "q:ru": "quotes:'\\00AB' '\\00BB' '\\201E' '\\201C';",
393 | "q:en": "quotes:'\\201C' '\\201D' '\\2018' '\\2019';",
394 | "ct": "content:|;",
395 | "ct:n": "content:normal;",
396 | "ct:oq": "content:open-quote;",
397 | "ct:noq": "content:no-open-quote;",
398 | "ct:cq": "content:close-quote;",
399 | "ct:ncq": "content:no-close-quote;",
400 | "ct:a": "content:attr(|);",
401 | "ct:c": "content:counter(|);",
402 | "ct:cs": "content:counters(|);",
403 | "coi": "counter-increment:|;",
404 | "cor": "counter-reset:|;",
405 | "va": "vertical-align:${1:top};",
406 | "va:sup": "vertical-align:super;",
407 | "va:t": "vertical-align:top;",
408 | "va:tt": "vertical-align:text-top;",
409 | "va:m": "vertical-align:middle;",
410 | "va:bl": "vertical-align:baseline;",
411 | "va:b": "vertical-align:bottom;",
412 | "va:tb": "vertical-align:text-bottom;",
413 | "va:sub": "vertical-align:sub;",
414 | "ta": "text-align:${1:left};",
415 | "ta:l": "text-align:left;",
416 | "ta:c": "text-align:center;",
417 | "ta:r": "text-align:right;",
418 | "ta:j": "text-align:justify;",
419 | "ta-lst": "text-align-last:|;",
420 | "tal:a": "text-align-last:auto;",
421 | "tal:l": "text-align-last:left;",
422 | "tal:c": "text-align-last:center;",
423 | "tal:r": "text-align-last:right;",
424 | "td": "text-decoration:${1:none};",
425 | "td:n": "text-decoration:none;",
426 | "td:u": "text-decoration:underline;",
427 | "td:o": "text-decoration:overline;",
428 | "td:l": "text-decoration:line-through;",
429 | "te": "text-emphasis:|;",
430 | "te:n": "text-emphasis:none;",
431 | "te:ac": "text-emphasis:accent;",
432 | "te:dt": "text-emphasis:dot;",
433 | "te:c": "text-emphasis:circle;",
434 | "te:ds": "text-emphasis:disc;",
435 | "te:b": "text-emphasis:before;",
436 | "te:a": "text-emphasis:after;",
437 | "th": "text-height:|;",
438 | "th:a": "text-height:auto;",
439 | "th:f": "text-height:font-size;",
440 | "th:t": "text-height:text-size;",
441 | "th:m": "text-height:max-size;",
442 | "ti": "text-indent:|;",
443 | "ti:-": "text-indent:-9999px;",
444 | "tj": "text-justify:|;",
445 | "tj:a": "text-justify:auto;",
446 | "tj:iw": "text-justify:inter-word;",
447 | "tj:ii": "text-justify:inter-ideograph;",
448 | "tj:ic": "text-justify:inter-cluster;",
449 | "tj:d": "text-justify:distribute;",
450 | "tj:k": "text-justify:kashida;",
451 | "tj:t": "text-justify:tibetan;",
452 | "tov": "text-overflow:${ellipsis};",
453 | "tov:e": "text-overflow:ellipsis;",
454 | "tov:c": "text-overflow:clip;",
455 | "to": "text-outline:|;",
456 | "to+": "text-outline:${1:0} ${2:0} ${3:#000};",
457 | "to:n": "text-outline:none;",
458 | "tr": "text-replace:|;",
459 | "tr:n": "text-replace:none;",
460 | "tt": "text-transform:${1:uppercase};",
461 | "tt:n": "text-transform:none;",
462 | "tt:c": "text-transform:capitalize;",
463 | "tt:u": "text-transform:uppercase;",
464 | "tt:l": "text-transform:lowercase;",
465 | "tw": "text-wrap:|;",
466 | "tw:n": "text-wrap:normal;",
467 | "tw:no": "text-wrap:none;",
468 | "tw:u": "text-wrap:unrestricted;",
469 | "tw:s": "text-wrap:suppress;",
470 | "tsh": "text-shadow:${1:hoff} ${2:voff} ${3:blur} ${4:#000};",
471 | "tsh:r": "text-shadow:${1:h} ${2:v} ${3:blur} rgb(${4:0}, ${5:0}, ${6:0});",
472 | "tsh:ra": "text-shadow:${1:h} ${2:v} ${3:blur} rgba(${4:0}, ${5:0}, ${6:0}, .${7:5});",
473 | "tsh+": "text-shadow:${1:0} ${2:0} ${3:0} ${4:#000};",
474 | "tsh:n": "text-shadow:none;",
475 | "trf": "transform:|;",
476 | "trf:skx": "transform: skewX(${1:angle});",
477 | "trf:sky": "transform: skewY(${1:angle});",
478 | "trf:sc": "transform: scale(${1:x}, ${2:y});",
479 | "trf:scx": "transform: scaleX(${1:x});",
480 | "trf:scy": "transform: scaleY(${1:y});",
481 | "trf:scz": "transform: scaleZ(${1:z});",
482 | "trf:sc3": "transform: scale3d(${1:x}, ${2:y}, ${3:z});",
483 | "trf:r": "transform: rotate(${1:angle});",
484 | "trf:rx": "transform: rotateX(${1:angle});",
485 | "trf:ry": "transform: rotateY(${1:angle});",
486 | "trf:rz": "transform: rotateZ(${1:angle});",
487 | "trf:t": "transform: translate(${1:x}, ${2:y});",
488 | "trf:tx": "transform: translateX(${1:x});",
489 | "trf:ty": "transform: translateY(${1:y});",
490 | "trf:tz": "transform: translateZ(${1:z});",
491 | "trf:t3": "transform: translate3d(${1:tx}, ${2:ty}, ${3:tz});",
492 | "trfo": "transform-origin:|;",
493 | "trfs": "transform-style:${1:preserve-3d};",
494 | "trs": "transition:${1:prop} ${2:time};",
495 | "trsde": "transition-delay:${1:time};",
496 | "trsdu": "transition-duration:${1:time};",
497 | "trsp": "transition-property:${1:prop};",
498 | "trstf": "transition-timing-function:${1:tfunc};",
499 | "lh": "line-height:|;",
500 | "whs": "white-space:|;",
501 | "whs:n": "white-space:normal;",
502 | "whs:p": "white-space:pre;",
503 | "whs:nw": "white-space:nowrap;",
504 | "whs:pw": "white-space:pre-wrap;",
505 | "whs:pl": "white-space:pre-line;",
506 | "whsc": "white-space-collapse:|;",
507 | "whsc:n": "white-space-collapse:normal;",
508 | "whsc:k": "white-space-collapse:keep-all;",
509 | "whsc:l": "white-space-collapse:loose;",
510 | "whsc:bs": "white-space-collapse:break-strict;",
511 | "whsc:ba": "white-space-collapse:break-all;",
512 | "wob": "word-break:|;",
513 | "wob:n": "word-break:normal;",
514 | "wob:k": "word-break:keep-all;",
515 | "wob:ba": "word-break:break-all;",
516 | "wos": "word-spacing:|;",
517 | "wow": "word-wrap:|;",
518 | "wow:nm": "word-wrap:normal;",
519 | "wow:n": "word-wrap:none;",
520 | "wow:u": "word-wrap:unrestricted;",
521 | "wow:s": "word-wrap:suppress;",
522 | "wow:b": "word-wrap:break-word;",
523 | "wm": "writing-mode:${1:lr-tb};",
524 | "wm:lrt": "writing-mode:lr-tb;",
525 | "wm:lrb": "writing-mode:lr-bt;",
526 | "wm:rlt": "writing-mode:rl-tb;",
527 | "wm:rlb": "writing-mode:rl-bt;",
528 | "wm:tbr": "writing-mode:tb-rl;",
529 | "wm:tbl": "writing-mode:tb-lr;",
530 | "wm:btl": "writing-mode:bt-lr;",
531 | "wm:btr": "writing-mode:bt-rl;",
532 | "lts": "letter-spacing:|;",
533 | "lts-n": "letter-spacing:normal;",
534 | "f": "font:|;",
535 | "f+": "font:${1:1em} ${2:Arial,sans-serif};",
536 | "fw": "font-weight:|;",
537 | "fw:n": "font-weight:normal;",
538 | "fw:b": "font-weight:bold;",
539 | "fw:br": "font-weight:bolder;",
540 | "fw:lr": "font-weight:lighter;",
541 | "fs": "font-style:${italic};",
542 | "fs:n": "font-style:normal;",
543 | "fs:i": "font-style:italic;",
544 | "fs:o": "font-style:oblique;",
545 | "fv": "font-variant:|;",
546 | "fv:n": "font-variant:normal;",
547 | "fv:sc": "font-variant:small-caps;",
548 | "fz": "font-size:|;",
549 | "fza": "font-size-adjust:|;",
550 | "fza:n": "font-size-adjust:none;",
551 | "ff": "font-family:|;",
552 | "ff:s": "font-family:serif;",
553 | "ff:ss": "font-family:sans-serif;",
554 | "ff:c": "font-family:cursive;",
555 | "ff:f": "font-family:fantasy;",
556 | "ff:m": "font-family:monospace;",
557 | "ff:a": "font-family: Arial, \"Helvetica Neue\", Helvetica, sans-serif;",
558 | "ff:t": "font-family: \"Times New Roman\", Times, Baskerville, Georgia, serif;",
559 | "ff:v": "font-family: Verdana, Geneva, sans-serif;",
560 | "fef": "font-effect:|;",
561 | "fef:n": "font-effect:none;",
562 | "fef:eg": "font-effect:engrave;",
563 | "fef:eb": "font-effect:emboss;",
564 | "fef:o": "font-effect:outline;",
565 | "fem": "font-emphasize:|;",
566 | "femp": "font-emphasize-position:|;",
567 | "femp:b": "font-emphasize-position:before;",
568 | "femp:a": "font-emphasize-position:after;",
569 | "fems": "font-emphasize-style:|;",
570 | "fems:n": "font-emphasize-style:none;",
571 | "fems:ac": "font-emphasize-style:accent;",
572 | "fems:dt": "font-emphasize-style:dot;",
573 | "fems:c": "font-emphasize-style:circle;",
574 | "fems:ds": "font-emphasize-style:disc;",
575 | "fsm": "font-smooth:|;",
576 | "fsm:a": "font-smooth:auto;",
577 | "fsm:n": "font-smooth:never;",
578 | "fsm:aw": "font-smooth:always;",
579 | "fst": "font-stretch:|;",
580 | "fst:n": "font-stretch:normal;",
581 | "fst:uc": "font-stretch:ultra-condensed;",
582 | "fst:ec": "font-stretch:extra-condensed;",
583 | "fst:c": "font-stretch:condensed;",
584 | "fst:sc": "font-stretch:semi-condensed;",
585 | "fst:se": "font-stretch:semi-expanded;",
586 | "fst:e": "font-stretch:expanded;",
587 | "fst:ee": "font-stretch:extra-expanded;",
588 | "fst:ue": "font-stretch:ultra-expanded;",
589 | "op": "opacity:|;",
590 | "op+": "opacity: $1;\nfilter: alpha(opacity=$2);",
591 | "op:ie": "filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100);",
592 | "op:ms": "-ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)';",
593 | "rsz": "resize:|;",
594 | "rsz:n": "resize:none;",
595 | "rsz:b": "resize:both;",
596 | "rsz:h": "resize:horizontal;",
597 | "rsz:v": "resize:vertical;",
598 | "cur": "cursor:${pointer};",
599 | "cur:a": "cursor:auto;",
600 | "cur:d": "cursor:default;",
601 | "cur:c": "cursor:crosshair;",
602 | "cur:ha": "cursor:hand;",
603 | "cur:he": "cursor:help;",
604 | "cur:m": "cursor:move;",
605 | "cur:p": "cursor:pointer;",
606 | "cur:t": "cursor:text;",
607 | "fxd": "flex-direction:|;",
608 | "fxd:r": "flex-direction:row;",
609 | "fxd:rr": "flex-direction:row-reverse;",
610 | "fxd:c": "flex-direction:column;",
611 | "fxd:cr": "flex-direction:column-reverse;",
612 | "fxw": "flex-wrap: |;",
613 | "fxw:n": "flex-wrap:nowrap;",
614 | "fxw:w": "flex-wrap:wrap;",
615 | "fxw:wr": "flex-wrap:wrap-reverse;",
616 | "fxf": "flex-flow:|;",
617 | "jc": "justify-content:|;",
618 | "jc:fs": "justify-content:flex-start;",
619 | "jc:fe": "justify-content:flex-end;",
620 | "jc:c": "justify-content:center;",
621 | "jc:sb": "justify-content:space-between;",
622 | "jc:sa": "justify-content:space-around;",
623 | "ai": "align-items:|;",
624 | "ai:fs": "align-items:flex-start;",
625 | "ai:fe": "align-items:flex-end;",
626 | "ai:c": "align-items:center;",
627 | "ai:b": "align-items:baseline;",
628 | "ai:s": "align-items:stretch;",
629 | "ac": "align-content:|;",
630 | "ac:fs": "align-content:flex-start;",
631 | "ac:fe": "align-content:flex-end;",
632 | "ac:c": "align-content:center;",
633 | "ac:sb": "align-content:space-between;",
634 | "ac:sa": "align-content:space-around;",
635 | "ac:s": "align-content:stretch;",
636 | "ord": "order:|;",
637 | "fxg": "flex-grow:|;",
638 | "fxsh": "flex-shrink:|;",
639 | "fxb": "flex-basis:|;",
640 | "fx": "flex:|;",
641 | "as": "align-self:|;",
642 | "as:a": "align-self:auto;",
643 | "as:fs": "align-self:flex-start;",
644 | "as:fe": "align-self:flex-end;",
645 | "as:c": "align-self:center;",
646 | "as:b": "align-self:baseline;",
647 | "as:s": "align-self:stretch;",
648 | "pgbb": "page-break-before:|;",
649 | "pgbb:au": "page-break-before:auto;",
650 | "pgbb:al": "page-break-before:always;",
651 | "pgbb:l": "page-break-before:left;",
652 | "pgbb:r": "page-break-before:right;",
653 | "pgbi": "page-break-inside:|;",
654 | "pgbi:au": "page-break-inside:auto;",
655 | "pgbi:av": "page-break-inside:avoid;",
656 | "pgba": "page-break-after:|;",
657 | "pgba:au": "page-break-after:auto;",
658 | "pgba:al": "page-break-after:always;",
659 | "pgba:l": "page-break-after:left;",
660 | "pgba:r": "page-break-after:right;",
661 | "orp": "orphans:|;",
662 | "us": "user-select:${none};",
663 | "wid": "widows:|;",
664 | "wfsm": "-webkit-font-smoothing:${antialiased};",
665 | "wfsm:a": "-webkit-font-smoothing:antialiased;",
666 | "wfsm:s": "-webkit-font-smoothing:subpixel-antialiased;",
667 | "wfsm:sa": "-webkit-font-smoothing:subpixel-antialiased;",
668 | "wfsm:n": "-webkit-font-smoothing:none;"
669 | }
670 | },
671 |
672 | "html": {
673 | "filters": "html",
674 | "profile": "html",
675 | "snippets": {
676 | "!!!": "",
677 | "!!!4t": "",
678 | "!!!4s": "",
679 | "!!!xt": "",
680 | "!!!xs": "",
681 | "!!!xxs": "",
682 |
683 | "c": "",
684 | "cc:ie6": "",
685 | "cc:ie": "",
686 | "cc:noie": "\n\t${child}|\n"
687 | },
688 |
689 | "abbreviations": {
690 | "!": "html:5",
691 | "a": "",
692 | "a:link": "",
693 | "a:mail": "",
694 | "abbr": "",
695 | "acr|acronym": "",
696 | "base": "",
697 | "basefont": "",
698 | "br": "
",
699 | "frame": "",
700 | "hr": "
",
701 | "bdo": "",
702 | "bdo:r": "",
703 | "bdo:l": "",
704 | "col": "",
705 | "link": "",
706 | "link:css": "",
707 | "link:print": "",
708 | "link:favicon": "",
709 | "link:touch": "",
710 | "link:rss": "",
711 | "link:atom": "",
712 | "link:im|link:import": "",
713 | "meta": "",
714 | "meta:utf": "",
715 | "meta:win": "",
716 | "meta:vp": "",
717 | "meta:compat": "",
718 | "style": "