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

471 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-20 06:09 +0000

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""" 

27from __future__ import annotations 

28 

29from asyncio import get_running_loop 

30from contextlib import contextmanager 

31from enum import Enum 

32from functools import partial 

33from typing import TYPE_CHECKING, Callable, Generic, Iterator, TypeVar, Union, cast 

34 

35from prompt_toolkit.application import Application 

36from prompt_toolkit.application.current import get_app 

37from prompt_toolkit.auto_suggest import AutoSuggest, DynamicAutoSuggest 

38from prompt_toolkit.buffer import Buffer 

39from prompt_toolkit.clipboard import Clipboard, DynamicClipboard, InMemoryClipboard 

40from prompt_toolkit.completion import Completer, DynamicCompleter, ThreadedCompleter 

41from prompt_toolkit.cursor_shapes import ( 

42 AnyCursorShapeConfig, 

43 CursorShapeConfig, 

44 DynamicCursorShapeConfig, 

45) 

46from prompt_toolkit.document import Document 

47from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER, EditingMode 

48from prompt_toolkit.eventloop import InputHook 

49from prompt_toolkit.filters import ( 

50 Condition, 

51 FilterOrBool, 

52 has_arg, 

53 has_focus, 

54 is_done, 

55 is_true, 

56 renderer_height_is_known, 

57 to_filter, 

58) 

59from prompt_toolkit.formatted_text import ( 

60 AnyFormattedText, 

61 StyleAndTextTuples, 

62 fragment_list_to_text, 

63 merge_formatted_text, 

64 to_formatted_text, 

65) 

66from prompt_toolkit.history import History, InMemoryHistory 

67from prompt_toolkit.input.base import Input 

68from prompt_toolkit.key_binding.bindings.auto_suggest import load_auto_suggest_bindings 

69from prompt_toolkit.key_binding.bindings.completion import ( 

70 display_completions_like_readline, 

71) 

72from prompt_toolkit.key_binding.bindings.open_in_editor import ( 

73 load_open_in_editor_bindings, 

74) 

75from prompt_toolkit.key_binding.key_bindings import ( 

76 ConditionalKeyBindings, 

77 DynamicKeyBindings, 

78 KeyBindings, 

79 KeyBindingsBase, 

80 merge_key_bindings, 

81) 

82from prompt_toolkit.key_binding.key_processor import KeyPressEvent 

83from prompt_toolkit.keys import Keys 

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

85from prompt_toolkit.layout.containers import ConditionalContainer, WindowAlign 

86from prompt_toolkit.layout.controls import ( 

87 BufferControl, 

88 FormattedTextControl, 

89 SearchBufferControl, 

90) 

91from prompt_toolkit.layout.dimension import Dimension 

92from prompt_toolkit.layout.layout import Layout 

93from prompt_toolkit.layout.menus import CompletionsMenu, MultiColumnCompletionsMenu 

94from prompt_toolkit.layout.processors import ( 

95 AfterInput, 

96 AppendAutoSuggestion, 

97 ConditionalProcessor, 

98 DisplayMultipleCursors, 

99 DynamicProcessor, 

100 HighlightIncrementalSearchProcessor, 

101 HighlightSelectionProcessor, 

102 PasswordProcessor, 

103 Processor, 

104 ReverseSearchProcessor, 

105 merge_processors, 

106) 

107from prompt_toolkit.layout.utils import explode_text_fragments 

108from prompt_toolkit.lexers import DynamicLexer, Lexer 

109from prompt_toolkit.output import ColorDepth, DummyOutput, Output 

110from prompt_toolkit.styles import ( 

111 BaseStyle, 

112 ConditionalStyleTransformation, 

113 DynamicStyle, 

114 DynamicStyleTransformation, 

115 StyleTransformation, 

116 SwapLightAndDarkStyleTransformation, 

117 merge_style_transformations, 

118) 

119from prompt_toolkit.utils import ( 

120 get_cwidth, 

121 is_dumb_terminal, 

122 suspend_to_background_supported, 

123 to_str, 

124) 

125from prompt_toolkit.validation import DynamicValidator, Validator 

126from prompt_toolkit.widgets.toolbars import ( 

127 SearchToolbar, 

128 SystemToolbar, 

129 ValidationToolbar, 

130) 

131 

132if TYPE_CHECKING: 

133 from prompt_toolkit.formatted_text.base import MagicFormattedText 

134 

135__all__ = [ 

136 "PromptSession", 

137 "prompt", 

138 "confirm", 

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

140 "CompleteStyle", 

141] 

142 

143_StyleAndTextTuplesCallable = Callable[[], StyleAndTextTuples] 

144E = KeyPressEvent 

145 

146 

147def _split_multiline_prompt( 

148 get_prompt_text: _StyleAndTextTuplesCallable, 

149) -> tuple[ 

150 Callable[[], bool], _StyleAndTextTuplesCallable, _StyleAndTextTuplesCallable 

151]: 

152 """ 

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

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

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

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

157 """ 

158 

159 def has_before_fragments() -> bool: 

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

161 if "\n" in char: 

162 return True 

163 return False 

164 

165 def before() -> StyleAndTextTuples: 

166 result: StyleAndTextTuples = [] 

167 found_nl = False 

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

169 if found_nl: 

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

171 elif char == "\n": 

172 found_nl = True 

173 return result 

174 

175 def first_input_line() -> StyleAndTextTuples: 

176 result: StyleAndTextTuples = [] 

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

178 if char == "\n": 

179 break 

180 else: 

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

182 return result 

183 

184 return has_before_fragments, before, first_input_line 

185 

186 

187class _RPrompt(Window): 

188 """ 

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

190 """ 

191 

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

193 super().__init__( 

194 FormattedTextControl(text=text), 

195 align=WindowAlign.RIGHT, 

196 style="class:rprompt", 

197 ) 

198 

199 

200class CompleteStyle(str, Enum): 

201 """ 

202 How to display autocompletions for the prompt. 

203 """ 

204 

205 value: str 

206 

207 COLUMN = "COLUMN" 

208 MULTI_COLUMN = "MULTI_COLUMN" 

209 READLINE_LIKE = "READLINE_LIKE" 

210 

211 

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

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

214PromptContinuationText = Union[ 

215 str, 

216 "MagicFormattedText", 

217 StyleAndTextTuples, 

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

219 Callable[[int, int, int], AnyFormattedText], 

220] 

221 

222_T = TypeVar("_T") 

223 

224 

225class PromptSession(Generic[_T]): 

226 """ 

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

228 replacement. 

229 

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

231 be a replacement for `raw_input`. 

232 

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

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

235 

236 Example usage:: 

237 

238 s = PromptSession(message='>') 

239 text = s.prompt() 

240 

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

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

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

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

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

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

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

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

249 scrolling horizontally. 

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

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

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

253 :param complete_while_typing: `bool` or 

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

255 typing. 

256 :param validate_while_typing: `bool` or 

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

258 typing. 

259 :param enable_history_search: `bool` or 

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

261 string matching. 

262 :param search_ignore_case: 

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

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

265 syntax highlighting. 

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

267 for input validation. 

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

269 for input completion. 

270 :param complete_in_thread: `bool` or 

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

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

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

274 we always run the completions in the main thread. 

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

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

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

278 instance for input suggestions. 

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

280 :param include_default_pygments_style: `bool` or 

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

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

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

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

285 merged. 

286 :param style_transformation: 

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

288 :param swap_light_and_dark_colors: `bool` or 

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

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

291 This is useful for switching between dark and light terminal 

292 backgrounds. 

293 :param enable_system_prompt: `bool` or 

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

295 a system prompt. 

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

297 Enable Control-Z style suspension. 

298 :param enable_open_in_editor: `bool` or 

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

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

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

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

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

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

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

306 :param bottom_toolbar: Formatted text or callable which is supposed to 

307 return formatted text. 

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

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

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

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

312 `prompt_width` spaces will be used. 

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

314 ``CompleteStyle.MULTI_COLUMN`` or ``CompleteStyle.READLINE_LIKE``. 

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

316 to enable mouse support. 

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

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

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

320 formatted text. 

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

322 every so many seconds. 

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

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

325 :param output: `Output` object. 

326 """ 

327 

328 _fields = ( 

329 "message", 

330 "lexer", 

331 "completer", 

332 "complete_in_thread", 

333 "is_password", 

334 "editing_mode", 

335 "key_bindings", 

336 "is_password", 

337 "bottom_toolbar", 

338 "style", 

339 "style_transformation", 

340 "swap_light_and_dark_colors", 

341 "color_depth", 

342 "cursor", 

343 "include_default_pygments_style", 

344 "rprompt", 

345 "multiline", 

346 "prompt_continuation", 

347 "wrap_lines", 

348 "enable_history_search", 

349 "search_ignore_case", 

350 "complete_while_typing", 

351 "validate_while_typing", 

352 "complete_style", 

353 "mouse_support", 

354 "auto_suggest", 

355 "clipboard", 

356 "validator", 

357 "refresh_interval", 

358 "input_processors", 

359 "placeholder", 

360 "enable_system_prompt", 

361 "enable_suspend", 

362 "enable_open_in_editor", 

363 "reserve_space_for_menu", 

364 "tempfile_suffix", 

365 "tempfile", 

366 ) 

367 

368 def __init__( 

369 self, 

370 message: AnyFormattedText = "", 

371 *, 

372 multiline: FilterOrBool = False, 

373 wrap_lines: FilterOrBool = True, 

374 is_password: FilterOrBool = False, 

375 vi_mode: bool = False, 

376 editing_mode: EditingMode = EditingMode.EMACS, 

377 complete_while_typing: FilterOrBool = True, 

378 validate_while_typing: FilterOrBool = True, 

379 enable_history_search: FilterOrBool = False, 

380 search_ignore_case: FilterOrBool = False, 

381 lexer: Lexer | None = None, 

382 enable_system_prompt: FilterOrBool = False, 

383 enable_suspend: FilterOrBool = False, 

384 enable_open_in_editor: FilterOrBool = False, 

385 validator: Validator | None = None, 

386 completer: Completer | None = None, 

387 complete_in_thread: bool = False, 

388 reserve_space_for_menu: int = 8, 

389 complete_style: CompleteStyle = CompleteStyle.COLUMN, 

390 auto_suggest: AutoSuggest | None = None, 

391 style: BaseStyle | None = None, 

392 style_transformation: StyleTransformation | None = None, 

393 swap_light_and_dark_colors: FilterOrBool = False, 

394 color_depth: ColorDepth | None = None, 

395 cursor: AnyCursorShapeConfig = None, 

396 include_default_pygments_style: FilterOrBool = True, 

397 history: History | None = None, 

398 clipboard: Clipboard | None = None, 

399 prompt_continuation: PromptContinuationText | None = None, 

400 rprompt: AnyFormattedText = None, 

401 bottom_toolbar: AnyFormattedText = None, 

402 mouse_support: FilterOrBool = False, 

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

404 placeholder: AnyFormattedText | None = None, 

405 key_bindings: KeyBindingsBase | None = None, 

406 erase_when_done: bool = False, 

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

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

409 refresh_interval: float = 0, 

410 input: Input | None = None, 

411 output: Output | None = None, 

412 ) -> None: 

413 history = history or InMemoryHistory() 

414 clipboard = clipboard or InMemoryClipboard() 

415 

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

417 if vi_mode: 

418 editing_mode = EditingMode.VI 

419 

420 # Store all settings in this class. 

421 self._input = input 

422 self._output = output 

423 

424 # Store attributes. 

425 # (All except 'editing_mode'.) 

426 self.message = message 

427 self.lexer = lexer 

428 self.completer = completer 

429 self.complete_in_thread = complete_in_thread 

430 self.is_password = is_password 

431 self.key_bindings = key_bindings 

432 self.bottom_toolbar = bottom_toolbar 

433 self.style = style 

434 self.style_transformation = style_transformation 

435 self.swap_light_and_dark_colors = swap_light_and_dark_colors 

436 self.color_depth = color_depth 

437 self.cursor = cursor 

438 self.include_default_pygments_style = include_default_pygments_style 

439 self.rprompt = rprompt 

440 self.multiline = multiline 

441 self.prompt_continuation = prompt_continuation 

442 self.wrap_lines = wrap_lines 

443 self.enable_history_search = enable_history_search 

444 self.search_ignore_case = search_ignore_case 

445 self.complete_while_typing = complete_while_typing 

446 self.validate_while_typing = validate_while_typing 

447 self.complete_style = complete_style 

448 self.mouse_support = mouse_support 

449 self.auto_suggest = auto_suggest 

450 self.clipboard = clipboard 

451 self.validator = validator 

452 self.refresh_interval = refresh_interval 

453 self.input_processors = input_processors 

454 self.placeholder = placeholder 

455 self.enable_system_prompt = enable_system_prompt 

456 self.enable_suspend = enable_suspend 

457 self.enable_open_in_editor = enable_open_in_editor 

458 self.reserve_space_for_menu = reserve_space_for_menu 

459 self.tempfile_suffix = tempfile_suffix 

460 self.tempfile = tempfile 

461 

462 # Create buffers, layout and Application. 

463 self.history = history 

464 self.default_buffer = self._create_default_buffer() 

465 self.search_buffer = self._create_search_buffer() 

466 self.layout = self._create_layout() 

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

468 

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

470 """ 

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

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

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

474 

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

476 or `Filter`. 

477 """ 

478 

479 @Condition 

480 def dynamic() -> bool: 

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

482 return to_filter(value)() 

483 

484 return dynamic 

485 

486 def _create_default_buffer(self) -> Buffer: 

487 """ 

488 Create and return the default input buffer. 

489 """ 

490 dyncond = self._dyncond 

491 

492 # Create buffers list. 

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

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

495 the validation succeeds.""" 

496 cast(Application[str], get_app()).exit(result=buff.document.text) 

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

498 

499 return Buffer( 

500 name=DEFAULT_BUFFER, 

501 # Make sure that complete_while_typing is disabled when 

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

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

504 complete_while_typing=Condition( 

505 lambda: is_true(self.complete_while_typing) 

506 and not is_true(self.enable_history_search) 

507 and not self.complete_style == CompleteStyle.READLINE_LIKE 

508 ), 

509 validate_while_typing=dyncond("validate_while_typing"), 

510 enable_history_search=dyncond("enable_history_search"), 

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

512 completer=DynamicCompleter( 

513 lambda: ThreadedCompleter(self.completer) 

514 if self.complete_in_thread and self.completer 

515 else self.completer 

516 ), 

517 history=self.history, 

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

519 accept_handler=accept, 

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

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

522 ) 

523 

524 def _create_search_buffer(self) -> Buffer: 

525 return Buffer(name=SEARCH_BUFFER) 

526 

527 def _create_layout(self) -> Layout: 

528 """ 

529 Create `Layout` for this prompt. 

530 """ 

531 dyncond = self._dyncond 

532 

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

534 # a multiline prompt.) 

535 ( 

536 has_before_fragments, 

537 get_prompt_text_1, 

538 get_prompt_text_2, 

539 ) = _split_multiline_prompt(self._get_prompt) 

540 

541 default_buffer = self.default_buffer 

542 search_buffer = self.search_buffer 

543 

544 # Create processors list. 

545 @Condition 

546 def display_placeholder() -> bool: 

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

548 

549 all_input_processors = [ 

550 HighlightIncrementalSearchProcessor(), 

551 HighlightSelectionProcessor(), 

552 ConditionalProcessor( 

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

554 ), 

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

556 DisplayMultipleCursors(), 

557 # Users can insert processors here. 

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

559 ConditionalProcessor( 

560 AfterInput(lambda: self.placeholder), 

561 filter=display_placeholder, 

562 ), 

563 ] 

564 

565 # Create bottom toolbars. 

566 bottom_toolbar = ConditionalContainer( 

567 Window( 

568 FormattedTextControl( 

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

570 ), 

571 style="class:bottom-toolbar", 

572 dont_extend_height=True, 

573 height=Dimension(min=1), 

574 ), 

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

576 & ~is_done 

577 & renderer_height_is_known, 

578 ) 

579 

580 search_toolbar = SearchToolbar( 

581 search_buffer, ignore_case=dyncond("search_ignore_case") 

582 ) 

583 

584 search_buffer_control = SearchBufferControl( 

585 buffer=search_buffer, 

586 input_processors=[ReverseSearchProcessor()], 

587 ignore_case=dyncond("search_ignore_case"), 

588 ) 

589 

590 system_toolbar = SystemToolbar( 

591 enable_global_bindings=dyncond("enable_system_prompt") 

592 ) 

593 

594 def get_search_buffer_control() -> SearchBufferControl: 

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

596 if is_true(self.multiline): 

597 return search_toolbar.control 

598 else: 

599 return search_buffer_control 

600 

601 default_buffer_control = BufferControl( 

602 buffer=default_buffer, 

603 search_buffer_control=get_search_buffer_control, 

604 input_processors=all_input_processors, 

605 include_default_input_processors=False, 

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

607 preview_search=True, 

608 ) 

609 

610 default_buffer_window = Window( 

611 default_buffer_control, 

612 height=self._get_default_buffer_control_height, 

613 get_line_prefix=partial( 

614 self._get_line_prefix, get_prompt_text_2=get_prompt_text_2 

615 ), 

616 wrap_lines=dyncond("wrap_lines"), 

617 ) 

618 

619 @Condition 

620 def multi_column_complete_style() -> bool: 

621 return self.complete_style == CompleteStyle.MULTI_COLUMN 

622 

623 # Build the layout. 

624 layout = HSplit( 

625 [ 

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

627 FloatContainer( 

628 HSplit( 

629 [ 

630 ConditionalContainer( 

631 Window( 

632 FormattedTextControl(get_prompt_text_1), 

633 dont_extend_height=True, 

634 ), 

635 Condition(has_before_fragments), 

636 ), 

637 ConditionalContainer( 

638 default_buffer_window, 

639 Condition( 

640 lambda: get_app().layout.current_control 

641 != search_buffer_control 

642 ), 

643 ), 

644 ConditionalContainer( 

645 Window(search_buffer_control), 

646 Condition( 

647 lambda: get_app().layout.current_control 

648 == search_buffer_control 

649 ), 

650 ), 

651 ] 

652 ), 

653 [ 

654 # Completion menus. 

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

656 # transparent, because the shape is not always 

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

658 Float( 

659 xcursor=True, 

660 ycursor=True, 

661 transparent=True, 

662 content=CompletionsMenu( 

663 max_height=16, 

664 scroll_offset=1, 

665 extra_filter=has_focus(default_buffer) 

666 & ~multi_column_complete_style, 

667 ), 

668 ), 

669 Float( 

670 xcursor=True, 

671 ycursor=True, 

672 transparent=True, 

673 content=MultiColumnCompletionsMenu( 

674 show_meta=True, 

675 extra_filter=has_focus(default_buffer) 

676 & multi_column_complete_style, 

677 ), 

678 ), 

679 # The right prompt. 

680 Float( 

681 right=0, 

682 top=0, 

683 hide_when_covering_content=True, 

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

685 ), 

686 ], 

687 ), 

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

689 ConditionalContainer( 

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

691 ), 

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

693 ConditionalContainer( 

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

695 dyncond("multiline") & has_arg, 

696 ), 

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

698 bottom_toolbar, 

699 ] 

700 ) 

701 

702 return Layout(layout, default_buffer_window) 

703 

704 def _create_application( 

705 self, editing_mode: EditingMode, erase_when_done: bool 

706 ) -> Application[_T]: 

707 """ 

708 Create the `Application` object. 

709 """ 

710 dyncond = self._dyncond 

711 

712 # Default key bindings. 

713 auto_suggest_bindings = load_auto_suggest_bindings() 

714 open_in_editor_bindings = load_open_in_editor_bindings() 

715 prompt_bindings = self._create_prompt_bindings() 

716 

717 # Create application 

718 application: Application[_T] = Application( 

719 layout=self.layout, 

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

721 style_transformation=merge_style_transformations( 

722 [ 

723 DynamicStyleTransformation(lambda: self.style_transformation), 

724 ConditionalStyleTransformation( 

725 SwapLightAndDarkStyleTransformation(), 

726 dyncond("swap_light_and_dark_colors"), 

727 ), 

728 ] 

729 ), 

730 include_default_pygments_style=dyncond("include_default_pygments_style"), 

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

732 key_bindings=merge_key_bindings( 

733 [ 

734 merge_key_bindings( 

735 [ 

736 auto_suggest_bindings, 

737 ConditionalKeyBindings( 

738 open_in_editor_bindings, 

739 dyncond("enable_open_in_editor") 

740 & has_focus(DEFAULT_BUFFER), 

741 ), 

742 prompt_bindings, 

743 ] 

744 ), 

745 DynamicKeyBindings(lambda: self.key_bindings), 

746 ] 

747 ), 

748 mouse_support=dyncond("mouse_support"), 

749 editing_mode=editing_mode, 

750 erase_when_done=erase_when_done, 

751 reverse_vi_search_direction=True, 

752 color_depth=lambda: self.color_depth, 

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

754 refresh_interval=self.refresh_interval, 

755 input=self._input, 

756 output=self._output, 

757 ) 

758 

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

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

761 # 'multiline' property dynamic. 

762 """ 

763 def on_render(app): 

764 multiline = is_true(self.multiline) 

765 current_control = app.layout.current_control 

766 

767 if multiline: 

768 if current_control == search_buffer_control: 

769 app.layout.current_control = search_toolbar.control 

770 app.invalidate() 

771 else: 

772 if current_control == search_toolbar.control: 

773 app.layout.current_control = search_buffer_control 

774 app.invalidate() 

775 

776 app.on_render += on_render 

777 """ 

778 

779 return application 

780 

781 def _create_prompt_bindings(self) -> KeyBindings: 

782 """ 

783 Create the KeyBindings for a prompt application. 

784 """ 

785 kb = KeyBindings() 

786 handle = kb.add 

787 default_focused = has_focus(DEFAULT_BUFFER) 

788 

789 @Condition 

790 def do_accept() -> bool: 

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

792 DEFAULT_BUFFER 

793 ) 

794 

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

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

797 "Accept input when enter has been pressed." 

798 self.default_buffer.validate_and_handle() 

799 

800 @Condition 

801 def readline_complete_style() -> bool: 

802 return self.complete_style == CompleteStyle.READLINE_LIKE 

803 

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

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

806 "Display completions (like Readline)." 

807 display_completions_like_readline(event) 

808 

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

810 @handle("<sigint>") 

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

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

813 event.app.exit(exception=KeyboardInterrupt, style="class:aborting") 

814 

815 @Condition 

816 def ctrl_d_condition() -> bool: 

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

818 and empty.""" 

819 app = get_app() 

820 return ( 

821 app.current_buffer.name == DEFAULT_BUFFER 

822 and not app.current_buffer.text 

823 ) 

824 

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

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

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

828 event.app.exit(exception=EOFError, style="class:exiting") 

829 

830 suspend_supported = Condition(suspend_to_background_supported) 

831 

832 @Condition 

833 def enable_suspend() -> bool: 

834 return to_filter(self.enable_suspend)() 

835 

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

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

838 """ 

839 Suspend process to background. 

840 """ 

841 event.app.suspend_to_background() 

842 

843 return kb 

844 

845 def prompt( 

846 self, 

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

848 # in this PromptSession. 

849 message: AnyFormattedText | None = None, 

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

851 # positional argument. 

852 *, 

853 editing_mode: EditingMode | None = None, 

854 refresh_interval: float | None = None, 

855 vi_mode: bool | None = None, 

856 lexer: Lexer | None = None, 

857 completer: Completer | None = None, 

858 complete_in_thread: bool | None = None, 

859 is_password: bool | None = None, 

860 key_bindings: KeyBindingsBase | None = None, 

861 bottom_toolbar: AnyFormattedText | None = None, 

862 style: BaseStyle | None = None, 

863 color_depth: ColorDepth | None = None, 

864 cursor: AnyCursorShapeConfig | None = None, 

865 include_default_pygments_style: FilterOrBool | None = None, 

866 style_transformation: StyleTransformation | None = None, 

867 swap_light_and_dark_colors: FilterOrBool | None = None, 

868 rprompt: AnyFormattedText | None = None, 

869 multiline: FilterOrBool | None = None, 

870 prompt_continuation: PromptContinuationText | None = None, 

871 wrap_lines: FilterOrBool | None = None, 

872 enable_history_search: FilterOrBool | None = None, 

873 search_ignore_case: FilterOrBool | None = None, 

874 complete_while_typing: FilterOrBool | None = None, 

875 validate_while_typing: FilterOrBool | None = None, 

876 complete_style: CompleteStyle | None = None, 

877 auto_suggest: AutoSuggest | None = None, 

878 validator: Validator | None = None, 

879 clipboard: Clipboard | None = None, 

880 mouse_support: FilterOrBool | None = None, 

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

882 placeholder: AnyFormattedText | None = None, 

883 reserve_space_for_menu: int | None = None, 

884 enable_system_prompt: FilterOrBool | None = None, 

885 enable_suspend: FilterOrBool | None = None, 

886 enable_open_in_editor: FilterOrBool | None = None, 

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

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

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

890 default: str | Document = "", 

891 accept_default: bool = False, 

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

893 set_exception_handler: bool = True, 

894 handle_sigint: bool = True, 

895 in_thread: bool = False, 

896 inputhook: InputHook | None = None, 

897 ) -> _T: 

898 """ 

899 Display the prompt. 

900 

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

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

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

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

905 but also to the next prompts. 

906 

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

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

909 ``DummyCompleter``, ``DummyValidator`` or ``DummyAutoSuggest`` instance 

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

911 

912 Additional arguments, specific for this prompt: 

913 

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

915 by the user). 

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

917 value without allowing the user to edit the input. 

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

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

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

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

922 

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

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

925 (for exit). 

926 """ 

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

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

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

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

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

932 

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

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

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

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

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

938 # to verify the code. 

939 if message is not None: 

940 self.message = message 

941 if editing_mode is not None: 

942 self.editing_mode = editing_mode 

943 if refresh_interval is not None: 

944 self.refresh_interval = refresh_interval 

945 if vi_mode: 

946 self.editing_mode = EditingMode.VI 

947 if lexer is not None: 

948 self.lexer = lexer 

949 if completer is not None: 

950 self.completer = completer 

951 if complete_in_thread is not None: 

952 self.complete_in_thread = complete_in_thread 

953 if is_password is not None: 

954 self.is_password = is_password 

955 if key_bindings is not None: 

956 self.key_bindings = key_bindings 

957 if bottom_toolbar is not None: 

958 self.bottom_toolbar = bottom_toolbar 

959 if style is not None: 

960 self.style = style 

961 if color_depth is not None: 

962 self.color_depth = color_depth 

963 if cursor is not None: 

964 self.cursor = cursor 

965 if include_default_pygments_style is not None: 

966 self.include_default_pygments_style = include_default_pygments_style 

967 if style_transformation is not None: 

968 self.style_transformation = style_transformation 

969 if swap_light_and_dark_colors is not None: 

970 self.swap_light_and_dark_colors = swap_light_and_dark_colors 

971 if rprompt is not None: 

972 self.rprompt = rprompt 

973 if multiline is not None: 

974 self.multiline = multiline 

975 if prompt_continuation is not None: 

976 self.prompt_continuation = prompt_continuation 

977 if wrap_lines is not None: 

978 self.wrap_lines = wrap_lines 

979 if enable_history_search is not None: 

980 self.enable_history_search = enable_history_search 

981 if search_ignore_case is not None: 

982 self.search_ignore_case = search_ignore_case 

983 if complete_while_typing is not None: 

984 self.complete_while_typing = complete_while_typing 

985 if validate_while_typing is not None: 

986 self.validate_while_typing = validate_while_typing 

987 if complete_style is not None: 

988 self.complete_style = complete_style 

989 if auto_suggest is not None: 

990 self.auto_suggest = auto_suggest 

991 if validator is not None: 

992 self.validator = validator 

993 if clipboard is not None: 

994 self.clipboard = clipboard 

995 if mouse_support is not None: 

996 self.mouse_support = mouse_support 

997 if input_processors is not None: 

998 self.input_processors = input_processors 

999 if placeholder is not None: 

1000 self.placeholder = placeholder 

1001 if reserve_space_for_menu is not None: 

1002 self.reserve_space_for_menu = reserve_space_for_menu 

1003 if enable_system_prompt is not None: 

1004 self.enable_system_prompt = enable_system_prompt 

1005 if enable_suspend is not None: 

1006 self.enable_suspend = enable_suspend 

1007 if enable_open_in_editor is not None: 

1008 self.enable_open_in_editor = enable_open_in_editor 

1009 if tempfile_suffix is not None: 

1010 self.tempfile_suffix = tempfile_suffix 

1011 if tempfile is not None: 

1012 self.tempfile = tempfile 

1013 

1014 self._add_pre_run_callables(pre_run, accept_default) 

1015 self.default_buffer.reset( 

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

1017 ) 

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

1019 

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

1021 # dumb prompt. 

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

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

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

1025 

1026 return self.app.run( 

1027 set_exception_handler=set_exception_handler, 

1028 in_thread=in_thread, 

1029 handle_sigint=handle_sigint, 

1030 inputhook=inputhook, 

1031 ) 

1032 

1033 @contextmanager 

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

1035 """ 

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

1037 

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

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

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

1041 

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

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

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

1045 right before the cursor. 

1046 """ 

1047 # Send prompt to output. 

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

1049 self.output.flush() 

1050 

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

1052 key_bindings: KeyBindingsBase = self._create_prompt_bindings() 

1053 if self.key_bindings: 

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

1055 

1056 # Create and run application. 

1057 application = cast( 

1058 Application[_T], 

1059 Application( 

1060 input=self.input, 

1061 output=DummyOutput(), 

1062 layout=self.layout, 

1063 key_bindings=key_bindings, 

1064 ), 

1065 ) 

1066 

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

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

1069 self.output.flush() 

1070 

1071 self.default_buffer.on_text_changed += on_text_changed 

1072 

1073 try: 

1074 yield application 

1075 finally: 

1076 # Render line ending. 

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

1078 self.output.flush() 

1079 

1080 self.default_buffer.on_text_changed -= on_text_changed 

1081 

1082 async def prompt_async( 

1083 self, 

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

1085 # in this PromptSession. 

1086 message: AnyFormattedText | None = None, 

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

1088 # positional argument. 

1089 *, 

1090 editing_mode: EditingMode | None = None, 

1091 refresh_interval: float | None = None, 

1092 vi_mode: bool | None = None, 

1093 lexer: Lexer | None = None, 

1094 completer: Completer | None = None, 

1095 complete_in_thread: bool | None = None, 

1096 is_password: bool | None = None, 

1097 key_bindings: KeyBindingsBase | None = None, 

1098 bottom_toolbar: AnyFormattedText | None = None, 

1099 style: BaseStyle | None = None, 

1100 color_depth: ColorDepth | None = None, 

1101 cursor: CursorShapeConfig | None = None, 

1102 include_default_pygments_style: FilterOrBool | None = None, 

1103 style_transformation: StyleTransformation | None = None, 

1104 swap_light_and_dark_colors: FilterOrBool | None = None, 

1105 rprompt: AnyFormattedText | None = None, 

1106 multiline: FilterOrBool | None = None, 

1107 prompt_continuation: PromptContinuationText | None = None, 

1108 wrap_lines: FilterOrBool | None = None, 

1109 enable_history_search: FilterOrBool | None = None, 

1110 search_ignore_case: FilterOrBool | None = None, 

1111 complete_while_typing: FilterOrBool | None = None, 

1112 validate_while_typing: FilterOrBool | None = None, 

1113 complete_style: CompleteStyle | None = None, 

1114 auto_suggest: AutoSuggest | None = None, 

1115 validator: Validator | None = None, 

1116 clipboard: Clipboard | None = None, 

1117 mouse_support: FilterOrBool | None = None, 

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

1119 placeholder: AnyFormattedText | None = None, 

1120 reserve_space_for_menu: int | None = None, 

1121 enable_system_prompt: FilterOrBool | None = None, 

1122 enable_suspend: FilterOrBool | None = None, 

1123 enable_open_in_editor: FilterOrBool | None = None, 

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

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

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

1127 default: str | Document = "", 

1128 accept_default: bool = False, 

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

1130 set_exception_handler: bool = True, 

1131 handle_sigint: bool = True, 

1132 ) -> _T: 

1133 if message is not None: 

1134 self.message = message 

1135 if editing_mode is not None: 

1136 self.editing_mode = editing_mode 

1137 if refresh_interval is not None: 

1138 self.refresh_interval = refresh_interval 

1139 if vi_mode: 

1140 self.editing_mode = EditingMode.VI 

1141 if lexer is not None: 

1142 self.lexer = lexer 

1143 if completer is not None: 

1144 self.completer = completer 

1145 if complete_in_thread is not None: 

1146 self.complete_in_thread = complete_in_thread 

1147 if is_password is not None: 

1148 self.is_password = is_password 

1149 if key_bindings is not None: 

1150 self.key_bindings = key_bindings 

1151 if bottom_toolbar is not None: 

1152 self.bottom_toolbar = bottom_toolbar 

1153 if style is not None: 

1154 self.style = style 

1155 if color_depth is not None: 

1156 self.color_depth = color_depth 

1157 if cursor is not None: 

1158 self.cursor = cursor 

1159 if include_default_pygments_style is not None: 

1160 self.include_default_pygments_style = include_default_pygments_style 

1161 if style_transformation is not None: 

1162 self.style_transformation = style_transformation 

1163 if swap_light_and_dark_colors is not None: 

1164 self.swap_light_and_dark_colors = swap_light_and_dark_colors 

1165 if rprompt is not None: 

1166 self.rprompt = rprompt 

1167 if multiline is not None: 

1168 self.multiline = multiline 

1169 if prompt_continuation is not None: 

1170 self.prompt_continuation = prompt_continuation 

1171 if wrap_lines is not None: 

1172 self.wrap_lines = wrap_lines 

1173 if enable_history_search is not None: 

1174 self.enable_history_search = enable_history_search 

1175 if search_ignore_case is not None: 

1176 self.search_ignore_case = search_ignore_case 

1177 if complete_while_typing is not None: 

1178 self.complete_while_typing = complete_while_typing 

1179 if validate_while_typing is not None: 

1180 self.validate_while_typing = validate_while_typing 

1181 if complete_style is not None: 

1182 self.complete_style = complete_style 

1183 if auto_suggest is not None: 

1184 self.auto_suggest = auto_suggest 

1185 if validator is not None: 

1186 self.validator = validator 

1187 if clipboard is not None: 

1188 self.clipboard = clipboard 

1189 if mouse_support is not None: 

1190 self.mouse_support = mouse_support 

1191 if input_processors is not None: 

1192 self.input_processors = input_processors 

1193 if placeholder is not None: 

1194 self.placeholder = placeholder 

1195 if reserve_space_for_menu is not None: 

1196 self.reserve_space_for_menu = reserve_space_for_menu 

1197 if enable_system_prompt is not None: 

1198 self.enable_system_prompt = enable_system_prompt 

1199 if enable_suspend is not None: 

1200 self.enable_suspend = enable_suspend 

1201 if enable_open_in_editor is not None: 

1202 self.enable_open_in_editor = enable_open_in_editor 

1203 if tempfile_suffix is not None: 

1204 self.tempfile_suffix = tempfile_suffix 

1205 if tempfile is not None: 

1206 self.tempfile = tempfile 

1207 

1208 self._add_pre_run_callables(pre_run, accept_default) 

1209 self.default_buffer.reset( 

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

1211 ) 

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

1213 

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

1215 # dumb prompt. 

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

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

1218 return await dump_app.run_async(handle_sigint=handle_sigint) 

1219 

1220 return await self.app.run_async( 

1221 set_exception_handler=set_exception_handler, handle_sigint=handle_sigint 

1222 ) 

1223 

1224 def _add_pre_run_callables( 

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

1226 ) -> None: 

1227 def pre_run2() -> None: 

1228 if pre_run: 

1229 pre_run() 

1230 

1231 if accept_default: 

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

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

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

1235 # display the default value. 

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

1237 

1238 self.app.pre_run_callables.append(pre_run2) 

1239 

1240 @property 

1241 def editing_mode(self) -> EditingMode: 

1242 return self.app.editing_mode 

1243 

1244 @editing_mode.setter 

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

1246 self.app.editing_mode = value 

1247 

1248 def _get_default_buffer_control_height(self) -> Dimension: 

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

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

1251 if ( 

1252 self.completer is not None 

1253 and self.complete_style != CompleteStyle.READLINE_LIKE 

1254 ): 

1255 space = self.reserve_space_for_menu 

1256 else: 

1257 space = 0 

1258 

1259 if space and not get_app().is_done: 

1260 buff = self.default_buffer 

1261 

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

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

1264 # soon. 

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

1266 return Dimension(min=space) 

1267 

1268 return Dimension() 

1269 

1270 def _get_prompt(self) -> StyleAndTextTuples: 

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

1272 

1273 def _get_continuation( 

1274 self, width: int, line_number: int, wrap_count: int 

1275 ) -> StyleAndTextTuples: 

1276 """ 

1277 Insert the prompt continuation. 

1278 

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

1280 be used.) 

1281 :param line_number: 

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

1283 """ 

1284 prompt_continuation = self.prompt_continuation 

1285 

1286 if callable(prompt_continuation): 

1287 continuation: AnyFormattedText = prompt_continuation( 

1288 width, line_number, wrap_count 

1289 ) 

1290 else: 

1291 continuation = prompt_continuation 

1292 

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

1294 # the actual prompt. 

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

1296 continuation = " " * width 

1297 

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

1299 

1300 def _get_line_prefix( 

1301 self, 

1302 line_number: int, 

1303 wrap_count: int, 

1304 get_prompt_text_2: _StyleAndTextTuplesCallable, 

1305 ) -> StyleAndTextTuples: 

1306 """ 

1307 Return whatever needs to be inserted before every line. 

1308 (the prompt, or a line continuation.) 

1309 """ 

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

1311 if line_number == 0 and wrap_count == 0: 

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

1313 return self._inline_arg() 

1314 else: 

1315 return get_prompt_text_2() 

1316 

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

1318 prompt_width = get_cwidth(fragment_list_to_text(get_prompt_text_2())) 

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

1320 

1321 def _get_arg_text(self) -> StyleAndTextTuples: 

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

1323 arg = self.app.key_processor.arg 

1324 if arg is None: 

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

1326 return [] 

1327 

1328 if arg == "-": 

1329 arg = "-1" 

1330 

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

1332 

1333 def _inline_arg(self) -> StyleAndTextTuples: 

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

1335 app = get_app() 

1336 if app.key_processor.arg is None: 

1337 return [] 

1338 else: 

1339 arg = app.key_processor.arg 

1340 

1341 return [ 

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

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

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

1345 ] 

1346 

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

1348 # backward-compatibility. 

1349 

1350 @property 

1351 def input(self) -> Input: 

1352 return self.app.input 

1353 

1354 @property 

1355 def output(self) -> Output: 

1356 return self.app.output 

1357 

1358 

1359def prompt( 

1360 message: AnyFormattedText | None = None, 

1361 *, 

1362 history: History | None = None, 

1363 editing_mode: EditingMode | None = None, 

1364 refresh_interval: float | None = None, 

1365 vi_mode: bool | None = None, 

1366 lexer: Lexer | None = None, 

1367 completer: Completer | None = None, 

1368 complete_in_thread: bool | None = None, 

1369 is_password: bool | None = None, 

1370 key_bindings: KeyBindingsBase | None = None, 

1371 bottom_toolbar: AnyFormattedText | None = None, 

1372 style: BaseStyle | None = None, 

1373 color_depth: ColorDepth | None = None, 

1374 cursor: AnyCursorShapeConfig = None, 

1375 include_default_pygments_style: FilterOrBool | None = None, 

1376 style_transformation: StyleTransformation | None = None, 

1377 swap_light_and_dark_colors: FilterOrBool | None = None, 

1378 rprompt: AnyFormattedText | None = None, 

1379 multiline: FilterOrBool | None = None, 

1380 prompt_continuation: PromptContinuationText | None = None, 

1381 wrap_lines: FilterOrBool | None = None, 

1382 enable_history_search: FilterOrBool | None = None, 

1383 search_ignore_case: FilterOrBool | None = None, 

1384 complete_while_typing: FilterOrBool | None = None, 

1385 validate_while_typing: FilterOrBool | None = None, 

1386 complete_style: CompleteStyle | None = None, 

1387 auto_suggest: AutoSuggest | None = None, 

1388 validator: Validator | None = None, 

1389 clipboard: Clipboard | None = None, 

1390 mouse_support: FilterOrBool | None = None, 

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

1392 placeholder: AnyFormattedText | None = None, 

1393 reserve_space_for_menu: int | None = None, 

1394 enable_system_prompt: FilterOrBool | None = None, 

1395 enable_suspend: FilterOrBool | None = None, 

1396 enable_open_in_editor: FilterOrBool | None = None, 

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

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

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

1400 default: str = "", 

1401 accept_default: bool = False, 

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

1403 set_exception_handler: bool = True, 

1404 handle_sigint: bool = True, 

1405 in_thread: bool = False, 

1406 inputhook: InputHook | None = None, 

1407) -> str: 

1408 """ 

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

1410 instance for every call. 

1411 """ 

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

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

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

1415 

1416 return session.prompt( 

1417 message, 

1418 editing_mode=editing_mode, 

1419 refresh_interval=refresh_interval, 

1420 vi_mode=vi_mode, 

1421 lexer=lexer, 

1422 completer=completer, 

1423 complete_in_thread=complete_in_thread, 

1424 is_password=is_password, 

1425 key_bindings=key_bindings, 

1426 bottom_toolbar=bottom_toolbar, 

1427 style=style, 

1428 color_depth=color_depth, 

1429 cursor=cursor, 

1430 include_default_pygments_style=include_default_pygments_style, 

1431 style_transformation=style_transformation, 

1432 swap_light_and_dark_colors=swap_light_and_dark_colors, 

1433 rprompt=rprompt, 

1434 multiline=multiline, 

1435 prompt_continuation=prompt_continuation, 

1436 wrap_lines=wrap_lines, 

1437 enable_history_search=enable_history_search, 

1438 search_ignore_case=search_ignore_case, 

1439 complete_while_typing=complete_while_typing, 

1440 validate_while_typing=validate_while_typing, 

1441 complete_style=complete_style, 

1442 auto_suggest=auto_suggest, 

1443 validator=validator, 

1444 clipboard=clipboard, 

1445 mouse_support=mouse_support, 

1446 input_processors=input_processors, 

1447 placeholder=placeholder, 

1448 reserve_space_for_menu=reserve_space_for_menu, 

1449 enable_system_prompt=enable_system_prompt, 

1450 enable_suspend=enable_suspend, 

1451 enable_open_in_editor=enable_open_in_editor, 

1452 tempfile_suffix=tempfile_suffix, 

1453 tempfile=tempfile, 

1454 default=default, 

1455 accept_default=accept_default, 

1456 pre_run=pre_run, 

1457 set_exception_handler=set_exception_handler, 

1458 handle_sigint=handle_sigint, 

1459 in_thread=in_thread, 

1460 inputhook=inputhook, 

1461 ) 

1462 

1463 

1464prompt.__doc__ = PromptSession.prompt.__doc__ 

1465 

1466 

1467def create_confirm_session( 

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

1469) -> PromptSession[bool]: 

1470 """ 

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

1472 """ 

1473 bindings = KeyBindings() 

1474 

1475 @bindings.add("y") 

1476 @bindings.add("Y") 

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

1478 session.default_buffer.text = "y" 

1479 event.app.exit(result=True) 

1480 

1481 @bindings.add("n") 

1482 @bindings.add("N") 

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

1484 session.default_buffer.text = "n" 

1485 event.app.exit(result=False) 

1486 

1487 @bindings.add(Keys.Any) 

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

1489 "Disallow inserting other text." 

1490 pass 

1491 

1492 complete_message = merge_formatted_text([message, suffix]) 

1493 session: PromptSession[bool] = PromptSession( 

1494 complete_message, key_bindings=bindings 

1495 ) 

1496 return session 

1497 

1498 

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

1500 """ 

1501 Display a confirmation prompt that returns True/False. 

1502 """ 

1503 session = create_confirm_session(message, suffix) 

1504 return session.prompt()