0
# Completion and Validation
1
2
Auto-completion system with built-in completers for files, words, and custom data, plus validation framework for input checking. These systems work together to provide intelligent input assistance and error prevention.
3
4
## Capabilities
5
6
### Base Completion Classes
7
8
Core classes for implementing and managing auto-completion functionality.
9
10
```python { .api }
11
class Completion:
12
def __init__(
13
self,
14
text,
15
start_position=0,
16
display=None,
17
display_meta=None,
18
style="",
19
selected_style=""
20
):
21
"""
22
Single completion result.
23
24
Parameters:
25
- text: str, completion text to insert
26
- start_position: int, position where completion starts (negative offset)
27
- display: str, text to display in completion menu (default: text)
28
- display_meta: str, additional metadata to show
29
- style: str, CSS-like style for completion
30
- selected_style: str, style when completion is selected
31
"""
32
33
class Completer:
34
"""Abstract base class for completers."""
35
36
def get_completions(self, document, complete_event):
37
"""
38
Generate completions for document.
39
40
Parameters:
41
- document: Document instance with current text and cursor
42
- complete_event: CompleteEvent with completion context
43
44
Yields:
45
Completion instances
46
"""
47
48
class CompleteEvent:
49
def __init__(self, text_inserted=False, completion_requested=False):
50
"""
51
Completion event context.
52
53
Parameters:
54
- text_inserted: bool, True if triggered by text insertion
55
- completion_requested: bool, True if explicitly requested (Tab)
56
"""
57
58
class ThreadedCompleter(Completer):
59
def __init__(self, completer):
60
"""
61
Wrapper for running completer in background thread.
62
63
Parameters:
64
- completer: Completer instance to wrap
65
"""
66
67
class DummyCompleter(Completer):
68
"""Dummy completer that returns no completions."""
69
70
def __init__(self):
71
"""Create dummy completer for testing."""
72
73
class DynamicCompleter(Completer):
74
def __init__(self, get_completer):
75
"""
76
Dynamic completer that changes based on function.
77
78
Parameters:
79
- get_completer: Function returning Completer instance
80
"""
81
82
class ConditionalCompleter(Completer):
83
def __init__(self, completer, filter):
84
"""
85
Completer active only when filter is true.
86
87
Parameters:
88
- completer: Completer instance to wrap
89
- filter: Filter determining when completer is active
90
"""
91
92
def merge_completers(completers):
93
"""
94
Merge multiple completers into one.
95
96
Parameters:
97
- completers: List of Completer instances
98
99
Returns:
100
Combined Completer instance
101
"""
102
103
def get_common_complete_suffix(completions):
104
"""
105
Get common suffix of all completions.
106
107
Parameters:
108
- completions: List of Completion instances
109
110
Returns:
111
str: Common suffix or empty string
112
"""
113
```
114
115
### Built-in Completers
116
117
Pre-built completers for common completion scenarios.
118
119
```python { .api }
120
class WordCompleter(Completer):
121
def __init__(
122
self,
123
words,
124
ignore_case=False,
125
meta_dict=None,
126
WORD=False,
127
sentence=False,
128
match_middle=False,
129
pattern=None
130
):
131
"""
132
Completer for static word list.
133
134
Parameters:
135
- words: List of words to complete
136
- ignore_case: bool, case-insensitive matching
137
- meta_dict: Dict mapping words to metadata
138
- WORD: bool, match Vi-style WORD boundaries
139
- sentence: bool, complete sentences instead of words
140
- match_middle: bool, match anywhere in word
141
- pattern: compiled regex pattern for word boundaries
142
"""
143
144
class PathCompleter(Completer):
145
def __init__(
146
self,
147
only_directories=False,
148
get_paths=None,
149
file_filter=None,
150
min_input_len=0,
151
expanduser=False
152
):
153
"""
154
Completer for file and directory paths.
155
156
Parameters:
157
- only_directories: bool, complete only directories
158
- get_paths: Function returning list of paths to search
159
- file_filter: Function to filter files/directories
160
- min_input_len: int, minimum input length before completing
161
- expanduser: bool, expand ~ to user home directory
162
"""
163
164
class ExecutableCompleter(PathCompleter):
165
def __init__(self):
166
"""Completer for executable files in PATH."""
167
168
class NestedCompleter(Completer):
169
def __init__(self, options, ignore_case=True):
170
"""
171
Completer for nested command structures.
172
173
Parameters:
174
- options: Dict defining nested completion structure
175
- ignore_case: bool, case-insensitive matching
176
177
Options format:
178
{
179
'command1': {
180
'subcommand1': None,
181
'subcommand2': {'option1': None, 'option2': None}
182
},
183
'command2': WordCompleter(['arg1', 'arg2'])
184
}
185
"""
186
187
class FuzzyCompleter(Completer):
188
def __init__(self, completer, WORD=False, pattern=None):
189
"""
190
Fuzzy matching wrapper for any completer.
191
192
Parameters:
193
- completer: Completer instance to wrap
194
- WORD: bool, use Vi-style WORD boundaries
195
- pattern: compiled regex for word boundaries
196
"""
197
198
class FuzzyWordCompleter(Completer):
199
def __init__(
200
self,
201
words,
202
meta_dict=None,
203
WORD=False,
204
pattern=None
205
):
206
"""
207
Fuzzy word completer with built-in word list.
208
209
Parameters:
210
- words: List of words for completion
211
- meta_dict: Dict mapping words to metadata
212
- WORD: bool, use Vi-style WORD boundaries
213
- pattern: compiled regex for word boundaries
214
"""
215
216
class DeduplicateCompleter(Completer):
217
def __init__(self, completer):
218
"""
219
Remove duplicate completions from wrapped completer.
220
221
Parameters:
222
- completer: Completer instance to wrap
223
"""
224
```
225
226
### Validation System
227
228
Framework for validating user input with custom validation rules.
229
230
```python { .api }
231
class Validator:
232
"""Abstract base class for input validators."""
233
234
def validate(self, document):
235
"""
236
Validate document content.
237
238
Parameters:
239
- document: Document instance to validate
240
241
Raises:
242
ValidationError: If validation fails
243
"""
244
245
class ValidationError(Exception):
246
def __init__(self, cursor_position=0, message=""):
247
"""
248
Validation error with position and message.
249
250
Parameters:
251
- cursor_position: int, position where error occurred
252
- message: str, error message to display
253
"""
254
255
@property
256
def cursor_position(self):
257
"""int: Position where validation failed."""
258
259
@property
260
def message(self):
261
"""str: Error message."""
262
263
class ThreadedValidator(Validator):
264
def __init__(self, validator):
265
"""
266
Wrapper for running validator in background thread.
267
268
Parameters:
269
- validator: Validator instance to wrap
270
"""
271
272
class ConditionalValidator(Validator):
273
def __init__(self, validator, filter):
274
"""
275
Validator active only when filter is true.
276
277
Parameters:
278
- validator: Validator instance to wrap
279
- filter: Filter determining when validator is active
280
"""
281
282
class DummyValidator(Validator):
283
"""Dummy validator that never fails."""
284
285
def __init__(self):
286
"""Create dummy validator for testing."""
287
288
class DynamicValidator(Validator):
289
def __init__(self, get_validator):
290
"""
291
Dynamic validator that changes based on function.
292
293
Parameters:
294
- get_validator: Function returning Validator instance
295
"""
296
```
297
298
### Completion Style
299
300
Enumeration for controlling how completions are displayed.
301
302
```python { .api }
303
class CompleteStyle(Enum):
304
"""Completion display styles."""
305
COLUMN = "column"
306
MULTI_COLUMN = "multi-column"
307
READLINE_LIKE = "readline-like"
308
```
309
310
### Auto-suggestion
311
312
System for providing auto-suggestions based on history or other sources.
313
314
```python { .api }
315
class AutoSuggest:
316
"""Abstract base class for auto-suggestion."""
317
318
def get_suggestion(self, buffer, document):
319
"""
320
Get suggestion for current input.
321
322
Parameters:
323
- buffer: Buffer instance
324
- document: Document with current text
325
326
Returns:
327
Suggestion instance or None
328
"""
329
330
class AutoSuggestFromHistory(AutoSuggest):
331
def __init__(self):
332
"""Auto-suggest based on command history."""
333
334
class Suggestion:
335
def __init__(self, text):
336
"""
337
Auto-suggestion text.
338
339
Parameters:
340
- text: str, suggested text to append
341
"""
342
343
@property
344
def text(self):
345
"""str: Suggestion text."""
346
```
347
348
## Usage Examples
349
350
### Word Completion
351
352
```python
353
from prompt_toolkit import prompt
354
from prompt_toolkit.completion import WordCompleter
355
356
# Simple word completer
357
words = ['apple', 'banana', 'cherry', 'date', 'elderberry']
358
completer = WordCompleter(words)
359
360
result = prompt('Enter fruit: ', completer=completer)
361
print(f'You chose: {result}')
362
363
# Word completer with metadata
364
meta_dict = {
365
'apple': 'Red or green fruit',
366
'banana': 'Yellow tropical fruit',
367
'cherry': 'Small red fruit',
368
'date': 'Sweet brown fruit',
369
'elderberry': 'Dark purple berry'
370
}
371
372
completer_with_meta = WordCompleter(words, meta_dict=meta_dict)
373
result = prompt('Enter fruit (with descriptions): ', completer=completer_with_meta)
374
```
375
376
### Path Completion
377
378
```python
379
from prompt_toolkit import prompt
380
from prompt_toolkit.completion import PathCompleter
381
382
# File and directory completer
383
path_completer = PathCompleter()
384
file_path = prompt('Enter file path: ', completer=path_completer)
385
386
# Directory-only completer
387
dir_completer = PathCompleter(only_directories=True)
388
directory = prompt('Enter directory: ', completer=dir_completer)
389
390
# Executable completer
391
from prompt_toolkit.completion import ExecutableCompleter
392
exec_completer = ExecutableCompleter()
393
command = prompt('Enter command: ', completer=exec_completer)
394
```
395
396
### Nested Command Completion
397
398
```python
399
from prompt_toolkit import prompt
400
from prompt_toolkit.completion import NestedCompleter, WordCompleter
401
402
# Define nested command structure
403
nested_completer = NestedCompleter({
404
'git': {
405
'add': None,
406
'commit': {
407
'-m': None,
408
'--message': None,
409
'--amend': None
410
},
411
'push': {
412
'origin': WordCompleter(['master', 'main', 'develop']),
413
'upstream': None
414
},
415
'pull': {
416
'origin': WordCompleter(['master', 'main', 'develop'])
417
},
418
'checkout': {
419
'-b': None,
420
'master': None,
421
'main': None,
422
'develop': None
423
}
424
},
425
'docker': {
426
'run': None,
427
'build': None,
428
'ps': None,
429
'images': None,
430
'stop': None,
431
'rm': None
432
}
433
})
434
435
command = prompt('Enter command: ', completer=nested_completer)
436
print(f'Command: {command}')
437
```
438
439
### Fuzzy Completion
440
441
```python
442
from prompt_toolkit import prompt
443
from prompt_toolkit.completion import FuzzyWordCompleter
444
445
# Fuzzy word matching
446
words = [
447
'application', 'authentication', 'authorization',
448
'configuration', 'documentation', 'implementation',
449
'infrastructure', 'internationalization', 'optimization'
450
]
451
452
fuzzy_completer = FuzzyWordCompleter(words)
453
454
# Type partial matches like 'auth' to get 'authentication', 'authorization'
455
result = prompt('Enter term (fuzzy matching): ', completer=fuzzy_completer)
456
```
457
458
### Custom Completer
459
460
```python
461
from prompt_toolkit import prompt
462
from prompt_toolkit.completion import Completer, Completion
463
from prompt_toolkit.document import Document
464
465
class DatabaseTableCompleter(Completer):
466
def __init__(self, database_connection):
467
self.db = database_connection
468
469
def get_completions(self, document, complete_event):
470
# Get word before cursor
471
word = document.get_word_before_cursor()
472
473
# Query database for table names
474
try:
475
tables = self.db.get_table_names()
476
for table in tables:
477
if table.startswith(word.lower()):
478
yield Completion(
479
text=table,
480
start_position=-len(word),
481
display=table,
482
display_meta=f'Table: {table}'
483
)
484
except Exception:
485
# Handle database errors gracefully
486
pass
487
488
# Usage (assuming database connection exists)
489
# db_completer = DatabaseTableCompleter(db_connection)
490
# query = prompt('Enter table name: ', completer=db_completer)
491
```
492
493
### Input Validation
494
495
```python
496
from prompt_toolkit import prompt
497
from prompt_toolkit.validation import Validator, ValidationError
498
import re
499
500
class EmailValidator(Validator):
501
def validate(self, document):
502
text = document.text
503
if not re.match(r'^[^@]+@[^@]+\.[^@]+$', text):
504
raise ValidationError(
505
message='Invalid email format',
506
cursor_position=len(text)
507
)
508
509
class PasswordValidator(Validator):
510
def validate(self, document):
511
text = document.text
512
if len(text) < 8:
513
raise ValidationError(
514
message='Password must be at least 8 characters',
515
cursor_position=len(text)
516
)
517
if not re.search(r'[A-Z]', text):
518
raise ValidationError(
519
message='Password must contain uppercase letter',
520
cursor_position=len(text)
521
)
522
if not re.search(r'[0-9]', text):
523
raise ValidationError(
524
message='Password must contain a number',
525
cursor_position=len(text)
526
)
527
528
# Use validators
529
email = prompt('Email: ', validator=EmailValidator())
530
password = prompt('Password: ', password=True, validator=PasswordValidator())
531
```
532
533
### Completion with Validation
534
535
```python
536
from prompt_toolkit import prompt
537
from prompt_toolkit.completion import WordCompleter
538
from prompt_toolkit.validation import Validator, ValidationError
539
540
# Valid countries list
541
countries = ['USA', 'Canada', 'Mexico', 'Germany', 'France', 'Japan', 'Australia']
542
543
class CountryValidator(Validator):
544
def validate(self, document):
545
text = document.text
546
if text and text not in countries:
547
raise ValidationError(
548
message=f'"{text}" is not a valid country',
549
cursor_position=len(text)
550
)
551
552
# Combine completion and validation
553
country_completer = WordCompleter(countries, ignore_case=True)
554
country_validator = CountryValidator()
555
556
country = prompt(
557
'Enter country: ',
558
completer=country_completer,
559
validator=country_validator
560
)
561
print(f'Selected country: {country}')
562
```
563
564
### Dynamic Completion
565
566
```python
567
from prompt_toolkit import prompt
568
from prompt_toolkit.completion import DynamicCompleter, WordCompleter
569
570
# Different completion sets
571
programming_languages = ['python', 'javascript', 'java', 'c++', 'go']
572
frameworks = ['django', 'flask', 'react', 'vue', 'angular']
573
574
# Function to select completer based on context
575
def get_completer():
576
# This could be based on application state, user input, etc.
577
if current_mode == 'languages':
578
return WordCompleter(programming_languages)
579
elif current_mode == 'frameworks':
580
return WordCompleter(frameworks)
581
else:
582
return WordCompleter([])
583
584
# Use dynamic completer
585
dynamic_completer = DynamicCompleter(get_completer)
586
587
# Set mode and prompt
588
current_mode = 'languages'
589
language = prompt('Programming language: ', completer=dynamic_completer)
590
591
current_mode = 'frameworks'
592
framework = prompt('Framework: ', completer=dynamic_completer)
593
```
594
595
### Auto-suggestion with History
596
597
```python
598
from prompt_toolkit import PromptSession
599
from prompt_toolkit.history import FileHistory
600
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
601
from prompt_toolkit.completion import WordCompleter
602
603
# Create session with history and auto-suggestions
604
session = PromptSession(
605
history=FileHistory('.command_history'),
606
auto_suggest=AutoSuggestFromHistory(),
607
completer=WordCompleter(['help', 'exit', 'status', 'config'])
608
)
609
610
print("Type commands. Previous commands will be suggested in gray.")
611
print("Press Ctrl-C to exit.")
612
613
while True:
614
try:
615
command = session.prompt('> ')
616
if command.lower() == 'exit':
617
break
618
print(f'Executing: {command}')
619
except (EOFError, KeyboardInterrupt):
620
break
621
```
622
623
### Threaded Completion for Slow Operations
624
625
```python
626
from prompt_toolkit import prompt
627
from prompt_toolkit.completion import ThreadedCompleter, Completer, Completion
628
import time
629
630
class SlowCompleter(Completer):
631
"""Completer that simulates slow operations like network requests."""
632
633
def get_completions(self, document, complete_event):
634
word = document.get_word_before_cursor()
635
636
# Simulate slow operation (e.g., API call)
637
time.sleep(0.5)
638
639
# Generate completions
640
options = ['slow_option_1', 'slow_option_2', 'slow_option_3']
641
for option in options:
642
if option.startswith(word):
643
yield Completion(
644
text=option,
645
start_position=-len(word),
646
display_meta='From slow API'
647
)
648
649
# Wrap in ThreadedCompleter to avoid blocking UI
650
threaded_completer = ThreadedCompleter(SlowCompleter())
651
652
result = prompt('Enter option (completions load in background): ',
653
completer=threaded_completer)
654
```