Skip to content

Commit

Permalink
Hashed syntax highlight support (#47)
Browse files Browse the repository at this point in the history
* Backwards compatible gradient support

Ref #46

* Adjustments to parse logic

- Ensure user overrides are always merged last
- If colors are not string, don't fail, just assign none

* Fix doc errors

* Rework merge logic as it was wrong.
  • Loading branch information
facelessuser authored Nov 4, 2017
1 parent 8ee2157 commit 67bf68e
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 42 deletions.
7 changes: 7 additions & 0 deletions docs/src/markdown/changelog.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
23 changes: 13 additions & 10 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ copyright: |
Copyright &copy; 2015 - 2017 <a href="https://github.com/facelessuser">Isaac Muse</a>
<br><span class="md-footer-custom-text">emoji provided free by </span><a href="http://www.emojione.com">EmojiOne</a>
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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
136 changes: 105 additions & 31 deletions st3/mdpopups/st_color_scheme_matcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -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\S]*?-->[\s\r\n]*|<!--[\s\S]*?-->")

# For new Sublime format
Expand Down Expand Up @@ -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
)
):
Expand All @@ -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()
Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -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":
Expand All @@ -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
Expand All @@ -497,13 +519,45 @@ 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,
"selection_color_simulated": sfg_sim,
"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.
Expand Down Expand Up @@ -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']
Expand All @@ -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"]
Expand All @@ -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"]
Expand Down Expand Up @@ -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)
}
}

Expand All @@ -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
)
2 changes: 1 addition & 1 deletion st3/mdpopups/version.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Version."""

_version_info = (3, 0, 5)
_version_info = (3, 1, 0)
__version__ = '.'.join([str(x) for x in _version_info])


Expand Down

0 comments on commit 67bf68e

Please sign in to comment.