diff --git a/docs/src/markdown/changelog.md b/docs/src/markdown/changelog.md index 680333a..27743c3 100644 --- a/docs/src/markdown/changelog.md +++ b/docs/src/markdown/changelog.md @@ -1,5 +1,12 @@ # Changelog +## 3.1.0 + +> Nov 3, 2017 + +- **NEW**: Handle parsing `.sublime-color-scheme` files with hashed syntax highlighting foreground colors. +- **FIX**: Rework `*.sublime-color-scheme` merging and ensure `User` package is merged last. + ## 3.0.5 > Oct 30, 2017 diff --git a/mkdocs.yml b/mkdocs.yml index d120df9..b73fd68 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -7,6 +7,19 @@ copyright: | Copyright © 2015 - 2017 Isaac Muse
emoji provided free by EmojiOne +docs_dir: docs/src/markdown +theme: + name: material + custom_dir: docs/theme + palette: + primary: blue + accent: blue + logo: + icon: description + font: + text: Roboto + code: Roboto Mono + pages: - About Markdown Popups: index.md - Installation: installation.md @@ -18,10 +31,6 @@ pages: - Changelog: changelog.md - License: license.md -theme: material -docs_dir: docs/src/markdown -theme_dir: docs/theme - markdown_extensions: - markdown.extensions.toc: slugify: !!python/name:pymdownx.slugs.uslugify @@ -67,12 +76,6 @@ markdown_extensions: - pymdownx.details: extra: - palette: - primary: blue - accent: blue - font: - text: Roboto - code: Roboto Mono social: - type: github link: https://github.com/facelessuser diff --git a/st3/mdpopups/st_color_scheme_matcher.py b/st3/mdpopups/st_color_scheme_matcher.py index a705406..da8dc11 100644 --- a/st3/mdpopups/st_color_scheme_matcher.py +++ b/st3/mdpopups/st_color_scheme_matcher.py @@ -38,7 +38,6 @@ GLOBAL_OPTIONS = "globals" if int(sublime.version()) >= 3152 else "defaults" # XML -IS_XML_RE = re.compile(br'^[\r\n\s]*<') XML_COMMENT_RE = re.compile(br"^[\r\n\s]*[\s\r\n]*|") # For new Sublime format @@ -309,7 +308,10 @@ def sublime_format_path(pth): class SchemeColors( namedtuple( 'SchemeColors', - ['fg', 'fg_simulated', 'bg', "bg_simulated", "style", "fg_selector", "bg_selector", "style_selectors"], + [ + 'fg', 'fg_simulated', 'bg', "bg_simulated", "style", "color_gradient", + "fg_selector", "bg_selector", "style_selectors", "color_gradient_selector" + ], verbose=False ) ): @@ -330,19 +332,17 @@ def __init__(self, scheme_file, color_filter=None): self.color_scheme = path.normpath(scheme_file) self.scheme_file = path.basename(self.color_scheme) - content = sublime.load_binary_resource(sublime_format_path(self.color_scheme)) - if scheme_file.lower().endswith(('.tmtheme', '.hidden-tmtheme')) or IS_XML_RE.match(content) is not None: + if NEW_SCHEMES and scheme_file.endswith('.sublime-color-scheme'): + self.legacy = False + self.scheme_obj = { + 'variables': {}, + GLOBAL_OPTIONS: {}, + 'rules': [] + } + else: + content = sublime.load_binary_resource(sublime_format_path(self.color_scheme)) self.legacy = True self.convert_format(readPlistFromBytes(XML_COMMENT_RE.sub(b'', content))) - else: - self.legacy = False - self.scheme_obj = sublime.decode_value(content.decode('utf-8')) - if 'variables' not in self.scheme_obj: - self.scheme_obj['variables'] = {} - if GLOBAL_OPTIONS not in self.scheme_obj: - self.scheme_obj[GLOBAL_OPTIONS] = {} - if 'rules' not in self.scheme_obj: - self.scheme_obj['rules'] = [] self.overrides = [] if NEW_SCHEMES: self.merge_overrides() @@ -386,21 +386,26 @@ def convert_format(self, obj): def merge_overrides(self): """Merge override schemes.""" - current_file = sublime_format_path(self.color_scheme) + package_overrides = [] + user_overrides = [] for override in sublime.find_resources('%s.sublime-color-scheme' % path.splitext(self.scheme_file)[0]): - if override != current_file: - ojson = sublime.decode_value(sublime.load_resource(override)) + if override.startswith('Packages/User/'): + user_overrides.append(override) + else: + package_overrides.append(override) + for override in (package_overrides + user_overrides): + ojson = sublime.decode_value(sublime.load_resource(override)) - for k, v in ojson.get('variables', {}).items(): - self.scheme_obj['variables'][k] = v + for k, v in ojson.get('variables', {}).items(): + self.scheme_obj['variables'][k] = v - for k, v in ojson.get(GLOBAL_OPTIONS, {}).items(): - self.scheme_obj[GLOBAL_OPTIONS][k] = v + for k, v in ojson.get(GLOBAL_OPTIONS, {}).items(): + self.scheme_obj[GLOBAL_OPTIONS][k] = v - for item in ojson.get('rules', []): - self.scheme_obj['rules'].append(item) + for item in ojson.get('rules', []): + self.scheme_obj['rules'].append(item) - self.overrides.append(override) + self.overrides.append(override) def filter(self, scheme): """Dummy filter call that does nothing.""" @@ -458,15 +463,29 @@ def parse_scheme(self): scolor = None style = [] if scope is not None: + # Foreground color color = item.get('foreground', None) - if color is not None: + if isinstance(color, list): + # Hashed Syntax Highlighting + for index, c in enumerate(color): + color[index] = translate_color(COLOR_RE.match(c.strip()), self.variables, {}) + elif isinstance(color, str): color = translate_color(COLOR_RE.match(color.strip()), self.variables, {}) + else: + color = None + # Background color bgcolor = item.get('background', None) - if bgcolor is not None: + if isinstance(bgcolor, str): bgcolor = translate_color(COLOR_RE.match(bgcolor.strip()), self.variables, {}) + else: + bgcolor = None + # Selection foreground color scolor = item.get('selection_foreground', None) - if scolor is not None: + if isinstance(scolor, str): scolor = translate_color(COLOR_RE.match(scolor.strip()), self.variables, {}) + else: + scolor = None + # Font style if FONT_STYLE in item: for s in item.get(FONT_STYLE, '').split(' '): if s == "bold" or s == "italic": # or s == "underline": @@ -478,7 +497,10 @@ def parse_scheme(self): def add_entry(self, name, scope, color, bgcolor, scolor, style): """Add color entry.""" - if color is not None: + color_gradient = None + if isinstance(color, list): + fg, fg_sim, color_gradient = self.process_color_gradient(color) + elif color is not None: fg, fg_sim = self.process_color(color) else: fg, fg_sim = None, None @@ -497,6 +519,7 @@ def add_entry(self, name, scope, color, bgcolor, scolor, style): "scope": scope, "color": fg, "color_simulated": fg_sim, + "color_gradient": color_gradient, "bgcolor": bg, "bgcolor_simulated": bg_sim, "selection_color": sfg, @@ -504,6 +527,37 @@ def add_entry(self, name, scope, color, bgcolor, scolor, style): "style": style } + def process_color_gradient(self, colors, simple_strip=False, bground=None): + """ + Strip transparency from the color gradient list. + + Transparency can be stripped in one of two ways: + - Simply mask off the alpha channel. + - Apply the alpha channel to the color essential getting the color seen by the eye. + """ + + gradient = [] + + for color in colors: + if color is None or color.strip() == "": + continue + + if not color.startswith('#'): + continue + + rgba = RGBA(color.replace(" ", "")) + if not simple_strip: + if bground is None: + bground = self.special_colors['background']['color_simulated'] + rgba.apply_alpha(bground if bground != "" else "#FFFFFF") + + gradient.append((color, rgba.get_rgb())) + if gradient: + color, color_sim = gradient[0] + return color, color_sim, gradient + else: + return None, None, None + def process_color(self, color, simple_strip=False, bground=None): """ Strip transparency from the color value. @@ -565,6 +619,8 @@ def guess_color(self, scope_key, selected=False, explicit_background=False): color = self.special_colors['foreground']['color'] color_sim = self.special_colors['foreground']['color_simulated'] + color_gradient = None + color_gradient_selector = None bgcolor = self.special_colors['background']['color'] if not explicit_background else None bgcolor_sim = self.special_colors['background']['color_simulated'] if not explicit_background else None scolor = self.special_colors['selection_foreground']['color'] @@ -577,6 +633,7 @@ def guess_color(self, scope_key, selected=False, explicit_background=False): if scope_key in self.matched: color = self.matched[scope_key]["color"] color_sim = self.matched[scope_key]["color_simulated"] + color_gradient = self.matched[scope_key]["color_gradient"] style = self.matched[scope_key]["style"] bgcolor = self.matched[scope_key]["bgcolor"] bgcolor_sim = self.matched[scope_key]["bgcolor_simulated"] @@ -587,18 +644,31 @@ def guess_color(self, scope_key, selected=False, explicit_background=False): bg_selector = selectors["background"] scolor_selector = selectors["scolor"] style_selectors = selectors["style"] + color_gradient_selector = selectors['color_gradient'] else: best_match_bg = 0 best_match_fg = 0 best_match_style = 0 best_match_sfg = 0 + best_match_fg_gradient = 0 for key in self.colors: match = sublime.score_selector(scope_key, key) - if self.colors[key]["color"] is not None and match > best_match_fg: + if ( + not self.colors[key]['color_gradient'] and + self.colors[key]["color"] is not None and + match > best_match_fg + ): best_match_fg = match color = self.colors[key]["color"] color_sim = self.colors[key]["color_simulated"] color_selector = SchemeSelectors(self.colors[key]["name"], self.colors[key]["scope"]) + if ( + self.colors[key]["color"] is not None and + match > best_match_fg_gradient + ): + best_match_fg_gradient = match + color_gradient = self.colors[key]["color_gradient"] + color_gradient_selector = SchemeSelectors(self.colors[key]["name"], self.colors[key]["scope"]) if self.colors[key]["selection_color"] is not None and match > best_match_sfg: best_match_sfg = match scolor = self.colors[key]["selection_color"] @@ -634,12 +704,14 @@ def guess_color(self, scope_key, selected=False, explicit_background=False): "color_simulated": color_sim, "bgcolor_simulated": bgcolor_sim, "scolor_simulated": scolor_sim, + "color_gradient": color_gradient, "style": style, "selectors": { "color": color_selector, "background": bg_selector, "scolor": scolor_selector, - "style": style_selectors + "style": style_selectors, + "color_gradient": (color_gradient_selector if color_gradient else None) } } @@ -648,12 +720,14 @@ def guess_color(self, scope_key, selected=False, explicit_background=False): color = scolor color_sim = scolor_sim color_selector = scolor_selector + color_gradient = None + color_gradient_selector = None if self.special_colors['selection']['color']: bgcolor = self.special_colors['selection']['color'] bgcolor_sim = self.special_colors['selection']['color_simulated'] bg_selector = SchemeSelectors("selection", "selection") return SchemeColors( - color, color_sim, bgcolor, bgcolor_sim, style, - color_selector, bg_selector, style_selectors + color, color_sim, bgcolor, bgcolor_sim, style, color_gradient, + color_selector, bg_selector, style_selectors, color_gradient_selector ) diff --git a/st3/mdpopups/version.py b/st3/mdpopups/version.py index 49f77d6..f280e11 100644 --- a/st3/mdpopups/version.py +++ b/st3/mdpopups/version.py @@ -1,6 +1,6 @@ """Version.""" -_version_info = (3, 0, 5) +_version_info = (3, 1, 0) __version__ = '.'.join([str(x) for x in _version_info])