Skip to content

Commit

Permalink
Merge 8752619 into 996ad5b
Browse files Browse the repository at this point in the history
  • Loading branch information
baturayo committed May 31, 2023
2 parents 996ad5b + 8752619 commit a2dbe6e
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 0 deletions.
18 changes: 18 additions & 0 deletions questionary/prompts/checkbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def checkbox(
use_jk_keys: bool = True,
use_emacs_keys: bool = True,
instruction: Optional[str] = None,
custom_key_binding: Optional[Dict[Union[str, Keys], Callable]] = None,
**kwargs: Any,
) -> Question:
"""Ask the user to select from a list of items.
Expand Down Expand Up @@ -104,8 +105,22 @@ def checkbox(
use_emacs_keys: Allow the user to select items from the list using
`Ctrl+N` (down) and `Ctrl+P` (up) keys.
instruction: A message describing how to navigate the menu.
custom_key_binding: Dictionary of custom key bindings. The keys are either
strings or `prompt_toolkit.keys.Keys` objects and the values are
callables that accept a `prompt_toolkit.key_binding.KeyBinding` event
Example:
The following will exit the application with the result "custom" when the
user presses "c" and with the result "ctrl-q" when the user presses "ctrl-q"
```
{
"c": lambda event: event.app.exit(result="custom"),
Keys.ControlQ: lambda event: event.app.exit(result="ctrl-q"),
}
```
Returns:
:class:`Question`: Question instance, ready to be prompted (using ``.ask()``).
"""
Expand Down Expand Up @@ -202,6 +217,9 @@ def perform_validation(selected_values: List[str]) -> bool:
layout = common.create_inquirer_layout(ic, get_prompt_tokens, **kwargs)

bindings = KeyBindings()
if custom_key_binding is not None:
for key, func in custom_key_binding.items():
bindings.add(key, eager=True)(func)

@bindings.add(Keys.ControlQ, eager=True)
@bindings.add(Keys.ControlC, eager=True)
Expand Down
21 changes: 21 additions & 0 deletions questionary/prompts/confirm.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from typing import Any
from typing import Callable
from typing import Dict
from typing import Optional
from typing import Union

from prompt_toolkit import PromptSession
from prompt_toolkit.formatted_text import to_formatted_text
Expand All @@ -22,6 +25,7 @@ def confirm(
qmark: str = DEFAULT_QUESTION_PREFIX,
style: Optional[Style] = None,
auto_enter: bool = True,
custom_key_binding: Optional[Dict[Union[str, Keys], Callable]] = None,
**kwargs: Any,
) -> Question:
"""A yes or no question. The user can either confirm or deny.
Expand Down Expand Up @@ -58,6 +62,20 @@ def confirm(
accept their answer. If set to `True`, a valid input will be
accepted without the need to press 'Enter'.
custom_key_binding: Dictionary of custom key bindings. The keys are either
strings or `prompt_toolkit.keys.Keys` objects and the values are
callables that accept a `prompt_toolkit.key_binding.KeyBinding` event
Example:
The following will exit the application with the result "custom" when the
user presses "c" and with the result "ctrl-q" when the user presses "ctrl-q"
```
{
"c": lambda event: event.app.exit(result="custom"),
Keys.ControlQ: lambda event: event.app.exit(result="ctrl-q"),
}
```
Returns:
:class:`Question`: Question instance, ready to be prompted (using `.ask()`).
"""
Expand Down Expand Up @@ -86,6 +104,9 @@ def exit_with_result(event):
event.app.exit(result=status["answer"])

bindings = KeyBindings()
if custom_key_binding is not None:
for key, func in custom_key_binding.items():
bindings.add(key, eager=True)(func)

@bindings.add(Keys.ControlQ, eager=True)
@bindings.add(Keys.ControlC, eager=True)
Expand Down
18 changes: 18 additions & 0 deletions questionary/prompts/select.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-

from typing import Any
from typing import Callable
from typing import Dict
from typing import Optional
from typing import Sequence
Expand Down Expand Up @@ -36,6 +37,7 @@ def select(
use_emacs_keys: bool = True,
show_selected: bool = False,
instruction: Optional[str] = None,
custom_key_binding: Optional[Dict[Union[str, Keys], Callable]] = None,
**kwargs: Any,
) -> Question:
"""A list of items to select **one** option from.
Expand Down Expand Up @@ -110,6 +112,18 @@ def select(
show_selected: Display current selection choice at the bottom of list.
custom_key_binding: Dictionary of custom key bindings. The keys are either
strings or `prompt_toolkit.keys.Keys` objects and the values are
callables that accept a `prompt_toolkit.key_binding.KeyBinding` event
Example:
The following will exit the application with the result "custom" when the
user presses "c" and with the result "ctrl-q" when the user presses "ctrl-q"
```
{
"c": lambda event: event.app.exit(result="custom"),
Keys.ControlQ: lambda event: event.app.exit(result="ctrl-q"),
}
Returns:
:class:`Question`: Question instance, ready to be prompted (using ``.ask()``).
"""
Expand Down Expand Up @@ -186,6 +200,10 @@ def get_prompt_tokens():

bindings = KeyBindings()

if custom_key_binding is not None:
for key, func in custom_key_binding.items():
bindings.add(key, eager=True)(func)

@bindings.add(Keys.ControlQ, eager=True)
@bindings.add(Keys.ControlC, eager=True)
def _(event):
Expand Down
26 changes: 26 additions & 0 deletions questionary/prompts/text.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
from typing import Any
from typing import Callable
from typing import Dict
from typing import List
from typing import Optional
from typing import Tuple
from typing import Union

from prompt_toolkit.document import Document
from prompt_toolkit.key_binding import KeyBindings
from prompt_toolkit.keys import Keys
from prompt_toolkit.lexers import Lexer
from prompt_toolkit.lexers import SimpleLexer
from prompt_toolkit.shortcuts.prompt import PromptSession
Expand All @@ -25,6 +30,7 @@ def text(
multiline: bool = False,
instruction: Optional[str] = None,
lexer: Optional[Lexer] = None,
custom_key_binding: Optional[Dict[Union[str, Keys], Callable]] = None,
**kwargs: Any,
) -> Question:
"""Prompt the user to enter a free text message.
Expand Down Expand Up @@ -70,6 +76,20 @@ def text(
lexer: Supply a valid lexer to style the answer. Leave empty to
use a simple one by default.
custom_key_binding: Dictionary of custom key bindings. The keys are either
strings or `prompt_toolkit.keys.Keys` objects and the values are
callables that accept a `prompt_toolkit.key_binding.KeyBinding` event
Example:
The following will exit the application with the result "custom" when the
user presses "c" and with the result "ctrl-q" when the user presses "ctrl-q"
```
{
"c": lambda event: event.app.exit(result="custom"),
Keys.ControlQ: lambda event: event.app.exit(result="ctrl-q"),
}
```
kwargs: Additional arguments, they will be passed to prompt toolkit.
Returns:
Expand All @@ -88,8 +108,14 @@ def get_prompt_tokens() -> List[Tuple[str, str]]:
result.append(("class:instruction", " {} ".format(instruction)))
return result

bindings = KeyBindings()
if custom_key_binding is not None:
for key, func in custom_key_binding.items():
bindings.add(key, eager=True)(func)

p: PromptSession = PromptSession(
get_prompt_tokens,
key_bindings=bindings,
style=merged_style,
validator=validator,
lexer=lexer,
Expand Down
14 changes: 14 additions & 0 deletions tests/prompts/test_checkbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,20 @@ def test_select_with_instruction():
assert result == ["foo"]


def test_select_with_custom_key_bindings():
message = "Foo message"
kwargs = {
"choices": ["foo", "bar", "bazz"],
"custom_key_binding": {
KeyInputs.ONE: lambda event: event.app.exit(result="1-pressed")
},
}
text = KeyInputs.ONE + "\r"

result, cli = feed_cli_with_input("checkbox", message, text, **kwargs)
assert result == "1-pressed"


def test_select_first_choice_with_token_title():
message = "Foo message"
kwargs = {
Expand Down
15 changes: 15 additions & 0 deletions tests/prompts/test_confirm.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,18 @@ def test_confirm_not_autoenter_backspace():

result, cli = feed_cli_with_input("confirm", message, text, auto_enter=False)
assert result is True


def test_confirm_with_custom_key_bindings():
message = "Foo message"
kwargs = {
"custom_key_binding": {
KeyInputs.ONE: lambda event: event.app.exit(result="1-pressed")
}
}
text = KeyInputs.ONE + "\r"

result, cli = feed_cli_with_input(
"confirm", message, text, auto_enter=False, **kwargs
)
assert result == "1-pressed"
14 changes: 14 additions & 0 deletions tests/prompts/test_select.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,20 @@ def test_select_with_instruction():
assert result == "foo"


def test_select_with_custom_key_bindings():
message = "Foo message"
kwargs = {
"choices": ["foo", "bar", "bazz"],
"custom_key_binding": {
KeyInputs.ONE: lambda event: event.app.exit(result="1-pressed")
},
}
text = KeyInputs.ONE + "\r"

result, cli = feed_cli_with_input("select", message, text, **kwargs)
assert result == "1-pressed"


def test_cycle_to_first_choice():
message = "Foo message"
kwargs = {"choices": ["foo", "bar", "bazz"]}
Expand Down
14 changes: 14 additions & 0 deletions tests/prompts/test_text.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from prompt_toolkit.validation import ValidationError
from prompt_toolkit.validation import Validator

from tests.utils import KeyInputs
from tests.utils import feed_cli_with_input


Expand Down Expand Up @@ -36,6 +37,19 @@ def test_text_validate():
assert result == "Doe"


def test_text_with_custom_key_bindings():
message = "What is your name"
kwargs = {
"custom_key_binding": {
KeyInputs.ONE: lambda event: event.app.exit(result="1-pressed")
}
}
text = KeyInputs.ONE + "\r"

result, cli = feed_cli_with_input("text", message, text, **kwargs)
assert result == "1-pressed"


def test_text_validate_with_class():
class SimpleValidator(Validator):
def validate(self, document):
Expand Down

0 comments on commit a2dbe6e

Please sign in to comment.