mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-06 22:14:27 +08:00
move completion to interpreter, which improves shell completion
This commit is contained in:
46
jedi/api.py
46
jedi/api.py
@@ -11,6 +11,7 @@ from __future__ import with_statement
|
|||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
import warnings
|
import warnings
|
||||||
|
from itertools import chain
|
||||||
|
|
||||||
from jedi import parsing
|
from jedi import parsing
|
||||||
from jedi import parsing_representation as pr
|
from jedi import parsing_representation as pr
|
||||||
@@ -21,7 +22,7 @@ from jedi import common
|
|||||||
from jedi import cache
|
from jedi import cache
|
||||||
from jedi import modules
|
from jedi import modules
|
||||||
from jedi import interpret
|
from jedi import interpret
|
||||||
from jedi._compatibility import next, unicode
|
from jedi._compatibility import next, unicode, builtins
|
||||||
import keywords
|
import keywords
|
||||||
import evaluate
|
import evaluate
|
||||||
import api_classes
|
import api_classes
|
||||||
@@ -586,12 +587,55 @@ class Interpreter(Script):
|
|||||||
`source`.
|
`source`.
|
||||||
"""
|
"""
|
||||||
super(Interpreter, self).__init__(source, **kwds)
|
super(Interpreter, self).__init__(source, **kwds)
|
||||||
|
self.namespaces = namespaces
|
||||||
|
|
||||||
# Here we add the namespaces to the current parser.
|
# Here we add the namespaces to the current parser.
|
||||||
importer = interpret.ObjectImporter(self._parser.user_scope)
|
importer = interpret.ObjectImporter(self._parser.user_scope)
|
||||||
for ns in namespaces:
|
for ns in namespaces:
|
||||||
importer.import_raw_namespace(ns)
|
importer.import_raw_namespace(ns)
|
||||||
|
|
||||||
|
def _simple_complete(self, path, like):
|
||||||
|
user_stmt = self._user_stmt(True)
|
||||||
|
is_simple_path = not path or re.search('^[\w][\w\d.]*$', path)
|
||||||
|
if isinstance(user_stmt, pr.Import) or not is_simple_path:
|
||||||
|
return super(type(self), self)._simple_complete(path, like)
|
||||||
|
else:
|
||||||
|
class NamespaceModule:
|
||||||
|
def __getattr__(_, name):
|
||||||
|
for n in self.namespaces:
|
||||||
|
try:
|
||||||
|
return n[name]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
raise AttributeError()
|
||||||
|
|
||||||
|
def __dir__(_):
|
||||||
|
return list(set(chain.from_iterable(n.keys()
|
||||||
|
for n in self.namespaces)))
|
||||||
|
|
||||||
|
paths = path.split('.') if path else []
|
||||||
|
|
||||||
|
namespaces = (NamespaceModule(), builtins)
|
||||||
|
for p in paths:
|
||||||
|
old, namespaces = namespaces, []
|
||||||
|
for n in old:
|
||||||
|
try:
|
||||||
|
namespaces.append(getattr(n, p))
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
completions = []
|
||||||
|
for n in namespaces:
|
||||||
|
for name in dir(n):
|
||||||
|
if name.lower().startswith(like.lower()):
|
||||||
|
scope = self._parser.module
|
||||||
|
n = pr.Name(self._parser.module, [(name, (0, 0))],
|
||||||
|
(0, 0), (0, 0), scope)
|
||||||
|
completions.append((n, scope))
|
||||||
|
return completions
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def defined_names(source, path=None, source_encoding='utf-8'):
|
def defined_names(source, path=None, source_encoding='utf-8'):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -4,9 +4,7 @@ Utilities for end-users.
|
|||||||
|
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
import __main__
|
import __main__
|
||||||
import re
|
|
||||||
|
|
||||||
from jedi._compatibility import builtins
|
|
||||||
from jedi import Interpreter
|
from jedi import Interpreter
|
||||||
|
|
||||||
|
|
||||||
@@ -65,36 +63,11 @@ def setup_readline(namespace_module=__main__):
|
|||||||
if state == 0:
|
if state == 0:
|
||||||
interpreter = Interpreter(text, [namespace_module.__dict__])
|
interpreter = Interpreter(text, [namespace_module.__dict__])
|
||||||
|
|
||||||
# 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()
|
path, dot, like = interpreter._get_completion_parts()
|
||||||
before = text[:len(text) - len(like)]
|
before = text[:len(text) - len(like)]
|
||||||
# Shouldn't be an import statement and just a simple path
|
completions = interpreter.completions()
|
||||||
# with dots.
|
|
||||||
is_import = re.match('^\s*(import|from) ', text)
|
|
||||||
if not is_import and (not path or re.match('^[\w][\w\d.]*$', path)):
|
|
||||||
paths = path.split('.') if path else []
|
|
||||||
namespaces = (namespace_module, builtins)
|
|
||||||
for p in paths:
|
|
||||||
old, namespaces = namespaces, []
|
|
||||||
for n in old:
|
|
||||||
try:
|
|
||||||
namespaces.append(getattr(n, p))
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
self.matches = []
|
self.matches = [before + c.name_with_symbols for c in completions]
|
||||||
for n in namespaces:
|
|
||||||
for name in dir(n):
|
|
||||||
if name.lower().startswith(like.lower()):
|
|
||||||
self.matches.append(before + name)
|
|
||||||
else:
|
|
||||||
completions = interpreter.completions()
|
|
||||||
|
|
||||||
self.matches = [before + c.name_with_symbols
|
|
||||||
for c in completions]
|
|
||||||
try:
|
try:
|
||||||
return self.matches[state]
|
return self.matches[state]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
|
|||||||
@@ -60,6 +60,13 @@ class TestSetupReadline(TestCase):
|
|||||||
assert set(self.completions(s)) == set([s + 'ltsep', s + 'bspath'])
|
assert set(self.completions(s)) == set([s + 'ltsep', s + 'bspath'])
|
||||||
assert self.completions('import keyword') == ['import keyword']
|
assert self.completions('import keyword') == ['import keyword']
|
||||||
|
|
||||||
|
import os
|
||||||
|
s = 'from os import '
|
||||||
|
goal = set([s + el for el in dir(os)])
|
||||||
|
# There are minor differences, e.g. the dir doesn't include deleted
|
||||||
|
# items as well as items that are not only available on linux.
|
||||||
|
assert len(set(self.completions(s)).symmetric_difference(goal)) < 20
|
||||||
|
|
||||||
def test_preexisting_values(self):
|
def test_preexisting_values(self):
|
||||||
self.namespace.a = range(10)
|
self.namespace.a = range(10)
|
||||||
assert set(self.completions('a.')) == set(['a.' + n for n in dir(range(1))])
|
assert set(self.completions('a.')) == set(['a.' + n for n in dir(range(1))])
|
||||||
|
|||||||
Reference in New Issue
Block a user