Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
732cbcd
feat(copier/_user_data.py): add `questionary` `use_shorcuts` & `use_f…
RR5555 Jan 8, 2026
06c942f
docs(copier/_user_data.py): complement `Question` docstring to doc `u…
RR5555 Jan 8, 2026
75bcd23
test(copier/_user_data.py): add tests for `Question` `use_shortcuts` …
RR5555 Jan 8, 2026
8f951e4
Merge branch 'copier-org:master' into question_filter_shortcut
RR5555 Jan 8, 2026
08371bf
test(tests/): rename `test_shortcut.py` to `test_choices.py`
RR5555 Jan 11, 2026
13d341b
fix(copier/_user_data.py): fix attribute name of `Question` from `use…
RR5555 Jan 11, 2026
20d24f8
docs(Question): line-wrap the docstring to max length 88 for consistency
RR5555 Jan 11, 2026
f8a8cb1
test(tests/test_choices.py): remove useless elements
RR5555 Jan 11, 2026
f734a35
test(tests/test_choices.py): curate the test template
RR5555 Jan 11, 2026
f8c695e
docs(docs/configuring.md): add docs for `use_shortcuts` & `use_search…
RR5555 Jan 11, 2026
70e7e42
test(tests/test_choices.py): split tests between `use_shortcuts` & `u…
RR5555 Jan 12, 2026
4ba04f7
test(tests/test_choices): fix type hints for compliance with `test_ty…
RR5555 Jan 12, 2026
c1f6643
Merge branch 'copier-org:master' into question_filter_shortcut
RR5555 Mar 16, 2026
bcdf3d2
Merge branch 'copier-org:master' into question_filter_shortcut
RR5555 Mar 16, 2026
5b3e902
fix(Question): change from superseeding to `ValidationError` for mutu…
RR5555 Mar 16, 2026
928e117
docs(Question): improve & adapt docstring to mutual exclusiveness
RR5555 Mar 16, 2026
215cd0c
test(tests/test_choices.py): break down fixtures into clearer tests; …
RR5555 Mar 16, 2026
04cd33d
docs(docs/configuring.md): improve & simplify the docs relative to `u…
RR5555 Mar 16, 2026
db9794e
feat(mkdocs.yml): enable `pymdownx.keys` for pretty keyboard keys
RR5555 Mar 16, 2026
70b67b2
Merge remote-tracking branch 'upstream/master' into question_filter_s…
RR5555 May 3, 2026
5d0c9c7
refactor(Question): move `use_shortcuts` validation to a Pydantic Mod…
RR5555 May 3, 2026
ddddbea
docs(docs/configuring.md): switch "Supported keys" item order
RR5555 May 3, 2026
38a3954
docs(docs/configuring.md): disable `rumdl` for `pre` html tag blocks
RR5555 May 5, 2026
aa1c05a
docs(docs/configuring.md): disable `rumdl` for `pre` html tag blocks
RR5555 May 5, 2026
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
36 changes: 35 additions & 1 deletion copier/_user_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@
from jinja2 import StrictUndefined, UndefinedError
from jinja2.sandbox import SandboxedEnvironment
from prompt_toolkit.lexers import PygmentsLexer
from pydantic import ConfigDict, Field, field_validator
from pydantic import ConfigDict, Field, field_validator, model_validator
from pydantic.dataclasses import dataclass
from pydantic_core.core_schema import ValidationInfo
from pygments.lexers.data import JsonLexer, YamlLexer
from questionary.prompts.common import Choice
from typing_extensions import Self

from copier._jinja_ext import UnsetError
from copier._settings import SettingsModel
Expand Down Expand Up @@ -201,6 +202,17 @@ class Question:
If it is a boolean, it is used directly. If it is a str, it is
converted to boolean using a parser similar to YAML, but only for
boolean values.

use_shortcuts:
Condition that, if `True`, allows selecting choice question items via
number shortcuts. Mutually exclusive with `multiselect` and
`use_search_filter`.

use_search_filter:
Condition that, if `True`, enables filtering choice question items by
typing a search string. Disables j/k navigation, as "j" and "k" can be part
of a prefix and therefore cannot be used for navigation. Mutually exclusive
with `use_shortcuts`.
"""

var_name: str
Expand All @@ -219,6 +231,8 @@ class Question:
type: str = Field(default="", validate_default=True)
validator: str = ""
when: str | bool = True
use_shortcuts: bool = False
use_search_filter: bool = False

@field_validator("var_name")
@classmethod
Expand All @@ -244,6 +258,19 @@ def _check_secret_question_default_value(
raise ValueError("Secret question requires a default value")
return v

@model_validator(mode="after")
def _check_no_multiselect_or_search_filter_with_use_shortcuts(self) -> Self:
if self.use_shortcuts:
if self.multiselect:
raise ValueError(
f"[Question Name: `{self.var_name}`]\n `use_shortcuts` & `multiselect` are mutually exclusive\n Use either `use_shortcuts: true` or `multiselect: true`\n "
)
if self.use_search_filter:
raise ValueError(
f"[Question Name: `{self.var_name}`]\n `use_shortcuts` & `use_search_filter` are mutually exclusive\n Use either `use_shortcuts: true` or `use_search_filter: true`\n "
)
return self

def cast_answer(self, answer: Any) -> Any:
"""Cast answer to expected type."""
type_name = self.get_type_name()
Expand Down Expand Up @@ -417,6 +444,13 @@ def _validate(answer: str) -> str | Literal[True]:
result["default"] = False
if self.choices:
questionary_type = "checkbox" if self.multiselect else "select"

if self.use_search_filter:
result["use_search_filter"] = True
result["use_jk_keys"] = False
if self.use_shortcuts:
result["use_shortcuts"] = True

choices = self._formatted_choices
# Select default choices for a multiselect question.
if self.multiselect and isinstance(
Expand Down
116 changes: 116 additions & 0 deletions docs/configuring.md
Comment thread
RR5555 marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,122 @@ Supported keys:
- **multiselect**: When set to `true`, allows multiple choices. The answer will be a
`list[T]` instead of a `T` where `T` is of type `type`.

- **use_shortcuts**: When set to `true`, allows selecting choice question items via
number shortcuts. Mutually exclusive with `multiselect` and `use_search_filter`.

!!! example

```yaml title="copier.yml"
language:
type: str
help: Which programming language do you use?
use_shortcuts: true
choices:
- python
- node
- c
- c++
- rust
- zig
- asm
```

Will result in:

<!-- rumdl-disable -->
<pre>
<span style="font-weight:bold">🎤 Which programming language do you use?</span>
(Use shortcuts or arrow keys)
» 1) python
2) node
3) c
4) c++
5) rust
6) zig
7) asm
</pre>
<!-- rumdl-enable -->

Pressing `5` gives:

<!-- rumdl-disable -->
<pre>
<span style="font-weight:bold">🎤 Which programming language do you use?</span>
(Use shortcuts or arrow keys)
1) python
2) node
3) c
4) c++
» 5) rust
6) zig
7) asm
</pre>
<!-- rumdl-enable -->

- **use_search_filter**: When set to `true`, enables filtering choice question items
by typing a search string. Also deactivates the use of `j`/`k` keys for navigation,
as these are captured as prompts for the search filter. Mutually exclusive with
`use_shortcuts`.

!!! note

If `multiselect` is `true`, you cannot use ++space++ in the search, as this would only select the choice item. If it is `false`, ++space++ can be used.

!!! example

```yaml title="copier.yml"
language:
type: str
help: Which programming language do you use?
use_search_filter: true
choices:
- python
- node
- c
- c++
- rust
- zig
- asm
```

<!-- rumdl-disable -->
<pre>
<span style="font-weight:bold">🎤 Which programming language do you use?</span>
(Use arrow keys, type to filter)
» python
node
c
c++
rust
zig
asm
</pre>
<!-- rumdl-enable -->

---

Typing `o`:

<!-- rumdl-disable -->
<pre>
<span style="font-weight:bold">🎤 Which programming language do you use?</span>
(Use arrow keys, type to filter)
» python
node


/ <span style="color:green;font-weight:bold">o</span>...
</pre>
<!-- rumdl-enable -->

---

When the filter fails, all options are displayed.

---

You can use ++backspace++ to modify the search filter.

- **default**: Leave empty to force the user to answer. Provide a default to save them
from typing it if it's quite common. When using `choices`, the default must be the
choice _value_, not its _key_, and it must match its _type_. If values are quite
Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ markdown_extensions:
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format
- pymdownx.emoji
- pymdownx.keys
- pymdownx.magiclink
- toc:
permalink: true
Expand Down
Loading
Loading