diff --git a/src/prompt_toolkit/shortcuts/prompt.py b/src/prompt_toolkit/shortcuts/prompt.py index 68cfeb9aa..76a75fcb0 100644 --- a/src/prompt_toolkit/shortcuts/prompt.py +++ b/src/prompt_toolkit/shortcuts/prompt.py @@ -89,7 +89,7 @@ FormattedTextControl, SearchBufferControl, ) -from prompt_toolkit.layout.dimension import Dimension +from prompt_toolkit.layout.dimension import AnyDimension, Dimension from prompt_toolkit.layout.layout import Layout from prompt_toolkit.layout.menus import CompletionsMenu, MultiColumnCompletionsMenu from prompt_toolkit.layout.processors import ( @@ -307,6 +307,10 @@ class PromptSession(Generic[_T]): This can also be a callable that returns (formatted) text. :param bottom_toolbar: Formatted text or callable that returns formatted text to be displayed at the bottom of the screen. + :param bottom_toolbar_height: Height of the bottom toolbar. Can be an + integer, a :class:`~prompt_toolkit.layout.Dimension`, or `None` for + dynamic height (useful for multiline content). Defaults to + ``Dimension(min=1)``. :param prompt_continuation: Text that needs to be displayed for a multiline prompt continuation. This can either be formatted text or a callable that takes a `prompt_width`, `line_number` and `wrap_count` as input @@ -323,6 +327,10 @@ class PromptSession(Generic[_T]): :param show_frame: `bool` or :class:`~prompt_toolkit.filters.Filter`. When True, surround the input with a frame. + :param validation_toolbar_height: Height of the validation toolbar. Can be + an integer, a :class:`~prompt_toolkit.layout.Dimension`, or `None` for + dynamic height (useful for multiline validation error messages). + Defaults to 1. :param refresh_interval: (number; in seconds) When given, refresh the UI every so many seconds. :param input: `Input` object. (Note that the preferred way to change the @@ -373,6 +381,8 @@ class PromptSession(Generic[_T]): "tempfile_suffix", "tempfile", "show_frame", + "validation_toolbar_height", + "bottom_toolbar_height", ) def __init__( @@ -409,6 +419,7 @@ def __init__( prompt_continuation: PromptContinuationText | None = None, rprompt: AnyFormattedText = None, bottom_toolbar: AnyFormattedText = None, + bottom_toolbar_height: AnyDimension = Dimension(min=1), mouse_support: FilterOrBool = False, input_processors: list[Processor] | None = None, placeholder: AnyFormattedText | None = None, @@ -418,6 +429,7 @@ def __init__( tempfile: str | Callable[[], str] | None = None, refresh_interval: float = 0, show_frame: FilterOrBool = False, + validation_toolbar_height: AnyDimension = 1, input: Input | None = None, output: Output | None = None, interrupt_exception: type[BaseException] = KeyboardInterrupt, @@ -443,6 +455,7 @@ def __init__( self.is_password = is_password self.key_bindings = key_bindings self.bottom_toolbar = bottom_toolbar + self.bottom_toolbar_height = bottom_toolbar_height self.style = style self.style_transformation = style_transformation self.swap_light_and_dark_colors = swap_light_and_dark_colors @@ -472,6 +485,7 @@ def __init__( self.tempfile_suffix = tempfile_suffix self.tempfile = tempfile self.show_frame = show_frame + self.validation_toolbar_height = validation_toolbar_height self.interrupt_exception = interrupt_exception self.eof_exception = eof_exception @@ -588,7 +602,7 @@ def display_placeholder() -> bool: ), style="class:bottom-toolbar", dont_extend_height=True, - height=Dimension(min=1), + height=self.bottom_toolbar_height, ), filter=Condition(lambda: self.bottom_toolbar is not None) & ~is_done @@ -711,7 +725,10 @@ def multi_column_complete_style() -> bool: filter=dyncond("show_frame"), alternative_content=main_input_container, ), - ConditionalContainer(ValidationToolbar(), filter=~is_done), + ConditionalContainer( + ValidationToolbar(height=self.validation_toolbar_height), + filter=~is_done, + ), ConditionalContainer( system_toolbar, dyncond("enable_system_prompt") & ~is_done ), @@ -885,6 +902,7 @@ def prompt( is_password: bool | None = None, key_bindings: KeyBindingsBase | None = None, bottom_toolbar: AnyFormattedText | None = None, + bottom_toolbar_height: AnyDimension | None = None, style: BaseStyle | None = None, color_depth: ColorDepth | None = None, cursor: AnyCursorShapeConfig | None = None, @@ -913,6 +931,7 @@ def prompt( tempfile_suffix: str | Callable[[], str] | None = None, tempfile: str | Callable[[], str] | None = None, show_frame: FilterOrBool | None = None, + validation_toolbar_height: AnyDimension | None = None, # Following arguments are specific to the current `prompt()` call. default: str | Document = "", accept_default: bool = False, @@ -1039,6 +1058,10 @@ class itself. For these, passing in ``None`` will keep the current self.tempfile = tempfile if show_frame is not None: self.show_frame = show_frame + if validation_toolbar_height is not None: + self.validation_toolbar_height = validation_toolbar_height + if bottom_toolbar_height is not None: + self.bottom_toolbar_height = bottom_toolbar_height self._add_pre_run_callables(pre_run, accept_default) self.default_buffer.reset( @@ -1125,6 +1148,7 @@ async def prompt_async( is_password: bool | None = None, key_bindings: KeyBindingsBase | None = None, bottom_toolbar: AnyFormattedText | None = None, + bottom_toolbar_height: AnyDimension | None = None, style: BaseStyle | None = None, color_depth: ColorDepth | None = None, cursor: CursorShapeConfig | None = None, @@ -1153,6 +1177,7 @@ async def prompt_async( tempfile_suffix: str | Callable[[], str] | None = None, tempfile: str | Callable[[], str] | None = None, show_frame: FilterOrBool = False, + validation_toolbar_height: AnyDimension | None = None, # Following arguments are specific to the current `prompt()` call. default: str | Document = "", accept_default: bool = False, @@ -1236,6 +1261,10 @@ async def prompt_async( self.tempfile = tempfile if show_frame is not None: self.show_frame = show_frame + if validation_toolbar_height is not None: + self.validation_toolbar_height = validation_toolbar_height + if bottom_toolbar_height is not None: + self.bottom_toolbar_height = bottom_toolbar_height self._add_pre_run_callables(pre_run, accept_default) self.default_buffer.reset( @@ -1401,6 +1430,7 @@ def prompt( is_password: bool | None = None, key_bindings: KeyBindingsBase | None = None, bottom_toolbar: AnyFormattedText | None = None, + bottom_toolbar_height: AnyDimension | None = None, style: BaseStyle | None = None, color_depth: ColorDepth | None = None, cursor: AnyCursorShapeConfig = None, @@ -1429,6 +1459,7 @@ def prompt( tempfile_suffix: str | Callable[[], str] | None = None, tempfile: str | Callable[[], str] | None = None, show_frame: FilterOrBool | None = None, + validation_toolbar_height: AnyDimension | None = None, # Following arguments are specific to the current `prompt()` call. default: str = "", accept_default: bool = False, @@ -1457,6 +1488,7 @@ def prompt( is_password=is_password, key_bindings=key_bindings, bottom_toolbar=bottom_toolbar, + bottom_toolbar_height=bottom_toolbar_height, style=style, color_depth=color_depth, cursor=cursor, @@ -1485,6 +1517,7 @@ def prompt( tempfile_suffix=tempfile_suffix, tempfile=tempfile, show_frame=show_frame, + validation_toolbar_height=validation_toolbar_height, default=default, accept_default=accept_default, pre_run=pre_run, diff --git a/src/prompt_toolkit/widgets/toolbars.py b/src/prompt_toolkit/widgets/toolbars.py index c5deffc58..d4e40302a 100644 --- a/src/prompt_toolkit/widgets/toolbars.py +++ b/src/prompt_toolkit/widgets/toolbars.py @@ -40,7 +40,7 @@ UIContent, UIControl, ) -from prompt_toolkit.layout.dimension import Dimension +from prompt_toolkit.layout.dimension import AnyDimension, Dimension from prompt_toolkit.layout.processors import BeforeInput from prompt_toolkit.lexers import SimpleLexer from prompt_toolkit.search import SearchDirection @@ -342,7 +342,9 @@ def __pt_container__(self) -> Container: class ValidationToolbar: - def __init__(self, show_position: bool = False) -> None: + def __init__( + self, show_position: bool = False, height: AnyDimension = 1 + ) -> None: def get_formatted_text() -> StyleAndTextTuples: buff = get_app().current_buffer @@ -363,7 +365,7 @@ def get_formatted_text() -> StyleAndTextTuples: self.control = FormattedTextControl(get_formatted_text) self.container = ConditionalContainer( - content=Window(self.control, height=1), filter=has_validation_error + content=Window(self.control, height=height), filter=has_validation_error ) def __pt_container__(self) -> Container: