aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text
diff options
context:
space:
mode:
authorshadchin <shadchin@yandex-team.ru>2022-02-10 16:44:39 +0300
committerDaniil Cherednik <dcherednik@yandex-team.ru>2022-02-10 16:44:39 +0300
commite9656aae26e0358d5378e5b63dcac5c8dbe0e4d0 (patch)
tree64175d5cadab313b3e7039ebaa06c5bc3295e274 /contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text
parent2598ef1d0aee359b4b6d5fdd1758916d5907d04f (diff)
downloadydb-e9656aae26e0358d5378e5b63dcac5c8dbe0e4d0.tar.gz
Restoring authorship annotation for <shadchin@yandex-team.ru>. Commit 2 of 2.
Diffstat (limited to 'contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text')
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/__init__.py104
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/ansi.py506
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/base.py352
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/html.py276
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/pygments.py60
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/utils.py168
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 7272539fcc..f0c92c96f9 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 2d8c5dac95..3d57063357 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 95b6cf716a..c1761f2640 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 58d4c08c82..06c6020f54 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("&", "&amp;")
- .replace("<", "&lt;")
- .replace(">", "&gt;")
- .replace('"', "&quot;")
- )
+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("&", "&amp;")
+ .replace("<", "&lt;")
+ .replace(">", "&gt;")
+ .replace('"', "&quot;")
+ )
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 34197840e5..dd16f0efbe 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 126a6e0b39..cda4233e06 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