You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			81 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			81 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			Python
		
	
from __future__ import annotations
 | 
						|
 | 
						|
from typing import TYPE_CHECKING, Iterable, List, TypeVar, 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: SupportsIndex | slice, value: _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(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:
 | 
						|
        for c in string:
 | 
						|
            result.append((style, c, *rest))  # type: ignore
 | 
						|
 | 
						|
    return _ExplodedList(result)
 |