1from __future__ import annotations 
    2 
    3from typing import TYPE_CHECKING, Iterable, List, TypeVar, cast, overload 
    4 
    5from prompt_toolkit.formatted_text.base import OneStyleAndTextTuple 
    6 
    7if TYPE_CHECKING: 
    8    from typing_extensions import SupportsIndex 
    9 
    10__all__ = [ 
    11    "explode_text_fragments", 
    12] 
    13 
    14_T = TypeVar("_T", bound=OneStyleAndTextTuple) 
    15 
    16 
    17class _ExplodedList(List[_T]): 
    18    """ 
    19    Wrapper around a list, that marks it as 'exploded'. 
    20 
    21    As soon as items are added or the list is extended, the new items are 
    22    automatically exploded as well. 
    23    """ 
    24 
    25    exploded = True 
    26 
    27    def append(self, item: _T) -> None: 
    28        self.extend([item]) 
    29 
    30    def extend(self, lst: Iterable[_T]) -> None: 
    31        super().extend(explode_text_fragments(lst)) 
    32 
    33    def insert(self, index: SupportsIndex, item: _T) -> None: 
    34        raise NotImplementedError  # TODO 
    35 
    36    # TODO: When creating a copy() or [:], return also an _ExplodedList. 
    37 
    38    @overload 
    39    def __setitem__(self, index: SupportsIndex, value: _T) -> None: ... 
    40 
    41    @overload 
    42    def __setitem__(self, index: slice, value: Iterable[_T]) -> None: ... 
    43 
    44    def __setitem__( 
    45        self, index: SupportsIndex | slice, value: _T | Iterable[_T] 
    46    ) -> None: 
    47        """ 
    48        Ensure that when `(style_str, 'long string')` is set, the string will be 
    49        exploded. 
    50        """ 
    51        if not isinstance(index, slice): 
    52            int_index = index.__index__() 
    53            index = slice(int_index, int_index + 1) 
    54        if isinstance(value, tuple):  # In case of `OneStyleAndTextTuple`. 
    55            value = cast("List[_T]", [value]) 
    56 
    57        super().__setitem__(index, explode_text_fragments(value)) 
    58 
    59 
    60def explode_text_fragments(fragments: Iterable[_T]) -> _ExplodedList[_T]: 
    61    """ 
    62    Turn a list of (style_str, text) tuples into another list where each string is 
    63    exactly one character. 
    64 
    65    It should be fine to call this function several times. Calling this on a 
    66    list that is already exploded, is a null operation. 
    67 
    68    :param fragments: List of (style, text) tuples. 
    69    """ 
    70    # When the fragments is already exploded, don't explode again. 
    71    if isinstance(fragments, _ExplodedList): 
    72        return fragments 
    73 
    74    result: list[_T] = [] 
    75 
    76    for style, string, *rest in fragments: 
    77        for c in string: 
    78            result.append((style, c, *rest))  # type: ignore 
    79 
    80    return _ExplodedList(result)