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