utils.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. from __future__ import annotations
  2. from asyncio.events import AbstractEventLoop
  3. from typing import TYPE_CHECKING, Any, TextIO
  4. from prompt_toolkit.application import Application
  5. from prompt_toolkit.application.current import get_app_or_none, get_app_session
  6. from prompt_toolkit.application.run_in_terminal import run_in_terminal
  7. from prompt_toolkit.formatted_text import (
  8. FormattedText,
  9. StyleAndTextTuples,
  10. to_formatted_text,
  11. )
  12. from prompt_toolkit.input import DummyInput
  13. from prompt_toolkit.layout import Layout
  14. from prompt_toolkit.output import ColorDepth, Output
  15. from prompt_toolkit.output.defaults import create_output
  16. from prompt_toolkit.renderer import (
  17. print_formatted_text as renderer_print_formatted_text,
  18. )
  19. from prompt_toolkit.styles import (
  20. BaseStyle,
  21. StyleTransformation,
  22. default_pygments_style,
  23. default_ui_style,
  24. merge_styles,
  25. )
  26. if TYPE_CHECKING:
  27. from prompt_toolkit.layout.containers import AnyContainer
  28. __all__ = [
  29. "print_formatted_text",
  30. "print_container",
  31. "clear",
  32. "set_title",
  33. "clear_title",
  34. ]
  35. def print_formatted_text(
  36. *values: Any,
  37. sep: str = " ",
  38. end: str = "\n",
  39. file: TextIO | None = None,
  40. flush: bool = False,
  41. style: BaseStyle | None = None,
  42. output: Output | None = None,
  43. color_depth: ColorDepth | None = None,
  44. style_transformation: StyleTransformation | None = None,
  45. include_default_pygments_style: bool = True,
  46. ) -> None:
  47. """
  48. ::
  49. print_formatted_text(*values, sep=' ', end='\\n', file=None, flush=False, style=None, output=None)
  50. Print text to stdout. This is supposed to be compatible with Python's print
  51. function, but supports printing of formatted text. You can pass a
  52. :class:`~prompt_toolkit.formatted_text.FormattedText`,
  53. :class:`~prompt_toolkit.formatted_text.HTML` or
  54. :class:`~prompt_toolkit.formatted_text.ANSI` object to print formatted
  55. text.
  56. * Print HTML as follows::
  57. print_formatted_text(HTML('<i>Some italic text</i> <ansired>This is red!</ansired>'))
  58. style = Style.from_dict({
  59. 'hello': '#ff0066',
  60. 'world': '#884444 italic',
  61. })
  62. print_formatted_text(HTML('<hello>Hello</hello> <world>world</world>!'), style=style)
  63. * Print a list of (style_str, text) tuples in the given style to the
  64. output. E.g.::
  65. style = Style.from_dict({
  66. 'hello': '#ff0066',
  67. 'world': '#884444 italic',
  68. })
  69. fragments = FormattedText([
  70. ('class:hello', 'Hello'),
  71. ('class:world', 'World'),
  72. ])
  73. print_formatted_text(fragments, style=style)
  74. If you want to print a list of Pygments tokens, wrap it in
  75. :class:`~prompt_toolkit.formatted_text.PygmentsTokens` to do the
  76. conversion.
  77. If a prompt_toolkit `Application` is currently running, this will always
  78. print above the application or prompt (similar to `patch_stdout`). So,
  79. `print_formatted_text` will erase the current application, print the text,
  80. and render the application again.
  81. :param values: Any kind of printable object, or formatted string.
  82. :param sep: String inserted between values, default a space.
  83. :param end: String appended after the last value, default a newline.
  84. :param style: :class:`.Style` instance for the color scheme.
  85. :param include_default_pygments_style: `bool`. Include the default Pygments
  86. style when set to `True` (the default).
  87. """
  88. assert not (output and file)
  89. # Create Output object.
  90. if output is None:
  91. if file:
  92. output = create_output(stdout=file)
  93. else:
  94. output = get_app_session().output
  95. assert isinstance(output, Output)
  96. # Get color depth.
  97. color_depth = color_depth or output.get_default_color_depth()
  98. # Merges values.
  99. def to_text(val: Any) -> StyleAndTextTuples:
  100. # Normal lists which are not instances of `FormattedText` are
  101. # considered plain text.
  102. if isinstance(val, list) and not isinstance(val, FormattedText):
  103. return to_formatted_text(f"{val}")
  104. return to_formatted_text(val, auto_convert=True)
  105. fragments = []
  106. for i, value in enumerate(values):
  107. fragments.extend(to_text(value))
  108. if sep and i != len(values) - 1:
  109. fragments.extend(to_text(sep))
  110. fragments.extend(to_text(end))
  111. # Print output.
  112. def render() -> None:
  113. assert isinstance(output, Output)
  114. renderer_print_formatted_text(
  115. output,
  116. fragments,
  117. _create_merged_style(
  118. style, include_default_pygments_style=include_default_pygments_style
  119. ),
  120. color_depth=color_depth,
  121. style_transformation=style_transformation,
  122. )
  123. # Flush the output stream.
  124. if flush:
  125. output.flush()
  126. # If an application is running, print above the app. This does not require
  127. # `patch_stdout`.
  128. loop: AbstractEventLoop | None = None
  129. app = get_app_or_none()
  130. if app is not None:
  131. loop = app.loop
  132. if loop is not None:
  133. loop.call_soon_threadsafe(lambda: run_in_terminal(render))
  134. else:
  135. render()
  136. def print_container(
  137. container: AnyContainer,
  138. file: TextIO | None = None,
  139. style: BaseStyle | None = None,
  140. include_default_pygments_style: bool = True,
  141. ) -> None:
  142. """
  143. Print any layout to the output in a non-interactive way.
  144. Example usage::
  145. from prompt_toolkit.widgets import Frame, TextArea
  146. print_container(
  147. Frame(TextArea(text='Hello world!')))
  148. """
  149. if file:
  150. output = create_output(stdout=file)
  151. else:
  152. output = get_app_session().output
  153. app: Application[None] = Application(
  154. layout=Layout(container=container),
  155. output=output,
  156. # `DummyInput` will cause the application to terminate immediately.
  157. input=DummyInput(),
  158. style=_create_merged_style(
  159. style, include_default_pygments_style=include_default_pygments_style
  160. ),
  161. )
  162. try:
  163. app.run(in_thread=True)
  164. except EOFError:
  165. pass
  166. def _create_merged_style(
  167. style: BaseStyle | None, include_default_pygments_style: bool
  168. ) -> BaseStyle:
  169. """
  170. Merge user defined style with built-in style.
  171. """
  172. styles = [default_ui_style()]
  173. if include_default_pygments_style:
  174. styles.append(default_pygments_style())
  175. if style:
  176. styles.append(style)
  177. return merge_styles(styles)
  178. def clear() -> None:
  179. """
  180. Clear the screen.
  181. """
  182. output = get_app_session().output
  183. output.erase_screen()
  184. output.cursor_goto(0, 0)
  185. output.flush()
  186. def set_title(text: str) -> None:
  187. """
  188. Set the terminal title.
  189. """
  190. output = get_app_session().output
  191. output.set_title(text)
  192. def clear_title() -> None:
  193. """
  194. Erase the current title.
  195. """
  196. set_title("")