├── .gitignore
├── Completion Rules.tmPreferences
├── Main.sublime-menu
├── README.md
├── Side Bar.sublime-menu
├── cache.py
├── changelog
├── 0.1.1.txt
├── 0.1.2.txt
├── 0.2.0.txt
├── 0.2.1.txt
├── 0.2.2.txt
├── 0.2.3.txt
├── 0.2.4.txt
├── 0.2.5.txt
├── 0.2.6.txt
├── 0.3.0.txt
├── 0.3.1.txt
├── 0.3.2.txt
├── 0.3.3.txt
├── 0.3.4.txt
├── 0.3.5.txt
├── 0.3.6.txt
├── 0.3.7.txt
└── install.txt
├── commands.py
├── completions.py
├── css_style_completions.py
├── css_style_completions.sublime-commands
├── css_style_completions.sublime-settings
├── extended_css_completions.py
├── location.py
├── messages.json
├── project.py
├── settings.py
└── style_parser.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 |
--------------------------------------------------------------------------------
/Completion Rules.tmPreferences:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | scope
6 | source.css meta.selector
7 | settings
8 |
9 | completion
10 | .*
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Main.sublime-menu:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "caption": "Preferences",
4 | "mnemonic": "n",
5 | "id": "preferences",
6 | "children":
7 | [
8 | {
9 | "caption": "Package Settings",
10 | "mnemonic": "P",
11 | "id": "package-settings",
12 | "children":
13 | [
14 | {
15 | "caption": "CSS Extended Completions",
16 | "children":
17 | [
18 | {
19 | "command": "open_file",
20 | "args": {"file": "${packages}/CSS Extended Completions/css_style_completions.sublime-settings"},
21 | "caption": "Settings – Default"
22 | },
23 | {
24 | "command": "open_file",
25 | "args": {"file": "${packages}/User/css_style_completions.sublime-settings"},
26 | "caption": "Settings – User"
27 | },
28 | {
29 | "caption": "-"
30 | },
31 | {
32 | "command": "css_extended_completion_set_setting",
33 | "args": {"setting": "use_emmet"},
34 | "caption": "Enable Emmet Completions",
35 | "checkbox": true
36 | },
37 | {
38 | "command": "css_extended_completion_set_setting",
39 | "args": {"setting": "auto_trigger_emmet_completions"},
40 | "caption": "Auto Trigger Emmet Completions",
41 | "checkbox": true
42 | },
43 | {
44 | "command": "css_extended_completion_set_setting",
45 | "args": {"setting": "save_cache_to_file"},
46 | "caption": "Save Cache To File",
47 | "checkbox": true
48 | }
49 | ]
50 | }
51 | ]
52 | }
53 | ]
54 | }
55 | ]
56 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | CSS Extended Completions
2 | ===================
3 |
4 | ###Sublime Text 2/3
5 |
6 | ---
7 |
8 | ####Issue Reporting
9 |
10 | **Please include the following information when submitting a ticket**
11 | - Sublime Text version
12 | - OS
13 | - any related error that you can find in the Sublime Text console (ctrl+`) if no related error is found please state that you saw no errors in the console.
14 |
15 | This plug-in is beta quality so please file any issues you run into here: https://github.com/subhaze/CSS-Extended/issues?state=open
16 |
17 | ---
18 | ###Features
19 | - CSS class completions within HTML class attributes (class="|") and CSS files
20 | - ID completions within HTML id attributes (id="|") and CSS files
21 | - LESS variable and mixin completions (with parametric tab-stops)
22 | - SCSS variable and mixin completions (with parametric tab-stops)
23 | - element completions within CSS files
24 | - pseudo selector completions within CSS files
25 | - font stack completions within the `font-family:` property
26 | - a more up-to-date property/value completion list within CSS files
27 | - parse linked style sheets in HTML files, can be disabled via `index_linked_style_sheets` setting
28 |
29 | ---
30 | ###Usage
31 |
32 | #### Load Files From Side Bar Menu
33 |
34 | You can add files from the side bar, just right click on a folder and select the type of files you'd like to load via `CSS Extended Completions > [file type(s)]`
35 |
36 | This is *not* a recursive process, so, only the immediate files in the folder are processed, the subfolders are not processed.
37 |
38 | 
39 |
40 | ----
41 |
42 | ####Cache On Save
43 |
44 | Caches completions on document save.
45 |
46 | * If the document is a .css file (stand alone file) it will add any symbols that are found to the main project index.
47 |
48 | * If the document is a .html/.php/etc... it will parse that file and extract any classes/IDs found within style tags and only show you those completions within that file, plus any from the main project cache.
49 |
50 | ----
51 |
52 | ####Loading External Files
53 |
54 | You can eager load files from folders outside of your project via `load_external_files` setting.
55 |
56 | Example: `"load_external_files": ["/abs/path/to/css/*.css", "/abs/path/to/less/*.less"]`
57 |
58 | ----
59 |
60 | ####Deleting Cache File
61 |
62 | You can delete the cache via the command palette `CSS Completions: Delete Cache`
63 |
64 | ----
65 |
66 | ####Pruning Cache File
67 |
68 | You can remove missing/moved files from the cache via the command palette `CSS Completions: Prune Cache`
69 |
70 | ----
71 |
72 | ####Property/Value Completions
73 |
74 | property/value completions such as `box-sizing`, additional font names for `font-family`, `animation`, `flex-box`, etc...
75 |
76 | ----
77 |
78 | #### [Emmet](http://emmet.io) Support
79 |
80 | Emmet support is enabled by default, you just need to add the following to your User Settings:
81 |
82 | ```json
83 | "auto_complete_selector": "source - comment, meta.tag - punctuation.definition.tag.begin, text.html.basic"
84 | ```
85 |
86 | Don't have Emmet? No problem, you won't have any problems with it being enable by default.
87 | Still want to disable Emmet support? Just set `"use_emmet": false` in the user's package settings or use the `Use Emmet` toggle from the menu `Preferences > Package Settings > CSS Extended Completions`
88 |
89 | ----
90 |
91 | ### CSS Completion Examples
92 | ----
93 | ####Pseudo Selector Completions
94 |
95 | 
96 |
97 | ----
98 | #### Extended Property Value Completions
99 |
100 | 
101 |
102 | ----
103 | #### Class Completions Within Class Attribute
104 | 
105 |
106 | ----
107 | #### Class Completions Within CSS Scope
108 | 
109 |
110 | ### LESS Completion Examples
111 | ----
112 | ####Mixin Completions, with Parametric Mixin Tab Order
113 | 
114 | 
115 |
116 | ### SCSS Completion Examples
117 | ----
118 | ####Mixin Completions, with Parametric Mixin Tab Order
119 | 
120 | 
121 |
122 |
--------------------------------------------------------------------------------
/Side Bar.sublime-menu:
--------------------------------------------------------------------------------
1 | [
2 | {"caption":"-", "id":"css-extended-completions"},
3 | {
4 | "caption": "CSS Extended Completions",
5 | "id": "css-extended-completions-parent",
6 | "args": {"paths": []},
7 | "children":[
8 | {
9 | "caption": "Add CSS Files To Cache",
10 | "id": "css-extended-completions-add-css-to-cache",
11 | "command": "add_to_cache",
12 | "args": {"paths": [], "file_type": "*.css"}
13 | },
14 | {
15 | "caption": "Add LESS Files To Cache",
16 | "id": "css-extended-completions-add-less-to-cache",
17 | "command": "add_to_cache",
18 | "args": {"paths": [], "file_type": "*.less"}
19 | },
20 | {
21 | "caption": "Add SCSS Files To Cache",
22 | "id": "css-extended-completions-add-scss-to-cache",
23 | "command": "add_to_cache",
24 | "args": {"paths": [], "file_type": "*.scss"}
25 | },
26 | {
27 | "caption": "Add All File Types To Cache",
28 | "id": "css-extended-completions-add-scss-to-cache",
29 | "command": "add_to_cache",
30 | "args": {"paths": [], "file_type": "*.*"}
31 | }
32 | ]
33 | }
34 | ]
35 |
--------------------------------------------------------------------------------
/cache.py:
--------------------------------------------------------------------------------
1 | import sublime, os, json
2 |
3 | ST2 = int(sublime.version()) < 3000
4 |
5 | if ST2:
6 | import commands
7 | import settings
8 | else:
9 | from . import commands
10 | from . import settings
11 |
12 | projects_cache = {}
13 | _file_path = None
14 |
15 |
16 | def get_cache_path():
17 | global _file_path, ST2
18 | if _file_path:
19 | return _file_path
20 |
21 | cache_end_point = ['CSS', 'CSS.completions.cache']
22 | if ST2:
23 | _file_path = [sublime.packages_path(), '..', 'Cache'] + cache_end_point
24 | else:
25 | _file_path = [sublime.cache_path()] + cache_end_point
26 |
27 | _file_path = os.path.join(*_file_path)
28 |
29 | _file_path = os.path.abspath(_file_path)
30 | cache_dir = _file_path.replace('CSS.completions.cache', '')
31 |
32 | if not os.path.exists(cache_dir):
33 | os.makedirs(cache_dir)
34 |
35 | return _file_path
36 |
37 |
38 | def prune_cache():
39 | global projects_cache
40 |
41 | load()
42 | missing = []
43 | for path in projects_cache:
44 | if not os.path.isfile(path) and not os.path.isdir(path):
45 | missing.append((projects_cache, path))
46 | if os.path.isdir(path):
47 | for project in projects_cache[path]:
48 | # f (dict of files)
49 | for f in projects_cache[path][project]:
50 | if not os.path.isfile(f):
51 | missing.append((projects_cache[path][project],f))
52 | print('CSS Extended: Removing %s items from cache.' % len(missing))
53 | for items in missing:
54 | del items[0][items[1]]
55 | save_cache()
56 |
57 |
58 | def remove_cache():
59 | global _file_path
60 |
61 | if _file_path and os.path.isfile(_file_path):
62 | os.remove(_file_path)
63 |
64 |
65 | def load():
66 | global projects_cache
67 | if not settings.get('save_cache_to_file'):
68 | return
69 | try:
70 | with open(get_cache_path(), 'r') as json_data:
71 | projects_cache = json.loads(json_data.read())
72 | except:
73 | return
74 |
75 |
76 | def save_cache():
77 | global projects_cache
78 | if settings.get('save_cache_to_file'):
79 | # save data to disk
80 | json_data = open(get_cache_path(), 'w')
81 | json_data.write(json.dumps(projects_cache))
82 | json_data.close()
83 |
84 |
85 | def get_keys(view, return_both=False):
86 | if view.is_scratch():
87 | project_name = view.name()
88 | if not ST2:
89 | project_name = sublime.active_window().project_file_name()
90 | if ST2 or not project_name:
91 | # we could be ST3 but not in a true project
92 | # so fall back to using current folders opened within ST
93 | project_name = '-'.join(sublime.active_window().folders())
94 |
95 | css_extension = settings.get("css_extension")
96 | try:
97 | file_extension = os.path.splitext(view.file_name())[1]
98 | except:
99 | file_extension = os.path.splitext(view.name())[1]
100 | file_name = view.file_name()
101 | if not file_name:
102 | file_name = view.name()
103 |
104 | # if we have a project and we're working in a stand alone style file
105 | # return the project file name as the key
106 | if file_extension in css_extension and project_name:
107 | return (file_name, project_name)
108 | # if we are not overriding to get both keys
109 | # just return the file_name/file_key
110 | if not return_both:
111 | return (file_name, None)
112 | elif return_both and project_name:
113 | return (file_name, project_name)
114 |
--------------------------------------------------------------------------------
/changelog/0.1.1.txt:
--------------------------------------------------------------------------------
1 | Update
2 |
3 | - Allow index caching in Sublime Text 3 even when you don't have a project file save.
--------------------------------------------------------------------------------
/changelog/0.1.2.txt:
--------------------------------------------------------------------------------
1 | Bug Fix
2 |
3 | - Resolves regression in ST 2 where it could not properly cache symbols
--------------------------------------------------------------------------------
/changelog/0.2.0.txt:
--------------------------------------------------------------------------------
1 | *NOTICE*
2 | With this update you should delete the current cache you have.
3 | To do this, run the command 'CSS Completions: Delete Cache' within the command palette
4 |
5 | Bug fixes
6 | - Remove cached symbols from file that are no longer present
7 |
8 | Updates
9 | - LESS variable completions
10 | - LESS mixin completions
11 | - SCSS variable completions
12 | - SCSS mixin completions
--------------------------------------------------------------------------------
/changelog/0.2.1.txt:
--------------------------------------------------------------------------------
1 | Settings file has been added
2 |
3 | Available Options
4 |
5 | - Options for what scopes trigger completions
6 | - Option to auto clear cache on close.
7 | Set "save_cache_to_file":false (defaults to true)
8 | - Auto load css/less/scss from external folders
9 | Example: "load_external_files":["/some/folder", "/another/folder"]
10 | (Must be absolute paths)
11 | - Option to adjust what pseudo selectors are added to completions
12 | - And more, just checkout the Default Settings file
13 | Preferences > Package Settings > CSS Extended Completions
14 |
15 |
16 | Bug Fixes
17 |
18 | - Tweak what scope less/scss mixins are triggered on
19 | (prevents mixins from trigger inside mixin function parameters)
--------------------------------------------------------------------------------
/changelog/0.2.2.txt:
--------------------------------------------------------------------------------
1 | Bug fix
2 |
3 | Adds a delay on loading external files for Sublime Text 2 which
4 | prevents plug-in from loading.
--------------------------------------------------------------------------------
/changelog/0.2.3.txt:
--------------------------------------------------------------------------------
1 | Bug Fixes
2 | - Fixes an issue in ST 3 when parsing some LESS files.
3 |
4 | Enhancements
5 | - Adds Side Bar menu item to easily add folders to index and cache within projects
6 |
--------------------------------------------------------------------------------
/changelog/0.2.4.txt:
--------------------------------------------------------------------------------
1 | Bug Fixes
2 | - Fixes issue where ST 3 would not properly load the cache file #13
3 |
4 | Enhancements
5 | - Adds a toggle in 'Preferences > Package Settings > CSS Extended Completions'
6 | for emmet completions called 'Use Emmet'
7 |
--------------------------------------------------------------------------------
/changelog/0.2.5.txt:
--------------------------------------------------------------------------------
1 | Maintenance Release
2 |
3 | Updates
4 | - Adds CSS symbol completions within LESS files
5 |
--------------------------------------------------------------------------------
/changelog/0.2.6.txt:
--------------------------------------------------------------------------------
1 | Bug Fix (ST 2)
2 |
3 | - Not displaying Class or ID's in HTML doc (ST2), Fixes #15
--------------------------------------------------------------------------------
/changelog/0.3.0.txt:
--------------------------------------------------------------------------------
1 | Updates
2 |
3 | - Adds scopes for Jade so most completions can be triggered within Jade files
4 | - Adds updated font-family stack and allows it to be configured via the package settings #21
5 | - - Uses the stacks found here http://www.awayback.com/revised-font-stack/
6 | - Adds option to try and parse linked style sheets on document save #21
7 | - Adds elements to the completion list in CSS files #21
8 | - - Configurable in the settings file
9 |
10 | Bug fixes
11 |
12 | - ST3 Issue - Current beta of ST3 will not add scopes to extremely long lines - Package now adds a new line after each "{" character before processing - Fixes #22
13 | - Fixes scope selector for LESS variable declarations
--------------------------------------------------------------------------------
/changelog/0.3.1.txt:
--------------------------------------------------------------------------------
1 | Bug Fixes
2 |
3 | - This update should resolve issues where the plug-in fails to parse files due to Unicode chars
--------------------------------------------------------------------------------
/changelog/0.3.2.txt:
--------------------------------------------------------------------------------
1 | Update
2 |
3 | - Now overrides ST 3's default behavior of blocking auto completion triggers in CSS scopes
--------------------------------------------------------------------------------
/changelog/0.3.3.txt:
--------------------------------------------------------------------------------
1 | Updates
2 |
3 | - Allows default CSS property/values to flow into the completion list
4 | - Adds source.stylus to give "some" completions to stylus
5 | the stylus syntax file I've installed doesn't have much going on
6 | in the scopes, so, there is minimal support.
7 |
--------------------------------------------------------------------------------
/changelog/0.3.4.txt:
--------------------------------------------------------------------------------
1 | Updates
2 |
3 | - Adds new command 'CSS Completions: Prune Cache' so you can remove missing/moved files from cache without having to delete the entire cache
4 |
--------------------------------------------------------------------------------
/changelog/0.3.5.txt:
--------------------------------------------------------------------------------
1 | Updates
2 |
3 | - Should now prevent CSS completions from popping up while typing `.` within embedded JS in HTML
4 | - Fixes issue where 'scratch_view' is, sometimes, not initialized correctly
5 |
--------------------------------------------------------------------------------
/changelog/0.3.6.txt:
--------------------------------------------------------------------------------
1 | Updates
2 |
3 | - Fixes scratch view regression from previous build
4 | - Adds scss class name completions
5 |
6 | Recommend to restart Sublime
7 |
--------------------------------------------------------------------------------
/changelog/0.3.7.txt:
--------------------------------------------------------------------------------
1 | Updates
2 |
3 | - Adds 'placeholder' completions for SCSS
4 |
--------------------------------------------------------------------------------
/changelog/install.txt:
--------------------------------------------------------------------------------
1 | CSS Extended Completions
2 |
3 | Save any file you're working on to add its CSS/LESS/SCSS symbols to the completion index.
4 |
5 | The following completions are provided:
6 |
7 | - Extended CSS property/values
8 | - CSS class names
9 | - CSS IDs
10 | - LESS variables/mixins/classes
11 | - SCSS variables/mixins/classes
12 |
13 | Available Options
14 |
15 | - Options for what scopes trigger completions
16 | - Option to auto clear cache on close.
17 | Set "save_cache_to_file":false (defaults to true)
18 | - Auto load css/less/scss from external folders
19 | Example: "load_external_files":["/some/folder", "/another/folder"]
20 | (Must be absolute paths)
21 | - Option to adjust what pseudo selectors are added to completions
22 | - And more, just checkout the Default Settings file
23 | Preferences > Package Settings > CSS Extended Completions
24 |
25 |
26 | To delete your cache, open the command palette and type 'CSS delete cache'
27 |
--------------------------------------------------------------------------------
/commands.py:
--------------------------------------------------------------------------------
1 | import sublime, sublime_plugin, re
2 |
3 | ST2 = int(sublime.version()) < 3000
4 |
5 |
6 | def simpleCompletionSet(view, point, file_name):
7 | symbols = view.substr(point).strip()
8 | completion = [(
9 | symbol + "\t " + file_name, symbol
10 | # use the first char to split on (#|.)
11 | ) for symbol in symbols.split(symbols[0])[1:]]
12 | return completion
13 |
14 |
15 | def scssMixinCompletionSet(view, region, file_name):
16 | # pattern for splitting up parameters
17 | re_split_params = re.compile(r',')
18 | end_region = view.find(r'\{', region.b)
19 | symbol = view.substr(region)
20 | symbol_snippet = view.substr(sublime.Region(region.b, end_region.a)).strip()
21 | # removes the parenthesis so we can template the parameters
22 | symbol_snippet = symbol_snippet[1:-1].strip()
23 | # used for displaying in the completion list
24 | mixin_params = re_split_params.split(symbol_snippet)
25 | # used for executing the completion
26 | mixin_params_completion = []
27 | if symbol_snippet:
28 | # if we have parameters
29 | # builds out the snippet for easily tabbing through parameters
30 | mixin_params_completion = [
31 | # we should end up with a string like: ${1:paramName}
32 | # make sure we also escape the '$' so completions expand properly
33 | '${%s:%s}' % (indx + 1, val.replace('$', '\\$'))
34 | for indx, val in enumerate(mixin_params)
35 | ]
36 | symbol_snippet_completion = '(' + ', '.join(mixin_params_completion) + ')'
37 | symbol_snippet = '(' + ', '.join(mixin_params) + ')'
38 | result = [(
39 | symbol + symbol_snippet + "\t " + file_name, symbol + symbol_snippet_completion + ';'
40 | )]
41 | return result
42 |
43 |
44 | def lessMixinCompletionSet(view, region, file_name):
45 | # pattern for splitting up parameters
46 | re_split_params = re.compile(r',|;')
47 | # pattern to determine if the mixin symbol
48 | # is a definition or it's being called
49 | end_region = view.find(r'(?'d style sheets in 'emmet_scoped' scoped files on save
9 | "index_linked_style_sheets": true,
10 | // returns class/id completions when typing out emmet expressions
11 | "use_emmet": true,
12 | // 'use_emmet' must be enabled for this to work correctly.
13 | // Adds the text.html scope with chars . and # to auto trigger the completion list
14 | "auto_trigger_emmet_completions": true,
15 | "emmet_scope": "text.html - source.js, source.cshtml, source.jade",
16 | "css_extension": [".css",".less",".scss"],
17 |
18 | // List from http://www.awayback.com/revised-font-stack/
19 | "font_list": [
20 | // serif
21 | "Garamond, Baskerville, 'Baskerville Old Face', 'Hoefler Text', 'Times New Roman', serif",
22 | "'Lucida Bright', Georgia, serif",
23 | "Palatino, 'Palatino Linotype', 'Palatino LT STD', 'Book Antiqua', Georgia, serif",
24 | "'Big Caslon', 'Book Antiqua', 'Palatino Linotype', Georgia, serif",
25 | "Didot, 'Didot LT STD', 'Hoefler Text', Garamond, 'Times New Roman', serif",
26 | "Baskerville, 'Baskerville old face', 'Hoefler Text', Garamond, 'Times New Roman', serif",
27 | "'Hoefler Text', 'Baskerville old face', Garamond, 'Times New Roman', serif",
28 | "'Bodoni MT', Didot, 'Didot LT STD', 'Hoefler Text', Garamond, 'Times New Roman', serif",
29 | "'Goudy Old Style', Garamond, 'Big Caslon', 'Times New Roman', serif",
30 | "Constantia, Palatino, 'Palatino Linotype', 'Palatino LT STD', Georgia, serif",
31 | "Cambria, Georgia, serif",
32 | "'Book Antiqua', Palatino, 'Palatino Linotype', 'Palatino LT STD', Georgia, serif",
33 |
34 | // sans-serif
35 | "Optima, Segoe, 'Segoe UI', Candara, Calibri, Arial, sans-serif",
36 | "Futura, 'Trebuchet MS', Arial, sans-serif",
37 | "'Gill Sans', 'Gill Sans MT', Calibri, sans-serif",
38 | "'Trebuchet MS', 'Lucida Grande', 'Lucida Sans Unicode', 'Lucida Sans', Tahoma, sans-serif",
39 | "'Helvetica Neue', Helvetica, Arial, sans-serif",
40 | "Verdana, Geneva, sans-serif",
41 | "'Lucida Grande', 'Lucida Sans Unicode', 'Lucida Sans', Geneva, Verdana, sans-serif",
42 | "Geneva, Tahoma, Verdana, sans-serif",
43 | "Segoe, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif",
44 | "Candara, Calibri, Segoe, 'Segoe UI', Optima, Arial, sans-serif",
45 | "Calibri, Candara, Segoe, 'Segoe UI', Optima, Arial, sans-serif",
46 | "'Franklin Gothic Medium', Arial, sans-serif",
47 | "Tahoma, Geneva, Verdana, sans-serif"
48 | ],
49 | "pseudo_selector_list": [
50 | "after",
51 | "before",
52 | "checked",
53 | "default",
54 | "disabled",
55 | "empty",
56 | "enabled",
57 | "first",
58 | "first-child",
59 | "first-letter",
60 | "first-line",
61 | "first-of-type",
62 | "focus",
63 | "fullscreen",
64 | "hover",
65 | "indeterminate",
66 | "invalid",
67 | "lang",
68 | "last-child",
69 | "last-of-type",
70 | "left",
71 | "link",
72 | "not",
73 | "nth-child",
74 | "nth-last-child",
75 | "nth-last-of-type",
76 | "nth-of-type",
77 | "only-child",
78 | "only-type-of",
79 | "optional",
80 | "read-only",
81 | "read-write",
82 | "required",
83 | "right",
84 | "root",
85 | "scope",
86 | "target",
87 | "valid",
88 | "visited"
89 | ],
90 | "element_list": [
91 | "html",
92 | "body",
93 | "section",
94 | "nav",
95 | "article",
96 | "aside",
97 | "h1",
98 | "h2",
99 | "h3",
100 | "h4",
101 | "h5",
102 | "h6",
103 | "header",
104 | "footer",
105 | "address",
106 | "main",
107 | "p",
108 | "hr",
109 | "pre",
110 | "blockquote",
111 | "ol",
112 | "ul",
113 | "li",
114 | "dl",
115 | "dt",
116 | "dd",
117 | "figure",
118 | "figcaption",
119 | "div",
120 | "a",
121 | "em",
122 | "strong",
123 | "small",
124 | "s",
125 | "cite",
126 | "q",
127 | "dfn",
128 | "abbr",
129 | "itl",
130 | "data",
131 | "time",
132 | "atetim",
133 | "code",
134 | "var",
135 | "samp",
136 | "kbd",
137 | "sub",
138 | "sup",
139 | "b",
140 | "u",
141 | "mark",
142 | "ruby",
143 | "rt",
144 | "rp",
145 | "bdi",
146 | "bdo",
147 | "span",
148 | "las",
149 | "an",
150 | "i",
151 | "br",
152 | "wbr",
153 | "ins",
154 | "del",
155 | "img",
156 | "iframe",
157 | "embed",
158 | "object",
159 | "param",
160 | "video",
161 | "audio",
162 | "source",
163 | "track",
164 | "canvas",
165 | "map",
166 | "area",
167 | "svg",
168 | "math",
169 | "table",
170 | "caption",
171 | "colgroup",
172 | "col",
173 | "tbody",
174 | "thead",
175 | "tfoot",
176 | "tr",
177 | "td",
178 | "th",
179 | "form",
180 | "fieldset",
181 | "legend",
182 | "label",
183 | "input",
184 | "button",
185 | "select",
186 | "datalist",
187 | "optgroup",
188 | "option",
189 | "textarea",
190 | "keygen",
191 | "output",
192 | "progress",
193 | "meter",
194 | "details",
195 | "summary",
196 | "menuitem",
197 | "menu"
198 | ]
199 | }
--------------------------------------------------------------------------------
/extended_css_completions.py:
--------------------------------------------------------------------------------
1 | import sublime, sublime_plugin, re
2 |
3 | ST2 = int(sublime.version()) < 3000
4 | if ST2:
5 | import settings
6 | else:
7 | from . import settings
8 |
9 |
10 | def extended_common():
11 | return {
12 | "color": ["rgb($1)", "rgba($1)", "hsl($1)", "hsla($1)", "transparent"],
13 | "uri": ["url($1)"],
14 | "border-style": ["none", "hidden", "dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset"],
15 | "border-width": ["thin", "medium", "thick"],
16 | "box": ["border-box", "padding-box", "content-box"],
17 | "shape": ["rect($1)"],
18 | "generic-family": ["serif", "sans-serif", "cursive", "fantasy", "monospace"],
19 | "family-name": settings.get('font_list', [])
20 | }
21 |
22 | extended_css_data = """
23 | "font-family"= | | inherit
24 | "display"=flex | inline-flex | compact | container | run-in
25 | "icon"=auto | | inherit
26 | "box-sizing"=content-box | padding-box | border-box | inherit
27 | "outline-offset"= | inherit
28 | "resize"=none | both | horizontal | vertical | inherit
29 | "text-overflow"=clip | ellipsis | inherit
30 | "cursor"= | auto | default | none | context-menu | help | pointer | progress | wait | cell | crosshair | text | vertical-text | alias | copy | move | no-drop | not-allowed | e-resize | n-resize | ne-resize | nw-resize | s-resize | se-resize | sw-resize | w-resize | ew-resize | ns-resize | nesw-resize | nwse-resize | col-resize | row-resize | all-scroll | zoom-in | zoom-out | inherit
31 | "nav-index"=auto | | inherit
32 | "nav-up" "nav-right" "nav-down" "nav-left"=auto | current | root | inherit
33 | "ime-mode"=auto | normal | active | inactive | disabled | inherit
34 |
35 | "flex-basis"= | | inherit | auto
36 | "flex-direction"=row | row-reverse | column | column-reverse | inherit
37 | "flex-flow"= |
38 | "flex-grow"= | inherit
39 | "flex-shrink"= | inherit
40 | "flex-wrap"=nowrap | wrap | wrap-reverse | inherit
41 | "justify-content"=flex-start | flex-end | center | space-between | space-around
42 | "order"= | inherit
43 | "align-content"=flex-start | flex-end | center | space-between | space-around | stretch | inherit
44 | "align-items"=flex-start | flex-end | center | baseline | stretch | inherit
45 | "align-self"=auto | flex-start | flex-end | center | baseline | stretch | inherit
46 |
47 | "transform"=none |
48 | "transform-origin"=left | center | right | top | bottom | |
49 | "transform-style"=flat | preserve-3d
50 | "perspective"=none |
51 | "perspective-origin"=left | center | right | top | bottom | |
52 | "backface-visibility"=visible | hidden
53 |
54 | "text-transform"=none | capitalize | uppercase | lowercase | full-width
55 | "white-space"=normal | pre | nowrap | pre-wrap | pre-line
56 | "tab-size"= |
57 | "line-break"=auto | loose | normal | strict
58 | "word-break"=normal | keep-all | break-all
59 | "hyphens"=none | manual | auto
60 | "overflow-wrap"=normal | break-word
61 | "word-wrap"=normal | break-word
62 | "text-align"=start | end | left | right | center | justify | match-parent | start end
63 | "text-align-last"=auto | start | end | left | right | center | justify
64 | "text-justify"=auto | none | inter-word | distribute
65 | "word-spacing"=normal | |
66 | "letter-spacing"=normal |
67 | "text-indent"= | | hanging | each-line
68 | "hanging-punctuation"=none | first | force-end | allow-end | last
69 |
70 | "backface-visibility"=visible | hidden
71 | "transition-property"=none |
72 | "transition-duration"=
73 | "transition-timing-function"=
74 | "transition-delay"=
75 | "transition"=
76 |
77 | "animation-name"=none
78 | "animation-duration"=
79 | "animation-timing-function"=ease | steps($1, start)$0 | steps($1, end)$0 | step-start | step-end | linear | ease-out | ease-in-out | ease-in | cubic-bezier($1, $2, $3, $4)$0
80 | "animation-iteration-count"=1 | infinite |
81 | "animation-direction"=normal | reverse | alternate-reverse | alternate
82 | "animation-play-state"=running | paused
83 | "animation-delay"=
84 | "animation-fill-mode"=none | forwards | both | backwards
85 | "animation"= | | | | | |
86 |
87 | "font-family"= |
88 | "font-weight"=normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900
89 | "font-stretch"=normal | ultra-condensed | extra-condensed | condensed | semi-condensed | semi-expanded | expanded | extra-expanded | ultra-expanded
90 | "font-style"=normal | italic | oblique
91 | "font-size"= | | |
92 | "font-size-adjust"=none |
93 | "font"= | | | | | | | caption | icon | menu | message-box | small-caption | status-bar
94 | "font-synthesis"=none | weight | style
95 | "font-kerning"=auto | normal | none
96 | "font-variant-ligatures"=normal | none | | | |
97 | "font-variant-position"=normal | sub | super
98 | "font-variant-caps"=normal | small-caps | all-small-caps | petite-caps | all-petite-caps | unicase | titling-caps
99 | "font-variant-numeric"=normal | | | | ordinal | slashed-zero
100 | "font-variant-alternates"=normal | stylistic() | historical-forms | styleset() | character-variant() | swash() | ornaments() | annotation()
101 | "font-variant-east-asian"=normal | | | ruby
102 | "font-variant"=normal | none | | | | | stylistic() | historical-forms | styleset() | character-variant() | swash() | ornaments() | annotation() | small-caps | all-small-caps | petite-caps | all-petite-caps | unicase | titling-caps | | | | ordinal | slashed-zero | | | ruby
103 | "font-feature-settings"=normal |
104 | "font-language-override"=normal |
105 | """
106 |
107 |
108 | def parse_css_data(data):
109 | props = {}
110 | for l in data.splitlines():
111 | if l == "":
112 | continue
113 |
114 | names, values = l.split('=')
115 |
116 | allowed_values = []
117 | for v in values.split('|'):
118 | v = v.strip()
119 | if v[0] == '<' and v[-1] == '>':
120 | key = v[1:-1]
121 | if key in extended_common():
122 | allowed_values += extended_common()[key]
123 | else:
124 | allowed_values.append(v)
125 |
126 | for e in names.split():
127 | if e[0] == '"':
128 | props[e[1:-1]] = sorted(allowed_values)
129 | else:
130 | break
131 |
132 | return props
133 |
134 |
135 | class CSSCompletions(sublime_plugin.EventListener):
136 | props = None
137 | rex = None
138 |
139 | def on_query_completions(self, view, prefix, locations):
140 | if not view.match_selector(locations[0], "source.stylus, source.scss - meta.selector.css, source.less - meta.selector.css, source.css - meta.selector.css"):
141 | return []
142 |
143 | if not self.props:
144 | self.props = parse_css_data(extended_css_data)
145 | self.rex = re.compile("([a-zA-Z-]+):\s*$")
146 |
147 | l = []
148 | if (
149 | view.match_selector(locations[0], "meta.property-value.css, meta.property-value.scss")
150 | # This will catch scenarios like .foo {font-style: |}
151 | or view.match_selector(locations[0] - 1, "meta.property-value.css, meta.property-value.scss")
152 | ):
153 | loc = locations[0] - len(prefix)
154 | line = view.substr(sublime.Region(view.line(loc).begin(), loc))
155 |
156 | m = re.search(self.rex, line)
157 | if m:
158 | prop_name = m.group(1)
159 | if prop_name in self.props:
160 | values = self.props[prop_name]
161 |
162 | add_semi_colon = view.substr(sublime.Region(locations[0], locations[0] + 1)) != ';'
163 |
164 | for v in values:
165 | desc = v
166 | snippet = v
167 |
168 | if add_semi_colon:
169 | snippet += ";"
170 |
171 | if snippet.find("$1") != -1:
172 | desc = desc.replace("$1", "")
173 |
174 | l.append((desc, snippet))
175 |
176 | return (l, sublime.INHIBIT_WORD_COMPLETIONS)
177 |
178 | return None
179 | else:
180 | add_colon = not view.match_selector(locations[0], "meta.property-name.css")
181 |
182 | for p in self.props:
183 | if add_colon:
184 | l.append((p, p + ": "))
185 | else:
186 | l.append((p, p))
187 |
188 | return (l, sublime.INHIBIT_WORD_COMPLETIONS)
189 |
--------------------------------------------------------------------------------
/location.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 |
4 | def at_html_attribute(attribute, view, locations):
5 | check_attribute = ''
6 | view_point = locations[0]
7 | char = ''
8 | selector_score = 1
9 | while((not char.isspace() or selector_score != 0) and view_point > -1):
10 | char = view.substr(view_point)
11 | selector_score = view.score_selector(view_point, 'string')
12 | if(not char.isspace() or selector_score != 0):
13 | check_attribute += char
14 | view_point -= 1
15 | check_attribute = check_attribute[::-1].replace('(', '')
16 | if check_attribute.startswith(attribute):
17 | return True
18 | return False
19 |
20 |
21 | def at_style_symbol(style_symbol, style_scope, view, locations):
22 | selector = view.match_selector(locations[0]-1, style_scope)
23 | if not selector:
24 | return False
25 | check_attribute = ''
26 | view_point = locations[0] - 1
27 | char = ''
28 | while(
29 | char != style_symbol and not re.match(r'[\n ]', char)
30 | and view_point > -1
31 | ):
32 | char = view.substr(view_point)
33 | check_attribute += char
34 | view_point -= 1
35 | check_attribute = check_attribute[::-1]
36 | if check_attribute.startswith(style_symbol):
37 | return True
38 | return False
39 |
--------------------------------------------------------------------------------
/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "install": "changelog/install.txt",
3 | "0.1.1": "changelog/0.1.1.txt",
4 | "0.1.2": "changelog/0.1.2.txt",
5 | "0.2.0": "changelog/0.2.0.txt",
6 | "0.2.1": "changelog/0.2.1.txt",
7 | "0.2.2": "changelog/0.2.2.txt",
8 | "0.2.3": "changelog/0.2.3.txt",
9 | "0.2.4": "changelog/0.2.4.txt",
10 | "0.2.5": "changelog/0.2.5.txt",
11 | "0.2.6": "changelog/0.2.6.txt",
12 | "0.3.0": "changelog/0.3.0.txt",
13 | "0.3.1": "changelog/0.3.1.txt",
14 | "0.3.2": "changelog/0.3.2.txt",
15 | "0.3.3": "changelog/0.3.3.txt",
16 | "0.3.4": "changelog/0.3.4.txt",
17 | "0.3.5": "changelog/0.3.5.txt",
18 | "0.3.6": "changelog/0.3.6.txt",
19 | "0.3.7": "changelog/0.3.7.txt"
20 | }
--------------------------------------------------------------------------------
/project.py:
--------------------------------------------------------------------------------
1 | import sublime, glob
2 |
3 | ST2 = int(sublime.version()) < 3000
4 |
5 | if ST2:
6 | import settings
7 | else:
8 | from . import settings
9 |
10 |
11 | def get_external_files():
12 | external_files = []
13 | for file_path in settings.get('load_external_files', []):
14 | external_files.extend(glob.glob(file_path))
15 | return external_files
16 |
--------------------------------------------------------------------------------
/settings.py:
--------------------------------------------------------------------------------
1 | import sublime
2 |
3 |
4 | def get(key, default=None):
5 | settings = sublime.load_settings('css_style_completions.sublime-settings')
6 | if settings:
7 | return settings.get(key, default)
8 | else:
9 | return default
10 |
--------------------------------------------------------------------------------
/style_parser.py:
--------------------------------------------------------------------------------
1 | import sublime, sublime_plugin, os, re, time
2 |
3 | ST2 = int(sublime.version()) < 3000
4 | scratch_view = None
5 |
6 | if ST2:
7 | import cache
8 | import commands
9 | import completions
10 | import settings
11 | import project
12 | else:
13 | from . import cache
14 | from . import commands
15 | from . import completions
16 | from . import settings
17 | from . import project
18 |
19 |
20 | def init_file_loading():
21 | if not sublime.active_window():
22 | sublime.set_timeout(lambda: init_file_loading(), 500)
23 | else:
24 | load_files(project.get_external_files())
25 |
26 |
27 | def get_output_panel(name='CSS Extended Completions'):
28 | '''
29 | Used for loading in files outside of project view
30 | '''
31 | global scratch_view
32 | if not scratch_view:
33 | if ST2:
34 | scratch_view = sublime.active_window().get_output_panel(name)
35 | else:
36 | scratch_view = sublime.active_window().create_output_panel(name)
37 | return scratch_view
38 |
39 |
40 | def _find_file(name, path):
41 | result = []
42 | for root, dirs, files in os.walk(path):
43 | if name in files:
44 | result.append(os.path.join(root, name))
45 | return result
46 |
47 |
48 | def load_linked_files(view):
49 | html_scope = settings.get('emmet_scope', '')
50 | if settings.get('index_linked_style_sheets', True) and view.score_selector(0, html_scope):
51 | import ntpath
52 | files = []
53 | links = []
54 | # regex should find, html|jade|haml style links
55 | view.find_all(r'()\s*("|\')(.*?)("|\')', 0, r'$4', links)
56 | for path in view.window().folders():
57 | for css_path in links:
58 | files.extend(_find_file(ntpath.basename(css_path), path))
59 | print('Found styles linked in HTML')
60 | print(files)
61 | load_files(files, as_scratch=False)
62 |
63 |
64 | def load_files(file_list, as_scratch=True):
65 | syntax_file = {
66 | 'css': 'Packages/CSS/CSS.tmLanguage',
67 | 'less': 'Packages/LESS/LESS.tmLanguage',
68 | 'scss': 'Packages/SCSS/SCSS.tmLanguage'
69 | }
70 |
71 | get_output_panel().set_scratch(as_scratch)
72 | # sort file list by extension to reduce the frequency
73 | # of syntax file loads
74 | sorted(file_list, key=lambda x: x.split(".")[-1])
75 | current_syntax = {
76 | 'isThis': ''
77 | }
78 | file_count = len(file_list)
79 |
80 | def parse_file(file_path, indx):
81 | file_extension = os.path.splitext(file_path)[1][1:]
82 |
83 | # Check if we have a syntax file
84 | if not file_extension in syntax_file:
85 | return
86 | # Check if we match CSS extensions listed
87 | if not file_path.endswith(tuple(settings.get('css_extension', ()))):
88 | return
89 |
90 | print('PARSING FILE', file_path)
91 | if not syntax_file[file_extension] == current_syntax['isThis']:
92 | get_output_panel().set_syntax_file(syntax_file[file_extension])
93 | current_syntax['isThis'] = syntax_file[file_extension]
94 | sublime.status_message(
95 | 'CSS Extended: parsing file %s of %s' % (indx + 1, file_count)
96 | )
97 | try:
98 | get_output_panel().set_name(file_path)
99 | with open(file_path, 'r', encoding='utf8') as f:
100 | content = f.read()
101 | sublime.active_window().run_command(
102 | 'css_extended_completions_file',
103 | {"content": content}
104 | )
105 | update_cache(get_output_panel())
106 | except IOError:
107 | pass
108 |
109 | parse_delay = 100
110 | for indx, file_path in enumerate(file_list):
111 | if not os.path.isfile(file_path):
112 | continue
113 | sublime.set_timeout(
114 | lambda file_path=file_path, indx=indx: parse_file(file_path, indx),
115 | parse_delay
116 | )
117 | parse_delay = parse_delay + 500
118 |
119 |
120 | def parse_view(view):
121 | file_path = view.file_name()
122 |
123 | # Check if we match CSS extensions listed
124 | if not file_path.endswith(tuple(settings.get('css_extension', ()))):
125 | return
126 |
127 | print('PARSING SAVED VIEW')
128 | get_output_panel().set_syntax_file(view.settings().get('syntax'))
129 | try:
130 | get_output_panel().set_name(file_path)
131 | with open(file_path, 'r', encoding='utf8') as f:
132 | content = f.read()
133 | sublime.active_window().run_command(
134 | 'css_extended_completions_file',
135 | {"content": content}
136 | )
137 | update_cache(get_output_panel())
138 | except IOError:
139 | pass
140 |
141 |
142 | def update_cache(view):
143 | projects_cache = cache.projects_cache
144 | file_key, project_key = cache.get_keys(view)
145 | # if there is no project_key set the project_key as the file_key
146 | # so that we can cache on a per file basis
147 | if not project_key:
148 | project_key = file_key
149 | if project_key in projects_cache:
150 | _cache = projects_cache[project_key]
151 | else:
152 | _cache = {}
153 |
154 | for symbol in commands.symbol_dict:
155 | if '_command' in symbol:
156 | continue
157 | if symbol not in _cache:
158 | _cache[symbol] = {}
159 | _completions = completions.get_view_completions(view, symbol)
160 | if _completions:
161 | _cache[symbol][file_key] = _completions
162 | elif not _cache[symbol]:
163 | _cache.pop(symbol, None)
164 | if _cache:
165 | projects_cache[project_key] = _cache
166 | cache.save_cache()
167 |
168 |
169 | class CssExtendedCompletionsFileCommand(sublime_plugin.TextCommand):
170 |
171 | def run(self, edit, content):
172 | # add space between any )} chars
173 | # ST3 throws an error in some LESS files that do this
174 | content = re.sub(r'\)\}', r') }', content)
175 | content = re.sub(r'\}', '}\n', content)
176 | content = re.sub(r'\*/', '*/\n', content)
177 | panel = get_output_panel()
178 | panel.erase(edit, sublime.Region(0, panel.size()))
179 | panel.insert(edit, 0, content)
180 | # call size to force ST to acknowledge new content
181 | # sometimes it seems to fail on knowing new content is there
182 | panel.size()
183 |
--------------------------------------------------------------------------------