|
32 | 32 | import contextlib |
33 | 33 | import copy |
34 | 34 | import dataclasses |
| 35 | +import datetime |
35 | 36 | import functools |
36 | 37 | import glob |
37 | 38 | import inspect |
|
73 | 74 | from prompt_toolkit.application import create_app_session, get_app |
74 | 75 | from prompt_toolkit.auto_suggest import AutoSuggestFromHistory |
75 | 76 | from prompt_toolkit.completion import Completer, DummyCompleter |
76 | | -from prompt_toolkit.formatted_text import ANSI, FormattedText |
| 77 | +from prompt_toolkit.formatted_text import ANSI, AnyFormattedText |
77 | 78 | from prompt_toolkit.history import InMemoryHistory |
78 | 79 | from prompt_toolkit.input import DummyInput, create_input |
79 | 80 | from prompt_toolkit.key_binding import KeyBindings |
@@ -367,16 +368,17 @@ def __init__( |
367 | 368 | allow_redirection: bool = True, |
368 | 369 | auto_load_commands: bool = False, |
369 | 370 | auto_suggest: bool = True, |
370 | | - bottom_toolbar: bool = False, |
371 | 371 | complete_in_thread: bool = True, |
372 | 372 | command_sets: Iterable[CommandSet[Any]] | None = None, |
| 373 | + enable_bottom_toolbar: bool = False, |
| 374 | + enable_rprompt: bool = False, |
373 | 375 | include_ipy: bool = False, |
374 | 376 | include_py: bool = False, |
375 | 377 | intro: RenderableType = "", |
376 | 378 | multiline_commands: Iterable[str] | None = None, |
377 | 379 | persistent_history_file: str = "", |
378 | 380 | persistent_history_length: int = 1000, |
379 | | - refresh_interval: float = 0, |
| 381 | + refresh_interval: float = 0.0, |
380 | 382 | shortcuts: Mapping[str, str] | None = None, |
381 | 383 | silence_startup_script: bool = False, |
382 | 384 | startup_script: str = "", |
@@ -405,20 +407,23 @@ def __init__( |
405 | 407 | :param auto_suggest: If True, cmd2 will provide fish shell style auto-suggestions |
406 | 408 | based on history. User can press right-arrow key to accept the |
407 | 409 | provided suggestion. |
408 | | - :param bottom_toolbar: if ``True``, then a bottom toolbar will be displayed. |
409 | 410 | :param complete_in_thread: if ``True``, then completion will run in a separate thread. |
410 | 411 | :param command_sets: Provide CommandSet instances to load during cmd2 initialization. |
411 | 412 | This allows CommandSets with custom constructor parameters to be |
412 | 413 | loaded. This also allows the a set of CommandSets to be provided |
413 | 414 | when `auto_load_commands` is set to False |
| 415 | + :param enable_bottom_toolbar: if ``True``, enables a bottom toolbar while at the main prompt. |
| 416 | + Override ``get_bottom_toolbar()`` to define its content. |
| 417 | + :param enable_rprompt: if ``True``, enables a right prompt while at the main prompt. |
| 418 | + Override ``get_rprompt()`` to define its content. |
414 | 419 | :param include_ipy: should the "ipy" command be included for an embedded IPython shell |
415 | 420 | :param include_py: should the "py" command be included for an embedded Python shell |
416 | 421 | :param intro: introduction to display at startup |
417 | 422 | :param multiline_commands: Iterable of commands allowed to accept multi-line input |
418 | 423 | :param persistent_history_file: file path to load a persistent cmd2 command history from |
419 | 424 | :param persistent_history_length: max number of history items to write |
420 | 425 | to the persistent history file |
421 | | - :param refresh_interval: How often, in seconds, to refresh the UI. Defaults to 0. |
| 426 | + :param refresh_interval: How often, in seconds, to refresh the UI. Defaults to 0.0. |
422 | 427 | prompt-toolkit already refreshes the UI every time a key is pressed. |
423 | 428 | Set this value if you need the UI to update automatically without |
424 | 429 | user input (e.g., for displaying a clock or background status |
@@ -535,10 +540,14 @@ def __init__( |
535 | 540 | self._initialize_history(persistent_history_file) |
536 | 541 |
|
537 | 542 | # Create the main PromptSession |
538 | | - self.bottom_toolbar = bottom_toolbar |
539 | | - self.complete_in_thread = complete_in_thread |
540 | | - self.refresh_interval = refresh_interval |
541 | | - self.main_session = self._create_main_session(auto_suggest, completekey) |
| 543 | + self.main_session = self._create_main_session( |
| 544 | + auto_suggest=auto_suggest, |
| 545 | + complete_in_thread=complete_in_thread, |
| 546 | + completekey=completekey, |
| 547 | + enable_bottom_toolbar=enable_bottom_toolbar, |
| 548 | + enable_rprompt=enable_rprompt, |
| 549 | + refresh_interval=refresh_interval, |
| 550 | + ) |
542 | 551 |
|
543 | 552 | # The session currently holding focus (either the main REPL or a command's |
544 | 553 | # custom prompt). Completion and UI logic should reference this variable |
@@ -729,7 +738,16 @@ def _should_continue_multiline(self) -> bool: |
729 | 738 | # No macro found or already processed. The statement is complete. |
730 | 739 | return False |
731 | 740 |
|
732 | | - def _create_main_session(self, auto_suggest: bool, completekey: str) -> PromptSession[str]: |
| 741 | + def _create_main_session( |
| 742 | + self, |
| 743 | + *, |
| 744 | + auto_suggest: bool, |
| 745 | + complete_in_thread: bool, |
| 746 | + completekey: str, |
| 747 | + enable_bottom_toolbar: bool, |
| 748 | + enable_rprompt: bool, |
| 749 | + refresh_interval: float, |
| 750 | + ) -> PromptSession[str]: |
733 | 751 | """Create and return the main PromptSession for the application. |
734 | 752 |
|
735 | 753 | Builds an interactive session if self.stdin and self.stdout are TTYs. |
@@ -759,19 +777,19 @@ def _(event: Any) -> None: # pragma: no cover |
759 | 777 | # Base configuration |
760 | 778 | kwargs: dict[str, Any] = { |
761 | 779 | "auto_suggest": AutoSuggestFromHistory() if auto_suggest else None, |
762 | | - "bottom_toolbar": self.get_bottom_toolbar if self.bottom_toolbar else None, |
| 780 | + "bottom_toolbar": self.get_bottom_toolbar if enable_bottom_toolbar else None, |
763 | 781 | "color_depth": ColorDepth.TRUE_COLOR, |
764 | 782 | "complete_style": CompleteStyle.MULTI_COLUMN, |
765 | | - "complete_in_thread": self.complete_in_thread, |
| 783 | + "complete_in_thread": complete_in_thread, |
766 | 784 | "complete_while_typing": False, |
767 | 785 | "completer": Cmd2Completer(self), |
768 | 786 | "history": Cmd2History(item.raw for item in self.history), |
769 | 787 | "key_bindings": key_bindings, |
770 | 788 | "lexer": Cmd2Lexer(self), |
771 | 789 | "multiline": filters.Condition(self._should_continue_multiline), |
772 | 790 | "prompt_continuation": self.continuation_prompt, |
773 | | - "refresh_interval": self.refresh_interval, |
774 | | - "rprompt": self.get_rprompt, |
| 791 | + "refresh_interval": refresh_interval, |
| 792 | + "rprompt": self.get_rprompt if enable_rprompt else None, |
775 | 793 | "style": DynamicStyle(get_pt_theme), |
776 | 794 | } |
777 | 795 |
|
@@ -1983,49 +2001,35 @@ def ppretty( |
1983 | 2001 | end=end, |
1984 | 2002 | ) |
1985 | 2003 |
|
1986 | | - def get_bottom_toolbar(self) -> list[str | tuple[str, str]] | None: |
| 2004 | + def get_bottom_toolbar(self) -> AnyFormattedText: |
1987 | 2005 | """Get the bottom toolbar content. |
1988 | 2006 |
|
1989 | | - Returns None if `self.bottom_toolbar` is False. Otherwise, returns a |
1990 | | - list of tokens to populate the toolbar (which can span multiple lines). |
1991 | | -
|
1992 | | - NOTE: prompt-toolkit calls this method on every UI refresh (e.g., on every keypress |
1993 | | - and at scheduled refresh intervals). To ensure the CLI remains responsive, keep |
1994 | | - this function highly optimized. |
1995 | | - """ |
1996 | | - if not self.bottom_toolbar: |
1997 | | - return None |
1998 | | - |
1999 | | - import datetime |
2000 | | - import shutil |
| 2007 | + This method is called by prompt-toolkit while at the main prompt if ``enable_bottom_toolbar`` |
| 2008 | + was set to ``True`` during initialization. Because prompt-toolkit executes this callback |
| 2009 | + on every UI refresh (such as on every keypress or at scheduled refresh intervals), keeping |
| 2010 | + this function highly optimized is critical to ensuring the CLI remains responsive. |
2001 | 2011 |
|
2002 | | - # Get the current time in ISO format with 0.01s precision |
2003 | | - dt = datetime.datetime.now(datetime.timezone.utc).astimezone() |
2004 | | - now = dt.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-4] + dt.strftime("%z") |
2005 | | - left_text = sys.argv[0] |
| 2012 | + Override this if you want a bottom toolbar displaying contextual information useful for |
| 2013 | + your application. This could be information like the application name, current state, |
| 2014 | + or even a real-time clock. |
2006 | 2015 |
|
2007 | | - # Get terminal width to calculate padding for right-alignment |
2008 | | - cols, _ = shutil.get_terminal_size() |
2009 | | - padding_size = cols - len(left_text) - len(now) - 1 |
2010 | | - if padding_size < 1: |
2011 | | - padding_size = 1 |
2012 | | - padding = " " * padding_size |
| 2016 | + :return: Content to populate the bottom toolbar. |
| 2017 | + """ |
| 2018 | + return None |
2013 | 2019 |
|
2014 | | - # Return formatted text for prompt-toolkit |
2015 | | - return [ |
2016 | | - ("ansigreen", left_text), |
2017 | | - ("", padding), |
2018 | | - ("ansicyan", now), |
2019 | | - ] |
| 2020 | + def get_rprompt(self) -> AnyFormattedText: |
| 2021 | + """Provide text to populate the prompt-toolkit right prompt. |
2020 | 2022 |
|
2021 | | - def get_rprompt(self) -> str | FormattedText | None: |
2022 | | - """Provide text to populate prompt-toolkit right prompt with. |
| 2023 | + This method is called by prompt-toolkit while at the main prompt if ``enable_rprompt`` |
| 2024 | + was set to ``True`` during initialization. Because prompt-toolkit executes this callback |
| 2025 | + on every UI refresh (such as on every keypress or at scheduled refresh intervals), keeping |
| 2026 | + this function highly optimized is critical to ensuring the CLI remains responsive. |
2023 | 2027 |
|
2024 | | - Override this if you want a right-prompt displaying contetual information useful for your application. |
2025 | | - This could be information like current Git branch, time, current working directory, etc that is displayed |
2026 | | - without cluttering the main input area. |
| 2028 | + Override this if you want a right prompt displaying contextual information useful for |
| 2029 | + your application. This could be information like the current Git branch, time, or current |
| 2030 | + working directory that is displayed without cluttering the main input area. |
2027 | 2031 |
|
2028 | | - :return: any type of formatted text to display as the right prompt |
| 2032 | + :return: Content to populate the right prompt. |
2029 | 2033 | """ |
2030 | 2034 | return None |
2031 | 2035 |
|
@@ -2932,8 +2936,6 @@ def onecmd_plus_hooks( |
2932 | 2936 | command's stdout. |
2933 | 2937 | :return: True if running of commands should stop |
2934 | 2938 | """ |
2935 | | - import datetime |
2936 | | - |
2937 | 2939 | stop = False |
2938 | 2940 | statement = None |
2939 | 2941 |
|
|
0 commit comments