0
# Key Bindings and Input
1
2
Configurable key binding system supporting Vi and Emacs editing modes with customizable key mappings. The key binding system provides the foundation for all keyboard interaction in prompt-toolkit applications.
3
4
## Capabilities
5
6
### Key Binding Containers
7
8
Core classes for managing and organizing key bindings.
9
10
```python { .api }
11
class KeyBindings:
12
def __init__(self):
13
"""Create mutable key binding container."""
14
15
def add(self, *keys, **kwargs):
16
"""
17
Decorator to add key binding.
18
19
Parameters:
20
- *keys: Key sequence (e.g., 'c-c', 'a', 'escape c-c')
21
- filter: Filter determining when binding is active
22
- eager: bool, execute immediately without waiting for more keys
23
- is_global: bool, binding applies globally
24
- save_before: Function to call before executing
25
- record_in_macro: bool, record action in Vi macro
26
27
Returns:
28
Decorator function
29
"""
30
31
def remove(self, *keys):
32
"""
33
Remove key binding.
34
35
Parameters:
36
- *keys: Key sequence to remove
37
"""
38
39
def get_bindings_for_keys(self, keys):
40
"""
41
Get bindings matching key sequence.
42
43
Parameters:
44
- keys: Tuple of key presses
45
46
Returns:
47
List of matching bindings
48
"""
49
50
def get_bindings_starting_with_keys(self, keys):
51
"""
52
Get bindings that start with key sequence.
53
54
Parameters:
55
- keys: Tuple of key presses
56
57
Returns:
58
List of bindings starting with keys
59
"""
60
61
class KeyBindingsBase:
62
"""Abstract base class for key binding containers."""
63
64
def get_bindings_for_keys(self, keys):
65
"""Get bindings for key sequence."""
66
67
def get_bindings_starting_with_keys(self, keys):
68
"""Get bindings starting with key sequence."""
69
70
class ConditionalKeyBindings(KeyBindingsBase):
71
def __init__(self, key_bindings, filter):
72
"""
73
Key bindings active only when filter is true.
74
75
Parameters:
76
- key_bindings: KeyBindings instance to wrap
77
- filter: Filter determining when bindings are active
78
"""
79
80
class DynamicKeyBindings(KeyBindingsBase):
81
def __init__(self, get_key_bindings):
82
"""
83
Dynamic key bindings based on function.
84
85
Parameters:
86
- get_key_bindings: Function returning KeyBindings instance
87
"""
88
89
def merge_key_bindings(bindings_list):
90
"""
91
Merge multiple key binding objects.
92
93
Parameters:
94
- bindings_list: List of KeyBindings instances
95
96
Returns:
97
Merged KeyBindings instance
98
"""
99
```
100
101
### Key Press Events
102
103
Classes representing key press events and their context.
104
105
```python { .api }
106
class KeyPress:
107
def __init__(self, key, data=""):
108
"""
109
Represent a single key press.
110
111
Parameters:
112
- key: str, key identifier
113
- data: str, additional key data
114
"""
115
116
@property
117
def key(self):
118
"""str: The key identifier."""
119
120
@property
121
def data(self):
122
"""str: Additional key data."""
123
124
class KeyPressEvent:
125
def __init__(self, key_press, previous_key_sequence=None):
126
"""
127
Key press event with application context.
128
129
Parameters:
130
- key_press: KeyPress instance
131
- previous_key_sequence: Previous key sequence
132
"""
133
134
@property
135
def key_sequence(self):
136
"""List[KeyPress]: Complete key sequence."""
137
138
@property
139
def data(self):
140
"""str: Key data from current key press."""
141
142
@property
143
def app(self):
144
"""Application: Current application instance."""
145
146
@property
147
def current_buffer(self):
148
"""Buffer: Currently focused buffer."""
149
150
@property
151
def arg(self):
152
"""int: Numeric argument if provided."""
153
154
@property
155
def is_repeat(self):
156
"""bool: True if this is a repeat event."""
157
```
158
159
### Key Constants
160
161
Enumeration and constants for key identifiers.
162
163
```python { .api }
164
class Keys:
165
"""Key constant definitions."""
166
167
# Control keys
168
ControlA = "c-a"
169
ControlB = "c-b"
170
ControlC = "c-c"
171
ControlD = "c-d"
172
ControlE = "c-e"
173
ControlF = "c-f"
174
ControlG = "c-g"
175
ControlH = "c-h"
176
ControlI = "c-i"
177
ControlJ = "c-j"
178
ControlK = "c-k"
179
ControlL = "c-l"
180
ControlM = "c-m"
181
ControlN = "c-n"
182
ControlO = "c-o"
183
ControlP = "c-p"
184
ControlQ = "c-q"
185
ControlR = "c-r"
186
ControlS = "c-s"
187
ControlT = "c-t"
188
ControlU = "c-u"
189
ControlV = "c-v"
190
ControlW = "c-w"
191
ControlX = "c-x"
192
ControlY = "c-y"
193
ControlZ = "c-z"
194
195
# Special keys
196
Enter = "\r"
197
Escape = "\x1b"
198
Backspace = "\x7f"
199
Delete = "\x1b[3~"
200
Insert = "\x1b[2~"
201
Home = "\x1b[H"
202
End = "\x1b[F"
203
PageUp = "\x1b[5~"
204
PageDown = "\x1b[6~"
205
206
# Arrow keys
207
Up = "\x1b[A"
208
Down = "\x1b[B"
209
Right = "\x1b[C"
210
Left = "\x1b[D"
211
212
# Function keys
213
F1 = "\x1bOP"
214
F2 = "\x1bOQ"
215
F3 = "\x1bOR"
216
F4 = "\x1bOS"
217
F5 = "\x1b[15~"
218
F6 = "\x1b[17~"
219
F7 = "\x1b[18~"
220
F8 = "\x1b[19~"
221
F9 = "\x1b[20~"
222
F10 = "\x1b[21~"
223
F11 = "\x1b[23~"
224
F12 = "\x1b[24~"
225
226
# Meta/Alt keys
227
Escape = "\x1b"
228
229
# Shift combinations
230
ShiftTab = "\x1b[Z"
231
232
# Special characters
233
Tab = "\t"
234
Space = " "
235
236
ALL_KEYS = [
237
# List of all available key constants
238
Keys.ControlA, Keys.ControlB, Keys.ControlC,
239
# ... (complete list of all keys)
240
]
241
```
242
243
### Built-in Key Binding Sets
244
245
Pre-configured key binding sets for common editing modes.
246
247
```python { .api }
248
def load_vi_bindings():
249
"""
250
Load Vi-style key bindings.
251
252
Returns:
253
KeyBindings instance with Vi key mappings
254
255
Key bindings include:
256
- Normal mode: h/j/k/l navigation, i/a/o insert modes
257
- Insert mode: standard text editing
258
- Command mode: search, replace, file operations
259
- Visual mode: text selection and manipulation
260
"""
261
262
def load_emacs_bindings():
263
"""
264
Load Emacs-style key bindings.
265
266
Returns:
267
KeyBindings instance with Emacs key mappings
268
269
Key bindings include:
270
- Cursor movement: C-f/C-b/C-n/C-p
271
- Text editing: C-k/C-y/C-d/C-h
272
- Search: C-s/C-r for incremental search
273
- Word operations: M-f/M-b/M-d
274
"""
275
276
def load_basic_bindings():
277
"""
278
Load basic key bindings for simple editing.
279
280
Returns:
281
KeyBindings instance with basic key mappings
282
"""
283
284
def load_auto_suggest_bindings():
285
"""
286
Load key bindings for auto-suggestion features.
287
288
Returns:
289
KeyBindings instance for auto-suggest interaction
290
"""
291
292
def load_open_in_editor_bindings():
293
"""
294
Load key bindings for opening content in external editor.
295
296
Returns:
297
KeyBindings instance for editor integration
298
"""
299
300
def load_page_navigation_bindings():
301
"""
302
Load key bindings for page navigation.
303
304
Returns:
305
KeyBindings instance for page up/down navigation
306
"""
307
```
308
309
### Key Processing
310
311
Classes for processing key sequences and executing bindings.
312
313
```python { .api }
314
class KeyProcessor:
315
def __init__(self, key_bindings):
316
"""
317
Process key sequences and execute bindings.
318
319
Parameters:
320
- key_bindings: KeyBindings instance
321
"""
322
323
def feed(self, key_press):
324
"""
325
Feed key press to processor.
326
327
Parameters:
328
- key_press: KeyPress instance
329
330
Returns:
331
List of actions to execute
332
"""
333
334
def reset(self):
335
"""Reset key processor state."""
336
337
def empty(self):
338
"""
339
Check if processor has pending keys.
340
341
Returns:
342
bool: True if no pending keys
343
"""
344
345
def generate_completions(key_processor):
346
"""
347
Generate possible key completions.
348
349
Parameters:
350
- key_processor: KeyProcessor instance
351
352
Yields:
353
Possible key completion sequences
354
"""
355
```
356
357
### Vi Mode State
358
359
Classes for managing Vi editor mode state.
360
361
```python { .api }
362
class ViState:
363
def __init__(self):
364
"""Vi editor mode state management."""
365
366
@property
367
def input_mode(self):
368
"""InputMode: Current Vi input mode."""
369
370
@input_mode.setter
371
def input_mode(self, value):
372
"""Set Vi input mode."""
373
374
@property
375
def waiting_for_digraph(self):
376
"""bool: True if waiting for digraph input."""
377
378
@property
379
def operator_func(self):
380
"""Function: Current operator function."""
381
382
@property
383
def yank_buffer(self):
384
"""str: Current yank buffer content."""
385
386
class InputMode(Enum):
387
"""Vi input modes."""
388
INSERT = "vi-insert"
389
NAVIGATION = "vi-navigation"
390
REPLACE = "vi-replace"
391
REPLACE_SINGLE = "vi-replace-single"
392
INSERT_MULTIPLE = "vi-insert-multiple"
393
```
394
395
## Usage Examples
396
397
### Basic Key Bindings
398
399
```python
400
from prompt_toolkit.application import Application
401
from prompt_toolkit.key_binding import KeyBindings
402
from prompt_toolkit.layout import Layout
403
from prompt_toolkit.layout.controls import FormattedTextControl
404
405
# Create key bindings
406
kb = KeyBindings()
407
408
@kb.add('c-c')
409
def exit_app(event):
410
"""Exit on Ctrl-C."""
411
event.app.exit()
412
413
@kb.add('c-h')
414
def show_help(event):
415
"""Show help on Ctrl-H."""
416
print("Help: Press Ctrl-C to exit")
417
418
@kb.add('c-l')
419
def clear_screen(event):
420
"""Clear screen on Ctrl-L."""
421
event.app.invalidate()
422
423
# Create application with key bindings
424
app = Application(
425
layout=Layout(FormattedTextControl('Press Ctrl-H for help, Ctrl-C to exit')),
426
key_bindings=kb,
427
full_screen=True
428
)
429
430
app.run()
431
```
432
433
### Key Sequences and Combinations
434
435
```python
436
from prompt_toolkit.key_binding import KeyBindings
437
438
kb = KeyBindings()
439
440
# Single key
441
@kb.add('q')
442
def quit_app(event):
443
event.app.exit()
444
445
# Control key combination
446
@kb.add('c-x', 'c-c')
447
def exit_emacs_style(event):
448
"""Emacs-style exit sequence."""
449
event.app.exit()
450
451
# Escape sequences
452
@kb.add('escape', 'q')
453
def escape_quit(event):
454
"""Exit with Escape+q."""
455
event.app.exit()
456
457
# Function key
458
@kb.add('f1')
459
def show_help(event):
460
"""Show help on F1."""
461
print("F1 Help")
462
463
# Multiple key sequence
464
@kb.add('c-x', 's')
465
def save_file(event):
466
"""Save file with Ctrl-X, S."""
467
print("Saving file...")
468
469
# Arrow keys
470
@kb.add('up')
471
def move_up(event):
472
"""Move cursor up."""
473
print("Moving up")
474
475
@kb.add('down')
476
def move_down(event):
477
"""Move cursor down."""
478
print("Moving down")
479
```
480
481
### Conditional Key Bindings
482
483
```python
484
from prompt_toolkit.key_binding import KeyBindings
485
from prompt_toolkit.filters import has_focus, vi_mode, emacs_mode
486
487
kb = KeyBindings()
488
489
# Only active when specific buffer has focus
490
@kb.add('c-t', filter=has_focus('main'))
491
def toggle_feature(event):
492
"""Toggle feature when main buffer focused."""
493
print("Feature toggled")
494
495
# Vi mode specific bindings
496
@kb.add('i', filter=vi_mode)
497
def vi_insert_mode(event):
498
"""Enter Vi insert mode."""
499
from prompt_toolkit.key_binding.vi_state import InputMode
500
event.app.vi_state.input_mode = InputMode.INSERT
501
502
# Emacs mode specific bindings
503
@kb.add('c-n', filter=emacs_mode)
504
def emacs_next_line(event):
505
"""Emacs next line."""
506
buffer = event.current_buffer
507
buffer.cursor_down()
508
509
# Custom filter
510
from prompt_toolkit.filters import Condition
511
512
@Condition
513
def custom_condition():
514
return some_application_state
515
516
@kb.add('c-k', filter=custom_condition)
517
def custom_action(event):
518
"""Action only when custom condition is true."""
519
print("Custom action executed")
520
```
521
522
### Buffer Key Bindings
523
524
```python
525
from prompt_toolkit.key_binding import KeyBindings
526
from prompt_toolkit.filters import has_focus
527
528
kb = KeyBindings()
529
530
@kb.add('c-a')
531
def move_to_start(event):
532
"""Move cursor to start of line."""
533
event.current_buffer.cursor_position = 0
534
535
@kb.add('c-e')
536
def move_to_end(event):
537
"""Move cursor to end of line."""
538
buffer = event.current_buffer
539
buffer.cursor_position = len(buffer.text)
540
541
@kb.add('c-k')
542
def kill_line(event):
543
"""Kill from cursor to end of line."""
544
buffer = event.current_buffer
545
buffer.delete(count=len(buffer.text) - buffer.cursor_position)
546
547
@kb.add('c-w')
548
def kill_word(event):
549
"""Kill word backwards."""
550
buffer = event.current_buffer
551
pos = buffer.document.find_start_of_word(count=-1)
552
if pos:
553
buffer.delete_before_cursor(count=-pos)
554
555
@kb.add('c-y')
556
def yank(event):
557
"""Yank (paste) text."""
558
# Implementation would restore previously killed text
559
pass
560
```
561
562
### Vi Key Bindings
563
564
```python
565
from prompt_toolkit.key_binding import KeyBindings
566
from prompt_toolkit.key_binding.bindings.vi import load_vi_bindings
567
from prompt_toolkit.filters import vi_mode, vi_navigation_mode, vi_insert_mode
568
569
# Load base Vi bindings
570
kb = load_vi_bindings()
571
572
# Add custom Vi bindings
573
@kb.add('j', 'j', filter=vi_insert_mode)
574
def jj_escape(event):
575
"""Exit insert mode with 'jj'."""
576
from prompt_toolkit.key_binding.vi_state import InputMode
577
event.app.vi_state.input_mode = InputMode.NAVIGATION
578
579
@kb.add('g', 'g', filter=vi_navigation_mode)
580
def go_to_top(event):
581
"""Go to top of buffer with 'gg'."""
582
event.current_buffer.cursor_position = 0
583
584
@kb.add('G', filter=vi_navigation_mode)
585
def go_to_bottom(event):
586
"""Go to bottom of buffer with 'G'."""
587
buffer = event.current_buffer
588
buffer.cursor_position = len(buffer.text)
589
590
# Custom Vi command
591
@kb.add('c-x', 'c-e', filter=vi_mode)
592
def edit_in_editor(event):
593
"""Edit buffer in external editor."""
594
# Implementation would open external editor
595
pass
596
```
597
598
### Dynamic Key Bindings
599
600
```python
601
from prompt_toolkit.key_binding import KeyBindings, DynamicKeyBindings
602
from prompt_toolkit.filters import Condition
603
604
# Different key binding sets
605
basic_bindings = KeyBindings()
606
advanced_bindings = KeyBindings()
607
608
@basic_bindings.add('c-c')
609
def basic_exit(event):
610
event.app.exit()
611
612
@advanced_bindings.add('c-c')
613
def advanced_exit(event):
614
print("Advanced exit")
615
event.app.exit()
616
617
@advanced_bindings.add('f1')
618
def advanced_help(event):
619
print("Advanced help")
620
621
# Dynamic selection based on application state
622
def get_current_bindings():
623
if application_in_advanced_mode:
624
return advanced_bindings
625
else:
626
return basic_bindings
627
628
# Use dynamic bindings
629
dynamic_kb = DynamicKeyBindings(get_current_bindings)
630
```
631
632
### Key Binding with Arguments
633
634
```python
635
from prompt_toolkit.key_binding import KeyBindings
636
637
kb = KeyBindings()
638
639
@kb.add('c-u')
640
def universal_argument(event):
641
"""Set universal argument."""
642
# This would set a numeric argument for next command
643
event.app.key_processor.arg = event.arg or 4
644
645
@kb.add('c-k')
646
def kill_line_with_arg(event):
647
"""Kill lines with numeric argument."""
648
buffer = event.current_buffer
649
count = event.arg or 1
650
651
for _ in range(count):
652
# Kill one line
653
buffer.delete(count=len(buffer.document.current_line_after_cursor))
654
if buffer.document.cursor_position < len(buffer.text):
655
buffer.delete(count=1) # Delete newline
656
657
@kb.add('c-d')
658
def delete_with_arg(event):
659
"""Delete characters with numeric argument."""
660
buffer = event.current_buffer
661
count = event.arg or 1
662
buffer.delete(count=count)
663
```