You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
98 lines
3.1 KiB
98 lines
3.1 KiB
6 years ago
|
"""
|
||
|
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 unicode_literals
|
||
|
from prompt_toolkit.utils import get_cwidth
|
||
|
|
||
|
__all__ = [
|
||
|
'fragment_list_len',
|
||
|
'fragment_list_width',
|
||
|
'fragment_list_to_text',
|
||
|
'split_lines',
|
||
|
]
|
||
|
|
||
|
|
||
|
def fragment_list_len(fragments):
|
||
|
"""
|
||
|
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):
|
||
|
"""
|
||
|
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):
|
||
|
"""
|
||
|
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):
|
||
|
"""
|
||
|
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 = []
|
||
|
|
||
|
for item in fragments:
|
||
|
# For (style_str, text) tuples.
|
||
|
if len(item) == 2:
|
||
|
style, string = item
|
||
|
parts = string.split('\n')
|
||
|
|
||
|
for part in parts[:-1]:
|
||
|
if part:
|
||
|
line.append((style, part))
|
||
|
yield line
|
||
|
line = []
|
||
|
|
||
|
line.append((style, parts[-1]))
|
||
|
# Note that parts[-1] can be empty, and that's fine. It happens
|
||
|
# in the case of [('[SetCursorPosition]', '')].
|
||
|
|
||
|
# For (style_str, text, mouse_handler) tuples.
|
||
|
# I know, partly copy/paste, but understandable and more efficient
|
||
|
# than many tests.
|
||
|
else:
|
||
|
style, string, mouse_handler = item
|
||
|
parts = string.split('\n')
|
||
|
|
||
|
for part in parts[:-1]:
|
||
|
if part:
|
||
|
line.append((style, part, mouse_handler))
|
||
|
yield line
|
||
|
line = []
|
||
|
|
||
|
line.append((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
|