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 """Automatic completion for optparse module.
34
35 This module provide automatic bash completion support for programs that use the
36 optparse module. The premise is that the optparse options parser specifies
37 enough information (and more) for us to be able to generate completion strings
38 easily. Another advantage of this over traditional completion schemes where
39 the completion strings are hard-coded in a separate bash source file, is that
40 the same code that parses the options is used to generate the completions, so
41 the completions is always up-to-date with the program itself.
42
43 In addition, we allow you specify a list of regular expressions or code that
44 define what kinds of files should be proposed as completions to this file if
45 needed. If you want to implement more complex behavior, you can instead
46 specify a function, which will be called with the current directory as an
47 argument.
48
49 You need to activate bash completion using the shell script function that comes
50 with optcomplete (see http://furius.ca/optcomplete for more details).
51
52 """
53
54 __version__ = "$Revision$"
55 __author__ = "Martin Blais <blais@furius.ca>"
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 import os
92 import sys
93 import types
94 import re
95
96 from pprint import pformat
97
98 from optparse import OptionParser
99
100 from starcluster import static
101
102 debugfn = os.path.join(static.STARCLUSTER_LOG_DIR, 'completion-debug.log')
103
104
106
107 """Completes by listing all possible files in current directory."""
108
109 - def __call__(self, pwd, line, point, prefix, suffix):
110 return os.listdir(pwd)
111
112
114
115 """Generates empty completion list."""
116
117 - def __call__(self, pwd, line, point, prefix, suffix):
119
120
122
123 """Completes by listing subdirectories only."""
124
125 - def __call__(self, pwd, line, point, prefix, suffix):
126 return filter(os.path.isdir, os.listdir(pwd))
127
128
130
131 """Completes by filtering all possible files with the given list of
132 regexps."""
133
134 - def __init__(self, regexlist, always_dirs=True):
135 self.always_dirs = always_dirs
136
137 if isinstance(regexlist, types.StringType):
138 regexlist = [regexlist]
139 self.regexlist = []
140 for r in regexlist:
141 if isinstance(r, types.StringType):
142 r = re.compile(r)
143 self.regexlist.append(r)
144
145 - def __call__(self, pwd, line, point, prefix, suffix):
146 dn = os.path.dirname(prefix)
147 if dn:
148 pwd = dn
149 files = os.listdir(pwd)
150 ofiles = []
151 for fn in files:
152 for r in self.regexlist:
153 if r.match(fn):
154 if dn:
155 fn = os.path.join(dn, fn)
156 ofiles.append(fn)
157 break
158 if self.always_dirs and os.path.isdir(fn):
159 ofiles.append(fn + '/')
160 return ofiles
161
162
164
165 """Completes by filtering using a fixed list of strings."""
166
168 self.olist = stringlist
169
170 - def __call__(self, pwd, line, point, prefix, suffix):
172
173
175
176 """Return a prefix and suffix of the enclosing word. The character under
177 the cursor is the first character of the suffix."""
178
179 wsre = re.compile('[ \t]')
180
181 if point < 0 or point > len(line):
182 return '', ''
183
184 preii = point - 1
185 while preii >= 0:
186 if wsre.match(line[preii]):
187 break
188 preii -= 1
189 preii += 1
190
191 sufii = point
192 while sufii < len(line):
193 if wsre.match(line[sufii]):
194 break
195 sufii += 1
196
197 return line[preii:point], line[point:sufii]
198
199
200 -def autocomplete(parser,
201 arg_completer=None,
202 opt_completer=None,
203 subcmd_completer=None,
204 subcommands=None):
205
206 """Automatically detect if we are requested completing and if so generate
207 completion automatically from given parser.
208
209 'parser' is the options parser to use.
210
211 'arg_completer' is a callable object that gets invoked to produce a list of
212 completions for arguments completion (oftentimes files).
213
214 'opt_completer' is the default completer to the options that require a
215 value. 'subcmd_completer' is the default completer for the subcommand
216 arguments.
217
218 If 'subcommands' is specified, the script expects it to be a map of
219 command-name to an object of any kind. We are assuming that this object is
220 a map from command name to a pair of (options parser, completer) for the
221 command. If the value is not such a tuple, the method
222 'autocomplete(completer)' is invoked on the resulting object.
223
224 This will attempt to match the first non-option argument into a subcommand
225 name and if so will use the local parser in the corresponding map entry's
226 value. This is used to implement completion for subcommand syntax and will
227 not be needed in most cases."""
228
229
230
231 if 'OPTPARSE_AUTO_COMPLETE' not in os.environ:
232 return
233
234
235 if arg_completer is None:
236 arg_completer = NoneCompleter()
237 if opt_completer is None:
238 opt_completer = NoneCompleter()
239 if subcmd_completer is None:
240
241 subcmd_completer = NoneCompleter()
242
243
244
245 completer = arg_completer
246
247
248
249
250
251
252
253
254
255 if not 'COMP_WORDS' in os.environ:
256 os.environ['COMP_WORDS'] = os.environ['COMP_LINE']
257
258 cwords = os.environ['COMP_WORDS'].split()
259 cline = os.environ['COMP_LINE']
260 cpoint = int(os.environ['COMP_POINT'])
261 cword = int(os.environ['COMP_CWORD'])
262
263
264
265 if subcommands:
266 assert isinstance(subcommands, types.DictType)
267 value = guess_first_nonoption(parser, subcommands)
268 if value:
269 if isinstance(value, types.ListType) or \
270 isinstance(value, types.TupleType):
271 parser = value[0]
272 if len(value) > 1 and value[1]:
273
274 completer = value[1]
275 else:
276 completer = subcmd_completer
277 return autocomplete(parser, completer)
278 else:
279
280
281 if hasattr(value, 'autocomplete'):
282 return value.autocomplete(subcmd_completer)
283 else:
284 sys.exit(1)
285
286
287 prefix, suffix = extract_word(cline, cpoint)
288
289
290
291
292
293
294
295 optarg = False
296 try:
297
298
299 prev = None
300 if cword < len(cwords):
301 mo = re.search('(--.*)=(.*)', cwords[cword])
302 if mo:
303 prev, prefix = mo.groups()
304 if not prev:
305 prev = cwords[cword - 1]
306
307 if prev and prev.startswith('-'):
308 option = parser.get_option(prev)
309 if option:
310 if option.nargs > 0:
311 optarg = True
312 if hasattr(option, 'completer'):
313 completer = option.completer
314 elif option.type != 'string':
315 completer = NoneCompleter()
316 else:
317 completer = opt_completer
318
319 elif hasattr(option, 'completer'):
320 raise SystemExit(
321 "Error: optparse option with a completer "
322 "does not take arguments: %s" % str(option))
323 except KeyError:
324 pass
325
326 completions = []
327
328
329 if not optarg and (not prefix or prefix.startswith('-')):
330 completions += parser._short_opt.keys()
331 completions += parser._long_opt.keys()
332
333
334
335 if completer and (not prefix or not prefix.startswith('-')):
336
337
338 if isinstance(completer, types.StringType) or \
339 isinstance(completer, types.ListType) or \
340 isinstance(completer, types.TupleType):
341 completer = RegexCompleter(completer)
342 completions += completer(os.getcwd(), cline,
343 cpoint, prefix, suffix)
344 elif isinstance(completer, types.FunctionType) or \
345 isinstance(completer, types.LambdaType) or \
346 isinstance(completer, types.ClassType) or \
347 isinstance(completer, types.ObjectType):
348 completions += completer(os.getcwd(), cline,
349 cpoint, prefix, suffix)
350
351
352 if prefix:
353 completions = filter(lambda x: x.startswith(prefix), completions)
354
355
356 print ' '.join(completions)
357
358
359
360 if debugfn:
361 f = open(debugfn, 'a')
362 print >> f, '---------------------------------------------------------'
363 print >> f, 'CWORDS', cwords
364 print >> f, 'CLINE', cline
365 print >> f, 'CPOINT', cpoint
366 print >> f, 'CWORD', cword
367 print >> f, '\nShort options'
368 print >> f, pformat(parser._short_opt)
369 print >> f, '\nLong options'
370 print >> f, pformat(parser._long_opt)
371 print >> f, 'Prefix/Suffix:', prefix, suffix
372 print >> f, 'completions', completions
373 f.close()
374
375
376
377 sys.exit(1)
378
379
381 """Hack to keep OptionParser from writing to sys.stderr when
382 calling self.exit from self.error"""
383 self.exit(2, msg=None)
384
385
387
388 """Given a global options parser, try to guess the first non-option without
389 generating an exception. This is used for scripts that implement a
390 subcommand syntax, so that we can generate the appropriate completions for
391 the subcommand."""
392
393 import copy
394 gparser = copy.deepcopy(gparser)
395
396 def print_usage_nousage(self, file=None):
397 pass
398 gparser.print_usage = print_usage_nousage
399
400
401 prev_interspersed = gparser.allow_interspersed_args
402 gparser.disable_interspersed_args()
403
404 cwords = os.environ['COMP_WORDS'].split()
405
406
407 error_func = gparser.error
408 try:
409 try:
410 instancemethod = type(OptionParser.error)
411
412 gparser.error = instancemethod(error_override,
413 gparser, OptionParser)
414 gopts, args = gparser.parse_args(cwords[1:])
415 except SystemExit:
416 return None
417 finally:
418
419 gparser.error = instancemethod(error_func, gparser, OptionParser)
420
421 value = None
422 if args:
423 subcmdname = args[0]
424 try:
425 value = subcmds_map[subcmdname]
426 except KeyError:
427 pass
428
429 gparser.allow_interspersed_args = prev_interspersed
430
431 return value
432
433
435
436 """Simple default base class implementation for a subcommand that supports
437 command completion. This class is assuming that there might be a method
438 addopts(self, parser) to declare options for this subcommand, and an
439 optional completer data member to contain command-specific completion. Of
440 course, you don't really have to use this, but if you do it is convenient
441 to have it here."""
442
454
455
466
467 if __name__ == '__main__':
468 test()
469