##// END OF EJS Templates
Add line splitting for text completion (like readline does internally)....
Fernando Perez -
Show More
@@ -61,6 +61,7 b' used, and this module (and the readline module) are silently inactive.'
61 61 # the file COPYING, distributed as part of this software.
62 62 #
63 63 #*****************************************************************************
64 from __future__ import print_function
64 65
65 66 #-----------------------------------------------------------------------------
66 67 # Imports
@@ -79,7 +80,7 b' import sys'
79 80
80 81 from IPython.core.error import TryNext
81 82 from IPython.core.prefilter import ESC_MAGIC
82 from IPython.utils import generics
83 from IPython.utils import generics, io
83 84 from IPython.utils.frame import debugx
84 85 from IPython.utils.dir2 import dir2
85 86
@@ -138,9 +139,60 b' def single_dir_expand(matches):'
138 139 else:
139 140 return matches
140 141
141 class Bunch: pass
142 142
143 class Completer:
143 class Bunch(object): pass
144
145
146 class CompletionSplitter(object):
147 """An object to split an input line in a manner similar to readline.
148
149 By having our own implementation, we can expose readline-like completion in
150 a uniform manner to all frontends. This object only needs to be given the
151 line of text to be split and the cursor position on said line, and it
152 returns the 'word' to be completed on at the cursor after splitting the
153 entire line.
154
155 What characters are used as splitting delimiters can be controlled by
156 setting the `delims` attribute (this is a property that internally
157 automatically builds the necessary """
158
159 # Private interface
160
161 # A string of delimiter characters. The default value makes sense for
162 # IPython's most typical usage patterns.
163 _delims = ' \t\n`!@#$^&*()=+[{]}\\|;:\'",<>?'
164
165 # The expression (a normal string) to be compiled into a regular expression
166 # for actual splitting. We store it as an attribute mostly for ease of
167 # debugging, since this type of code can be so tricky to debug.
168 _delim_expr = None
169
170 # The regular expression that does the actual splitting
171 _delim_re = None
172
173 def __init__(self, delims=None):
174 delims = CompletionSplitter._delims if delims is None else delims
175 self.set_delims(delims)
176
177 def set_delims(self, delims):
178 """Set the delimiters for line splitting."""
179 expr = '[' + ''.join('\\'+ c for c in delims) + ']'
180 self._delim_re = re.compile(expr)
181 self._delims = delims
182 self._delim_expr = expr
183
184 def get_delims(self):
185 """Return the string of delimiter characters."""
186 return self._delims
187
188 def split_line(self, line, cursor_pos=None):
189 """Split a line of text with a cursor at the given position.
190 """
191 l = line if cursor_pos is None else line[:cursor_pos]
192 return self._delim_re.split(l)[-1]
193
194
195 class Completer(object):
144 196 def __init__(self,namespace=None,global_namespace=None):
145 197 """Create a new completer for the command line.
146 198
@@ -631,7 +683,8 b' class IPCompleter(Completer):'
631 683 Index of the cursor in the full line buffer. Should be provided by
632 684 remote frontends where kernel has no access to frontend state.
633 685 """
634
686 #io.rprint('COMP', text, line_buffer, cursor_pos) # dbg
687
635 688 magic_escape = self.magic_escape
636 689 self.full_lbuf = line_buffer
637 690 self.lbuf = self.full_lbuf[:cursor_pos]
@@ -663,7 +716,7 b' class IPCompleter(Completer):'
663 716 # simply collapse the dict into a list for readline, but we'd have
664 717 # richer completion semantics in other evironments.
665 718 self.matches = sorted(set(self.matches))
666 #from IPython.utils.io import rprint; rprint(self.matches) # dbg
719 #io.rprint('MATCHES', self.matches) # dbg
667 720 return self.matches
668 721
669 722 def rlcomplete(self, text, state):
@@ -679,15 +732,15 b' class IPCompleter(Completer):'
679 732
680 733 state : int
681 734 Counter used by readline.
682
683 735 """
684
685 #print "rlcomplete! '%s' %s" % (text, state) # dbg
686
687 736 if state==0:
737
688 738 self.full_lbuf = line_buffer = self.get_line_buffer()
689 739 cursor_pos = self.get_endidx()
690 740
741 #io.rprint("\nRLCOMPLETE: %r %r %r" %
742 # (text, line_buffer, cursor_pos) ) # dbg
743
691 744 # if there is only a tab on a line with only whitespace, instead of
692 745 # the mostly useless 'do you want to see all million completions'
693 746 # message, just do the right thing and give the user his tab!
@@ -699,7 +752,7 b' class IPCompleter(Completer):'
699 752
700 753 # don't apply this on 'dumb' terminals, such as emacs buffers, so
701 754 # we don't interfere with their own tab-completion mechanism.
702 if not (self.dumb_terminal or self.full_lbuf.strip()):
755 if not (self.dumb_terminal or line_buffer.strip()):
703 756 self.readline.insert_text('\t')
704 757 sys.stdout.flush()
705 758 return None
@@ -719,4 +772,3 b' class IPCompleter(Completer):'
719 772 return self.matches[state]
720 773 except IndexError:
721 774 return None
722
@@ -6,6 +6,7 b''
6 6
7 7 # stdlib
8 8 import sys
9 import unittest
9 10
10 11 # third party
11 12 import nose.tools as nt
@@ -33,3 +34,51 b' def test_protect_filename():'
33 34 for s1, s2 in pairs:
34 35 s1p = completer.protect_filename(s1)
35 36 nt.assert_equals(s1p, s2)
37
38
39 def check_line_split(splitter, test_specs):
40 for part1, part2, split in test_specs:
41 cursor_pos = len(part1)
42 line = part1+part2
43 out = splitter.split_line(line, cursor_pos)
44 nt.assert_equal(out, split)
45
46
47 def test_line_split():
48 """Basice line splitter test with default specs."""
49 sp = completer.CompletionSplitter()
50 # The format of the test specs is: part1, part2, expected answer. Parts 1
51 # and 2 are joined into the 'line' sent to the splitter, as if the cursor
52 # was at the end of part1. So an empty part2 represents someone hitting
53 # tab at the end of the line, the most common case.
54 t = [('run some/scrip', '', 'some/scrip'),
55 ('run scripts/er', 'ror.py foo', 'scripts/er'),
56 ('echo $HOM', '', 'HOM'),
57 ('print sys.pa', '', 'sys.pa'),
58 ('print(sys.pa', '', 'sys.pa'),
59 ("execfile('scripts/er", '', 'scripts/er'),
60 ('a[x.', '', 'x.'),
61 ('a[x.', 'y', 'x.'),
62 ('cd "some_file/', '', 'some_file/'),
63 ]
64 check_line_split(sp, t)
65
66
67 class CompletionSplitterTestCase(unittest.TestCase):
68 def setUp(self):
69 self.sp = completer.CompletionSplitter()
70
71 def test_delim_setting(self):
72 self.sp.delims = ' '
73 # Validate that property handling works ok
74 nt.assert_equal(self.sp.delims, ' ')
75 nt.assert_equal(self.sp.delim_expr, '[\ ]')
76
77 def test_spaces(self):
78 """Test with only spaces as split chars."""
79 self.sp.delims = ' '
80 t = [('foo', '', 'foo'),
81 ('run foo', '', 'foo'),
82 ('run foo', 'bar', 'foo'),
83 ]
84 check_line_split(self.sp, t)
General Comments 0
You need to be logged in to leave comments. Login now