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
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])