forked from VimPlug/jedi
Get closer to fixing a lot of issues with the completion for repl.
This commit is contained in:
@@ -626,46 +626,6 @@ class Interpreter(Script):
|
|||||||
interpreter.add_namespaces_to_parser(self._evaluator, namespaces,
|
interpreter.add_namespaces_to_parser(self._evaluator, namespaces,
|
||||||
self._parser.module())
|
self._parser.module())
|
||||||
|
|
||||||
def _simple_complete(self, path, dot, like):
|
|
||||||
user_stmt = self._parser.user_stmt_with_whitespace()
|
|
||||||
is_simple_path = not path or re.search('^[\w][\w\d.]*$', path)
|
|
||||||
if isinstance(user_stmt, tree.Import) or not is_simple_path:
|
|
||||||
return super(Interpreter, self)._simple_complete(path, dot, like)
|
|
||||||
else:
|
|
||||||
# TODO Remove this branch? The above branch should be fast enough IMO.
|
|
||||||
class NamespaceModule(object):
|
|
||||||
def __getattr__(_, name):
|
|
||||||
for n in self.namespaces:
|
|
||||||
try:
|
|
||||||
return n[name]
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
raise AttributeError()
|
|
||||||
|
|
||||||
def __dir__(_):
|
|
||||||
gen = (n.keys() for n in self.namespaces)
|
|
||||||
return list(set(chain.from_iterable(gen)))
|
|
||||||
|
|
||||||
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 Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
completion_names = []
|
|
||||||
for namespace in namespaces:
|
|
||||||
for name in dir(namespace):
|
|
||||||
if name.lower().startswith(like.lower()):
|
|
||||||
scope = self._parser.module()
|
|
||||||
n = FakeName(name, scope)
|
|
||||||
completion_names.append(n)
|
|
||||||
return completion_names
|
|
||||||
|
|
||||||
|
|
||||||
def defined_names(source, path=None, encoding='utf-8'):
|
def defined_names(source, path=None, encoding='utf-8'):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ TODO Some parts of this module are still not well documented.
|
|||||||
"""
|
"""
|
||||||
import inspect
|
import inspect
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
from jedi._compatibility import builtins
|
from jedi._compatibility import builtins
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi.common import source_to_unicode
|
from jedi.common import source_to_unicode
|
||||||
from jedi.cache import underscore_memoization
|
from jedi.cache import underscore_memoization
|
||||||
from jedi.evaluate import compiled
|
from jedi.evaluate import compiled
|
||||||
from jedi.evaluate.compiled.fake import get_module
|
|
||||||
from jedi.parser import tree as pt
|
from jedi.parser import tree as pt
|
||||||
from jedi.parser import load_grammar
|
from jedi.parser import load_grammar
|
||||||
from jedi.parser.fast import FastParser
|
from jedi.parser.fast import FastParser
|
||||||
@@ -42,6 +42,9 @@ class LazyName(helpers.FakeName):
|
|||||||
def parent(self):
|
def parent(self):
|
||||||
"""
|
"""
|
||||||
Creating fake statements for the interpreter.
|
Creating fake statements for the interpreter.
|
||||||
|
|
||||||
|
Here we are trying to link back to Python code, if possible. This means
|
||||||
|
we try to find the python module for a name (not the builtin).
|
||||||
"""
|
"""
|
||||||
obj = self._value
|
obj = self._value
|
||||||
parser_path = []
|
parser_path = []
|
||||||
@@ -63,10 +66,10 @@ class LazyName(helpers.FakeName):
|
|||||||
# Unfortunately in some cases like `int` there's no __module__
|
# Unfortunately in some cases like `int` there's no __module__
|
||||||
module = builtins
|
module = builtins
|
||||||
else:
|
else:
|
||||||
# TODO this import is wrong. Yields x for x.y.z instead of z
|
# If we put anything into fromlist, it will just return the
|
||||||
module = __import__(module_name)
|
# latest name.
|
||||||
|
module = __import__(module_name, fromlist=[''])
|
||||||
parser_path = names
|
parser_path = names
|
||||||
raw_module = get_module(self._value)
|
|
||||||
|
|
||||||
found = []
|
found = []
|
||||||
try:
|
try:
|
||||||
@@ -74,17 +77,36 @@ class LazyName(helpers.FakeName):
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
# cut the `c` from `.pyc`
|
# Find the corresponding Python file for an interpreted one.
|
||||||
path = re.sub('c$', '', path)
|
path = re.sub(r'\.pyc$', '.py', path)
|
||||||
|
|
||||||
if path.endswith('.py'):
|
if path.endswith('.py'):
|
||||||
with open(path) as f:
|
with open(path) as f:
|
||||||
source = source_to_unicode(f.read())
|
source = source_to_unicode(f.read())
|
||||||
mod = FastParser(load_grammar(), source, path).module
|
mod = FastParser(load_grammar(), source, path).module
|
||||||
|
mod = self._evaluator.wrap(mod)
|
||||||
|
|
||||||
|
# We have to make sure that the modules that we import are also
|
||||||
|
# part of evaluator.modules.
|
||||||
|
for module_name, module in sys.modules.items():
|
||||||
|
try:
|
||||||
|
iterated_path = module.__file__
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
if iterated_path == path:
|
||||||
|
self._evaluator.modules[module_name] = mod
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise NotImplementedError('This should not happen, a module '
|
||||||
|
'should be part of sys.modules.')
|
||||||
|
|
||||||
if parser_path:
|
if parser_path:
|
||||||
assert len(parser_path) == 1
|
assert len(parser_path) == 1
|
||||||
found = self._evaluator.find_types(mod, parser_path[0], search_global=True)
|
found = list(self._evaluator.find_types(mod, parser_path[0],
|
||||||
|
search_global=True))
|
||||||
else:
|
else:
|
||||||
found = [self._evaluator.wrap(mod)]
|
found = [mod]
|
||||||
|
|
||||||
if not found:
|
if not found:
|
||||||
debug.warning('Interpreter lookup failed in global scope for %s',
|
debug.warning('Interpreter lookup failed in global scope for %s',
|
||||||
@@ -94,11 +116,14 @@ class LazyName(helpers.FakeName):
|
|||||||
evaluated = compiled.create(self._evaluator, obj)
|
evaluated = compiled.create(self._evaluator, obj)
|
||||||
found = [evaluated]
|
found = [evaluated]
|
||||||
|
|
||||||
|
if len(found) > 1 or True:
|
||||||
content = iterable.AlreadyEvaluated(found)
|
content = iterable.AlreadyEvaluated(found)
|
||||||
stmt = pt.ExprStmt([self, pt.Operator(pt.zero_position_modifier,
|
stmt = pt.ExprStmt([self, pt.Operator(pt.zero_position_modifier,
|
||||||
'=', (0, 0), ''), content])
|
'=', (0, 0), ''), content])
|
||||||
stmt.parent = self._module
|
stmt.parent = self._module
|
||||||
return stmt
|
return stmt
|
||||||
|
else:
|
||||||
|
return found[0]
|
||||||
|
|
||||||
@parent.setter
|
@parent.setter
|
||||||
def parent(self, value):
|
def parent(self, value):
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ class CompiledObject(Base):
|
|||||||
elif inspect.ismodule(cls):
|
elif inspect.ismodule(cls):
|
||||||
return 'module'
|
return 'module'
|
||||||
elif inspect.isbuiltin(cls) or inspect.ismethod(cls) \
|
elif inspect.isbuiltin(cls) or inspect.ismethod(cls) \
|
||||||
or inspect.ismethoddescriptor(cls):
|
or inspect.ismethoddescriptor(cls) or inspect.isfunction(cls):
|
||||||
return 'function'
|
return 'function'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ mixing in Python code, the autocompletion should work much better for builtins.
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import inspect
|
import inspect
|
||||||
|
import types
|
||||||
|
|
||||||
from jedi._compatibility import is_py3, builtins, unicode
|
from jedi._compatibility import is_py3, builtins, unicode
|
||||||
from jedi.parser import ParserWithRecovery, load_grammar
|
from jedi.parser import ParserWithRecovery, load_grammar
|
||||||
@@ -15,6 +16,28 @@ from jedi.evaluate.helpers import FakeName
|
|||||||
modules = {}
|
modules = {}
|
||||||
|
|
||||||
|
|
||||||
|
MethodDescriptorType = type(str.replace)
|
||||||
|
# These are not considered classes and access is granted even though they have
|
||||||
|
# a __class__ attribute.
|
||||||
|
NOT_CLASS_TYPES = (
|
||||||
|
types.BuiltinFunctionType,
|
||||||
|
types.CodeType,
|
||||||
|
types.DynamicClassAttribute,
|
||||||
|
types.FrameType,
|
||||||
|
types.FunctionType,
|
||||||
|
types.GeneratorType,
|
||||||
|
types.GetSetDescriptorType,
|
||||||
|
types.LambdaType,
|
||||||
|
types.MappingProxyType,
|
||||||
|
types.MemberDescriptorType,
|
||||||
|
types.MethodType,
|
||||||
|
types.ModuleType,
|
||||||
|
types.SimpleNamespace,
|
||||||
|
types.TracebackType,
|
||||||
|
MethodDescriptorType
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _load_faked_module(module):
|
def _load_faked_module(module):
|
||||||
module_name = module.__name__
|
module_name = module.__name__
|
||||||
if module_name == '__builtin__' and not is_py3:
|
if module_name == '__builtin__' and not is_py3:
|
||||||
@@ -134,7 +157,9 @@ def get_faked(module, obj, name=None):
|
|||||||
|
|
||||||
def is_class_instance(obj):
|
def is_class_instance(obj):
|
||||||
"""Like inspect.* methods."""
|
"""Like inspect.* methods."""
|
||||||
return not (inspect.isclass(obj) or inspect.ismodule(obj)
|
try:
|
||||||
or inspect.isbuiltin(obj) or inspect.ismethod(obj)
|
cls = obj.__class__
|
||||||
or inspect.ismethoddescriptor(obj) or inspect.iscode(obj)
|
except AttributeError:
|
||||||
or inspect.isgenerator(obj))
|
return False
|
||||||
|
else:
|
||||||
|
return cls != type and not issubclass(cls, NOT_CLASS_TYPES)
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ def setup_readline(namespace_module=__main__):
|
|||||||
try:
|
try:
|
||||||
import readline
|
import readline
|
||||||
except ImportError:
|
except ImportError:
|
||||||
print("Module readline not available.")
|
print("Jedi: Module readline not available.")
|
||||||
else:
|
else:
|
||||||
readline.set_completer(JediRL().complete)
|
readline.set_completer(JediRL().complete)
|
||||||
readline.parse_and_bind("tab: complete")
|
readline.parse_and_bind("tab: complete")
|
||||||
|
|||||||
@@ -7,6 +7,34 @@ import jedi
|
|||||||
from jedi._compatibility import is_py33
|
from jedi._compatibility import is_py33
|
||||||
|
|
||||||
|
|
||||||
|
def get_completion(source, namespace):
|
||||||
|
i = jedi.Interpreter(source, [namespace])
|
||||||
|
completions = i.completions()
|
||||||
|
assert len(completions) == 1
|
||||||
|
return completions[0]
|
||||||
|
|
||||||
|
|
||||||
|
def test_builtin_details():
|
||||||
|
import keyword
|
||||||
|
|
||||||
|
class EmptyClass:
|
||||||
|
pass
|
||||||
|
|
||||||
|
variable = EmptyClass()
|
||||||
|
|
||||||
|
def func():
|
||||||
|
pass
|
||||||
|
|
||||||
|
cls = get_completion('EmptyClass', locals())
|
||||||
|
var = get_completion('variable', locals())
|
||||||
|
f = get_completion('func', locals())
|
||||||
|
m = get_completion('keyword', locals())
|
||||||
|
assert cls.type == 'class'
|
||||||
|
assert var.type == 'instance'
|
||||||
|
assert f.type == 'function'
|
||||||
|
assert m.type == 'module'
|
||||||
|
|
||||||
|
|
||||||
class TestInterpreterAPI(TestCase):
|
class TestInterpreterAPI(TestCase):
|
||||||
def check_interpreter_complete(self, source, namespace, completions,
|
def check_interpreter_complete(self, source, namespace, completions,
|
||||||
**kwds):
|
**kwds):
|
||||||
|
|||||||
Reference in New Issue
Block a user