Skip to content

Commit

Permalink
Merge Sass/SCSS completions provider
Browse files Browse the repository at this point in the history
This commit implements SassCompletions and ScssCompletions based on 
a common BaseCompletionsProvider to share as much as possible code.
  • Loading branch information
deathaxe committed Mar 10, 2024
1 parent dce2100 commit 37b280d
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 1,024 deletions.
2 changes: 1 addition & 1 deletion plugins/completions/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from .provider import ScssCompletions
from .provider import *
128 changes: 71 additions & 57 deletions plugins/completions/provider.py
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -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):
Expand All @@ -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:
Expand All @@ -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 (
Expand All @@ -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",
Expand All @@ -144,10 +96,10 @@ def complete_property_value(self, view, prefix, pt):
if values:
details = f"<code>{prop}</code> 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):
Expand Down Expand Up @@ -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
Loading

0 comments on commit 37b280d

Please sign in to comment.