Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion docs/pages/printing_text.rst
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,8 @@ to_formatted_text
A useful function to know about is
:func:`~prompt_toolkit.formatted_text.to_formatted_text`. This ensures that the
given input is valid formatted text. While doing so, an additional style can be
applied as well.
applied as well. The output will be the canonical form of formatted text, which
is a list of ``(style, text)`` tuples.

.. code:: python

Expand All @@ -272,3 +273,50 @@ applied as well.
text = to_formatted_text(html, style='class:my_html bg:#00ff00 italic')

print_formatted_text(text)


String concatenation
^^^^^^^^^^^^^^^^^^^^

Formatted text can be concatenated (or merged) together, with the
:func:`~prompt_toolkit.formatted_text.merge_formatted_text` helper. This helper
can take multiple text objects which either are plain text or formatted text,
and will concatenate them into one formatted text object.


.. code:: python

from prompt_toolkit import print_formatted_text
from prompt_toolkit.formatted_text import ANSI, HTML, merge_formatted_text


def main():
html = HTML("<u><ansired>Hello</ansired></u> ")
ansi = ANSI("\x1b[32mworld\n")

merged_text = merge_formatted_text([html, ansi])

print_formatted_text(merged_text)


if __name__ == "__main__":
main()


Other utility functions
^^^^^^^^^^^^^^^^^^^^^^^

- :func:`~prompt_toolkit.formatted_text.formatted_text_width`: Turn the
formatted text string back into a plain text string.
- :func:`~prompt_toolkit.formatted_text.formatted_text_len`: This returns the
amount of characters in a formatted text string. It's the same as turning the
formatted text back into a plain text string, and calling ``len()``.
- :func:`~prompt_toolkit.formatted_text.formatted_text_width`: This returns the
amount of space the formatted text takes when printed in a terminal. It's
especially useful for aligning text that contains double width characters.
- :func:`~prompt_toolkit.formatted_text.formatted_text_split_lines`: Split the
lines and return a list of formatted text objects.
- :func:`~prompt_toolkit.formatted_text.formatted_text_indent`: Indent all the
lines and return a a new formatted text object.
- :func:`~prompt_toolkit.formatted_text.formatted_text_strip`: Strip
surrounding whitespace and return a new formatted text object.
19 changes: 19 additions & 0 deletions examples/print-text/merge-formatted-text.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env python
"""
Demonstration of string concatenation with ``merge_formatted_text``.
"""
from prompt_toolkit import print_formatted_text
from prompt_toolkit.formatted_text import ANSI, HTML, merge_formatted_text


def main():
html = HTML("<u><ansired>Hello</ansired></u> ")
ansi = ANSI("\x1b[32mworld\n")

merged_text = merge_formatted_text([html, ansi])

print_formatted_text(merged_text)


if __name__ == "__main__":
main()
12 changes: 12 additions & 0 deletions prompt_toolkit/formatted_text/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@
from .html import HTML
from .pygments import PygmentsTokens
from .utils import (
formatted_text_indent,
formatted_text_len,
formatted_text_split_lines,
formatted_text_strip,
formatted_text_to_text,
formatted_text_width,
fragment_list_len,
fragment_list_to_text,
fragment_list_width,
Expand All @@ -49,4 +55,10 @@
"fragment_list_width",
"fragment_list_to_text",
"split_lines",
"formatted_text_len",
"formatted_text_width",
"formatted_text_to_text",
"formatted_text_split_lines",
"formatted_text_indent",
"formatted_text_strip",
]
6 changes: 6 additions & 0 deletions prompt_toolkit/formatted_text/pygments.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ class PygmentsTokens:
"""
Turn a pygments token list into a list of prompt_toolkit text fragments
(``(style_str, text)`` tuples).

Usage::

from pygments.lexers import PythonLexer
tokens = list(PythonLexer().get_tokens("def hello(): pass"))
PygmentsTokens(tokens)
"""

def __init__(self, token_list: List[Tuple["Token", str]]) -> None:
Expand Down
76 changes: 74 additions & 2 deletions prompt_toolkit/formatted_text/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,92 @@
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 typing import Iterable, List, cast

from prompt_toolkit.utils import get_cwidth

from .base import OneStyleAndTextTuple, StyleAndTextTuples
from .base import (
AnyFormattedText,
OneStyleAndTextTuple,
StyleAndTextTuples,
merge_formatted_text,
to_formatted_text,
)

__all__ = [
# Higher level formatted-text operations.
"formatted_text_len",
"formatted_text_width",
"formatted_text_to_text",
"formatted_text_split_lines",
"formatted_text_indent",
"formatted_text_strip",
# Lower-level fragment operations.
"fragment_list_len",
"fragment_list_width",
"fragment_list_to_text",
"split_lines",
]


def formatted_text_len(text: AnyFormattedText) -> int:
"""
Return the amount of characters in this formatted text.
"""
return fragment_list_len(to_formatted_text(text))


def formatted_text_width(text: AnyFormattedText) -> int:
"""
Return the amount of space this formatted text would occupy when printing
on a terminal (this takes double width characters into account).
"""
return fragment_list_width(to_formatted_text(text))


def formatted_text_to_text(text: AnyFormattedText) -> str:
"""
Turn formatted text back into plain text.
"""
return fragment_list_to_text(to_formatted_text(text))


def formatted_text_split_lines(text: AnyFormattedText) -> List[StyleAndTextTuples]:
"""
Take any formatted text, split the lines and turn it into a list of
formatted text objects.
"""
return list(split_lines(to_formatted_text(text)))


def formatted_text_indent(
text: AnyFormattedText,
prefix: AnyFormattedText = "",
indent_first_line: bool = True,
) -> StyleAndTextTuples:
"""
Insert the given prefix before every line of text.
"""
lines = split_lines(to_formatted_text(text))

result = []
for i, line in enumerate(lines):
if i > 0 or (indent_first_line and i == 0):
line = to_formatted_text(merge_formatted_text([prefix, line]))
result.extend(line)
return result


def formatted_text_strip(text: AnyFormattedText) -> StyleAndTextTuples:
"""
Strip whitespace around formatted text.
"""
# TODO


# ---


def fragment_list_len(fragments: StyleAndTextTuples) -> int:
"""
Return the amount of characters in this text fragment list.
Expand Down
10 changes: 10 additions & 0 deletions tests/test_formatted_text.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
FormattedText,
PygmentsTokens,
Template,
formatted_text_len,
formatted_text_to_text,
merge_formatted_text,
to_formatted_text,
)
Expand Down Expand Up @@ -214,3 +216,11 @@ def test_split_lines_3():
assert lines == [
[("class:a", "")],
]


def test_formatted_text_to_text():
assert formatted_text_to_text(HTML("<b>Hello</b> <u>world</u>!")) == "Hello world!"


def test_formatted_text_len():
assert formatted_text_len(HTML("<b>Hello</b> <u>world</u>!")) == len("Hello world!")