Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/prompt_toolkit/shortcuts/prompt.py: 18%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

483 statements  

1""" 

2Line editing functionality. 

3--------------------------- 

4 

5This provides a UI for a line input, similar to GNU Readline, libedit and 

6linenoise. 

7 

8Either call the `prompt` function for every line input. Or create an instance 

9of the :class:`.PromptSession` class and call the `prompt` method from that 

10class. In the second case, we'll have a 'session' that keeps all the state like 

11the history in between several calls. 

12 

13There is a lot of overlap between the arguments taken by the `prompt` function 

14and the `PromptSession` (like `completer`, `style`, etcetera). There we have 

15the freedom to decide which settings we want for the whole 'session', and which 

16we want for an individual `prompt`. 

17 

18Example:: 

19 

20 # Simple `prompt` call. 

21 result = prompt('Say something: ') 

22 

23 # Using a 'session'. 

24 s = PromptSession() 

25 result = s.prompt('Say something: ') 

26""" 

27 

28from __future__ import annotations 

29 

30from asyncio import get_running_loop 

31from collections.abc import Callable, Iterator 

32from contextlib import contextmanager 

33from enum import Enum 

34from functools import partial 

35from typing import TYPE_CHECKING, Generic, TypeVar, Union, cast 

36 

37from prompt_toolkit.application import Application 

38from prompt_toolkit.application.current import get_app 

39from prompt_toolkit.auto_suggest import AutoSuggest, DynamicAutoSuggest 

40from prompt_toolkit.buffer import Buffer 

41from prompt_toolkit.clipboard import Clipboard, DynamicClipboard, InMemoryClipboard 

42from prompt_toolkit.completion import Completer, DynamicCompleter, ThreadedCompleter 

43from prompt_toolkit.cursor_shapes import ( 

44 AnyCursorShapeConfig, 

45 CursorShapeConfig, 

46 DynamicCursorShapeConfig, 

47) 

48from prompt_toolkit.document import Document 

49from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER, EditingMode 

50from prompt_toolkit.eventloop import InputHook 

51from prompt_toolkit.filters import ( 

52 Condition, 

53 FilterOrBool, 

54 has_arg, 

55 has_focus, 

56 is_done, 

57 is_true, 

58 renderer_height_is_known, 

59 to_filter, 

60) 

61from prompt_toolkit.formatted_text import ( 

62 AnyFormattedText, 

63 StyleAndTextTuples, 

64 fragment_list_to_text, 

65 merge_formatted_text, 

66 to_formatted_text, 

67) 

68from prompt_toolkit.history import History, InMemoryHistory 

69from prompt_toolkit.input.base import Input 

70from prompt_toolkit.key_binding.bindings.auto_suggest import load_auto_suggest_bindings 

71from prompt_toolkit.key_binding.bindings.completion import ( 

72 display_completions_like_readline, 

73) 

74from prompt_toolkit.key_binding.bindings.open_in_editor import ( 

75 load_open_in_editor_bindings, 

76) 

77from prompt_toolkit.key_binding.key_bindings import ( 

78 ConditionalKeyBindings, 

79 DynamicKeyBindings, 

80 KeyBindings, 

81 KeyBindingsBase, 

82 merge_key_bindings, 

83) 

84from prompt_toolkit.key_binding.key_processor import KeyPressEvent 

85from prompt_toolkit.keys import Keys 

86from prompt_toolkit.layout import Float, FloatContainer, HSplit, Window 

87from prompt_toolkit.layout.containers import ConditionalContainer, WindowAlign 

88from prompt_toolkit.layout.controls import ( 

89 BufferControl, 

90 FormattedTextControl, 

91 SearchBufferControl, 

92) 

93from prompt_toolkit.layout.dimension import Dimension 

94from prompt_toolkit.layout.layout import Layout 

95from prompt_toolkit.layout.menus import CompletionsMenu, MultiColumnCompletionsMenu 

96from prompt_toolkit.layout.processors import ( 

97 AfterInput, 

98 AppendAutoSuggestion, 

99 ConditionalProcessor, 

100 DisplayMultipleCursors, 

101 DynamicProcessor, 

102 HighlightIncrementalSearchProcessor, 

103 HighlightSelectionProcessor, 

104 PasswordProcessor, 

105 Processor, 

106 ReverseSearchProcessor, 

107 merge_processors, 

108) 

109from prompt_toolkit.layout.utils import explode_text_fragments 

110from prompt_toolkit.lexers import DynamicLexer, Lexer 

111from prompt_toolkit.output import ColorDepth, DummyOutput, Output 

112from prompt_toolkit.styles import ( 

113 BaseStyle, 

114 ConditionalStyleTransformation, 

115 DynamicStyle, 

116 DynamicStyleTransformation, 

117 StyleTransformation, 

118 SwapLightAndDarkStyleTransformation, 

119 merge_style_transformations, 

120) 

121from prompt_toolkit.utils import ( 

122 get_cwidth, 

123 is_dumb_terminal, 

124 suspend_to_background_supported, 

125 to_str, 

126) 

127from prompt_toolkit.validation import DynamicValidator, Validator 

128from prompt_toolkit.widgets import Frame 

129from prompt_toolkit.widgets.toolbars import ( 

130 SearchToolbar, 

131 SystemToolbar, 

132 ValidationToolbar, 

133) 

134 

135if TYPE_CHECKING: 

136 from prompt_toolkit.formatted_text.base import MagicFormattedText 

137 

138__all__ = [ 

139 "PromptSession", 

140 "prompt", 

141 "confirm", 

142 "create_confirm_session", # Used by '_display_completions_like_readline'. 

143 "CompleteStyle", 

144] 

145 

146_StyleAndTextTuplesCallable = Callable[[], StyleAndTextTuples] 

147E = KeyPressEvent 

148 

149 

150def _split_multiline_prompt( 

151 get_prompt_text: _StyleAndTextTuplesCallable, 

152) -> tuple[ 

153 Callable[[], bool], _StyleAndTextTuplesCallable, _StyleAndTextTuplesCallable 

154]: 

155 """ 

156 Take a `get_prompt_text` function and return three new functions instead. 

157 One that tells whether this prompt consists of multiple lines; one that 

158 returns the fragments to be shown on the lines above the input; and another 

159 one with the fragments to be shown at the first line of the input. 

160 """ 

161 

162 def has_before_fragments() -> bool: 

163 for fragment, char, *_ in get_prompt_text(): 

164 if "\n" in char: 

165 return True 

166 return False 

167 

168 def before() -> StyleAndTextTuples: 

169 result: StyleAndTextTuples = [] 

170 found_nl = False 

171 for fragment, char, *_ in reversed(explode_text_fragments(get_prompt_text())): 

172 if found_nl: 

173 result.insert(0, (fragment, char)) 

174 elif char == "\n": 

175 found_nl = True 

176 return result 

177 

178 def first_input_line() -> StyleAndTextTuples: 

179 result: StyleAndTextTuples = [] 

180 for fragment, char, *_ in reversed(explode_text_fragments(get_prompt_text())): 

181 if char == "\n": 

182 break 

183 else: 

184 result.insert(0, (fragment, char)) 

185 return result 

186 

187 return has_before_fragments, before, first_input_line 

188 

189 

190class _RPrompt(Window): 

191 """ 

192 The prompt that is displayed on the right side of the Window. 

193 """ 

194 

195 def __init__(self, text: AnyFormattedText) -> None: 

196 super().__init__( 

197 FormattedTextControl(text=text), 

198 align=WindowAlign.RIGHT, 

199 style="class:rprompt", 

200 ) 

201 

202 

203class CompleteStyle(str, Enum): 

204 """ 

205 How to display autocompletions for the prompt. 

206 """ 

207 

208 value: str 

209 

210 COLUMN = "COLUMN" 

211 MULTI_COLUMN = "MULTI_COLUMN" 

212 READLINE_LIKE = "READLINE_LIKE" 

213 

214 

215# Formatted text for the continuation prompt. It's the same like other 

216# formatted text, except that if it's a callable, it takes three arguments. 

217PromptContinuationText = Union[ 

218 str, 

219 "MagicFormattedText", 

220 StyleAndTextTuples, 

221 # (prompt_width, line_number, wrap_count) -> AnyFormattedText. 

222 Callable[[int, int, int], AnyFormattedText], 

223] 

224 

225_T = TypeVar("_T") 

226 

227 

228class PromptSession(Generic[_T]): 

229 """ 

230 PromptSession for a prompt application, which can be used as a GNU Readline 

231 replacement. 

232 

233 This is a wrapper around a lot of ``prompt_toolkit`` functionality and can 

234 be a replacement for `raw_input`. 

235 

236 All parameters that expect "formatted text" can take either just plain text 

237 (a unicode object), a list of ``(style_str, text)`` tuples or an HTML object. 

238 

239 Example usage:: 

240 

241 s = PromptSession(message='>') 

242 text = s.prompt() 

243 

244 :param message: Plain text or formatted text to be shown before the prompt. 

245 This can also be a callable that returns formatted text. 

246 :param multiline: `bool` or :class:`~prompt_toolkit.filters.Filter`. 

247 When True, prefer a layout that is more adapted for multiline input. 

248 Text after newlines is automatically indented, and search/arg input is 

249 shown below the input, instead of replacing the prompt. 

250 :param wrap_lines: `bool` or :class:`~prompt_toolkit.filters.Filter`. 

251 When True (the default), automatically wrap long lines instead of 

252 scrolling horizontally. 

253 :param is_password: Show asterisks instead of the actual typed characters. 

254 :param editing_mode: ``EditingMode.VI`` or ``EditingMode.EMACS``. 

255 :param vi_mode: `bool`, if True, Identical to ``editing_mode=EditingMode.VI``. 

256 :param complete_while_typing: `bool` or 

257 :class:`~prompt_toolkit.filters.Filter`. Enable autocompletion while 

258 typing. 

259 :param validate_while_typing: `bool` or 

260 :class:`~prompt_toolkit.filters.Filter`. Enable input validation while 

261 typing. 

262 :param enable_history_search: `bool` or 

263 :class:`~prompt_toolkit.filters.Filter`. Enable up-arrow parting 

264 string matching. 

265 :param search_ignore_case: 

266 :class:`~prompt_toolkit.filters.Filter`. Search case insensitive. 

267 :param lexer: :class:`~prompt_toolkit.lexers.Lexer` to be used for the 

268 syntax highlighting. 

269 :param validator: :class:`~prompt_toolkit.validation.Validator` instance 

270 for input validation. 

271 :param completer: :class:`~prompt_toolkit.completion.Completer` instance 

272 for input completion. 

273 :param complete_in_thread: `bool` or 

274 :class:`~prompt_toolkit.filters.Filter`. Run the completer code in a 

275 background thread in order to avoid blocking the user interface. 

276 For ``CompleteStyle.READLINE_LIKE``, this setting has no effect. There 

277 we always run the completions in the main thread. 

278 :param reserve_space_for_menu: Space to be reserved for displaying the menu. 

279 (0 means that no space needs to be reserved.) 

280 :param auto_suggest: :class:`~prompt_toolkit.auto_suggest.AutoSuggest` 

281 instance for input suggestions. 

282 :param style: :class:`.Style` instance for the color scheme. 

283 :param include_default_pygments_style: `bool` or 

284 :class:`~prompt_toolkit.filters.Filter`. Tell whether the default 

285 styling for Pygments lexers has to be included. By default, this is 

286 true, but it is recommended to be disabled if another Pygments style is 

287 passed as the `style` argument, otherwise, two Pygments styles will be 

288 merged. 

289 :param style_transformation: 

290 :class:`~prompt_toolkit.style.StyleTransformation` instance. 

291 :param swap_light_and_dark_colors: `bool` or 

292 :class:`~prompt_toolkit.filters.Filter`. When enabled, apply 

293 :class:`~prompt_toolkit.style.SwapLightAndDarkStyleTransformation`. 

294 This is useful for switching between dark and light terminal 

295 backgrounds. 

296 :param enable_system_prompt: `bool` or 

297 :class:`~prompt_toolkit.filters.Filter`. Pressing Meta+'!' will show 

298 a system prompt. 

299 :param enable_suspend: `bool` or :class:`~prompt_toolkit.filters.Filter`. 

300 Enable Control-Z style suspension. 

301 :param enable_open_in_editor: `bool` or 

302 :class:`~prompt_toolkit.filters.Filter`. Pressing 'v' in Vi mode or 

303 C-X C-E in emacs mode will open an external editor. 

304 :param history: :class:`~prompt_toolkit.history.History` instance. 

305 :param clipboard: :class:`~prompt_toolkit.clipboard.Clipboard` instance. 

306 (e.g. :class:`~prompt_toolkit.clipboard.InMemoryClipboard`) 

307 :param rprompt: Text or formatted text to be displayed on the right side. 

308 This can also be a callable that returns (formatted) text. 

309 :param bottom_toolbar: Formatted text or callable that returns formatted 

310 text to be displayed at the bottom of the screen. 

311 :param prompt_continuation: Text that needs to be displayed for a multiline 

312 prompt continuation. This can either be formatted text or a callable 

313 that takes a `prompt_width`, `line_number` and `wrap_count` as input 

314 and returns formatted text. When this is `None` (the default), then 

315 `prompt_width` spaces will be used. 

316 :param complete_style: ``CompleteStyle.COLUMN``, 

317 ``CompleteStyle.MULTI_COLUMN`` or ``CompleteStyle.READLINE_LIKE``. 

318 :param mouse_support: `bool` or :class:`~prompt_toolkit.filters.Filter` 

319 to enable mouse support. 

320 :param placeholder: Text to be displayed when no input has been given 

321 yet. Unlike the `default` parameter, this won't be returned as part of 

322 the output ever. This can be formatted text or a callable that returns 

323 formatted text. 

324 :param show_frame: `bool` or 

325 :class:`~prompt_toolkit.filters.Filter`. When True, surround the input 

326 with a frame. 

327 :param refresh_interval: (number; in seconds) When given, refresh the UI 

328 every so many seconds. 

329 :param input: `Input` object. (Note that the preferred way to change the 

330 input/output is by creating an `AppSession`.) 

331 :param output: `Output` object. 

332 :param interrupt_exception: The exception type that will be raised when 

333 there is a keyboard interrupt (control-c keypress). 

334 :param eof_exception: The exception type that will be raised when there is 

335 an end-of-file/exit event (control-d keypress). 

336 """ 

337 

338 _fields = ( 

339 "message", 

340 "lexer", 

341 "completer", 

342 "complete_in_thread", 

343 "is_password", 

344 "editing_mode", 

345 "key_bindings", 

346 "is_password", 

347 "bottom_toolbar", 

348 "style", 

349 "style_transformation", 

350 "swap_light_and_dark_colors", 

351 "color_depth", 

352 "cursor", 

353 "include_default_pygments_style", 

354 "rprompt", 

355 "multiline", 

356 "prompt_continuation", 

357 "wrap_lines", 

358 "enable_history_search", 

359 "search_ignore_case", 

360 "complete_while_typing", 

361 "validate_while_typing", 

362 "complete_style", 

363 "mouse_support", 

364 "auto_suggest", 

365 "clipboard", 

366 "validator", 

367 "refresh_interval", 

368 "input_processors", 

369 "placeholder", 

370 "enable_system_prompt", 

371 "enable_suspend", 

372 "enable_open_in_editor", 

373 "reserve_space_for_menu", 

374 "tempfile_suffix", 

375 "tempfile", 

376 "show_frame", 

377 ) 

378 

379 def __init__( 

380 self, 

381 message: AnyFormattedText = "", 

382 *, 

383 multiline: FilterOrBool = False, 

384 wrap_lines: FilterOrBool = True, 

385 is_password: FilterOrBool = False, 

386 vi_mode: bool = False, 

387 editing_mode: EditingMode = EditingMode.EMACS, 

388 complete_while_typing: FilterOrBool = True, 

389 validate_while_typing: FilterOrBool = True, 

390 enable_history_search: FilterOrBool = False, 

391 search_ignore_case: FilterOrBool = False, 

392 lexer: Lexer | None = None, 

393 enable_system_prompt: FilterOrBool = False, 

394 enable_suspend: FilterOrBool = False, 

395 enable_open_in_editor: FilterOrBool = False, 

396 validator: Validator | None = None, 

397 completer: Completer | None = None, 

398 complete_in_thread: bool = False, 

399 reserve_space_for_menu: int = 8, 

400 complete_style: CompleteStyle = CompleteStyle.COLUMN, 

401 auto_suggest: AutoSuggest | None = None, 

402 style: BaseStyle | None = None, 

403 style_transformation: StyleTransformation | None = None, 

404 swap_light_and_dark_colors: FilterOrBool = False, 

405 color_depth: ColorDepth | None = None, 

406 cursor: AnyCursorShapeConfig = None, 

407 include_default_pygments_style: FilterOrBool = True, 

408 history: History | None = None, 

409 clipboard: Clipboard | None = None, 

410 prompt_continuation: PromptContinuationText | None = None, 

411 rprompt: AnyFormattedText = None, 

412 bottom_toolbar: AnyFormattedText = None, 

413 mouse_support: FilterOrBool = False, 

414 input_processors: list[Processor] | None = None, 

415 placeholder: AnyFormattedText | None = None, 

416 key_bindings: KeyBindingsBase | None = None, 

417 erase_when_done: bool = False, 

418 tempfile_suffix: str | Callable[[], str] | None = ".txt", 

419 tempfile: str | Callable[[], str] | None = None, 

420 refresh_interval: float = 0, 

421 show_frame: FilterOrBool = False, 

422 input: Input | None = None, 

423 output: Output | None = None, 

424 interrupt_exception: type[BaseException] = KeyboardInterrupt, 

425 eof_exception: type[BaseException] = EOFError, 

426 ) -> None: 

427 history = history or InMemoryHistory() 

428 clipboard = clipboard or InMemoryClipboard() 

429 

430 # Ensure backwards-compatibility, when `vi_mode` is passed. 

431 if vi_mode: 

432 editing_mode = EditingMode.VI 

433 

434 # Store all settings in this class. 

435 self._input = input 

436 self._output = output 

437 

438 # Store attributes. 

439 # (All except 'editing_mode'.) 

440 self.message = message 

441 self.lexer = lexer 

442 self.completer = completer 

443 self.complete_in_thread = complete_in_thread 

444 self.is_password = is_password 

445 self.key_bindings = key_bindings 

446 self.bottom_toolbar = bottom_toolbar 

447 self.style = style 

448 self.style_transformation = style_transformation 

449 self.swap_light_and_dark_colors = swap_light_and_dark_colors 

450 self.color_depth = color_depth 

451 self.cursor = cursor 

452 self.include_default_pygments_style = include_default_pygments_style 

453 self.rprompt = rprompt 

454 self.multiline = multiline 

455 self.prompt_continuation = prompt_continuation 

456 self.wrap_lines = wrap_lines 

457 self.enable_history_search = enable_history_search 

458 self.search_ignore_case = search_ignore_case 

459 self.complete_while_typing = complete_while_typing 

460 self.validate_while_typing = validate_while_typing 

461 self.complete_style = complete_style 

462 self.mouse_support = mouse_support 

463 self.auto_suggest = auto_suggest 

464 self.clipboard = clipboard 

465 self.validator = validator 

466 self.refresh_interval = refresh_interval 

467 self.input_processors = input_processors 

468 self.placeholder = placeholder 

469 self.enable_system_prompt = enable_system_prompt 

470 self.enable_suspend = enable_suspend 

471 self.enable_open_in_editor = enable_open_in_editor 

472 self.reserve_space_for_menu = reserve_space_for_menu 

473 self.tempfile_suffix = tempfile_suffix 

474 self.tempfile = tempfile 

475 self.show_frame = show_frame 

476 self.interrupt_exception = interrupt_exception 

477 self.eof_exception = eof_exception 

478 

479 # Create buffers, layout and Application. 

480 self.history = history 

481 self.default_buffer = self._create_default_buffer() 

482 self.search_buffer = self._create_search_buffer() 

483 self.layout = self._create_layout() 

484 self.app = self._create_application(editing_mode, erase_when_done) 

485 

486 def _dyncond(self, attr_name: str) -> Condition: 

487 """ 

488 Dynamically take this setting from this 'PromptSession' class. 

489 `attr_name` represents an attribute name of this class. Its value 

490 can either be a boolean or a `Filter`. 

491 

492 This returns something that can be used as either a `Filter` 

493 or `Filter`. 

494 """ 

495 

496 @Condition 

497 def dynamic() -> bool: 

498 value = cast(FilterOrBool, getattr(self, attr_name)) 

499 return to_filter(value)() 

500 

501 return dynamic 

502 

503 def _create_default_buffer(self) -> Buffer: 

504 """ 

505 Create and return the default input buffer. 

506 """ 

507 dyncond = self._dyncond 

508 

509 # Create buffers list. 

510 def accept(buff: Buffer) -> bool: 

511 """Accept the content of the default buffer. This is called when 

512 the validation succeeds.""" 

513 cast(Application[str], get_app()).exit( 

514 result=buff.document.text, style="class:accepted" 

515 ) 

516 return True # Keep text, we call 'reset' later on. 

517 

518 return Buffer( 

519 name=DEFAULT_BUFFER, 

520 # Make sure that complete_while_typing is disabled when 

521 # enable_history_search is enabled. (First convert to Filter, 

522 # to avoid doing bitwise operations on bool objects.) 

523 complete_while_typing=Condition( 

524 lambda: ( 

525 is_true(self.complete_while_typing) 

526 and not is_true(self.enable_history_search) 

527 and not self.complete_style == CompleteStyle.READLINE_LIKE 

528 ) 

529 ), 

530 validate_while_typing=dyncond("validate_while_typing"), 

531 enable_history_search=dyncond("enable_history_search"), 

532 validator=DynamicValidator(lambda: self.validator), 

533 completer=DynamicCompleter( 

534 lambda: ( 

535 ThreadedCompleter(self.completer) 

536 if self.complete_in_thread and self.completer 

537 else self.completer 

538 ) 

539 ), 

540 history=self.history, 

541 auto_suggest=DynamicAutoSuggest(lambda: self.auto_suggest), 

542 accept_handler=accept, 

543 tempfile_suffix=lambda: to_str(self.tempfile_suffix or ""), 

544 tempfile=lambda: to_str(self.tempfile or ""), 

545 ) 

546 

547 def _create_search_buffer(self) -> Buffer: 

548 return Buffer(name=SEARCH_BUFFER) 

549 

550 def _create_layout(self) -> Layout: 

551 """ 

552 Create `Layout` for this prompt. 

553 """ 

554 dyncond = self._dyncond 

555 

556 # Create functions that will dynamically split the prompt. (If we have 

557 # a multiline prompt.) 

558 ( 

559 has_before_fragments, 

560 get_prompt_text_1, 

561 get_prompt_text_2, 

562 ) = _split_multiline_prompt(self._get_prompt) 

563 

564 default_buffer = self.default_buffer 

565 search_buffer = self.search_buffer 

566 

567 # Create processors list. 

568 @Condition 

569 def display_placeholder() -> bool: 

570 return self.placeholder is not None and self.default_buffer.text == "" 

571 

572 all_input_processors = [ 

573 HighlightIncrementalSearchProcessor(), 

574 HighlightSelectionProcessor(), 

575 ConditionalProcessor( 

576 AppendAutoSuggestion(), has_focus(default_buffer) & ~is_done 

577 ), 

578 ConditionalProcessor(PasswordProcessor(), dyncond("is_password")), 

579 DisplayMultipleCursors(), 

580 # Users can insert processors here. 

581 DynamicProcessor(lambda: merge_processors(self.input_processors or [])), 

582 ConditionalProcessor( 

583 AfterInput(lambda: self.placeholder), 

584 filter=display_placeholder, 

585 ), 

586 ] 

587 

588 # Create bottom toolbars. 

589 bottom_toolbar = ConditionalContainer( 

590 Window( 

591 FormattedTextControl( 

592 lambda: self.bottom_toolbar, style="class:bottom-toolbar.text" 

593 ), 

594 style="class:bottom-toolbar", 

595 dont_extend_height=True, 

596 height=Dimension(min=1), 

597 ), 

598 filter=Condition(lambda: self.bottom_toolbar is not None) 

599 & ~is_done 

600 & renderer_height_is_known, 

601 ) 

602 

603 search_toolbar = SearchToolbar( 

604 search_buffer, ignore_case=dyncond("search_ignore_case") 

605 ) 

606 

607 search_buffer_control = SearchBufferControl( 

608 buffer=search_buffer, 

609 input_processors=[ReverseSearchProcessor()], 

610 ignore_case=dyncond("search_ignore_case"), 

611 ) 

612 

613 system_toolbar = SystemToolbar( 

614 enable_global_bindings=dyncond("enable_system_prompt") 

615 ) 

616 

617 def get_search_buffer_control() -> SearchBufferControl: 

618 "Return the UIControl to be focused when searching start." 

619 if is_true(self.multiline): 

620 return search_toolbar.control 

621 else: 

622 return search_buffer_control 

623 

624 default_buffer_control = BufferControl( 

625 buffer=default_buffer, 

626 search_buffer_control=get_search_buffer_control, 

627 input_processors=all_input_processors, 

628 include_default_input_processors=False, 

629 lexer=DynamicLexer(lambda: self.lexer), 

630 preview_search=True, 

631 ) 

632 

633 default_buffer_window = Window( 

634 default_buffer_control, 

635 height=self._get_default_buffer_control_height, 

636 get_line_prefix=partial( 

637 self._get_line_prefix, get_prompt_text_2=get_prompt_text_2 

638 ), 

639 wrap_lines=dyncond("wrap_lines"), 

640 ) 

641 

642 @Condition 

643 def multi_column_complete_style() -> bool: 

644 return self.complete_style == CompleteStyle.MULTI_COLUMN 

645 

646 # Build the layout. 

647 

648 # The main input, with completion menus floating on top of it. 

649 main_input_container = FloatContainer( 

650 HSplit( 

651 [ 

652 ConditionalContainer( 

653 Window( 

654 FormattedTextControl(get_prompt_text_1), 

655 dont_extend_height=True, 

656 ), 

657 Condition(has_before_fragments), 

658 ), 

659 ConditionalContainer( 

660 default_buffer_window, 

661 Condition( 

662 lambda: ( 

663 get_app().layout.current_control 

664 != search_buffer_control 

665 ) 

666 ), 

667 ), 

668 ConditionalContainer( 

669 Window(search_buffer_control), 

670 Condition( 

671 lambda: ( 

672 get_app().layout.current_control 

673 == search_buffer_control 

674 ) 

675 ), 

676 ), 

677 ] 

678 ), 

679 [ 

680 # Completion menus. 

681 # NOTE: Especially the multi-column menu needs to be 

682 # transparent, because the shape is not always 

683 # rectangular due to the meta-text below the menu. 

684 Float( 

685 xcursor=True, 

686 ycursor=True, 

687 transparent=True, 

688 content=CompletionsMenu( 

689 max_height=16, 

690 scroll_offset=1, 

691 extra_filter=has_focus(default_buffer) 

692 & ~multi_column_complete_style, 

693 ), 

694 ), 

695 Float( 

696 xcursor=True, 

697 ycursor=True, 

698 transparent=True, 

699 content=MultiColumnCompletionsMenu( 

700 show_meta=True, 

701 extra_filter=has_focus(default_buffer) 

702 & multi_column_complete_style, 

703 ), 

704 ), 

705 # The right prompt. 

706 Float( 

707 right=0, 

708 top=0, 

709 hide_when_covering_content=True, 

710 content=_RPrompt(lambda: self.rprompt), 

711 ), 

712 ], 

713 ) 

714 

715 layout = HSplit( 

716 [ 

717 # Wrap the main input in a frame, if requested. 

718 ConditionalContainer( 

719 Frame(main_input_container), 

720 filter=dyncond("show_frame"), 

721 alternative_content=main_input_container, 

722 ), 

723 ConditionalContainer(ValidationToolbar(), filter=~is_done), 

724 ConditionalContainer( 

725 system_toolbar, dyncond("enable_system_prompt") & ~is_done 

726 ), 

727 # In multiline mode, we use two toolbars for 'arg' and 'search'. 

728 ConditionalContainer( 

729 Window(FormattedTextControl(self._get_arg_text), height=1), 

730 dyncond("multiline") & has_arg, 

731 ), 

732 ConditionalContainer(search_toolbar, dyncond("multiline") & ~is_done), 

733 bottom_toolbar, 

734 ] 

735 ) 

736 

737 return Layout(layout, default_buffer_window) 

738 

739 def _create_application( 

740 self, editing_mode: EditingMode, erase_when_done: bool 

741 ) -> Application[_T]: 

742 """ 

743 Create the `Application` object. 

744 """ 

745 dyncond = self._dyncond 

746 

747 # Default key bindings. 

748 auto_suggest_bindings = load_auto_suggest_bindings() 

749 open_in_editor_bindings = load_open_in_editor_bindings() 

750 prompt_bindings = self._create_prompt_bindings() 

751 

752 # Create application 

753 application: Application[_T] = Application( 

754 layout=self.layout, 

755 style=DynamicStyle(lambda: self.style), 

756 style_transformation=merge_style_transformations( 

757 [ 

758 DynamicStyleTransformation(lambda: self.style_transformation), 

759 ConditionalStyleTransformation( 

760 SwapLightAndDarkStyleTransformation(), 

761 dyncond("swap_light_and_dark_colors"), 

762 ), 

763 ] 

764 ), 

765 include_default_pygments_style=dyncond("include_default_pygments_style"), 

766 clipboard=DynamicClipboard(lambda: self.clipboard), 

767 key_bindings=merge_key_bindings( 

768 [ 

769 merge_key_bindings( 

770 [ 

771 auto_suggest_bindings, 

772 ConditionalKeyBindings( 

773 open_in_editor_bindings, 

774 dyncond("enable_open_in_editor") 

775 & has_focus(DEFAULT_BUFFER), 

776 ), 

777 prompt_bindings, 

778 ] 

779 ), 

780 DynamicKeyBindings(lambda: self.key_bindings), 

781 ] 

782 ), 

783 mouse_support=dyncond("mouse_support"), 

784 editing_mode=editing_mode, 

785 erase_when_done=erase_when_done, 

786 reverse_vi_search_direction=True, 

787 color_depth=lambda: self.color_depth, 

788 cursor=DynamicCursorShapeConfig(lambda: self.cursor), 

789 refresh_interval=self.refresh_interval, 

790 input=self._input, 

791 output=self._output, 

792 ) 

793 

794 # During render time, make sure that we focus the right search control 

795 # (if we are searching). - This could be useful if people make the 

796 # 'multiline' property dynamic. 

797 """ 

798 def on_render(app): 

799 multiline = is_true(self.multiline) 

800 current_control = app.layout.current_control 

801 

802 if multiline: 

803 if current_control == search_buffer_control: 

804 app.layout.current_control = search_toolbar.control 

805 app.invalidate() 

806 else: 

807 if current_control == search_toolbar.control: 

808 app.layout.current_control = search_buffer_control 

809 app.invalidate() 

810 

811 app.on_render += on_render 

812 """ 

813 

814 return application 

815 

816 def _create_prompt_bindings(self) -> KeyBindings: 

817 """ 

818 Create the KeyBindings for a prompt application. 

819 """ 

820 kb = KeyBindings() 

821 handle = kb.add 

822 default_focused = has_focus(DEFAULT_BUFFER) 

823 

824 @Condition 

825 def do_accept() -> bool: 

826 return not is_true(self.multiline) and self.app.layout.has_focus( 

827 DEFAULT_BUFFER 

828 ) 

829 

830 @handle("enter", filter=do_accept & default_focused) 

831 def _accept_input(event: E) -> None: 

832 "Accept input when enter has been pressed." 

833 self.default_buffer.validate_and_handle() 

834 

835 @Condition 

836 def readline_complete_style() -> bool: 

837 return self.complete_style == CompleteStyle.READLINE_LIKE 

838 

839 @handle("tab", filter=readline_complete_style & default_focused) 

840 def _complete_like_readline(event: E) -> None: 

841 "Display completions (like Readline)." 

842 display_completions_like_readline(event) 

843 

844 @handle("c-c", filter=default_focused) 

845 @handle("<sigint>") 

846 def _keyboard_interrupt(event: E) -> None: 

847 "Abort when Control-C has been pressed." 

848 event.app.exit(exception=self.interrupt_exception(), style="class:aborting") 

849 

850 @Condition 

851 def ctrl_d_condition() -> bool: 

852 """Ctrl-D binding is only active when the default buffer is selected 

853 and empty.""" 

854 app = get_app() 

855 return ( 

856 app.current_buffer.name == DEFAULT_BUFFER 

857 and not app.current_buffer.text 

858 ) 

859 

860 @handle("c-d", filter=ctrl_d_condition & default_focused) 

861 def _eof(event: E) -> None: 

862 "Exit when Control-D has been pressed." 

863 event.app.exit(exception=self.eof_exception(), style="class:exiting") 

864 

865 suspend_supported = Condition(suspend_to_background_supported) 

866 

867 @Condition 

868 def enable_suspend() -> bool: 

869 return to_filter(self.enable_suspend)() 

870 

871 @handle("c-z", filter=suspend_supported & enable_suspend) 

872 def _suspend(event: E) -> None: 

873 """ 

874 Suspend process to background. 

875 """ 

876 event.app.suspend_to_background() 

877 

878 return kb 

879 

880 def prompt( 

881 self, 

882 # When any of these arguments are passed, this value is overwritten 

883 # in this PromptSession. 

884 message: AnyFormattedText | None = None, 

885 # `message` should go first, because people call it as 

886 # positional argument. 

887 *, 

888 editing_mode: EditingMode | None = None, 

889 refresh_interval: float | None = None, 

890 vi_mode: bool | None = None, 

891 lexer: Lexer | None = None, 

892 completer: Completer | None = None, 

893 complete_in_thread: bool | None = None, 

894 is_password: bool | None = None, 

895 key_bindings: KeyBindingsBase | None = None, 

896 bottom_toolbar: AnyFormattedText | None = None, 

897 style: BaseStyle | None = None, 

898 color_depth: ColorDepth | None = None, 

899 cursor: AnyCursorShapeConfig | None = None, 

900 include_default_pygments_style: FilterOrBool | None = None, 

901 style_transformation: StyleTransformation | None = None, 

902 swap_light_and_dark_colors: FilterOrBool | None = None, 

903 rprompt: AnyFormattedText | None = None, 

904 multiline: FilterOrBool | None = None, 

905 prompt_continuation: PromptContinuationText | None = None, 

906 wrap_lines: FilterOrBool | None = None, 

907 enable_history_search: FilterOrBool | None = None, 

908 search_ignore_case: FilterOrBool | None = None, 

909 complete_while_typing: FilterOrBool | None = None, 

910 validate_while_typing: FilterOrBool | None = None, 

911 complete_style: CompleteStyle | None = None, 

912 auto_suggest: AutoSuggest | None = None, 

913 validator: Validator | None = None, 

914 clipboard: Clipboard | None = None, 

915 mouse_support: FilterOrBool | None = None, 

916 input_processors: list[Processor] | None = None, 

917 placeholder: AnyFormattedText | None = None, 

918 reserve_space_for_menu: int | None = None, 

919 enable_system_prompt: FilterOrBool | None = None, 

920 enable_suspend: FilterOrBool | None = None, 

921 enable_open_in_editor: FilterOrBool | None = None, 

922 tempfile_suffix: str | Callable[[], str] | None = None, 

923 tempfile: str | Callable[[], str] | None = None, 

924 show_frame: FilterOrBool | None = None, 

925 # Following arguments are specific to the current `prompt()` call. 

926 default: str | Document = "", 

927 accept_default: bool = False, 

928 pre_run: Callable[[], None] | None = None, 

929 set_exception_handler: bool = True, 

930 handle_sigint: bool = True, 

931 in_thread: bool = False, 

932 inputhook: InputHook | None = None, 

933 ) -> _T: 

934 """ 

935 Display the prompt. 

936 

937 The first set of arguments is a subset of the :class:`~.PromptSession` 

938 class itself. For these, passing in ``None`` will keep the current 

939 values that are active in the session. Passing in a value will set the 

940 attribute for the session, which means that it applies to the current, 

941 but also to the next prompts. 

942 

943 Note that in order to erase a ``Completer``, ``Validator`` or 

944 ``AutoSuggest``, you can't use ``None``. Instead pass in a 

945 ``DummyCompleter``, ``DummyValidator`` or ``DummyAutoSuggest`` instance 

946 respectively. For a ``Lexer`` you can pass in an empty ``SimpleLexer``. 

947 

948 Additional arguments, specific for this prompt: 

949 

950 :param default: The default input text to be shown. (This can be edited 

951 by the user). 

952 :param accept_default: When `True`, automatically accept the default 

953 value without allowing the user to edit the input. 

954 :param pre_run: Callable, called at the start of `Application.run`. 

955 :param in_thread: Run the prompt in a background thread; block the 

956 current thread. This avoids interference with an event loop in the 

957 current thread. Like `Application.run(in_thread=True)`. 

958 

959 This method will raise ``KeyboardInterrupt`` when control-c has been 

960 pressed (for abort) and ``EOFError`` when control-d has been pressed 

961 (for exit). 

962 """ 

963 # NOTE: We used to create a backup of the PromptSession attributes and 

964 # restore them after exiting the prompt. This code has been 

965 # removed, because it was confusing and didn't really serve a use 

966 # case. (People were changing `Application.editing_mode` 

967 # dynamically and surprised that it was reset after every call.) 

968 

969 # NOTE 2: YES, this is a lot of repeation below... 

970 # However, it is a very convenient for a user to accept all 

971 # these parameters in this `prompt` method as well. We could 

972 # use `locals()` and `setattr` to avoid the repetition, but 

973 # then we loose the advantage of mypy and pyflakes to be able 

974 # to verify the code. 

975 if message is not None: 

976 self.message = message 

977 if editing_mode is not None: 

978 self.editing_mode = editing_mode 

979 if refresh_interval is not None: 

980 self.refresh_interval = refresh_interval 

981 if vi_mode: 

982 self.editing_mode = EditingMode.VI 

983 if lexer is not None: 

984 self.lexer = lexer 

985 if completer is not None: 

986 self.completer = completer 

987 if complete_in_thread is not None: 

988 self.complete_in_thread = complete_in_thread 

989 if is_password is not None: 

990 self.is_password = is_password 

991 if key_bindings is not None: 

992 self.key_bindings = key_bindings 

993 if bottom_toolbar is not None: 

994 self.bottom_toolbar = bottom_toolbar 

995 if style is not None: 

996 self.style = style 

997 if color_depth is not None: 

998 self.color_depth = color_depth 

999 if cursor is not None: 

1000 self.cursor = cursor 

1001 if include_default_pygments_style is not None: 

1002 self.include_default_pygments_style = include_default_pygments_style 

1003 if style_transformation is not None: 

1004 self.style_transformation = style_transformation 

1005 if swap_light_and_dark_colors is not None: 

1006 self.swap_light_and_dark_colors = swap_light_and_dark_colors 

1007 if rprompt is not None: 

1008 self.rprompt = rprompt 

1009 if multiline is not None: 

1010 self.multiline = multiline 

1011 if prompt_continuation is not None: 

1012 self.prompt_continuation = prompt_continuation 

1013 if wrap_lines is not None: 

1014 self.wrap_lines = wrap_lines 

1015 if enable_history_search is not None: 

1016 self.enable_history_search = enable_history_search 

1017 if search_ignore_case is not None: 

1018 self.search_ignore_case = search_ignore_case 

1019 if complete_while_typing is not None: 

1020 self.complete_while_typing = complete_while_typing 

1021 if validate_while_typing is not None: 

1022 self.validate_while_typing = validate_while_typing 

1023 if complete_style is not None: 

1024 self.complete_style = complete_style 

1025 if auto_suggest is not None: 

1026 self.auto_suggest = auto_suggest 

1027 if validator is not None: 

1028 self.validator = validator 

1029 if clipboard is not None: 

1030 self.clipboard = clipboard 

1031 if mouse_support is not None: 

1032 self.mouse_support = mouse_support 

1033 if input_processors is not None: 

1034 self.input_processors = input_processors 

1035 if placeholder is not None: 

1036 self.placeholder = placeholder 

1037 if reserve_space_for_menu is not None: 

1038 self.reserve_space_for_menu = reserve_space_for_menu 

1039 if enable_system_prompt is not None: 

1040 self.enable_system_prompt = enable_system_prompt 

1041 if enable_suspend is not None: 

1042 self.enable_suspend = enable_suspend 

1043 if enable_open_in_editor is not None: 

1044 self.enable_open_in_editor = enable_open_in_editor 

1045 if tempfile_suffix is not None: 

1046 self.tempfile_suffix = tempfile_suffix 

1047 if tempfile is not None: 

1048 self.tempfile = tempfile 

1049 if show_frame is not None: 

1050 self.show_frame = show_frame 

1051 

1052 self._add_pre_run_callables(pre_run, accept_default) 

1053 self.default_buffer.reset( 

1054 default if isinstance(default, Document) else Document(default) 

1055 ) 

1056 self.app.refresh_interval = self.refresh_interval # This is not reactive. 

1057 

1058 # If we are using the default output, and have a dumb terminal. Use the 

1059 # dumb prompt. 

1060 if self._output is None and is_dumb_terminal(): 

1061 with self._dumb_prompt(self.message) as dump_app: 

1062 return dump_app.run(in_thread=in_thread, handle_sigint=handle_sigint) 

1063 

1064 return self.app.run( 

1065 set_exception_handler=set_exception_handler, 

1066 in_thread=in_thread, 

1067 handle_sigint=handle_sigint, 

1068 inputhook=inputhook, 

1069 ) 

1070 

1071 @contextmanager 

1072 def _dumb_prompt(self, message: AnyFormattedText = "") -> Iterator[Application[_T]]: 

1073 """ 

1074 Create prompt `Application` for prompt function for dumb terminals. 

1075 

1076 Dumb terminals have minimum rendering capabilities. We can only print 

1077 text to the screen. We can't use colors, and we can't do cursor 

1078 movements. The Emacs inferior shell is an example of a dumb terminal. 

1079 

1080 We will show the prompt, and wait for the input. We still handle arrow 

1081 keys, and all custom key bindings, but we don't really render the 

1082 cursor movements. Instead we only print the typed character that's 

1083 right before the cursor. 

1084 """ 

1085 # Send prompt to output. 

1086 self.output.write(fragment_list_to_text(to_formatted_text(self.message))) 

1087 self.output.flush() 

1088 

1089 # Key bindings for the dumb prompt: mostly the same as the full prompt. 

1090 key_bindings: KeyBindingsBase = self._create_prompt_bindings() 

1091 if self.key_bindings: 

1092 key_bindings = merge_key_bindings([self.key_bindings, key_bindings]) 

1093 

1094 # Create and run application. 

1095 application = cast( 

1096 Application[_T], 

1097 Application( 

1098 input=self.input, 

1099 output=DummyOutput(), 

1100 layout=self.layout, 

1101 key_bindings=key_bindings, 

1102 ), 

1103 ) 

1104 

1105 def on_text_changed(_: object) -> None: 

1106 self.output.write(self.default_buffer.document.text_before_cursor[-1:]) 

1107 self.output.flush() 

1108 

1109 self.default_buffer.on_text_changed += on_text_changed 

1110 

1111 try: 

1112 yield application 

1113 finally: 

1114 # Render line ending. 

1115 self.output.write("\r\n") 

1116 self.output.flush() 

1117 

1118 self.default_buffer.on_text_changed -= on_text_changed 

1119 

1120 async def prompt_async( 

1121 self, 

1122 # When any of these arguments are passed, this value is overwritten 

1123 # in this PromptSession. 

1124 message: AnyFormattedText | None = None, 

1125 # `message` should go first, because people call it as 

1126 # positional argument. 

1127 *, 

1128 editing_mode: EditingMode | None = None, 

1129 refresh_interval: float | None = None, 

1130 vi_mode: bool | None = None, 

1131 lexer: Lexer | None = None, 

1132 completer: Completer | None = None, 

1133 complete_in_thread: bool | None = None, 

1134 is_password: bool | None = None, 

1135 key_bindings: KeyBindingsBase | None = None, 

1136 bottom_toolbar: AnyFormattedText | None = None, 

1137 style: BaseStyle | None = None, 

1138 color_depth: ColorDepth | None = None, 

1139 cursor: CursorShapeConfig | None = None, 

1140 include_default_pygments_style: FilterOrBool | None = None, 

1141 style_transformation: StyleTransformation | None = None, 

1142 swap_light_and_dark_colors: FilterOrBool | None = None, 

1143 rprompt: AnyFormattedText | None = None, 

1144 multiline: FilterOrBool | None = None, 

1145 prompt_continuation: PromptContinuationText | None = None, 

1146 wrap_lines: FilterOrBool | None = None, 

1147 enable_history_search: FilterOrBool | None = None, 

1148 search_ignore_case: FilterOrBool | None = None, 

1149 complete_while_typing: FilterOrBool | None = None, 

1150 validate_while_typing: FilterOrBool | None = None, 

1151 complete_style: CompleteStyle | None = None, 

1152 auto_suggest: AutoSuggest | None = None, 

1153 validator: Validator | None = None, 

1154 clipboard: Clipboard | None = None, 

1155 mouse_support: FilterOrBool | None = None, 

1156 input_processors: list[Processor] | None = None, 

1157 placeholder: AnyFormattedText | None = None, 

1158 reserve_space_for_menu: int | None = None, 

1159 enable_system_prompt: FilterOrBool | None = None, 

1160 enable_suspend: FilterOrBool | None = None, 

1161 enable_open_in_editor: FilterOrBool | None = None, 

1162 tempfile_suffix: str | Callable[[], str] | None = None, 

1163 tempfile: str | Callable[[], str] | None = None, 

1164 show_frame: FilterOrBool = False, 

1165 # Following arguments are specific to the current `prompt()` call. 

1166 default: str | Document = "", 

1167 accept_default: bool = False, 

1168 pre_run: Callable[[], None] | None = None, 

1169 set_exception_handler: bool = True, 

1170 handle_sigint: bool = True, 

1171 ) -> _T: 

1172 if message is not None: 

1173 self.message = message 

1174 if editing_mode is not None: 

1175 self.editing_mode = editing_mode 

1176 if refresh_interval is not None: 

1177 self.refresh_interval = refresh_interval 

1178 if vi_mode: 

1179 self.editing_mode = EditingMode.VI 

1180 if lexer is not None: 

1181 self.lexer = lexer 

1182 if completer is not None: 

1183 self.completer = completer 

1184 if complete_in_thread is not None: 

1185 self.complete_in_thread = complete_in_thread 

1186 if is_password is not None: 

1187 self.is_password = is_password 

1188 if key_bindings is not None: 

1189 self.key_bindings = key_bindings 

1190 if bottom_toolbar is not None: 

1191 self.bottom_toolbar = bottom_toolbar 

1192 if style is not None: 

1193 self.style = style 

1194 if color_depth is not None: 

1195 self.color_depth = color_depth 

1196 if cursor is not None: 

1197 self.cursor = cursor 

1198 if include_default_pygments_style is not None: 

1199 self.include_default_pygments_style = include_default_pygments_style 

1200 if style_transformation is not None: 

1201 self.style_transformation = style_transformation 

1202 if swap_light_and_dark_colors is not None: 

1203 self.swap_light_and_dark_colors = swap_light_and_dark_colors 

1204 if rprompt is not None: 

1205 self.rprompt = rprompt 

1206 if multiline is not None: 

1207 self.multiline = multiline 

1208 if prompt_continuation is not None: 

1209 self.prompt_continuation = prompt_continuation 

1210 if wrap_lines is not None: 

1211 self.wrap_lines = wrap_lines 

1212 if enable_history_search is not None: 

1213 self.enable_history_search = enable_history_search 

1214 if search_ignore_case is not None: 

1215 self.search_ignore_case = search_ignore_case 

1216 if complete_while_typing is not None: 

1217 self.complete_while_typing = complete_while_typing 

1218 if validate_while_typing is not None: 

1219 self.validate_while_typing = validate_while_typing 

1220 if complete_style is not None: 

1221 self.complete_style = complete_style 

1222 if auto_suggest is not None: 

1223 self.auto_suggest = auto_suggest 

1224 if validator is not None: 

1225 self.validator = validator 

1226 if clipboard is not None: 

1227 self.clipboard = clipboard 

1228 if mouse_support is not None: 

1229 self.mouse_support = mouse_support 

1230 if input_processors is not None: 

1231 self.input_processors = input_processors 

1232 if placeholder is not None: 

1233 self.placeholder = placeholder 

1234 if reserve_space_for_menu is not None: 

1235 self.reserve_space_for_menu = reserve_space_for_menu 

1236 if enable_system_prompt is not None: 

1237 self.enable_system_prompt = enable_system_prompt 

1238 if enable_suspend is not None: 

1239 self.enable_suspend = enable_suspend 

1240 if enable_open_in_editor is not None: 

1241 self.enable_open_in_editor = enable_open_in_editor 

1242 if tempfile_suffix is not None: 

1243 self.tempfile_suffix = tempfile_suffix 

1244 if tempfile is not None: 

1245 self.tempfile = tempfile 

1246 if show_frame is not None: 

1247 self.show_frame = show_frame 

1248 

1249 self._add_pre_run_callables(pre_run, accept_default) 

1250 self.default_buffer.reset( 

1251 default if isinstance(default, Document) else Document(default) 

1252 ) 

1253 self.app.refresh_interval = self.refresh_interval # This is not reactive. 

1254 

1255 # If we are using the default output, and have a dumb terminal. Use the 

1256 # dumb prompt. 

1257 if self._output is None and is_dumb_terminal(): 

1258 with self._dumb_prompt(self.message) as dump_app: 

1259 return await dump_app.run_async(handle_sigint=handle_sigint) 

1260 

1261 return await self.app.run_async( 

1262 set_exception_handler=set_exception_handler, handle_sigint=handle_sigint 

1263 ) 

1264 

1265 def _add_pre_run_callables( 

1266 self, pre_run: Callable[[], None] | None, accept_default: bool 

1267 ) -> None: 

1268 def pre_run2() -> None: 

1269 if pre_run: 

1270 pre_run() 

1271 

1272 if accept_default: 

1273 # Validate and handle input. We use `call_from_executor` in 

1274 # order to run it "soon" (during the next iteration of the 

1275 # event loop), instead of right now. Otherwise, it won't 

1276 # display the default value. 

1277 get_running_loop().call_soon(self.default_buffer.validate_and_handle) 

1278 

1279 self.app.pre_run_callables.append(pre_run2) 

1280 

1281 @property 

1282 def editing_mode(self) -> EditingMode: 

1283 return self.app.editing_mode 

1284 

1285 @editing_mode.setter 

1286 def editing_mode(self, value: EditingMode) -> None: 

1287 self.app.editing_mode = value 

1288 

1289 def _get_default_buffer_control_height(self) -> Dimension: 

1290 # If there is an autocompletion menu to be shown, make sure that our 

1291 # layout has at least a minimal height in order to display it. 

1292 if ( 

1293 self.completer is not None 

1294 and self.complete_style != CompleteStyle.READLINE_LIKE 

1295 ): 

1296 space = self.reserve_space_for_menu 

1297 else: 

1298 space = 0 

1299 

1300 if space and not get_app().is_done: 

1301 buff = self.default_buffer 

1302 

1303 # Reserve the space, either when there are completions, or when 

1304 # `complete_while_typing` is true and we expect completions very 

1305 # soon. 

1306 if buff.complete_while_typing() or buff.complete_state is not None: 

1307 return Dimension(min=space) 

1308 

1309 return Dimension() 

1310 

1311 def _get_prompt(self) -> StyleAndTextTuples: 

1312 return to_formatted_text(self.message, style="class:prompt") 

1313 

1314 def _get_continuation( 

1315 self, width: int, line_number: int, wrap_count: int 

1316 ) -> StyleAndTextTuples: 

1317 """ 

1318 Insert the prompt continuation. 

1319 

1320 :param width: The width that was used for the prompt. (more or less can 

1321 be used.) 

1322 :param line_number: 

1323 :param wrap_count: Amount of times that the line has been wrapped. 

1324 """ 

1325 prompt_continuation = self.prompt_continuation 

1326 

1327 if callable(prompt_continuation): 

1328 continuation: AnyFormattedText = prompt_continuation( 

1329 width, line_number, wrap_count 

1330 ) 

1331 else: 

1332 continuation = prompt_continuation 

1333 

1334 # When the continuation prompt is not given, choose the same width as 

1335 # the actual prompt. 

1336 if continuation is None and is_true(self.multiline): 

1337 continuation = " " * width 

1338 

1339 return to_formatted_text(continuation, style="class:prompt-continuation") 

1340 

1341 def _get_line_prefix( 

1342 self, 

1343 line_number: int, 

1344 wrap_count: int, 

1345 get_prompt_text_2: _StyleAndTextTuplesCallable, 

1346 ) -> StyleAndTextTuples: 

1347 """ 

1348 Return whatever needs to be inserted before every line. 

1349 (the prompt, or a line continuation.) 

1350 """ 

1351 # First line: display the "arg" or the prompt. 

1352 if line_number == 0 and wrap_count == 0: 

1353 if not is_true(self.multiline) and get_app().key_processor.arg is not None: 

1354 return self._inline_arg() 

1355 else: 

1356 return get_prompt_text_2() 

1357 

1358 # For the next lines, display the appropriate continuation. 

1359 prompt_width = get_cwidth(fragment_list_to_text(get_prompt_text_2())) 

1360 return self._get_continuation(prompt_width, line_number, wrap_count) 

1361 

1362 def _get_arg_text(self) -> StyleAndTextTuples: 

1363 "'arg' toolbar, for in multiline mode." 

1364 arg = self.app.key_processor.arg 

1365 if arg is None: 

1366 # Should not happen because of the `has_arg` filter in the layout. 

1367 return [] 

1368 

1369 if arg == "-": 

1370 arg = "-1" 

1371 

1372 return [("class:arg-toolbar", "Repeat: "), ("class:arg-toolbar.text", arg)] 

1373 

1374 def _inline_arg(self) -> StyleAndTextTuples: 

1375 "'arg' prefix, for in single line mode." 

1376 app = get_app() 

1377 if app.key_processor.arg is None: 

1378 return [] 

1379 else: 

1380 arg = app.key_processor.arg 

1381 

1382 return [ 

1383 ("class:prompt.arg", "(arg: "), 

1384 ("class:prompt.arg.text", str(arg)), 

1385 ("class:prompt.arg", ") "), 

1386 ] 

1387 

1388 # Expose the Input and Output objects as attributes, mainly for 

1389 # backward-compatibility. 

1390 

1391 @property 

1392 def input(self) -> Input: 

1393 return self.app.input 

1394 

1395 @property 

1396 def output(self) -> Output: 

1397 return self.app.output 

1398 

1399 

1400def prompt( 

1401 message: AnyFormattedText | None = None, 

1402 *, 

1403 history: History | None = None, 

1404 editing_mode: EditingMode | None = None, 

1405 refresh_interval: float | None = None, 

1406 vi_mode: bool | None = None, 

1407 lexer: Lexer | None = None, 

1408 completer: Completer | None = None, 

1409 complete_in_thread: bool | None = None, 

1410 is_password: bool | None = None, 

1411 key_bindings: KeyBindingsBase | None = None, 

1412 bottom_toolbar: AnyFormattedText | None = None, 

1413 style: BaseStyle | None = None, 

1414 color_depth: ColorDepth | None = None, 

1415 cursor: AnyCursorShapeConfig = None, 

1416 include_default_pygments_style: FilterOrBool | None = None, 

1417 style_transformation: StyleTransformation | None = None, 

1418 swap_light_and_dark_colors: FilterOrBool | None = None, 

1419 rprompt: AnyFormattedText | None = None, 

1420 multiline: FilterOrBool | None = None, 

1421 prompt_continuation: PromptContinuationText | None = None, 

1422 wrap_lines: FilterOrBool | None = None, 

1423 enable_history_search: FilterOrBool | None = None, 

1424 search_ignore_case: FilterOrBool | None = None, 

1425 complete_while_typing: FilterOrBool | None = None, 

1426 validate_while_typing: FilterOrBool | None = None, 

1427 complete_style: CompleteStyle | None = None, 

1428 auto_suggest: AutoSuggest | None = None, 

1429 validator: Validator | None = None, 

1430 clipboard: Clipboard | None = None, 

1431 mouse_support: FilterOrBool | None = None, 

1432 input_processors: list[Processor] | None = None, 

1433 placeholder: AnyFormattedText | None = None, 

1434 reserve_space_for_menu: int | None = None, 

1435 enable_system_prompt: FilterOrBool | None = None, 

1436 enable_suspend: FilterOrBool | None = None, 

1437 enable_open_in_editor: FilterOrBool | None = None, 

1438 tempfile_suffix: str | Callable[[], str] | None = None, 

1439 tempfile: str | Callable[[], str] | None = None, 

1440 show_frame: FilterOrBool | None = None, 

1441 # Following arguments are specific to the current `prompt()` call. 

1442 default: str = "", 

1443 accept_default: bool = False, 

1444 pre_run: Callable[[], None] | None = None, 

1445 set_exception_handler: bool = True, 

1446 handle_sigint: bool = True, 

1447 in_thread: bool = False, 

1448 inputhook: InputHook | None = None, 

1449) -> str: 

1450 """ 

1451 The global `prompt` function. This will create a new `PromptSession` 

1452 instance for every call. 

1453 """ 

1454 # The history is the only attribute that has to be passed to the 

1455 # `PromptSession`, it can't be passed into the `prompt()` method. 

1456 session: PromptSession[str] = PromptSession(history=history) 

1457 

1458 return session.prompt( 

1459 message, 

1460 editing_mode=editing_mode, 

1461 refresh_interval=refresh_interval, 

1462 vi_mode=vi_mode, 

1463 lexer=lexer, 

1464 completer=completer, 

1465 complete_in_thread=complete_in_thread, 

1466 is_password=is_password, 

1467 key_bindings=key_bindings, 

1468 bottom_toolbar=bottom_toolbar, 

1469 style=style, 

1470 color_depth=color_depth, 

1471 cursor=cursor, 

1472 include_default_pygments_style=include_default_pygments_style, 

1473 style_transformation=style_transformation, 

1474 swap_light_and_dark_colors=swap_light_and_dark_colors, 

1475 rprompt=rprompt, 

1476 multiline=multiline, 

1477 prompt_continuation=prompt_continuation, 

1478 wrap_lines=wrap_lines, 

1479 enable_history_search=enable_history_search, 

1480 search_ignore_case=search_ignore_case, 

1481 complete_while_typing=complete_while_typing, 

1482 validate_while_typing=validate_while_typing, 

1483 complete_style=complete_style, 

1484 auto_suggest=auto_suggest, 

1485 validator=validator, 

1486 clipboard=clipboard, 

1487 mouse_support=mouse_support, 

1488 input_processors=input_processors, 

1489 placeholder=placeholder, 

1490 reserve_space_for_menu=reserve_space_for_menu, 

1491 enable_system_prompt=enable_system_prompt, 

1492 enable_suspend=enable_suspend, 

1493 enable_open_in_editor=enable_open_in_editor, 

1494 tempfile_suffix=tempfile_suffix, 

1495 tempfile=tempfile, 

1496 show_frame=show_frame, 

1497 default=default, 

1498 accept_default=accept_default, 

1499 pre_run=pre_run, 

1500 set_exception_handler=set_exception_handler, 

1501 handle_sigint=handle_sigint, 

1502 in_thread=in_thread, 

1503 inputhook=inputhook, 

1504 ) 

1505 

1506 

1507prompt.__doc__ = PromptSession.prompt.__doc__ 

1508 

1509 

1510def create_confirm_session( 

1511 message: AnyFormattedText, suffix: str = " (y/n) " 

1512) -> PromptSession[bool]: 

1513 """ 

1514 Create a `PromptSession` object for the 'confirm' function. 

1515 """ 

1516 bindings = KeyBindings() 

1517 

1518 @bindings.add("y") 

1519 @bindings.add("Y") 

1520 def yes(event: E) -> None: 

1521 session.default_buffer.text = "y" 

1522 event.app.exit(result=True) 

1523 

1524 @bindings.add("n") 

1525 @bindings.add("N") 

1526 def no(event: E) -> None: 

1527 session.default_buffer.text = "n" 

1528 event.app.exit(result=False) 

1529 

1530 @bindings.add(Keys.Any) 

1531 def _(event: E) -> None: 

1532 "Disallow inserting other text." 

1533 pass 

1534 

1535 complete_message = merge_formatted_text([message, suffix]) 

1536 session: PromptSession[bool] = PromptSession( 

1537 complete_message, key_bindings=bindings 

1538 ) 

1539 return session 

1540 

1541 

1542def confirm(message: AnyFormattedText = "Confirm?", suffix: str = " (y/n) ") -> bool: 

1543 """ 

1544 Display a confirmation prompt that returns True/False. 

1545 """ 

1546 session = create_confirm_session(message, suffix) 

1547 return session.prompt()