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