base.py 4.9 KB

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