API to get theme/scheme colours etc
Currenly, I have some code to get the popupCss background/foreground colours for the current view so that I can use these to generate an image that will not look out of place with the colour scheme. My current code looks like this (thanks to @FichteFoll for getting me started) :
def parse_popupCss(css):
words = css.split()
i = 0
bg = None
fg = None
while words[i] != "html":
i += 1
while words[i] != "}":
if words[i] == "background-color:":
bg = words[i+1]
if words[i] == "color:":
fg = words[i+1]
i += 1
# Defaults if not found
if bg == None:
bg = "#FFFFFF"
if fg == None:
fg = "#000000"
# Remove leading # and trailing ;
bg = bg[1:7]
fg = fg[1:7]
return bg, fg
scheme_path = view.settings().get("color_scheme")
scheme_content = sublime.load_binary_resource(scheme_path)
scheme_data = plistlib.readPlistFromBytes(scheme_content)
css = scheme_data["settings"][0]["settings"]["popupCss"]
bg, fg = parse_popupCss(css)
This is not ideal:
-
finding, loading and parsing the PList again seems hacky, when Sublime presumably has it in memory already
-
the structure of the nested lists/dicts seems potentially fragile
-
scheme_data["settings"][0]
is necessary to avoid searching through the entire list for the dict who’s only key is "settings"
, but is this always guaranteed to be the first in the list?
-
scheme_data["settings"][0]["settings"]
doesn’t give the reader any clue about why I am doing this
-
some of the colours are in dicts, others are in dicts inside of dicts, and some are now in chunks of CSS code inside of dicts
-
Colour strings can be found in a variety of different formats (FFF
vs #FFFFFF
vs #FFFFFF00
, for example)
-
There is no CSS parser that ships with Sublime (that I know of), so I had to implement my own basic one. It is likely to break, depending on how schemes write their CSS.
-
The colour I’m looking for may not even have been defined in the end, and the code above doesn’t even check for this but is already quite long
The situation with themes further complicates matters:
-
There are potentially multiple resources (i.e. if a user overrides a theme, or uses a theme addon) that have to be searched through in the right order to find the final colour or setting
-
The file format and dict structure are both different which requires different logic to handle
Finally, in trying to reproduce all of the internal logic that Sublime uses, we are likely to make mistakes and end up being inconsistent, leading to hard-to-track-down bugs.
Since all of this is already being done by Sublime, I would prefer to see an API exposing this functionality that looks something like this:
# Each of these returns a flat dict with a predefined set of keys, all set
# the main "settings" section of a scheme
d = view.appearance()
# {'background': '#FFFFFF',
# 'caret': '#7C7C7C',
# 'foreground': '#000000',
# 'invisibles': '#B6B6B6',
# 'lineHighlight': '#EFFCA68F',
# 'selection': '#E3FC8D'}
# how text with the given scope string would be drawn in the current view
d = view.scope_appearance("some.combination.of.scopes", selection=False)
# {'background': '#FFFFFF',
# 'foreground': '#000000',
# 'italic': false,
# ...
# }
# get CSS properties
d = view.html_appearance(where="popup", what="h3")
d = view.html_appearance(where="phantom", what="html.background")
# {'background': '#FFFFFF',
# 'foreground': '#000000',
# 'fontsize': 10,
# ...
# }
# get theme properties
d = sublime.theme_appearance("tab", current=True)
# {'background': '#FFFFFF',
# 'foreground': '#000000',
# 'height': 10,
# ...
# }
The exact API could probably be better, but the important things are
-
Consistent with Sublime’s internal representation of these things
-
Automatically selects the most appropriate resource to read from
-
Guarantees existence of values by substituting in the defaults
-
Consistent form for colour strings (maybe even a Color type?)
Importance: minor I suppose