|
""" |
|
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 __future__ import annotations |
|
|
|
from typing import Iterable, cast |
|
|
|
from prompt_toolkit.utils import get_cwidth |
|
|
|
from .base import ( |
|
AnyFormattedText, |
|
OneStyleAndTextTuple, |
|
StyleAndTextTuples, |
|
to_formatted_text, |
|
) |
|
|
|
__all__ = [ |
|
"to_plain_text", |
|
"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. |
|
""" |
|
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: Iterable[OneStyleAndTextTuple], |
|
) -> 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: Iterable 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]: |
|
line.append(cast(OneStyleAndTextTuple, (style, part, *mouse_handler))) |
|
yield line |
|
line = [] |
|
|
|
line.append(cast(OneStyleAndTextTuple, (style, parts[-1], *mouse_handler))) |
|
|
|
|
|
|
|
|
|
|
|
yield line |
|
|