diff options
author | Devtools Arcadia <arcadia-devtools@yandex-team.ru> | 2022-02-07 18:08:42 +0300 |
---|---|---|
committer | Devtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net> | 2022-02-07 18:08:42 +0300 |
commit | 1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch) | |
tree | e26c9fed0de5d9873cce7e00bc214573dc2195b7 /contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/base.py | |
download | ydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz |
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/base.py')
-rw-r--r-- | contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/base.py | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/base.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/base.py new file mode 100644 index 00000000000..c1761f26402 --- /dev/null +++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/base.py @@ -0,0 +1,176 @@ +from typing import TYPE_CHECKING, Any, Callable, Iterable, List, Tuple, Union, cast + +from prompt_toolkit.mouse_events import MouseEvent + +if TYPE_CHECKING: + from typing_extensions import Protocol + +__all__ = [ + "OneStyleAndTextTuple", + "StyleAndTextTuples", + "MagicFormattedText", + "AnyFormattedText", + "to_formatted_text", + "is_formatted_text", + "Template", + "merge_formatted_text", + "FormattedText", +] + +OneStyleAndTextTuple = Union[ + Tuple[str, str], Tuple[str, str, Callable[[MouseEvent], None]] +] + +# List of (style, text) tuples. +StyleAndTextTuples = List[OneStyleAndTextTuple] + + +if TYPE_CHECKING: + from typing_extensions import TypeGuard + + class MagicFormattedText(Protocol): + """ + Any object that implements ``__pt_formatted_text__`` represents formatted + text. + """ + + def __pt_formatted_text__(self) -> StyleAndTextTuples: + ... + + +AnyFormattedText = Union[ + str, + "MagicFormattedText", + StyleAndTextTuples, + # Callable[[], 'AnyFormattedText'] # Recursive definition not supported by mypy. + Callable[[], Any], + None, +] + + +def to_formatted_text( + value: AnyFormattedText, style: str = "", auto_convert: bool = False +) -> "FormattedText": + """ + Convert the given value (which can be formatted text) into a list of text + fragments. (Which is the canonical form of formatted text.) The outcome is + always a `FormattedText` instance, which is a list of (style, text) tuples. + + It can take a plain text string, an `HTML` or `ANSI` object, anything that + implements `__pt_formatted_text__` or a callable that takes no arguments and + returns one of those. + + :param style: An additional style string which is applied to all text + fragments. + :param auto_convert: If `True`, also accept other types, and convert them + to a string first. + """ + result: Union[FormattedText, StyleAndTextTuples] + + if value is None: + result = [] + elif isinstance(value, str): + result = [("", value)] + elif isinstance(value, list): + result = value # StyleAndTextTuples + elif hasattr(value, "__pt_formatted_text__"): + result = cast("MagicFormattedText", value).__pt_formatted_text__() + elif callable(value): + return to_formatted_text(value(), style=style) + elif auto_convert: + result = [("", "{}".format(value))] + else: + raise ValueError( + "No formatted text. Expecting a unicode object, " + "HTML, ANSI or a FormattedText instance. Got %r" % (value,) + ) + + # Apply extra style. + if style: + result = cast( + StyleAndTextTuples, + [(style + " " + item_style, *rest) for item_style, *rest in result], + ) + + # Make sure the result is wrapped in a `FormattedText`. Among other + # reasons, this is important for `print_formatted_text` to work correctly + # and distinguish between lists and formatted text. + if isinstance(result, FormattedText): + return result + else: + return FormattedText(result) + + +def is_formatted_text(value: object) -> "TypeGuard[AnyFormattedText]": + """ + Check whether the input is valid formatted text (for use in assert + statements). + In case of a callable, it doesn't check the return type. + """ + if callable(value): + return True + if isinstance(value, (str, list)): + return True + if hasattr(value, "__pt_formatted_text__"): + return True + return False + + +class FormattedText(StyleAndTextTuples): + """ + A list of ``(style, text)`` tuples. + + (In some situations, this can also be ``(style, text, mouse_handler)`` + tuples.) + """ + + def __pt_formatted_text__(self) -> StyleAndTextTuples: + return self + + def __repr__(self) -> str: + return "FormattedText(%s)" % super().__repr__() + + +class Template: + """ + Template for string interpolation with formatted text. + + Example:: + + Template(' ... {} ... ').format(HTML(...)) + + :param text: Plain text. + """ + + def __init__(self, text: str) -> None: + assert "{0}" not in text + self.text = text + + def format(self, *values: AnyFormattedText) -> AnyFormattedText: + def get_result() -> AnyFormattedText: + # Split the template in parts. + parts = self.text.split("{}") + assert len(parts) - 1 == len(values) + + result = FormattedText() + for part, val in zip(parts, values): + result.append(("", part)) + result.extend(to_formatted_text(val)) + result.append(("", parts[-1])) + return result + + return get_result + + +def merge_formatted_text(items: Iterable[AnyFormattedText]) -> AnyFormattedText: + """ + Merge (Concatenate) several pieces of formatted text together. + """ + + def _merge_formatted_text() -> AnyFormattedText: + result = FormattedText() + for i in items: + result.extend(to_formatted_text(i)) + return result + + return _merge_formatted_text |