From 6b39212ba016b5e29de5ec1b3071e5c211abc154 Mon Sep 17 00:00:00 2001 From: Raoul Wols Date: Thu, 3 Aug 2017 01:02:56 +0200 Subject: [PATCH 01/16] Add preliminary structure --- TerminalView.py | 13 +++++++++++++ convert_color_scheme.py | 6 ++++++ 2 files changed, 19 insertions(+) create mode 100644 convert_color_scheme.py diff --git a/TerminalView.py b/TerminalView.py index e1d4837..bd8ef12 100644 --- a/TerminalView.py +++ b/TerminalView.py @@ -13,6 +13,7 @@ from . import sublime_terminal_buffer from . import linux_pty from . import utils +from .convert_color_scheme import convert_color_scheme class TerminalViewManager(): @@ -79,6 +80,18 @@ def run(self, cmd="/bin/bash -l", title="Terminal", cwd=None, syntax=None): class TerminalViewActivate(sublime_plugin.TextCommand): def run(self, _, cmd, title, cwd, syntax): + if not syntax: + scheme = self.view.settings().get("color_scheme") + name = os.path.splitext(os.path.basename(scheme))[0] + name += ".hidden-tmTheme" + path = os.path.join(sublime.packages_path(), "User", "TerminalView", name) + if not os.path.isfile(path): + converted = None + with open(os.path.join(sublime.packages_path(), "..", scheme), "r") as f: + converted = convert_color_scheme(f.read()) + os.makedirs(os.path.join(sublime.packages_path(), "User", "TerminalView"), exist_ok=True) + with open(path, "w") as f: + f.write(converted) terminal_view = TerminalView(self.view) terminal_view.run(cmd, title, cwd, syntax) diff --git a/convert_color_scheme.py b/convert_color_scheme.py new file mode 100644 index 0000000..a9ca3a0 --- /dev/null +++ b/convert_color_scheme.py @@ -0,0 +1,6 @@ +import colorsys +from xml.etree import ElementTree as ET + + +def convert_color_scheme(content): + return content From 02d982b39987a4060bc64ebdc70a1e17cb47234d Mon Sep 17 00:00:00 2001 From: Raoul Wols Date: Fri, 4 Aug 2017 22:08:37 +0200 Subject: [PATCH 02/16] Preliminary work on colors derived from the current color scheme Some things don't work as expected. For instance, when the terminal asks for black_red, the *background* is "red" and the *foreground* is "black". It should be the other way around. I don't understand the cause of this at the moment. --- .gitignore | 1 + TerminalView.py | 14 +--- TerminalView.sublime-project | 38 ++++++++++ convert_color_scheme.py | 138 ++++++++++++++++++++++++++++++++++- print-colors.sh | 37 ++++++++++ sublime_terminal_buffer.py | 50 ++++++++----- 6 files changed, 242 insertions(+), 36 deletions(-) create mode 100644 TerminalView.sublime-project create mode 100755 print-colors.sh diff --git a/.gitignore b/.gitignore index 0d20b64..03577d3 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.pyc +*.sublime-workspace diff --git a/TerminalView.py b/TerminalView.py index bd8ef12..df1e835 100644 --- a/TerminalView.py +++ b/TerminalView.py @@ -13,7 +13,6 @@ from . import sublime_terminal_buffer from . import linux_pty from . import utils -from .convert_color_scheme import convert_color_scheme class TerminalViewManager(): @@ -80,18 +79,7 @@ def run(self, cmd="/bin/bash -l", title="Terminal", cwd=None, syntax=None): class TerminalViewActivate(sublime_plugin.TextCommand): def run(self, _, cmd, title, cwd, syntax): - if not syntax: - scheme = self.view.settings().get("color_scheme") - name = os.path.splitext(os.path.basename(scheme))[0] - name += ".hidden-tmTheme" - path = os.path.join(sublime.packages_path(), "User", "TerminalView", name) - if not os.path.isfile(path): - converted = None - with open(os.path.join(sublime.packages_path(), "..", scheme), "r") as f: - converted = convert_color_scheme(f.read()) - os.makedirs(os.path.join(sublime.packages_path(), "User", "TerminalView"), exist_ok=True) - with open(path, "w") as f: - f.write(converted) + terminal_view = TerminalView(self.view) terminal_view.run(cmd, title, cwd, syntax) diff --git a/TerminalView.sublime-project b/TerminalView.sublime-project new file mode 100644 index 0000000..b49e4d5 --- /dev/null +++ b/TerminalView.sublime-project @@ -0,0 +1,38 @@ +{ + "build_systems": + [ + { + "name": "Remove Cache and User Color Schemes", + "cmd": ["rm", "-rf", "Cache", "Packages/User/TerminalView"], + "working_dir": "$project_path/../.." + } + ], + "folders": + [ + { + "path": ".." + } + ], + "settings": + { + "tab_size": 4, + "translate_tabs_to_spaces": true, + "trim_trailing_white_space_on_save": true, + "rulers": [100] + }, + "SublimeLinter": + { + "linters": + { + "flake8": + { + "excludes": + [ + "*/test/**" + ], + "ignore": "W", + "max-line-length": 100 + } + } + } +} diff --git a/convert_color_scheme.py b/convert_color_scheme.py index a9ca3a0..cc0cd0c 100644 --- a/convert_color_scheme.py +++ b/convert_color_scheme.py @@ -1,6 +1,136 @@ -import colorsys -from xml.etree import ElementTree as ET +"""Functionality for converting a color scheme to a "TerminalView" scheme.""" +import os +import plistlib +import sublime -def convert_color_scheme(content): - return content +def hex_to_rgb(hexstring): + """Convert a string representing a hex color to an RGB tuple.""" + return (int(hexstring[1:3], 16) / 255, + int(hexstring[3:5], 16) / 255, + int(hexstring[5:7], 16) / 255) + + +def rgb_to_hex(rgb): + """Convert an RGB tuple to a hex string.""" + return "#{}{}{}".format( + format(int(rgb[0] * 255), "02x"), + format(int(rgb[1] * 255), "02x"), + format(int(rgb[2] * 255), "02x")) + + +def norm2(vec3): + """Compute the squared norm of a three-dimensional vector.""" + return vec3[0]**2 + vec3[1]**2 + vec3[2]**2 + + +def distance2(a, b): + """Compute the squared distance between two 3D vectors.""" + return norm2((a[0] - b[0], a[1] - b[1], a[2] - b[2])) + + +_name_from_index = ["black", "white", "red", "green", "blue", "brown", "magenta", "cyan"] + +_rgb_from_name = { + "black": (0., 0., 0.), + "white": (1., 1., 1.), + + "red": (1., 0., 0.), + "green": (0., 1., 0.), + "blue": (0., 0., 1.), + + "cyan": (0., 1., 1.), + "magenta": (1., 0., 1.), + "brown": (1., 1., 0.) # should be yellow...? +} + + +def convert_color_scheme(infile, outfile): + """Convert a color scheme from infile into outfile.""" + print("loading file", infile) + base = plistlib.readPlistFromBytes(sublime.load_resource(infile).encode("utf-8")) + scheme = base["settings"] + colors = set() + + # Fetch the "black" color. In a light scheme, this is actually white-ish. + background = hex_to_rgb(scheme[0]["settings"]["background"]) + + # Fetch the "white" color. In a light scheme, it's actually black-ish. + foreground = hex_to_rgb(scheme[0]["settings"]["foreground"]) + + # Fetch the "selection" color. We make the assumption that the selection color is a suitable + # background color for all other colors. + selection = scheme[0]["settings"]["selection"] # TODO + + # Fetch all the other colors + for i in range(1, len(scheme)): + scope = scheme[i].get("scope", None) + if scope and "sublimelinter" in scope: + print("skipping sublimelinter scope...") + continue + hexcolor = scheme[i]["settings"].get("foreground", None) + if hexcolor: + colors.add(hex_to_rgb(hexcolor)) + colors = list(colors) + print("extracted", len(colors), "scope colors from scheme") + print("background color:", background) + print("foreground color:", foreground) + print("selection color:", selection) + + # Start processing our colors + terminal_colors = [background, foreground] + while len(colors) < 6: + print("adding extra foreground color so that we have enough colors to work with.") + # we need at least six colors + colors.append(foreground) + + # Skip the first two colors ("black" and "white"). + for i in range(2, 8): + best_index = -1 + smallest_distance = float("inf") + terminal_color = _rgb_from_name[_name_from_index[i]] + for j, color in enumerate(colors): + d = distance2(color, terminal_color) + if d < smallest_distance: + best_index = j + smallest_distance = d + assert best_index >= 0 + print("best color match for", _name_from_index[i], "(", + rgb_to_hex(_rgb_from_name[_name_from_index[i]]), ")", "is", + rgb_to_hex(colors[best_index]), "at index", best_index, "with a squared distance of", + smallest_distance) + terminal_colors.append(colors[best_index]) + del colors[best_index] # don't repeat colors + + # convert our colors back to hex + terminal_colors = [rgb_to_hex(c) for c in terminal_colors] + assert len(terminal_colors) == 8 + + # Remove scopes from the color scheme. + while len(scheme) > 1: + del scheme[-1] + + # Now start adding in our own scopes. + + # First add our hack scope. + scheme.append({ + "name": "Text and Source Base Colors (Hack)", + "scope": "text, source", + "settings": {"foreground": terminal_colors[1]} + }) + + # Now change the foreground color to the background color... For some reason this gives the + # desired result. + scheme[0]["settings"]["foreground"] = terminal_colors[0] + + # Bring in all the other colors. + for i in range(0, 8): + for j in range(0, 8): + scope = "terminalview.{}_{}".format(_name_from_index[i], _name_from_index[j]) + settings = {"background": terminal_colors[i], "foreground": terminal_colors[j]} + scheme.append({"scope": scope, "settings": settings}) + + # Save the results + os.makedirs(os.path.dirname(outfile), exist_ok=True) + print("saving to", outfile) + plistlib.writePlist(base, outfile) diff --git a/print-colors.sh b/print-colors.sh new file mode 100755 index 0000000..4559b4a --- /dev/null +++ b/print-colors.sh @@ -0,0 +1,37 @@ +#!/bin/bash +echo '--- FOREGROUND COLORS ---' +echo -e '\x1B[39mDefault' +echo -e '\x1B[30mBlack' +echo -e '\x1B[31mRed' +echo -e '\x1B[32mGreen' +echo -e '\x1B[33mYellow' +echo -e '\x1B[34mBlue' +echo -e '\x1B[35mMagenta' +echo -e '\x1B[36mCyan' +echo -e '\x1B[37mLight gray' +echo -e '\x1B[90mDark gray' +echo -e '\x1B[91mLight red' +echo -e '\x1B[92mLight green' +echo -e '\x1B[93mLight yellow' +echo -e '\x1B[94mLight blue' +echo -e '\x1B[95mLight magenta' +echo -e '\x1B[96mLight cyan' +echo -e '\x1B[97mWhite' +echo '--- BACKGROUND COLORS ---' +echo -e '\x1B[49mDefault' +echo -e '\x1B[40mBlack' +echo -e '\x1B[41mRed' +echo -e '\x1B[42mGreen' +echo -e '\x1B[43mYellow' +echo -e '\x1B[44mBlue' +echo -e '\x1B[45mMagenta' +echo -e '\x1B[46mCyan' +echo -e '\x1B[47mLight gray' +echo -e '\x1B[100mDark gray' +echo -e '\x1B[101mLight red' +echo -e '\x1B[102mLight green' +echo -e '\x1B[103mLight yellow' +echo -e '\x1B[104mLight blue' +echo -e '\x1B[105mLight magenta' +echo -e '\x1B[106mLight cyan' +echo -e '\x1B[107mWhite' diff --git a/sublime_terminal_buffer.py b/sublime_terminal_buffer.py index fccb6ef..992dbc4 100644 --- a/sublime_terminal_buffer.py +++ b/sublime_terminal_buffer.py @@ -3,6 +3,7 @@ """ import collections import time +import os import sublime import sublime_plugin @@ -11,7 +12,7 @@ from . import pyte_terminal_emulator from . import utils from . import sublime_view_cache - +from .convert_color_scheme import convert_color_scheme class SublimeBufferManager(): """ @@ -53,7 +54,9 @@ def __init__(self, sublime_view, title, syntax_file=None): self._view.settings().set("draw_indent_guides", False) self._view.settings().set("caret_style", "blink") self._view.settings().set("scroll_past_end", False) - self._view.settings().add_on_change('color_scheme', lambda: set_color_scheme(self._view)) + + self._is_setting_color_scheme = False + self._view.settings().add_on_change('color_scheme', self._set_color_scheme) if syntax_file is not None: self._view.set_syntax_file("Packages/User/" + syntax_file) @@ -168,6 +171,31 @@ def _scroll_terminal_if_requested(self): self._view.settings().set("terminal_view_scroll", None) + def _set_color_scheme(self): + """ + Set color scheme for view + """ + # color_scheme = "Packages/TerminalView/TerminalView.hidden-tmTheme" + if self._is_setting_color_scheme: + return + self._is_setting_color_scheme = True + scheme = self._view.settings().get("color_scheme") + print("the current color scheme is", scheme) + name = os.path.splitext(os.path.basename(scheme))[0] + name += ".hidden-tmTheme" + resource = "Packages/User/TerminalView/{}".format(name) + if self._view.settings().get('color_scheme') != resource: + outfile = os.path.join(sublime.packages_path(), "..", resource) + if not os.path.isfile(outfile): + outfile = os.path.join(sublime.packages_path(), "..", resource) + print("converting", scheme, "into", outfile) + convert_color_scheme(scheme, outfile) + # This triggers the function again, but it exits prematurely + # because self._is_setting_color_scheme is True. + print("changing color scheme to", resource) + self._view.settings().set('color_scheme', resource) + self._is_setting_color_scheme = False + class TerminalViewScroll(sublime_plugin.TextCommand): def run(self, _, forward=False, line=False): @@ -401,6 +429,7 @@ def _update_line_colors(self, line_no, line_color_map): for idx, field in line_color_map.items(): length = field["field_length"] color_scope = "terminalview.%s_%s" % (field["color"][0], field["color"][1]) + print(color_scope) # Get text point where color should start line_start, _ = view_content_cache.get_line_start_and_end_points(line_no) @@ -425,20 +454,3 @@ def run(self, edit, start=0, end=None): region = sublime.Region(start, end) self.view.erase(edit, region) self.view.set_read_only(True) - - -def set_color_scheme(view): - """ - Set color scheme for view - """ - color_scheme = "Packages/TerminalView/TerminalView.hidden-tmTheme" - - # Check if user color scheme exists - try: - sublime.load_resource("Packages/User/TerminalView.hidden-tmTheme") - color_scheme = "Packages/User/TerminalView.hidden-tmTheme" - except: - pass - - if view.settings().get('color_scheme') != color_scheme: - view.settings().set('color_scheme', color_scheme) From 15c908b3bf40f583357c2d389d0e3544be91b411 Mon Sep 17 00:00:00 2001 From: Raoul Wols Date: Sun, 13 Aug 2017 11:54:04 +0200 Subject: [PATCH 03/16] Fix colors getting inverted by using a workaround function Thanks @randy3k! Also see: https://github.com/SublimeTextIssues/Core/issues/817 --- convert_color_scheme.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/convert_color_scheme.py b/convert_color_scheme.py index cc0cd0c..3713216 100644 --- a/convert_color_scheme.py +++ b/convert_color_scheme.py @@ -29,6 +29,17 @@ def distance2(a, b): return norm2((a[0] - b[0], a[1] - b[1], a[2] - b[2])) +def next_color(color_text): + """ + Given a color string "#xxxxxy", returns its next color "#xxxxx{y+1}". + """ + hex_value = int(color_text[1:], 16) + if hex_value == 16777215: # #ffffff + return "#fffffe" + else: + return "#{}".format(hex(hex_value+1)[2:]) + + _name_from_index = ["black", "white", "red", "green", "blue", "brown", "magenta", "cyan"] _rgb_from_name = { @@ -111,23 +122,15 @@ def convert_color_scheme(infile, outfile): del scheme[-1] # Now start adding in our own scopes. - - # First add our hack scope. - scheme.append({ - "name": "Text and Source Base Colors (Hack)", - "scope": "text, source", - "settings": {"foreground": terminal_colors[1]} - }) - - # Now change the foreground color to the background color... For some reason this gives the - # desired result. - scheme[0]["settings"]["foreground"] = terminal_colors[0] - - # Bring in all the other colors. for i in range(0, 8): + if i == 0: + background = next_color(terminal_colors[i]) + else: + background = terminal_colors[i] for j in range(0, 8): scope = "terminalview.{}_{}".format(_name_from_index[i], _name_from_index[j]) - settings = {"background": terminal_colors[i], "foreground": terminal_colors[j]} + foreground = terminal_colors[j] + settings = {"background": background, "foreground": foreground} scheme.append({"scope": scope, "settings": settings}) # Save the results From bd0bdf26c7ee75f9e183a972c08f657bc8a50695 Mon Sep 17 00:00:00 2001 From: Raoul Wols Date: Sun, 13 Aug 2017 12:03:25 +0200 Subject: [PATCH 04/16] Remove a few debug prints --- sublime_terminal_buffer.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/sublime_terminal_buffer.py b/sublime_terminal_buffer.py index 0819bfb..73aed57 100644 --- a/sublime_terminal_buffer.py +++ b/sublime_terminal_buffer.py @@ -56,7 +56,7 @@ def __init__(self, sublime_view, title, syntax_file=None): self._view.settings().set("scroll_past_end", False) self._is_setting_color_scheme = False - self._view.settings().add_on_change('color_scheme', self._set_color_scheme) + self._view.settings().add_on_change("terminal_view_color_scheme", self._set_color_scheme) if syntax_file is not None: self._view.set_syntax_file("Packages/User/" + syntax_file) @@ -179,25 +179,20 @@ def _set_color_scheme(self): """ Set color scheme for view """ - # color_scheme = "Packages/TerminalView/TerminalView.hidden-tmTheme" if self._is_setting_color_scheme: return self._is_setting_color_scheme = True scheme = self._view.settings().get("color_scheme") - print("the current color scheme is", scheme) - name = os.path.splitext(os.path.basename(scheme))[0] - name += ".hidden-tmTheme" + name = os.path.splitext(os.path.basename(scheme))[0] + ".hidden-tmTheme" resource = "Packages/User/TerminalView/{}".format(name) - if self._view.settings().get('color_scheme') != resource: + if self._view.settings().get("color_scheme") != resource: outfile = os.path.join(sublime.packages_path(), "..", resource) if not os.path.isfile(outfile): outfile = os.path.join(sublime.packages_path(), "..", resource) - print("converting", scheme, "into", outfile) convert_color_scheme(scheme, outfile) # This triggers the function again, but it exits prematurely # because self._is_setting_color_scheme is True. - print("changing color scheme to", resource) - self._view.settings().set('color_scheme', resource) + self._view.settings().set("color_scheme", resource) self._is_setting_color_scheme = False @@ -433,7 +428,6 @@ def _update_line_colors(self, line_no, line_color_map): for idx, field in line_color_map.items(): length = field["field_length"] color_scope = "terminalview.%s_%s" % (field["color"][0], field["color"][1]) - print(color_scope) # Get text point where color should start line_start, _ = view_content_cache.get_line_start_and_end_points(line_no) From afb881ba122341272079395da6e1b688b5694ec0 Mon Sep 17 00:00:00 2001 From: Raoul Wols Date: Sun, 13 Aug 2017 13:35:41 +0200 Subject: [PATCH 05/16] Update "print-colors" script to test colors, and use selection color --- convert_color_scheme.py | 22 ++++---- print-colors.sh | 118 ++++++++++++++++++++++++++++------------ 2 files changed, 94 insertions(+), 46 deletions(-) diff --git a/convert_color_scheme.py b/convert_color_scheme.py index 3713216..0b15483 100644 --- a/convert_color_scheme.py +++ b/convert_color_scheme.py @@ -63,11 +63,11 @@ def convert_color_scheme(infile, outfile): scheme = base["settings"] colors = set() - # Fetch the "black" color. In a light scheme, this is actually white-ish. - background = hex_to_rgb(scheme[0]["settings"]["background"]) + # Fetch the "default" color. + default = hex_to_rgb(scheme[0]["settings"]["background"]) - # Fetch the "white" color. In a light scheme, it's actually black-ish. - foreground = hex_to_rgb(scheme[0]["settings"]["foreground"]) + # Fetch the "black" color. In a dark scheme, it's actually white-ish. + black = hex_to_rgb(scheme[0]["settings"]["foreground"]) # Fetch the "selection" color. We make the assumption that the selection color is a suitable # background color for all other colors. @@ -84,16 +84,16 @@ def convert_color_scheme(infile, outfile): colors.add(hex_to_rgb(hexcolor)) colors = list(colors) print("extracted", len(colors), "scope colors from scheme") - print("background color:", background) - print("foreground color:", foreground) + print("default color:", default) + print("foreground color:", black) print("selection color:", selection) # Start processing our colors - terminal_colors = [background, foreground] + terminal_colors = [default, black] while len(colors) < 6: - print("adding extra foreground color so that we have enough colors to work with.") + print("adding extra black color so that we have enough colors to work with.") # we need at least six colors - colors.append(foreground) + colors.append(black) # Skip the first two colors ("black" and "white"). for i in range(2, 8): @@ -129,7 +129,9 @@ def convert_color_scheme(infile, outfile): background = terminal_colors[i] for j in range(0, 8): scope = "terminalview.{}_{}".format(_name_from_index[i], _name_from_index[j]) - foreground = terminal_colors[j] + # If the foreground color is the same as the background, use the "selection" color for + # the foreground. + foreground = selection if i == j else terminal_colors[j] settings = {"background": background, "foreground": foreground} scheme.append({"scope": scope, "settings": settings}) diff --git a/print-colors.sh b/print-colors.sh index 4559b4a..8563df0 100755 --- a/print-colors.sh +++ b/print-colors.sh @@ -1,37 +1,83 @@ #!/bin/bash -echo '--- FOREGROUND COLORS ---' -echo -e '\x1B[39mDefault' -echo -e '\x1B[30mBlack' -echo -e '\x1B[31mRed' -echo -e '\x1B[32mGreen' -echo -e '\x1B[33mYellow' -echo -e '\x1B[34mBlue' -echo -e '\x1B[35mMagenta' -echo -e '\x1B[36mCyan' -echo -e '\x1B[37mLight gray' -echo -e '\x1B[90mDark gray' -echo -e '\x1B[91mLight red' -echo -e '\x1B[92mLight green' -echo -e '\x1B[93mLight yellow' -echo -e '\x1B[94mLight blue' -echo -e '\x1B[95mLight magenta' -echo -e '\x1B[96mLight cyan' -echo -e '\x1B[97mWhite' -echo '--- BACKGROUND COLORS ---' -echo -e '\x1B[49mDefault' -echo -e '\x1B[40mBlack' -echo -e '\x1B[41mRed' -echo -e '\x1B[42mGreen' -echo -e '\x1B[43mYellow' -echo -e '\x1B[44mBlue' -echo -e '\x1B[45mMagenta' -echo -e '\x1B[46mCyan' -echo -e '\x1B[47mLight gray' -echo -e '\x1B[100mDark gray' -echo -e '\x1B[101mLight red' -echo -e '\x1B[102mLight green' -echo -e '\x1B[103mLight yellow' -echo -e '\x1B[104mLight blue' -echo -e '\x1B[105mLight magenta' -echo -e '\x1B[106mLight cyan' -echo -e '\x1B[107mWhite' +# generated from pyte/graphics.py +echo -e '\x1B[49m\x1B[32mdefault green ' +echo -e '\x1B[49m\x1B[33mdefault brown ' +echo -e '\x1B[49m\x1B[34mdefault blue ' +echo -e '\x1B[49m\x1B[35mdefault magenta' +echo -e '\x1B[49m\x1B[36mdefault cyan ' +echo -e '\x1B[49m\x1B[37mdefault white ' +echo -e '\x1B[49m\x1B[39mdefault default' +echo -e '\x1B[49m\x1B[30mdefault black ' +echo -e '\x1B[49m\x1B[31mdefault red ' +echo -e '\x1B[40m\x1B[32m black green ' +echo -e '\x1B[40m\x1B[33m black brown ' +echo -e '\x1B[40m\x1B[34m black blue ' +echo -e '\x1B[40m\x1B[35m black magenta' +echo -e '\x1B[40m\x1B[36m black cyan ' +echo -e '\x1B[40m\x1B[37m black white ' +echo -e '\x1B[40m\x1B[39m black default' +echo -e '\x1B[40m\x1B[30m black black ' +echo -e '\x1B[40m\x1B[31m black red ' +echo -e '\x1B[41m\x1B[32m red green ' +echo -e '\x1B[41m\x1B[33m red brown ' +echo -e '\x1B[41m\x1B[34m red blue ' +echo -e '\x1B[41m\x1B[35m red magenta' +echo -e '\x1B[41m\x1B[36m red cyan ' +echo -e '\x1B[41m\x1B[37m red white ' +echo -e '\x1B[41m\x1B[39m red default' +echo -e '\x1B[41m\x1B[30m red black ' +echo -e '\x1B[41m\x1B[31m red red ' +echo -e '\x1B[42m\x1B[32m green green ' +echo -e '\x1B[42m\x1B[33m green brown ' +echo -e '\x1B[42m\x1B[34m green blue ' +echo -e '\x1B[42m\x1B[35m green magenta' +echo -e '\x1B[42m\x1B[36m green cyan ' +echo -e '\x1B[42m\x1B[37m green white ' +echo -e '\x1B[42m\x1B[39m green default' +echo -e '\x1B[42m\x1B[30m green black ' +echo -e '\x1B[42m\x1B[31m green red ' +echo -e '\x1B[43m\x1B[32m brown green ' +echo -e '\x1B[43m\x1B[33m brown brown ' +echo -e '\x1B[43m\x1B[34m brown blue ' +echo -e '\x1B[43m\x1B[35m brown magenta' +echo -e '\x1B[43m\x1B[36m brown cyan ' +echo -e '\x1B[43m\x1B[37m brown white ' +echo -e '\x1B[43m\x1B[39m brown default' +echo -e '\x1B[43m\x1B[30m brown black ' +echo -e '\x1B[43m\x1B[31m brown red ' +echo -e '\x1B[44m\x1B[32m blue green ' +echo -e '\x1B[44m\x1B[33m blue brown ' +echo -e '\x1B[44m\x1B[34m blue blue ' +echo -e '\x1B[44m\x1B[35m blue magenta' +echo -e '\x1B[44m\x1B[36m blue cyan ' +echo -e '\x1B[44m\x1B[37m blue white ' +echo -e '\x1B[44m\x1B[39m blue default' +echo -e '\x1B[44m\x1B[30m blue black ' +echo -e '\x1B[44m\x1B[31m blue red ' +echo -e '\x1B[45m\x1B[32mmagenta green ' +echo -e '\x1B[45m\x1B[33mmagenta brown ' +echo -e '\x1B[45m\x1B[34mmagenta blue ' +echo -e '\x1B[45m\x1B[35mmagenta magenta' +echo -e '\x1B[45m\x1B[36mmagenta cyan ' +echo -e '\x1B[45m\x1B[37mmagenta white ' +echo -e '\x1B[45m\x1B[39mmagenta default' +echo -e '\x1B[45m\x1B[30mmagenta black ' +echo -e '\x1B[45m\x1B[31mmagenta red ' +echo -e '\x1B[46m\x1B[32m cyan green ' +echo -e '\x1B[46m\x1B[33m cyan brown ' +echo -e '\x1B[46m\x1B[34m cyan blue ' +echo -e '\x1B[46m\x1B[35m cyan magenta' +echo -e '\x1B[46m\x1B[36m cyan cyan ' +echo -e '\x1B[46m\x1B[37m cyan white ' +echo -e '\x1B[46m\x1B[39m cyan default' +echo -e '\x1B[46m\x1B[30m cyan black ' +echo -e '\x1B[46m\x1B[31m cyan red ' +echo -e '\x1B[47m\x1B[32m white green ' +echo -e '\x1B[47m\x1B[33m white brown ' +echo -e '\x1B[47m\x1B[34m white blue ' +echo -e '\x1B[47m\x1B[35m white magenta' +echo -e '\x1B[47m\x1B[36m white cyan ' +echo -e '\x1B[47m\x1B[37m white white ' +echo -e '\x1B[47m\x1B[39m white default' +echo -e '\x1B[47m\x1B[30m white black ' +echo -e '\x1B[47m\x1B[31m white red ' From 838e0b7c5d0106cf96cec299d27b650e6aebdeb1 Mon Sep 17 00:00:00 2001 From: Raoul Wols Date: Sun, 13 Aug 2017 13:39:20 +0200 Subject: [PATCH 06/16] Prevent exception when a color has no "settings" key --- convert_color_scheme.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/convert_color_scheme.py b/convert_color_scheme.py index 0b15483..ecb44ed 100644 --- a/convert_color_scheme.py +++ b/convert_color_scheme.py @@ -79,7 +79,7 @@ def convert_color_scheme(infile, outfile): if scope and "sublimelinter" in scope: print("skipping sublimelinter scope...") continue - hexcolor = scheme[i]["settings"].get("foreground", None) + hexcolor = scheme[i].get("settings", {}).get("foreground", None) if hexcolor: colors.add(hex_to_rgb(hexcolor)) colors = list(colors) From d2e7c5f5187845861da03c33707d893a3518d47e Mon Sep 17 00:00:00 2001 From: Raoul Wols Date: Sun, 13 Aug 2017 13:40:27 +0200 Subject: [PATCH 07/16] Remove some debug prints and asserts --- convert_color_scheme.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/convert_color_scheme.py b/convert_color_scheme.py index ecb44ed..7c5b64a 100644 --- a/convert_color_scheme.py +++ b/convert_color_scheme.py @@ -58,7 +58,7 @@ def next_color(color_text): def convert_color_scheme(infile, outfile): """Convert a color scheme from infile into outfile.""" - print("loading file", infile) + print("processing file", infile) base = plistlib.readPlistFromBytes(sublime.load_resource(infile).encode("utf-8")) scheme = base["settings"] colors = set() @@ -71,7 +71,7 @@ def convert_color_scheme(infile, outfile): # Fetch the "selection" color. We make the assumption that the selection color is a suitable # background color for all other colors. - selection = scheme[0]["settings"]["selection"] # TODO + selection = scheme[0]["settings"]["selection"] # Fetch all the other colors for i in range(1, len(scheme)): @@ -84,9 +84,6 @@ def convert_color_scheme(infile, outfile): colors.add(hex_to_rgb(hexcolor)) colors = list(colors) print("extracted", len(colors), "scope colors from scheme") - print("default color:", default) - print("foreground color:", black) - print("selection color:", selection) # Start processing our colors terminal_colors = [default, black] @@ -105,17 +102,11 @@ def convert_color_scheme(infile, outfile): if d < smallest_distance: best_index = j smallest_distance = d - assert best_index >= 0 - print("best color match for", _name_from_index[i], "(", - rgb_to_hex(_rgb_from_name[_name_from_index[i]]), ")", "is", - rgb_to_hex(colors[best_index]), "at index", best_index, "with a squared distance of", - smallest_distance) terminal_colors.append(colors[best_index]) del colors[best_index] # don't repeat colors # convert our colors back to hex terminal_colors = [rgb_to_hex(c) for c in terminal_colors] - assert len(terminal_colors) == 8 # Remove scopes from the color scheme. while len(scheme) > 1: From 66c1957dde09d594a6d94e1469be08a5b3ff32ca Mon Sep 17 00:00:00 2001 From: Raoul Wols Date: Sun, 13 Aug 2017 13:41:25 +0200 Subject: [PATCH 08/16] flake8 improvements --- convert_color_scheme.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/convert_color_scheme.py b/convert_color_scheme.py index 7c5b64a..c3105e5 100644 --- a/convert_color_scheme.py +++ b/convert_color_scheme.py @@ -30,14 +30,12 @@ def distance2(a, b): def next_color(color_text): - """ - Given a color string "#xxxxxy", returns its next color "#xxxxx{y+1}". - """ + """Given a color string "#xxxxxy", returns its next color "#xxxxx{y+1}".""" hex_value = int(color_text[1:], 16) if hex_value == 16777215: # #ffffff return "#fffffe" else: - return "#{}".format(hex(hex_value+1)[2:]) + return "#{}".format(hex(hex_value + 1)[2:]) _name_from_index = ["black", "white", "red", "green", "blue", "brown", "magenta", "cyan"] From 911ed7cf2e9d771381548fc83de70a093e6ce15b Mon Sep 17 00:00:00 2001 From: Raoul Wols Date: Sun, 13 Aug 2017 13:49:30 +0200 Subject: [PATCH 09/16] Remove all flake8 warnings --- convert_color_scheme.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/convert_color_scheme.py b/convert_color_scheme.py index c3105e5..dc008d6 100644 --- a/convert_color_scheme.py +++ b/convert_color_scheme.py @@ -41,16 +41,16 @@ def next_color(color_text): _name_from_index = ["black", "white", "red", "green", "blue", "brown", "magenta", "cyan"] _rgb_from_name = { - "black": (0., 0., 0.), - "white": (1., 1., 1.), + "black": (0., 0., 0.), # NOQA (silence flake8 linter about extraneous whitespace) + "white": (1., 1., 1.), # NOQA - "red": (1., 0., 0.), - "green": (0., 1., 0.), - "blue": (0., 0., 1.), + "red": (1., 0., 0.), # NOQA + "green": (0., 1., 0.), # NOQA + "blue": (0., 0., 1.), # NOQA - "cyan": (0., 1., 1.), - "magenta": (1., 0., 1.), - "brown": (1., 1., 0.) # should be yellow...? + "cyan": (0., 1., 1.), # NOQA + "magenta": (1., 0., 1.), # NOQA + "brown": (1., 1., 0.) # NOQA FIXME: Should be yellow...? This looks like a pyte issue. } @@ -71,7 +71,7 @@ def convert_color_scheme(infile, outfile): # background color for all other colors. selection = scheme[0]["settings"]["selection"] - # Fetch all the other colors + # Fetch all the other colors. for i in range(1, len(scheme)): scope = scheme[i].get("scope", None) if scope and "sublimelinter" in scope: @@ -83,7 +83,7 @@ def convert_color_scheme(infile, outfile): colors = list(colors) print("extracted", len(colors), "scope colors from scheme") - # Start processing our colors + # Start processing our colors. terminal_colors = [default, black] while len(colors) < 6: print("adding extra black color so that we have enough colors to work with.") @@ -91,6 +91,7 @@ def convert_color_scheme(infile, outfile): colors.append(black) # Skip the first two colors ("black" and "white"). + # This is the main "algorithm" of this function. for i in range(2, 8): best_index = -1 smallest_distance = float("inf") @@ -101,9 +102,9 @@ def convert_color_scheme(infile, outfile): best_index = j smallest_distance = d terminal_colors.append(colors[best_index]) - del colors[best_index] # don't repeat colors + del colors[best_index] # Don't repeat colors. - # convert our colors back to hex + # Convert our colors back to hex. terminal_colors = [rgb_to_hex(c) for c in terminal_colors] # Remove scopes from the color scheme. @@ -124,7 +125,7 @@ def convert_color_scheme(infile, outfile): settings = {"background": background, "foreground": foreground} scheme.append({"scope": scope, "settings": settings}) - # Save the results + # Save the results. os.makedirs(os.path.dirname(outfile), exist_ok=True) print("saving to", outfile) plistlib.writePlist(base, outfile) From 9c068bff8aa864709f3a68617b331ab8be7c5424 Mon Sep 17 00:00:00 2001 From: Raoul Wols Date: Sun, 13 Aug 2017 13:56:40 +0200 Subject: [PATCH 10/16] Add minor comment clarifications --- convert_color_scheme.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/convert_color_scheme.py b/convert_color_scheme.py index dc008d6..a7569ec 100644 --- a/convert_color_scheme.py +++ b/convert_color_scheme.py @@ -38,6 +38,7 @@ def next_color(color_text): return "#{}".format(hex(hex_value + 1)[2:]) +# Also see: pyte/graphics.py _name_from_index = ["black", "white", "red", "green", "blue", "brown", "magenta", "cyan"] _rgb_from_name = { @@ -59,7 +60,6 @@ def convert_color_scheme(infile, outfile): print("processing file", infile) base = plistlib.readPlistFromBytes(sublime.load_resource(infile).encode("utf-8")) scheme = base["settings"] - colors = set() # Fetch the "default" color. default = hex_to_rgb(scheme[0]["settings"]["background"]) @@ -71,15 +71,20 @@ def convert_color_scheme(infile, outfile): # background color for all other colors. selection = scheme[0]["settings"]["selection"] - # Fetch all the other colors. + # Fetch all the other colors (start at 1). + colors = set() for i in range(1, len(scheme)): - scope = scheme[i].get("scope", None) + item = scheme[i] + scope = item.get("scope", None) if scope and "sublimelinter" in scope: print("skipping sublimelinter scope...") continue - hexcolor = scheme[i].get("settings", {}).get("foreground", None) + hexcolor = item.get("settings", {}).get("foreground", None) if hexcolor: + # Note that colors is a set, so duplicates are removed while we iterate. colors.add(hex_to_rgb(hexcolor)) + + # Convert into a list colors = list(colors) print("extracted", len(colors), "scope colors from scheme") From 73c8e4f0421c998b23be28ef399170084e4c453d Mon Sep 17 00:00:00 2001 From: Raoul Wols Date: Sun, 13 Aug 2017 14:17:04 +0200 Subject: [PATCH 11/16] Fix minor bug in next_color function When the result of converting the number to a hex value starts with a 0, then that starting digit wasn't recorded in the string. It is now. --- convert_color_scheme.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/convert_color_scheme.py b/convert_color_scheme.py index a7569ec..839a1ca 100644 --- a/convert_color_scheme.py +++ b/convert_color_scheme.py @@ -13,10 +13,9 @@ def hex_to_rgb(hexstring): def rgb_to_hex(rgb): """Convert an RGB tuple to a hex string.""" - return "#{}{}{}".format( - format(int(rgb[0] * 255), "02x"), - format(int(rgb[1] * 255), "02x"), - format(int(rgb[2] * 255), "02x")) + return "#{:02x}{:02x}{:02x}".format(int(rgb[0] * 255), + int(rgb[1] * 255), + int(rgb[2] * 255)) def norm2(vec3): @@ -35,7 +34,7 @@ def next_color(color_text): if hex_value == 16777215: # #ffffff return "#fffffe" else: - return "#{}".format(hex(hex_value + 1)[2:]) + return "#{:06x}".format(hex_value + 1) # Also see: pyte/graphics.py From 4ab50da376806afcffa48a0b7d21a54a5cc09bd7 Mon Sep 17 00:00:00 2001 From: Raoul Wols Date: Sun, 13 Aug 2017 14:50:01 +0200 Subject: [PATCH 12/16] Prettify the print-colors script --- print-colors.sh | 111 +++++++++++++----------------------------------- 1 file changed, 30 insertions(+), 81 deletions(-) diff --git a/print-colors.sh b/print-colors.sh index 8563df0..d2412d7 100755 --- a/print-colors.sh +++ b/print-colors.sh @@ -1,83 +1,32 @@ #!/bin/bash # generated from pyte/graphics.py -echo -e '\x1B[49m\x1B[32mdefault green ' -echo -e '\x1B[49m\x1B[33mdefault brown ' -echo -e '\x1B[49m\x1B[34mdefault blue ' -echo -e '\x1B[49m\x1B[35mdefault magenta' -echo -e '\x1B[49m\x1B[36mdefault cyan ' -echo -e '\x1B[49m\x1B[37mdefault white ' -echo -e '\x1B[49m\x1B[39mdefault default' -echo -e '\x1B[49m\x1B[30mdefault black ' -echo -e '\x1B[49m\x1B[31mdefault red ' -echo -e '\x1B[40m\x1B[32m black green ' -echo -e '\x1B[40m\x1B[33m black brown ' -echo -e '\x1B[40m\x1B[34m black blue ' -echo -e '\x1B[40m\x1B[35m black magenta' -echo -e '\x1B[40m\x1B[36m black cyan ' -echo -e '\x1B[40m\x1B[37m black white ' -echo -e '\x1B[40m\x1B[39m black default' -echo -e '\x1B[40m\x1B[30m black black ' -echo -e '\x1B[40m\x1B[31m black red ' -echo -e '\x1B[41m\x1B[32m red green ' -echo -e '\x1B[41m\x1B[33m red brown ' -echo -e '\x1B[41m\x1B[34m red blue ' -echo -e '\x1B[41m\x1B[35m red magenta' -echo -e '\x1B[41m\x1B[36m red cyan ' -echo -e '\x1B[41m\x1B[37m red white ' -echo -e '\x1B[41m\x1B[39m red default' -echo -e '\x1B[41m\x1B[30m red black ' -echo -e '\x1B[41m\x1B[31m red red ' -echo -e '\x1B[42m\x1B[32m green green ' -echo -e '\x1B[42m\x1B[33m green brown ' -echo -e '\x1B[42m\x1B[34m green blue ' -echo -e '\x1B[42m\x1B[35m green magenta' -echo -e '\x1B[42m\x1B[36m green cyan ' -echo -e '\x1B[42m\x1B[37m green white ' -echo -e '\x1B[42m\x1B[39m green default' -echo -e '\x1B[42m\x1B[30m green black ' -echo -e '\x1B[42m\x1B[31m green red ' -echo -e '\x1B[43m\x1B[32m brown green ' -echo -e '\x1B[43m\x1B[33m brown brown ' -echo -e '\x1B[43m\x1B[34m brown blue ' -echo -e '\x1B[43m\x1B[35m brown magenta' -echo -e '\x1B[43m\x1B[36m brown cyan ' -echo -e '\x1B[43m\x1B[37m brown white ' -echo -e '\x1B[43m\x1B[39m brown default' -echo -e '\x1B[43m\x1B[30m brown black ' -echo -e '\x1B[43m\x1B[31m brown red ' -echo -e '\x1B[44m\x1B[32m blue green ' -echo -e '\x1B[44m\x1B[33m blue brown ' -echo -e '\x1B[44m\x1B[34m blue blue ' -echo -e '\x1B[44m\x1B[35m blue magenta' -echo -e '\x1B[44m\x1B[36m blue cyan ' -echo -e '\x1B[44m\x1B[37m blue white ' -echo -e '\x1B[44m\x1B[39m blue default' -echo -e '\x1B[44m\x1B[30m blue black ' -echo -e '\x1B[44m\x1B[31m blue red ' -echo -e '\x1B[45m\x1B[32mmagenta green ' -echo -e '\x1B[45m\x1B[33mmagenta brown ' -echo -e '\x1B[45m\x1B[34mmagenta blue ' -echo -e '\x1B[45m\x1B[35mmagenta magenta' -echo -e '\x1B[45m\x1B[36mmagenta cyan ' -echo -e '\x1B[45m\x1B[37mmagenta white ' -echo -e '\x1B[45m\x1B[39mmagenta default' -echo -e '\x1B[45m\x1B[30mmagenta black ' -echo -e '\x1B[45m\x1B[31mmagenta red ' -echo -e '\x1B[46m\x1B[32m cyan green ' -echo -e '\x1B[46m\x1B[33m cyan brown ' -echo -e '\x1B[46m\x1B[34m cyan blue ' -echo -e '\x1B[46m\x1B[35m cyan magenta' -echo -e '\x1B[46m\x1B[36m cyan cyan ' -echo -e '\x1B[46m\x1B[37m cyan white ' -echo -e '\x1B[46m\x1B[39m cyan default' -echo -e '\x1B[46m\x1B[30m cyan black ' -echo -e '\x1B[46m\x1B[31m cyan red ' -echo -e '\x1B[47m\x1B[32m white green ' -echo -e '\x1B[47m\x1B[33m white brown ' -echo -e '\x1B[47m\x1B[34m white blue ' -echo -e '\x1B[47m\x1B[35m white magenta' -echo -e '\x1B[47m\x1B[36m white cyan ' -echo -e '\x1B[47m\x1B[37m white white ' -echo -e '\x1B[47m\x1B[39m white default' -echo -e '\x1B[47m\x1B[30m white black ' -echo -e '\x1B[47m\x1B[31m white red ' + +echo -e '\x1B[49m\x1B[30mdefault black \x1B[40m\x1B[30m black black \x1B[41m\x1B[30m red black \x1B[49m\x1B[39m' +echo -e '\x1B[49m\x1B[31mdefault red \x1B[40m\x1B[31m black red \x1B[41m\x1B[31m red red \x1B[49m\x1B[39m' +echo -e '\x1B[49m\x1B[32mdefault green \x1B[40m\x1B[32m black green \x1B[41m\x1B[32m red green \x1B[49m\x1B[39m' +echo -e '\x1B[49m\x1B[33mdefault brown \x1B[40m\x1B[33m black brown \x1B[41m\x1B[33m red brown \x1B[49m\x1B[39m' +echo -e '\x1B[49m\x1B[34mdefault blue \x1B[40m\x1B[34m black blue \x1B[41m\x1B[34m red blue \x1B[49m\x1B[39m' +echo -e '\x1B[49m\x1B[35mdefault magenta\x1B[40m\x1B[35m black magenta\x1B[41m\x1B[35m red magenta\x1B[49m\x1B[39m' +echo -e '\x1B[49m\x1B[36mdefault cyan \x1B[40m\x1B[36m black cyan \x1B[41m\x1B[36m red cyan \x1B[49m\x1B[39m' +echo -e '\x1B[49m\x1B[37mdefault white \x1B[40m\x1B[37m black white \x1B[41m\x1B[37m red white \x1B[49m\x1B[39m' +echo -e '\x1B[49m\x1B[39mdefault default\x1B[40m\x1B[39m black default\x1B[41m\x1B[39m red default\x1B[49m\x1B[39m' + +echo -e '\x1B[42m\x1B[30m green black \x1B[43m\x1B[30m brown black \x1B[44m\x1B[30m blue black \x1B[49m\x1B[39m' +echo -e '\x1B[42m\x1B[31m green red \x1B[43m\x1B[31m brown red \x1B[44m\x1B[31m blue red \x1B[49m\x1B[39m' +echo -e '\x1B[42m\x1B[32m green green \x1B[43m\x1B[32m brown green \x1B[44m\x1B[32m blue green \x1B[49m\x1B[39m' +echo -e '\x1B[42m\x1B[33m green brown \x1B[43m\x1B[33m brown brown \x1B[44m\x1B[33m blue brown \x1B[49m\x1B[39m' +echo -e '\x1B[42m\x1B[34m green blue \x1B[43m\x1B[34m brown blue \x1B[44m\x1B[34m blue blue \x1B[49m\x1B[39m' +echo -e '\x1B[42m\x1B[35m green magenta\x1B[43m\x1B[35m brown magenta\x1B[44m\x1B[35m blue magenta\x1B[49m\x1B[39m' +echo -e '\x1B[42m\x1B[36m green cyan \x1B[43m\x1B[36m brown cyan \x1B[44m\x1B[36m blue cyan \x1B[49m\x1B[39m' +echo -e '\x1B[42m\x1B[37m green white \x1B[43m\x1B[37m brown white \x1B[44m\x1B[37m blue white \x1B[49m\x1B[39m' +echo -e '\x1B[42m\x1B[39m green default\x1B[43m\x1B[39m brown default\x1B[44m\x1B[39m blue default\x1B[49m\x1B[39m' + +echo -e '\x1B[45m\x1B[30mmagenta black \x1B[46m\x1B[30m cyan black \x1B[47m\x1B[30m white black \x1B[49m\x1B[39m' +echo -e '\x1B[45m\x1B[31mmagenta red \x1B[46m\x1B[31m cyan red \x1B[47m\x1B[31m white red \x1B[49m\x1B[39m' +echo -e '\x1B[45m\x1B[32mmagenta green \x1B[46m\x1B[32m cyan green \x1B[47m\x1B[32m white green \x1B[49m\x1B[39m' +echo -e '\x1B[45m\x1B[33mmagenta brown \x1B[46m\x1B[33m cyan brown \x1B[47m\x1B[33m white brown \x1B[49m\x1B[39m' +echo -e '\x1B[45m\x1B[34mmagenta blue \x1B[46m\x1B[34m cyan blue \x1B[47m\x1B[34m white blue \x1B[49m\x1B[39m' +echo -e '\x1B[45m\x1B[35mmagenta magenta\x1B[46m\x1B[35m cyan magenta\x1B[47m\x1B[35m white magenta\x1B[49m\x1B[39m' +echo -e '\x1B[45m\x1B[36mmagenta cyan \x1B[46m\x1B[36m cyan cyan \x1B[47m\x1B[36m white cyan \x1B[49m\x1B[39m' +echo -e '\x1B[45m\x1B[37mmagenta white \x1B[46m\x1B[37m cyan white \x1B[47m\x1B[37m white white \x1B[49m\x1B[39m' +echo -e '\x1B[45m\x1B[39mmagenta default\x1B[46m\x1B[39m cyan default\x1B[47m\x1B[39m white default\x1B[49m\x1B[39m' From ba4d2a325083106d0e4b5526d7bae4b8c1b2eb64 Mon Sep 17 00:00:00 2001 From: Raoul Wols Date: Sun, 13 Aug 2017 15:30:32 +0200 Subject: [PATCH 13/16] Add application command to clear/remove all generated color schemes --- Main.sublime-menu | 4 ++++ TerminalView.sublime-commands | 4 ++++ clear_color_scheme_cache.py | 21 +++++++++++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 clear_color_scheme_cache.py diff --git a/Main.sublime-menu b/Main.sublime-menu index 7b3af89..df9d09b 100644 --- a/Main.sublime-menu +++ b/Main.sublime-menu @@ -38,6 +38,10 @@ "args": {"file": "${packages}/TerminalView/README.md"}, "caption": "Readme" }, + { + "command": "terminal_view_clear_color_scheme_cache", + "caption": "Clear Color Scheme Cache" + }, ] } ] diff --git a/TerminalView.sublime-commands b/TerminalView.sublime-commands index 90c4826..34278a3 100644 --- a/TerminalView.sublime-commands +++ b/TerminalView.sublime-commands @@ -35,4 +35,8 @@ "command": "edit_settings", "args": {"base_file": "${packages}/TerminalView/TerminalView.sublime-commands", "default": "[]"}, }, + { + "caption": "Preferences: Terminal View: Clear Color Scheme Cache", + "command": "terminal_view_clear_color_scheme_cache" + }, ] diff --git a/clear_color_scheme_cache.py b/clear_color_scheme_cache.py new file mode 100644 index 0000000..116917d --- /dev/null +++ b/clear_color_scheme_cache.py @@ -0,0 +1,21 @@ +"""Removes all trace of generated color schemes.""" +import sublime +import sublime_plugin +import shutil +import os + + +class TerminalViewClearColorSchemeCacheCommand(sublime_plugin.ApplicationCommand): + """Removes the folder $cache/User/TerminalView and $packages/User/TerminalView.""" + + def run(self): + """Run this command.""" + self._force_remove(os.path.join(sublime.cache_path(), "User", "TerminalView")) + self._force_remove(os.path.join(sublime.packages_path(), "User", "TerminalView")) + sublime.message_dialog("Cache is cleared.") + + def _force_remove(self, path): + try: + shutil.rmtree(path) + except FileNotFoundError: + pass From 8b061d95699e2b01b0e60b97016bda36484d519a Mon Sep 17 00:00:00 2001 From: Raoul Wols Date: Sun, 13 Aug 2017 17:40:05 +0200 Subject: [PATCH 14/16] Minor fix for colors with alpha channels --- convert_color_scheme.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/convert_color_scheme.py b/convert_color_scheme.py index 839a1ca..b822509 100644 --- a/convert_color_scheme.py +++ b/convert_color_scheme.py @@ -6,6 +6,8 @@ def hex_to_rgb(hexstring): """Convert a string representing a hex color to an RGB tuple.""" + + # Forget about the alpha channel (that's possibly stored in hexstring[7:9]). return (int(hexstring[1:3], 16) / 255, int(hexstring[3:5], 16) / 255, int(hexstring[5:7], 16) / 255) @@ -13,6 +15,9 @@ def hex_to_rgb(hexstring): def rgb_to_hex(rgb): """Convert an RGB tuple to a hex string.""" + + # Note that if a hexstring has an alpha channel, then that information is lost when you go + # from hexstring -> rgb-tuple -> hexstring. return "#{:02x}{:02x}{:02x}".format(int(rgb[0] * 255), int(rgb[1] * 255), int(rgb[2] * 255)) @@ -28,13 +33,14 @@ def distance2(a, b): return norm2((a[0] - b[0], a[1] - b[1], a[2] - b[2])) -def next_color(color_text): +def next_color(hexstring): """Given a color string "#xxxxxy", returns its next color "#xxxxx{y+1}".""" - hex_value = int(color_text[1:], 16) - if hex_value == 16777215: # #ffffff - return "#fffffe" - else: - return "#{:06x}".format(hex_value + 1) + + # Forget about the alpha channel (that's possibly stored in hexstring[7:9]). + h = int(hexstring[1:7], 16) + + # Return one more than we got, or one less if we're already at the max. + return "#fffffe" if h == 0xffffff else "#{:06x}".format(h + 1) # Also see: pyte/graphics.py From 2b140005be24201aaa35005ede89808e4a39cb5f Mon Sep 17 00:00:00 2001 From: Raoul Wols Date: Mon, 14 Aug 2017 14:58:39 +0200 Subject: [PATCH 15/16] Use CIELAB 1994 metric instead of Eucledean distance metric There's also a preliminary implementation of the CIELAB 2000 metric, but it looks like it gives some wrong results for some colors, so it probably has bugs. This commit uses the 1994 version. Also, cie94 really loves the green component. This is why the reference value for the yellow color is changed from (1,1,0) to (1,200/255,0). Empirically better results. --- convert_color_scheme.py | 185 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 181 insertions(+), 4 deletions(-) diff --git a/convert_color_scheme.py b/convert_color_scheme.py index b822509..41153c4 100644 --- a/convert_color_scheme.py +++ b/convert_color_scheme.py @@ -2,7 +2,174 @@ import os import plistlib import sublime - +from math import sqrt, sin, cos, pi, atan2, fabs, exp + + +# https://gist.github.com/fikr4n/368f2f2070e0f9a15fb4 + +def _square(x): + return x * x + + +def cie76(L1_a1_b1, L2_a2_b2): + L1, a1, b1 = L1_a1_b1 + L2, a2, b2 = L2_a2_b2 + return sqrt(_square(L1 - L2) + _square(a1 - a2) + _square(b1 - b2)) + + +def cie94(L1_a1_b1, L2_a2_b2): + """Calculate color difference by using CIE94 formulae + + See http://en.wikipedia.org/wiki/Color_difference or + http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CIE94.html. + + cie94(rgb2lab((255, 255, 255)), rgb2lab((0, 0, 0))) + >>> 58.0 + cie94(rgb2lab(rgb(0xff0000)), rgb2lab(rgb('#ff0000'))) + >>> 0.0 + """ + + L1, a1, b1 = L1_a1_b1 + L2, a2, b2 = L2_a2_b2 + + C1 = sqrt(_square(a1) + _square(b1)) + C2 = sqrt(_square(a2) + _square(b2)) + delta_L = L1 - L2 + delta_C = C1 - C2 + delta_a = a1 - a2 + delta_b = b1 - b2 + delta_H_square = _square(delta_a) + _square(delta_b) - _square(delta_C) + return sqrt(_square(delta_L) + _square(delta_C) / _square(1.0 + 0.045 * C1) + + delta_H_square / _square(1.0 + 0.015 * C1)) + + +def cie2000(L1_a1_b1, L2_a2_b2): + """Calculate color difference by using CIE2000 formulae""" + + # blatantly copied from + # http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CIE2000.html + L1, a1, b1 = L1_a1_b1 + L2, a2, b2 = L2_a2_b2 + C1 = sqrt(_square(a1) + _square(b1)) + C2 = sqrt(_square(a2) + _square(b2)) + Lbarprime = 0.5 * (L1 + L2) + Cbar = 0.5 * (C1 + C2) + Cbar_7 = Cbar**7.0 + G = 0.5 * (1.0 - sqrt(Cbar_7 / (Cbar_7 + 25.0**7.0))) + a1prime = a1 * (1.0 + G) + a2prime = a2 * (1.0 + G) + C1prime = sqrt(_square(a1prime) + _square(b1)) + C2prime = sqrt(_square(a2prime) + _square(b2)) + Cbarprime = 0.5 * (C1prime + C2prime) + h1prime = atan2(b1, a1prime) + if h1prime < 0.0: + h1prime += 2.0 * pi + h2prime = atan2(b2, a2prime) + if h2prime < 0.0: + h2prime += 2.0 * pi + if fabs(h1prime - h2prime) > pi: + Hbarprime = 0.5 * (h1prime + h2prime + 2.0 * pi) + else: + Hbarprime = 0.5 * (h1prime + h2prime) + # 30 deg == 0.523598776 rad + # 6 deg == 0.104719755 rad + # 63 deg == 1.09955743 rad + T = 1.0 - \ + 0.17 * cos(Hbarprime - 0.523598776) + \ + 0.24 * cos(2.0 * Hbarprime) + \ + 0.32 * cos(3.0 * Hbarprime + 0.104719755) - \ + 0.20 * cos(4.0 * Hbarprime - 1.09955743) + if fabs(h1prime - h2prime) <= pi: + delta_hprime = h2prime - h1prime + elif fabs(h1prime - h2prime) > pi and h2prime <= h1prime: + delta_hprime = h2prime - h1prime + 2 * pi + else: + delta_hprime = h2prime - h1prime - 2 * pi + + delta_Lprime = L2 - L1 + delta_Cprime = C2prime - C1prime + delta_Hprime = 2.0 * sqrt(C1prime * C2prime) * sin(delta_hprime * 0.5) + + S_L = 1.0 + 0.015 * _square(Lbarprime - 50.0) / sqrt(20.0 + _square(Lbarprime - 50.0)) + S_C = 1.0 + 0.045 * Cbarprime + S_H = 1.0 + 0.015 * Cbarprime * T + + # 275 deg = 4.79965544 rad + delta_theta = 30.0 * exp(-_square((Hbarprime - 4.79965544) / 25.0)) + + Cbarprime_7 = Cbarprime**7.0 + R_C = 2.0 * sqrt(Cbarprime_7 / (Cbarprime_7 + 25.0**7)) + R_T = - R_C * sin(2.0 * delta_theta) + + K_L = 1.0 # default + K_C = 1.0 # default + K_H = 1.0 # default + + return sqrt(_square(delta_Lprime / (K_L * S_L)) + \ + _square(delta_Cprime / (K_C * S_C)) + \ + _square(delta_Hprime / (K_H * S_H)) + \ + R_T * (delta_Cprime / (K_C * S_C)) * (delta_Hprime / (K_H * S_H))) + + +def rgb2lab(R_G_B): + """Convert RGB colorspace to Lab + + Adapted from http://www.easyrgb.com/index.php?X=MATH. + """ + + R, G, B = R_G_B + + # Convert RGB to XYZ + + var_R = R / 255.0 # R from 0 to 255 + var_G = G / 255.0 # G from 0 to 255 + var_B = B / 255.0 # B from 0 to 255 + + if var_R > 0.04045: + var_R = ((var_R + 0.055) / 1.055) ** 2.4 + else: + var_R = var_R / 12.92 + if var_G > 0.04045: + var_G = ((var_G + 0.055) / 1.055) ** 2.4 + else: + var_G = var_G / 12.92 + if var_B > 0.04045: + var_B = ((var_B + 0.055) / 1.055) ** 2.4 + else: + var_B = var_B / 12.92 + + var_R = var_R * 100.0 + var_G = var_G * 100.0 + var_B = var_B * 100.0 + + # Observer. = 2°, Illuminant = D65 + X = var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805 + Y = var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722 + Z = var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505 + + # Convert XYZ to L*a*b* + + var_X = X / 95.047 # ref_X = 95.047 Observer= 2°, Illuminant= D65 + var_Y = Y / 100.000 # ref_Y = 100.000 + var_Z = Z / 108.883 # ref_Z = 108.883 + + if var_X > 0.008856: + var_X = var_X ** (1.0/3.0) + else: + var_X = (7.787 * var_X) + (16.0 / 116.0) + if var_Y > 0.008856: + var_Y = var_Y ** (1.0/3.0) + else: + var_Y = (7.787 * var_Y) + (16.0 / 116.0) + if var_Z > 0.008856: + var_Z = var_Z ** (1.0/3.0) + else: + var_Z = (7.787 * var_Z) + (16.0 / 116.0) + + CIE_L = (116.0 * var_Y) - 16.0 + CIE_a = 500.0 * (var_X - var_Y) + CIE_b = 200.0 * (var_Y - var_Z) + return (CIE_L, CIE_a, CIE_b) def hex_to_rgb(hexstring): """Convert a string representing a hex color to an RGB tuple.""" @@ -56,7 +223,7 @@ def next_color(hexstring): "cyan": (0., 1., 1.), # NOQA "magenta": (1., 0., 1.), # NOQA - "brown": (1., 1., 0.) # NOQA FIXME: Should be yellow...? This looks like a pyte issue. + "brown": (1., 200/255., 0.) # NOQA FIXME: Should be yellow...? This looks like a pyte issue. } @@ -93,6 +260,9 @@ def convert_color_scheme(infile, outfile): colors = list(colors) print("extracted", len(colors), "scope colors from scheme") + # Convert them to Lab coordinates + colors_lab = [rgb2lab(c) for c in colors] + # Start processing our colors. terminal_colors = [default, black] while len(colors) < 6: @@ -106,13 +276,20 @@ def convert_color_scheme(infile, outfile): best_index = -1 smallest_distance = float("inf") terminal_color = _rgb_from_name[_name_from_index[i]] - for j, color in enumerate(colors): - d = distance2(color, terminal_color) + terminal_color = rgb2lab(terminal_color) + for j, lab in enumerate(colors_lab): + # We can choose from 4 different metrics: + # - distance2 (using RGB coordinates), + # - cie76 (using Lab coordinates), + # - cie94 (using Lab coordinates), + # - cie2000 (using Lab coordinates). + d = cie94(terminal_color, lab) if d < smallest_distance: best_index = j smallest_distance = d terminal_colors.append(colors[best_index]) del colors[best_index] # Don't repeat colors. + del colors_lab[best_index] # Convert our colors back to hex. terminal_colors = [rgb_to_hex(c) for c in terminal_colors] From 190114bacbfe8397c205867f7537fa00b7a6c51e Mon Sep 17 00:00:00 2001 From: Raoul Wols Date: Mon, 14 Aug 2017 18:24:08 +0200 Subject: [PATCH 16/16] Remove rulers for a terminal view --- sublime_terminal_buffer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sublime_terminal_buffer.py b/sublime_terminal_buffer.py index 73aed57..fb6f242 100644 --- a/sublime_terminal_buffer.py +++ b/sublime_terminal_buffer.py @@ -54,6 +54,7 @@ def __init__(self, sublime_view, title, syntax_file=None): self._view.settings().set("draw_indent_guides", False) self._view.settings().set("caret_style", "blink") self._view.settings().set("scroll_past_end", False) + self._view.settings().set("rulers", []) self._is_setting_color_scheme = False self._view.settings().add_on_change("terminal_view_color_scheme", self._set_color_scheme)