Sublime Forum

Testing color schemes

#1

Is it possible to test color schemes? Something like:

COLOR_SCHEME_TEST "color_scheme_name"

SYNTAX "source.php"
<?php
$variable = true;
//                ^^^^ fg=#xxxxxx bg=#xxxxxx font=name font_style=none

SYNTAX "python.py"
variable = True;
//               ^^^^ fg=#xxxxxx bg=#xxxxxx font=name font_style=none

 // ...

The only part I don’t know how to do is extract the color a styles applied to a specific point in a view. Is it possible to do this?

2 Likes

API Suggestions
#2

Only by parsing the color scheme xml yourself and matching the scopes manually - for example, the following, when you execute the get_styles_at_cursor command, will print, to the sublime console, the styles applied at the first cursor position from the view’s current color scheme:

import sublime, sublime_plugin
from lxml import etree

class GetStylesAtCursorCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        self.parse_color_scheme_file(self.view.settings().get('color_scheme'))
        
        scope_at_position = self.view.scope_name(self.view.sel()[0].begin())
        print(self.get_styles_for_scope(scope_at_position))
    
    def parse_color_scheme_file(self, color_scheme_file):
        color_scheme_content = sublime.load_resource(color_scheme_file)
        color_scheme_xml = etree.fromstring(bytes(color_scheme_content, 'UTF-8'))
        self.selectors_in_xml = color_scheme_xml.xpath('//dict/key[.="scope"]/following-sibling::string')
        self.default_styles = self.get_styles_from_xml(color_scheme_xml.xpath('/plist/dict/array/dict[1]')[0])
    
    def get_styles_from_xml(self, xml_dict):
        styles = dict()
        style_keys_xml = xml_dict.xpath('./key[.="settings"]/following-sibling::dict/key')
        for key_xml in style_keys_xml:
            styles[key_xml.text] = key_xml.xpath('./following-sibling::string/text()')[0]
        return styles
    
    def get_styles_for_scope(self, scope):
        styles = self.default_styles.copy()
        for xml_selector in self.selectors_in_xml:
            if sublime.score_selector(scope, xml_selector.text):
                selector_styles = self.get_styles_from_xml(xml_selector.getparent())
                for style in selector_styles:
                    styles[style] = selector_styles[style]
        return styles

Notes:

  • requires the lxml dependency to be installed - for example, by installing the XPath plugin
  • takes into account the default styles applied by the color scheme for when no scopes match or the scope doesn’t define a particular style
  • works on the basis that styles specified later in the xml override styles specified higher up in the document.

Example output:

>>> view.run_command('get_styles_at_cursor')
{'background': '#272822FF', 'foreground': '#F92672', 'caret': '#DCDDD7FF'}
0 Likes

#3

in fact, you might be better off looking at the source code of ScopeHunter, specifically the /lib/color_scheme_matcher.py file, as I just remembered that it can show colors in addition to scopes.

0 Likes

#4

If it wasn’t for the dependency on the XPath plugin this would be almost perfect.

0 Likes

#5

feel free to rewrite it to use ElementTree instead of lxml :slight_smile:

0 Likes

#6

Ok so there’s enough there to say it’s certainly possible to write a test suite plugin for testing color schemes. It’s a lot of work, I’d rather not waste my time if this will be done in the ST core later. @wbond Is this something that will be done in the ST core like the syntax tests?

1 Like

#7

@kingkeith Thanks. I had quick look at the XPath plugin. It wouldn’t be worth writing it from scratch. The scope hunter code is complicated so the easiest would be your code and use the XPath dependency.

0 Likes

#8

if you were to create a package on Package Control, you wouldn’t need the XPath plugin, just reference lxml as a dependency :wink:

0 Likes

#9

Since color scheme files are .plist files, you can use plistlib to parse them as well (which removes any dependency requirement).

2 Likes

#10

Nice,

>>> import plistlib
>>> plistlib.readPlistFromBytes(bytes(sublime.load_resource(view.settings().get('color_scheme')), 'UTF-8'))

returns a dict. So rather than using XPath, you can just loop through the array to find scopes that match. :slight_smile:

0 Likes

#11

okay, here is the same script rewritten to use the built in plistlib module instead of the third party lxml module:

import sublime, sublime_plugin
import plistlib

class GetStylesAtCursorCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        self.parse_color_scheme_file(self.view.settings().get('color_scheme'))
        
        scope_at_position = self.view.scope_name(self.view.sel()[0].begin())
        print(self.get_styles_for_scope(scope_at_position))
    
    def parse_color_scheme_file(self, color_scheme_file):
        color_scheme_content = sublime.load_resource(color_scheme_file)
        color_scheme_dict = plistlib.readPlistFromBytes(bytes(color_scheme_content, 'UTF-8'))
        self.selectors_in_scheme = color_scheme_dict['settings']
    
    def get_styles_from_dict(self, dict_settings):
        return dict_settings['settings']
    
    def get_styles_for_scope(self, scope):
        styles = dict()
        for scheme_selector in self.selectors_in_scheme:
            if 'scope' not in scheme_selector or sublime.score_selector(scope, scheme_selector['scope']):
                selector_styles = self.get_styles_from_dict(scheme_selector)
                for style in selector_styles:
                    styles[style] = selector_styles[style]
        return styles
2 Likes

API Suggestions
Get Current Foreground Theme
#12

Forgot to say, that I really like the idea of a color scheme test that is based on code and not scopes, so that even if a particular scope changes in the syntax definition, you can see whether the color changes etc.

0 Likes

#13

Ok, so here is a plugin for testing color schemes: ColorSchemeUnit.

originally I intended to just write a proof of concept, but this does the job. It’s a little rough around the edges but it gets the job done for basic cases. It needs some documentation See the examples in these color schemes.

1 Like

#14

note that with the release of build 3150 and the new color scheme format and APIs, my previous advice is officially depreciated :slight_smile:

0 Likes

#15

ColorSchemeUnit will be updates to use the new style api’s later. It’s an internal change, it won’t make any difference to the usage.

As an aside, Travis CI support has been added to ColorSchemeUnit. Appveyor support will be available soon.

0 Likes