diff options
| author | shadchin <[email protected]> | 2022-02-10 16:44:30 +0300 | 
|---|---|---|
| committer | Daniil Cherednik <[email protected]> | 2022-02-10 16:44:30 +0300 | 
| commit | 2598ef1d0aee359b4b6d5fdd1758916d5907d04f (patch) | |
| tree | 012bb94d777798f1f56ac1cec429509766d05181 /contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text | |
| parent | 6751af0b0c1b952fede40b19b71da8025b5d8bcf (diff) | |
Restoring authorship annotation for <[email protected]>. Commit 1 of 2.
Diffstat (limited to 'contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text')
6 files changed, 733 insertions, 733 deletions
diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/__init__.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/__init__.py index f0c92c96f94..7272539fcc9 100644 --- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/__init__.py +++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/__init__.py @@ -1,54 +1,54 @@ -""" -Many places in prompt_toolkit can take either plain text, or formatted text. -For instance the :func:`~prompt_toolkit.shortcuts.prompt` function takes either -plain text or formatted text for the prompt. The -:class:`~prompt_toolkit.layout.FormattedTextControl` can also take either plain -text or formatted text. - -In any case, there is an input that can either be just plain text (a string), -an :class:`.HTML` object, an :class:`.ANSI` object or a sequence of -`(style_string, text)` tuples. The :func:`.to_formatted_text` conversion -function takes any of these and turns all of them into such a tuple sequence. -""" -from .ansi import ANSI -from .base import ( -    AnyFormattedText, -    FormattedText, -    StyleAndTextTuples, -    Template, -    is_formatted_text, -    merge_formatted_text, -    to_formatted_text, -) -from .html import HTML -from .pygments import PygmentsTokens -from .utils import ( -    fragment_list_len, -    fragment_list_to_text, -    fragment_list_width, -    split_lines, +"""  +Many places in prompt_toolkit can take either plain text, or formatted text.  +For instance the :func:`~prompt_toolkit.shortcuts.prompt` function takes either  +plain text or formatted text for the prompt. The  +:class:`~prompt_toolkit.layout.FormattedTextControl` can also take either plain  +text or formatted text.  +  +In any case, there is an input that can either be just plain text (a string),  +an :class:`.HTML` object, an :class:`.ANSI` object or a sequence of  +`(style_string, text)` tuples. The :func:`.to_formatted_text` conversion  +function takes any of these and turns all of them into such a tuple sequence.  +"""  +from .ansi import ANSI  +from .base import (  +    AnyFormattedText,  +    FormattedText,  +    StyleAndTextTuples,  +    Template,  +    is_formatted_text,  +    merge_formatted_text,  +    to_formatted_text,  +)  +from .html import HTML  +from .pygments import PygmentsTokens  +from .utils import (  +    fragment_list_len,  +    fragment_list_to_text,  +    fragment_list_width,  +    split_lines,       to_plain_text, -) - -__all__ = [ -    # Base. -    "AnyFormattedText", -    "to_formatted_text", -    "is_formatted_text", -    "Template", -    "merge_formatted_text", -    "FormattedText", -    "StyleAndTextTuples", -    # HTML. -    "HTML", -    # ANSI. -    "ANSI", -    # Pygments. -    "PygmentsTokens", -    # Utils. -    "fragment_list_len", -    "fragment_list_width", -    "fragment_list_to_text", -    "split_lines", +)  +  +__all__ = [  +    # Base.  +    "AnyFormattedText",  +    "to_formatted_text",  +    "is_formatted_text",  +    "Template",  +    "merge_formatted_text",  +    "FormattedText",  +    "StyleAndTextTuples",  +    # HTML.  +    "HTML",  +    # ANSI.  +    "ANSI",  +    # Pygments.  +    "PygmentsTokens",  +    # Utils.  +    "fragment_list_len",  +    "fragment_list_width",  +    "fragment_list_to_text",  +    "split_lines",       "to_plain_text", -] +]  diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/ansi.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/ansi.py index 3d570633570..2d8c5dac95e 100644 --- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/ansi.py +++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/ansi.py @@ -1,117 +1,117 @@ -from typing import Generator, List, Optional - -from prompt_toolkit.output.vt100 import BG_ANSI_COLORS, FG_ANSI_COLORS -from prompt_toolkit.output.vt100 import _256_colors as _256_colors_table - -from .base import StyleAndTextTuples - -__all__ = [ -    "ANSI", -    "ansi_escape", -] - - -class ANSI: -    """ -    ANSI formatted text. -    Take something ANSI escaped text, for use as a formatted string. E.g. - -    :: - -        ANSI('\\x1b[31mhello \\x1b[32mworld') - -    Characters between ``\\001`` and ``\\002`` are supposed to have a zero width -    when printed, but these are literally sent to the terminal output. This can -    be used for instance, for inserting Final Term prompt commands.  They will -    be translated into a prompt_toolkit '[ZeroWidthEscape]' fragment. -    """ - -    def __init__(self, value: str) -> None: -        self.value = value -        self._formatted_text: StyleAndTextTuples = [] - -        # Default style attributes. -        self._color: Optional[str] = None -        self._bgcolor: Optional[str] = None -        self._bold = False -        self._underline = False -        self._strike = False -        self._italic = False -        self._blink = False -        self._reverse = False -        self._hidden = False - -        # Process received text. -        parser = self._parse_corot() -        parser.send(None)  # type: ignore -        for c in value: -            parser.send(c) - -    def _parse_corot(self) -> Generator[None, str, None]: -        """ -        Coroutine that parses the ANSI escape sequences. -        """ -        style = "" -        formatted_text = self._formatted_text - -        while True: +from typing import Generator, List, Optional  +  +from prompt_toolkit.output.vt100 import BG_ANSI_COLORS, FG_ANSI_COLORS  +from prompt_toolkit.output.vt100 import _256_colors as _256_colors_table  +  +from .base import StyleAndTextTuples  +  +__all__ = [  +    "ANSI",  +    "ansi_escape",  +]  +  +  +class ANSI:  +    """  +    ANSI formatted text.  +    Take something ANSI escaped text, for use as a formatted string. E.g.  +  +    ::  +  +        ANSI('\\x1b[31mhello \\x1b[32mworld')  +  +    Characters between ``\\001`` and ``\\002`` are supposed to have a zero width  +    when printed, but these are literally sent to the terminal output. This can  +    be used for instance, for inserting Final Term prompt commands.  They will  +    be translated into a prompt_toolkit '[ZeroWidthEscape]' fragment.  +    """  +  +    def __init__(self, value: str) -> None:  +        self.value = value  +        self._formatted_text: StyleAndTextTuples = []  +  +        # Default style attributes.  +        self._color: Optional[str] = None  +        self._bgcolor: Optional[str] = None  +        self._bold = False  +        self._underline = False  +        self._strike = False  +        self._italic = False  +        self._blink = False  +        self._reverse = False  +        self._hidden = False  +  +        # Process received text.  +        parser = self._parse_corot()  +        parser.send(None)  # type: ignore  +        for c in value:  +            parser.send(c)  +  +    def _parse_corot(self) -> Generator[None, str, None]:  +        """  +        Coroutine that parses the ANSI escape sequences.  +        """  +        style = ""  +        formatted_text = self._formatted_text  +  +        while True:               # NOTE: CSI is a special token within a stream of characters that              #       introduces an ANSI control sequence used to set the              #       style attributes of the following characters. -            csi = False - -            c = yield - -            # Everything between \001 and \002 should become a ZeroWidthEscape. -            if c == "\001": -                escaped_text = "" -                while c != "\002": -                    c = yield -                    if c == "\002": -                        formatted_text.append(("[ZeroWidthEscape]", escaped_text)) -                        c = yield -                        break -                    else: -                        escaped_text += c - +            csi = False  + +            c = yield  +  +            # Everything between \001 and \002 should become a ZeroWidthEscape.  +            if c == "\001":  +                escaped_text = ""  +                while c != "\002":  +                    c = yield  +                    if c == "\002":  +                        formatted_text.append(("[ZeroWidthEscape]", escaped_text))  +                        c = yield  +                        break  +                    else:  +                        escaped_text += c  +               # Check for CSI -            if c == "\x1b": -                # Start of color escape sequence. -                square_bracket = yield -                if square_bracket == "[": -                    csi = True -                else: -                    continue -            elif c == "\x9b": -                csi = True - -            if csi: -                # Got a CSI sequence. Color codes are following. -                current = "" -                params = [] - -                while True: -                    char = yield +            if c == "\x1b":  +                # Start of color escape sequence.  +                square_bracket = yield  +                if square_bracket == "[":  +                    csi = True  +                else:  +                    continue  +            elif c == "\x9b":  +                csi = True  +  +            if csi:  +                # Got a CSI sequence. Color codes are following.  +                current = ""  +                params = []  + +                while True:  +                    char = yield                       # Construct number -                    if char.isdigit(): -                        current += char +                    if char.isdigit():  +                        current += char                       # Eval number -                    else: +                    else:                           # Limit and save number value -                        params.append(min(int(current or 0), 9999)) +                        params.append(min(int(current or 0), 9999))                           # Get delimiter token if present -                        if char == ";": -                            current = "" +                        if char == ";":  +                            current = ""                           # Check and evaluate color codes -                        elif char == "m": -                            # Set attributes and token. -                            self._select_graphic_rendition(params) -                            style = self._create_style_string() -                            break +                        elif char == "m":  +                            # Set attributes and token.  +                            self._select_graphic_rendition(params)  +                            style = self._create_style_string()  +                            break                           # Check and evaluate cursor forward                          elif char == "C": @@ -120,163 +120,163 @@ class ANSI:                                  formatted_text.append((style, " "))                              break -                        else: -                            # Ignore unsupported sequence. -                            break -            else: -                # Add current character. -                # NOTE: At this point, we could merge the current character -                #       into the previous tuple if the style did not change, -                #       however, it's not worth the effort given that it will -                #       be "Exploded" once again when it's rendered to the -                #       output. -                formatted_text.append((style, c)) - -    def _select_graphic_rendition(self, attrs: List[int]) -> None: -        """ -        Taken a list of graphics attributes and apply changes. -        """ -        if not attrs: -            attrs = [0] -        else: -            attrs = list(attrs[::-1]) - -        while attrs: -            attr = attrs.pop() - -            if attr in _fg_colors: -                self._color = _fg_colors[attr] -            elif attr in _bg_colors: -                self._bgcolor = _bg_colors[attr] -            elif attr == 1: -                self._bold = True +                        else:  +                            # Ignore unsupported sequence.  +                            break  +            else:  +                # Add current character.  +                # NOTE: At this point, we could merge the current character  +                #       into the previous tuple if the style did not change,  +                #       however, it's not worth the effort given that it will  +                #       be "Exploded" once again when it's rendered to the  +                #       output.  +                formatted_text.append((style, c))  +  +    def _select_graphic_rendition(self, attrs: List[int]) -> None:  +        """  +        Taken a list of graphics attributes and apply changes.  +        """  +        if not attrs:  +            attrs = [0]  +        else:  +            attrs = list(attrs[::-1])  +  +        while attrs:  +            attr = attrs.pop()  +  +            if attr in _fg_colors:  +                self._color = _fg_colors[attr]  +            elif attr in _bg_colors:  +                self._bgcolor = _bg_colors[attr]  +            elif attr == 1:  +                self._bold = True               # elif attr == 2:              #   self._faint = True -            elif attr == 3: -                self._italic = True -            elif attr == 4: -                self._underline = True -            elif attr == 5: +            elif attr == 3:  +                self._italic = True  +            elif attr == 4:  +                self._underline = True  +            elif attr == 5:                   self._blink = True  # Slow blink -            elif attr == 6: +            elif attr == 6:                   self._blink = True  # Fast blink -            elif attr == 7: -                self._reverse = True -            elif attr == 8: -                self._hidden = True -            elif attr == 9: -                self._strike = True -            elif attr == 22: +            elif attr == 7:  +                self._reverse = True  +            elif attr == 8:  +                self._hidden = True  +            elif attr == 9:  +                self._strike = True  +            elif attr == 22:                   self._bold = False  # Normal intensity -            elif attr == 23: -                self._italic = False -            elif attr == 24: -                self._underline = False -            elif attr == 25: -                self._blink = False -            elif attr == 27: -                self._reverse = False +            elif attr == 23:  +                self._italic = False  +            elif attr == 24:  +                self._underline = False  +            elif attr == 25:  +                self._blink = False  +            elif attr == 27:  +                self._reverse = False               elif attr == 28:                  self._hidden = False -            elif attr == 29: -                self._strike = False -            elif not attr: +            elif attr == 29:  +                self._strike = False  +            elif not attr:                   # Reset all style attributes -                self._color = None -                self._bgcolor = None -                self._bold = False -                self._underline = False -                self._strike = False -                self._italic = False -                self._blink = False -                self._reverse = False -                self._hidden = False - -            elif attr in (38, 48) and len(attrs) > 1: -                n = attrs.pop() - -                # 256 colors. -                if n == 5 and len(attrs) >= 1: -                    if attr == 38: -                        m = attrs.pop() -                        self._color = _256_colors.get(m) -                    elif attr == 48: -                        m = attrs.pop() -                        self._bgcolor = _256_colors.get(m) - -                # True colors. -                if n == 2 and len(attrs) >= 3: -                    try: -                        color_str = "#%02x%02x%02x" % ( -                            attrs.pop(), -                            attrs.pop(), -                            attrs.pop(), -                        ) -                    except IndexError: -                        pass -                    else: -                        if attr == 38: -                            self._color = color_str -                        elif attr == 48: -                            self._bgcolor = color_str - -    def _create_style_string(self) -> str: -        """ -        Turn current style flags into a string for usage in a formatted text. -        """ -        result = [] -        if self._color: -            result.append(self._color) -        if self._bgcolor: -            result.append("bg:" + self._bgcolor) -        if self._bold: -            result.append("bold") -        if self._underline: -            result.append("underline") -        if self._strike: -            result.append("strike") -        if self._italic: -            result.append("italic") -        if self._blink: -            result.append("blink") -        if self._reverse: -            result.append("reverse") -        if self._hidden: -            result.append("hidden") - -        return " ".join(result) - -    def __repr__(self) -> str: -        return "ANSI(%r)" % (self.value,) - -    def __pt_formatted_text__(self) -> StyleAndTextTuples: -        return self._formatted_text - -    def format(self, *args: str, **kwargs: str) -> "ANSI": -        """ -        Like `str.format`, but make sure that the arguments are properly -        escaped. (No ANSI escapes can be injected.) -        """ -        # Escape all the arguments. -        args = tuple(ansi_escape(a) for a in args) -        kwargs = {k: ansi_escape(v) for k, v in kwargs.items()} - -        return ANSI(self.value.format(*args, **kwargs)) - - -# Mapping of the ANSI color codes to their names. -_fg_colors = {v: k for k, v in FG_ANSI_COLORS.items()} -_bg_colors = {v: k for k, v in BG_ANSI_COLORS.items()} - -# Mapping of the escape codes for 256colors to their 'ffffff' value. -_256_colors = {} - -for i, (r, g, b) in enumerate(_256_colors_table.colors): -    _256_colors[i] = "#%02x%02x%02x" % (r, g, b) - - -def ansi_escape(text: str) -> str: -    """ -    Replace characters with a special meaning. -    """ -    return text.replace("\x1b", "?").replace("\b", "?") +                self._color = None  +                self._bgcolor = None  +                self._bold = False  +                self._underline = False  +                self._strike = False  +                self._italic = False  +                self._blink = False  +                self._reverse = False  +                self._hidden = False  +  +            elif attr in (38, 48) and len(attrs) > 1:  +                n = attrs.pop()  +  +                # 256 colors.  +                if n == 5 and len(attrs) >= 1:  +                    if attr == 38:  +                        m = attrs.pop()  +                        self._color = _256_colors.get(m)  +                    elif attr == 48:  +                        m = attrs.pop()  +                        self._bgcolor = _256_colors.get(m)  +  +                # True colors.  +                if n == 2 and len(attrs) >= 3:  +                    try:  +                        color_str = "#%02x%02x%02x" % (  +                            attrs.pop(),  +                            attrs.pop(),  +                            attrs.pop(),  +                        )  +                    except IndexError:  +                        pass  +                    else:  +                        if attr == 38:  +                            self._color = color_str  +                        elif attr == 48:  +                            self._bgcolor = color_str  +  +    def _create_style_string(self) -> str:  +        """  +        Turn current style flags into a string for usage in a formatted text.  +        """  +        result = []  +        if self._color:  +            result.append(self._color)  +        if self._bgcolor:  +            result.append("bg:" + self._bgcolor)  +        if self._bold:  +            result.append("bold")  +        if self._underline:  +            result.append("underline")  +        if self._strike:  +            result.append("strike")  +        if self._italic:  +            result.append("italic")  +        if self._blink:  +            result.append("blink")  +        if self._reverse:  +            result.append("reverse")  +        if self._hidden:  +            result.append("hidden")  +  +        return " ".join(result)  +  +    def __repr__(self) -> str:  +        return "ANSI(%r)" % (self.value,)  +  +    def __pt_formatted_text__(self) -> StyleAndTextTuples:  +        return self._formatted_text  +  +    def format(self, *args: str, **kwargs: str) -> "ANSI":  +        """  +        Like `str.format`, but make sure that the arguments are properly  +        escaped. (No ANSI escapes can be injected.)  +        """  +        # Escape all the arguments.  +        args = tuple(ansi_escape(a) for a in args)  +        kwargs = {k: ansi_escape(v) for k, v in kwargs.items()}  +  +        return ANSI(self.value.format(*args, **kwargs))  +  +  +# Mapping of the ANSI color codes to their names.  +_fg_colors = {v: k for k, v in FG_ANSI_COLORS.items()}  +_bg_colors = {v: k for k, v in BG_ANSI_COLORS.items()}  +  +# Mapping of the escape codes for 256colors to their 'ffffff' value.  +_256_colors = {}  +  +for i, (r, g, b) in enumerate(_256_colors_table.colors):  +    _256_colors[i] = "#%02x%02x%02x" % (r, g, b)  +  +  +def ansi_escape(text: str) -> str:  +    """  +    Replace characters with a special meaning.  +    """  +    return text.replace("\x1b", "?").replace("\b", "?")  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 index c1761f26402..95b6cf716a6 100644 --- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/base.py +++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/base.py @@ -1,176 +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 +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  diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/html.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/html.py index 06c6020f543..58d4c08c820 100644 --- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/html.py +++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/html.py @@ -1,138 +1,138 @@ -import xml.dom.minidom as minidom -from typing import Any, List, Tuple, Union - -from .base import FormattedText, StyleAndTextTuples - -__all__ = ["HTML"] - - -class HTML: -    """ -    HTML formatted text. -    Take something HTML-like, for use as a formatted string. - -    :: - -        # Turn something into red. -        HTML('<style fg="ansired" bg="#00ff44">...</style>') - -        # Italic, bold, underline and strike. -        HTML('<i>...</i>') -        HTML('<b>...</b>') -        HTML('<u>...</u>') -        HTML('<s>...</s>') - -    All HTML elements become available as a "class" in the style sheet. -    E.g. ``<username>...</username>`` can be styled, by setting a style for -    ``username``. -    """ - -    def __init__(self, value: str) -> None: -        self.value = value -        document = minidom.parseString("<html-root>%s</html-root>" % (value,)) - -        result: StyleAndTextTuples = [] -        name_stack: List[str] = [] -        fg_stack: List[str] = [] -        bg_stack: List[str] = [] - -        def get_current_style() -> str: -            "Build style string for current node." -            parts = [] -            if name_stack: -                parts.append("class:" + ",".join(name_stack)) - -            if fg_stack: -                parts.append("fg:" + fg_stack[-1]) -            if bg_stack: -                parts.append("bg:" + bg_stack[-1]) -            return " ".join(parts) - -        def process_node(node: Any) -> None: -            "Process node recursively." -            for child in node.childNodes: -                if child.nodeType == child.TEXT_NODE: -                    result.append((get_current_style(), child.data)) -                else: -                    add_to_name_stack = child.nodeName not in ( -                        "#document", -                        "html-root", -                        "style", -                    ) -                    fg = bg = "" - -                    for k, v in child.attributes.items(): -                        if k == "fg": -                            fg = v -                        if k == "bg": -                            bg = v -                        if k == "color": -                            fg = v  # Alias for 'fg'. - -                    # Check for spaces in attributes. This would result in -                    # invalid style strings otherwise. -                    if " " in fg: -                        raise ValueError('"fg" attribute contains a space.') -                    if " " in bg: -                        raise ValueError('"bg" attribute contains a space.') - -                    if add_to_name_stack: -                        name_stack.append(child.nodeName) -                    if fg: -                        fg_stack.append(fg) -                    if bg: -                        bg_stack.append(bg) - -                    process_node(child) - -                    if add_to_name_stack: -                        name_stack.pop() -                    if fg: -                        fg_stack.pop() -                    if bg: -                        bg_stack.pop() - -        process_node(document) - -        self.formatted_text = FormattedText(result) - -    def __repr__(self) -> str: -        return "HTML(%r)" % (self.value,) - -    def __pt_formatted_text__(self) -> StyleAndTextTuples: -        return self.formatted_text - -    def format(self, *args: object, **kwargs: object) -> "HTML": -        """ -        Like `str.format`, but make sure that the arguments are properly -        escaped. -        """ -        # Escape all the arguments. -        escaped_args = [html_escape(a) for a in args] -        escaped_kwargs = {k: html_escape(v) for k, v in kwargs.items()} - -        return HTML(self.value.format(*escaped_args, **escaped_kwargs)) - -    def __mod__(self, value: Union[object, Tuple[object, ...]]) -> "HTML": -        """ -        HTML('<b>%s</b>') % value -        """ -        if not isinstance(value, tuple): -            value = (value,) - -        value = tuple(html_escape(i) for i in value) -        return HTML(self.value % value) - - -def html_escape(text: object) -> str: -    # The string interpolation functions also take integers and other types. -    # Convert to string first. -    if not isinstance(text, str): -        text = "{}".format(text) - -    return ( -        text.replace("&", "&") -        .replace("<", "<") -        .replace(">", ">") -        .replace('"', """) -    ) +import xml.dom.minidom as minidom  +from typing import Any, List, Tuple, Union  +  +from .base import FormattedText, StyleAndTextTuples  +  +__all__ = ["HTML"]  +  +  +class HTML:  +    """  +    HTML formatted text.  +    Take something HTML-like, for use as a formatted string.  +  +    ::  +  +        # Turn something into red.  +        HTML('<style fg="ansired" bg="#00ff44">...</style>')  +  +        # Italic, bold, underline and strike.  +        HTML('<i>...</i>')  +        HTML('<b>...</b>')  +        HTML('<u>...</u>')  +        HTML('<s>...</s>')  +  +    All HTML elements become available as a "class" in the style sheet.  +    E.g. ``<username>...</username>`` can be styled, by setting a style for  +    ``username``.  +    """  +  +    def __init__(self, value: str) -> None:  +        self.value = value  +        document = minidom.parseString("<html-root>%s</html-root>" % (value,))  +  +        result: StyleAndTextTuples = []  +        name_stack: List[str] = []  +        fg_stack: List[str] = []  +        bg_stack: List[str] = []  +  +        def get_current_style() -> str:  +            "Build style string for current node."  +            parts = []  +            if name_stack:  +                parts.append("class:" + ",".join(name_stack))  +  +            if fg_stack:  +                parts.append("fg:" + fg_stack[-1])  +            if bg_stack:  +                parts.append("bg:" + bg_stack[-1])  +            return " ".join(parts)  +  +        def process_node(node: Any) -> None:  +            "Process node recursively."  +            for child in node.childNodes:  +                if child.nodeType == child.TEXT_NODE:  +                    result.append((get_current_style(), child.data))  +                else:  +                    add_to_name_stack = child.nodeName not in (  +                        "#document",  +                        "html-root",  +                        "style",  +                    )  +                    fg = bg = ""  +  +                    for k, v in child.attributes.items():  +                        if k == "fg":  +                            fg = v  +                        if k == "bg":  +                            bg = v  +                        if k == "color":  +                            fg = v  # Alias for 'fg'.  +  +                    # Check for spaces in attributes. This would result in  +                    # invalid style strings otherwise.  +                    if " " in fg:  +                        raise ValueError('"fg" attribute contains a space.')  +                    if " " in bg:  +                        raise ValueError('"bg" attribute contains a space.')  +  +                    if add_to_name_stack:  +                        name_stack.append(child.nodeName)  +                    if fg:  +                        fg_stack.append(fg)  +                    if bg:  +                        bg_stack.append(bg)  +  +                    process_node(child)  +  +                    if add_to_name_stack:  +                        name_stack.pop()  +                    if fg:  +                        fg_stack.pop()  +                    if bg:  +                        bg_stack.pop()  +  +        process_node(document)  +  +        self.formatted_text = FormattedText(result)  +  +    def __repr__(self) -> str:  +        return "HTML(%r)" % (self.value,)  +  +    def __pt_formatted_text__(self) -> StyleAndTextTuples:  +        return self.formatted_text  +  +    def format(self, *args: object, **kwargs: object) -> "HTML":  +        """  +        Like `str.format`, but make sure that the arguments are properly  +        escaped.  +        """  +        # Escape all the arguments.  +        escaped_args = [html_escape(a) for a in args]  +        escaped_kwargs = {k: html_escape(v) for k, v in kwargs.items()}  +  +        return HTML(self.value.format(*escaped_args, **escaped_kwargs))  +  +    def __mod__(self, value: Union[object, Tuple[object, ...]]) -> "HTML":  +        """  +        HTML('<b>%s</b>') % value  +        """  +        if not isinstance(value, tuple):  +            value = (value,)  +  +        value = tuple(html_escape(i) for i in value)  +        return HTML(self.value % value)  +  +  +def html_escape(text: object) -> str:  +    # The string interpolation functions also take integers and other types.  +    # Convert to string first.  +    if not isinstance(text, str):  +        text = "{}".format(text)  +  +    return (  +        text.replace("&", "&")  +        .replace("<", "<")  +        .replace(">", ">")  +        .replace('"', """)  +    )  diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/pygments.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/pygments.py index dd16f0efbeb..34197840e59 100644 --- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/pygments.py +++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/pygments.py @@ -1,30 +1,30 @@ -from typing import TYPE_CHECKING, List, Tuple - -from prompt_toolkit.styles.pygments import pygments_token_to_classname - -from .base import StyleAndTextTuples - -if TYPE_CHECKING: -    from pygments.token import Token - -__all__ = [ -    "PygmentsTokens", -] - - -class PygmentsTokens: -    """ -    Turn a pygments token list into a list of prompt_toolkit text fragments -    (``(style_str, text)`` tuples). -    """ - -    def __init__(self, token_list: List[Tuple["Token", str]]) -> None: -        self.token_list = token_list - -    def __pt_formatted_text__(self) -> StyleAndTextTuples: -        result: StyleAndTextTuples = [] - -        for token, text in self.token_list: -            result.append(("class:" + pygments_token_to_classname(token), text)) - -        return result +from typing import TYPE_CHECKING, List, Tuple  +  +from prompt_toolkit.styles.pygments import pygments_token_to_classname  +  +from .base import StyleAndTextTuples  +  +if TYPE_CHECKING:  +    from pygments.token import Token  +  +__all__ = [  +    "PygmentsTokens",  +]  +  +  +class PygmentsTokens:  +    """  +    Turn a pygments token list into a list of prompt_toolkit text fragments  +    (``(style_str, text)`` tuples).  +    """  +  +    def __init__(self, token_list: List[Tuple["Token", str]]) -> None:  +        self.token_list = token_list  +  +    def __pt_formatted_text__(self) -> StyleAndTextTuples:  +        result: StyleAndTextTuples = []  +  +        for token, text in self.token_list:  +            result.append(("class:" + pygments_token_to_classname(token), text))  +  +        return result  diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/utils.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/utils.py index cda4233e063..126a6e0b39a 100644 --- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/utils.py +++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/utils.py @@ -1,29 +1,29 @@ -""" -Utilities for manipulating formatted text. - -When ``to_formatted_text`` has been called, we get a list of ``(style, text)`` -tuples. This file contains functions for manipulating such a list. -""" -from typing import Iterable, cast - -from prompt_toolkit.utils import get_cwidth - +"""  +Utilities for manipulating formatted text.  +  +When ``to_formatted_text`` has been called, we get a list of ``(style, text)``  +tuples. This file contains functions for manipulating such a list.  +"""  +from typing import Iterable, cast  +  +from prompt_toolkit.utils import get_cwidth  +   from .base import (      AnyFormattedText,      OneStyleAndTextTuple,      StyleAndTextTuples,      to_formatted_text,  ) - -__all__ = [ +  +__all__ = [       "to_plain_text", -    "fragment_list_len", -    "fragment_list_width", -    "fragment_list_to_text", -    "split_lines", -] - - +    "fragment_list_len",  +    "fragment_list_width",  +    "fragment_list_to_text",  +    "split_lines",  +]  +  +   def to_plain_text(value: AnyFormattedText) -> str:      """      Turn any kind of formatted text back into plain text. @@ -31,68 +31,68 @@ def to_plain_text(value: AnyFormattedText) -> str:      return fragment_list_to_text(to_formatted_text(value)) -def fragment_list_len(fragments: StyleAndTextTuples) -> int: -    """ -    Return the amount of characters in this text fragment list. - -    :param fragments: List of ``(style_str, text)`` or -        ``(style_str, text, mouse_handler)`` tuples. -    """ -    ZeroWidthEscape = "[ZeroWidthEscape]" -    return sum(len(item[1]) for item in fragments if ZeroWidthEscape not in item[0]) - - -def fragment_list_width(fragments: StyleAndTextTuples) -> int: -    """ -    Return the character width of this text fragment list. -    (Take double width characters into account.) - -    :param fragments: List of ``(style_str, text)`` or -        ``(style_str, text, mouse_handler)`` tuples. -    """ -    ZeroWidthEscape = "[ZeroWidthEscape]" -    return sum( -        get_cwidth(c) -        for item in fragments -        for c in item[1] -        if ZeroWidthEscape not in item[0] -    ) - - -def fragment_list_to_text(fragments: StyleAndTextTuples) -> str: -    """ -    Concatenate all the text parts again. - -    :param fragments: List of ``(style_str, text)`` or -        ``(style_str, text, mouse_handler)`` tuples. -    """ -    ZeroWidthEscape = "[ZeroWidthEscape]" -    return "".join(item[1] for item in fragments if ZeroWidthEscape not in item[0]) - - -def split_lines(fragments: StyleAndTextTuples) -> Iterable[StyleAndTextTuples]: -    """ -    Take a single list of (style_str, text) tuples and yield one such list for each -    line. Just like str.split, this will yield at least one item. - -    :param fragments: List of (style_str, text) or (style_str, text, mouse_handler) -                      tuples. -    """ -    line: StyleAndTextTuples = [] - -    for style, string, *mouse_handler in fragments: -        parts = string.split("\n") - -        for part in parts[:-1]: -            if part: -                line.append(cast(OneStyleAndTextTuple, (style, part, *mouse_handler))) -            yield line -            line = [] - -        line.append(cast(OneStyleAndTextTuple, (style, parts[-1], *mouse_handler))) - -    # Always yield the last line, even when this is an empty line. This ensures -    # that when `fragments` ends with a newline character, an additional empty -    # line is yielded. (Otherwise, there's no way to differentiate between the -    # cases where `fragments` does and doesn't end with a newline.) -    yield line +def fragment_list_len(fragments: StyleAndTextTuples) -> int:  +    """  +    Return the amount of characters in this text fragment list.  +  +    :param fragments: List of ``(style_str, text)`` or  +        ``(style_str, text, mouse_handler)`` tuples.  +    """  +    ZeroWidthEscape = "[ZeroWidthEscape]"  +    return sum(len(item[1]) for item in fragments if ZeroWidthEscape not in item[0])  +  +  +def fragment_list_width(fragments: StyleAndTextTuples) -> int:  +    """  +    Return the character width of this text fragment list.  +    (Take double width characters into account.)  +  +    :param fragments: List of ``(style_str, text)`` or  +        ``(style_str, text, mouse_handler)`` tuples.  +    """  +    ZeroWidthEscape = "[ZeroWidthEscape]"  +    return sum(  +        get_cwidth(c)  +        for item in fragments  +        for c in item[1]  +        if ZeroWidthEscape not in item[0]  +    )  +  +  +def fragment_list_to_text(fragments: StyleAndTextTuples) -> str:  +    """  +    Concatenate all the text parts again.  +  +    :param fragments: List of ``(style_str, text)`` or  +        ``(style_str, text, mouse_handler)`` tuples.  +    """  +    ZeroWidthEscape = "[ZeroWidthEscape]"  +    return "".join(item[1] for item in fragments if ZeroWidthEscape not in item[0])  +  +  +def split_lines(fragments: StyleAndTextTuples) -> Iterable[StyleAndTextTuples]:  +    """  +    Take a single list of (style_str, text) tuples and yield one such list for each  +    line. Just like str.split, this will yield at least one item.  +  +    :param fragments: List of (style_str, text) or (style_str, text, mouse_handler)  +                      tuples.  +    """  +    line: StyleAndTextTuples = []  +  +    for style, string, *mouse_handler in fragments:  +        parts = string.split("\n")  +  +        for part in parts[:-1]:  +            if part:  +                line.append(cast(OneStyleAndTextTuple, (style, part, *mouse_handler)))  +            yield line  +            line = []  +  +        line.append(cast(OneStyleAndTextTuple, (style, parts[-1], *mouse_handler)))  +  +    # Always yield the last line, even when this is an empty line. This ensures  +    # that when `fragments` ends with a newline character, an additional empty  +    # line is yielded. (Otherwise, there's no way to differentiate between the  +    # cases where `fragments` does and doesn't end with a newline.)  +    yield line   | 
