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