├── .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 | ![](https://dl.dropboxusercontent.com/u/4790638/images/ST-load-files-from-side-bar.png) 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 | ![](https://dl.dropboxusercontent.com/u/4790638/images/ST-pseudo-selector.png) 96 | 97 | ---- 98 | #### Extended Property Value Completions 99 | 100 | ![](https://dl.dropboxusercontent.com/u/4790638/images/ST-extended-css-property-values-2.png) 101 | 102 | ---- 103 | #### Class Completions Within Class Attribute 104 | ![](https://dl.dropboxusercontent.com/u/4790638/images/ST-class-completion-in-class-attribute-2.png) 105 | 106 | ---- 107 | #### Class Completions Within CSS Scope 108 | ![](https://dl.dropboxusercontent.com/u/4790638/images/ST-class-completion-in-css.png) 109 | 110 | ### LESS Completion Examples 111 | ---- 112 | ####Mixin Completions, with Parametric Mixin Tab Order 113 | ![](https://dl.dropboxusercontent.com/u/4790638/images/ST-LESS-mixin-completions.png) 114 | ![](https://dl.dropboxusercontent.com/u/4790638/images/ST-LESS-mixin-completions-with-snippet-tabbing.png) 115 | 116 | ### SCSS Completion Examples 117 | ---- 118 | ####Mixin Completions, with Parametric Mixin Tab Order 119 | ![](https://dl.dropboxusercontent.com/u/4790638/images/ST-SCSS-mixin-completions.png) 120 | ![](https://dl.dropboxusercontent.com/u/4790638/images/ST-SCSS-mixin-completions-with-snippet-tabbing.png) 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 | --------------------------------------------------------------------------------