diff --git a/IPython/core/completer.py b/IPython/core/completer.py index a86a1a1..b0032d5 100644 --- a/IPython/core/completer.py +++ b/IPython/core/completer.py @@ -346,20 +346,15 @@ class IPCompleter(Completer): Completer.__init__(self, namespace, global_namespace) self.magic_escape = ESC_MAGIC - self.splitter = CompletionSplitter() - # Readline-dependent code - self.use_readline = use_readline + # Readline configuration, only used by the rlcompleter method. if use_readline: + # We store the right version of readline so that later code import IPython.utils.rlineimpl as readline self.readline = readline - delims = self.readline.get_completer_delims() - delims = delims.replace(self.magic_escape,'') - self.readline.set_completer_delims(delims) - self.get_line_buffer = self.readline.get_line_buffer - self.get_endidx = self.readline.get_endidx - # /end readline-dependent code + else: + self.readline = None # List where completion matches will be stored self.matches = [] @@ -444,18 +439,18 @@ class IPCompleter(Completer): else: text_prefix = '' - lbuf = self.lbuf + text_until_cursor = self.text_until_cursor open_quotes = 0 # track strings with open quotes try: - lsplit = shlex.split(lbuf)[-1] + lsplit = shlex.split(text_until_cursor)[-1] except ValueError: # typically an unmatched ", or backslash without escaped char. - if lbuf.count('"')==1: + if text_until_cursor.count('"')==1: open_quotes = 1 - lsplit = lbuf.split('"')[-1] - elif lbuf.count("'")==1: + lsplit = text_until_cursor.split('"')[-1] + elif text_until_cursor.count("'")==1: open_quotes = 1 - lsplit = lbuf.split("'")[-1] + lsplit = text_until_cursor.split("'")[-1] else: return [] except IndexError: @@ -497,7 +492,7 @@ class IPCompleter(Completer): def magic_matches(self, text): """Match magics""" - #print 'Completer->magic_matches:',text,'lb',self.lbuf # dbg + #print 'Completer->magic_matches:',text,'lb',self.text_until_cursor # dbg # Get all shell magics now rather than statically, so magics loaded at # runtime show up too magics = self.shell.lsmagic() @@ -507,19 +502,19 @@ class IPCompleter(Completer): def alias_matches(self, text): """Match internal system aliases""" - #print 'Completer->alias_matches:',text,'lb',self.lbuf # dbg - - # if we are not in the first 'item', alias matching + #print 'Completer->alias_matches:',text,'lb',self.text_until_cursor # dbg + + # if we are not in the first 'item', alias matching # doesn't make sense - unless we are starting with 'sudo' command. - if ' ' in self.lbuf.lstrip() and \ - not self.lbuf.lstrip().startswith('sudo'): + main_text = self.text_until_cursor.lstrip() + if ' ' in main_text and not main_text.startswith('sudo'): return [] text = os.path.expanduser(text) aliases = self.alias_table.keys() - if text == "": + if text == '': return aliases else: - return [alias for alias in aliases if alias.startswith(text)] + return [a for a in aliases if a.startswith(text)] def python_matches(self,text): """Match attributes or global python names""" @@ -581,7 +576,7 @@ class IPCompleter(Completer): ''', re.VERBOSE | re.DOTALL) # 1. find the nearest identifier that comes before an unclosed # parenthesis e.g. for "foo (1+bar(x), pa", the candidate is "foo" - tokens = regexp.findall(self.get_line_buffer()) + tokens = regexp.findall(self.line_buffer) tokens.reverse() iterTokens = iter(tokens); openPar = 0 for token in iterTokens: @@ -626,7 +621,7 @@ class IPCompleter(Completer): def dispatch_custom_completer(self,text): #print "Custom! '%s' %s" % (text, self.custom_completers) # dbg - line = self.full_lbuf + line = self.line_buffer if not line.strip(): return None @@ -640,13 +635,13 @@ class IPCompleter(Completer): # for foo etc, try also to find completer for %foo if not cmd.startswith(self.magic_escape): try_magic = self.custom_completers.s_matches( - self.magic_escape + cmd) + self.magic_escape + cmd) else: try_magic = [] for c in itertools.chain(self.custom_completers.s_matches(cmd), - try_magic, - self.custom_completers.flat_matches(self.lbuf)): + try_magic, + self.custom_completers.flat_matches(self.text_until_cursor)): #print "try",c # dbg try: res = c(event) @@ -703,8 +698,8 @@ class IPCompleter(Completer): line_buffer = text magic_escape = self.magic_escape - self.full_lbuf = line_buffer - self.lbuf = self.full_lbuf[:cursor_pos] + self.line_buffer = line_buffer + self.text_until_cursor = self.line_buffer[:cursor_pos] #io.rprint('\nCOMP2 %r %r %r' % (text, line_buffer, cursor_pos)) # dbg # Start with a clean slate of completions @@ -750,8 +745,8 @@ class IPCompleter(Completer): """ if state==0: - self.full_lbuf = line_buffer = self.get_line_buffer() - cursor_pos = self.get_endidx() + self.line_buffer = line_buffer = self.readline.get_line_buffer() + cursor_pos = self.readline.get_endidx() #io.rprint("\nRLCOMPLETE: %r %r %r" % # (text, line_buffer, cursor_pos) ) # dbg diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 04f7a67..f46d16d 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -260,6 +260,11 @@ class InteractiveShell(Configurable, Magic): # init_readline() must come before init_io(), because init_io uses # readline related things. self.init_readline() + # init_completer must come after init_readline, because it needs to + # know whether readline is present or not system-wide to configure the + # completers, since the completion machinery can now operate + # independently of readline (e.g. over the network) + self.init_completer() # TODO: init_io() needs to happen before init_traceback handlers # because the traceback handlers hardcode the stdout/stderr streams. # This logic in in debugger.Pdb and should eventually be changed. @@ -1511,78 +1516,6 @@ class InteractiveShell(Configurable, Magic): self._showtraceback(etype, value, stb) #------------------------------------------------------------------------- - # Things related to tab completion - #------------------------------------------------------------------------- - - def complete(self, text, line=None, cursor_pos=None): - """Return the completed text and a list of completions. - - Parameters - ---------- - - text : string - A string of text to be completed on. It can be given as empty and - instead a line/position pair are given. In this case, the - completer itself will split the line like readline does. - - line : string, optional - The complete line that text is part of. - - cursor_pos : int, optional - The position of the cursor on the input line. - - Returns - ------- - text : string - The actual text that was completed. - - matches : list - A sorted list with all possible completions. - - The optional arguments allow the completion to take more context into - account, and are part of the low-level completion API. - - This is a wrapper around the completion mechanism, similar to what - readline does at the command line when the TAB key is hit. By - exposing it as a method, it can be used by other non-readline - environments (such as GUIs) for text completion. - - Simple usage example: - - In [1]: x = 'hello' - - In [2]: _ip.complete('x.l') - Out[2]: ('x.l', ['x.ljust', 'x.lower', 'x.lstrip']) - """ - - # Inject names into __builtin__ so we can complete on the added names. - with self.builtin_trap: - return self.Completer.complete(text, line, cursor_pos) - - def set_custom_completer(self, completer, pos=0): - """Adds a new custom completer function. - - The position argument (defaults to 0) is the index in the completers - list where you want the completer to be inserted.""" - - newcomp = new.instancemethod(completer,self.Completer, - self.Completer.__class__) - self.Completer.matchers.insert(pos,newcomp) - - def set_readline_completer(self): - """Reset readline's completer to be our own.""" - self.readline.set_completer(self.Completer.rlcomplete) - - def set_completer_frame(self, frame=None): - """Set the frame of the completer.""" - if frame: - self.Completer.namespace = frame.f_locals - self.Completer.global_namespace = frame.f_globals - else: - self.Completer.namespace = self.user_ns - self.Completer.global_namespace = self.user_global_ns - - #------------------------------------------------------------------------- # Things related to readline #------------------------------------------------------------------------- @@ -1610,16 +1543,6 @@ class InteractiveShell(Configurable, Magic): self.readline = readline sys.modules['readline'] = readline - from IPython.core.completer import IPCompleter - self.Completer = IPCompleter(self, - self.user_ns, - self.user_global_ns, - self.readline_omit__names, - self.alias_manager.alias_table) - sdisp = self.strdispatchers.get('complete_command', StrDispatch()) - self.strdispatchers['complete_command'] = sdisp - self.Completer.custom_completers = sdisp - # Platform-specific configuration if os.name == 'nt': # FIXME - check with Frederick to see if we can harmonize @@ -1646,8 +1569,6 @@ class InteractiveShell(Configurable, Magic): warn('Problems reading readline initialization file <%s>' % inputrc_name) - self.set_readline_completer() - # Configure readline according to user's prefs # This is only done if GNU readline is being used. If libedit # is being used (as on Leopard) the readline config is @@ -1662,6 +1583,7 @@ class InteractiveShell(Configurable, Magic): delims = readline.get_completer_delims().encode("ascii", "ignore") delims = delims.translate(string._idmap, self.readline_remove_delims) + delims = delims.replace(ESC_MAGIC, '') readline.set_completer_delims(delims) # otherwise we end up with a monster history after a while: readline.set_history_length(1000) @@ -1708,6 +1630,100 @@ class InteractiveShell(Configurable, Magic): return self.indent_current_nsp * ' ' #------------------------------------------------------------------------- + # Things related to text completion + #------------------------------------------------------------------------- + + def init_completer(self): + """Initialize the completion machinery. + + This creates completion machinery that can be used by client code, + either interactively in-process (typically triggered by the readline + library), programatically (such as in test suites) or out-of-prcess + (typically over the network by remote frontends). + """ + from IPython.core.completer import IPCompleter + self.Completer = IPCompleter(self, + self.user_ns, + self.user_global_ns, + self.readline_omit__names, + self.alias_manager.alias_table, + self.has_readline) + sdisp = self.strdispatchers.get('complete_command', StrDispatch()) + self.strdispatchers['complete_command'] = sdisp + self.Completer.custom_completers = sdisp + + if self.has_readline: + self.set_readline_completer() + + def complete(self, text, line=None, cursor_pos=None): + """Return the completed text and a list of completions. + + Parameters + ---------- + + text : string + A string of text to be completed on. It can be given as empty and + instead a line/position pair are given. In this case, the + completer itself will split the line like readline does. + + line : string, optional + The complete line that text is part of. + + cursor_pos : int, optional + The position of the cursor on the input line. + + Returns + ------- + text : string + The actual text that was completed. + + matches : list + A sorted list with all possible completions. + + The optional arguments allow the completion to take more context into + account, and are part of the low-level completion API. + + This is a wrapper around the completion mechanism, similar to what + readline does at the command line when the TAB key is hit. By + exposing it as a method, it can be used by other non-readline + environments (such as GUIs) for text completion. + + Simple usage example: + + In [1]: x = 'hello' + + In [2]: _ip.complete('x.l') + Out[2]: ('x.l', ['x.ljust', 'x.lower', 'x.lstrip']) + """ + + # Inject names into __builtin__ so we can complete on the added names. + with self.builtin_trap: + return self.Completer.complete(text, line, cursor_pos) + + def set_custom_completer(self, completer, pos=0): + """Adds a new custom completer function. + + The position argument (defaults to 0) is the index in the completers + list where you want the completer to be inserted.""" + + newcomp = new.instancemethod(completer,self.Completer, + self.Completer.__class__) + self.Completer.matchers.insert(pos,newcomp) + + def set_readline_completer(self): + """Reset readline's completer to be our own.""" + self.readline.set_completer(self.Completer.rlcomplete) + + def set_completer_frame(self, frame=None): + """Set the frame of the completer.""" + if frame: + self.Completer.namespace = frame.f_locals + self.Completer.global_namespace = frame.f_globals + else: + self.Completer.namespace = self.user_ns + self.Completer.global_namespace = self.user_global_ns + + #------------------------------------------------------------------------- # Things related to magics #-------------------------------------------------------------------------