shortcuts.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717
  1. """
  2. Shortcuts for retrieving input from the user.
  3. If you are using this library for retrieving some input from the user (as a
  4. pure Python replacement for GNU readline), probably for 90% of the use cases,
  5. the :func:`.prompt` function is all you need. It's the easiest shortcut which
  6. does a lot of the underlying work like creating a
  7. :class:`~prompt_toolkit.interface.CommandLineInterface` instance for you.
  8. When is this not sufficient:
  9. - When you want to have more complicated layouts (maybe with sidebars or
  10. multiple toolbars. Or visibility of certain user interface controls
  11. according to some conditions.)
  12. - When you wish to have multiple input buffers. (If you would create an
  13. editor like a Vi clone.)
  14. - Something else that requires more customization than what is possible
  15. with the parameters of `prompt`.
  16. In that case, study the code in this file and build your own
  17. `CommandLineInterface` instance. It's not too complicated.
  18. """
  19. from __future__ import unicode_literals
  20. from .buffer import Buffer, AcceptAction
  21. from .document import Document
  22. from .enums import DEFAULT_BUFFER, SEARCH_BUFFER, EditingMode
  23. from .filters import IsDone, HasFocus, RendererHeightIsKnown, to_simple_filter, to_cli_filter, Condition
  24. from .history import InMemoryHistory
  25. from .interface import CommandLineInterface, Application, AbortAction
  26. from .key_binding.defaults import load_key_bindings_for_prompt
  27. from .key_binding.registry import Registry
  28. from .keys import Keys
  29. from .layout import Window, HSplit, FloatContainer, Float
  30. from .layout.containers import ConditionalContainer
  31. from .layout.controls import BufferControl, TokenListControl
  32. from .layout.dimension import LayoutDimension
  33. from .layout.lexers import PygmentsLexer
  34. from .layout.margins import PromptMargin, ConditionalMargin
  35. from .layout.menus import CompletionsMenu, MultiColumnCompletionsMenu
  36. from .layout.processors import PasswordProcessor, ConditionalProcessor, AppendAutoSuggestion, HighlightSearchProcessor, HighlightSelectionProcessor, DisplayMultipleCursors
  37. from .layout.prompt import DefaultPrompt
  38. from .layout.screen import Char
  39. from .layout.toolbars import ValidationToolbar, SystemToolbar, ArgToolbar, SearchToolbar
  40. from .layout.utils import explode_tokens
  41. from .renderer import print_tokens as renderer_print_tokens
  42. from .styles import DEFAULT_STYLE, Style, style_from_dict
  43. from .token import Token
  44. from .utils import is_conemu_ansi, is_windows, DummyContext
  45. from six import text_type, exec_, PY2
  46. import os
  47. import sys
  48. import textwrap
  49. import threading
  50. import time
  51. try:
  52. from pygments.lexer import Lexer as pygments_Lexer
  53. from pygments.style import Style as pygments_Style
  54. except ImportError:
  55. pygments_Lexer = None
  56. pygments_Style = None
  57. if is_windows():
  58. from .terminal.win32_output import Win32Output
  59. from .terminal.conemu_output import ConEmuOutput
  60. else:
  61. from .terminal.vt100_output import Vt100_Output
  62. __all__ = (
  63. 'create_eventloop',
  64. 'create_output',
  65. 'create_prompt_layout',
  66. 'create_prompt_application',
  67. 'prompt',
  68. 'prompt_async',
  69. 'create_confirm_application',
  70. 'run_application',
  71. 'confirm',
  72. 'print_tokens',
  73. 'clear',
  74. )
  75. def create_eventloop(inputhook=None, recognize_win32_paste=True):
  76. """
  77. Create and return an
  78. :class:`~prompt_toolkit.eventloop.base.EventLoop` instance for a
  79. :class:`~prompt_toolkit.interface.CommandLineInterface`.
  80. """
  81. if is_windows():
  82. from prompt_toolkit.eventloop.win32 import Win32EventLoop as Loop
  83. return Loop(inputhook=inputhook, recognize_paste=recognize_win32_paste)
  84. else:
  85. from prompt_toolkit.eventloop.posix import PosixEventLoop as Loop
  86. return Loop(inputhook=inputhook)
  87. def create_output(stdout=None, true_color=False, ansi_colors_only=None):
  88. """
  89. Return an :class:`~prompt_toolkit.output.Output` instance for the command
  90. line.
  91. :param true_color: When True, use 24bit colors instead of 256 colors.
  92. (`bool` or :class:`~prompt_toolkit.filters.SimpleFilter`.)
  93. :param ansi_colors_only: When True, restrict to 16 ANSI colors only.
  94. (`bool` or :class:`~prompt_toolkit.filters.SimpleFilter`.)
  95. """
  96. stdout = stdout or sys.__stdout__
  97. true_color = to_simple_filter(true_color)
  98. if is_windows():
  99. if is_conemu_ansi():
  100. return ConEmuOutput(stdout)
  101. else:
  102. return Win32Output(stdout)
  103. else:
  104. term = os.environ.get('TERM', '')
  105. if PY2:
  106. term = term.decode('utf-8')
  107. return Vt100_Output.from_pty(
  108. stdout, true_color=true_color,
  109. ansi_colors_only=ansi_colors_only, term=term)
  110. def create_asyncio_eventloop(loop=None):
  111. """
  112. Returns an asyncio :class:`~prompt_toolkit.eventloop.EventLoop` instance
  113. for usage in a :class:`~prompt_toolkit.interface.CommandLineInterface`. It
  114. is a wrapper around an asyncio loop.
  115. :param loop: The asyncio eventloop (or `None` if the default asyncioloop
  116. should be used.)
  117. """
  118. # Inline import, to make sure the rest doesn't break on Python 2. (Where
  119. # asyncio is not available.)
  120. if is_windows():
  121. from prompt_toolkit.eventloop.asyncio_win32 import Win32AsyncioEventLoop as AsyncioEventLoop
  122. else:
  123. from prompt_toolkit.eventloop.asyncio_posix import PosixAsyncioEventLoop as AsyncioEventLoop
  124. return AsyncioEventLoop(loop)
  125. def _split_multiline_prompt(get_prompt_tokens):
  126. """
  127. Take a `get_prompt_tokens` function and return three new functions instead.
  128. One that tells whether this prompt consists of multiple lines; one that
  129. returns the tokens to be shown on the lines above the input; and another
  130. one with the tokens to be shown at the first line of the input.
  131. """
  132. def has_before_tokens(cli):
  133. for token, char in get_prompt_tokens(cli):
  134. if '\n' in char:
  135. return True
  136. return False
  137. def before(cli):
  138. result = []
  139. found_nl = False
  140. for token, char in reversed(explode_tokens(get_prompt_tokens(cli))):
  141. if found_nl:
  142. result.insert(0, (token, char))
  143. elif char == '\n':
  144. found_nl = True
  145. return result
  146. def first_input_line(cli):
  147. result = []
  148. for token, char in reversed(explode_tokens(get_prompt_tokens(cli))):
  149. if char == '\n':
  150. break
  151. else:
  152. result.insert(0, (token, char))
  153. return result
  154. return has_before_tokens, before, first_input_line
  155. class _RPrompt(Window):
  156. " The prompt that is displayed on the right side of the Window. "
  157. def __init__(self, get_tokens=None):
  158. get_tokens = get_tokens or (lambda cli: [])
  159. super(_RPrompt, self).__init__(
  160. TokenListControl(get_tokens, align_right=True))
  161. def create_prompt_layout(message='', lexer=None, is_password=False,
  162. reserve_space_for_menu=8,
  163. get_prompt_tokens=None, get_continuation_tokens=None,
  164. get_rprompt_tokens=None,
  165. get_bottom_toolbar_tokens=None,
  166. display_completions_in_columns=False,
  167. extra_input_processors=None, multiline=False,
  168. wrap_lines=True):
  169. """
  170. Create a :class:`.Container` instance for a prompt.
  171. :param message: Text to be used as prompt.
  172. :param lexer: :class:`~prompt_toolkit.layout.lexers.Lexer` to be used for
  173. the highlighting.
  174. :param is_password: `bool` or :class:`~prompt_toolkit.filters.CLIFilter`.
  175. When True, display input as '*'.
  176. :param reserve_space_for_menu: Space to be reserved for the menu. When >0,
  177. make sure that a minimal height is allocated in the terminal, in order
  178. to display the completion menu.
  179. :param get_prompt_tokens: An optional callable that returns the tokens to be
  180. shown in the menu. (To be used instead of a `message`.)
  181. :param get_continuation_tokens: An optional callable that takes a
  182. CommandLineInterface and width as input and returns a list of (Token,
  183. text) tuples to be used for the continuation.
  184. :param get_bottom_toolbar_tokens: An optional callable that returns the
  185. tokens for a toolbar at the bottom.
  186. :param display_completions_in_columns: `bool` or
  187. :class:`~prompt_toolkit.filters.CLIFilter`. Display the completions in
  188. multiple columns.
  189. :param multiline: `bool` or :class:`~prompt_toolkit.filters.CLIFilter`.
  190. When True, prefer a layout that is more adapted for multiline input.
  191. Text after newlines is automatically indented, and search/arg input is
  192. shown below the input, instead of replacing the prompt.
  193. :param wrap_lines: `bool` or :class:`~prompt_toolkit.filters.CLIFilter`.
  194. When True (the default), automatically wrap long lines instead of
  195. scrolling horizontally.
  196. """
  197. assert isinstance(message, text_type), 'Please provide a unicode string.'
  198. assert get_bottom_toolbar_tokens is None or callable(get_bottom_toolbar_tokens)
  199. assert get_prompt_tokens is None or callable(get_prompt_tokens)
  200. assert get_rprompt_tokens is None or callable(get_rprompt_tokens)
  201. assert not (message and get_prompt_tokens)
  202. display_completions_in_columns = to_cli_filter(display_completions_in_columns)
  203. multiline = to_cli_filter(multiline)
  204. if get_prompt_tokens is None:
  205. get_prompt_tokens = lambda _: [(Token.Prompt, message)]
  206. has_before_tokens, get_prompt_tokens_1, get_prompt_tokens_2 = \
  207. _split_multiline_prompt(get_prompt_tokens)
  208. # `lexer` is supposed to be a `Lexer` instance. But if a Pygments lexer
  209. # class is given, turn it into a PygmentsLexer. (Important for
  210. # backwards-compatibility.)
  211. try:
  212. if pygments_Lexer and issubclass(lexer, pygments_Lexer):
  213. lexer = PygmentsLexer(lexer, sync_from_start=True)
  214. except TypeError: # Happens when lexer is `None` or an instance of something else.
  215. pass
  216. # Create processors list.
  217. input_processors = [
  218. ConditionalProcessor(
  219. # By default, only highlight search when the search
  220. # input has the focus. (Note that this doesn't mean
  221. # there is no search: the Vi 'n' binding for instance
  222. # still allows to jump to the next match in
  223. # navigation mode.)
  224. HighlightSearchProcessor(preview_search=True),
  225. HasFocus(SEARCH_BUFFER)),
  226. HighlightSelectionProcessor(),
  227. ConditionalProcessor(AppendAutoSuggestion(), HasFocus(DEFAULT_BUFFER) & ~IsDone()),
  228. ConditionalProcessor(PasswordProcessor(), is_password),
  229. DisplayMultipleCursors(DEFAULT_BUFFER),
  230. ]
  231. if extra_input_processors:
  232. input_processors.extend(extra_input_processors)
  233. # Show the prompt before the input (using the DefaultPrompt processor.
  234. # This also replaces it with reverse-i-search and 'arg' when required.
  235. # (Only for single line mode.)
  236. # (DefaultPrompt should always be at the end of the processors.)
  237. input_processors.append(ConditionalProcessor(
  238. DefaultPrompt(get_prompt_tokens_2), ~multiline))
  239. # Create bottom toolbar.
  240. if get_bottom_toolbar_tokens:
  241. toolbars = [ConditionalContainer(
  242. Window(TokenListControl(get_bottom_toolbar_tokens,
  243. default_char=Char(' ', Token.Toolbar)),
  244. height=LayoutDimension.exact(1)),
  245. filter=~IsDone() & RendererHeightIsKnown())]
  246. else:
  247. toolbars = []
  248. def get_height(cli):
  249. # If there is an autocompletion menu to be shown, make sure that our
  250. # layout has at least a minimal height in order to display it.
  251. if reserve_space_for_menu and not cli.is_done:
  252. buff = cli.current_buffer
  253. # Reserve the space, either when there are completions, or when
  254. # `complete_while_typing` is true and we expect completions very
  255. # soon.
  256. if buff.complete_while_typing() or buff.complete_state is not None:
  257. return LayoutDimension(min=reserve_space_for_menu)
  258. return LayoutDimension()
  259. # Create and return Container instance.
  260. return HSplit([
  261. # The main input, with completion menus floating on top of it.
  262. FloatContainer(
  263. HSplit([
  264. ConditionalContainer(
  265. Window(
  266. TokenListControl(get_prompt_tokens_1),
  267. dont_extend_height=True),
  268. Condition(has_before_tokens)
  269. ),
  270. Window(
  271. BufferControl(
  272. input_processors=input_processors,
  273. lexer=lexer,
  274. # Enable preview_search, we want to have immediate feedback
  275. # in reverse-i-search mode.
  276. preview_search=True),
  277. get_height=get_height,
  278. left_margins=[
  279. # In multiline mode, use the window margin to display
  280. # the prompt and continuation tokens.
  281. ConditionalMargin(
  282. PromptMargin(get_prompt_tokens_2, get_continuation_tokens),
  283. filter=multiline
  284. )
  285. ],
  286. wrap_lines=wrap_lines,
  287. ),
  288. ]),
  289. [
  290. # Completion menus.
  291. Float(xcursor=True,
  292. ycursor=True,
  293. content=CompletionsMenu(
  294. max_height=16,
  295. scroll_offset=1,
  296. extra_filter=HasFocus(DEFAULT_BUFFER) &
  297. ~display_completions_in_columns)),
  298. Float(xcursor=True,
  299. ycursor=True,
  300. content=MultiColumnCompletionsMenu(
  301. extra_filter=HasFocus(DEFAULT_BUFFER) &
  302. display_completions_in_columns,
  303. show_meta=True)),
  304. # The right prompt.
  305. Float(right=0, top=0, hide_when_covering_content=True,
  306. content=_RPrompt(get_rprompt_tokens)),
  307. ]
  308. ),
  309. ValidationToolbar(),
  310. SystemToolbar(),
  311. # In multiline mode, we use two toolbars for 'arg' and 'search'.
  312. ConditionalContainer(ArgToolbar(), multiline),
  313. ConditionalContainer(SearchToolbar(), multiline),
  314. ] + toolbars)
  315. def create_prompt_application(
  316. message='',
  317. multiline=False,
  318. wrap_lines=True,
  319. is_password=False,
  320. vi_mode=False,
  321. editing_mode=EditingMode.EMACS,
  322. complete_while_typing=True,
  323. enable_history_search=False,
  324. lexer=None,
  325. enable_system_bindings=False,
  326. enable_open_in_editor=False,
  327. validator=None,
  328. completer=None,
  329. reserve_space_for_menu=8,
  330. auto_suggest=None,
  331. style=None,
  332. history=None,
  333. clipboard=None,
  334. get_prompt_tokens=None,
  335. get_continuation_tokens=None,
  336. get_rprompt_tokens=None,
  337. get_bottom_toolbar_tokens=None,
  338. display_completions_in_columns=False,
  339. get_title=None,
  340. mouse_support=False,
  341. extra_input_processors=None,
  342. key_bindings_registry=None,
  343. on_abort=AbortAction.RAISE_EXCEPTION,
  344. on_exit=AbortAction.RAISE_EXCEPTION,
  345. accept_action=AcceptAction.RETURN_DOCUMENT,
  346. erase_when_done=False,
  347. default=''):
  348. """
  349. Create an :class:`~Application` instance for a prompt.
  350. (It is meant to cover 90% of the prompt use cases, where no extreme
  351. customization is required. For more complex input, it is required to create
  352. a custom :class:`~Application` instance.)
  353. :param message: Text to be shown before the prompt.
  354. :param mulitiline: Allow multiline input. Pressing enter will insert a
  355. newline. (This requires Meta+Enter to accept the input.)
  356. :param wrap_lines: `bool` or :class:`~prompt_toolkit.filters.CLIFilter`.
  357. When True (the default), automatically wrap long lines instead of
  358. scrolling horizontally.
  359. :param is_password: Show asterisks instead of the actual typed characters.
  360. :param editing_mode: ``EditingMode.VI`` or ``EditingMode.EMACS``.
  361. :param vi_mode: `bool`, if True, Identical to ``editing_mode=EditingMode.VI``.
  362. :param complete_while_typing: `bool` or
  363. :class:`~prompt_toolkit.filters.SimpleFilter`. Enable autocompletion
  364. while typing.
  365. :param enable_history_search: `bool` or
  366. :class:`~prompt_toolkit.filters.SimpleFilter`. Enable up-arrow parting
  367. string matching.
  368. :param lexer: :class:`~prompt_toolkit.layout.lexers.Lexer` to be used for
  369. the syntax highlighting.
  370. :param validator: :class:`~prompt_toolkit.validation.Validator` instance
  371. for input validation.
  372. :param completer: :class:`~prompt_toolkit.completion.Completer` instance
  373. for input completion.
  374. :param reserve_space_for_menu: Space to be reserved for displaying the menu.
  375. (0 means that no space needs to be reserved.)
  376. :param auto_suggest: :class:`~prompt_toolkit.auto_suggest.AutoSuggest`
  377. instance for input suggestions.
  378. :param style: :class:`.Style` instance for the color scheme.
  379. :param enable_system_bindings: `bool` or
  380. :class:`~prompt_toolkit.filters.CLIFilter`. Pressing Meta+'!' will show
  381. a system prompt.
  382. :param enable_open_in_editor: `bool` or
  383. :class:`~prompt_toolkit.filters.CLIFilter`. Pressing 'v' in Vi mode or
  384. C-X C-E in emacs mode will open an external editor.
  385. :param history: :class:`~prompt_toolkit.history.History` instance.
  386. :param clipboard: :class:`~prompt_toolkit.clipboard.base.Clipboard` instance.
  387. (e.g. :class:`~prompt_toolkit.clipboard.in_memory.InMemoryClipboard`)
  388. :param get_bottom_toolbar_tokens: Optional callable which takes a
  389. :class:`~prompt_toolkit.interface.CommandLineInterface` and returns a
  390. list of tokens for the bottom toolbar.
  391. :param display_completions_in_columns: `bool` or
  392. :class:`~prompt_toolkit.filters.CLIFilter`. Display the completions in
  393. multiple columns.
  394. :param get_title: Callable that returns the title to be displayed in the
  395. terminal.
  396. :param mouse_support: `bool` or :class:`~prompt_toolkit.filters.CLIFilter`
  397. to enable mouse support.
  398. :param default: The default text to be shown in the input buffer. (This can
  399. be edited by the user.)
  400. """
  401. if key_bindings_registry is None:
  402. key_bindings_registry = load_key_bindings_for_prompt(
  403. enable_system_bindings=enable_system_bindings,
  404. enable_open_in_editor=enable_open_in_editor)
  405. # Ensure backwards-compatibility, when `vi_mode` is passed.
  406. if vi_mode:
  407. editing_mode = EditingMode.VI
  408. # Make sure that complete_while_typing is disabled when enable_history_search
  409. # is enabled. (First convert to SimpleFilter, to avoid doing bitwise operations
  410. # on bool objects.)
  411. complete_while_typing = to_simple_filter(complete_while_typing)
  412. enable_history_search = to_simple_filter(enable_history_search)
  413. multiline = to_simple_filter(multiline)
  414. complete_while_typing = complete_while_typing & ~enable_history_search
  415. # Accept Pygments styles as well for backwards compatibility.
  416. try:
  417. if pygments_Style and issubclass(style, pygments_Style):
  418. style = style_from_dict(style.styles)
  419. except TypeError: # Happens when style is `None` or an instance of something else.
  420. pass
  421. # Create application
  422. return Application(
  423. layout=create_prompt_layout(
  424. message=message,
  425. lexer=lexer,
  426. is_password=is_password,
  427. reserve_space_for_menu=(reserve_space_for_menu if completer is not None else 0),
  428. multiline=Condition(lambda cli: multiline()),
  429. get_prompt_tokens=get_prompt_tokens,
  430. get_continuation_tokens=get_continuation_tokens,
  431. get_rprompt_tokens=get_rprompt_tokens,
  432. get_bottom_toolbar_tokens=get_bottom_toolbar_tokens,
  433. display_completions_in_columns=display_completions_in_columns,
  434. extra_input_processors=extra_input_processors,
  435. wrap_lines=wrap_lines),
  436. buffer=Buffer(
  437. enable_history_search=enable_history_search,
  438. complete_while_typing=complete_while_typing,
  439. is_multiline=multiline,
  440. history=(history or InMemoryHistory()),
  441. validator=validator,
  442. completer=completer,
  443. auto_suggest=auto_suggest,
  444. accept_action=accept_action,
  445. initial_document=Document(default),
  446. ),
  447. style=style or DEFAULT_STYLE,
  448. clipboard=clipboard,
  449. key_bindings_registry=key_bindings_registry,
  450. get_title=get_title,
  451. mouse_support=mouse_support,
  452. editing_mode=editing_mode,
  453. erase_when_done=erase_when_done,
  454. reverse_vi_search_direction=True,
  455. on_abort=on_abort,
  456. on_exit=on_exit)
  457. def prompt(message='', **kwargs):
  458. """
  459. Get input from the user and return it.
  460. This is a wrapper around a lot of ``prompt_toolkit`` functionality and can
  461. be a replacement for `raw_input`. (or GNU readline.)
  462. If you want to keep your history across several calls, create one
  463. :class:`~prompt_toolkit.history.History` instance and pass it every time.
  464. This function accepts many keyword arguments. Except for the following,
  465. they are a proxy to the arguments of :func:`.create_prompt_application`.
  466. :param patch_stdout: Replace ``sys.stdout`` by a proxy that ensures that
  467. print statements from other threads won't destroy the prompt. (They
  468. will be printed above the prompt instead.)
  469. :param return_asyncio_coroutine: When True, return a asyncio coroutine. (Python >3.3)
  470. :param true_color: When True, use 24bit colors instead of 256 colors.
  471. :param refresh_interval: (number; in seconds) When given, refresh the UI
  472. every so many seconds.
  473. """
  474. patch_stdout = kwargs.pop('patch_stdout', False)
  475. return_asyncio_coroutine = kwargs.pop('return_asyncio_coroutine', False)
  476. true_color = kwargs.pop('true_color', False)
  477. refresh_interval = kwargs.pop('refresh_interval', 0)
  478. eventloop = kwargs.pop('eventloop', None)
  479. application = create_prompt_application(message, **kwargs)
  480. return run_application(application,
  481. patch_stdout=patch_stdout,
  482. return_asyncio_coroutine=return_asyncio_coroutine,
  483. true_color=true_color,
  484. refresh_interval=refresh_interval,
  485. eventloop=eventloop)
  486. def run_application(
  487. application, patch_stdout=False, return_asyncio_coroutine=False,
  488. true_color=False, refresh_interval=0, eventloop=None):
  489. """
  490. Run a prompt toolkit application.
  491. :param patch_stdout: Replace ``sys.stdout`` by a proxy that ensures that
  492. print statements from other threads won't destroy the prompt. (They
  493. will be printed above the prompt instead.)
  494. :param return_asyncio_coroutine: When True, return a asyncio coroutine. (Python >3.3)
  495. :param true_color: When True, use 24bit colors instead of 256 colors.
  496. :param refresh_interval: (number; in seconds) When given, refresh the UI
  497. every so many seconds.
  498. """
  499. assert isinstance(application, Application)
  500. if return_asyncio_coroutine:
  501. eventloop = create_asyncio_eventloop()
  502. else:
  503. eventloop = eventloop or create_eventloop()
  504. # Create CommandLineInterface.
  505. cli = CommandLineInterface(
  506. application=application,
  507. eventloop=eventloop,
  508. output=create_output(true_color=true_color))
  509. # Set up refresh interval.
  510. if refresh_interval:
  511. done = [False]
  512. def start_refresh_loop(cli):
  513. def run():
  514. while not done[0]:
  515. time.sleep(refresh_interval)
  516. cli.request_redraw()
  517. t = threading.Thread(target=run)
  518. t.daemon = True
  519. t.start()
  520. def stop_refresh_loop(cli):
  521. done[0] = True
  522. cli.on_start += start_refresh_loop
  523. cli.on_stop += stop_refresh_loop
  524. # Replace stdout.
  525. patch_context = cli.patch_stdout_context(raw=True) if patch_stdout else DummyContext()
  526. # Read input and return it.
  527. if return_asyncio_coroutine:
  528. # Create an asyncio coroutine and call it.
  529. exec_context = {'patch_context': patch_context, 'cli': cli,
  530. 'Document': Document}
  531. exec_(textwrap.dedent('''
  532. def prompt_coro():
  533. # Inline import, because it slows down startup when asyncio is not
  534. # needed.
  535. import asyncio
  536. @asyncio.coroutine
  537. def run():
  538. with patch_context:
  539. result = yield from cli.run_async()
  540. if isinstance(result, Document): # Backwards-compatibility.
  541. return result.text
  542. return result
  543. return run()
  544. '''), exec_context)
  545. return exec_context['prompt_coro']()
  546. else:
  547. try:
  548. with patch_context:
  549. result = cli.run()
  550. if isinstance(result, Document): # Backwards-compatibility.
  551. return result.text
  552. return result
  553. finally:
  554. eventloop.close()
  555. def prompt_async(message='', **kwargs):
  556. """
  557. Similar to :func:`.prompt`, but return an asyncio coroutine instead.
  558. """
  559. kwargs['return_asyncio_coroutine'] = True
  560. return prompt(message, **kwargs)
  561. def create_confirm_application(message):
  562. """
  563. Create a confirmation `Application` that returns True/False.
  564. """
  565. registry = Registry()
  566. @registry.add_binding('y')
  567. @registry.add_binding('Y')
  568. def _(event):
  569. event.cli.buffers[DEFAULT_BUFFER].text = 'y'
  570. event.cli.set_return_value(True)
  571. @registry.add_binding('n')
  572. @registry.add_binding('N')
  573. @registry.add_binding(Keys.ControlC)
  574. def _(event):
  575. event.cli.buffers[DEFAULT_BUFFER].text = 'n'
  576. event.cli.set_return_value(False)
  577. return create_prompt_application(message, key_bindings_registry=registry)
  578. def confirm(message='Confirm (y or n) '):
  579. """
  580. Display a confirmation prompt.
  581. """
  582. assert isinstance(message, text_type)
  583. app = create_confirm_application(message)
  584. return run_application(app)
  585. def print_tokens(tokens, style=None, true_color=False, file=None):
  586. """
  587. Print a list of (Token, text) tuples in the given style to the output.
  588. E.g.::
  589. style = style_from_dict({
  590. Token.Hello: '#ff0066',
  591. Token.World: '#884444 italic',
  592. })
  593. tokens = [
  594. (Token.Hello, 'Hello'),
  595. (Token.World, 'World'),
  596. ]
  597. print_tokens(tokens, style=style)
  598. :param tokens: List of ``(Token, text)`` tuples.
  599. :param style: :class:`.Style` instance for the color scheme.
  600. :param true_color: When True, use 24bit colors instead of 256 colors.
  601. :param file: The output file. This can be `sys.stdout` or `sys.stderr`.
  602. """
  603. if style is None:
  604. style = DEFAULT_STYLE
  605. assert isinstance(style, Style)
  606. output = create_output(true_color=true_color, stdout=file)
  607. renderer_print_tokens(output, tokens, style)
  608. def clear():
  609. """
  610. Clear the screen.
  611. """
  612. out = create_output()
  613. out.erase_screen()
  614. out.cursor_goto(0, 0)
  615. out.flush()
  616. # Deprecated alias for `prompt`.
  617. get_input = prompt
  618. # Deprecated alias for create_prompt_layout
  619. create_default_layout = create_prompt_layout
  620. # Deprecated alias for create_prompt_application
  621. create_default_application = create_prompt_application