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
|
from typing import TYPE_CHECKING, Iterable, List, TypeVar, Union, cast, overload
from prompt_toolkit.formatted_text.base import OneStyleAndTextTuple
if TYPE_CHECKING:
from typing_extensions import SupportsIndex
__all__ = [
"explode_text_fragments",
]
_T = TypeVar("_T", bound=OneStyleAndTextTuple)
class _ExplodedList(List[_T]):
"""
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.
"""
exploded = True
def append(self, item: _T) -> None:
self.extend([item])
def extend(self, lst: Iterable[_T]) -> None:
super().extend(explode_text_fragments(lst))
def insert(self, index: "SupportsIndex", item: _T) -> None:
raise NotImplementedError # TODO
# TODO: When creating a copy() or [:], return also an _ExplodedList.
@overload
def __setitem__(self, index: "SupportsIndex", value: _T) -> None:
...
@overload
def __setitem__(self, index: slice, value: Iterable[_T]) -> None:
...
def __setitem__(
self, index: Union["SupportsIndex", slice], value: Union[_T, Iterable[_T]]
) -> None:
"""
Ensure that when `(style_str, 'long string')` is set, the string will be
exploded.
"""
if not isinstance(index, slice):
int_index = index.__index__()
index = slice(int_index, int_index + 1)
if isinstance(value, tuple): # In case of `OneStyleAndTextTuple`.
value = cast("List[_T]", [value])
super().__setitem__(index, explode_text_fragments(cast("Iterable[_T]", value)))
def explode_text_fragments(fragments: Iterable[_T]) -> _ExplodedList[_T]:
"""
Turn a list of (style_str, 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 fragments: List of (style, text) tuples.
"""
# When the fragments is already exploded, don't explode again.
if isinstance(fragments, _ExplodedList):
return fragments
result: List[_T] = []
for style, string, *rest in fragments: # type: ignore
for c in string: # type: ignore
result.append((style, c, *rest)) # type: ignore
return _ExplodedList(result)
|