utils.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. from __future__ import unicode_literals
  2. from prompt_toolkit.utils import get_cwidth
  3. from prompt_toolkit.token import Token
  4. __all__ = (
  5. 'token_list_len',
  6. 'token_list_width',
  7. 'token_list_to_text',
  8. 'explode_tokens',
  9. 'split_lines',
  10. 'find_window_for_buffer_name',
  11. )
  12. def token_list_len(tokenlist):
  13. """
  14. Return the amount of characters in this token list.
  15. :param tokenlist: List of (token, text) or (token, text, mouse_handler)
  16. tuples.
  17. """
  18. ZeroWidthEscape = Token.ZeroWidthEscape
  19. return sum(len(item[1]) for item in tokenlist if item[0] != ZeroWidthEscape)
  20. def token_list_width(tokenlist):
  21. """
  22. Return the character width of this token list.
  23. (Take double width characters into account.)
  24. :param tokenlist: List of (token, text) or (token, text, mouse_handler)
  25. tuples.
  26. """
  27. ZeroWidthEscape = Token.ZeroWidthEscape
  28. return sum(get_cwidth(c) for item in tokenlist for c in item[1] if item[0] != ZeroWidthEscape)
  29. def token_list_to_text(tokenlist):
  30. """
  31. Concatenate all the text parts again.
  32. """
  33. ZeroWidthEscape = Token.ZeroWidthEscape
  34. return ''.join(item[1] for item in tokenlist if item[0] != ZeroWidthEscape)
  35. def iter_token_lines(tokenlist):
  36. """
  37. Iterator that yields tokenlists for each line.
  38. """
  39. line = []
  40. for token, c in explode_tokens(tokenlist):
  41. line.append((token, c))
  42. if c == '\n':
  43. yield line
  44. line = []
  45. yield line
  46. def split_lines(tokenlist):
  47. """
  48. Take a single list of (Token, text) tuples and yield one such list for each
  49. line. Just like str.split, this will yield at least one item.
  50. :param tokenlist: List of (token, text) or (token, text, mouse_handler)
  51. tuples.
  52. """
  53. line = []
  54. for item in tokenlist:
  55. # For (token, text) tuples.
  56. if len(item) == 2:
  57. token, string = item
  58. parts = string.split('\n')
  59. for part in parts[:-1]:
  60. if part:
  61. line.append((token, part))
  62. yield line
  63. line = []
  64. line.append((token, parts[-1]))
  65. # Note that parts[-1] can be empty, and that's fine. It happens
  66. # in the case of [(Token.SetCursorPosition, '')].
  67. # For (token, text, mouse_handler) tuples.
  68. # I know, partly copy/paste, but understandable and more efficient
  69. # than many tests.
  70. else:
  71. token, string, mouse_handler = item
  72. parts = string.split('\n')
  73. for part in parts[:-1]:
  74. if part:
  75. line.append((token, part, mouse_handler))
  76. yield line
  77. line = []
  78. line.append((token, parts[-1], mouse_handler))
  79. # Always yield the last line, even when this is an empty line. This ensures
  80. # that when `tokenlist` ends with a newline character, an additional empty
  81. # line is yielded. (Otherwise, there's no way to differentiate between the
  82. # cases where `tokenlist` does and doesn't end with a newline.)
  83. yield line
  84. class _ExplodedList(list):
  85. """
  86. Wrapper around a list, that marks it as 'exploded'.
  87. As soon as items are added or the list is extended, the new items are
  88. automatically exploded as well.
  89. """
  90. def __init__(self, *a, **kw):
  91. super(_ExplodedList, self).__init__(*a, **kw)
  92. self.exploded = True
  93. def append(self, item):
  94. self.extend([item])
  95. def extend(self, lst):
  96. super(_ExplodedList, self).extend(explode_tokens(lst))
  97. def insert(self, index, item):
  98. raise NotImplementedError # TODO
  99. # TODO: When creating a copy() or [:], return also an _ExplodedList.
  100. def __setitem__(self, index, value):
  101. """
  102. Ensure that when `(Token, 'long string')` is set, the string will be
  103. exploded.
  104. """
  105. if not isinstance(index, slice):
  106. index = slice(index, index + 1)
  107. value = explode_tokens([value])
  108. super(_ExplodedList, self).__setitem__(index, value)
  109. def explode_tokens(tokenlist):
  110. """
  111. Turn a list of (token, text) tuples into another list where each string is
  112. exactly one character.
  113. It should be fine to call this function several times. Calling this on a
  114. list that is already exploded, is a null operation.
  115. :param tokenlist: List of (token, text) tuples.
  116. """
  117. # When the tokenlist is already exploded, don't explode again.
  118. if getattr(tokenlist, 'exploded', False):
  119. return tokenlist
  120. result = []
  121. for token, string in tokenlist:
  122. for c in string:
  123. result.append((token, c))
  124. return _ExplodedList(result)
  125. def find_window_for_buffer_name(cli, buffer_name):
  126. """
  127. Look for a :class:`~prompt_toolkit.layout.containers.Window` in the Layout
  128. that contains the :class:`~prompt_toolkit.layout.controls.BufferControl`
  129. for the given buffer and return it. If no such Window is found, return None.
  130. """
  131. from prompt_toolkit.interface import CommandLineInterface
  132. assert isinstance(cli, CommandLineInterface)
  133. from .containers import Window
  134. from .controls import BufferControl
  135. for l in cli.layout.walk(cli):
  136. if isinstance(l, Window) and isinstance(l.content, BufferControl):
  137. if l.content.buffer_name == buffer_name:
  138. return l