Beispiel #1
0
def variable_color_swatch(view: sublime.View, region: sublime.Region, definition_scope_selector: str, show_errors: bool, on_pre_show_popup, on_hide_popup) -> None:
    """
    Display preview for color variable in Sass, SCSS & Less
    """
    variable_name = view.substr(region)
    definition_regions = [region for region in view.find_by_selector(definition_scope_selector) if view.substr(region) == variable_name]
    if len(definition_regions) == 0:
        status_message(view, show_errors, 'No definition found for variable {}'.format(variable_name))
        return
    elif len(definition_regions) > 1:
        status_message(view, show_errors, 'More than one definition found for variable {}'.format(variable_name))
        return
    a = view.find(r'\S', definition_regions[0].b).a
    msg = 'No valid color could be identified for variable {}'.format(variable_name)
    if view.substr(a) != ':':
        status_message(view, show_errors, msg)
        return
    a += 1
    b = view.find_by_class(definition_regions[0].b, forward=True, classes=sublime.CLASS_LINE_END)
    if a >= b:
        status_message(view, show_errors, msg)
        return
    value_region = sublime.Region(a, b)
    text = re.split('[;}]', view.substr(value_region))[0].strip()
    mcolor = Color.match(text)
    if mcolor is not None:
        mcolor.color.convert('srgb', in_place=True)  # type: ignore
        r = int(255 * mcolor.color.red)
        g = int(255 * mcolor.color.green)
        b = int(255 * mcolor.color.blue)
        a = mcolor.color.alpha
        rgba_color_swatch(view, region, r, g, b, a, on_pre_show_popup, on_hide_popup)
        return
    status_message(view, show_errors, msg)
Beispiel #2
0
 def on_hover(self, view: sublime.View, point: int, hover_zone: int) -> None:
     if hover_zone != sublime.HOVER_TEXT:
         return
     if self.active_region and self.active_region.contains(point):  # prevent flickering on small mouse movements
         return
     settings = sublime.load_settings(SETTINGS_FILE)
     if view.match_selector(point, settings.get('image_scope_selector')):
         if not settings.get('image_preview'):
             return
         region = view.extract_scope(point)
         image_preview(view, region, settings, settings.get('extensionless_image_preview'), False, self.set_active_region, self.reset_active_region)
     elif settings.get('color_preview'):
         if view.match_selector(point, SCOPE_SELECTOR_CSS_COLORNAME):
             region = view.word(point)
             rgb_color_swatch(view, region, self.set_active_region, self.reset_active_region)
         elif view.match_selector(point, SCOPE_SELECTOR_CSS_RGB_LITERAL):
             region = view.extract_scope(point)
             if region.a > 0 and region.size() in [3, 6] and view.substr(region.a - 1) == '#':  # fix for unconventional scopes from SCSS package
                 region.a -= 1
             rgb_color_swatch(view, region, self.set_active_region, self.reset_active_region)
         elif view.match_selector(point, SCOPE_SELECTOR_CSS_RGBA_LITERAL):
             region = view.extract_scope(point)
             r, g, b, a = hex2rgba(view.substr(region))
             rgba_color_swatch(view, region, r, g, b, a, self.set_active_region, self.reset_active_region)
         elif view.match_selector(point, SCOPE_SELECTOR_CSS_CUSTOM_PROPERTY_REFERENCE):
             region = view.extract_scope(point)
             css_custom_property_color_swatch(view, region, False, self.set_active_region, self.reset_active_region)
         elif view.match_selector(point, SCOPE_SELECTOR_SASS_VARIABLE_REFERENCE):
             region = view.extract_scope(point)
             variable_color_swatch(view, region, SCOPE_SELECTOR_SASS_VARIABLE_DEFINITION, False, self.set_active_region, self.reset_active_region)
         elif view.match_selector(point, SCOPE_SELECTOR_LESS_VARIABLE_REFERENCE):
             region = view.extract_scope(point)
             variable_color_swatch(view, region, SCOPE_SELECTOR_LESS_VARIABLE_DEFINITION, False, self.set_active_region, self.reset_active_region)
         elif view.match_selector(point, SCOPE_SELECTOR_SUBLIME_COLOR_SCHEME_VARIABLE_REFERENCE):
             region = view.extract_scope(point)
             sublime_variable_color_swatch(view, region, False, self.set_active_region, self.reset_active_region)
         # for now color variables from themes are not supported, because they can use legacy color syntax and it would be required to resolve 'extends' for themes
         elif view.match_selector(point, SCOPE_SELECTOR_CSS_FUNCTION):
             regions = view.find_by_selector(SCOPE_SELECTOR_CSS_FUNCTION)
             for region in regions:
                 if region.contains(point):
                     logging.debug(view.substr(region))
                     if view.match_selector(region.a, 'support.function.color'):
                         mcolor = Color.match(view.substr(region), fullmatch=True)  # https://facelessuser.github.io/coloraide/color/#color-matching
                         if mcolor is not None:
                             mcolor.color.convert('srgb', in_place=True)  # type: ignore
                             r = int(255 * mcolor.color.red)
                             g = int(255 * mcolor.color.green)
                             b = int(255 * mcolor.color.blue)
                             a = mcolor.color.alpha
                             rgba_color_swatch(view, region, r, g, b, a, self.set_active_region, self.reset_active_region)
                             return
                     elif view.match_selector(region.a, 'support.function.gradient'):
                         # @todo Find a python library to parse CSS gradients and convert to png image if possible
                         logging.debug('CSS gradients are not supported yet')
                         return
                         # if view.substr(region).startswith('linear-gradient'):
                         #     pass
                     break
Beispiel #3
0
def find_colors(text):
    """Find colors in text buffer."""

    colors = []
    for m in RE_COLOR_START.finditer(text):
        start = m.start()
        mcolor = Color.match(text, start=start)
        if mcolor is not None:
            colors.append(ColorTuple(text[mcolor.start:mcolor.end], mcolor.color))
    return colors
Beispiel #4
0
def parse_color(string, start=0, second=False):
    """
    Parse colors.

    The return of `more`:
    - `None`: there is no more colors to process
    - `True`: there are more colors to process
    - `False`: there are more colors to process, but we failed to find them.
    """

    length = len(string)
    more = None
    percent = None
    space = None
    # First color
    color = Color.match(string, start=start, fullmatch=False)
    if color:
        start = color.end
        if color.end != length:
            more = True
            # Percentage if provided
            m = RE_PERCENT.match(string, start)
            if m:
                start = m.end(0)
                text = m.group(1)
                percent = float(text.rstrip('%')) / 100.0

            # Is the first color in the input or the second?
            if not second:
                # Plus sign indicating we have an additional color to mix
                m = RE_PLUS.match(string, start)
                if m:
                    start = m.end(0)
                    more = start != length
                else:
                    more = False
            else:
                # Color space indicator
                m = RE_SPACE.match(string, start)
                if m:
                    text = m.group(1).lower()
                    if text in color.color.CS_MAP:
                        space = text
                        start = m.end(0)
                more = None if start == length else False

    if color:
        color.end = start
    return color, percent, more, space
Beispiel #5
0
def parse_color_contrast(string, start=0, second=False):
    """
    Parse colors.

    The return of `more`:
    - `None`: there is no more colors to process
    - `True`: there are more colors to process
    - `False`: there are more colors to process, but we failed to find them.
    """

    length = len(string)
    more = None
    ratio = None
    # First color
    color = Color.match(string, start=start, fullmatch=False, filters=util.SRGB_SPACES)
    if color:
        start = color.end
        if color.end != length:
            more = True

            m = RE_RATIO.match(string, start)
            if m:
                ratio = float(m.group(1))
                start = m.end(0)

            # Is the first color in the input or the second?
            if not second and not ratio:
                # Plus sign indicating we have an additional color to mix
                m = RE_SLASH.match(string, start)
                if m and not ratio:
                    start = m.end(0)
                    more = start != length
                else:
                    more = False
            else:
                more = None if start == length else False

    if color:
        color.end = start
    return color, ratio, more
Beispiel #6
0
def css_custom_property_color_swatch(view: sublime.View, region: sublime.Region, show_errors: bool, on_pre_show_popup, on_hide_popup) -> None:
    """
    Display preview for custom properties (variables) in CSS
    """
    custom_property_name = view.substr(region)
    definition_regions = [region for region in view.find_by_selector(SCOPE_SELECTOR_CSS_CUSTOM_PROPERTY_DEFINITION) if view.substr(region) == custom_property_name]
    # only proceed if there is exactly 1 definition for the custom property, because this implementation is
    # not aware of CSS rule scopes or possible inheritance resulting from the HTML structure
    if len(definition_regions) == 0:
        status_message(view, show_errors, 'No definition found for custom property {}'.format(custom_property_name))
        return
    elif len(definition_regions) > 1:
        status_message(view, show_errors, 'More than one definition found for custom property {}'.format(custom_property_name))
        return
    # extract next token
    a = view.find(r'\S', definition_regions[0].b).a
    msg = 'No valid color could be identified for custom property {}'.format(custom_property_name)
    if view.substr(a) != ':':
        status_message(view, show_errors, msg)
        return
    a += 1
    b = view.find_by_class(definition_regions[0].b, forward=True, classes=sublime.CLASS_LINE_END)
    if a >= b:
        status_message(view, show_errors, msg)
        return
    value_region = sublime.Region(a, b)
    text = re.split('[;}]', view.substr(value_region))[0].strip()
    mcolor = Color.match(text, fullmatch=True)  # fullmatch=True ensures that the custom property is only a color
    if mcolor is not None:
        mcolor.color.convert('srgb', in_place=True)  # type: ignore
        r = int(255 * mcolor.color.red)
        g = int(255 * mcolor.color.green)
        b = int(255 * mcolor.color.blue)
        a = mcolor.color.alpha
        rgba_color_swatch(view, region, r, g, b, a, on_pre_show_popup, on_hide_popup)
        return
    status_message(view, show_errors, msg)
Beispiel #7
0
def sublime_variable_color_swatch(view: sublime.View, region: sublime.Region, show_errors: bool, on_pre_show_popup, on_hide_popup) -> None:
    """
    Display preview for color variables in Sublime resource files (JSON)
    """
    filename = view.file_name()
    variable_name = view.substr(region)
    value = None
    if filename:  # search for variable also in overridden files
        data_path = os.path.dirname(sublime.packages_path())
        for resource in sublime.find_resources(os.path.basename(filename)):
            try:
                is_current_view = os.path.samefile(filename, os.path.join(data_path, resource))
                # use buffer content for current view, because there can be unsaved changes
                content = sublime.decode_value(view.substr(sublime.Region(0, view.size()))) if is_current_view else sublime.decode_value(sublime.load_resource(resource))
                if isinstance(content, dict) and isinstance(content.get('variables'), dict) and isinstance(content['variables'].get(variable_name), str):
                    value = content['variables'][variable_name]
            except:
                pass
    else:  # search for variable only in current view
        try:
            content = sublime.decode_value(view.substr(sublime.Region(0, view.size())))
            if isinstance(content, dict) and isinstance(content.get('variables'), dict) and isinstance(content['variables'].get(variable_name), str):
                value = content['variables'][variable_name]
        except:  # @todo Try to resolve variable via scopes like in CSS
            pass
    if value:
        mcolor = Color.match(value, fullmatch=True)  # @todo Also support minihtml color() mod function
        if mcolor is not None:
            mcolor.color.convert('srgb', in_place=True)  # type: ignore
            r = int(255 * mcolor.color.red)
            g = int(255 * mcolor.color.green)
            b = int(255 * mcolor.color.blue)
            a = mcolor.color.alpha
            rgba_color_swatch(view, region, r, g, b, a, on_pre_show_popup, on_hide_popup)
            return
    status_message(view, show_errors, 'No valid color could be identified for variable {}'.format(variable_name))
Beispiel #8
0
 def run(self, edit: sublime.Edit) -> None:
     if self.popup_active:
         self.view.hide_popup()
         return
     try:
         region = self.view.sel()[0]  # in case of multiple cursors only the first one is used, because there can only a single popup be visible at a time
     except IndexError:
         logging.error('no selections in the active view')
         return
     is_empty_selection = region.empty()
     if is_empty_selection:
         point = region.b
         region = self.view.line(point)
     elif len(self.view.lines(region)) > 1:
         window = self.view.window()
         if window:
             window.status_message('QuickView not possible for selections that span multiple lines')
         return
     else:
         point = region.begin()
     settings = sublime.load_settings(SETTINGS_FILE)
     if is_empty_selection and self.view.match_selector(point, settings.get('image_scope_selector')):
         region = self.view.extract_scope(point)
         image_preview(self.view, region, settings, True, True, self.set_popup_active, self.set_popup_inactive)
         return
     elif is_empty_selection and self.view.match_selector(point, SCOPE_SELECTOR_CSS_CUSTOM_PROPERTY_REFERENCE):
         region = self.view.extract_scope(point)
         css_custom_property_color_swatch(self.view, region, True, self.set_popup_active, self.set_popup_inactive)
         return
     elif is_empty_selection and self.view.match_selector(point, SCOPE_SELECTOR_SASS_VARIABLE_REFERENCE):
         region = self.view.extract_scope(point)
         variable_color_swatch(self.view, region, SCOPE_SELECTOR_SASS_VARIABLE_DEFINITION, True, self.set_popup_active, self.set_popup_inactive)
         return
     elif is_empty_selection and self.view.match_selector(point, SCOPE_SELECTOR_LESS_VARIABLE_REFERENCE):
         region = self.view.extract_scope(point)
         variable_color_swatch(self.view, region, SCOPE_SELECTOR_LESS_VARIABLE_DEFINITION, True, self.set_popup_active, self.set_popup_inactive)
         return
     elif is_empty_selection and self.view.match_selector(point, SCOPE_SELECTOR_SUBLIME_COLOR_SCHEME_VARIABLE_REFERENCE):
         region = self.view.extract_scope(point)
         sublime_variable_color_swatch(self.view, region, True, self.set_popup_active, self.set_popup_inactive)
         return
     else:
         text = self.view.substr(region)
         offset = region.begin()
         for m in IMAGE_URI_PATTERN.finditer(text):
             if not is_empty_selection or m.start() <= point - offset <= m.end():
                 link_region = sublime.Region(offset + m.start(), offset + m.end())
                 logging.debug('potential image URI found: %s', self.view.substr(link_region))
                 image_preview(self.view, link_region, settings, True, True, self.set_popup_active, self.set_popup_inactive)
                 return
             else:
                 break
         for m in COLOR_START_PATTERN.finditer(text):
             if not is_empty_selection or m.start() <= point - offset:
                 mcolor = Color.match(text, start=m.start())
                 if mcolor is not None and (not is_empty_selection or point - offset <= mcolor.end):  # type: ignore
                     color_region = sublime.Region(offset + mcolor.start, offset + mcolor.end)  # type: ignore
                     mcolor.color.convert('srgb', in_place=True)  # type: ignore
                     r = int(255 * mcolor.color.red)
                     g = int(255 * mcolor.color.green)
                     b = int(255 * mcolor.color.blue)
                     a = mcolor.color.alpha
                     rgba_color_swatch(self.view, color_region, r, g, b, a, self.set_popup_active, self.set_popup_inactive)
                     return
             else:
                 break
         msg = 'QuickView not possible at current cursor position' if is_empty_selection else 'QuickView not available for selection "{}"'.format(text)
         window = self.view.window()
         if window:
             window.status_message(msg)