├── .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: '' 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": "