feat(SublimeText2.WebPackages): cache packages

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

View File

@@ -0,0 +1,133 @@
# Changelog for Hayaku
## 1.3.3 <sup>2013.03.02</sup>
- **New setting:** `hayaku_CSS_syntax_quote_symbol` for used quote symbol ([#71][])
- **New setting:** `hayaku_CSS_syntax_url_quotes` for wrapping clipboarded links in urls with quotes ([#208][])
- Added support for `user-select` property ([#207][])
- Fixing values in abbreviations for `opacity` property ([#209][])
- Don't add units if the value could be unitless (like `line-height`), also `hayaku_CSS_units_for_unitless_numbers` setting for this ([#153][])
[#71]: https://github.com/hayaku/hayaku/issues/71
[#207]: https://github.com/hayaku/hayaku/issues/207
[#208]: https://github.com/hayaku/hayaku/issues/208
[#209]: https://github.com/hayaku/hayaku/issues/209
[#153]: https://github.com/hayaku/hayaku/issues/153
## 1.3.2 <sup>2013.02.27</sup>
- Fix the importing method for ST3 again, now should work from the `.sublime-package`.
## 1.3.1 <sup>2013.02.08</sup>
- Fix the importing method, now the plugin would work from PC in ST3 ([#205][])
[#205]: https://github.com/hayaku/hayaku/issues/205
## 1.3.0 <sup>2013.02.07</sup>
- **Support for [Sublime Text 3](http://www.sublimetext.com/3) ([#201][])**
- Fixed inline comment setting for OS X and Linux ([#200][], thanks to @freshmango)
- Disable inline commenting in functions and quotes ([#203][])
[#200]: https://github.com/hayaku/hayaku/issues/200
[#201]: https://github.com/hayaku/hayaku/issues/201
[#203]: https://github.com/hayaku/hayaku/issues/203
## 1.2.1 <sup>2012.12.23</sup>
- Hotfixing automatic new line after expand's bug ([#190][])
[#190]: https://github.com/hayaku/hayaku/issues/190
## 1.2.0 <sup>2012.12.23</sup>
- **New feature:** [basic clipboard defaults](https://github.com/hayaku/hayaku/#clipboard-defaults) (for colors and urls) ([#180][])
- **New setting:** optional [automatic new line after expand](https://github.com/hayaku/hayaku/#automatic-new-line-after-expand) (not by default) ([#123][])
- Better handling of multiple carets in snippets ([#188][])
- Fixed an issue with color postexpands and their default values ([#189][])
- Restructured the repo, so it would be better updatable and maintainable.
[#123]: https://github.com/hayaku/hayaku/issues/123
[#180]: https://github.com/hayaku/hayaku/issues/180
[#188]: https://github.com/hayaku/hayaku/issues/188
[#189]: https://github.com/hayaku/hayaku/issues/189
## 1.1.1 <sup>2012.12.16</sup>
- Fixed bug with more than 99 completion parts in a snippet (`display: inline` affected) ([#182][])
- Fixed bug with wrong position of color postexpand in prefixed clusters ([#183][])
- Better handling for values that can be parts of other values in postexpands ([#184][])
- Overall refactoring of the postexpands, not completed, but already fixed some minor issues and the code is almost ready for moving the postexpands to the dictionaries.
[#182]: https://github.com/hayaku/hayaku/issues/182
[#183]: https://github.com/hayaku/hayaku/issues/183
[#184]: https://github.com/hayaku/hayaku/issues/184
## 1.1.0 <sup>2012.12.10</sup>
- **Changed default setting**: now when you use the block expand it expands to the more common code style.
- **New feature:** added [importance to the postexpand](https://github.com/hayaku/hayaku/#postexpand-for-importance) ([#156][])
- **New setting:** disabling the [inline comment](https://github.com/hayaku/hayaku/#inline-comments) shortcut for CSS ([#169][])
- **New setting:** [handling the case of expanded colors](https://github.com/hayaku/hayaku/#colors-case) ([#177][])
- **New setting:** [handling the length of expanded colors](https://github.com/hayaku/hayaku/#shorthand-colors) ([#50][])
- Moved the default syntax settings to code, so no restart needed for them to apply ([#160][])
- Don't indent prefixed properties when using Stylus or Sass ([#176][])
[#169]: https://github.com/hayaku/hayaku/issues/169
[#156]: https://github.com/hayaku/hayaku/issues/156
[#160]: https://github.com/hayaku/hayaku/issues/160
[#176]: https://github.com/hayaku/hayaku/issues/176
[#177]: https://github.com/hayaku/hayaku/issues/177
[#50]: https://github.com/hayaku/hayaku/issues/50
## 1.0.4 <sup>2012.11.29</sup>
- Fixed jumping to newline with proper indentation by tab/enter in non-CSS syntaxes ([#166][])
- Fixed the occasional removing of the content right to the point where the tab/enter happened ([#168][])
- Allowing expand to work on a line with other properties (“single line” code style) ([#170][])
- Some minor refactoring.
[#166]: https://github.com/hayaku/hayaku/issues/166
[#168]: https://github.com/hayaku/hayaku/issues/168
[#170]: https://github.com/hayaku/hayaku/issues/170
## 1.0.3 <sup>2012.11.27</sup>
- **New feature:** Added a way to write [color abbreviations for rgba](https://github.com/hayaku/hayaku/#rgba-values), like `cF.5` to `color: rgba(255,255,255,.5)` etc. ([#66][])
- Removed colons from default Stylus syntax ([#161][])
- Fixed possible leaks of default values ([#164][])
[#66]: https://github.com/hayaku/hayaku/issues/66
[#161]: https://github.com/hayaku/hayaku/issues/161
[#164]: https://github.com/hayaku/hayaku/issues/164
## 1.0.2 <sup>2012.11.26</sup>
- Tab didn't work at the empty line after the last statement in Stylus/Sass ([#146][])
- Enhanced the behaviour of the `enter`/`tab` at the end of the prefixed cluster ([#52][])
- Fixed strange bugs in expands, when the `<dimension>` token could show up ([#155][])
- Upgraded expand code block action (you can press `enter` inside the brackets — in this position: `{|}` — to create a block), so it is not hardcoded now ([#159][])
- Added an option to disable postexpand ([#152][])
[#146]: https://github.com/hayaku/hayaku/issues/146
[#52]: https://github.com/hayaku/hayaku/issues/52
[#155]: https://github.com/hayaku/hayaku/issues/155
[#159]: https://github.com/hayaku/hayaku/issues/159
[#152]: https://github.com/hayaku/hayaku/issues/152
## 1.0.1 <sup>2012.11.23</sup>
- Updated installation instructions ([#147][])
[#147]: https://github.com/hayaku/hayaku/issues/147
## 1.0.0 <sup>2012.11.22</sup>
- Initial public alpha

View File

@@ -0,0 +1,92 @@
[
// Main Hayaku context
{
"keys": ["tab"],
"command": "hayaku",
"context": [{"key": "hayaku_css_context"}]
}
// tab or enter at the end of a line should jump to a correct line
, {
"keys": ["tab"],
"command": "hayaku_add_line",
"context": [
{"key": "hayaku_add_line"},
{"key": "hayaku_at_css"},
{"key": "hayaku_single_caret"}
]
}
, {
"keys": ["enter"],
"command": "hayaku_add_line",
"context": [
{"key": "hayaku_add_line"},
{"key": "hayaku_at_css"},
{"key": "hayaku_single_caret"}
]
}
// Temporary fix for the semicolon inserting (bad bad whitespace after)
, { "keys": [":"], "command": "insert_snippet", "args": {"contents": ":$0"}, "context":
[
{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
{ "key": "hayaku_at_css" },
{ "key": "hayaku_single_caret" },
{ "key": "following_text", "operator": "regex_contains", "operand": "^(?:\t| |\\}|$)", "match_all": true }
]
}
// Temporary inline comment for CSS (would be replaced by a more useful command)
, {
"keys": ["/","/"],
"command": "insert_snippet",
"args": {"contents": "/* ${1} */$0"},
"context": [
{ "key": "selector", "operator": "equal", "operand": "source.css -comment.block.css -punctuation.section.function.css -string.quoted", "match_all": true },
{ "key": "setting.hayaku_CSS_disable_inline_comment", "operator": "not_equal", "operand": true }
]
}
// Command for inserting CSS code block
, {
"keys": ["ctrl+enter"],
"command": "hayaku_add_code_block",
"context": [
{"key": "hayaku_add_code_block"},
{"key": "hayaku_at_css"},
{"key": "hayaku_single_caret"}
]
}
// Command for inserting right indent for code block by enter in braces
, {
"keys": ["enter"],
"command": "hayaku_expand_code_block",
"context": [
{"key": "hayaku_at_css"},
{"key": "hayaku_single_caret"},
{"key": "preceding_text", "operator": "regex_match", "operand": ".*\\{$" },
{"key": "following_text", "operator": "regex_match", "operand": "^\\}" }
]
},
// Commands to jump out of multiple selections in CSS
{
"keys": ["up"],
"command": "clear_fields",
"context": [
{"key": "has_next_field", "operator": "equal", "operand": true},
{"key": "hayaku_at_css"},
{"key": "hayaku_going_up"}
]
},
{
"keys": ["down"],
"command": "clear_fields",
"context": [
{"key": "has_next_field", "operator": "equal", "operand": true},
{"key": "hayaku_at_css"},
{"key": "hayaku_going_down"}
]
}
]

View File

@@ -0,0 +1,92 @@
[
// Main Hayaku context
{
"keys": ["tab"],
"command": "hayaku",
"context": [{"key": "hayaku_css_context"}]
}
// tab or enter at the end of a line should jump to a correct line
, {
"keys": ["tab"],
"command": "hayaku_add_line",
"context": [
{"key": "hayaku_add_line"},
{"key": "hayaku_at_css"},
{"key": "hayaku_single_caret"}
]
}
, {
"keys": ["enter"],
"command": "hayaku_add_line",
"context": [
{"key": "hayaku_add_line"},
{"key": "hayaku_at_css"},
{"key": "hayaku_single_caret"}
]
}
// Temporary fix for the semicolon inserting (bad bad whitespace after)
, { "keys": [":"], "command": "insert_snippet", "args": {"contents": ":$0"}, "context":
[
{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
{ "key": "hayaku_at_css" },
{ "key": "hayaku_single_caret" },
{ "key": "following_text", "operator": "regex_contains", "operand": "^(?:\t| |\\}|$)", "match_all": true }
]
}
// Temporary inline comment for CSS (would be replaced by a more useful command)
, {
"keys": ["/","/"],
"command": "insert_snippet",
"args": {"contents": "/* ${1} */$0"},
"context": [
{ "key": "selector", "operator": "equal", "operand": "source.css -comment.block.css -punctuation.section.function.css -string.quoted", "match_all": true },
{ "key": "setting.hayaku_CSS_disable_inline_comment", "operator": "not_equal", "operand": true }
]
}
// Command for inserting CSS code block
, {
"keys": ["super+enter"],
"command": "hayaku_add_code_block",
"context": [
{"key": "hayaku_add_code_block"},
{"key": "hayaku_at_css"},
{"key": "hayaku_single_caret"}
]
}
// Command for inserting right indent for code block by enter in braces
, {
"keys": ["enter"],
"command": "hayaku_expand_code_block",
"context": [
{"key": "hayaku_at_css"},
{"key": "hayaku_single_caret"},
{"key": "preceding_text", "operator": "regex_match", "operand": ".*\\{$" },
{"key": "following_text", "operator": "regex_match", "operand": "^\\}" }
]
},
// Commands to jump out of multiple selections in CSS
{
"keys": ["up"],
"command": "clear_fields",
"context": [
{"key": "has_next_field", "operator": "equal", "operand": true},
{"key": "hayaku_at_css"},
{"key": "hayaku_going_up"}
]
},
{
"keys": ["down"],
"command": "clear_fields",
"context": [
{"key": "has_next_field", "operator": "equal", "operand": true},
{"key": "hayaku_at_css"},
{"key": "hayaku_going_down"}
]
}
]

View File

@@ -0,0 +1,92 @@
[
// Main Hayaku context
{
"keys": ["tab"],
"command": "hayaku",
"context": [{"key": "hayaku_css_context"}]
}
// tab or enter at the end of a line should jump to a correct line
, {
"keys": ["tab"],
"command": "hayaku_add_line",
"context": [
{"key": "hayaku_add_line"},
{"key": "hayaku_at_css"},
{"key": "hayaku_single_caret"}
]
}
, {
"keys": ["enter"],
"command": "hayaku_add_line",
"context": [
{"key": "hayaku_add_line"},
{"key": "hayaku_at_css"},
{"key": "hayaku_single_caret"}
]
}
// Temporary fix for the semicolon inserting (bad bad whitespace after)
, { "keys": [":"], "command": "insert_snippet", "args": {"contents": ":$0"}, "context":
[
{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
{ "key": "hayaku_at_css" },
{ "key": "hayaku_single_caret" },
{ "key": "following_text", "operator": "regex_contains", "operand": "^(?:\t| |\\}|$)", "match_all": true }
]
}
// Temporary inline comment for CSS (would be replaced by a more useful command)
, {
"keys": ["/","/"],
"command": "insert_snippet",
"args": {"contents": "/* ${1} */$0"},
"context": [
{ "key": "selector", "operator": "equal", "operand": "source.css -comment.block.css -punctuation.section.function.css -string.quoted", "match_all": true },
{ "key": "setting.hayaku_CSS_disable_inline_comment", "operator": "not_equal", "operand": true }
]
}
// Command for inserting CSS code block
, {
"keys": ["ctrl+enter"],
"command": "hayaku_add_code_block",
"context": [
{"key": "hayaku_add_code_block"},
{"key": "hayaku_at_css"},
{"key": "hayaku_single_caret"}
]
}
// Command for inserting right indent for code block by enter in braces
, {
"keys": ["enter"],
"command": "hayaku_expand_code_block",
"context": [
{"key": "hayaku_at_css"},
{"key": "hayaku_single_caret"},
{"key": "preceding_text", "operator": "regex_match", "operand": ".*\\{$" },
{"key": "following_text", "operator": "regex_match", "operand": "^\\}" }
]
},
// Commands to jump out of multiple selections in CSS
{
"keys": ["up"],
"command": "clear_fields",
"context": [
{"key": "has_next_field", "operator": "equal", "operand": true},
{"key": "hayaku_at_css"},
{"key": "hayaku_going_up"}
]
},
{
"keys": ["down"],
"command": "clear_fields",
"context": [
{"key": "has_next_field", "operator": "equal", "operand": true},
{"key": "hayaku_at_css"},
{"key": "hayaku_going_down"}
]
}
]

View File

@@ -0,0 +1,22 @@
(The MIT License)
Copyright (c) 2012 Roman Komarov <kizmarh@yandex.ru>, Sergey Mezentsev <thebits@yandex.ru>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,54 @@
{
"disable_tab_abbreviations_for_scopes": "css,less,sass,scss,stylus",
"auto_complete_selector": "source -comment -source.css -source.sass -source.scss -source.stylus",
// Unused settings, work in progress
// Code Style
// "hayaku_CSS_colors_use_names": false, // Would expand `#000` to `black` when `true`
// "hayaku_CSS_numbers_leading_zero": true, // Would expand `.3` to `0.3` when `true`
// "hayaku_CSS_default_unit": "px", // `w10` -> `width: 10px`
// "hayaku_CSS_default_unit_decimal": "em" // `w.5` -> `width: 10em`
// DO NOT EDIT
"hayaku_css_dict_snippets": {
"<length>": {
".": "em"
},
"<percentage>": {
"percentage": "%"
},
"<image>": {
"linear-gradient()" : "linear-gradient(${1:top}, ${2:#000} ${3:0}, ${4:#FFF} ${5:100%})",
"repeating-linear-gradient()" : "repeating-linear-gradient(${1:center}, ${2:#000} ${3:0}, ${4:#FFF} ${5:25%})",
"radial-gradient()" : "radial-gradient(${1:center}, ${2:#000} ${3:0}, ${4:#FFF} ${5:100%})",
"repeating-radial-gradient()" : "repeating-radial-gradient(${1:center}, ${2:#000} ${3:0}, ${4:#FFF} ${5:25%})"
},
// "box-shadow": {
// "inset" : "inset ${1:0} ${2:0} ${3:0} ${4:rgba(0,0,0,.5)}",
// "[${1:0} ${2:0} ${3:0} ${4:rgba(0,0,0,.5)}]"
// },
// "text-shadow": {
// "[${1:0} ${2:0} ${3:0} ${4:rgba(255,255,255,.5)}]"
// },
// "content": {
// "[\"$1\"]"
// },
"quotes": {
"english": "\"\\201C\" \"\\201D\" \"\\2018\" \"\\2019\"",
"russian": "\"\\00AB\" \"\\00BB\" \"\\201E\" \"\\201C\""
},
"font": {
"arial" : "1em/1.4 \"Helvetica Neue\", Arial, sans-serif",
"verdana" : "86%/1.4 Verdana, sans-serif"
}
// "transition" : {
// "[${1:all} ${2:linear} ${3:.3s}]"
// }
}
}

View File

@@ -0,0 +1,404 @@
# Hayaku <sup>[1.3.3](https://github.com/hayaku/hayaku/blob/master/CHANGELOG.md)</sup>
Hayaku is a bundle of useful scripts aiming for rapid front-end web development.
The main aim of Hayaku is to create the fastest way to write and maintain CSS code in an editor.
# Table of Contents
1. [Install Hayaku for Sublime Text](#install-hayaku-for-sublime-text)
2. [Features](#features)
- [Smart CSS Abbreviations](#smart-css-abbreviations)
- [Fuzzy CSS property abbreviations](#fuzzy-css-property-abbreviations)
- [Smart CSS values abbreviations](#smart-css-values-abbreviations)
- [Numeric values in abbreviations](#numeric-values-in-abbreviations)
- [Color values in abbreviations](#color-values-in-abbreviations) with [RGBA values](#rgba-values)
- [Importance modifier](#importance-modifier)
- [Default values](#Default-values)
- [Clipboard defaults](#clipoard-defaults)
- [Postexpands](#postexpands)
- [Simple property postexpands](#simple-property-postexpands)
- [Postexpands for units](#postexpands-for-units)
- [Postexpands for colors](#postexpands-for-colors)
- [Postexpand for importance](#postexpand-for-importance)
- [Creating new CSS rule blocks](#creating-new-css-rule-blocks)
- [Inline comments](#inline-comments)
<br/><br/>
3. [Settings and Preferences](#settings-and-preferences)
- [Autoguessing the code style](#autoguessing-the-code-style)
- [Single code style](#single-code-style)
- [Automatic new line after expand](#automatic-new-line-after-expand)
- [Quotes and URLs](#quotes-and-urls)
- [Units for unitless values](#units-for-unitless-values)
- [Prefixes options](#prefixes-options)
- [The aligning for the prefixes](#the-aligning-for-the-prefixes)
- [Using only specific prefixes](#using-only-specific-prefixes)
- [Options for colors](#options-for-colors)
- [Colors' case](#colors-case)
- [Shorthand colors](#shorthand-colors)
<br/><br/>
4. [Using Hayaku with CSS Preprocessors](#using-hayaku-with-css-preprocessors)
5. [License and copyrights](#license-and-copyrights)
# Install Hayaku for [Sublime Text](http://www.sublimetext.com/2)
Right now Hayaku is available only for Sublime Text (even for third version!), but when it would be complete, we would port it to some other editors.
#### Using [Package Control](http://wbond.net/sublime_packages/package_control):
1. Run `Package Control: Install Package` command
2. Search for `Hayaku - tools for writing CSS faster` (`Hayaku` should be enough) and wait for it to be installed
3. Restart Sublime Text (required to make default settings for different syntaxes to work)
#### Or manually, using git:
Clone repository into Packages directory (can be found using `Preferences: Browse Packages` command in Sublime Text)
``` sh
git clone git://github.com/hayaku/hayaku.git
```
And then restart Sublime Text.
# Features
## Smart CSS Abbreviations
Hayaku is not your average snippet engine. Most of the CSS snippets to date are static — you need to remember all the abbreviations if you want to use them.
Hayaku offers a better and faster way: you don't need to remember anything, you can just try to write the shortest abbreviation for a thing you want to write in CSS — and Hayaku would try to guess it when you press `tab`.
There are a lot of things Hayaku can do in abbeviations, here are some of them:
### Fuzzy CSS property abbreviations
This is the first basic thing: Hayaku don't have any premade snippets for CSS, it have a dictionary with a lot of CSS properties, so when you write some abrakadabra, it tries to parse it and guess what you meant. For most properties those abbreviations could be rather short, but you're not sticked to them: you can write as much letters for a property as you wish.
So, writing `w`, `wi` or `wid` would give you `width`. And don't forget about the fuzzy part: `wdt` and `wdth` would work too.
Sometimes you would guess that some abbreviations must become other things, but in most cases all the variants have some logic beyound. `b` could be expanded to `background` or `border`, but expanded to `bottom` instead — it's becouse all the “sides” values are abbreviated to just one letter: **l**eft, **r**eft, **t**op, so **b**ottom goes by this path.
However, if you feel that some abbreviation just need to be not that is expands to, feel free to [fill up an issue](https://github.com/hayaku/hayaku/issues/new).
### Smart CSS values abbreviations
Here comes the second basic thing of Hayaku, the awesome one. You can expand abbreviations for the property+value parts, but you don't need to use any delimiters in those abbreviations! That's right — you can just write something like `por` and get `position: relative`!
This works also fuzzy, so to get `position: relative` you could use any number of letters: `pore`, `posrel`, `pstnrltv` etc. Also, if you want, you can still use a delimiter — just add a colon between the property and value and get the same result. So, if you want to stick to Zen style, use it — `pos:r` would work as intended. And `p:r` would work too — while `pr` would expand to `padding-right`, adding delimiter could help by removing ambiguity — padding can't have any values containing `r`, so hayaku falls to `position`.
### Numeric values in abbreviations
Hayaku understands a lot of ways of writing numeric abbreviations.
- You can just write a number after abbreviation to treat it as a value: `w10` would expand to `width: 10px` (see? automatic pixels!).
- Negative numbers supported too: `ml-10` would expand to `margin-left: -10px`.
- If you'd write a dot somewhere in abbreviation, Hayaku would guess what you need `em`s, so `w10.5` would expand to `width: 10.5em`.
- There are some abbreviations for some units, like `percents` for `%`, or `.` for em, so `100p` would expand to `100%` and `10.` to `10em`.
- All other units are supported, `h2pt` would expand to `height:2pt` and so on. Fuzzy guess is there too: if you'd want `vh` you could write just `w10h` and get `width: 10vh`.
### Color values in abbreviations
Actually, you can not only expand strings and numbers, you can expand even colors using abbreviations! You can use different ways to achieve that (as anything in Hayaku), so just look at those examples:
- `c0` → `color: #000`
- `cF` → `color: #FFF` (use uppercase to tell Hayaku it's a color)
- `cFA` → `color: #FAFAFA`
- `c#fa` → `color: #FAFAFA` (no need in uppercase if you use `#`)
And, of course, this works everywhere you would expect colors to work, so `brc0` would expand to `border-right-color: #000;`
#### RGBA values
There is also a way to expand `rgba` values for colors — you can either use rgba's alpha after the dot, either use hexadecimal alpha after the full color, if you'd like. This would look like this:
- `c0.5` → `color: rgba(0,0,0,.5)`
- `cF.2` → `color: rgba(255,255,255,.2)`
- `cABCD` → `color: rgba(170,187,204,0.87)`
- `cABC80` → `color: rgba(170,187,204,0.5)`
You can also write just the dot and get the placeholder on the `alpha` part of the `rgba`:
- `cF00.` → `color: rgba(255,0,0,.[5])`
### Importance modifier
A nice little feature: add `!` after abbreviation and get ` !important` at the end. Not that importance is something you would want to use everyday, but still.
`dn!` would give you `display:none !important;`, yeah.
### Automatic vendor prefixes
If you need some vendor prefixes, Hayaku could provide them!
`bra1.5` would expand to this:
``` CSS
-webkit-border-radius: 1.5em;
border-radius: 1.5em;
```
Right now there are no prefixes for values (like gradients etc.) but someday they'd be there.
### Default values
If you'd write something that is only a property (as Hayaku would guess), Hayaku would insert a snippet with some default value already selected for you, so you could start writing your own value to replace it or to press `tab` again to keep it and move forward. So, writing `w` would actually expand to `width: [100%]` (braces mean that this value is selected by default).
### Clipboard defaults
Aside from the normal defaults, Hayaku would try to use your clipboard for getting the value from it as the default value.
Right now it's available for colors and images urls:
- If you'd have color in hexadecimal, rgb(a) or hsl(a) in your clipboard, Hayaku would use it as a default shown value. That would work even is the value is hashless, so if you've copied `808080` from anywhere, then on expanding `c` you would get `color: #[808080]`.
- If you'd have an image url in your clipboard (even relative, Hayaku would look at extension), you'd have it added as default values along inside an `url()`. Also, see [quotes and URLs](#quotes-and-urls) settings on how to adjust the quoting of the inserted url if you want.
#### Configure clipboard defaults
Hayaku offers a setting to set up the behavior of the Clipboard defaults: `hayaku_CSS_clipboard_defaults`. It is an array of the value types that Hayaku could accept as the defaults. So, to disable all the clipboard defaults you could use this setting:
``` JSON
{
"hayaku_CSS_clipboard_defaults": [""]
}
```
## Postexpands
“Postexpands” is a nice Hayaku's feature, that allows you to expand only the property at first and then use instant autocomplete for the values of numbers.
That must be said is that postexpand is a bit different from the usual abbreviation expands — it don't have any fuzzy search inside, so only the first letters matter. However, as you'd use it you would see that it is still a powerfull feature.
### Simple property postexpands
The simplest postexpand feature is autocomplete for the string values of different properties.
If you'd expand some property like `po` to `position: |;`, then you could start writing any of it's values and get they expanded right after the cursor. So, writing `a` would give you `position: a|bsolute;`.
### Postexpands for units
Another postexpand feature would allow you to firstly expand the property that can have numeric values, like `width` and then write only the digits and let Hayaku place the corresponding units automatically.
So, when you expand, for example, `w` to `width: |;`, you'd get different options:
- write any iteger like `10` and you'd get `width: 10|px;`
- write any float like `1.5` and you'd get `width: 1.5|em;`
- write an integer and them `e`, so you'd get `width: 10e|m;`
- if the value have any string values, you can also use them: writing `a` would give you `width: a|uto;`
Negative numbers could still be used and if you'd like any other unit, you could just write it down, the autocompleted units won't bother you.
### Postexpands for colors
As you can use shortcuts to colors in abbreviations, you could also write the color values after expanding only the property. The basics are the same: `color: |;` + `F` would give you `color: #F|FF;`, and so on. You can use or don't use the hash symbol.
Another somewhat obscure (but helpful) feature is postexpand for `rgba` colors. This is triggered by writing the comma after decimal value. There is also a shortcut to the alpha value.
- `color: 255,|` would transform to `color: rgba(255,|255,255,1);`
- `color: 255,.|` would transform to `color: rgba(255,255,255,.|5);`
There are a lot of things we could improve there, so stay tuned.
### Postexpand for importance
If you'd like to make some value important, you could just write the first symbols of `!important` keyword and Hayaku would autocomplete it for you.
### Disabling postexpands
If you'd wish to disable postexpands at all for some reason, you could use this setting for this: `"hayaku_CSS_disable_postexpand": true`
## Creating new CSS rule blocks
In Hayaku there is a simple but powerful feature: when you wrote a selector, you could just press `CMD+Enter` to get a block for writing CSS here.
## Inline comments
Another little helper: write `//` in CSS to have it expanded to `/* | */` (where the pipe is a caret placement).
If you'd wish to disable inline comments, you could use this setting: `"hayaku_CSS_disable_inline_comment": true`
*This feature is in development, we plan on adding a lot of things to make commenting fun.*
# Settings and Preferences
Hayaku have **a lot** of different configurable options, both for your code style and for different features you'd wish to use.
## Autoguessing the code style
The easiest way to set the basic settings for your codestyle, is to use `hayaku_CSS_syntax_autoguess` option:
``` JSON
{
"hayaku_CSS_syntax_autoguess": [
" selector { ",
" property: value; ",
" } "
]
}
```
There you can use any whitespaces between the predefined keywords and they would be used by Hayaku. A few notes regarding this setting:
- You should use the newline symbol `\n` or multiple array items, because JSON don't support multiline well.
- For your convenience you can use any leading of trailing spaces. Trailing spaces would be stripped at all, leading spaces would be stripped as if there weren't spaces at the start of the selector.
Maybe someday there'd be a _real_ autoguessing, that would read your open stylesheet and find what better suits it, but not today.
## Single code style
If you don't want to use autoguessing, then you could define single options one by one. This would also be helpful if you'd want to redefine only some of the code styling settings in other project or syntax.
Here is a JSON with all the available single code styling options:
``` JSON
{
"hayaku_CSS_whitespace_after_colon": " ",
"hayaku_CSS_whitespace_block_start_before": " ",
"hayaku_CSS_whitespace_block_start_after": "\n\t",
"hayaku_CSS_whitespace_block_end_before": "\n\t",
"hayaku_CSS_whitespace_block_end_after": ""
}
```
The names speak for themselves there.
The important thing is that the single code style settings always override the autoguessed one.
## Automatic new line after expand
That's somewhat experimental feature, that is disabled by default. To enable it use this setting:
``` JSON
{
"hayaku_CSS_newline_after_expand": true
}
```
With this setting you could save a bit more time, cause Hayaku would add a new line after you expand your abbreviations. The only downside is that you'll need to delete a line when you finish with the selector or when you're inserting something between existing lines.
## Quotes and URLs
By default Hayaku uses double quotes for different CSS stuff (like `content: ""`). You can change this by setting this:
``` JSON
{
"hayaku_CSS_syntax_quote_symbol": "'"
}
```
Also, by default the image urls wouldn't have quotes in CSS-like syntaxes and would have them in Sass or Stylus, you can override this automatic behaviour by setting `hayaku_CSS_syntax_url_quotes` setting to `true` or `false`.
## Units for unitless values
By default Hayaku won't add `em` or `px` after values for properties like `line-height`. If you're not using unit less values for those properties, you could enable them like this:
``` JSON
{
"hayaku_CSS_units_for_unitless_numbers": true
}
```
## Prefixes options
If you don't want to use any prefixes at all (as if you're using some mixins for it in Stylus, or use prefix-free), you can disable them with that option:
``` JSON
{
"hayaku_CSS_prefixes_disable": true
}
```
### The aligning for the prefixes
By default Hayaku aligns expanded prefixed properties in this nice way:
``` CSS
.foo {
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
-o-transform: rotate(45deg);
transform: rotate(45deg);
}
```
This way it's easier to spot changes to a single prefixed property and to use multiline edit on them.
However, if you'd want to expand such properties left aligned, set
``` JSON
{
"hayaku_CSS_prefixes_align": false
}
```
### Using only specific prefixes
This is not something that you would use often, but if you'd need, you could use only prefixes for browsers you want. There are two settigns for this:
``` JSON
{
"hayaku_CSS_prefixes_only": ["webkit","moz","o"],
"hayaku_CSS_prefixes_no_unprefixed": true
}
```
- `hayaku_CSS_prefixes_only` is an array of the prefixes you'd want to use **only**. In the upper example I excuded `ms` prefix, so if you'd use meta to emulate all IE versions to IE7 for example, then you could remove `ms` prefix, so your CSS would be a bit cleaner.
- when `hayaku_CSS_prefixes_no_unprefixed` is set to `True`, such prefixed clusters won't contain the official unprefixed variant.
Right now there is no easy way to adjust prefixes per property, but it would be there in a near feature, so stay tuned!
## Options for colors
Note that those settings would work for every pre-set and expanded colors, like the default color values, but they won't work for postexpands due to their mechanics.
### Colors' case
You can tell Hayaku if you prefer `lowercase` or `uppercase` for color values, so it would change the case while expanding abbreviations like `c#f`, `cF` etc.
The default value is `uppercase`, so `c#f` would become `color: #FFF`. If you'd like to change that to `lowercase`, you can set it this way:
``` JSON
{
"hayaku_CSS_colors_case": "lowercase"
}
```
And if you'd like it to leave the color as is, you could use value `initial`.
### Shorthand colors
By default Hayaku shortens the colous, so if there could be `#FFFFFF` expanded, Hayaku would make it `#FFF`.
However, if you wish, you can redefine this behavior using this setting:
``` JSON
{
"hayaku_CSS_colors_length": "long"
}
```
That would make `cF` to be expanded into `color: #FFFFFF`.
# Using Hayaku with CSS Preprocessors
“Hey! I don't need to write CSS faster — I use Preprocessors!” you could say. But, well, you would still need to write all those extra symbols, so abbreviations would fit preprocessors well. And as Hayaku is highly customizable, you could use it with any preprocessor: Sass, Less, Stylus etc.
Right now only basic things are available, but in feature you could expand different mixins and functions too, so just wait for it.
- - -
And this is just the start, there would be a lot of other nice features, so still tuned and follow the [official bundle's twitter](http://twitter.com/#!/hayakubundle)!
# License and copyrights
This software is released under the terms of the [MIT license](https://github.com/hayaku/hayaku/blob/master/LICENSE).

View File

@@ -0,0 +1,159 @@
#!/usr/bin/python
import re
import sublime
import sublime_plugin
# __all__ = [
# 'HayakuAddCodeBlockCommand',
# 'HayakuExpandCodeBlockCommand',
# ]
# Guessing the codestyle 1 2 3 4 5 6 7 8 9
GUESS_REGEX = re.compile(r'selector(\s*)(\{)?(\s*)property(:)?(\s*)value(;)?(\s*)(\})?(\s*)', re.IGNORECASE)
def get_hayaku_options(self):
settings = self.view.settings()
options = {}
match = {}
# Autoguessing the options
if settings.get("hayaku_CSS_syntax_autoguess"):
autoguess = settings.get("hayaku_CSS_syntax_autoguess")
offset = len(autoguess[0]) - len(autoguess[0].lstrip())
autoguess = [ s[offset:].rstrip() for s in autoguess]
match = GUESS_REGEX.search('\n'.join(autoguess))
# Helper to set an option got from multiple sources
def get_setting(setting, fallback, match_group = False):
if match_group and match:
fallback = match.group(match_group)
single_setting = False
if settings.has("hayaku_" + setting):
single_setting = settings.get("hayaku_" + setting, fallback)
options[setting] = single_setting or fallback
# Some hardcode for different scopes
# (could this be defined better?)
scope_name = self.view.scope_name(self.view.sel()[0].a)
is_sass = sublime.score_selector(scope_name, 'source.sass') > 0
is_stylus = sublime.score_selector(scope_name, 'source.stylus') > 0
disable_braces = is_stylus or is_sass
if is_stylus and match and match.group(2) and match.group(8):
disable_braces = False
disable_colons = is_stylus
if match and match.group(4):
disable_colons = False
disable_semicolons = is_stylus or is_sass
if is_stylus and match and match.group(6):
disable_semicolons = False
# Calling helper, getting all the needed options
get_setting("CSS_whitespace_block_start_before", " ", 1 )
get_setting("CSS_whitespace_block_start_after", "\n\t", 3 )
get_setting("CSS_whitespace_block_end_before", "\n", 7 )
get_setting("CSS_whitespace_block_end_after", "", 9 )
get_setting("CSS_whitespace_after_colon", " ", 5 )
get_setting("CSS_newline_after_expand", False)
get_setting("CSS_syntax_no_curly_braces", disable_braces )
get_setting("CSS_syntax_no_colons", disable_colons )
get_setting("CSS_syntax_no_semicolons", disable_semicolons )
get_setting("CSS_syntax_url_quotes", (is_stylus or is_sass) )
get_setting("CSS_syntax_quote_symbol", "\"" ) # or "'"
get_setting("CSS_prefixes_disable", False )
get_setting("CSS_prefixes_align", not (is_stylus or is_sass) )
get_setting("CSS_prefixes_only", [] )
get_setting("CSS_prefixes_no_unprefixed", False )
get_setting("CSS_disable_postexpand", False )
get_setting("CSS_units_for_unitless_numbers", False )
get_setting("CSS_colors_case", "uppercase" ) # or "lowercase" or "initial"
get_setting("CSS_colors_length", "short" ) # or "long" or "initial"
get_setting("CSS_clipboard_defaults", ["colors","images"] )
return options
def hayaku_get_block_snippet(options, inside = False):
start_before = options["CSS_whitespace_block_start_before"]
start_after = options["CSS_whitespace_block_start_after"]
end_before = options["CSS_whitespace_block_end_before"]
end_after = options["CSS_whitespace_block_end_after"]
opening_brace = "{"
closing_brace = "}"
if options["CSS_syntax_no_curly_braces"]:
opening_brace = ""
closing_brace = ""
start_after = ""
end_after = ""
if inside:
opening_brace = ""
closing_brace = ""
start_before = ""
end_after = ""
return ''.join([
start_before
, opening_brace
, start_after
, "$0"
, end_before
, closing_brace
, end_after
])
# Command
class HayakuExpandCodeBlockCommand(sublime_plugin.TextCommand):
def run(self, edit):
# TODO: consume the braces and whitespaces around and inside
self.view.run_command("insert_snippet", {"contents": hayaku_get_block_snippet(get_hayaku_options(self),True)})
class HayakuAddCodeBlockCommand(sublime_plugin.TextCommand):
def run(self, edit):
result = '/* OVERRIDE ME */'
# Determine the limits for place searching
regions = self.view.sel()
region = regions[0]
line = self.view.line(region)
stop_point = self.view.find('[}]\s*',line.begin())
if stop_point is not None and not (-1, -1):
end = stop_point.end()
else:
end = self.view.find('[^}]*',line.begin()).end()
where_to_search = self.view.substr(
sublime.Region(
line.begin(),
end
)
)
options = get_hayaku_options(self)
# Insert a code block if we must
found_insert_position = re.search('^([^}{]*?[^;,}{\s])\s*(?=\n|$)',where_to_search)
if found_insert_position is not None:
self.view.sel().clear()
self.view.sel().add(sublime.Region(len(found_insert_position.group(1)) + line.begin(), len(found_insert_position.group(1)) + line.begin()))
result = hayaku_get_block_snippet(options)
else:
# Place a caret + create a new line otherwise
# FIXME: the newline is not perfectly inserted. Must rethink it so there wouldn't
# be replacement of all whitespaces and would be better insertion handling
found_insert_rule = re.search('^(([^}]*?[^;]?)\s*)(?=\})',where_to_search)
if found_insert_rule:
self.view.sel().clear()
self.view.sel().add(sublime.Region(len(found_insert_rule.group(2)) + line.begin(), len(found_insert_rule.group(1)) + line.begin()))
result = ''.join([
options["CSS_whitespace_block_start_after"]
, "$0"
, options["CSS_whitespace_block_end_before"]
])
self.view.run_command("insert_snippet", {"contents": result})

View File

@@ -0,0 +1,141 @@
#!/usr/bin/python
import re
import sublime
import sublime_plugin
REGEX_WHITESPACES = re.compile(r'^\s*$')
class HayakuSingleCaretContext(sublime_plugin.EventListener):
def on_query_context(self, view, key, *args):
if key != "hayaku_single_caret":
return None
# Multiple blocks inserting doesn't make sense
if len(view.sel()) > 1:
return None
# TODO: understand selection, but don't replace it on code block inserting
if not view.sel()[0].empty():
return None
return True
class HayakuAtCssContext(sublime_plugin.EventListener):
def on_query_context(self, view, key, *args):
if key != "hayaku_at_css":
return None
# Looking for the scope
if not view.score_selector(view.sel()[0].begin(),'source.css, source.stylus, source.sass, source.scss'):
return None
return True
class HayakuAddCodeBlockContext(sublime_plugin.EventListener):
def on_query_context(self, view, key, *args):
if key != "hayaku_add_code_block":
return None
# Determining the left and the right parts
region = view.sel()[0]
line = view.line(region)
left_part = view.substr(sublime.Region(line.begin(), region.begin()))
right_part = view.substr(sublime.Region(region.begin(), line.end()))
# Check if the line isn't just a line of whitespace
if REGEX_WHITESPACES.search(left_part + right_part) is not None:
return None
# Simple check if the left part is ok
if left_part.find(';') != -1:
return None
# Simple check if the right part is ok
if right_part.find(';') != -1:
return None
return True
class HayakuAddLineContext(sublime_plugin.EventListener):
def on_query_context(self, view, key, *args):
if key != "hayaku_add_line":
return None
# Determining the left and the right parts
region = view.sel()[0]
line = view.line(region)
left_part = view.substr(sublime.Region(line.begin(), region.begin()))
right_part = view.substr(sublime.Region(region.begin(), line.end()))
# Simple check if the left part is ok
if re.search(';\s*$|[^\s;\{] [^;\{]+$',left_part) is None:
return None
# Simple check if the right part is ok
if re.search('^\s*\}?$',right_part) is None:
return None
return True
class HayakuStyleContext(sublime_plugin.EventListener):
def on_query_context(self, view, key, *args):
if key != "hayaku_css_context":
return None
regions = view.sel()
# We won't do anything for multiple carets for now
if len(regions) > 1:
return None
region = regions[0]
# We don't do anything for selection for now
if not region.empty():
return None
# Looking for the scope
# TODO: Make it expandable in HTML's attributes (+ left/right fixes)
if view.score_selector(region.begin(),'source.css -meta.selector.css, source.stylus, source.sass, source.scss') == 0:
return None
# Determining the left and the right parts
line = view.line(region)
left_part = view.substr(sublime.Region(line.begin(), region.begin()))
right_part = view.substr(sublime.Region(region.begin(),line.end()))
# Simple check if the left part is ok
# 1. Caret is not straight after semicolon, slash or plus sign
# 2. We're not at the empty line
# 3. There were no property/value like entities before caret
# 1 2 3
if re.search('[;\s\/\+]$|^$|[^\s;\{] [^;\{]+$',left_part) is not None:
return None
# Simple check if the right part is ok
# 1. The next symbol after caret is not space or curly brace
# 2. There could be only full property+value part afterwards
# 1 2
if re.search('^[^\s\}]|^\s[^:\}]+[;\}]',right_part) is not None:
return None
return True
# Context-commands to jump out of multiple selections in snippets
class HayakuGoingUpContext(sublime_plugin.EventListener):
def on_query_context(self, view, key, *args):
if key != "hayaku_going_up":
return None
if len(view.sel()) > 1:
region = view.sel()[0]
view.sel().clear()
view.sel().add(region)
return None
class HayakuGoingDownContext(sublime_plugin.EventListener):
def on_query_context(self, view, key, *args):
if key != "hayaku_going_down":
return None
if len(view.sel()) > 1:
region = view.sel()[1]
view.sel().clear()
view.sel().add(region)
return None

View File

@@ -0,0 +1,109 @@
# -*- coding: utf-8 -*-
# (c) 2012 Sergey Mezentsev
import string
from itertools import chain, product, starmap
def parse_dict_json(raw_dict):
result_dict = {}
valuable = (i for i in raw_dict if 'name' in i and 'values' in i)
def strip(s):
return string.strip(s) if hasattr(string, 'strip') else s.strip()
for i in valuable:
name, values, default = i['name'], i['values'], i.get('default')
names = name if isinstance(name, list) else map(strip, name.split(','))
for n in names:
assert n not in result_dict
val = { 'values': values }
if default is not None:
val['default'] = default
if 'prefixes' in i:
val['prefixes'] = i['prefixes']
if 'no-unprefixed-property' in i:
val['no-unprefixed-property'] = i['no-unprefixed-property']
else:
assert 'no-unprefixed-property' not in i
result_dict[n] = val
return result_dict
get_css_dict_cache = None
def get_css_dict():
global get_css_dict_cache
if get_css_dict_cache is not None:
return get_css_dict_cache
else:
CSS_DICT_DIR = 'dictionaries'
CSS_DICT_FILENAME = 'hayaku_CSS_dictionary.json'
DICT_KEY = 'hayaku_CSS_dictionary'
import json
import os
try:
import sublime
css_dict = sublime.load_settings(CSS_DICT_FILENAME).get(DICT_KEY)
if css_dict is None:
import zipfile
zf = zipfile.ZipFile(os.path.dirname(os.path.realpath(__file__)))
f = zf.read('{0}/{1}'.format(CSS_DICT_DIR, CSS_DICT_FILENAME))
css_dict = json.loads(f.decode())[DICT_KEY]
except ImportError:
css_dict_path = os.path.join(CSS_DICT_DIR, CSS_DICT_FILENAME)
css_dict = json.load(open(css_dict_path))[DICT_KEY]
assert css_dict is not None
get_css_dict_cache = parse_dict_json(css_dict)
return get_css_dict_cache
def css_defaults(name, css_dict):
"""Находит первое значение по-умолчанию
background -> #FFF
color -> #FFF
content -> ""
"""
cur = css_dict.get(name) or css_dict.get(name[1:-1])
if cur is None:
return None
default = cur.get('default')
if default is not None:
return default
for v in cur['values']:
if v.startswith('<') and v.endswith('>'):
ret = css_defaults(v, css_dict)
if ret is not None:
return ret
def css_flat(name, values=None, css_dict=None):
"""Все значения у свойства (по порядку)
left -> [u'auto', u'<dimension>', u'<number>', u'<length>', u'.em', u'.ex',
u'.vw', u'.vh', u'.vmin', u'.vmax', u'.ch', u'.rem', u'.px', u'.cm',
u'.mm', u'.in', u'.pt', u'.pc', u'<percentage>', u'.%']
"""
cur = css_dict.get(name) or css_dict.get(name[1:-1])
if values is None:
values = []
if cur is None:
return values
for value in cur['values']:
values.append(value)
if value.startswith('<') and value.endswith('>'):
values = css_flat(value, values, css_dict)
return values
def css_flat_list(name, css_dict):
"""Возвращает список кортежей (свойство, возможное значение)
left -> [(left, auto), (left, <integer>), (left, .px)...]
"""
return list(product((name,), css_flat(name, css_dict=get_css_dict())))
def get_flat_css():
return list(chain.from_iterable(starmap(css_flat_list, ((i, get_css_dict()) for i in get_css_dict()))))

View File

@@ -0,0 +1,533 @@
{ "hayaku_CSS_dictionary":
[
{"_Description": [
" Dictionary for Hayaku Core, http://hayakubundle.com ",
" Copyright © 2011 Roman Komarov, http://kizu.ru/en/ ",
" Licensed under MIT license ",
" Got inspiration from ",
" Zen Coding snippets for CSS ",
" Copyright © 20082011 Vadim Makeev, http://pepelsbey.net ",
" Licensed under MIT license "
]
},{
"name": "<common>",
"values": [ "inherit" ],
"_comment": [
"Common values, that any single property can have",
"Must have lesser priority than any other value"
]
},{
"name": "<dimension>",
"values": [ "<length>", "<percentage>" ]
},{
"name": "<length>",
"values": [
".em", ".ex", ".vw", ".vh", ".vmin", ".vmax", ".ch", ".rem",
".px", ".cm", ".mm", ".in", ".pt", ".pc"
]
},{
"name": "<percentage>",
"values": [ ".%" ]
},{
"name": "<angle>",
"values": [ ".deg", ".grad", ".rad", ".turn" ]
},{
"name": "<time>",
"values": [ ".s", ".ms" ]
},{
"name": "<url>",
"values": [ "url()" ]
},{
"name": "<attr>",
"values": [ "attr()" ]
},{
"name": "<image>",
"values": [ "linear-gradient()", "repeating-linear-gradient()", "radial-gradient()", "repeating-radial-gradient()" ],
"prefixes": [ "webkit", "moz", "ms", "o" ]
},{
"name": "<timing-function>",
"values": [ "ease", "linear", "ease-in", "ease-out", "ease-in-out", "cubic-bezier()" ]
},{
"name": "position",
"values": [ "static", "relative", "absolute", "fixed" ]
},{
"name": [ "top", "right", "bottom", "left" ],
"values": [ "auto", "<dimension>" ],
"default": "100%"
},{
"name": "z-index",
"values": [ "auto", "<integer>" ]
},{
"name": "float",
"values": [ "left", "right", "none" ]
},{
"name": "clear",
"values": [ "none", "left", "right", "both" ]
},{
"name": "display",
"values": [ "inline", "block", "inline-block", "list-item", "none", "table", "inline-table", "table-row-group", "table-header-group", "table-footer-group", "table-row", "table-cell", "table-column", "table-caption", "table-column-group", "run-in", "compact", "flexbox", "inline-flexbox" ]
},{
"name": "visibility",
"values": [ "visible", "hidden", "collapse" ]
},{
"name": [ "overflow" ],
"values": [ "visible", "hidden", "scroll", "auto" ]
},{
"name": [ "overflow-x", "overflow-y" ],
"values": [ "<overflow>" ],
"prefixes": [ "ms" ]
},{
"name": "clip",
"values": [ "auto", "rect()" ]
},{
"name": "box-sizing",
"values": [ "content-box", "border-box" ],
"prefixes": [ "webkit", "moz" ]
},{
"name": "-moz-box-sizing",
"values": [ "<box-sizing>", "padding-box" ]
},{
"name": "flex-direction",
"values": [ "lr", "rl", "tb", "bt", "inline", "inline-reverse", "block", "block-reverse" ]
},{
"name": "flex-order",
"values": [ "<integer>" ]
},{
"name": "flex-pack",
"values": [ "start", "end", "center", "justify" ]
},{
"name": "flex-align",
"values": [ "auto", "baseline" ]
},{
"name": "pointer-events",
"values": [ "none", "all" ]
},{
"name": [ "margin", "margin-top", "margin-right", "margin-bottom", "margin-left" ],
"values": [ "auto", "<dimension>" ]
},{
"name": "padding, padding-top, padding-right, padding-bottom, padding-left",
"values": [ "<dimension>" ]
},{
"name": "width, height, min-width, min-height",
"values": [ "auto", "<dimension>" ],
"default": "100%"
},{
"name": "max-height, max-width",
"values": [ "none", "<dimension>" ],
"default": "100%"
},{
"name": "outline-color",
"values": [ "invert", "<color>" ]
},{
"name": "outline-style",
"values": [ "<border-style>" ]
},{
"name": "outline-width",
"values": [ "<border-width>" ]
},{
"name": "outline-offset",
"values": [ "<length>" ]
},{
"name": "outline",
"values": [ "<outline-color>", "<_outline-style>", "<outline-width>" ]
},{
"name": "border-collapse",
"values": [ "collapse", "separate" ]
},{
"name": "border-spacing",
"values": [ "<length>" ]
},{
"name": [ "border-color", "border-top-color", "border-right-color", "border-bottom-color", "border-left-color" ],
"values": [ "<color>" ]
},{
"name": [ "border-style", "border-top-style", "border-right-style", "border-bottom-style", "border-left-style" ],
"values": [ "none", "hidden", "dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset" ]
},{
"name": [ "border-width", "border-top-width", "border-right-width", "border-bottom-width", "border-left-width" ],
"values": [ "thin", "medium", "thick", "<length>" ]
},{
"name": [ "border", "border-top", "border-right", "border-bottom", "border-left" ],
"values": [ "<border-color>", "<_border-style>", "<border-width>" ],
"default": "1px solid"
},{
"name": [ "border-radius", "border-top-right-radius", "border-top-left-radius", "border-bottom-left-radius", "border-bottom-right-radius" ],
"values": [ "<length>" ],
"prefixes": [ "webkit" ]
},{
"name": "<border-image-source>",
"values": [ "<url>", "none" ],
"prefixes": [ "webkit", "moz", "o" ]
},{
"name": "<border-image-slice>",
"values": [ "<number>", "<percentage>", "fill" ],
"prefixes": [ "webkit", "moz", "o" ]
},{
"name": "<border-image-width>",
"values": [ "<border-width>" ],
"prefixes": [ "webkit", "moz", "o" ]
},{
"name": "<border-image-outset>",
"values": [ "<length>", "<number>" ],
"prefixes": [ "webkit", "moz", "o" ]
},{
"name": "<border-image-repeat>",
"values": [ "stretch", "repeat", "round" ],
"prefixes": [ "webkit", "moz", "o" ]
},{
"name": "border-image",
"values": [ "<border-image-source>", "<border-image-slice>", "<border-image-width>", "<border-image-outset>", "<border-image-repeat>" ],
"prefixes": [ "webkit", "moz", "o" ]
},{
"name": "background-attachment",
"values": [ "scroll", "fixed", "local" ]
},{
"name": "background-color",
"values": [ "<color>" ]
},{
"name": "background-image",
"values": [ "<url>", "none", "<image>" ]
},{
"name": "background-position-x",
"values": [ "left", "center", "right", "<dimension>" ],
"prefixes": [ "ms" ]
},{
"name": "background-position-y",
"values": [ "top", "center", "bottom", "<dimension>" ],
"prefixes": [ "ms" ]
},{
"name": "background-position",
"values": [ "<background-position-x>", "<background-position-y>" ]
},{
"name": "background-repeat",
"values": [ "repeat", "repeat-x", "repeat-y", "no-repeat", "space", "round" ]
},{
"name": [ "background-clip", "background-origin" ],
"values": [ "border-box", "padding-box", "content-box" ],
"prefixes": [ "o" ]
},{
"name": "background-size",
"values": [ "auto", "contain", "cover", "<dimension>" ],
"prefixes": [ "webkit", "o" ]
},{
"name": "background",
"values": [ "<_background-attachment>", "<background-color>", "<background-image>", "<_background-position>", "<_background-repeat>", "none" ]
},{
"name": "box-decoration-break",
"values": [ "slice", "clone" ]
},{
"name": "box-shadow",
"values": [ "inset", "<length>", "<color>" ],
"prefixes": [ "webkit" ]
},{
"name": "color",
"values": [ "<color_values>" ],
"default": "#FFF"
},{
"name": "table-layout",
"values": [ "auto", "fixed" ]
},{
"name": "caption-side",
"values": [ "top", "bottom" ]
},{
"name": "empty-cells",
"values": [ "show", "hide" ]
},{
"name": "list-style-image",
"values": [ "none", "<url>" ]
},{
"name": "list-style-position",
"values": [ "inside", "outside" ]
},{
"name": "list-style-type",
"values": [ "disc", "circle", "square", "decimal", "decimal-leading-zero", "lower-roman", "upper-roman", "lower-greek", "lower-latin", "upper-latin", "armenian", "georgian", "lower-alpha", "upper-alpha", "none" ]
},{
"name": "list-style",
"values": [ "<list-style-image>", "<list-style-position>", "<list-style-type>" ]
},{
"name": "quotes",
"values": [ "none" ]
},{
"name": "content",
"values": [ "normal", "none", "open-quote", "close-quote", "no-open-quote", "no-close-quote", "icon", "<attr>", "<string>", "<url>", "<counter>" ],
"default": "\"\""
},{
"name": [ "counter-increment", "counter-reset" ],
"values": [ "none" ]
},{
"name": "vertical-align",
"values": [ "baseline", "sub", "super", "top", "text-top", "middle", "bottom", "text-bottom", "<dimension>" ]
},{
"name": "text-align",
"values": [ "left", "right", "center", "justify", "start", "end" ]
},{
"name": "text-align-last",
"values": [ "<text-align>", "auto" ],
"prefixes": [ "ms" ]
},{
"name": "text-decoration",
"values": [ "none", "underline", "overline", "line-through" ]
},{
"name": "text-emphasis-color",
"values": [ "<color>" ]
},{
"name": "text-emphasis-style",
"values": [ "none", "filled", "open", "dot", "circle", "double-circle", "triangle", "sesame" ]
},{
"name": "text-emphasis-position",
"values": [ "above", "below", "right", "left" ]
},{
"name": "text-emphasis",
"values": [ "<text-emphasis-color>", "<text-emphasis-style>", "<text-emphasis-position>" ]
},{
"name": "text-indent",
"values": [ "<dimension>", "each-line", "hanging" ]
},{
"name": "text-justify",
"values": [ "auto", "inter-word", "inter-ideograph", "inter-cluster", "distribute", "distribute-all-lines", "distribute-center-last", "kashida", "newspaper", "tibetan" ],
"prefixes": [ "ms" ]
},{
"name": "text-outline",
"values": [ "none" ]
},{
"name": "text-transform",
"values": [ "capitalize", "uppercase", "lowercase", "none", "fullwidth" ]
},{
"name": "text-wrap",
"values": [ "none", "normal", "unrestricted", "avoid" ]
},{
"name": "text-overflow",
"values": [ "ellipsis", "clip" ],
"prefixes": [ "ms" ]
},{
"name": "text-shadow",
"values": [ "<length>", "none" ]
},{
"name": "line-height",
"values": [ "normal", "<number>", "<dimension>" ]
},{
"name": "white-space",
"values": [ "normal", "pre", "nowrap", "pre-wrap", "pre-line" ]
},{
"name": "word-spacing",
"values": [ "normal", "<length>" ]
},{
"name": "word-wrap",
"values": [ "normal", "break-word" ],
"prefixes": [ "ms" ]
},{
"name": "word-break",
"values": [ "normal", "keep-all", "break-all" ],
"prefixes": [ "ms" ]
},{
"name": "hyphens",
"values": [ "none", "manual", "auto" ],
"prefixes": [ "webkit", "moz" ]
},{
"name": "letter-spacing",
"values": [ "normal", "<length>" ]
},{
"name": "font-family",
"values": [ "serif", "sans-serif", "cursive", "fantasy", "monospace", "<family-name>" ]
},{
"name": "font-size",
"values": [ "larger", "smaller", "xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large", "<dimension>" ]
},{
"name": "font-style",
"values": [ "normal", "italic", "oblique" ]
},{
"name": "font-variant",
"values": [ "normal", "small-caps" ]
},{
"name": "font-weight",
"values": [ "normal", "bold", "bolder", "lighter", "100", "200", "300", "400", "500", "600", "700", "800", "900" ]
},{
"name": "font-size-adjust",
"values": [ "none" ]
},{
"name": "font-stretch",
"values": [ "normal", "ultra-condensed", "extra-condensed", "condensed", "semi-condensed", "semi-expanded", "expanded", "extra-expanded", "ultra-expanded" ]
},{
"name": "font",
"values": [ "<font_values>" ]
},{
"name": "tab-size",
"values": [ "<integer>" ],
"prefixes": [ "moz", "o" ]
},{
"name": "writing-mode",
"values": [ "lr-tb", "rl-tb", "tb-rl", "bt-rl", "tb-lr", "bt-lr", "lr-bt", "rl-bt", "lr", "rl", "tb" ],
"prefixes": [ "moz", "o" ],
"no-unprefixed-property": true
},{
"name": "opacity",
"values": [ "<alphavalue>" ]
},{
"name": "interpolation-mode",
"values": [ "bicubic", "nearest-neighbor" ],
"prefixes": [ "ms" ],
"no-unprefixed-property": true
},{
"name": "resize",
"values": [ "none", "both", "horizontal", "vertical" ]
},{
"name": "cursor",
"values": [ "auto", "none", "alias", "all-scroll", "cell", "col-resize", "context-menu", "copy", "crosshair", "default", "e-resize", "ew-resize", "help", "move", "n-resize", "ne-resize", "nesw-resize", "no-drop", "not-allowed", "ns-resize", "nw-resize", "nwse-resize", "pointer", "progress", "row-resize", "s-resize", "se-resize", "sw-resize", "text", "vertical-text", "w-resize", "wait", "<url>" ]
},{
"name": [ "nav-index", "nav-up", "nav-right", "nav-down", "nav-left" ],
"values": [ "auto" ]
},{
"name": [ "transition-delay", "transition-duration" ],
"values": [ "<time>" ],
"prefixes": [ "webkit", "moz", "ms", "o" ]
},{
"name": "transition-property",
"values": [ "none", "all", "<properties>" ],
"prefixes": [ "webkit", "moz", "ms", "o" ]
},{
"name": "transition-timing-function",
"values": [ "<timing-function>" ],
"prefixes": [ "webkit", "moz", "ms", "o" ]
},{
"name": "transition",
"values": [ "<transition-property>", "<transition-duration>", "<transition-delay>", "<transition-timing-function>" ],
"prefixes": [ "webkit", "moz", "ms", "o" ]
},{
"name": "transform",
"values": [ "none", "matrix()", "matrix3d()", "perspective()", "rotate()", "rotateX()", "rotateY()", "rotateZ()", "scale()", "scaleX()", "scaleY()", "scaleZ()", "skew()", "skewX()", "skewY()", "translate()", "translateX()", "translateY()", "translateZ()", "translate3d()" ],
"prefixes": [ "webkit", "moz", "ms", "o" ]
},{
"name": "transform-origin",
"values": [ "top", "center", "bottom", "left", "center", "right", "<percentage>", "<length>" ],
"prefixes": [ "webkit", "moz", "ms", "o" ]
},{
"name": "animation-name",
"values": [ "none", "<properties>" ],
"prefixes": [ "webkit", "moz", "ms", "o" ]
},{
"name": [ "animation-duration", "animation-delay" ],
"values": [ "<time>" ],
"prefixes": [ "webkit", "moz", "ms", "o" ]
},{
"name": "animation-timing-function",
"values": [ "<timing-function>" ],
"prefixes": [ "webkit", "moz", "ms", "o" ]
},{
"name": "animation-iteration-count",
"values": [ "infinite", "<number>" ],
"prefixes": [ "webkit", "moz", "ms", "o" ]
},{
"name": "animation-direction",
"values": [ "normal", "alternate" ],
"prefixes": [ "webkit", "moz", "ms", "o" ]
},{
"name": "animation",
"values": [ "<animation-name>", "<animation-duration>", "<animation-timing-function>", "<animation-delay>", "<animation-iteration-count>", "<animation-direction>" ],
"prefixes": [ "webkit", "moz", "ms", "o" ]
},{
"name": "animation-play-state",
"values": [ "running", "paused" ],
"prefixes": [ "webkit", "moz", "ms", "o" ]
},{
"name": "filter",
"_comment": "conflicts with font-style etc.",
"_values": [ "none", "grayscale()", "sepia()", "saturate()", "hue-rotate()", "invert()", "opacity()", "brightness()", "contrast()", "blur()", "drop-shadow()", "custom()" ]
},{
"name": "unicode-bidi",
"values": [ "normal", "embed", "bidi-override" ]
},{
"name": "direction",
"values": [ "ltr", "rtl" ]
},{
"name": "column-width",
"values": [ "<length>", "auto" ]
},{
"name": "column-count",
"values": [ "<integer>", "auto" ]
},{
"name": "columns",
"values": [ "<columns-width>", "<columns-count>" ],
"prefixes": [ "webkit", "moz" ]
},{
"name": "column-gap",
"values": [ "<length>", "normal" ],
"prefixes": [ "webkit", "moz" ]
},{
"name": "column-rule-color",
"values": [ "<color>" ],
"prefixes": [ "webkit", "moz" ]
},{
"name": "column-rule-style",
"values": [ "<border-style>" ],
"prefixes": [ "webkit", "moz" ]
},{
"name": "column-rule-width",
"values": [ "<border-width>" ],
"prefixes": [ "webkit", "moz" ]
},{
"name": "column-rule",
"values": [ "<column-rule-width>", "<column-rule-style>", "<column-rule-color>" ],
"prefixes": [ "webkit", "moz" ]
},{
"name": [ "break-before", "break-after" ],
"values": [ "auto", "always", "avoid", "left", "right", "page", "column", "avoid-page", "avoid-column" ]
},{
"name": "break-inside",
"values": [ "auto", "avoid", "avoid-page", "avoid-column" ]
},{
"name": "column-span",
"values": [ "none", "all" ]
},{
"name": "column-fill",
"values": [ "auto", "balance" ]
},{
"name": "orphans",
"values": [ "<integer>" ]
},{
"name": "widows",
"values": [ "<integer>" ]
},{
"name": "zoom",
"values": [ "auto", "<integer>" ]
},{
"name": "behavior",
"values": [ "none", "<url>" ]
},{
"name": "user-select",
"values": [ "none", "text", "all" ],
"prefixes": [ "webkit", "moz", "ms" ]
}
]
}

View File

@@ -0,0 +1,151 @@
# -*- coding: utf-8 -*-
import os
import re
from itertools import chain, product
import sublime
import sublime_plugin
def import_dir(name, fromlist=()):
PACKAGE_EXT = '.sublime-package'
dirname = os.path.basename(os.path.dirname(os.path.realpath(__file__)))
if dirname.endswith(PACKAGE_EXT):
dirname = dirname[:-len(PACKAGE_EXT)]
return __import__('{0}.{1}'.format(dirname, name), fromlist=fromlist)
try:
extract = import_dir('probe', ('extract',)).extract
except ImportError:
from probe import extract
try:
make_template = import_dir('templates', ('make_template',)).make_template
except ImportError:
from templates import make_template
try:
parse_dict_json = import_dir('css_dict_driver', ('parse_dict_json',)).parse_dict_json
except ImportError:
from css_dict_driver import parse_dict_json
try:
get_hayaku_options = import_dir('add_code_block', ('add_code_block',)).get_hayaku_options
except ImportError:
from add_code_block import get_hayaku_options
# The maximum size of a single propery to limit the lookbehind
MAX_SIZE_CSS = len('-webkit-transition-timing-function')
ABBR_REGEX = re.compile(r'[\s|;|{]([\.:%#a-z-,\d]+!?)$', re.IGNORECASE)
class HayakuCommand(sublime_plugin.TextCommand):
def run(self, edit):
cur_pos = self.view.sel()[0].begin()
start_pos = cur_pos - MAX_SIZE_CSS
if start_pos < 0:
start_pos = 0
# TODO: Move this to the contexts, it's not needed here
probably_abbr = self.view.substr(sublime.Region(start_pos, cur_pos))
match = ABBR_REGEX.search(probably_abbr)
if match is None:
self.view.insert(edit, cur_pos, '\t')
return
abbr = match.group(1)
# Extracting the data from the abbr
args = extract(abbr)
if not args:
return
# Getting the options and making a snippet
# from the extracted data
get_hayaku_options(self)
options = get_hayaku_options(self)
template = make_template(args, options)
if template is None:
return
# Inserting the snippet
new_cur_pos = cur_pos - len(abbr)
assert cur_pos - len(abbr) >= 0
self.view.erase(edit, sublime.Region(new_cur_pos, cur_pos))
self.view.run_command("insert_snippet", {"contents": template})
# Helpers for getting the right indent for the Add Line Command
WHITE_SPACE_FINDER = re.compile(r'^(\s*)(-)?[\w]*')
def get_line_indent(line):
return WHITE_SPACE_FINDER.match(line).group(1)
def is_prefixed_property(line):
return WHITE_SPACE_FINDER.match(line).group(2) is not None
def get_previous_line(view, line_region):
return view.line(line_region.a - 1)
def get_nearest_indent(view):
line_region = view.line(view.sel()[0])
line = view.substr(line_region)
line_prev_region = get_previous_line(view,line_region)
found_indent = None
first_indent = None
first_is_ok = True
is_nested = False
# Can we do smth with all those if-else noodles?
if not is_prefixed_property(line):
first_indent = get_line_indent(line)
if not is_prefixed_property(view.substr(line_prev_region)):
return first_indent
if is_prefixed_property(view.substr(line_prev_region)):
first_is_ok = False
while not found_indent and line_prev_region != view.line(sublime.Region(0)):
line_prev = view.substr(line_prev_region)
if not first_indent:
if not is_prefixed_property(line_prev):
first_indent = get_line_indent(line_prev)
if is_prefixed_property(view.substr(get_previous_line(view,line_prev_region))):
first_is_ok = False
else:
if not is_prefixed_property(line_prev) and not is_prefixed_property(view.substr(get_previous_line(view,line_prev_region))):
found_indent = min(first_indent,get_line_indent(line_prev))
line_prev_region = get_previous_line(view,line_prev_region)
if line_prev.count("{"):
is_nested = True
if found_indent and found_indent < first_indent and not is_prefixed_property(view.substr(get_previous_line(view,line_region))) and first_is_ok or is_nested:
found_indent = found_indent + " "
if not found_indent:
if first_indent:
found_indent = first_indent
else:
found_indent = ""
return found_indent
class HayakuAddLineCommand(sublime_plugin.TextCommand):
def run(self, edit):
nearest_indent = get_nearest_indent(self.view)
# Saving current auto_indent setting
# This hack fixes ST2's bug with incorrect auto_indent for snippets
# It seems that with auto indent off it uses right auto_indent there lol.
current_auto_indent = self.view.settings().get("auto_indent")
self.view.settings().set("auto_indent",False)
self.view.run_command('insert', {"characters": "\n"})
self.view.erase(edit, sublime.Region(self.view.line(self.view.sel()[0]).a, self.view.sel()[0].a))
self.view.run_command('insert', {"characters": nearest_indent})
self.view.settings().set("auto_indent",current_auto_indent)

View File

@@ -0,0 +1 @@
{"url": "http://hayakubundle.com/", "version": "2013.03.02.05.49.43", "description": "Fuzzy abbreviations, support for preprocessors (Sass, Less, Stylus) and a lot of other features in easily configurable set of tools for writing CSS faster"}

View File

@@ -0,0 +1,421 @@
# -*- coding: utf-8 -*-
# (c) 2012 Sergey Mezentsev
import os
import re
from itertools import product, chain
def import_dir(name, fromlist=()):
PACKAGE_EXT = '.sublime-package'
dirname = os.path.basename(os.path.dirname(os.path.realpath(__file__)))
if dirname.endswith(PACKAGE_EXT):
dirname = dirname[:-len(PACKAGE_EXT)]
return __import__('{0}.{1}'.format(dirname, name), fromlist=fromlist)
try:
imp = import_dir('css_dict_driver', ('css_defaults', 'get_css_dict', 'get_flat_css', 'css_flat_list'))
css_defaults = imp.css_defaults
get_css_dict = imp.get_css_dict
get_flat_css = imp.get_flat_css
css_flat_list = imp.css_flat_list
except ImportError:
from css_dict_driver import css_defaults, get_css_dict, get_flat_css, css_flat_list
# TODO: Move this to dicts etc.
PRIORITY_PROPERTIES = [ 'display', 'color', 'margin', 'position', 'padding', 'width', 'background', 'zoom', 'height', 'top', 'vertical-align', 'overflow', 'left', 'margin-right', 'float', 'margin-left', 'cursor', 'text-decoration', 'font-size', 'margin-top', 'border', 'background-position', 'font', 'margin-bottom', 'padding-left', 'right', 'padding-right', 'line-height', 'white-space', 'text-align', 'border-color', 'padding-top', 'z-index', 'border-bottom', 'visibility', 'border-radius', 'padding-bottom', 'font-weight', 'clear', 'max-width', 'border-top', 'border-width', 'content', 'bottom', 'background-color', 'opacity', 'background-image', 'box-shadow', 'border-collapse', 'text-overflow', 'filter', 'border-right', 'text-indent', 'clip', 'min-width', 'min-height', 'border-left', 'max-height', 'border-right-color', 'border-top-color', 'transition', 'resize', 'overflow-x', 'list-style', 'word-wrap', 'border-left-color', 'word-spacing', 'background-repeat', 'user-select', 'border-bottom-color', 'box-sizing', 'border-top-left-radius', 'font-family', 'border-bottom-width', 'outline', 'border-bottom-right-radius', 'border-right-width', 'border-top-width', 'font-style', 'text-transform', 'border-bottom-left-radius', 'border-left-width', 'border-spacing', 'border-style', 'border-top-right-radius', 'text-shadow', 'border-image', 'overflow-y', 'table-layout', 'background-size', 'behavior', 'body', 'name', 'letter-spacing', 'background-clip', 'pointer-events', 'transform', 'counter-reset', ]
# __all__ = [
# 'extract',
# ]
STATIC_ABBR = dict([
('b', 'bottom'), # Sides consistency
('ba', 'background'), # Instead of background-attachment
('bg', 'background'), # Instead of background: linear-gradient
('bd', 'border'), # Instead of border-style: dashed;
('bbc', 'border-bottom-color'), # Instead of background-break continuous
('br', 'border-right'), # Instead of border-radius
('bt', 'border-top'), # Instead of border: thick
('bdr', 'border-right'), # Instead of border-radius
('bds', 'border-style'), # Instead of border-spacing
('bo', 'border'), # Instead of background-origin
('bos', 'border-style'), # Instead of box-shadow (?)
('ct', 'content'), # Istead of color transparent
('f', 'font'), # Istead of float (do we really need this?)
('p', 'padding'), # Instead of position (w/h/p/m consistency)
('pr', 'padding-right'), # Instead of position relative
])
PAIRS = dict([
('bg', 'background'), # Instead of border-style: groove;
('bd', 'border'), # Instead of background (Zen CSS support)
('pg', 'page'),
('lt', 'letter'),
('tf', 'transform'),
('tr', 'transition'),
])
def get_all_properties():
all_properties = list(get_css_dict().keys())
# раширить парами "свойство значение" (например "position absolute")
for prop_name in all_properties:
property_values = css_flat_list(prop_name, get_css_dict())
extends_sieve = (i for i in property_values if not i[1].startswith('<'))
unit_sieve = (i for i in extends_sieve if not i[1].startswith('.'))
all_properties.extend('{0} {1}'.format(prop_name, v[1]) for v in unit_sieve)
return all_properties
def score(a, b):
"""Оценочная функция"""
s = 0
# увеличивает вес свойству со значением (они разделены пробелом)
if a and ' ' == a[-1]:
s += 3.0
# уменьшить, если буква находится не на грницах слова
if '-' in a[1:-1] or '-' in b[1:-1]:
s += -2.0
# уменьшить, если буква находится не на грницах слова
if ' ' in a[1:-1] or ' ' in b[1:-1]:
s += -0.5
# если буква в начале слова после -
if a and a[-1] == '-':
s += 1.05
# если буквы подряд
if len(a) == 1:
s += 1.0
return s
def string_score(arr):
"""Получает оценку разбиения"""
# s = sum(score(arr[i-1], arr[i]) for i in range(1, len(arr)))
# if s >0 :
# print arr, s
return sum(score(arr[i-1], arr[i]) for i in range(1, len(arr)))
def tree(css_property, abbr):
# функция генерирует деревья (разбиения) из строки
# (abvbc, abc) -> [[a, bvb ,c], [avb, b, c]]
# print '\n', css_property
if len(css_property) < len(abbr):
return set([])
trees = [[css_property[0], css_property[1:],],]
for level in range(1, len(abbr)):
# print level, trees
for tr in trees:
if level == 1 and len(trees) == 1:
trees = []
# находит индексы букв
indexes = []
i = -1
try:
while True:
i = tr[-1].index(abbr[level], i+1)
indexes.append(i)
except ValueError:
pass
# print 'indexes len', len(indexes)
for ind in indexes:
if level == 1:
car = tr[:-1]
cdr = tr[-1]
first = cdr[:ind]
second = cdr[ind:]
add = []
add.append(car[-1] + first)
add.append(second)
# print '\t', car, '|', cdr,'|', first,'|', second, '-', add, level, '=', tr
trees.append(add)
else:
car = tr[:-1]
cdr = tr[-1]
first = cdr[:ind]
second = cdr[ind:]
add = car
add.append(first)
add.append(second)
# print '\t', car, '|', cdr,'|', first,'|', second, '-', add, level, '=', tr
# print repr(first)
trees.append(add)
# break
trees_i = set([tuple(t) for t in trees if len(t) == level+1])
trees = [list(t) for t in trees_i]
# print 'trees_i', trees_i
# break
# print
# break
# удалить разбиения с двумя "-" в шилде
ret = set([tuple(t) for t in trees])
filtered = []
for s in ret: # каждое элемент в сете
for t in s: # каждый шилд в элементе
# print '\t', t
if t.count('-') > 1:
break
else:
filtered.append(s)
# print set([tuple(t) for t in trees])
# print filtered
return filtered
def prop_value(s1, val):
"""Генератор возвращает свойства и значения разделённые пробелом
Из всех свойств выбирает только с совпадающим порядком букв"""
for pv in get_all_properties():
if ' ' not in pv.strip():
continue
prop, value = pv.split()
if sub_string(value, val):
if sub_string(prop, s1):
yield '{0} {1}'.format(prop, value).strip()
def sub_string(string, sub):
"""Функция проверяет, следуют ли буквы в нужном порядке в слове"""
index = 0
for c in sub:
try:
index += string[index:].index(c)+1
except ValueError:
return False
else:
return True
def segmentation(abbr):
"""Разбивает абрревиатуру на элементы"""
# Части аббревиатуры
parts = {
'abbr': abbr # todo: выкинуть, используется только в тестах
}
# Проверка на important свойство
if '!' == abbr[-1]:
abbr = abbr[:-1]
parts['important'] = True
else:
parts['important'] = False
# TODO: вынести regex в compile
# todo: начать тестировать regex
m = re.search(r'^([a-z]?[a-z-]*[a-z]).*$', abbr)
property_ = m if m is None else m.group(1)
if property_ is None:
# Аббревиатура не найдена
return parts
# del m
parts['property-value'] = property_
# удалить из аббревиатуры property
abbr = abbr[len(property_):]
if abbr:
parts['property-name'] = property_
del parts['property-value']
# убрать zen-style разделитель
if abbr and ':' == abbr[0]:
abbr = abbr[1:]
if not abbr:
return parts
parts.update(value_parser(abbr))
if 'value' in parts:
assert parts['value'] is None
del parts['value']
elif ('type-value' not in parts and 'type-name' not in parts):
parts['keyword-value'] = abbr
# TODO: сохранять принимаемые значения, например parts['allow'] = ['<color_values>']
return parts
def value_parser(abbr):
# todo: поддержка аббревиатур "w-.e" то есть "width -|em"
parts = {}
# Checking the color
# Better to replace with regex to simplify it
dot_index = 0
if '.' in abbr:
dot_index = abbr.index('.')
if abbr[0] == '#':
parts['color'] = (abbr[1:dot_index or 99])
if dot_index:
parts['color_alpha'] = (abbr[dot_index:])
parts['value'] = None
try:
if all((c.isupper() or c.isdigit() or c == '.') for c in abbr) and 0 <= int(abbr[:dot_index or 99], 16) <= 0xFFFFFF:
parts['color'] = abbr[:dot_index or 99]
if dot_index:
parts['color_alpha'] = (abbr[dot_index:])
parts['value'] = None
except ValueError:
pass
# Проверка на цифровое значение
val = None
numbers = re.sub("[a-z%]+$", "", abbr)
try:
val = float(numbers)
val = int(numbers)
except ValueError:
pass
if val is not None:
parts['type-value'] = val
if abbr != numbers:
parts['type-name'] = abbr[len(numbers):]
return parts
def extract(s1):
"""В зависимости от найденных компонент в аббревиатуре применяет функцию extract"""
# print repr(s1)
prop_iter = []
parts = segmentation(s1)
abbr_value = False
if 'property-name' in parts:
if parts['important']:
s1 = s1[:-1]
if s1[-1] != ':' and s1 != parts['property-name']:
abbr_value = True
if 'color' in parts:
prop_iter.extend(prop for prop, val in get_flat_css() if val == '<color_values>')
if isinstance(parts.get('type-value'), int):
prop_iter.extend(prop for prop, val in get_flat_css() if val == '<integer>')
if isinstance(parts.get('type-value'), float):
# TODO: добавить deg, grad, time
prop_iter.extend(prop for prop, val in get_flat_css() if val in ('<length>', '<number>', 'percentage'))
if 'keyword-value' in parts and not parts['keyword-value']:
prop_iter.extend(get_all_properties())
if 'keyword-value' in parts:
prop_iter.extend(prop_value(parts['property-name'], parts['keyword-value']))
elif 'color' not in parts or 'type-value' in parts:
prop_iter.extend(get_all_properties())
assert parts.get('property-name', '') or parts.get('property-value', '')
abbr = ' '.join([
parts.get('property-name', '') or parts.get('property-value', ''),
parts.get('keyword-value', ''),
])
# предустановленные правила
abbr = abbr.strip()
if abbr in STATIC_ABBR:
property_ = STATIC_ABBR[abbr]
else:
starts_properties = []
# todo: переделать механизм PAIRS
# надо вынести константы в css-dict
# по две буквы (bd, bg, ba)
pair = PAIRS.get(abbr[:2], None)
if pair is not None:
starts_properties = [prop for prop in prop_iter if prop.startswith(pair) and sub_string(prop, abbr)]
if not starts_properties:
starts_properties = [prop for prop in prop_iter if prop[0] == abbr[0] and sub_string(prop, abbr)]
if 'type-value' in parts:
starts_properties = [i for i in starts_properties if ' ' not in i]
property_ = hayaku_extract(abbr, starts_properties, PRIORITY_PROPERTIES, string_score)
property_, value = property_.split(' ') if ' ' in property_ else (property_, None)
# print property_, value
if not property_:
return {}
parts['property-name'] = property_
if value is not None:
parts['keyword-value'] = value
# Проверка соответствия свойства и значения
allow_values = [val for prop, val in get_flat_css() if prop == parts['property-name']]
if 'color' in parts and '<color_values>' not in allow_values:
del parts['color']
if 'type-value' in parts and not any((t in allow_values) for t in ['<integer>', 'percentage', '<length>', '<number>', '<alphavalue>']):
del parts['type-value']
if 'keyword-value' in parts and parts['keyword-value'] not in allow_values:
del parts['keyword-value']
if all([
'keyword-value' not in parts,
'type-value' not in parts,
'color' not in parts,
]) and abbr_value:
return {}
# Добавить значение по-умолчанию
if parts['property-name'] in get_css_dict():
default_value = css_defaults(parts['property-name'], get_css_dict())
if default_value is not None:
parts['default-value'] = default_value
obj = get_css_dict()[parts['property-name']]
if 'prefixes' in obj:
parts['prefixes'] = obj['prefixes']
if 'no-unprefixed-property' in obj:
parts['no-unprefixed-property'] = obj['no-unprefixed-property']
if parts['abbr'] == parts.get('property-value'):
del parts['property-value']
return parts
def hayaku_extract(abbr, filtered, priority=None, score_func=None):
# выбирает только те правила куда входят все буквы в нужном порядке
# все возможные разбиения
trees_filtered = []
for property_ in filtered:
trees_filtered.extend(tree(property_, abbr))
# оценки к разбиениям
if score_func is not None:
scores = [(score_func(i), i) for i in trees_filtered]
# выбрать с максимальной оценкой
if scores:
max_score = max(s[0] for s in scores)
filtered_scores = (i for s, i in scores if s == max_score)
filtered = [''.join(t) for t in filtered_scores]
if len(filtered) == 1:
return ''.join(filtered[0])
# выбрать более приоритетные
if len(filtered) == 1:
return filtered[0]
elif len(filtered) > 1 and priority is not None:
# выбирает по приоритету
prior = []
for f in filtered:
p = f.split(' ')[0] if ' ' in f else f
try:
prior.append((priority.index(p), f))
except ValueError:
prior.append((len(priority)+1, f))
prior.sort()
try:
return prior[0][1]
except IndexError:
return ''
else:
return ''

View File

@@ -0,0 +1,462 @@
# -*- coding: utf-8 -*-
import json
import os
import re
import sublime
def import_dir(name, fromlist=()):
PACKAGE_EXT = '.sublime-package'
dirname = os.path.basename(os.path.dirname(os.path.realpath(__file__)))
if dirname.endswith(PACKAGE_EXT):
dirname = dirname[:-len(PACKAGE_EXT)]
return __import__('{0}.{1}'.format(dirname, name), fromlist=fromlist)
try:
get_flat_css = import_dir('css_dict_driver', ('get_flat_css',)).get_flat_css
except ImportError:
from css_dict_driver import get_flat_css
try:
imp = import_dir('probe', ('hayaku_extract', 'sub_string'))
hayaku_extract, sub_string = imp.hayaku_extract, imp.sub_string
except ImportError:
from probe import hayaku_extract, sub_string
COLOR_REGEX = re.compile(r'#([0-9a-fA-F]{3,6})')
COMPLEX_COLOR_REGEX = re.compile(r'^\s*(#?([a-fA-F\d]{3}|[a-fA-F\d]{6})|(rgb|hsl)a?\([^\)]+\))\s*$')
IMAGE_REGEX = re.compile(r'^\s*([^\s]+\.(jpg|jpeg|gif|png))\s*$')
CAPTURING_GROUPS = re.compile(r'(?<!\\)\((?!\?[^<])')
CAPTURES = re.compile(r'(\(\?|\$)(\d+)|^(\d)')
def align_prefix(property_name, prefix_list, no_unprefixed_property, aligned_prefixes, use_only):
"""Если есть префиксы, сделать шаблон с правильными отступами"""
# if no_unprefixed_property:
# prefix_list = ('-{0}-{1}'.format(prefix_list[0], property_name),)
# skip if `use_only` is empty
if use_only:
prefix_list = [p for p in prefix_list if p in use_only]
if prefix_list:
prefix_list = ['-{0}-{1}'.format(p, property_name) for p in prefix_list]
if not no_unprefixed_property:
prefix_list.append(property_name)
if not aligned_prefixes:
return prefix_list
max_length = max(len(p) for p in prefix_list)
# TODO: сделать сортировку по размеру значений в prefix_list
return tuple((' '*(max_length-len(p))) + p for p in prefix_list)
return (property_name,)
def hex_to_coloralpha(hex):
if len(hex) == 1:
hex = hex*2
return round(float(int(hex, 16)) / 255, 2)
def color_expand(color,alpha):
if not color:
return '#'
if len(color) == 1:
if color == '#':
color = ''
else:
color = color * 3
elif len(color) == 2:
if color[0] == '#':
color = color[1] * 3
else:
color = color * 3
elif len(color) == 3:
if color[0] == '#':
color = color[1:] * 3
else:
color = color
elif len(color) == 4:
if color[0] != '#' and alpha == 1:
alpha = hex_to_coloralpha(color[3])
color = color[:3]
else:
return color
elif len(color) == 5:
if color[0] != '#':
alpha = hex_to_coloralpha(color[3:5])
color = color[:3]
else:
alpha = hex_to_coloralpha(color[4]*2)
color = color[1:4]
elif len(color) == 6:
if color[0] != '#':
pass
else:
alpha = hex_to_coloralpha(color[4:5])
color = color[1:4]
elif len(color) == 7:
color = color[1:]
else:
return color
# Convert color to rgba if there is some alpha
if alpha == '.' or float(alpha) < 1:
if alpha == '.':
alpha = '.${1:5}' # adding caret for entering alpha value
if alpha == '.0' or alpha == 0:
alpha = '0'
if len(color) == 3:
color = color[0] * 2 + color[1] * 2 + color[2] * 2
return "rgba({0},{1},{2},{3})".format(int(color[:2],16), int(color[2:4],16), int(color[4:],16), alpha)
return '#{0}'.format(color)
def length_expand(name, value, unit, options=None):
if options is None:
options = {}
if unit and 'percents'.startswith(unit):
unit = '%'
if isinstance(value, float):
full_unit = options.get('CSS_default_unit_decimal', 'em')
else:
full_unit = options.get('CSS_default_unit', 'px')
if '<number>' in [val for prop, val in get_flat_css() if prop == name] and not options.get('CSS_units_for_unitless_numbers'):
full_unit = ''
if value == 0:
return '0'
if value == '':
return ''
if unit:
units = (val[1:] for key, val in get_flat_css() if key == name and val.startswith('.'))
req_units = [u for u in units if sub_string(u, unit)]
PRIORITY = ("em", "ex", "vw", "vh", "vmin", "vmax" "vm", "ch", "rem",
"px", "cm", "mm", "in", "pt", "pc")
full_unit = hayaku_extract(unit, req_units, PRIORITY)
if not full_unit:
return
return '{0}{1}'.format(value, full_unit)
def expand_value(args, options=None):
if 'keyword-value' in args:
return args['keyword-value']
if args['property-name'] in set(p for p, v in get_flat_css() if v == '<color_values>'):
if 'color' in args and not args['color']:
return '#'
return color_expand(args.get('color', ''),args.get('color_alpha', 1))
elif args['property-name'] in set(p for p, v in get_flat_css() if v.startswith('.')) and 'keyword-value' not in args:
ret = length_expand(args['property-name'], args.get('type-value', ''), args.get('type-name', ''), options)
return ret
elif 'type-value' in args:
return str(args['type-value'])
return args.get('keyword-value', '')
def split_for_snippet(values, offset=0):
split_lefts = [[]]
split_rights = [[]]
parts = 0
new_offset = offset
for value in (v for v in values if len(v) > 1):
for i in range(1, len(value)):
if value[:i] not in [item for sublist in split_lefts for item in sublist] + values:
if len(split_lefts[parts]) > 98:
parts += 1
split_lefts.append([])
split_rights.append([])
split_lefts[parts].append(value[:i])
split_rights[parts].append(value[i:])
new_offset += 1
for index in range(0, parts + 1):
split_lefts[index] = ''.join('({0}$)?'.format(re.escape(i)) for i in split_lefts[index])
split_rights[index] = ''.join('(?{0}:{1})'.format(i+1+offset,re.escape(f)) for i,f in enumerate(split_rights[index]))
return (split_lefts, split_rights, new_offset)
def convert_to_parts(parts):
matches = []
inserts = []
parts_count = 1
# Function for offsetting the captured groups in inserts
def offset_captures(match):
if match.group(3):
return '()' + match.group(3)
else:
number = int(match.group(2))
return match.group(1) + str(number + parts_count)
for part in parts:
matches.append(''.join([
'(?=(',
part['match'],
')?)',
]))
inserts.append(''.join([
'(?',
str(parts_count),
':',
CAPTURES.sub(offset_captures, part['insert']),
')',
]))
# Incrementing the counter, adding the number of internal capturing groups
parts_count += 1 + len(CAPTURING_GROUPS.findall(part['match'] ))
return { "matches": matches, "inserts": inserts }
def generate_snippet(data):
value = data.get('value')
before = ''.join([
'_PROPERTY_',
data.get('colon'),
data.get('space'),
])
after = ''
importance = ''
if data.get('important'):
importance = ' !important'
if value:
after = importance + data.get('semicolon')
else:
if not importance:
importance_splitted = split_for_snippet(["!important"])
importance = ''.join([
'${1/.*?',
importance_splitted[0][0],
'$/',
importance_splitted[1][0],
'/}',
])
befores = convert_to_parts(data["before"])
before = ''.join([
'${1/^',
''.join(befores["matches"]),
'.+$|.*/',
before,
''.join(befores["inserts"]),
'/m}',
])
if data.get('semicolon') == '':
data['semicolon'] = ' '
afters = convert_to_parts(data["after"])
after = ''.join([
'${1/^',
''.join(afters["matches"]),
'.+$|.*/',
''.join(afters["inserts"]),
'/m}',
data.get('autovalues'),
importance,
data.get('semicolon'),
])
value = ''.join([
'${1:',
data.get('default'),
'}',
])
return (before + value + after).replace('{','{{').replace('}','}}').replace('_PROPERTY_','{0}')
def make_template(args, options):
whitespace = options.get('CSS_whitespace_after_colon', '')
disable_semicolon = options.get('CSS_syntax_no_semicolons', False)
disable_colon = options.get('CSS_syntax_no_colons', False)
disable_prefixes = options.get('CSS_prefixes_disable', False)
clipboard = sublime.get_clipboard()
if not whitespace and disable_colon:
whitespace = ' '
value = expand_value(args, options)
if value is None:
return
if value.startswith('[') and value.endswith(']'):
value = False
semicolon = ';'
colon = ':'
if disable_semicolon:
semicolon = ''
if disable_colon:
colon = ''
snippet_parts = {
'colon': colon,
'semicolon': semicolon,
'space': whitespace,
'default': args.get('default-value',''),
'important': args.get('important'),
'before': [],
'after': [],
'autovalues': '',
}
# Handling prefixes
property_ = (args['property-name'],)
if not disable_prefixes:
property_ = align_prefix(
args['property-name'],
args.get('prefixes', []),
args.get('no-unprefixed-property', False) or options.get('CSS_prefixes_no_unprefixed', False),
options.get('CSS_prefixes_align', True),
options.get('CSS_prefixes_only', []),
)
# Replace the parens with a tabstop snippet
# TODO: Move the inside snippets to the corresponding snippets dict
if value and '()' in value:
if value.replace('()', '') in ['rotate','rotateX','rotateY','rotateZ','skew','skewX','skewY']:
value = value.replace('()', '($1${1/^((?!0$)-?(\d*.)?\d+)?.*$/(?1:deg)/m})')
else:
value = value.replace('()', '($1)')
# Do things when there is no value expanded
if not value or value == "#":
if not options.get('CSS_disable_postexpand', False):
auto_values = [val for prop, val in get_flat_css() if prop == args['property-name']]
if auto_values:
units = []
values = []
for p_value in (v for v in auto_values if len(v) > 1):
if p_value.startswith('.'):
units.append(p_value[1:])
elif not p_value.startswith('<'):
values.append(p_value)
values_splitted = split_for_snippet(values)
snippet_values = ''
for index in range(0,len(values_splitted[0])):
snippet_values += ''.join([
'${1/^\s*',
values_splitted[0][index],
'.*/',
values_splitted[1][index],
'/m}',
])
snippet_parts['autovalues'] += snippet_values
snippet_units = ''
# TODO: find out when to use units or colors
# TODO: Rewrite using after
if units and value != "#":
units_splitted = split_for_snippet(units, 4)
snippet_parts['before'].append({
"match": "%$",
"insert": "100"
})
# If there can be `number` in value, don't add `em` automatically
optional_unit_for_snippet = '(?2:(?3::0)em:px)'
if '<number>' in auto_values and not options.get('CSS_units_for_unitless_numbers'):
optional_unit_for_snippet = '(?2:(?3::0):)'
snippet_units = ''.join([
'${1/^\s*((?!0$)(?=.)[\d\-]*(\.)?(\d+)?((?=.)',
units_splitted[0][0],
')?$)?.*/(?4:',
units_splitted[1][0],
':(?1:' + optional_unit_for_snippet + '))/m}',
])
snippet_parts['autovalues'] += snippet_units
# Adding snippets for colors
if value == "#":
value = ''
# Insert hash and doubling letters
snippet_parts['before'].append({
"match": "([0-9a-fA-F]{1,6}|[0-9a-fA-F]{3,6}\s*(!\w*\s*)?)$",
"insert": "#"
})
snippet_parts['after'].append({
"match": "#?([0-9a-fA-F]{1,2})$",
"insert": "(?1:$1$1)"
})
# Insert `rgba` thingies
snippet_parts['before'].append({
"match": "(\d{1,3}%?),(\.)?.*$",
"insert": "rgba\((?2:$1,$1,)"
})
snippet_parts['after'].append({
"match": "(\d{1,3}%?),(\.)?(.+)?$",
"insert": "(?2:(?3::5):(?3::$1,$1,1))\)"
})
# Getting the value from the clipboard
# TODO: Move to the whole clipboard2default function
check_clipboard_for_color = COMPLEX_COLOR_REGEX.match(clipboard)
if check_clipboard_for_color and 'colors' in options.get('CSS_clipboard_defaults'):
snippet_parts['default'] = check_clipboard_for_color.group(1)
# TODO: move this out of `if not value`,
# so we could use it for found `url()` values
if '<url>' in auto_values:
snippet_parts['before'].append({
"match": "[^\s]+\.(jpg|jpeg|gif|png)$",
"insert": "url\("
})
snippet_parts['after'].append({
"match": "[^\s]+\.(jpg|jpeg|gif|png)$",
"insert": "\)"
})
check_clipboard_for_image = IMAGE_REGEX.match(clipboard)
if check_clipboard_for_image and 'images' in options.get('CSS_clipboard_defaults'):
quote_symbol = ''
if options.get('CSS_syntax_url_quotes'):
quote_symbol = options.get('CSS_syntax_quote_symbol')
snippet_parts['default'] = 'url(' + quote_symbol + check_clipboard_for_image.group(1) + quote_symbol + ')'
snippet_parts['value'] = value or ''
snippet = generate_snippet(snippet_parts)
# Apply settings to the colors in the values
def restyle_colors(match):
color = match.group(1)
# Change case of the colors in the value
if options.get('CSS_colors_case').lower() in ('uppercase' 'upper'):
color = color.upper()
elif options.get('CSS_colors_case').lower() in ('lowercase' 'lower'):
color = color.lower()
# Make colors short or longhand
if options.get('CSS_colors_length').lower() in ('short' 'shorthand') and len(color) == 6:
if color[0] == color[1] and color[2] == color[3] and color[4] == color[5]:
color = color[0] + color[2] + color[4]
elif options.get('CSS_colors_length').lower() in ('long' 'longhand') and len(color) == 3:
color = color[0] * 2 + color[1] * 2 + color[2] * 2
return '#' + color
snippet = COLOR_REGEX.sub(restyle_colors, snippet)
# Apply setting of the prefered quote symbol
if options.get('CSS_syntax_quote_symbol') == "'" and '"' in snippet:
snippet = snippet.replace('"',"'")
if options.get('CSS_syntax_quote_symbol') == '"' and "'" in snippet:
snippet = snippet.replace("'",'"')
newline_ending = ''
if options.get('CSS_newline_after_expand'):
newline_ending = '\n'
return '\n'.join(snippet.format(prop) for prop in property_) + newline_ending
# TODO
# display: -moz-inline-box;
# display: inline-block;
# background-image: -webkit-linear-gradient(top,rgba(255,255,255,0.6),rgba(255,255,255,0));
# background-image: -moz-linear-gradient(top,rgba(255,255,255,0.6),rgba(255,255,255,0));
# background-image: -o-linear-gradient(top,rgba(255,255,255,0.6),rgba(255,255,255,0));
# background-image: linear-gradient(top,rgba(255,255,255,0.6),rgba(255,255,255,0));