diff --git a/jedi/api/__init__.py b/jedi/api/__init__.py index 66c17043..73c2187b 100644 --- a/jedi/api/__init__.py +++ b/jedi/api/__init__.py @@ -141,19 +141,22 @@ class Script(object): i = imports.get_importer(self._evaluator, imp_names, module, level) c_names = i.completion_names(self._evaluator, only_modules) completions = [(name, module) for name in c_names] - if isinstance(user_stmt, pr.Import): - # TODO this paragraph is necessary, but not sure it works. - context = self._user_context.get_context() - next(context) # skip the path + + # TODO this paragraph is necessary, but not sure it works. + context = self._user_context.get_context() + + print(next(self._user_context.get_context()), 'x') + if not next(context).startswith('.'): # skip the path if next(context) == 'from': # completion is just "import" if before stands from .. completions += ((k, bs) for k in keywords.keyword_names('import')) + return completions + if isinstance(user_stmt, pr.Import): module = self._parser.module() - name = user_stmt.name_for_position(self._pos) - if name is not None: - imp = imports.ImportWrapper(self._evaluator, name) - completions += [(n, module) for n in imp.completion_names()] + completion_names = imports.completion_names(self._evaluator, + user_stmt, self._pos) + return completions + [(n, module) for n in completion_names] if names is None and not isinstance(user_stmt, pr.Import): if not path and not dot: diff --git a/jedi/api/helpers.py b/jedi/api/helpers.py index 2d0b41f6..f1f2c992 100644 --- a/jedi/api/helpers.py +++ b/jedi/api/helpers.py @@ -59,13 +59,15 @@ def importer_from_error_statement(error_statement, pos): return None, 0, False elif typ == 'import_from': for node in nodes: - if isinstance(node, pt.Node) and node.type == 'dotted_name': + if node.start_pos >= pos: + break + elif isinstance(node, pt.Node) and node.type == 'dotted_name': names += check_dotted(node.children) elif node in ('.', '...'): level += len(node.value) - elif isinstance(node, pt.Name) and node.start_pos <= pos: + elif isinstance(node, pt.Name): names.append(node) - elif node == 'import' and node.start_pos <= pos: + elif node == 'import': only_modules = False return names, level, only_modules diff --git a/jedi/evaluate/imports.py b/jedi/evaluate/imports.py index 401b2423..2ceb92fe 100644 --- a/jedi/evaluate/imports.py +++ b/jedi/evaluate/imports.py @@ -37,6 +37,33 @@ class ModuleNotFound(Exception): self.name_part = name_part +def completion_names(evaluator, imp, pos): + name = imp.name_for_position(pos) + module = imp.get_parent_until() + if name is None: + level = 0 + for node in imp.children: + if node.end_pos <= pos: + if node in ('.', '...'): + level += len(node.value) + print(node, level) + import_path = [] + else: + # Completion on an existing name. + + # The import path needs to be reduced by one, because we're completing. + import_path = imp.path_for_name(name)[:-1] + level = imp.level + + importer = get_importer(evaluator, tuple(import_path), module, level) + if isinstance(imp, pr.ImportFrom): + c = imp.children + only_modules = c[c.index('import')].start_pos >= pos + else: + only_modules = True + return importer.completion_names(evaluator, only_modules) + + class ImportWrapper(pr.Base): GlobalNamespace = 'TODO PLEASE DELETE ME' def __init__(self, evaluator, name): diff --git a/test/test_integration_import.py b/test/test_integration_import.py index 9ae6f57f..ca6f37b8 100644 --- a/test/test_integration_import.py +++ b/test/test_integration_import.py @@ -16,11 +16,22 @@ def test_goto_definition_on_import(): @cwd_at('jedi') def test_complete_on_empty_import(): + assert Script("from datetime import").completions()[0].name == 'import' # should just list the files in the directory assert 10 < len(Script("from .", path='').completions()) < 30 - assert 10 < len(Script("from . import", 1, 5, '').completions()) < 30 - assert 10 < len(Script("from . import classes", 1, 5, '').completions()) < 30 - assert len(Script("import").completions()) == 0 + + # Global import + assert len(Script("from . import", 1, 5, '').completions()) > 30 + # relative import + assert 10 < len(Script("from . import", 1, 6, '').completions()) < 30 + + # Global import + assert len(Script("from . import classes", 1, 5, '').completions()) > 30 + # relative import + assert 10 < len(Script("from . import classes", 1, 6, '').completions()) < 30 + + wanted = set(['ImportError', 'import', 'ImportWarning']) + assert set([c.name for c in Script("import").completions()]) == wanted if not is_py26: # python 2.6 doesn't always come with a library `import*`. assert len(Script("import import", path='').completions()) > 0