initial translation from ANSI codes to xonsh color names

This commit is contained in:
Anthony Scopatz 2019-02-08 16:56:44 -05:00
parent bf1fd1d284
commit efea8f05dc
3 changed files with 104 additions and 6 deletions

34
tests/test_ansi_colors.py Normal file
View file

@ -0,0 +1,34 @@
"""Tests ANSI color tools."""
import pytest
from xonsh.ansi_colors import ansi_color_escape_code_to_name, ansi_reverse_style
RS = ansi_reverse_style(style='default')
@pytest.mark.parametrize('key, value', [
('', 'NO_COLOR'),
('31', 'RED'),
])
def test_ansi_reverse_style(key, value):
assert key in RS
assert RS[key] == value
@pytest.mark.parametrize('inp, exp', [
('0', ('NO_COLOR',)),
('\0010\002', ('NO_COLOR',)),
('\033[0m', ('NO_COLOR',)),
('\001\033[0m\002', ('NO_COLOR',)),
('00;36', ('CYAN',)),
('01;31', ('BOLD_RED',)),
('04;31', ('UNDERLINE_RED',)),
('1;4;31', ('BOLD_UNDERLINE_RED',)),
('4;1;31', ('BOLD_UNDERLINE_RED',)),
('31;42', ('RED', 'BACKGROUND_GREEN')),
('42;31', ('BACKGROUND_GREEN', 'RED')),
('40', ('BACKGROUND_BLACK',)),
])
def test_ansi_color_escape_code_to_name(inp, exp):
obs = ansi_color_escape_code_to_name(inp, reversed_style=RS)
assert obs == exp

View file

@ -1,10 +1,11 @@
"""Tools for helping with ANSI color codes."""
import re
import sys
import warnings
import builtins
from xonsh.platform import HAS_PYGMENTS
from xonsh.lazyasd import LazyDict
from xonsh.lazyasd import LazyDict, lazyobject
from xonsh.color_tools import (
RE_BACKGROUND,
BASE_XONSH_COLORS,
@ -114,6 +115,74 @@ def ansi_color_style(style="default"):
return cmap
def ansi_reverse_style(style='default', return_style=False):
"""Reverses an ANSI color style mapping so that escape codes map to
colors. Style may either be string or mapping. May also return
the style it looked up.
"""
style = ansi_style_by_name(style) if isinstance(style, str) else style
reversed_style = {v: k for k, v in style.items()}
# add keys to make this more useful
updates = {
'1': 'BOLD_',
'4': 'UNDERLINE_',
'1;4': 'BOLD_UNDERLINE_',
'4;1': 'BOLD_UNDERLINE_',
}
for ec, name in reversed_style.items():
no_left_zero = ec.lstrip('0')
if no_left_zero.startswith(';'):
updates[no_left_zero[1:]] = name
elif no_left_zero != ec:
updates[no_left_zero] = name
reversed_style.update(updates)
# return results
if return_style:
return style, reversed_style
else:
return reversed_style
@lazyobject
def ANSI_ESCAPE_CODE_RE():
return re.compile(r'\001?(\033\[)?([0-9;]+)m?\002?')
def ansi_color_escape_code_to_name(escape_code, style='default', reversed_style=None):
"""Converts an ASNI color code escape sequence to a tuple of color names
in the provided style ('default', by default). For example,
'0' becomes ('NO_COLOR',) and '32;41' becomes ('GREEN', 'BACKGROUND_RED').
The style keyword may either be a string, in which the style is looked up,
or an actual style dict. You can also provide a reversed style mapping,
too, which is just the keys/values of the style dict swapped. If reversed
style is not provided, it is computed.
"""
if reversed_style is None:
style, reversed_style = ansi_reverse_style(style, return_style=True)
# strip some actual escape codes, if needed.
ec = ANSI_ESCAPE_CODE_RE.match(escape_code).group(2)
names = [reversed_style[e.lstrip('0')] for e in ec.split(';')]
# normalize names
n = ''
norm_names = []
for name in names:
if name == 'NO_COLOR':
# skip '0' entries
continue
n = n + name if n else name
if n == 'UNDERLINE_BOLD_':
n = 'BOLD_UNDERLINE_'
if n.endswith('_'):
continue
norm_names.append(n)
n = ''
# return
if len(norm_names) == 0:
return ('NO_COLOR',)
else:
return tuple(norm_names)
def _ansi_expand_style(cmap):
"""Expands a style in order to more quickly make color map changes."""
for key, val in list(cmap.items()):

View file

@ -413,8 +413,3 @@ def make_palette(strings):
t, _, s = t.partition(" ")
palette[t] = rgb_to_ints(t)
return palette
@deprecated(deprecated_in="0.5.10", removed_in="0.6.0")
def make_pallete(*args, **kwargs):
make_palette(*args, **kwargs)