diff --git a/jedi/_compatibility.py b/jedi/_compatibility.py index 255660b5..dbe9bbcb 100644 --- a/jedi/_compatibility.py +++ b/jedi/_compatibility.py @@ -173,3 +173,8 @@ def u(string): elif not isinstance(string, unicode): return unicode(str(string), 'UTF-8') return string + +try: + import builtins # module name in python 3 +except ImportError: + import __builtin__ as builtins diff --git a/jedi/api.py b/jedi/api.py index f29fee87..040c4f4a 100644 --- a/jedi/api.py +++ b/jedi/api.py @@ -91,7 +91,7 @@ class Script(object): path = self._module.get_path_until_cursor() if re.search('^\.|\.\.$', path): return [] - path, dot, like = self._get_completion_parts(path) + path, dot, like = self._get_completion_parts() completion_line = self._module.get_line(self.pos[0])[:self.pos[1]] try: @@ -506,11 +506,12 @@ class Script(object): kill_count=kill_count, direct_resolve=True) return i, cur_name_part - def _get_completion_parts(self, path): + def _get_completion_parts(self): """ Returns the parts for the completion :return: tuple - (path, dot, like) """ + path = self._module.get_path_until_cursor() match = re.match(r'^(.*?)(\.|)(\w?[\w\d]*)$', path, flags=re.S) return match.groups() diff --git a/jedi/modules.py b/jedi/modules.py index 396d57b5..5cc45e7f 100644 --- a/jedi/modules.py +++ b/jedi/modules.py @@ -95,13 +95,13 @@ class ModuleWithCursor(Module): def __init__(self, path, source, position): super(ModuleWithCursor, self).__init__(path, source) self.position = position + self.source = source + self._path_until_cursor = None # this two are only used, because there is no nonlocal in Python 2 self._line_temp = None self._relevant_temp = None - self.source = source - @property def parser(self): """ get the parser lazy """ @@ -121,9 +121,10 @@ class ModuleWithCursor(Module): def get_path_until_cursor(self): """ Get the path under the cursor. """ - result = self._get_path_until_cursor() - self._start_cursor_pos = self._start_cursor_pos_temp - return result + if self._path_until_cursor is None: # small caching + self._path_until_cursor = self._get_path_until_cursor() + self._start_cursor_pos = self._start_cursor_pos_temp + return self._path_until_cursor def _get_path_until_cursor(self, start_pos=None): def fetch_line(): diff --git a/jedi/utils.py b/jedi/utils.py index cd8b0c48..af6b8eb3 100644 --- a/jedi/utils.py +++ b/jedi/utils.py @@ -4,7 +4,9 @@ Utilities for end-users. from __future__ import absolute_import import __main__ +import re +from jedi._compatibility import builtins from jedi import Interpreter @@ -61,20 +63,46 @@ def setup_readline(): a lot more sense, but probably due to backwards compatibility this is still the way how it works. - The only important part is the ``Interpreter`` call, everything - else hsa been copied from the ``rlcompleter`` std. library - module. + The only important part is stuff in the ``state == 0`` flow, + everything else has been copied from the ``rlcompleter`` std. + library module. """ if state == 0: namespace = __main__.__dict__ - completions = Interpreter(text, [namespace]).completions() - self.matches = [text + c.complete for c in completions] + interpreter = Interpreter(text, [namespace]) + + # The following part is a bit hackish, because it tries to + # directly access some jedi internals. The goal is to just + # use the "default" completion with ``getattr`` if + # possible. + path, dot, like = interpreter._get_completion_parts() + if re.match('^[\w][\w\d.]*$', path): + paths = path.split('.') if path else [] + namespaces = (namespace, builtins) + for p in paths: + old, namespaces = namespaces, [] + for n in old: + try: + namespaces.append(getattr(n, p)) + except AttributeError: + pass + + self.matches = [] + for n in namespaces: + for name in dir(n): + if name.lower().startswith(like.lower()): + self.matches.append(path + dot + name) + else: + completions = interpreter.completions() + self.matches = [text + c.complete for c in completions] try: return self.matches[state] except IndexError: return None readline.set_completer(JediRL().complete) + j = JediRL() + j.complete('r', 0) readline.parse_and_bind("tab: complete") # No delimiters, Jedi handles that.