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

470 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-25 06:05 +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.filters import ( 

49 Condition, 

50 FilterOrBool, 

51 has_arg, 

52 has_focus, 

53 is_done, 

54 is_true, 

55 renderer_height_is_known, 

56 to_filter, 

57) 

58from prompt_toolkit.formatted_text import ( 

59 AnyFormattedText, 

60 StyleAndTextTuples, 

61 fragment_list_to_text, 

62 merge_formatted_text, 

63 to_formatted_text, 

64) 

65from prompt_toolkit.history import History, InMemoryHistory 

66from prompt_toolkit.input.base import Input 

67from prompt_toolkit.key_binding.bindings.auto_suggest import load_auto_suggest_bindings 

68from prompt_toolkit.key_binding.bindings.completion import ( 

69 display_completions_like_readline, 

70) 

71from prompt_toolkit.key_binding.bindings.open_in_editor import ( 

72 load_open_in_editor_bindings, 

73) 

74from prompt_toolkit.key_binding.key_bindings import ( 

75 ConditionalKeyBindings, 

76 DynamicKeyBindings, 

77 KeyBindings, 

78 KeyBindingsBase, 

79 merge_key_bindings, 

80) 

81from prompt_toolkit.key_binding.key_processor import KeyPressEvent 

82from prompt_toolkit.keys import Keys 

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

84from prompt_toolkit.layout.containers import ConditionalContainer, WindowAlign 

85from prompt_toolkit.layout.controls import ( 

86 BufferControl, 

87 FormattedTextControl, 

88 SearchBufferControl, 

89) 

90from prompt_toolkit.layout.dimension import Dimension 

91from prompt_toolkit.layout.layout import Layout 

92from prompt_toolkit.layout.menus import CompletionsMenu, MultiColumnCompletionsMenu 

93from prompt_toolkit.layout.processors import ( 

94 AfterInput, 

95 AppendAutoSuggestion, 

96 ConditionalProcessor, 

97 DisplayMultipleCursors, 

98 DynamicProcessor, 

99 HighlightIncrementalSearchProcessor, 

100 HighlightSelectionProcessor, 

101 PasswordProcessor, 

102 Processor, 

103 ReverseSearchProcessor, 

104 merge_processors, 

105) 

106from prompt_toolkit.layout.utils import explode_text_fragments 

107from prompt_toolkit.lexers import DynamicLexer, Lexer 

108from prompt_toolkit.output import ColorDepth, DummyOutput, Output 

109from prompt_toolkit.styles import ( 

110 BaseStyle, 

111 ConditionalStyleTransformation, 

112 DynamicStyle, 

113 DynamicStyleTransformation, 

114 StyleTransformation, 

115 SwapLightAndDarkStyleTransformation, 

116 merge_style_transformations, 

117) 

118from prompt_toolkit.utils import ( 

119 get_cwidth, 

120 is_dumb_terminal, 

121 suspend_to_background_supported, 

122 to_str, 

123) 

124from prompt_toolkit.validation import DynamicValidator, Validator 

125from prompt_toolkit.widgets.toolbars import ( 

126 SearchToolbar, 

127 SystemToolbar, 

128 ValidationToolbar, 

129) 

130 

131if TYPE_CHECKING: 

132 from prompt_toolkit.formatted_text.base import MagicFormattedText 

133 

134__all__ = [ 

135 "PromptSession", 

136 "prompt", 

137 "confirm", 

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

139 "CompleteStyle", 

140] 

141 

142_StyleAndTextTuplesCallable = Callable[[], StyleAndTextTuples] 

143E = KeyPressEvent 

144 

145 

146def _split_multiline_prompt( 

147 get_prompt_text: _StyleAndTextTuplesCallable, 

148) -> tuple[ 

149 Callable[[], bool], _StyleAndTextTuplesCallable, _StyleAndTextTuplesCallable 

150]: 

151 """ 

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

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

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

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

156 """ 

157 

158 def has_before_fragments() -> bool: 

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

160 if "\n" in char: 

161 return True 

162 return False 

163 

164 def before() -> StyleAndTextTuples: 

165 result: StyleAndTextTuples = [] 

166 found_nl = False 

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

168 if found_nl: 

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

170 elif char == "\n": 

171 found_nl = True 

172 return result 

173 

174 def first_input_line() -> StyleAndTextTuples: 

175 result: StyleAndTextTuples = [] 

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

177 if char == "\n": 

178 break 

179 else: 

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

181 return result 

182 

183 return has_before_fragments, before, first_input_line 

184 

185 

186class _RPrompt(Window): 

187 """ 

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

189 """ 

190 

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

192 super().__init__( 

193 FormattedTextControl(text=text), 

194 align=WindowAlign.RIGHT, 

195 style="class:rprompt", 

196 ) 

197 

198 

199class CompleteStyle(str, Enum): 

200 """ 

201 How to display autocompletions for the prompt. 

202 """ 

203 

204 value: str 

205 

206 COLUMN = "COLUMN" 

207 MULTI_COLUMN = "MULTI_COLUMN" 

208 READLINE_LIKE = "READLINE_LIKE" 

209 

210 

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

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

213PromptContinuationText = Union[ 

214 str, 

215 "MagicFormattedText", 

216 StyleAndTextTuples, 

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

218 Callable[[int, int, int], AnyFormattedText], 

219] 

220 

221_T = TypeVar("_T") 

222 

223 

224class PromptSession(Generic[_T]): 

225 """ 

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

227 replacement. 

228 

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

230 be a replacement for `raw_input`. 

231 

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

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

234 

235 Example usage:: 

236 

237 s = PromptSession(message='>') 

238 text = s.prompt() 

239 

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

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

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

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

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

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

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

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

248 scrolling horizontally. 

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

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

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

252 :param complete_while_typing: `bool` or 

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

254 typing. 

255 :param validate_while_typing: `bool` or 

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

257 typing. 

258 :param enable_history_search: `bool` or 

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

260 string matching. 

261 :param search_ignore_case: 

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

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

264 syntax highlighting. 

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

266 for input validation. 

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

268 for input completion. 

269 :param complete_in_thread: `bool` or 

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

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

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

273 we always run the completions in the main thread. 

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

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

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

277 instance for input suggestions. 

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

279 :param include_default_pygments_style: `bool` or 

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

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

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

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

284 merged. 

285 :param style_transformation: 

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

287 :param swap_light_and_dark_colors: `bool` or 

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

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

290 This is useful for switching between dark and light terminal 

291 backgrounds. 

292 :param enable_system_prompt: `bool` or 

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

294 a system prompt. 

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

296 Enable Control-Z style suspension. 

297 :param enable_open_in_editor: `bool` or 

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

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

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

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

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

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

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

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

306 return formatted text. 

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

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

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

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

311 `prompt_width` spaces will be used. 

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

313 ``CompleteStyle.MULTI_COLUMN`` or ``CompleteStyle.READLINE_LIKE``. 

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

315 to enable mouse support. 

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

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

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

319 formatted text. 

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

321 every so many seconds. 

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

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

324 :param output: `Output` object. 

325 """ 

326 

327 _fields = ( 

328 "message", 

329 "lexer", 

330 "completer", 

331 "complete_in_thread", 

332 "is_password", 

333 "editing_mode", 

334 "key_bindings", 

335 "is_password", 

336 "bottom_toolbar", 

337 "style", 

338 "style_transformation", 

339 "swap_light_and_dark_colors", 

340 "color_depth", 

341 "cursor", 

342 "include_default_pygments_style", 

343 "rprompt", 

344 "multiline", 

345 "prompt_continuation", 

346 "wrap_lines", 

347 "enable_history_search", 

348 "search_ignore_case", 

349 "complete_while_typing", 

350 "validate_while_typing", 

351 "complete_style", 

352 "mouse_support", 

353 "auto_suggest", 

354 "clipboard", 

355 "validator", 

356 "refresh_interval", 

357 "input_processors", 

358 "placeholder", 

359 "enable_system_prompt", 

360 "enable_suspend", 

361 "enable_open_in_editor", 

362 "reserve_space_for_menu", 

363 "tempfile_suffix", 

364 "tempfile", 

365 ) 

366 

367 def __init__( 

368 self, 

369 message: AnyFormattedText = "", 

370 *, 

371 multiline: FilterOrBool = False, 

372 wrap_lines: FilterOrBool = True, 

373 is_password: FilterOrBool = False, 

374 vi_mode: bool = False, 

375 editing_mode: EditingMode = EditingMode.EMACS, 

376 complete_while_typing: FilterOrBool = True, 

377 validate_while_typing: FilterOrBool = True, 

378 enable_history_search: FilterOrBool = False, 

379 search_ignore_case: FilterOrBool = False, 

380 lexer: Lexer | None = None, 

381 enable_system_prompt: FilterOrBool = False, 

382 enable_suspend: FilterOrBool = False, 

383 enable_open_in_editor: FilterOrBool = False, 

384 validator: Validator | None = None, 

385 completer: Completer | None = None, 

386 complete_in_thread: bool = False, 

387 reserve_space_for_menu: int = 8, 

388 complete_style: CompleteStyle = CompleteStyle.COLUMN, 

389 auto_suggest: AutoSuggest | None = None, 

390 style: BaseStyle | None = None, 

391 style_transformation: StyleTransformation | None = None, 

392 swap_light_and_dark_colors: FilterOrBool = False, 

393 color_depth: ColorDepth | None = None, 

394 cursor: AnyCursorShapeConfig = None, 

395 include_default_pygments_style: FilterOrBool = True, 

396 history: History | None = None, 

397 clipboard: Clipboard | None = None, 

398 prompt_continuation: PromptContinuationText | None = None, 

399 rprompt: AnyFormattedText = None, 

400 bottom_toolbar: AnyFormattedText = None, 

401 mouse_support: FilterOrBool = False, 

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

403 placeholder: AnyFormattedText | None = None, 

404 key_bindings: KeyBindingsBase | None = None, 

405 erase_when_done: bool = False, 

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

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

408 refresh_interval: float = 0, 

409 input: Input | None = None, 

410 output: Output | None = None, 

411 ) -> None: 

412 history = history or InMemoryHistory() 

413 clipboard = clipboard or InMemoryClipboard() 

414 

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

416 if vi_mode: 

417 editing_mode = EditingMode.VI 

418 

419 # Store all settings in this class. 

420 self._input = input 

421 self._output = output 

422 

423 # Store attributes. 

424 # (All except 'editing_mode'.) 

425 self.message = message 

426 self.lexer = lexer 

427 self.completer = completer 

428 self.complete_in_thread = complete_in_thread 

429 self.is_password = is_password 

430 self.key_bindings = key_bindings 

431 self.bottom_toolbar = bottom_toolbar 

432 self.style = style 

433 self.style_transformation = style_transformation 

434 self.swap_light_and_dark_colors = swap_light_and_dark_colors 

435 self.color_depth = color_depth 

436 self.cursor = cursor 

437 self.include_default_pygments_style = include_default_pygments_style 

438 self.rprompt = rprompt 

439 self.multiline = multiline 

440 self.prompt_continuation = prompt_continuation 

441 self.wrap_lines = wrap_lines 

442 self.enable_history_search = enable_history_search 

443 self.search_ignore_case = search_ignore_case 

444 self.complete_while_typing = complete_while_typing 

445 self.validate_while_typing = validate_while_typing 

446 self.complete_style = complete_style 

447 self.mouse_support = mouse_support 

448 self.auto_suggest = auto_suggest 

449 self.clipboard = clipboard 

450 self.validator = validator 

451 self.refresh_interval = refresh_interval 

452 self.input_processors = input_processors 

453 self.placeholder = placeholder 

454 self.enable_system_prompt = enable_system_prompt 

455 self.enable_suspend = enable_suspend 

456 self.enable_open_in_editor = enable_open_in_editor 

457 self.reserve_space_for_menu = reserve_space_for_menu 

458 self.tempfile_suffix = tempfile_suffix 

459 self.tempfile = tempfile 

460 

461 # Create buffers, layout and Application. 

462 self.history = history 

463 self.default_buffer = self._create_default_buffer() 

464 self.search_buffer = self._create_search_buffer() 

465 self.layout = self._create_layout() 

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

467 

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

469 """ 

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

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

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

473 

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

475 or `Filter`. 

476 """ 

477 

478 @Condition 

479 def dynamic() -> bool: 

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

481 return to_filter(value)() 

482 

483 return dynamic 

484 

485 def _create_default_buffer(self) -> Buffer: 

486 """ 

487 Create and return the default input buffer. 

488 """ 

489 dyncond = self._dyncond 

490 

491 # Create buffers list. 

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

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

494 the validation succeeds.""" 

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

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

497 

498 return Buffer( 

499 name=DEFAULT_BUFFER, 

500 # Make sure that complete_while_typing is disabled when 

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

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

503 complete_while_typing=Condition( 

504 lambda: is_true(self.complete_while_typing) 

505 and not is_true(self.enable_history_search) 

506 and not self.complete_style == CompleteStyle.READLINE_LIKE 

507 ), 

508 validate_while_typing=dyncond("validate_while_typing"), 

509 enable_history_search=dyncond("enable_history_search"), 

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

511 completer=DynamicCompleter( 

512 lambda: ThreadedCompleter(self.completer) 

513 if self.complete_in_thread and self.completer 

514 else self.completer 

515 ), 

516 history=self.history, 

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

518 accept_handler=accept, 

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

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

521 ) 

522 

523 def _create_search_buffer(self) -> Buffer: 

524 return Buffer(name=SEARCH_BUFFER) 

525 

526 def _create_layout(self) -> Layout: 

527 """ 

528 Create `Layout` for this prompt. 

529 """ 

530 dyncond = self._dyncond 

531 

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

533 # a multiline prompt.) 

534 ( 

535 has_before_fragments, 

536 get_prompt_text_1, 

537 get_prompt_text_2, 

538 ) = _split_multiline_prompt(self._get_prompt) 

539 

540 default_buffer = self.default_buffer 

541 search_buffer = self.search_buffer 

542 

543 # Create processors list. 

544 @Condition 

545 def display_placeholder() -> bool: 

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

547 

548 all_input_processors = [ 

549 HighlightIncrementalSearchProcessor(), 

550 HighlightSelectionProcessor(), 

551 ConditionalProcessor( 

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

553 ), 

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

555 DisplayMultipleCursors(), 

556 # Users can insert processors here. 

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

558 ConditionalProcessor( 

559 AfterInput(lambda: self.placeholder), 

560 filter=display_placeholder, 

561 ), 

562 ] 

563 

564 # Create bottom toolbars. 

565 bottom_toolbar = ConditionalContainer( 

566 Window( 

567 FormattedTextControl( 

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

569 ), 

570 style="class:bottom-toolbar", 

571 dont_extend_height=True, 

572 height=Dimension(min=1), 

573 ), 

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

575 & ~is_done 

576 & renderer_height_is_known, 

577 ) 

578 

579 search_toolbar = SearchToolbar( 

580 search_buffer, ignore_case=dyncond("search_ignore_case") 

581 ) 

582 

583 search_buffer_control = SearchBufferControl( 

584 buffer=search_buffer, 

585 input_processors=[ReverseSearchProcessor()], 

586 ignore_case=dyncond("search_ignore_case"), 

587 ) 

588 

589 system_toolbar = SystemToolbar( 

590 enable_global_bindings=dyncond("enable_system_prompt") 

591 ) 

592 

593 def get_search_buffer_control() -> SearchBufferControl: 

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

595 if is_true(self.multiline): 

596 return search_toolbar.control 

597 else: 

598 return search_buffer_control 

599 

600 default_buffer_control = BufferControl( 

601 buffer=default_buffer, 

602 search_buffer_control=get_search_buffer_control, 

603 input_processors=all_input_processors, 

604 include_default_input_processors=False, 

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

606 preview_search=True, 

607 ) 

608 

609 default_buffer_window = Window( 

610 default_buffer_control, 

611 height=self._get_default_buffer_control_height, 

612 get_line_prefix=partial( 

613 self._get_line_prefix, get_prompt_text_2=get_prompt_text_2 

614 ), 

615 wrap_lines=dyncond("wrap_lines"), 

616 ) 

617 

618 @Condition 

619 def multi_column_complete_style() -> bool: 

620 return self.complete_style == CompleteStyle.MULTI_COLUMN 

621 

622 # Build the layout. 

623 layout = HSplit( 

624 [ 

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

626 FloatContainer( 

627 HSplit( 

628 [ 

629 ConditionalContainer( 

630 Window( 

631 FormattedTextControl(get_prompt_text_1), 

632 dont_extend_height=True, 

633 ), 

634 Condition(has_before_fragments), 

635 ), 

636 ConditionalContainer( 

637 default_buffer_window, 

638 Condition( 

639 lambda: get_app().layout.current_control 

640 != search_buffer_control 

641 ), 

642 ), 

643 ConditionalContainer( 

644 Window(search_buffer_control), 

645 Condition( 

646 lambda: get_app().layout.current_control 

647 == search_buffer_control 

648 ), 

649 ), 

650 ] 

651 ), 

652 [ 

653 # Completion menus. 

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

655 # transparent, because the shape is not always 

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

657 Float( 

658 xcursor=True, 

659 ycursor=True, 

660 transparent=True, 

661 content=CompletionsMenu( 

662 max_height=16, 

663 scroll_offset=1, 

664 extra_filter=has_focus(default_buffer) 

665 & ~multi_column_complete_style, 

666 ), 

667 ), 

668 Float( 

669 xcursor=True, 

670 ycursor=True, 

671 transparent=True, 

672 content=MultiColumnCompletionsMenu( 

673 show_meta=True, 

674 extra_filter=has_focus(default_buffer) 

675 & multi_column_complete_style, 

676 ), 

677 ), 

678 # The right prompt. 

679 Float( 

680 right=0, 

681 top=0, 

682 hide_when_covering_content=True, 

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

684 ), 

685 ], 

686 ), 

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

688 ConditionalContainer( 

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

690 ), 

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

692 ConditionalContainer( 

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

694 dyncond("multiline") & has_arg, 

695 ), 

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

697 bottom_toolbar, 

698 ] 

699 ) 

700 

701 return Layout(layout, default_buffer_window) 

702 

703 def _create_application( 

704 self, editing_mode: EditingMode, erase_when_done: bool 

705 ) -> Application[_T]: 

706 """ 

707 Create the `Application` object. 

708 """ 

709 dyncond = self._dyncond 

710 

711 # Default key bindings. 

712 auto_suggest_bindings = load_auto_suggest_bindings() 

713 open_in_editor_bindings = load_open_in_editor_bindings() 

714 prompt_bindings = self._create_prompt_bindings() 

715 

716 # Create application 

717 application: Application[_T] = Application( 

718 layout=self.layout, 

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

720 style_transformation=merge_style_transformations( 

721 [ 

722 DynamicStyleTransformation(lambda: self.style_transformation), 

723 ConditionalStyleTransformation( 

724 SwapLightAndDarkStyleTransformation(), 

725 dyncond("swap_light_and_dark_colors"), 

726 ), 

727 ] 

728 ), 

729 include_default_pygments_style=dyncond("include_default_pygments_style"), 

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

731 key_bindings=merge_key_bindings( 

732 [ 

733 merge_key_bindings( 

734 [ 

735 auto_suggest_bindings, 

736 ConditionalKeyBindings( 

737 open_in_editor_bindings, 

738 dyncond("enable_open_in_editor") 

739 & has_focus(DEFAULT_BUFFER), 

740 ), 

741 prompt_bindings, 

742 ] 

743 ), 

744 DynamicKeyBindings(lambda: self.key_bindings), 

745 ] 

746 ), 

747 mouse_support=dyncond("mouse_support"), 

748 editing_mode=editing_mode, 

749 erase_when_done=erase_when_done, 

750 reverse_vi_search_direction=True, 

751 color_depth=lambda: self.color_depth, 

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

753 refresh_interval=self.refresh_interval, 

754 input=self._input, 

755 output=self._output, 

756 ) 

757 

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

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

760 # 'multiline' property dynamic. 

761 """ 

762 def on_render(app): 

763 multiline = is_true(self.multiline) 

764 current_control = app.layout.current_control 

765 

766 if multiline: 

767 if current_control == search_buffer_control: 

768 app.layout.current_control = search_toolbar.control 

769 app.invalidate() 

770 else: 

771 if current_control == search_toolbar.control: 

772 app.layout.current_control = search_buffer_control 

773 app.invalidate() 

774 

775 app.on_render += on_render 

776 """ 

777 

778 return application 

779 

780 def _create_prompt_bindings(self) -> KeyBindings: 

781 """ 

782 Create the KeyBindings for a prompt application. 

783 """ 

784 kb = KeyBindings() 

785 handle = kb.add 

786 default_focused = has_focus(DEFAULT_BUFFER) 

787 

788 @Condition 

789 def do_accept() -> bool: 

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

791 DEFAULT_BUFFER 

792 ) 

793 

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

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

796 "Accept input when enter has been pressed." 

797 self.default_buffer.validate_and_handle() 

798 

799 @Condition 

800 def readline_complete_style() -> bool: 

801 return self.complete_style == CompleteStyle.READLINE_LIKE 

802 

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

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

805 "Display completions (like Readline)." 

806 display_completions_like_readline(event) 

807 

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

809 @handle("<sigint>") 

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

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

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

813 

814 @Condition 

815 def ctrl_d_condition() -> bool: 

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

817 and empty.""" 

818 app = get_app() 

819 return ( 

820 app.current_buffer.name == DEFAULT_BUFFER 

821 and not app.current_buffer.text 

822 ) 

823 

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

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

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

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

828 

829 suspend_supported = Condition(suspend_to_background_supported) 

830 

831 @Condition 

832 def enable_suspend() -> bool: 

833 return to_filter(self.enable_suspend)() 

834 

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

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

837 """ 

838 Suspend process to background. 

839 """ 

840 event.app.suspend_to_background() 

841 

842 return kb 

843 

844 def prompt( 

845 self, 

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

847 # in this PromptSession. 

848 message: AnyFormattedText | None = None, 

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

850 # positional argument. 

851 *, 

852 editing_mode: EditingMode | None = None, 

853 refresh_interval: float | None = None, 

854 vi_mode: bool | None = None, 

855 lexer: Lexer | None = None, 

856 completer: Completer | None = None, 

857 complete_in_thread: bool | None = None, 

858 is_password: bool | None = None, 

859 key_bindings: KeyBindingsBase | None = None, 

860 bottom_toolbar: AnyFormattedText | None = None, 

861 style: BaseStyle | None = None, 

862 color_depth: ColorDepth | None = None, 

863 cursor: AnyCursorShapeConfig | None = None, 

864 include_default_pygments_style: FilterOrBool | None = None, 

865 style_transformation: StyleTransformation | None = None, 

866 swap_light_and_dark_colors: FilterOrBool | None = None, 

867 rprompt: AnyFormattedText | None = None, 

868 multiline: FilterOrBool | None = None, 

869 prompt_continuation: PromptContinuationText | None = None, 

870 wrap_lines: FilterOrBool | None = None, 

871 enable_history_search: FilterOrBool | None = None, 

872 search_ignore_case: FilterOrBool | None = None, 

873 complete_while_typing: FilterOrBool | None = None, 

874 validate_while_typing: FilterOrBool | None = None, 

875 complete_style: CompleteStyle | None = None, 

876 auto_suggest: AutoSuggest | None = None, 

877 validator: Validator | None = None, 

878 clipboard: Clipboard | None = None, 

879 mouse_support: FilterOrBool | None = None, 

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

881 placeholder: AnyFormattedText | None = None, 

882 reserve_space_for_menu: int | None = None, 

883 enable_system_prompt: FilterOrBool | None = None, 

884 enable_suspend: FilterOrBool | None = None, 

885 enable_open_in_editor: FilterOrBool | None = None, 

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

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

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

889 default: str | Document = "", 

890 accept_default: bool = False, 

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

892 set_exception_handler: bool = True, 

893 handle_sigint: bool = True, 

894 in_thread: bool = False, 

895 ) -> _T: 

896 """ 

897 Display the prompt. 

898 

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

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

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

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

903 but also to the next prompts. 

904 

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

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

907 ``DummyCompleter``, ``DummyValidator`` or ``DummyAutoSuggest`` instance 

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

909 

910 Additional arguments, specific for this prompt: 

911 

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

913 by the user). 

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

915 value without allowing the user to edit the input. 

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

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

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

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

920 

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

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

923 (for exit). 

924 """ 

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

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

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

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

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

930 

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

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

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

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

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

936 # to verify the code. 

937 if message is not None: 

938 self.message = message 

939 if editing_mode is not None: 

940 self.editing_mode = editing_mode 

941 if refresh_interval is not None: 

942 self.refresh_interval = refresh_interval 

943 if vi_mode: 

944 self.editing_mode = EditingMode.VI 

945 if lexer is not None: 

946 self.lexer = lexer 

947 if completer is not None: 

948 self.completer = completer 

949 if complete_in_thread is not None: 

950 self.complete_in_thread = complete_in_thread 

951 if is_password is not None: 

952 self.is_password = is_password 

953 if key_bindings is not None: 

954 self.key_bindings = key_bindings 

955 if bottom_toolbar is not None: 

956 self.bottom_toolbar = bottom_toolbar 

957 if style is not None: 

958 self.style = style 

959 if color_depth is not None: 

960 self.color_depth = color_depth 

961 if cursor is not None: 

962 self.cursor = cursor 

963 if include_default_pygments_style is not None: 

964 self.include_default_pygments_style = include_default_pygments_style 

965 if style_transformation is not None: 

966 self.style_transformation = style_transformation 

967 if swap_light_and_dark_colors is not None: 

968 self.swap_light_and_dark_colors = swap_light_and_dark_colors 

969 if rprompt is not None: 

970 self.rprompt = rprompt 

971 if multiline is not None: 

972 self.multiline = multiline 

973 if prompt_continuation is not None: 

974 self.prompt_continuation = prompt_continuation 

975 if wrap_lines is not None: 

976 self.wrap_lines = wrap_lines 

977 if enable_history_search is not None: 

978 self.enable_history_search = enable_history_search 

979 if search_ignore_case is not None: 

980 self.search_ignore_case = search_ignore_case 

981 if complete_while_typing is not None: 

982 self.complete_while_typing = complete_while_typing 

983 if validate_while_typing is not None: 

984 self.validate_while_typing = validate_while_typing 

985 if complete_style is not None: 

986 self.complete_style = complete_style 

987 if auto_suggest is not None: 

988 self.auto_suggest = auto_suggest 

989 if validator is not None: 

990 self.validator = validator 

991 if clipboard is not None: 

992 self.clipboard = clipboard 

993 if mouse_support is not None: 

994 self.mouse_support = mouse_support 

995 if input_processors is not None: 

996 self.input_processors = input_processors 

997 if placeholder is not None: 

998 self.placeholder = placeholder 

999 if reserve_space_for_menu is not None: 

1000 self.reserve_space_for_menu = reserve_space_for_menu 

1001 if enable_system_prompt is not None: 

1002 self.enable_system_prompt = enable_system_prompt 

1003 if enable_suspend is not None: 

1004 self.enable_suspend = enable_suspend 

1005 if enable_open_in_editor is not None: 

1006 self.enable_open_in_editor = enable_open_in_editor 

1007 if tempfile_suffix is not None: 

1008 self.tempfile_suffix = tempfile_suffix 

1009 if tempfile is not None: 

1010 self.tempfile = tempfile 

1011 

1012 self._add_pre_run_callables(pre_run, accept_default) 

1013 self.default_buffer.reset( 

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

1015 ) 

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

1017 

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

1019 # dumb prompt. 

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

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

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

1023 

1024 return self.app.run( 

1025 set_exception_handler=set_exception_handler, 

1026 in_thread=in_thread, 

1027 handle_sigint=handle_sigint, 

1028 ) 

1029 

1030 @contextmanager 

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

1032 """ 

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

1034 

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

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

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

1038 

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

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

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

1042 right before the cursor. 

1043 """ 

1044 # Send prompt to output. 

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

1046 self.output.flush() 

1047 

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

1049 key_bindings: KeyBindingsBase = self._create_prompt_bindings() 

1050 if self.key_bindings: 

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

1052 

1053 # Create and run application. 

1054 application = cast( 

1055 Application[_T], 

1056 Application( 

1057 input=self.input, 

1058 output=DummyOutput(), 

1059 layout=self.layout, 

1060 key_bindings=key_bindings, 

1061 ), 

1062 ) 

1063 

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

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

1066 self.output.flush() 

1067 

1068 self.default_buffer.on_text_changed += on_text_changed 

1069 

1070 try: 

1071 yield application 

1072 finally: 

1073 # Render line ending. 

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

1075 self.output.flush() 

1076 

1077 self.default_buffer.on_text_changed -= on_text_changed 

1078 

1079 async def prompt_async( 

1080 self, 

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

1082 # in this PromptSession. 

1083 message: AnyFormattedText | None = None, 

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

1085 # positional argument. 

1086 *, 

1087 editing_mode: EditingMode | None = None, 

1088 refresh_interval: float | None = None, 

1089 vi_mode: bool | None = None, 

1090 lexer: Lexer | None = None, 

1091 completer: Completer | None = None, 

1092 complete_in_thread: bool | None = None, 

1093 is_password: bool | None = None, 

1094 key_bindings: KeyBindingsBase | None = None, 

1095 bottom_toolbar: AnyFormattedText | None = None, 

1096 style: BaseStyle | None = None, 

1097 color_depth: ColorDepth | None = None, 

1098 cursor: CursorShapeConfig | None = None, 

1099 include_default_pygments_style: FilterOrBool | None = None, 

1100 style_transformation: StyleTransformation | None = None, 

1101 swap_light_and_dark_colors: FilterOrBool | None = None, 

1102 rprompt: AnyFormattedText | None = None, 

1103 multiline: FilterOrBool | None = None, 

1104 prompt_continuation: PromptContinuationText | None = None, 

1105 wrap_lines: FilterOrBool | None = None, 

1106 enable_history_search: FilterOrBool | None = None, 

1107 search_ignore_case: FilterOrBool | None = None, 

1108 complete_while_typing: FilterOrBool | None = None, 

1109 validate_while_typing: FilterOrBool | None = None, 

1110 complete_style: CompleteStyle | None = None, 

1111 auto_suggest: AutoSuggest | None = None, 

1112 validator: Validator | None = None, 

1113 clipboard: Clipboard | None = None, 

1114 mouse_support: FilterOrBool | None = None, 

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

1116 placeholder: AnyFormattedText | None = None, 

1117 reserve_space_for_menu: int | None = None, 

1118 enable_system_prompt: FilterOrBool | None = None, 

1119 enable_suspend: FilterOrBool | None = None, 

1120 enable_open_in_editor: FilterOrBool | None = None, 

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

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

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

1124 default: str | Document = "", 

1125 accept_default: bool = False, 

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

1127 set_exception_handler: bool = True, 

1128 handle_sigint: bool = True, 

1129 ) -> _T: 

1130 if message is not None: 

1131 self.message = message 

1132 if editing_mode is not None: 

1133 self.editing_mode = editing_mode 

1134 if refresh_interval is not None: 

1135 self.refresh_interval = refresh_interval 

1136 if vi_mode: 

1137 self.editing_mode = EditingMode.VI 

1138 if lexer is not None: 

1139 self.lexer = lexer 

1140 if completer is not None: 

1141 self.completer = completer 

1142 if complete_in_thread is not None: 

1143 self.complete_in_thread = complete_in_thread 

1144 if is_password is not None: 

1145 self.is_password = is_password 

1146 if key_bindings is not None: 

1147 self.key_bindings = key_bindings 

1148 if bottom_toolbar is not None: 

1149 self.bottom_toolbar = bottom_toolbar 

1150 if style is not None: 

1151 self.style = style 

1152 if color_depth is not None: 

1153 self.color_depth = color_depth 

1154 if cursor is not None: 

1155 self.cursor = cursor 

1156 if include_default_pygments_style is not None: 

1157 self.include_default_pygments_style = include_default_pygments_style 

1158 if style_transformation is not None: 

1159 self.style_transformation = style_transformation 

1160 if swap_light_and_dark_colors is not None: 

1161 self.swap_light_and_dark_colors = swap_light_and_dark_colors 

1162 if rprompt is not None: 

1163 self.rprompt = rprompt 

1164 if multiline is not None: 

1165 self.multiline = multiline 

1166 if prompt_continuation is not None: 

1167 self.prompt_continuation = prompt_continuation 

1168 if wrap_lines is not None: 

1169 self.wrap_lines = wrap_lines 

1170 if enable_history_search is not None: 

1171 self.enable_history_search = enable_history_search 

1172 if search_ignore_case is not None: 

1173 self.search_ignore_case = search_ignore_case 

1174 if complete_while_typing is not None: 

1175 self.complete_while_typing = complete_while_typing 

1176 if validate_while_typing is not None: 

1177 self.validate_while_typing = validate_while_typing 

1178 if complete_style is not None: 

1179 self.complete_style = complete_style 

1180 if auto_suggest is not None: 

1181 self.auto_suggest = auto_suggest 

1182 if validator is not None: 

1183 self.validator = validator 

1184 if clipboard is not None: 

1185 self.clipboard = clipboard 

1186 if mouse_support is not None: 

1187 self.mouse_support = mouse_support 

1188 if input_processors is not None: 

1189 self.input_processors = input_processors 

1190 if placeholder is not None: 

1191 self.placeholder = placeholder 

1192 if reserve_space_for_menu is not None: 

1193 self.reserve_space_for_menu = reserve_space_for_menu 

1194 if enable_system_prompt is not None: 

1195 self.enable_system_prompt = enable_system_prompt 

1196 if enable_suspend is not None: 

1197 self.enable_suspend = enable_suspend 

1198 if enable_open_in_editor is not None: 

1199 self.enable_open_in_editor = enable_open_in_editor 

1200 if tempfile_suffix is not None: 

1201 self.tempfile_suffix = tempfile_suffix 

1202 if tempfile is not None: 

1203 self.tempfile = tempfile 

1204 

1205 self._add_pre_run_callables(pre_run, accept_default) 

1206 self.default_buffer.reset( 

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

1208 ) 

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

1210 

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

1212 # dumb prompt. 

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

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

1215 return await dump_app.run_async(handle_sigint=handle_sigint) 

1216 

1217 return await self.app.run_async( 

1218 set_exception_handler=set_exception_handler, handle_sigint=handle_sigint 

1219 ) 

1220 

1221 def _add_pre_run_callables( 

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

1223 ) -> None: 

1224 def pre_run2() -> None: 

1225 if pre_run: 

1226 pre_run() 

1227 

1228 if accept_default: 

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

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

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

1232 # display the default value. 

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

1234 

1235 self.app.pre_run_callables.append(pre_run2) 

1236 

1237 @property 

1238 def editing_mode(self) -> EditingMode: 

1239 return self.app.editing_mode 

1240 

1241 @editing_mode.setter 

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

1243 self.app.editing_mode = value 

1244 

1245 def _get_default_buffer_control_height(self) -> Dimension: 

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

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

1248 if ( 

1249 self.completer is not None 

1250 and self.complete_style != CompleteStyle.READLINE_LIKE 

1251 ): 

1252 space = self.reserve_space_for_menu 

1253 else: 

1254 space = 0 

1255 

1256 if space and not get_app().is_done: 

1257 buff = self.default_buffer 

1258 

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

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

1261 # soon. 

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

1263 return Dimension(min=space) 

1264 

1265 return Dimension() 

1266 

1267 def _get_prompt(self) -> StyleAndTextTuples: 

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

1269 

1270 def _get_continuation( 

1271 self, width: int, line_number: int, wrap_count: int 

1272 ) -> StyleAndTextTuples: 

1273 """ 

1274 Insert the prompt continuation. 

1275 

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

1277 be used.) 

1278 :param line_number: 

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

1280 """ 

1281 prompt_continuation = self.prompt_continuation 

1282 

1283 if callable(prompt_continuation): 

1284 continuation: AnyFormattedText = prompt_continuation( 

1285 width, line_number, wrap_count 

1286 ) 

1287 else: 

1288 continuation = prompt_continuation 

1289 

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

1291 # the actual prompt. 

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

1293 continuation = " " * width 

1294 

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

1296 

1297 def _get_line_prefix( 

1298 self, 

1299 line_number: int, 

1300 wrap_count: int, 

1301 get_prompt_text_2: _StyleAndTextTuplesCallable, 

1302 ) -> StyleAndTextTuples: 

1303 """ 

1304 Return whatever needs to be inserted before every line. 

1305 (the prompt, or a line continuation.) 

1306 """ 

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

1308 if line_number == 0 and wrap_count == 0: 

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

1310 return self._inline_arg() 

1311 else: 

1312 return get_prompt_text_2() 

1313 

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

1315 prompt_width = get_cwidth(fragment_list_to_text(get_prompt_text_2())) 

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

1317 

1318 def _get_arg_text(self) -> StyleAndTextTuples: 

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

1320 arg = self.app.key_processor.arg 

1321 if arg is None: 

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

1323 return [] 

1324 

1325 if arg == "-": 

1326 arg = "-1" 

1327 

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

1329 

1330 def _inline_arg(self) -> StyleAndTextTuples: 

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

1332 app = get_app() 

1333 if app.key_processor.arg is None: 

1334 return [] 

1335 else: 

1336 arg = app.key_processor.arg 

1337 

1338 return [ 

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

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

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

1342 ] 

1343 

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

1345 # backward-compatibility. 

1346 

1347 @property 

1348 def input(self) -> Input: 

1349 return self.app.input 

1350 

1351 @property 

1352 def output(self) -> Output: 

1353 return self.app.output 

1354 

1355 

1356def prompt( 

1357 message: AnyFormattedText | None = None, 

1358 *, 

1359 history: History | None = None, 

1360 editing_mode: EditingMode | None = None, 

1361 refresh_interval: float | None = None, 

1362 vi_mode: bool | None = None, 

1363 lexer: Lexer | None = None, 

1364 completer: Completer | None = None, 

1365 complete_in_thread: bool | None = None, 

1366 is_password: bool | None = None, 

1367 key_bindings: KeyBindingsBase | None = None, 

1368 bottom_toolbar: AnyFormattedText | None = None, 

1369 style: BaseStyle | None = None, 

1370 color_depth: ColorDepth | None = None, 

1371 cursor: AnyCursorShapeConfig = None, 

1372 include_default_pygments_style: FilterOrBool | None = None, 

1373 style_transformation: StyleTransformation | None = None, 

1374 swap_light_and_dark_colors: FilterOrBool | None = None, 

1375 rprompt: AnyFormattedText | None = None, 

1376 multiline: FilterOrBool | None = None, 

1377 prompt_continuation: PromptContinuationText | None = None, 

1378 wrap_lines: FilterOrBool | None = None, 

1379 enable_history_search: FilterOrBool | None = None, 

1380 search_ignore_case: FilterOrBool | None = None, 

1381 complete_while_typing: FilterOrBool | None = None, 

1382 validate_while_typing: FilterOrBool | None = None, 

1383 complete_style: CompleteStyle | None = None, 

1384 auto_suggest: AutoSuggest | None = None, 

1385 validator: Validator | None = None, 

1386 clipboard: Clipboard | None = None, 

1387 mouse_support: FilterOrBool | None = None, 

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

1389 placeholder: AnyFormattedText | None = None, 

1390 reserve_space_for_menu: int | None = None, 

1391 enable_system_prompt: FilterOrBool | None = None, 

1392 enable_suspend: FilterOrBool | None = None, 

1393 enable_open_in_editor: FilterOrBool | None = None, 

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

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

1396 in_thread: bool = False, 

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

1398 default: str = "", 

1399 accept_default: bool = False, 

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

1401) -> str: 

1402 """ 

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

1404 instance for every call. 

1405 """ 

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

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

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

1409 

1410 return session.prompt( 

1411 message, 

1412 editing_mode=editing_mode, 

1413 refresh_interval=refresh_interval, 

1414 vi_mode=vi_mode, 

1415 lexer=lexer, 

1416 completer=completer, 

1417 complete_in_thread=complete_in_thread, 

1418 is_password=is_password, 

1419 key_bindings=key_bindings, 

1420 bottom_toolbar=bottom_toolbar, 

1421 style=style, 

1422 color_depth=color_depth, 

1423 cursor=cursor, 

1424 include_default_pygments_style=include_default_pygments_style, 

1425 style_transformation=style_transformation, 

1426 swap_light_and_dark_colors=swap_light_and_dark_colors, 

1427 rprompt=rprompt, 

1428 multiline=multiline, 

1429 prompt_continuation=prompt_continuation, 

1430 wrap_lines=wrap_lines, 

1431 enable_history_search=enable_history_search, 

1432 search_ignore_case=search_ignore_case, 

1433 complete_while_typing=complete_while_typing, 

1434 validate_while_typing=validate_while_typing, 

1435 complete_style=complete_style, 

1436 auto_suggest=auto_suggest, 

1437 validator=validator, 

1438 clipboard=clipboard, 

1439 mouse_support=mouse_support, 

1440 input_processors=input_processors, 

1441 placeholder=placeholder, 

1442 reserve_space_for_menu=reserve_space_for_menu, 

1443 enable_system_prompt=enable_system_prompt, 

1444 enable_suspend=enable_suspend, 

1445 enable_open_in_editor=enable_open_in_editor, 

1446 tempfile_suffix=tempfile_suffix, 

1447 tempfile=tempfile, 

1448 default=default, 

1449 accept_default=accept_default, 

1450 pre_run=pre_run, 

1451 in_thread=in_thread, 

1452 ) 

1453 

1454 

1455prompt.__doc__ = PromptSession.prompt.__doc__ 

1456 

1457 

1458def create_confirm_session( 

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

1460) -> PromptSession[bool]: 

1461 """ 

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

1463 """ 

1464 bindings = KeyBindings() 

1465 

1466 @bindings.add("y") 

1467 @bindings.add("Y") 

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

1469 session.default_buffer.text = "y" 

1470 event.app.exit(result=True) 

1471 

1472 @bindings.add("n") 

1473 @bindings.add("N") 

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

1475 session.default_buffer.text = "n" 

1476 event.app.exit(result=False) 

1477 

1478 @bindings.add(Keys.Any) 

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

1480 "Disallow inserting other text." 

1481 pass 

1482 

1483 complete_message = merge_formatted_text([message, suffix]) 

1484 session: PromptSession[bool] = PromptSession( 

1485 complete_message, key_bindings=bindings 

1486 ) 

1487 return session 

1488 

1489 

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

1491 """ 

1492 Display a confirmation prompt that returns True/False. 

1493 """ 

1494 session = create_confirm_session(message, suffix) 

1495 return session.prompt()