1""" 2The base classes for the styling. 3""" 4from __future__ import absolute_import, unicode_literals 5 6from abc import ABCMeta, abstractmethod, abstractproperty 7from collections import namedtuple 8 9from six import with_metaclass 10 11__all__ = [ 12 'Attrs', 13 'DEFAULT_ATTRS', 14 'ANSI_COLOR_NAMES', 15 'ANSI_COLOR_NAMES_ALIASES', 16 'BaseStyle', 17 'DummyStyle', 18 'DynamicStyle', 19] 20 21 22#: Style attributes. 23Attrs = namedtuple('Attrs', 'color bgcolor bold underline italic blink reverse hidden') 24""" 25:param color: Hexadecimal string. E.g. '000000' or Ansi color name: e.g. 'ansiblue' 26:param bgcolor: Hexadecimal string. E.g. 'ffffff' or Ansi color name: e.g. 'ansired' 27:param bold: Boolean 28:param underline: Boolean 29:param italic: Boolean 30:param blink: Boolean 31:param reverse: Boolean 32:param hidden: Boolean 33""" 34 35#: The default `Attrs`. 36DEFAULT_ATTRS = Attrs(color='', bgcolor='', bold=False, underline=False, 37 italic=False, blink=False, reverse=False, hidden=False) 38 39 40#: ``Attrs.bgcolor/fgcolor`` can be in either 'ffffff' format, or can be any of 41#: the following in case we want to take colors from the 8/16 color palette. 42#: Usually, in that case, the terminal application allows to configure the RGB 43#: values for these names. 44#: ISO 6429 colors 45ANSI_COLOR_NAMES = [ 46 'ansidefault', 47 48 # Low intensity, dark. (One or two components 0x80, the other 0x00.) 49 'ansiblack', 'ansired', 'ansigreen', 'ansiyellow', 'ansiblue', 50 'ansimagenta', 'ansicyan', 'ansigray', 51 52 # High intensity, bright. (One or two components 0xff, the other 0x00. Not supported everywhere.) 53 'ansibrightblack', 'ansibrightred', 'ansibrightgreen', 'ansibrightyellow', 54 'ansibrightblue', 'ansibrightmagenta', 'ansibrightcyan', 'ansiwhite', 55] 56 57 58# People don't use the same ANSI color names everywhere. In prompt_toolkit 1.0 59# we used some unconvential names (which were contributed like that to 60# Pygments). This is fixed now, but we still support the old names. 61 62# The table below maps the old aliases to the current names. 63ANSI_COLOR_NAMES_ALIASES = { 64 'ansidarkgray': 'ansibrightblack', 65 'ansiteal': 'ansicyan', 66 'ansiturquoise': 'ansibrightcyan', 67 'ansibrown': 'ansiyellow', 68 'ansipurple': 'ansimagenta', 69 'ansifuchsia': 'ansibrightmagenta', 70 'ansilightgray': 'ansigray', 71 'ansidarkred': 'ansired', 72 'ansidarkgreen': 'ansigreen', 73 'ansidarkblue': 'ansiblue', 74} 75assert set(ANSI_COLOR_NAMES_ALIASES.values()).issubset(set(ANSI_COLOR_NAMES)) 76assert not (set(ANSI_COLOR_NAMES_ALIASES.keys()) & set(ANSI_COLOR_NAMES)) 77 78 79class BaseStyle(with_metaclass(ABCMeta, object)): 80 """ 81 Abstract base class for prompt_toolkit styles. 82 """ 83 @abstractmethod 84 def get_attrs_for_style_str(self, style_str, default=DEFAULT_ATTRS): 85 """ 86 Return :class:`.Attrs` for the given style string. 87 88 :param style_str: The style string. This can contain inline styling as 89 well as classnames (e.g. "class:title"). 90 :param default: `Attrs` to be used if no styling was defined. 91 """ 92 93 @abstractproperty 94 def style_rules(self): 95 """ 96 The list of style rules, used to create this style. 97 (Required for `DynamicStyle` and `_MergedStyle` to work.) 98 """ 99 return [] 100 101 @abstractmethod 102 def invalidation_hash(self): 103 """ 104 Invalidation hash for the style. When this changes over time, the 105 renderer knows that something in the style changed, and that everything 106 has to be redrawn. 107 """ 108 109 110class DummyStyle(BaseStyle): 111 """ 112 A style that doesn't style anything. 113 """ 114 def get_attrs_for_style_str(self, style_str, default=DEFAULT_ATTRS): 115 return default 116 117 def invalidation_hash(self): 118 return 1 # Always the same value. 119 120 @property 121 def style_rules(self): 122 return [] 123 124 125class DynamicStyle(BaseStyle): 126 """ 127 Style class that can dynamically returns an other Style. 128 129 :param get_style: Callable that returns a :class:`.Style` instance. 130 """ 131 def __init__(self, get_style): 132 self.get_style = get_style 133 self._dummy = DummyStyle() 134 135 def get_attrs_for_style_str(self, style_str, default=DEFAULT_ATTRS): 136 style = self.get_style() or self._dummy 137 138 assert isinstance(style, BaseStyle) 139 return style.get_attrs_for_style_str(style_str, default) 140 141 def invalidation_hash(self): 142 return (self.get_style() or self._dummy).invalidation_hash() 143 144 @property 145 def style_rules(self): 146 return (self.get_style() or self._dummy).style_rules 147