##// END OF EJS Templates
Fix tab-completion for magics, other completion cleanup and fixes....
Fernando Perez -
Show More
@@ -0,0 +1,35 b''
1 """Tests for the IPython tab-completion machinery.
2 """
3 #-----------------------------------------------------------------------------
4 # Module imports
5 #-----------------------------------------------------------------------------
6
7 # stdlib
8 import sys
9
10 # third party
11 import nose.tools as nt
12
13 # our own packages
14 from IPython.core import completer
15
16 #-----------------------------------------------------------------------------
17 # Test functions
18 #-----------------------------------------------------------------------------
19 def test_protect_filename():
20 pairs = [ ('abc','abc'),
21 (' abc',r'\ abc'),
22 ('a bc',r'a\ bc'),
23 ('a bc',r'a\ \ bc'),
24 (' bc',r'\ \ bc'),
25 ]
26 # On posix, we also protect parens
27 if sys.platform != 'win32':
28 pairs.extend( [('a(bc',r'a\(bc'),
29 ('a)bc',r'a\)bc'),
30 ('a( )bc',r'a\(\ \)bc'),
31 ] )
32 # run the actual tests
33 for s1, s2 in pairs:
34 s1p = completer.protect_filename(s1)
35 nt.assert_equals(s1p, s2)
@@ -44,7 +44,6 b' its input.'
44 44
45 45 - When the original stdin is not a tty device, GNU readline is never
46 46 used, and this module (and the readline module) are silently inactive.
47
48 47 """
49 48
50 49 #*****************************************************************************
@@ -54,14 +53,19 b' used, and this module (and the readline module) are silently inactive.'
54 53 # proper procedure is to maintain its copyright as belonging to the Python
55 54 # Software Foundation (in addition to my own, for all new code).
56 55 #
56 # Copyright (C) 2008-2010 IPython Development Team
57 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
57 58 # Copyright (C) 2001 Python Software Foundation, www.python.org
58 # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu>
59 59 #
60 60 # Distributed under the terms of the BSD License. The full license is in
61 61 # the file COPYING, distributed as part of this software.
62 62 #
63 63 #*****************************************************************************
64 64
65 #-----------------------------------------------------------------------------
66 # Imports
67 #-----------------------------------------------------------------------------
68
65 69 import __builtin__
66 70 import __main__
67 71 import glob
@@ -73,23 +77,57 b' import shlex'
73 77 import sys
74 78 import types
75 79
80 import IPython.utils.rlineimpl as readline
76 81 from IPython.core.error import TryNext
77 82 from IPython.core.prefilter import ESC_MAGIC
78
79 import IPython.utils.rlineimpl as readline
80 from IPython.utils.ipstruct import Struct
81 83 from IPython.utils import generics
82
83 # Python 2.4 offers sets as a builtin
84 try:
85 set()
86 except NameError:
87 from sets import Set as set
88
89 84 from IPython.utils.genutils import debugx, dir2
90 85
86 #-----------------------------------------------------------------------------
87 # Globals
88 #-----------------------------------------------------------------------------
89
90 # Public API
91 91 __all__ = ['Completer','IPCompleter']
92 92
93 if sys.platform == 'win32':
94 PROTECTABLES = ' '
95 else:
96 PROTECTABLES = ' ()'
97
98 #-----------------------------------------------------------------------------
99 # Main functions and classes
100 #-----------------------------------------------------------------------------
101
102 def protect_filename(s):
103 """Escape a string to protect certain characters."""
104
105 return "".join([(ch in PROTECTABLES and '\\' + ch or ch)
106 for ch in s])
107
108
109 def single_dir_expand(matches):
110 "Recursively expand match lists containing a single dir."
111
112 if len(matches) == 1 and os.path.isdir(matches[0]):
113 # Takes care of links to directories also. Use '/'
114 # explicitly, even under Windows, so that name completions
115 # don't end up escaped.
116 d = matches[0]
117 if d[-1] in ['/','\\']:
118 d = d[:-1]
119
120 subdirs = os.listdir(d)
121 if subdirs:
122 matches = [ (d + '/' + p) for p in subdirs]
123 return single_dir_expand(matches)
124 else:
125 return matches
126 else:
127 return matches
128
129 class Bunch: pass
130
93 131 class Completer:
94 132 def __init__(self,namespace=None,global_namespace=None):
95 133 """Create a new completer for the command line.
@@ -152,6 +190,7 b' class Completer:'
152 190 defined in self.namespace or self.global_namespace that match.
153 191
154 192 """
193 #print 'Completer->global_matches, txt=%r' % text # dbg
155 194 matches = []
156 195 match_append = matches.append
157 196 n = len(text)
@@ -179,6 +218,7 b' class Completer:'
179 218 """
180 219 import re
181 220
221 #print 'Completer->attr_matches, txt=%r' % text # dbg
182 222 # Another option, seems to work great. Catches things like ''.<tab>
183 223 m = re.match(r"(\S+(\.\w+)*)\.(\w*)$", text)
184 224
@@ -205,6 +245,7 b' class Completer:'
205 245 res = ["%s.%s" % (expr, w) for w in words if w[:n] == attr ]
206 246 return res
207 247
248
208 249 class IPCompleter(Completer):
209 250 """Extension of the completer class with IPython-specific features"""
210 251
@@ -235,7 +276,7 b' class IPCompleter(Completer):'
235 276 to complete. """
236 277
237 278 Completer.__init__(self,namespace,global_namespace)
238 self.magic_prefix = shell.name+'.magic_'
279
239 280 self.magic_escape = ESC_MAGIC
240 281 self.readline = readline
241 282 delims = self.readline.get_completer_delims()
@@ -244,7 +285,8 b' class IPCompleter(Completer):'
244 285 self.get_line_buffer = self.readline.get_line_buffer
245 286 self.get_endidx = self.readline.get_endidx
246 287 self.omit__names = omit__names
247 self.merge_completions = shell.readline_merge_completions
288 self.merge_completions = shell.readline_merge_completions
289 self.shell = shell.shell
248 290 if alias_table is None:
249 291 alias_table = {}
250 292 self.alias_table = alias_table
@@ -263,11 +305,13 b' class IPCompleter(Completer):'
263 305 self.clean_glob = self._clean_glob_win32
264 306 else:
265 307 self.clean_glob = self._clean_glob
308
309 # All active matcher routines for completion
266 310 self.matchers = [self.python_matches,
267 311 self.file_matches,
312 self.magic_matches,
268 313 self.alias_matches,
269 314 self.python_func_kw_matches]
270
271 315
272 316 # Code contributed by Alex Schmolck, for ipython/emacs integration
273 317 def all_completions(self, text):
@@ -278,9 +322,8 b' class IPCompleter(Completer):'
278 322 try:
279 323 for i in xrange(sys.maxint):
280 324 res = self.complete(text, i)
281
282 if not res: break
283
325 if not res:
326 break
284 327 comp_append(res)
285 328 #XXX workaround for ``notDefined.<tab>``
286 329 except NameError:
@@ -316,41 +359,12 b' class IPCompleter(Completer):'
316 359 # don't want to treat as delimiters in filename matching
317 360 # when escaped with backslash
318 361
319 if sys.platform == 'win32':
320 protectables = ' '
321 else:
322 protectables = ' ()'
323
324 362 if text.startswith('!'):
325 363 text = text[1:]
326 364 text_prefix = '!'
327 365 else:
328 366 text_prefix = ''
329 367
330 def protect_filename(s):
331 return "".join([(ch in protectables and '\\' + ch or ch)
332 for ch in s])
333
334 def single_dir_expand(matches):
335 "Recursively expand match lists containing a single dir."
336
337 if len(matches) == 1 and os.path.isdir(matches[0]):
338 # Takes care of links to directories also. Use '/'
339 # explicitly, even under Windows, so that name completions
340 # don't end up escaped.
341 d = matches[0]
342 if d[-1] in ['/','\\']:
343 d = d[:-1]
344
345 subdirs = os.listdir(d)
346 if subdirs:
347 matches = [ (d + '/' + p) for p in subdirs]
348 return single_dir_expand(matches)
349 else:
350 return matches
351 else:
352 return matches
353
354 368 lbuf = self.lbuf
355 369 open_quotes = 0 # track strings with open quotes
356 370 try:
@@ -402,13 +416,24 b' class IPCompleter(Completer):'
402 416 #print 'mm',matches # dbg
403 417 return single_dir_expand(matches)
404 418
419 def magic_matches(self, text):
420 """Match magics"""
421 #print 'Completer->magic_matches:',text,'lb',self.lbuf # dbg
422 # Get all shell magics now rather than statically, so magics loaded at
423 # runtime show up too
424 magics = self.shell.lsmagic()
425 pre = self.magic_escape
426 baretext = text.lstrip(pre)
427 return [ pre+m for m in magics if m.startswith(baretext)]
428
405 429 def alias_matches(self, text):
406 430 """Match internal system aliases"""
407 431 #print 'Completer->alias_matches:',text,'lb',self.lbuf # dbg
408 432
409 433 # if we are not in the first 'item', alias matching
410 434 # doesn't make sense - unless we are starting with 'sudo' command.
411 if ' ' in self.lbuf.lstrip() and not self.lbuf.lstrip().startswith('sudo'):
435 if ' ' in self.lbuf.lstrip() and \
436 not self.lbuf.lstrip().startswith('sudo'):
412 437 return []
413 438 text = os.path.expanduser(text)
414 439 aliases = self.alias_table.keys()
@@ -420,7 +445,7 b' class IPCompleter(Completer):'
420 445 def python_matches(self,text):
421 446 """Match attributes or global python names"""
422 447
423 #print 'Completer->python_matches, txt=<%s>' % text # dbg
448 #print 'Completer->python_matches, txt=%r' % text # dbg
424 449 if "." in text:
425 450 try:
426 451 matches = self.attr_matches(text)
@@ -439,11 +464,7 b' class IPCompleter(Completer):'
439 464 matches = []
440 465 else:
441 466 matches = self.global_matches(text)
442 # this is so completion finds magics when automagic is on:
443 if (matches == [] and
444 not text.startswith(os.sep) and
445 not ' ' in self.lbuf):
446 matches = self.attr_matches(self.magic_prefix+text)
467
447 468 return matches
448 469
449 470 def _default_arguments(self, obj):
@@ -514,9 +535,11 b' class IPCompleter(Completer):'
514 535 callableMatches = self.attr_matches('.'.join(ids[::-1]))
515 536 argMatches = []
516 537 for callableMatch in callableMatches:
517 try: namedArgs = self._default_arguments(eval(callableMatch,
538 try:
539 namedArgs = self._default_arguments(eval(callableMatch,
518 540 self.namespace))
519 except: continue
541 except:
542 continue
520 543 for namedArg in namedArgs:
521 544 if namedArg.startswith(text):
522 545 argMatches.append("%s=" %namedArg)
@@ -528,7 +551,7 b' class IPCompleter(Completer):'
528 551 if not line.strip():
529 552 return None
530 553
531 event = Struct()
554 event = Bunch()
532 555 event.line = line
533 556 event.symbol = text
534 557 cmd = line.split(None,1)[0]
@@ -540,11 +563,9 b' class IPCompleter(Completer):'
540 563 try_magic = self.custom_completers.s_matches(
541 564 self.magic_escape + cmd)
542 565 else:
543 try_magic = []
544
566 try_magic = []
545 567
546 for c in itertools.chain(
547 self.custom_completers.s_matches(cmd),
568 for c in itertools.chain(self.custom_completers.s_matches(cmd),
548 569 try_magic,
549 570 self.custom_completers.flat_matches(self.lbuf)):
550 571 #print "try",c # dbg
@@ -555,7 +576,8 b' class IPCompleter(Completer):'
555 576 if withcase:
556 577 return withcase
557 578 # if none, then case insensitive ones are ok too
558 return [r for r in res if r.lower().startswith(text.lower())]
579 text_low = text.lower()
580 return [r for r in res if r.lower().startswith(text_low)]
559 581 except TryNext:
560 582 pass
561 583
@@ -598,14 +620,11 b' class IPCompleter(Completer):'
598 620 return None
599 621
600 622 magic_escape = self.magic_escape
601 magic_prefix = self.magic_prefix
602 623
603 624 self.lbuf = self.full_lbuf[:self.get_endidx()]
604 625
605 626 try:
606 if text.startswith(magic_escape):
607 text = text.replace(magic_escape,magic_prefix)
608 elif text.startswith('~'):
627 if text.startswith('~'):
609 628 text = os.path.expanduser(text)
610 629 if state == 0:
611 630 custom_res = self.dispatch_custom_completer(text)
@@ -625,13 +644,10 b' class IPCompleter(Completer):'
625 644 self.matches = matcher(text)
626 645 if self.matches:
627 646 break
628 def uniq(alist):
629 set = {}
630 return [set.setdefault(e,e) for e in alist if e not in set]
631 self.matches = uniq(self.matches)
647 self.matches = list(set(self.matches))
632 648 try:
633 ret = self.matches[state].replace(magic_prefix,magic_escape)
634 return ret
649 #print "MATCH: %r" % self.matches[state] # dbg
650 return self.matches[state]
635 651 except IndexError:
636 652 return None
637 653 except:
General Comments 0
You need to be logged in to leave comments. Login now