1
0
forked from VimPlug/jedi

Fix a lot of the import completion issues.

This commit is contained in:
Dave Halter
2016-11-20 16:37:02 +01:00
parent e79ebe3ee7
commit cbd6713b5e
3 changed files with 52 additions and 38 deletions

View File

@@ -222,7 +222,7 @@ class Completion:
return level, names return level, names
def _get_importer_names(self, names, level=0, only_modules=True): def _get_importer_names(self, names, level=0, only_modules=True):
names = [str(n) for n in names] names = [n.value for n in names]
i = imports.Importer(self._evaluator, names, self._module_context, level) i = imports.Importer(self._evaluator, names, self._module_context, level)
return i.completion_names(self._evaluator, only_modules=only_modules) return i.completion_names(self._evaluator, only_modules=only_modules)

View File

@@ -28,6 +28,7 @@ from jedi import settings
from jedi.common import source_to_unicode, unite from jedi.common import source_to_unicode, unite
from jedi.evaluate import compiled from jedi.evaluate import compiled
from jedi.evaluate import analysis from jedi.evaluate import analysis
from jedi.evaluate.filters import AbstractNameDefinition
def completion_names(evaluator, imp, pos): def completion_names(evaluator, imp, pos):
@@ -163,6 +164,36 @@ def get_init_path(directory_path):
return None return None
class ImportName(AbstractNameDefinition):
api_type = 'module'
start_pos = (1, 0)
def __init__(self, parent_module, string_name):
self.parent_context = parent_module
self.string_name = string_name
def infer(self):
return Importer(
self.parent_context.evaluator,
[self.string_name],
self.parent_context,
).follow()
def get_root_context(self):
# Not sure if this is correct.
return self.parent_context.get_root_context()
class SubModuleName(ImportName):
def infer(self):
return Importer(
self.parent_context.evaluator,
[self.string_name],
self.parent_context,
level=1
).follow()
class Importer(object): class Importer(object):
def __init__(self, evaluator, import_path, module_context, level=0): def __init__(self, evaluator, import_path, module_context, level=0):
""" """
@@ -350,14 +381,16 @@ class Importer(object):
self._evaluator.modules[module_name] = module self._evaluator.modules[module_name] = module
return set([module]) return set([module])
def _generate_name(self, name): def _generate_name(self, name, in_module=None):
# Create a pseudo import to be able to follow them. # Create a pseudo import to be able to follow them.
name = helpers.FakeName(name) if in_module is None:
imp = helpers.FakeImport(name, parent=self.module_context) #imp = helpers.FakeImport(name, parent=self.module_context)
name.parent = imp #name.parent = imp
return name #return name
return ImportName(self.module_context, name)
return SubModuleName(in_module, name)
def _get_module_names(self, search_path=None): def _get_module_names(self, search_path=None, in_module=None):
""" """
Get the names of all modules in the search_path. This means file names Get the names of all modules in the search_path. This means file names
and not names defined in the files. and not names defined in the files.
@@ -365,13 +398,13 @@ class Importer(object):
names = [] names = []
# add builtin module names # add builtin module names
if search_path is None: if search_path is None and in_module is None:
names += [self._generate_name(name) for name in sys.builtin_module_names] names += [self._generate_name(name) for name in sys.builtin_module_names]
if search_path is None: if search_path is None:
search_path = self.sys_path_with_modifications() search_path = self.sys_path_with_modifications()
for module_loader, name, is_pkg in pkgutil.iter_modules(search_path): for module_loader, name, is_pkg in pkgutil.iter_modules(search_path):
names.append(self._generate_name(name)) names.append(self._generate_name(name, in_module=in_module))
return names return names
def completion_names(self, evaluator, only_modules=False): def completion_names(self, evaluator, only_modules=False):
@@ -380,6 +413,7 @@ class Importer(object):
definition that is not defined in a module. definition that is not defined in a module.
""" """
from jedi.evaluate import finder from jedi.evaluate import finder
from jedi.evaluate.representation import ModuleContext
names = [] names = []
if self.import_path: if self.import_path:
# flask # flask
@@ -396,15 +430,16 @@ class Importer(object):
if os.path.isdir(flaskext): if os.path.isdir(flaskext):
names += self._get_module_names([flaskext]) names += self._get_module_names([flaskext])
for scope in self.follow(): for context in self.follow():
# Non-modules are not completable. # Non-modules are not completable.
if not scope.type == 'file_input': # not a module if context.api_type != 'module': # not a module
continue continue
# namespace packages # namespace packages
if isinstance(scope, tree.Module) and scope.path.endswith('__init__.py'): if isinstance(context, ModuleContext) and \
paths = scope.py__path__() context.py__file__().endswith('__init__.py'):
names += self._get_module_names(paths) paths = context.py__path__()
names += self._get_module_names(paths, in_module=context)
if only_modules: if only_modules:
# In the case of an import like `from x.` we don't need to # In the case of an import like `from x.` we don't need to
@@ -416,12 +451,8 @@ class Importer(object):
continue continue
for names_dict in scope.names_dicts(search_global=False): for filter in context.get_filters(search_global=False):
_names = list(chain.from_iterable(names_dict.values())) names += filter.values()
if not _names:
continue
_names = finder.filter_definition_names(_names, scope)
names += _names
else: else:
# Empty import path=completion after import # Empty import path=completion after import
if not self.level: if not self.level:

View File

@@ -773,23 +773,6 @@ class ModuleAttributeName(AbstractNameDefinition):
) )
class SubModuleName(AbstractNameDefinition):
api_type = 'module'
start_pos = (1, 0)
def __init__(self, parent_module, string_name):
self.parent_context = parent_module
self.string_name = string_name
def infer(self):
return imports.Importer(
self.parent_context.evaluator,
[self.string_name],
self.parent_context,
level=1
).follow()
class ModuleContext(use_metaclass(CachedMetaClass, context.TreeContext, Wrapper)): class ModuleContext(use_metaclass(CachedMetaClass, context.TreeContext, Wrapper)):
api_type = 'module' api_type = 'module'
parent_context = None parent_context = None
@@ -941,7 +924,7 @@ class ModuleContext(use_metaclass(CachedMetaClass, context.TreeContext, Wrapper)
mods = pkgutil.iter_modules([os.path.dirname(path)]) mods = pkgutil.iter_modules([os.path.dirname(path)])
for module_loader, name, is_pkg in mods: for module_loader, name, is_pkg in mods:
# It's obviously a relative import to the current module. # It's obviously a relative import to the current module.
names[name] = SubModuleName(self, name) names[name] = imports.SubModuleName(self, name)
# TODO add something like this in the future, its cleaner than the # TODO add something like this in the future, its cleaner than the
# import hacks. # import hacks.