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.""" """Tools for helping with ANSI color codes."""
import re
import sys import sys
import warnings import warnings
import builtins import builtins
from xonsh.platform import HAS_PYGMENTS from xonsh.platform import HAS_PYGMENTS
from xonsh.lazyasd import LazyDict from xonsh.lazyasd import LazyDict, lazyobject
from xonsh.color_tools import ( from xonsh.color_tools import (
RE_BACKGROUND, RE_BACKGROUND,
BASE_XONSH_COLORS, BASE_XONSH_COLORS,
@ -114,6 +115,74 @@ def ansi_color_style(style="default"):
return cmap 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): def _ansi_expand_style(cmap):
"""Expands a style in order to more quickly make color map changes.""" """Expands a style in order to more quickly make color map changes."""
for key, val in list(cmap.items()): for key, val in list(cmap.items()):

View file

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