from_dict.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. """
  2. Tool for creating styles from a dictionary.
  3. This is very similar to the Pygments style dictionary, with some additions:
  4. - Support for reverse and blink.
  5. - Support for ANSI color names. (These will map directly to the 16 terminal
  6. colors.)
  7. """
  8. try:
  9. from collections.abc import Mapping
  10. except ImportError:
  11. from collections import Mapping
  12. from .base import Style, DEFAULT_ATTRS, ANSI_COLOR_NAMES
  13. from .defaults import DEFAULT_STYLE_EXTENSIONS
  14. from .utils import merge_attrs, split_token_in_parts
  15. from six.moves import range
  16. __all__ = (
  17. 'style_from_dict',
  18. )
  19. def _colorformat(text):
  20. """
  21. Parse/validate color format.
  22. Like in Pygments, but also support the ANSI color names.
  23. (These will map to the colors of the 16 color palette.)
  24. """
  25. if text[0:1] == '#':
  26. col = text[1:]
  27. if col in ANSI_COLOR_NAMES:
  28. return col
  29. elif len(col) == 6:
  30. return col
  31. elif len(col) == 3:
  32. return col[0]*2 + col[1]*2 + col[2]*2
  33. elif text == '':
  34. return text
  35. raise ValueError('Wrong color format %r' % text)
  36. def style_from_dict(style_dict, include_defaults=True):
  37. """
  38. Create a ``Style`` instance from a dictionary or other mapping.
  39. The dictionary is equivalent to the ``Style.styles`` dictionary from
  40. pygments, with a few additions: it supports 'reverse' and 'blink'.
  41. Usage::
  42. style_from_dict({
  43. Token: '#ff0000 bold underline',
  44. Token.Title: 'blink',
  45. Token.SomethingElse: 'reverse',
  46. })
  47. :param include_defaults: Include the defaults (built-in) styling for
  48. selected text, etc...)
  49. """
  50. assert isinstance(style_dict, Mapping)
  51. if include_defaults:
  52. s2 = {}
  53. s2.update(DEFAULT_STYLE_EXTENSIONS)
  54. s2.update(style_dict)
  55. style_dict = s2
  56. # Expand token inheritance and turn style description into Attrs.
  57. token_to_attrs = {}
  58. # (Loop through the tokens in order. Sorting makes sure that
  59. # we process the parent first.)
  60. for ttype, styledef in sorted(style_dict.items()):
  61. # Start from parent Attrs or default Attrs.
  62. attrs = DEFAULT_ATTRS
  63. if 'noinherit' not in styledef:
  64. for i in range(1, len(ttype) + 1):
  65. try:
  66. attrs = token_to_attrs[ttype[:-i]]
  67. except KeyError:
  68. pass
  69. else:
  70. break
  71. # Now update with the given attributes.
  72. for part in styledef.split():
  73. if part == 'noinherit':
  74. pass
  75. elif part == 'bold':
  76. attrs = attrs._replace(bold=True)
  77. elif part == 'nobold':
  78. attrs = attrs._replace(bold=False)
  79. elif part == 'italic':
  80. attrs = attrs._replace(italic=True)
  81. elif part == 'noitalic':
  82. attrs = attrs._replace(italic=False)
  83. elif part == 'underline':
  84. attrs = attrs._replace(underline=True)
  85. elif part == 'nounderline':
  86. attrs = attrs._replace(underline=False)
  87. # prompt_toolkit extensions. Not in Pygments.
  88. elif part == 'blink':
  89. attrs = attrs._replace(blink=True)
  90. elif part == 'noblink':
  91. attrs = attrs._replace(blink=False)
  92. elif part == 'reverse':
  93. attrs = attrs._replace(reverse=True)
  94. elif part == 'noreverse':
  95. attrs = attrs._replace(reverse=False)
  96. # Pygments properties that we ignore.
  97. elif part in ('roman', 'sans', 'mono'):
  98. pass
  99. elif part.startswith('border:'):
  100. pass
  101. # Colors.
  102. elif part.startswith('bg:'):
  103. attrs = attrs._replace(bgcolor=_colorformat(part[3:]))
  104. else:
  105. attrs = attrs._replace(color=_colorformat(part))
  106. token_to_attrs[ttype] = attrs
  107. return _StyleFromDict(token_to_attrs)
  108. class _StyleFromDict(Style):
  109. """
  110. Turn a dictionary that maps `Token` to `Attrs` into a style class.
  111. :param token_to_attrs: Dictionary that maps `Token` to `Attrs`.
  112. """
  113. def __init__(self, token_to_attrs):
  114. self.token_to_attrs = token_to_attrs
  115. def get_attrs_for_token(self, token):
  116. # Split Token.
  117. list_of_attrs = []
  118. for token in split_token_in_parts(token):
  119. list_of_attrs.append(self.token_to_attrs.get(token, DEFAULT_ATTRS))
  120. return merge_attrs(list_of_attrs)
  121. def invalidation_hash(self):
  122. return id(self.token_to_attrs)