diff --git a/plugins/completions/__init__.py b/plugins/completions/__init__.py
index b74c97eb..756e545f 100644
--- a/plugins/completions/__init__.py
+++ b/plugins/completions/__init__.py
@@ -1 +1 @@
-from .provider import ScssCompletions
+from .provider import *
diff --git a/plugins/completions/provider.py b/plugins/completions/provider.py
index 011a639e..355d7f9b 100755
--- a/plugins/completions/provider.py
+++ b/plugins/completions/provider.py
@@ -1,34 +1,18 @@
import re
import sublime
import sublime_plugin
-import timeit
-from functools import cached_property, wraps
+from functools import cached_property
from .function_args import get_func_args
from .properties import get_properties
-__all__ = ['ScssCompletions']
+__all__ = ['SassCompletions', 'ScssCompletions']
KIND_CSS_PROPERTY = (sublime.KIND_ID_KEYWORD, "p", "property")
KIND_CSS_FUNCTION = (sublime.KIND_ID_FUNCTION, "f", "function")
KIND_CSS_CONSTANT = (sublime.KIND_ID_VARIABLE, "c", "constant")
-ENABLE_TIMING = False
-
-
-def timing(func):
- @wraps(func)
- def wrap(*args, **kw):
- if ENABLE_TIMING:
- ts = timeit.default_timer()
- result = func(*args, **kw)
- if ENABLE_TIMING:
- te = timeit.default_timer()
- print(f"{func.__name__}({args}, {kw}) took: {1000.0 * (te - ts):2.3f} ms")
- return result
- return wrap
-
def match_selector(view, pt, scope):
# This will catch scenarios like:
@@ -44,7 +28,7 @@ def next_none_whitespace(view, pt):
return ch
-class ScssCompletions(sublime_plugin.EventListener):
+class BaseCompletionsProvider:
@cached_property
def func_args(self):
@@ -62,39 +46,7 @@ def re_name(self):
def re_value(self):
return re.compile(r"^(?:\s*(:)|([ \t]*))([^:]*)([;}])")
- @timing
- def on_query_completions(self, view, prefix, locations):
-
- settings = sublime.load_settings('SCSS.sublime-settings')
- if settings.get('disable_default_completions'):
- return None
-
- selector = settings.get('default_completions_selector')
- if not selector:
- return None
-
- if isinstance(selector, list):
- selector = ''.join(selector)
-
- pt = locations[0]
- if not match_selector(view, pt, selector):
- return None
-
- if match_selector(view, pt, "meta.property-value.css meta.function-call.arguments"):
- items = self.complete_function_argument(view, prefix, pt)
- elif view.match_selector(pt - 1, "meta.property-value.css, punctuation.separator.key-value"):
- items = self.complete_property_value(view, prefix, pt)
- elif view.match_selector(pt - 1, "meta.property-name.css, meta.property-list.css - meta.selector"):
- items = self.complete_property_name(view, prefix, pt)
- else:
- # TODO: provide selectors, at-rules
- items = None
-
- if items:
- return sublime.CompletionList(items)
- return None
-
- def complete_property_name(self, view, prefix, pt):
+ def complete_property_name(self, view, prefix, pt, semicolon):
text = view.substr(sublime.Region(pt, view.line(pt).end()))
matches = self.re_value.search(text)
if matches:
@@ -115,7 +67,7 @@ def complete_property_name(self, view, prefix, pt):
suffix = ":$0"
# terminate empty value if not within parentheses
- if not value and not term and not match_selector(view, pt, "meta.group"):
+ if semicolon and not value and not term and not match_selector(view, pt, "meta.group"):
suffix += ";"
return (
@@ -127,7 +79,7 @@ def complete_property_name(self, view, prefix, pt):
) for prop in self.props
)
- def complete_property_value(self, view, prefix, pt):
+ def complete_property_value(self, view, prefix, pt, semicolon):
completions = [
sublime.CompletionItem(
trigger="!important",
@@ -144,10 +96,10 @@ def complete_property_value(self, view, prefix, pt):
if values:
details = f"{prop}
property-value"
- if match_selector(view, pt, "meta.group") or next_none_whitespace(view, pt) == ";":
- suffix = ""
- else:
+ if semicolon and not match_selector(view, pt, "meta.group") and next_none_whitespace(view, pt) != ";":
suffix = "$0;"
+ else:
+ suffix = ""
for value in values:
if isinstance(value, list):
@@ -228,3 +180,65 @@ def complete_function_argument(self, view: sublime.View, prefix, pt):
))
return completions
+
+
+class SassCompletions(BaseCompletionsProvider, sublime_plugin.EventListener):
+
+ def on_query_completions(self, view, prefix, locations):
+ settings = sublime.load_settings('Sass.sublime-settings')
+ if settings.get('disable_default_completions'):
+ return None
+
+ selector = settings.get('default_completions_selector')
+ if not selector:
+ return None
+
+ if isinstance(selector, list):
+ selector = ''.join(selector)
+
+ pt = locations[0]
+ if not match_selector(view, pt, selector):
+ return None
+
+ if match_selector(view, pt, "meta.function-call.arguments"):
+ items = self.complete_function_argument(view, prefix, pt)
+ elif match_selector(view, pt - 1, "meta.property-value, punctuation.separator.key-value"):
+ items = self.complete_property_value(view, prefix, pt, False)
+ else:
+ items = self.complete_property_name(view, prefix, pt, False)
+
+ if items:
+ return sublime.CompletionList(items, sublime.INHIBIT_WORD_COMPLETIONS)
+ return None
+
+
+class ScssCompletions(BaseCompletionsProvider, sublime_plugin.EventListener):
+
+ def on_query_completions(self, view, prefix, locations):
+ settings = sublime.load_settings('SCSS.sublime-settings')
+ if settings.get('disable_default_completions'):
+ return None
+
+ selector = settings.get('default_completions_selector')
+ if not selector:
+ return None
+
+ if isinstance(selector, list):
+ selector = ''.join(selector)
+
+ pt = locations[0]
+ if not match_selector(view, pt, selector):
+ return None
+
+ if match_selector(view, pt, "meta.function-call.arguments"):
+ items = self.complete_function_argument(view, prefix, pt)
+ elif view.match_selector(pt - 1, "meta.property-value, punctuation.separator.key-value"):
+ items = self.complete_property_value(view, prefix, pt, True)
+ elif view.match_selector(pt - 1, "meta.property-name, meta.property-list - meta.selector"):
+ items = self.complete_property_name(view, prefix, pt, True)
+ else:
+ items = None
+
+ if items:
+ return sublime.CompletionList(items)
+ return None
diff --git a/sass_completions.py b/sass_completions.py
deleted file mode 100755
index 2001920a..00000000
--- a/sass_completions.py
+++ /dev/null
@@ -1,966 +0,0 @@
-import re
-import sublime
-import sublime_plugin
-import timeit
-
-from functools import cached_property, wraps
-
-__all__ = ['SassCompletions']
-
-KIND_CSS_PROPERTY = (sublime.KIND_ID_KEYWORD, "p", "property")
-KIND_CSS_FUNCTION = (sublime.KIND_ID_FUNCTION, "f", "function")
-KIND_CSS_CONSTANT = (sublime.KIND_ID_VARIABLE, "c", "constant")
-
-def get_common_values():
- common_values = {
- 'animation-direction': [
- 'alternate', 'alternate-reverse', 'normal', 'reverse'
- ],
- 'absolute-size': [
- 'xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large'
- ],
- 'absolute-weight': [
- '100', '200', '300', '400', '500', '600', '700', '800', '900',
- 'normal', 'bold'
- ],
- 'basic-shape': [
- ['circle()', 'circle($1)'],
- ['ellipse()', 'ellipse($1)'],
- ['inset()', 'inset($1)'],
- ['polygon()', 'polygon($1)']
- ],
- 'blend-mode': [
- 'normal', 'multiply', 'screen', 'overlay', 'darken', 'lighten',
- 'color-dodge', 'color-burn', 'hard-light', 'soft-light', 'difference',
- 'exclusion', 'hue', 'saturation', 'color', 'luminosity'
- ],
- 'border-style': [
- 'none', 'hidden', 'dotted', 'dashed', 'solid', 'double',
- 'groove', 'ridge', 'inset', 'outset'
- ],
- 'border-width': ['thin', 'medium', 'thick'],
- 'break-before-after': [
- 'always', 'left', 'right', 'recto', 'verso', 'page', 'column', 'region'
- ],
- 'break-inside': [
- 'auto', 'avoid', 'avoid-page', 'avoid-column', 'avoid-region'
- ],
- 'calc': [
- ['calc()', 'calc($1)'],
- ['clamp()', 'clamp(${1:0}, ${2:0}, ${3:0})'],
- ['max()', 'max(${1:0}, ${2:0})'],
- ['min()', 'min(${1:0}, ${2:0})']
- ],
- 'color': [
- 'currentColor',
- 'transparent',
- ['rgb()', 'rgb(${1:0}, ${2:0}, ${3:0})'],
- ['rgba()', 'rgba(${1:0}, ${2:0}, ${3:0}, ${4:1.0})'],
- ['hsl()', 'hsl(${1:0}, ${2:100%}, ${3:50%})'],
- ['hsla()', 'hsla(${1:0}, ${2:100%}, ${3:50%}, ${4:1.0})'],
- # Named colors
- 'aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure', 'beige',
- 'bisque', 'black', 'blanchedalmond', 'blue', 'blueviolet', 'brown',
- 'burlywood', 'cadetblue', 'chartreuse', 'chocolate', 'coral',
- 'cornflowerblue', 'cornsilk', 'crimson', 'cyan', 'darkblue',
- 'darkcyan', 'darkgoldenrod', 'darkgray', 'darkgrey', 'darkgreen',
- 'darkkhaki', 'darkmagenta', 'darkolivegreen', 'darkorange',
- 'darkorchid', 'darkred', 'darksalmon', 'darkseagreen',
- 'darkslateblue', 'darkslategray', 'darkslategrey', 'darkturquoise',
- 'darkviolet', 'deeppink', 'deepskyblue', 'dimgray', 'dimgrey',
- 'dodgerblue', 'firebrick', 'floralwhite', 'forestgreen', 'fuchsia',
- 'gainsboro', 'ghostwhite', 'gold', 'goldenrod', 'gray', 'grey',
- 'green', 'greenyellow', 'honeydew', 'hotpink', 'indianred', 'indigo',
- 'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen',
- 'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan',
- 'lightgoldenrodyellow', 'lightgray', 'lightgrey', 'lightgreen',
- 'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue',
- 'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow',
- 'lime', 'limegreen', 'linen', 'magenta', 'maroon', 'mediumaquamarine',
- 'mediumblue', 'mediumorchid', 'mediumpurple', 'mediumseagreen',
- 'mediumslateblue', 'mediumspringgreen', 'mediumturquoise',
- 'mediumvioletred', 'midnightblue', 'mintcream', 'mistyrose',
- 'moccasin', 'navajowhite', 'navy', 'oldlace', 'olive', 'olivedrab',
- 'orange', 'orangered', 'orchid', 'palegoldenrod', 'palegreen',
- 'paleturquoise', 'palevioletred', 'papayawhip', 'peachpuff', 'peru',
- 'pink', 'plum', 'powderblue', 'purple', 'rebeccapurple', 'red',
- 'rosybrown', 'royalblue', 'saddlebrown', 'salmon', 'sandybrown',
- 'seagreen', 'seashell', 'sienna', 'silver', 'skyblue', 'slateblue',
- 'slategray', 'slategrey', 'snow', 'springgreen', 'steelblue', 'tan',
- 'teal', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat', 'white',
- 'whitesmoke', 'yellow', 'yellowgreen'
- ],
- 'counter-style': [
- ['symbols()', 'symbols($1)']
- ],
- 'counter-symbols': [
- 'cyclic', 'numeric', 'alphabetic', 'symbolic', 'additive', 'fixed'
- ],
- 'ending-shape': ['circle', 'ellipse'],
- 'fill-rule': ['nonzero', 'evenodd'],
- 'filter-function': [
- ['blur()', 'blur($1)'],
- ['brightness()', 'brightness($1)'],
- ['contrast()', 'contrast($1)'],
- ['drop-shadow()', 'drop-shadow($1)'],
- ['grayscale()', 'grayscale($1)'],
- ['hue-rotate()', 'hue-rotate($1)'],
- ['invert()', 'invert($1)'],
- ['opacity()', 'opacity($1)'],
- ['saturate()', 'saturate($1)'],
- ['sepia()', 'sepia($1)']
- ],
- 'font-variant-alternates': [
- 'normal', 'historical-forms',
- ['stylistic()', 'stylistic($1)'],
- ['styleset()', 'styleset($1)'],
- ['character-variant()', 'character-variant($1)'],
- ['swash()', 'swash($1)'],
- ['ornaments()', 'ornaments($1)'],
- ['annotation()', 'annotation($1)']
- ],
- 'generic-font-name': [
- 'serif', 'sans-serif', 'cursive', 'fantasy', 'monospace'
- ],
- 'gradient': [
- ['conic-gradient()', 'conic-gradient($1)'],
- ['linear-gradient()', 'linear-gradient($1)'],
- ['radial-gradient()', 'radial-gradient($1)'],
- ['repeating-conic-gradient()', 'repeating-conic-gradient($1)'],
- ['repeating-linear-gradient()', 'repeating-linear-gradient($1)'],
- ['repeating-radial-gradient()', 'repeating-radial-gradient($1)']
- ],
- 'grid': [
- ['repeat()', 'repeat(${1:2}, ${2:1fr})'],
- ['minmax()', 'minmax(${1:100px}, ${2:1fr})'],
- ],
- 'image': [
- '',
- ['image()', 'image($1)'],
- ['image-set()', 'image-set($1)'],
- ['element()', 'element($1)'],
- ['paint()', 'paint($1)'],
- ['cross-fade()', 'cross-fade($1)'],
- ['linear-gradient()', 'linear-gradient($1)'],
- ['repeating-linear-gradient()', 'repeating-linear-gradient($1)'],
- ['radial-gradient()', 'radial-gradient($1)'],
- ['repeating-radial-gradient()', 'repeating-radial-gradient($1)'],
- ['conic-gradient()', 'conic-gradient($1)'],
- ],
- 'image-tags': ['ltr', 'rtl'],
- 'line-style': [
- 'none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove',
- 'ridge', 'inset', 'outset'
- ],
- 'leader-type': [
- 'dotted', 'solid', 'space'
- ],
- 'list-style-type': [
- 'none', 'inline', 'disc', 'circle', 'square', 'decimal',
- 'decimal-leading-zero', 'arabic-indic', 'binary', 'bengali',
- 'cambodian', 'khmer', 'devanagari', 'gujarati', 'gurmukhi',
- 'kannada', 'lower-hexadecimal', 'lao', 'malayalam', 'mongolian',
- 'myanmar', 'octal', 'oriya', 'persian', 'urdu', 'telugu',
- 'tibetan', 'thai', 'upper-hexadecimal', 'lower-roman',
- 'upper-roman', 'lower-greek', 'lower-alpha', 'lower-latin',
- 'upper-alpha', 'upper-latin', 'afar', 'ethiopic-halehame-aa-et',
- 'ethiopic-halehame-aa-er', 'amharic', 'ethiopic-halehame-am-et',
- 'amharic-abegede', 'ethiopic-abegede-am-et', 'cjk-earthly-branch',
- 'cjk-heavenly-stem', 'ethiopic', 'ethiopic-halehame-gez',
- 'ethiopic-abegede', 'ethiopic-abegede-gez', 'hangul-consonant',
- 'hangul', 'lower-norwegian', 'oromo', 'ethiopic-halehame-om-et',
- 'sidama', 'ethiopic-halehame-sid-et', 'somali',
- 'ethiopic-halehame-so-et', 'tigre', 'ethiopic-halehame-tig',
- 'tigrinya-er', 'ethiopic-halehame-ti-er', 'tigrinya-er-abegede',
- 'ethiopic-abegede-ti-er', 'tigrinya-et', 'ethiopic-halehame-ti-et',
- 'tigrinya-et-abegede', 'ethiopic-abegede-ti-et', 'upper-greek',
- 'upper-norwegian', 'asterisks', 'footnotes', 'hebrew', 'armenian',
- 'lower-armenian', 'upper-armenian', 'georgian', 'cjk-ideographic',
- 'hiragana', 'katakana', 'hiragana-iroha', 'katakana-iroha'
- ],
- 'position': ['', 'center'],
- 'relative-size': ['larger', 'smaller'],
- 'relative-weight': ['bolder', 'lighter'],
- 'repeat-style': [
- 'repeat', 'repeat-x', 'repeat-y', 'space', 'round', 'no-repeat'
- ],
- 'self-position': [
- 'center', 'start', 'end', 'self-start', 'self-end', 'flex-start',
- 'flex-end'
- ],
- 'shape-radius': [
- 'closest-side', 'farthest-side'
- ],
- 'side-or-corner': [
- 'left', 'right', 'top', 'bottom'
- ],
- 'timing-function': [
- 'linear',
- 'ease', 'ease-in', 'ease-out', 'ease-in-out', 'step-start', 'step-end',
- ['cubic-bezier()', 'cubic-bezier(${1:0.0}, ${2:0.0}, ${3:1.0}, ${4:1.0})'],
- ['steps()', 'steps(${1:2}, ${2:start})'],
- ],
- 'type-or-unit': [
- 'string', 'color', 'url', 'integer', 'number', 'length', 'angle',
- 'time', 'frequency', 'cap', 'ch', 'em', 'ex', 'ic', 'lh', 'rlh',
- 'rem', 'vb', 'vi', 'vw', 'vh', 'vmin', 'vmax', 'mm', 'Q', 'cm',
- 'in', 'pt', 'pc', 'px', 'deg', 'grad', 'rad', 'turn', 'ms', 's',
- 'Hz', 'kHz', '%'
- ],
- 'url': [['url()', 'url($1)']],
- }
-
- resolved_values = {}
-
- def resolve(vals):
- vals_resolved = []
- for val in vals:
- if val[0] == '<' and val[-1] == '>':
- key = val[1:-1]
- resolved = resolved_values.get(key)
- if resolved:
- vals_resolved += resolved
- continue
- resolved = common_values.get(key)
- if resolved:
- vals_resolved += resolve(resolved)
- continue
-
- vals_resolved.append(val)
- return vals_resolved
-
- for val_key, val_vals in common_values.items():
- resolved_values[val_key] = resolve(val_vals)
-
- return resolved_values
-
-
-def get_func_args():
-
- common_values = get_common_values()
-
- func_args = {
- 'attr': ['', ''],
- 'blur': [''],
- 'brightness': [''],
- 'calc': [
- ['attr()', 'attr($1)'],
- ''
- ],
- 'circle': ['', '', 'at', ''],
- 'clamp': [
- ['attr()', 'attr($1)'],
- ''
- ],
- 'conic-gradient': ['from', 'at', '', ''],
- 'contrast': [],
- 'counter': [''],
- 'counters': [''],
- 'cross-fade': ['', ''],
- 'cubic-bezier': [''],
- 'drop-shadow': ['', ''],
- 'element': [],
- 'ellipse': ['', '', 'at', ''],
- 'env': [],
- 'filter': ['', ''],
- 'fit-content': [],
- 'grayscale': [''],
- 'hsl': [''],
- 'hsla': [''],
- 'hue-rotate': [''],
- 'image': ['', '', ''],
- 'image-set': [
- ['type()', 'type($)'],
- '', ''
- ],
- 'inset': ['', 'round'],
- 'invert': [''],
- 'leader': [''],
- 'linear-gradient': ['', '', 'to'],
- 'matrix': [''],
- 'matrix3d': [''],
- 'max': [
- ['attr()', 'attr($1)'],
- ''
- ],
- 'min': [
- ['attr()', 'attr($1)'],
- ''
- ],
- 'minmax': ['min-content', 'max-content', 'auto'],
- 'opacity': [''],
- 'path': [''],
- 'paint': [],
- 'perspective': [''],
- 'polygon': ['', ''],
- 'radial-gradient': [
- '', '', 'at', '', ''
- ],
- 'rect': ['', 'auto'],
- 'repeat': ['', 'auto-fill', 'auto-fit'],
- 'repeating-conic-gradient': ['from', 'at', '', ''],
- 'repeating-linear-gradient': ['', '', 'to'],
- 'repeating-radial-gradient': [
- '', '', 'at', '', ''
- ],
- 'rgb': [''],
- 'rgba': [''],
- 'rotate': [''],
- 'rotate3d': [''],
- 'rotateX': [''],
- 'rotateY': [''],
- 'rotateZ': [''],
- 'saturate': [''],
- 'scale': [''],
- 'scale3d': [''],
- 'scaleX': [''],
- 'scaleY': [''],
- 'scaleZ': [''],
- 'skew': [''],
- 'skewX': [''],
- 'skewY': [''],
- 'sepia': [''],
- 'steps': ['', 'end', 'middle', 'start'],
- 'target-counter': ['', ''],
- 'target-counters': ['', ''],
- 'target-text': ['', 'content', 'before', 'after', 'first-letter'],
- 'toggle': ['', ''],
- 'translate': [''],
- 'translate3d': [''],
- 'translateX': [''],
- 'translateY': [''],
- 'translateZ': [''],
- 'var': [],
- }
-
- completions = {}
-
- for func, args in func_args.items():
- # args that are allowed for all properties
- expanded_args = [['var()', 'var($1)']]
-
- # Determine which args are available for the current property name
- for arg in args:
- if arg[0] == '<' and arg[-1] == '>':
- key = arg[1:-1]
- if key in common_values:
- expanded_args += common_values[key]
- else:
- expanded_args.append(arg)
-
- completions[func] = expanded_args
-
- return completions
-
-
-def get_properties():
- '''
- Gets the properties.
-
- Prepare some common property values for when there is more than one way to
- specify a certain value type. The color value for example can be specified
- by `rgb()` or `hsl()` and so on. Example where `|` denotes the caret:
-
- color: rg| --> color: rgb(|);
-
- This is also helpful when multiple properties share the same value types.
- '''
- common_values = get_common_values()
-
- properties_dict = {
- 'align-content': [
- 'center', 'flex-end', 'flex-start', 'space-around', 'space-between',
- 'stretch'
- ],
- 'align-items': [
- 'baseline', 'center', 'flex-end', 'flex-start', 'stretch'
- ],
- 'align-self': [
- 'auto', 'baseline', 'center', 'flex-end', 'flex-start', 'stretch'
- ],
- 'alignment-baseline': [
- 'baseline', 'middle', 'auto', 'before-edge', 'after-edge', 'central',
- 'text-before-edge', 'text-after-edge', 'ideographic', 'alphabetic',
- 'hanging', 'mathematical'
- ],
- 'animation': [
- 'none', '', 'infinite', '',
- 'forwards', 'backwards', 'both', 'running', 'paused'
- ],
- 'animation-name': ['none', ''],
- 'animation-duration': ['