File: kb.md

package info (click to toggle)
python-inquirerpy 0.3.4-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,088 kB
  • sloc: python: 9,463; makefile: 15
file content (340 lines) | stat: -rw-r--r-- 9,654 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
# Keybindings

By default, `InquirerPy` will use most of the standard `emacs` navigation keybindings. You
can easily switch to `vim` keybindings by setting the parameter [vi_mode](#using-vim-keybindings) to `True`.

You can customise the keybindings even further by utilising the parameter [keybindings](#customizing-keybindings).

## Default Keybindings

```{note}
Each keybinding consists of 2 parts, an **action** as the key and **bindings** as the value.
```

The following dictionary contains the default keybindings for all prompts.

<!-- start kb -->

```{code-block} python
{
    "answer": [{"key": "enter"}],   # answer the prompt
    "interrupt": [{"key": "c-c"}],  # raise KeyboardInterrupt
    "skip": [{"key": "c-z"}],   # skip the prompt
}
```

<!-- end kb -->

### Input Buffer (Text Fields)

The default keybinding for text fields uses the classic `emacs` keybindings.

You can use the regular `emacs` shortcuts to move between words such as `alt-b` and `alt-f` in any input buffer
such as `input`, `secret`, `filepath` and `fuzzy`.

You can reference keybindings through `emacs` [documentation](https://www.gnu.org/software/emacs/refcards/).

### Prompt Specific Keybindings

```{tip}
Keybindings in different types of prompt can have different sets of available actions and sometimes different default bindings.

Please checkout the individual prompt documentation for the available actions and default bindings for specific prompts.
```

## Using VIM Keybindings

```{tip}
All `InquirerPy` prompts accepts a boolean parameter `vi_mode`.
```

`InquirerPy` comes with `vim` keybinding preset. After setting `vi_mode=True`, the input buffer (text fields) will behave the same as
if you enable the `vi` mode in [readline/bash](https://www.gnu.org/software/bash/manual/html_node/Readline-vi-Mode.html).
Other keybinding will also have different effects (e.g. `up/down` will change from `ctrl-n/ctrl-p` to `j/k`), refer to individual
prompt documentation for more information.

<details>
  <summary>Classic Syntax</summary>

```python
from InquirerPy import prompt

result = prompt(
    questions=[
        {
            "type": "list",
            "message": "Select one:",
            "choices": ["Fruit", "Meat", "Drinks", "Vegetable"],
        },
    ],
    vi_mode=True,
)
```

</details>

<details open>
  <summary>Alternate Syntax</summary>

```python
from InquirerPy import inquirer

result = inquirer.select(
    message="Select one:",
    choices=["Fruit", "Meat", "Drinks", "Vegetable"],
    vi_mode=True,
).execute()
```

</details>

## Customising Keybindings

### keybindings

```
Dict[str, List[Dict[str, Union[str, "FilterOrBool", List[str]]]]]
```

`keybindings` should be a {class}`dict` where the `key` is the **action** and the `value` should be a list of keys that
will be the **bindings** to trigger it.

```{admonition} action
You can find the available actions via individual prompt documentation.
```

```{admonition} bindings
Each `binding` is another {class}`dict` which contains the following key:

- [key](#key-union-str-list-str)
- [filter](#filter-union-filter-bool)
```

#### key

The `key` can be either a list or a string. If you require multiple keys to be pressed in sequence, provide the `key` with a list of keys.

In the following example:

- pressing `ctrl-a` followed by `space` will trigger the action `toggle-all`
- pressing `ctrl-d` will raise {class}`KeyboardInterrupt`
- pressing `ctrl-c` will attempt to skip the prompt

<details>
  <summary>Classic Syntax</summary>

```python
from InquirerPy import prompt

keybindings = {
    "skip": [{"key": "c-c"}],
    "interrupt": [{"key": "c-d"}],
    "toggle-all": [{"key": ["c-a", "space"]}],
}

result = prompt(
    questions=[
        {
            "type": "list",
            "message": "Select one:",
            "choices": ["Fruit", "Meat", "Drinks", "Vegetable"],
            "multiselect": True
        },
    ],
    keybindings=keybindings,
)
```

</details>

<details open>
  <summary>Alternate Syntax</summary>

```python
from InquirerPy import inquirer

keybindings = {
    "skip": [{"key": "c-c"}],
    "interrupt": [{"key": "c-d"}],
    "toggle-all": [{"key": ["c-a", "space"]}],
}

result = inquirer.select(
    message="Select one:",
    choices=["Fruit", "Meat", "Drinks", "Vegetable"],
    keybindings=keybindings,
    multiselect=True
).execute()
```

</details>

Available keys/syntax:

| Name               | Possible keys                                           |
| ------------------ | ------------------------------------------------------- |
| Escape             | `escape`                                                |
| Arrows             | `left`, `right`, `up`, `down`                           |
| Navigation         | `home`, `end`, `delete`, `pageup`, `pagedown`, `insert` |
| Control+lowercase  | `c-a`, `c-b` ... `c-y`, `c-z`                           |
| Control+uppercase  | `c-A`, `c-B` ... `c-Y`, `c-Z`                           |
| Control + arrow    | `c-left`, `c-right`, `c-up`, `c-down`                   |
| Other control keys | `c-@`, `c-\`, `c-]`, `c-^`, `c-\_`, `c-delete`          |
| Shift + arrow      | s-left, s-right, s-up, s-down                           |
| Other shift keys   | `s-delete`, `s-tab`                                     |
| F-keys             | `f1`, `f2`, .... `f23`, `f24`                           |
| Alt+lowercase      | `alt-a`, `alt-b` ... `alt-y`, `alt-z`                   |
| Alt+uppercase      | `alt-A`, `alt-B` ... `alt-Y`, `alt-Z`                   |

Visit `prompt_toolkit` [documentation](https://python-prompt-toolkit.readthedocs.io/en/master/pages/advanced_topics/key_bindings.html#list-of-special-keys)
for more information about limitations and other advanced topics.

#### filter

Each keybinding also takes another **optional** key called `filter` which can be used to determine if certain keys should be enabled/disabled.
The `filter` key can be either a boolean or a `prompt_toolkit` [Conditon](https://python-prompt-toolkit.readthedocs.io/en/master/pages/advanced_topics/filters.html#filters).

**bool**

```python
special_vim = True

keybindings = {
    "down": [
        {"key": "c-j", "filter": special_vim},
    ],
    "up": [
        {"key": "c-k", "filter": special_vim},
    ],
    "toggle-all-false": [{"key": "alt-x"}],
}

# ....
```

**Filter**

```python
from prompt_toolkit.filters.base import Condition

@Condition
def special_vim():
    # logic ...
    return True

keybindings = {
    "down": [
        {"key": "c-j", "filter": special_vim},
    ],
    "up": [
        {"key": "c-k", "filter": special_vim},
    ],
    "toggle-all-false": [{"key": "alt-x"}],
}

# ....
```

## Binding Custom Functions

```{attention}
This section only applies to {ref}`index:Alternate Syntax`.
```

You can also create your own keybindings/actions. When creating a prompt via `inquirer`, instead of running
the `execute` function immediately, you can bind keys to your custom functions before running `execute` on the prompt.

### register_kb

```{seealso}
This method directly interacts with {meth}`prompt_toolkit.key_binding.KeyBindings.add`.
```

{meth}`~InquirerPy.base.simple.BaseSimplePrompt.register_kb` is a decorator function that's available to use once the prompt is created.
The function that are being bounded will be provided with an object {class}`~prompt_toolkit.key_binding.key_processor.KeyPressEvent` as an argument.

The {class}`~prompt_toolkit.key_binding.key_processor.KeyPressEvent` can give you access to the {class}`~prompt_toolkit.application.Application` which
will provide you with the ability to exit the prompt application with custom result.

```{code-block} python
from InquirerPy import inquirer

prompt = inquirer.select(
    message="Select item:",
    choices=["foo", "bar"],
    long_instruction="ENTER=view, D=delete",
)

@prompt.register_kb("d")
def _handle_delete(event):
    choice_name = prompt.result_name
    choice_value= prompt.result_value
    # some logic for processing
    # ...
    # you can then use the event API to exit the prompt with the value you desired
    event.app.exit(result=None)

result = prompt.execute()
```

There are also some internal APIs you could leverage within the keybinding functions.

```{code-block} python
from InquirerPy import inquirer

prompt = inquirer.select(
    message="Select item:",
    choices=["foo", "bar"],
    long_instruction="ENTER=view, D=delete",
)

@prompt.register_kb("d")
def _handle_delete(event):
    choice_name = prompt.result_name
    choice_value= prompt.result_value
    # some logic for processing
    # ...
    # skipping the prompt after processing
    prompt._mandatory = False
    prompt._handle_skip(event)
    # answer the prompt normally after processing
    prompt._handle_enter(event)

result = prompt.execute()
```

The following is a simpler example which will print "Hello World" on top of the prompt when pressing `alt-a`.

```{code-block} python
from InquirerPy import inquirer
from InquirerPy.utils import patched_print as print

name_prompt = inquirer.text(message="Name:")

kb_activate = True

@name_prompt.register_kb("alt-a")
def _(_):
    print("Hello World")

name = name_prompt.execute()
```

**keys and filter**

You can bind multiple keys and also have the ability to apply [filter](#filter).

```python
from prompt_toolkit.filters.base import Condition

hello_active = Condition(lambda: True)
world_active = False

@name_prompt.register_kb("alt-j", "alt-k" filter=hello_active)
def _(_):
    print("Hello")

@name_prompt.register_kb("escape", "k", "escape", "j" filter=world_active)
def _(_):
    print("World")
```