From 27fc9a433ca6d8f95ff65fe7afd15840e8d89ab8 Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Wed, 10 Nov 2021 23:58:59 +1000 Subject: [PATCH] Coding style. - Add xterm colors, and func for finding closest color, to termstr.py. - Remove termstr's dependencies on pygments and ColorMap.py. --- eris/eris/__main__.py | 4 +- termstr/requirements.txt | 1 - termstr/termstr/ColorMap.py | 128 ---------------------------------- termstr/termstr/__init__.py | 51 +++++++++----- termstr/tests/termstr_test.py | 1 - 5 files changed, 35 insertions(+), 150 deletions(-) delete mode 100644 termstr/termstr/ColorMap.py diff --git a/eris/eris/__main__.py b/eris/eris/__main__.py index c01b1cd..ca6ee64 100755 --- a/eris/eris/__main__.py +++ b/eris/eris/__main__.py @@ -174,9 +174,9 @@ def highlight_str(line, highlight_color, transparency): @functools.lru_cache() def blend_style(style): fg_color = (style.fg_color if type(style.fg_color) == tuple - else termstr.xterm_color_to_rgb(style.fg_color)) + else termstr.XTERM_COLORS[style.fg_color]) bg_color = (style.bg_color if type(style.bg_color) == tuple - else termstr.xterm_color_to_rgb(style.bg_color)) + else termstr.XTERM_COLORS[style.bg_color]) return termstr.CharStyle( blend_color(fg_color, highlight_color, transparency), blend_color(bg_color, highlight_color, transparency), diff --git a/termstr/requirements.txt b/termstr/requirements.txt index 3b9bac4..7db594d 100644 --- a/termstr/requirements.txt +++ b/termstr/requirements.txt @@ -1,2 +1 @@ -pygments==2.10.0 cwcwidth==0.1.5 diff --git a/termstr/termstr/ColorMap.py b/termstr/termstr/ColorMap.py deleted file mode 100644 index 20df651..0000000 --- a/termstr/termstr/ColorMap.py +++ /dev/null @@ -1,128 +0,0 @@ - -# This is from https://github.com/broadinstitute/xtermcolor - - -# Copyright (C) 2012 The Broad Institute - -# Permission is hereby granted, free of charge, to any person obtaining a copy of -# this software and associated documentation files (the "Software"), to deal in -# the Software without restriction, including without limitation the rights to -# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -# of the Software, and to permit persons to whom the Software is furnished to do -# so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - - -class TerminalColorMapException(Exception): - pass - - -def _rgb(color): - return ((color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff) - - -def _diff(color1, color2): - (r1, g1, b1) = _rgb(color1) - (r2, g2, b2) = _rgb(color2) - return abs(r1 - r2) + abs(g1 - g2) + abs(b1 - b2) - - -class TerminalColorMap: - - def getColors(self, order='rgb'): - return self.colors - - def convert(self, hexcolor): - diffs = {} - for xterm, rgb in self.colors.items(): - diffs[_diff(rgb, hexcolor)] = xterm - minDiffAnsi = diffs[min(diffs.keys())] - return (minDiffAnsi, self.colors[minDiffAnsi]) - - def colorize(self, string, rgb=None, ansi=None, bg=None, ansi_bg=None): - '''Returns the colored string''' - if not isinstance(string, str): - string = str(string) - if rgb is None and ansi is None: - raise TerminalColorMapException( - 'colorize: must specify one named parameter: rgb or ansi') - if rgb is not None and ansi is not None: - raise TerminalColorMapException( - 'colorize: must specify only one named parameter: rgb or ansi') - if bg is not None and ansi_bg is not None: - raise TerminalColorMapException( - 'colorize: must specify only one named parameter: bg or ansi_bg') - - if rgb is not None: - (closestAnsi, closestRgb) = self.convert(rgb) - elif ansi is not None: - (closestAnsi, closestRgb) = (ansi, self.colors[ansi]) - - if bg is None and ansi_bg is None: - return "\033[38;5;{ansiCode:d}m{string:s}\033[0m".format(ansiCode=closestAnsi, string=string) - - if bg is not None: - (closestBgAnsi, unused) = self.convert(bg) - elif ansi_bg is not None: - (closestBgAnsi, unused) = (ansi_bg, self.colors[ansi_bg]) - - return "\033[38;5;{ansiCode:d}m\033[48;5;{bf:d}m{string:s}\033[0m".format(ansiCode=closestAnsi, bf=closestBgAnsi, string=string) - - -class VT100ColorMap(TerminalColorMap): - primary = [ - 0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080, 0x008080, 0xc0c0c0 - ] - - bright = [ - 0x808080, 0xff0000, 0x00ff00, 0xffff00, 0x0000ff, 0xff00ff, 0x00ffff, 0xffffff - ] - - def __init__(self): - self.colors = dict() - self._compute() - - def _compute(self): - for index, color in enumerate(self.primary + self.bright): - self.colors[index] = color - - -class XTermColorMap(VT100ColorMap): - grayscale_start = 0x08 - grayscale_end = 0xf8 - grayscale_step = 10 - intensities = [ - 0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF - ] - - def _compute(self): - for index, color in enumerate(self.primary + self.bright): - self.colors[index] = color - - c = 16 - for i in self.intensities: - color = i << 16 - for j in self.intensities: - color &= ~(0xff << 8) - color |= j << 8 - for k in self.intensities: - color &= ~0xff - color |= k - self.colors[c] = color - c += 1 - - c = 232 - for hex in list(range(self.grayscale_start, self.grayscale_end, self.grayscale_step)): - color = (hex << 16) | (hex << 8) | hex - self.colors[c] = color - c += 1 diff --git a/termstr/termstr/__init__.py b/termstr/termstr/__init__.py index 6fa9425..fa595e2 100644 --- a/termstr/termstr/__init__.py +++ b/termstr/termstr/__init__.py @@ -7,11 +7,8 @@ import itertools import os import weakref -import pygments.formatters.terminal256 import cwcwidth -import termstr.ColorMap - ESC = "\x1b" @@ -21,9 +18,6 @@ ITALIC = "[3m" UNDERLINE = "[4m" -xterm_colormap = termstr.ColorMap.XTermColorMap() - - def color(color_number, is_foreground): return f"[{'38' if is_foreground else '48'};5;{color_number:d}m" @@ -32,11 +26,6 @@ def rgb_color(rgb, is_foreground): return f"[{'38' if is_foreground else '48'};2;" + "%i;%i;%im" % rgb -@functools.lru_cache() -def xterm_color_to_rgb(color_index): - return termstr.ColorMap._rgb(xterm_colormap.colors[color_index]) - - class Color: # https://en.wikipedia.org/wiki/Natural_Color_System @@ -60,11 +49,38 @@ class Color: orange = (255, 153, 0) +def _xterm_colors(): + result = [ + (0x00, 0x00, 0x00), (0xcd, 0x00, 0x00), (0x00, 0xcd, 0x00), + (0xcd, 0xcd, 0x00), (0x00, 0x00, 0xee), (0xcd, 0x00, 0xcd), + (0x00, 0xcd, 0xcd), (0xe5, 0xe5, 0xe5), (0x7f, 0x7f, 0x7f), + (0xff, 0x00, 0x00), (0x00, 0xff, 0x00), (0xff, 0xff, 0x00), + (0x5c, 0x5c, 0xff), (0xff, 0x00, 0xff), (0x00, 0xff, 0xff), + (0xff, 0xff, 0xff)] + intensities = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff] + result.extend([(intensities[(i // 36) % 6], intensities[(i // 6) % 6], + intensities[i % 6]) for i in range(216)]) + result.extend([(8 + i * 10, 8 + i * 10, 8 + i * 10) for i in range(24)]) + return result + + +XTERM_COLORS = _xterm_colors() + + +def closest_color_index(color, colors): + r, g, b = color + closest_distance = 3 * (256*256) + 1 + for index, (r_, g_, b_) in enumerate(colors): + distance = (r_ - r) ** 2 + (g_ - g) ** 2 + (b_ - b) ** 2 + if distance < closest_distance: + closest_distance = distance + color_index = index + return color_index + + class CharStyle: _POOL = weakref.WeakValueDictionary() - _TERMINAL256_FORMATTER = \ - pygments.formatters.terminal256.Terminal256Formatter() def __new__(cls, fg_color=None, bg_color=None, is_bold=False, is_italic=False, is_underlined=False): @@ -110,9 +126,8 @@ class CharStyle: return color(color_, is_foreground) else: # true color if os.environ.get("TERM", None) == "xterm": - closest_color = self._TERMINAL256_FORMATTER._closest_color( - *color_) - return color(closest_color, is_foreground) + color_index = closest_color_index(color_, XTERM_COLORS) + return color(color_index, is_foreground) else: return rgb_color(color_, is_foreground) @@ -132,9 +147,9 @@ class CharStyle: underline_code = ("text-decoration:underline; " if self.is_underlined else "") fg_color = (self.fg_color if type(self.fg_color) == tuple - else xterm_color_to_rgb(self.fg_color)) + else xterm_colors[self.fg_color]) bg_color = (self.bg_color if type(self.bg_color) == tuple - else xterm_color_to_rgb(self.bg_color)) + else xterm_colors[self.bg_color]) return (f"") diff --git a/termstr/tests/termstr_test.py b/termstr/tests/termstr_test.py index 0721176..6fbdaca 100755 --- a/termstr/tests/termstr_test.py +++ b/termstr/tests/termstr_test.py @@ -5,7 +5,6 @@ import os import pickle import unittest -os.environ["TERM"] = "xterm-256color" import fill3.terminal as terminal import termstr