coloransi.py
187 lines
| 6.8 KiB
| text/x-python
|
PythonLexer
fperez
|
r0 | # -*- coding: utf-8 -*- | ||
"""Tools for coloring text in ANSI terminals. | ||||
Fernando Perez
|
r1853 | """ | ||
fperez
|
r0 | |||
#***************************************************************************** | ||||
fperez
|
r88 | # Copyright (C) 2002-2006 Fernando Perez. <fperez@colorado.edu> | ||
fperez
|
r0 | # | ||
# Distributed under the terms of the BSD License. The full license is in | ||||
# the file COPYING, distributed as part of this software. | ||||
#***************************************************************************** | ||||
__all__ = ['TermColors','InputTermColors','ColorScheme','ColorSchemeTable'] | ||||
import os | ||||
Brian Granger
|
r2030 | from IPython.utils.ipstruct import Struct | ||
fperez
|
r0 | |||
Thomas Kluyver
|
r5495 | color_templates = ( | ||
Fernando Perez
|
r1593 | # Dark colors | ||
fperez
|
r0 | ("Black" , "0;30"), | ||
("Red" , "0;31"), | ||||
("Green" , "0;32"), | ||||
("Brown" , "0;33"), | ||||
("Blue" , "0;34"), | ||||
("Purple" , "0;35"), | ||||
("Cyan" , "0;36"), | ||||
("LightGray" , "0;37"), | ||||
Fernando Perez
|
r1593 | # Light colors | ||
fperez
|
r0 | ("DarkGray" , "1;30"), | ||
("LightRed" , "1;31"), | ||||
("LightGreen" , "1;32"), | ||||
("Yellow" , "1;33"), | ||||
("LightBlue" , "1;34"), | ||||
("LightPurple" , "1;35"), | ||||
("LightCyan" , "1;36"), | ||||
Bernardo B. Marques
|
r4872 | ("White" , "1;37"), | ||
Fernando Perez
|
r1593 | # Blinking colors. Probably should not be used in anything serious. | ||
("BlinkBlack" , "5;30"), | ||||
("BlinkRed" , "5;31"), | ||||
("BlinkGreen" , "5;32"), | ||||
("BlinkYellow" , "5;33"), | ||||
("BlinkBlue" , "5;34"), | ||||
("BlinkPurple" , "5;35"), | ||||
("BlinkCyan" , "5;36"), | ||||
("BlinkLightGray", "5;37"), | ||||
) | ||||
Fernando Perez
|
r1589 | |||
Thomas Kluyver
|
r5495 | def make_color_table(in_class): | ||
"""Build a set of color attributes in a class. | ||||
Thomas Kluyver
|
r13592 | Helper function for building the :class:`TermColors` and | ||
:class`InputTermColors`. | ||||
""" | ||||
Fernando Perez
|
r1594 | for name,value in color_templates: | ||
setattr(in_class,name,in_class._base % value) | ||||
Fernando Perez
|
r1589 | |||
fperez
|
r0 | class TermColors: | ||
"""Color escape sequences. | ||||
Bernardo B. Marques
|
r4872 | This class defines the escape sequences for all the standard (ANSI?) | ||
fperez
|
r0 | colors in terminals. Also defines a NoColor escape which is just the null | ||
string, suitable for defining 'dummy' color schemes in terminals which get | ||||
confused by color escapes. | ||||
This class should be used as a mixin for building color schemes.""" | ||||
Bernardo B. Marques
|
r4872 | |||
fperez
|
r0 | NoColor = '' # for color schemes in color-less terminals. | ||
Normal = '\033[0m' # Reset normal coloring | ||||
_base = '\033[%sm' # Template for all other colors | ||||
# Build the actual color table as a set of class attributes: | ||||
make_color_table(TermColors) | ||||
class InputTermColors: | ||||
"""Color escape sequences for input prompts. | ||||
This class is similar to TermColors, but the escapes are wrapped in \001 | ||||
and \002 so that readline can properly know the length of each line and | ||||
can wrap lines accordingly. Use this class for any colored text which | ||||
needs to be used in input prompts, such as in calls to raw_input(). | ||||
Bernardo B. Marques
|
r4872 | This class defines the escape sequences for all the standard (ANSI?) | ||
fperez
|
r0 | colors in terminals. Also defines a NoColor escape which is just the null | ||
string, suitable for defining 'dummy' color schemes in terminals which get | ||||
confused by color escapes. | ||||
This class should be used as a mixin for building color schemes.""" | ||||
Bernardo B. Marques
|
r4872 | |||
fperez
|
r0 | NoColor = '' # for color schemes in color-less terminals. | ||
fperez
|
r550 | |||
fperez
|
r564 | if os.name == 'nt' and os.environ.get('TERM','dumb') == 'emacs': | ||
fperez
|
r550 | # (X)emacs on W32 gets confused with \001 and \002 so we remove them | ||
Normal = '\033[0m' # Reset normal coloring | ||||
_base = '\033[%sm' # Template for all other colors | ||||
else: | ||||
Normal = '\001\033[0m\002' # Reset normal coloring | ||||
_base = '\001\033[%sm\002' # Template for all other colors | ||||
fperez
|
r0 | |||
# Build the actual color table as a set of class attributes: | ||||
make_color_table(InputTermColors) | ||||
Thomas Kluyver
|
r5495 | class NoColors: | ||
"""This defines all the same names as the colour classes, but maps them to | ||||
empty strings, so it can easily be substituted to turn off colours.""" | ||||
NoColor = '' | ||||
MinRK
|
r7580 | Normal = '' | ||
Thomas Kluyver
|
r5495 | |||
for name, value in color_templates: | ||||
setattr(NoColors, name, '') | ||||
fperez
|
r0 | class ColorScheme: | ||
"""Generic color scheme class. Just a name and a Struct.""" | ||||
def __init__(self,__scheme_name_,colordict=None,**colormap): | ||||
self.name = __scheme_name_ | ||||
if colordict is None: | ||||
self.colors = Struct(**colormap) | ||||
else: | ||||
self.colors = Struct(colordict) | ||||
fperez
|
r46 | |||
def copy(self,name=None): | ||||
"""Return a full copy of the object, optionally renaming it.""" | ||||
if name is None: | ||||
name = self.name | ||||
Brian Granger
|
r2077 | return ColorScheme(name, self.colors.dict()) | ||
Bernardo B. Marques
|
r4872 | |||
fperez
|
r46 | class ColorSchemeTable(dict): | ||
fperez
|
r0 | """General class to handle tables of color schemes. | ||
It's basically a dict of color schemes with a couple of shorthand | ||||
attributes and some convenient methods. | ||||
Bernardo B. Marques
|
r4872 | |||
fperez
|
r0 | active_scheme_name -> obvious | ||
active_colors -> actual color table of the active scheme""" | ||||
Matthias Bussonnier
|
r21778 | def __init__(self, scheme_list=None, default_scheme=''): | ||
fperez
|
r0 | """Create a table of color schemes. | ||
The table can be created empty and manually filled or it can be | ||||
created with a list of valid color schemes AND the specification for | ||||
the default active scheme. | ||||
""" | ||||
Bernardo B. Marques
|
r4872 | |||
fperez
|
r46 | # create object attributes to be set later | ||
self.active_scheme_name = '' | ||||
self.active_colors = None | ||||
Bernardo B. Marques
|
r4872 | |||
fperez
|
r46 | if scheme_list: | ||
fperez
|
r0 | if default_scheme == '': | ||
Bradley M. Froehle
|
r7843 | raise ValueError('you must specify the default color scheme') | ||
fperez
|
r0 | for scheme in scheme_list: | ||
self.add_scheme(scheme) | ||||
self.set_active_scheme(default_scheme) | ||||
fperez
|
r46 | def copy(self): | ||
"""Return full copy of object""" | ||||
return ColorSchemeTable(self.values(),self.active_scheme_name) | ||||
fperez
|
r0 | def add_scheme(self,new_scheme): | ||
"""Add a new color scheme to the table.""" | ||||
if not isinstance(new_scheme,ColorScheme): | ||||
Bradley M. Froehle
|
r7843 | raise ValueError('ColorSchemeTable only accepts ColorScheme instances') | ||
fperez
|
r0 | self[new_scheme.name] = new_scheme | ||
Bernardo B. Marques
|
r4872 | |||
fperez
|
r0 | def set_active_scheme(self,scheme,case_sensitive=0): | ||
"""Set the currently active scheme. | ||||
Names are by default compared in a case-insensitive way, but this can | ||||
be changed by setting the parameter case_sensitive to true.""" | ||||
Thomas Kluyver
|
r13358 | scheme_names = list(self.keys()) | ||
fperez
|
r0 | if case_sensitive: | ||
fperez
|
r46 | valid_schemes = scheme_names | ||
fperez
|
r0 | scheme_test = scheme | ||
else: | ||||
fperez
|
r46 | valid_schemes = [s.lower() for s in scheme_names] | ||
fperez
|
r0 | scheme_test = scheme.lower() | ||
try: | ||||
scheme_idx = valid_schemes.index(scheme_test) | ||||
Ram Rachum
|
r25833 | except ValueError as e: | ||
Bradley M. Froehle
|
r7843 | raise ValueError('Unrecognized color scheme: ' + scheme + \ | ||
Ram Rachum
|
r25833 | '\nValid schemes: '+str(scheme_names).replace("'', ",'')) from e | ||
fperez
|
r0 | else: | ||
fperez
|
r46 | active = scheme_names[scheme_idx] | ||
fperez
|
r0 | self.active_scheme_name = active | ||
self.active_colors = self[active].colors | ||||
# Now allow using '' as an index for the current active scheme | ||||
self[''] = self[active] | ||||