Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 23 additions & 9 deletions src/prompt_toolkit/shortcuts/prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,9 @@ class PromptSession(Generic[_T]):
and returns formatted text. When this is `None` (the default), then
`prompt_width` spaces will be used.
:param complete_style: ``CompleteStyle.COLUMN``,
``CompleteStyle.MULTI_COLUMN`` or ``CompleteStyle.READLINE_LIKE``.
``CompleteStyle.MULTI_COLUMN``, ``CompleteStyle.READLINE_LIKE`` or a
callable that returns a ``CompleteStyle``. The callable is evaluated
dynamically.
:param mouse_support: `bool` or :class:`~prompt_toolkit.filters.Filter`
to enable mouse support.
:param placeholder: Text to be displayed when no input has been given
Expand Down Expand Up @@ -397,7 +399,9 @@ def __init__(
completer: Completer | None = None,
complete_in_thread: bool = False,
reserve_space_for_menu: int = 8,
complete_style: CompleteStyle = CompleteStyle.COLUMN,
complete_style: CompleteStyle | Callable[[], CompleteStyle] = (
CompleteStyle.COLUMN
),
auto_suggest: AutoSuggest | None = None,
style: BaseStyle | None = None,
style_transformation: StyleTransformation | None = None,
Expand Down Expand Up @@ -524,7 +528,7 @@ def accept(buff: Buffer) -> bool:
lambda: (
is_true(self.complete_while_typing)
and not is_true(self.enable_history_search)
and not self.complete_style == CompleteStyle.READLINE_LIKE
and not self._get_complete_style() == CompleteStyle.READLINE_LIKE
)
),
validate_while_typing=dyncond("validate_while_typing"),
Expand Down Expand Up @@ -641,7 +645,7 @@ def get_search_buffer_control() -> SearchBufferControl:

@Condition
def multi_column_complete_style() -> bool:
return self.complete_style == CompleteStyle.MULTI_COLUMN
return self._get_complete_style() == CompleteStyle.MULTI_COLUMN

# Build the layout.

Expand Down Expand Up @@ -834,7 +838,7 @@ def _accept_input(event: E) -> None:

@Condition
def readline_complete_style() -> bool:
return self.complete_style == CompleteStyle.READLINE_LIKE
return self._get_complete_style() == CompleteStyle.READLINE_LIKE

@handle("tab", filter=readline_complete_style & default_focused)
def _complete_like_readline(event: E) -> None:
Expand Down Expand Up @@ -908,7 +912,7 @@ def prompt(
search_ignore_case: FilterOrBool | None = None,
complete_while_typing: FilterOrBool | None = None,
validate_while_typing: FilterOrBool | None = None,
complete_style: CompleteStyle | None = None,
complete_style: CompleteStyle | Callable[[], CompleteStyle] | None = None,
auto_suggest: AutoSuggest | None = None,
validator: Validator | None = None,
clipboard: Clipboard | None = None,
Expand Down Expand Up @@ -1148,7 +1152,7 @@ async def prompt_async(
search_ignore_case: FilterOrBool | None = None,
complete_while_typing: FilterOrBool | None = None,
validate_while_typing: FilterOrBool | None = None,
complete_style: CompleteStyle | None = None,
complete_style: CompleteStyle | Callable[[], CompleteStyle] | None = None,
auto_suggest: AutoSuggest | None = None,
validator: Validator | None = None,
clipboard: Clipboard | None = None,
Expand Down Expand Up @@ -1291,7 +1295,7 @@ def _get_default_buffer_control_height(self) -> Dimension:
# layout has at least a minimal height in order to display it.
if (
self.completer is not None
and self.complete_style != CompleteStyle.READLINE_LIKE
and self._get_complete_style() != CompleteStyle.READLINE_LIKE
):
space = self.reserve_space_for_menu
else:
Expand All @@ -1308,6 +1312,16 @@ def _get_default_buffer_control_height(self) -> Dimension:

return Dimension()

def _get_complete_style(self) -> CompleteStyle:
complete_style = self.complete_style

if callable(complete_style):
style: CompleteStyle = complete_style()
else:
style = complete_style

return style

def _get_prompt(self) -> StyleAndTextTuples:
return to_formatted_text(self.message, style="class:prompt")

Expand Down Expand Up @@ -1424,7 +1438,7 @@ def prompt(
search_ignore_case: FilterOrBool | None = None,
complete_while_typing: FilterOrBool | None = None,
validate_while_typing: FilterOrBool | None = None,
complete_style: CompleteStyle | None = None,
complete_style: CompleteStyle | Callable[[], CompleteStyle] | None = None,
auto_suggest: AutoSuggest | None = None,
validator: Validator | None = None,
clipboard: Clipboard | None = None,
Expand Down
34 changes: 33 additions & 1 deletion tests/test_shortcuts.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
from __future__ import annotations

from prompt_toolkit.application.current import get_app, set_app
from prompt_toolkit.shortcuts import print_container
from prompt_toolkit.shortcuts.prompt import _split_multiline_prompt
from prompt_toolkit.shortcuts.prompt import (
CompleteStyle,
PromptSession,
_split_multiline_prompt,
)
from prompt_toolkit.widgets import Frame, TextArea


Expand Down Expand Up @@ -55,6 +60,33 @@ def test_split_multiline_prompt():
assert first_input_line() == [("class:testclass", "a"), ("class:testclass", "b")]


def test_callable_complete_style_can_read_current_buffer():
complete_style_calls: list[str] = []

def get_complete_style() -> CompleteStyle:
text = get_app().current_buffer.text
complete_style_calls.append(text)
if text:
return CompleteStyle.MULTI_COLUMN
return CompleteStyle.READLINE_LIKE

session: PromptSession[str] = PromptSession(
complete_style=get_complete_style, validate_while_typing=False
)

# The callable requires an active application, so it should not be called
# while constructing the session.
assert complete_style_calls == []

with set_app(session.app):
assert session.default_buffer.complete_while_typing() is False

session.default_buffer.text = "text"
assert session.default_buffer.complete_while_typing() is True

assert complete_style_calls == ["", "text"]


def test_print_container(tmpdir):
# Call `print_container`, render to a dummy file.
f = tmpdir.join("output")
Expand Down
Loading