0
# Tab Completion
1
2
Programmable tab completion system with customizable word breaking and completion functions. The gnureadline completion system provides context-aware completion with full control over behavior and display.
3
4
## Capabilities
5
6
### Completion Function Management
7
8
Set up and manage the function that provides completion suggestions.
9
10
```python { .api }
11
def set_completer(function):
12
"""
13
Set the completion function.
14
15
Parameters:
16
- function: Completion function(text: str, state: int) -> str or None
17
Should return the state-th completion for text, or None when done
18
"""
19
20
def get_completer():
21
"""
22
Get the current completion function.
23
24
Returns:
25
callable or None: Current completion function
26
"""
27
```
28
29
### Usage Examples
30
31
```python
32
import gnureadline
33
import os
34
35
def file_completer(text: str, state: int) -> str:
36
"""Complete file and directory names."""
37
if state == 0:
38
# First call - generate list of matches
39
if not hasattr(file_completer, 'matches'):
40
file_completer.matches = []
41
42
# Get directory to search in
43
dirname = os.path.dirname(text) or '.'
44
basename = os.path.basename(text)
45
46
try:
47
# Find matching files
48
file_completer.matches = []
49
for filename in os.listdir(dirname):
50
if filename.startswith(basename):
51
full_path = os.path.join(dirname, filename)
52
if os.path.isdir(full_path):
53
file_completer.matches.append(filename + '/')
54
else:
55
file_completer.matches.append(filename)
56
except OSError:
57
file_completer.matches = []
58
59
# Return the state-th match
60
try:
61
return file_completer.matches[state]
62
except IndexError:
63
return None
64
65
# Set up file completion
66
gnureadline.set_completer(file_completer)
67
gnureadline.parse_and_bind("tab: complete")
68
69
# Test the completer
70
current_completer = gnureadline.get_completer()
71
print(f"Current completer: {current_completer.__name__}")
72
```
73
74
### Word Breaking Configuration
75
76
Control how readline splits the input into words for completion.
77
78
```python { .api }
79
def set_completer_delims(string: str):
80
"""
81
Set the word break characters for completion.
82
83
Parameters:
84
- string: Characters that separate words for completion
85
"""
86
87
def get_completer_delims() -> str:
88
"""
89
Get the current word break characters for completion.
90
91
Returns:
92
str: Current word break characters
93
"""
94
```
95
96
### Usage Examples
97
98
```python
99
import gnureadline
100
101
# Default delimiters include space, tab, newline and many punctuation chars
102
default_delims = gnureadline.get_completer_delims()
103
print(f"Default delimiters: '{default_delims}'")
104
105
# Set custom delimiters for specific applications
106
# For shell-like completion, keep most punctuation as delimiters
107
gnureadline.set_completer_delims(' \t\n`!@#$%^&*()=+[{]}\\|;:\'",<>?')
108
109
# For Python identifier completion, use fewer delimiters
110
gnureadline.set_completer_delims(' \t\n`!@#$%^&*()=+[{]}\\|;:\'",<>?/')
111
112
# For path completion, don't break on forward slashes
113
gnureadline.set_completer_delims(' \t\n`!@#$%^&*()=+[{]}\\|;:\'",<>?')
114
115
print(f"Updated delimiters: '{gnureadline.get_completer_delims()}'")
116
```
117
118
### Completion Context Information
119
120
Access information about the current completion context.
121
122
```python { .api }
123
def get_completion_type() -> int:
124
"""
125
Get the type of completion being attempted.
126
127
Returns:
128
int: Completion type constant
129
"""
130
131
def get_begidx() -> int:
132
"""
133
Get the beginning index of the completion.
134
135
Returns:
136
int: Start index of text being completed
137
"""
138
139
def get_endidx() -> int:
140
"""
141
Get the ending index of the completion.
142
143
Returns:
144
int: End index of text being completed
145
"""
146
```
147
148
### Usage Examples
149
150
```python
151
import gnureadline
152
153
def context_aware_completer(text: str, state: int) -> str:
154
"""Completer that uses context information."""
155
if state == 0:
156
# Get completion context
157
begidx = gnureadline.get_begidx()
158
endidx = gnureadline.get_endidx()
159
completion_type = gnureadline.get_completion_type()
160
line_buffer = gnureadline.get_line_buffer()
161
162
print(f"\nCompletion context:")
163
print(f" Text: '{text}'")
164
print(f" Begin: {begidx}, End: {endidx}")
165
print(f" Type: {completion_type}")
166
print(f" Buffer: '{line_buffer}'")
167
print(f" Before: '{line_buffer[:begidx]}'")
168
print(f" After: '{line_buffer[endidx:]}'")
169
170
# Generate completions based on context
171
before_text = line_buffer[:begidx].strip()
172
173
if not before_text:
174
# At beginning of line - complete commands
175
context_aware_completer.matches = [
176
cmd for cmd in ['help', 'list', 'show', 'quit', 'exit']
177
if cmd.startswith(text)
178
]
179
elif before_text.endswith('--'):
180
# Complete long options
181
context_aware_completer.matches = [
182
opt for opt in ['--help', '--version', '--verbose', '--quiet']
183
if opt.startswith(text)
184
]
185
else:
186
# Complete filenames
187
import os
188
try:
189
context_aware_completer.matches = [
190
f for f in os.listdir('.')
191
if f.startswith(text)
192
]
193
except OSError:
194
context_aware_completer.matches = []
195
196
try:
197
return context_aware_completer.matches[state]
198
except (IndexError, AttributeError):
199
return None
200
201
gnureadline.set_completer(context_aware_completer)
202
gnureadline.parse_and_bind("tab: complete")
203
```
204
205
## Advanced Completion Examples
206
207
### Command-Specific Completion
208
209
```python
210
import gnureadline
211
import shlex
212
213
class CommandCompleter:
214
def __init__(self):
215
self.commands = {
216
'ls': self._complete_files,
217
'cd': self._complete_directories,
218
'cat': self._complete_files,
219
'grep': self._complete_grep,
220
'help': self._complete_help_topics,
221
}
222
223
self.help_topics = ['commands', 'completion', 'history', 'configuration']
224
225
def complete(self, text: str, state: int) -> str:
226
"""Main completion entry point."""
227
if state == 0:
228
line_buffer = gnureadline.get_line_buffer()
229
begidx = gnureadline.get_begidx()
230
231
# Parse the command line
232
try:
233
tokens = shlex.split(line_buffer[:begidx])
234
except ValueError:
235
tokens = line_buffer[:begidx].split()
236
237
if not tokens:
238
# Complete command names
239
self.matches = [cmd for cmd in self.commands.keys() if cmd.startswith(text)]
240
else:
241
command = tokens[0]
242
if command in self.commands:
243
# Command-specific completion
244
self.matches = self.commands[command](text, tokens)
245
else:
246
# Default to file completion
247
self.matches = self._complete_files(text, tokens)
248
249
try:
250
return self.matches[state]
251
except (IndexError, AttributeError):
252
return None
253
254
def _complete_files(self, text: str, tokens: list) -> list:
255
"""Complete file names."""
256
import os
257
import glob
258
259
if text:
260
pattern = text + '*'
261
else:
262
pattern = '*'
263
264
matches = glob.glob(pattern)
265
return [match + ('/' if os.path.isdir(match) else '') for match in matches]
266
267
def _complete_directories(self, text: str, tokens: list) -> list:
268
"""Complete directory names only."""
269
import os
270
import glob
271
272
pattern = text + '*' if text else '*'
273
matches = glob.glob(pattern)
274
return [match + '/' for match in matches if os.path.isdir(match)]
275
276
def _complete_grep(self, text: str, tokens: list) -> list:
277
"""Complete grep command with patterns and files."""
278
if len(tokens) < 2:
279
# First argument - could be a pattern or option
280
if text.startswith('-'):
281
return [opt for opt in ['-i', '-v', '-r', '-n', '-l'] if opt.startswith(text)]
282
else:
283
# Return some common patterns
284
patterns = ['error', 'warning', 'TODO', 'FIXME', 'import', 'def ', 'class ']
285
return [p for p in patterns if p.startswith(text)]
286
else:
287
# Additional arguments - complete files
288
return self._complete_files(text, tokens)
289
290
def _complete_help_topics(self, text: str, tokens: list) -> list:
291
"""Complete help topics."""
292
return [topic for topic in self.help_topics if topic.startswith(text)]
293
294
# Set up the command completer
295
completer = CommandCompleter()
296
gnureadline.set_completer(completer.complete)
297
gnureadline.parse_and_bind("tab: complete")
298
299
# Configure word delimiters for shell-like behavior
300
gnureadline.set_completer_delims(' \t\n`!@#$%^&*()=+[{]}\\|;:\'",<>?')
301
```
302
303
### Python Code Completion
304
305
```python
306
import gnureadline
307
import keyword
308
import builtins
309
310
class PythonCompleter:
311
def __init__(self):
312
self.namespace = {
313
**builtins.__dict__,
314
**globals(),
315
**locals()
316
}
317
318
def complete(self, text: str, state: int) -> str:
319
"""Complete Python identifiers and keywords."""
320
if state == 0:
321
self.matches = []
322
323
# Get the current line context
324
line = gnureadline.get_line_buffer()
325
begidx = gnureadline.get_begidx()
326
327
# Simple completion logic
328
if '.' in text:
329
# Attribute completion
330
parts = text.split('.')
331
obj_name = '.'.join(parts[:-1])
332
attr_prefix = parts[-1]
333
334
try:
335
obj = eval(obj_name, self.namespace)
336
attrs = [attr for attr in dir(obj)
337
if not attr.startswith('_') and attr.startswith(attr_prefix)]
338
self.matches = [f"{obj_name}.{attr}" for attr in attrs]
339
except:
340
self.matches = []
341
else:
342
# Name completion
343
candidates = []
344
345
# Python keywords
346
candidates.extend(keyword.kwlist)
347
348
# Built-in functions and types
349
candidates.extend(dir(builtins))
350
351
# Names in current namespace
352
candidates.extend(self.namespace.keys())
353
354
# Filter matches
355
self.matches = [name for name in candidates
356
if name.startswith(text) and not name.startswith('_')]
357
358
# Sort matches
359
self.matches.sort()
360
361
try:
362
return self.matches[state]
363
except (IndexError, AttributeError):
364
return None
365
366
def update_namespace(self, new_namespace: dict):
367
"""Update the completion namespace."""
368
self.namespace.update(new_namespace)
369
370
# Example usage in a Python REPL
371
python_completer = PythonCompleter()
372
gnureadline.set_completer(python_completer.complete)
373
gnureadline.parse_and_bind("tab: complete")
374
375
# For Python code, use different delimiters
376
gnureadline.set_completer_delims(' \t\n`!@#$%^&*()=+[{]}\\|;:\'",<>?/')
377
378
# Update namespace as variables are defined
379
python_completer.update_namespace({'my_variable': 42, 'my_function': lambda x: x})
380
```
381
382
## Completion Display Customization
383
384
For advanced completion display options, see the [Hook Functions](./hooks.md) documentation, specifically the `set_completion_display_matches_hook` function which allows customization of how completion matches are presented to the user.