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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions examples/rich/choices.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/env python
from rich.markdown import Markdown
from rich.text import Text

from prompt_toolkit import choice
from prompt_toolkit.formatted_text.rich import Rich

# For the header, we wrap the `Markdown` object from `rich` in a `Rich` object
# from `prompt_toolkit`, so that we can explicitly set a width.
header = Rich(
Markdown(
"""
# Please select a dish

Choose *one* item please.

```python
def some_example_function() -> None: "test"
```
""".strip()
),
width=50,
style="black on blue",
)


def main():
answer = choice(
message=header,
options=[
("pizza", "Pizza with mushrooms"),
(
"salad",
Text.from_markup(
":warning: [green]Salad[/green] with [red]tomatoes[/red]"
),
),
("sushi", "Sushi"),
],
show_frame=True,
)
print(f"You said: {answer}")


if __name__ == "__main__":
main()
21 changes: 21 additions & 0 deletions examples/rich/dialog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env python
"""
Example of an input box dialog.
"""

from rich.text import Text

from prompt_toolkit.shortcuts import input_dialog


def main():
result = input_dialog(
title=Text.from_markup("[red]Input[/red] dialog [b]example[b]"),
text=Text.from_markup("Please type your [green]name[/green]:"),
).run()

print(f"Result = {result}")


if __name__ == "__main__":
main()
32 changes: 32 additions & 0 deletions examples/rich/multiline-prompt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/usr/bin/env python
from rich.markdown import Markdown

from prompt_toolkit import prompt
from prompt_toolkit.formatted_text import merge_formatted_text
from prompt_toolkit.formatted_text.rich import Rich

# For the header, we wrap the `Markdown` object from `rich` in a `Rich` object
# from `prompt_toolkit`, so that we can explicitly set a width.
header = Rich(
Markdown(
"""
# Type the name of the following function:

```python
def fibonacci(number: int) -> int:
"compute Fibonacci number"
```

"""
),
width=50,
)


def main():
answer = prompt(merge_formatted_text([header, "> "]))
print(f"You said: {answer}")


if __name__ == "__main__":
main()
15 changes: 15 additions & 0 deletions examples/rich/prompt-with-frame.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env python
from rich.text import Text

from prompt_toolkit import prompt


def main():
answer = prompt(
Text.from_markup("[green]Say[/green] [b]something[/b] > "), show_frame=True
)
print(f"You said: {answer}")


if __name__ == "__main__":
main()
13 changes: 13 additions & 0 deletions examples/rich/prompt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env python
from rich.text import Text

from prompt_toolkit import prompt


def main():
answer = prompt(Text.from_markup("[green]Say[/green] [b]something[/b] > "))
print(f"You said: {answer}")


if __name__ == "__main__":
main()
3 changes: 3 additions & 0 deletions src/prompt_toolkit/formatted_text/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
)
from .html import HTML
from .pygments import PygmentsTokens
from .rich import Rich
from .utils import (
fragment_list_len,
fragment_list_to_text,
Expand All @@ -50,6 +51,8 @@
"ANSI",
# Pygments.
"PygmentsTokens",
# Rich.
"Rich",
# Utils.
"fragment_list_len",
"fragment_list_width",
Expand Down
13 changes: 13 additions & 0 deletions src/prompt_toolkit/formatted_text/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,19 @@ class MagicFormattedText(Protocol):

def __pt_formatted_text__(self) -> StyleAndTextTuples: ...

class RichFormattedText(Protocol):
"""
Any rich text object from the rich library that implements
``__rich_console__``.
"""

def __rich_console__(self, console: Any = ..., options: Any = ...) -> Any: ...


AnyFormattedText = Union[
str,
"MagicFormattedText",
"RichFormattedText",
StyleAndTextTuples,
# Callable[[], 'AnyFormattedText'] # Recursive definition not supported by mypy.
Callable[[], Any],
Expand Down Expand Up @@ -78,6 +87,10 @@ def to_formatted_text(
result = value # StyleAndTextTuples
elif hasattr(value, "__pt_formatted_text__"):
result = cast("MagicFormattedText", value).__pt_formatted_text__()
elif hasattr(value, "__rich_console__"):
from .rich import Rich

result = Rich(value).__pt_formatted_text__()
elif callable(value):
return to_formatted_text(value(), style=style)
elif auto_convert:
Expand Down
50 changes: 50 additions & 0 deletions src/prompt_toolkit/formatted_text/rich.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from __future__ import annotations

from io import StringIO
from typing import TYPE_CHECKING, Any

from .ansi import ANSI
from .base import StyleAndTextTuples

if TYPE_CHECKING:
from rich.style import StyleType

__all__ = [
"Rich",
]


class Rich:
"""
Turn any rich text object from the `rich` library into prompt_toolkit
formatted text, so that it can be used in a prompt or anywhere else.

Note that `to_formatted_text` automatically recognizes objects that have a
`__rich_console__` attribute and will wrap them in a `Rich` instance.
"""

def __init__(
self,
rich_object: Any,
width: int | None = None,
style: StyleType | None = None,
) -> None:
self.rich_object = rich_object
self.width = width
self.style = style

def __pt_formatted_text__(self) -> StyleAndTextTuples:
from rich.console import Console

file = StringIO()

console = Console(
file=file,
force_terminal=True,
color_system="truecolor",
width=self.width,
style=self.style,
)
console.print(self.rich_object, end="")
ansi = file.getvalue()
return ANSI(ansi).__pt_formatted_text__()