From c12957e9806cba263d647d35ec9997eda0cac694 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 18 May 2011 13:41:43 +0200 Subject: [PATCH] complete imported submodules of a module (PY-3227) --- .../python/psi/types/PyModuleType.java | 64 +++++++++++++++---- .../completion/submodules/__init__.py | 0 .../completion/submodules/bar/__init__.py | 0 .../testData/completion/submodules/bar/baz.py | 0 .../completion/submodules/foo.after.py | 3 + python/testData/completion/submodules/foo.py | 3 + .../python/PythonCompletionTest.java | 7 ++ 7 files changed, 63 insertions(+), 14 deletions(-) create mode 100644 python/testData/completion/submodules/__init__.py create mode 100644 python/testData/completion/submodules/bar/__init__.py create mode 100644 python/testData/completion/submodules/bar/baz.py create mode 100644 python/testData/completion/submodules/foo.after.py create mode 100644 python/testData/completion/submodules/foo.py diff --git a/python/src/com/jetbrains/python/psi/types/PyModuleType.java b/python/src/com/jetbrains/python/psi/types/PyModuleType.java index 4250c36fd912..74051d0e3120 100644 --- a/python/src/com/jetbrains/python/psi/types/PyModuleType.java +++ b/python/src/com/jetbrains/python/psi/types/PyModuleType.java @@ -8,6 +8,7 @@ import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ProcessingContext; import com.intellij.util.SmartList; import com.jetbrains.python.PyNames; @@ -131,36 +132,71 @@ public class PyModuleType implements PyType { // Modules don't descend from obje result.addAll(processor.getResultList()); } } - if (point == ResolveImportUtil.PointInImport.AS_MODULE || point == ResolveImportUtil.PointInImport.AS_NAME) { // when imported from somehow, add submodules - if (PyNames.INIT_DOT_PY.equals(myModule.getName())) { // our module is a dir, not a single file + if (PyNames.INIT_DOT_PY.equals(myModule.getName())) { // our module is a dir, not a single file + if (point == ResolveImportUtil.PointInImport.AS_MODULE || point == ResolveImportUtil.PointInImport.AS_NAME) { // when imported from somehow, add submodules result.addAll(getSubmoduleVariants(myModule.getContainingDirectory(), location, names_already)); } + else { + addImportedSubmodules(location, names_already, result); + } } return result.toArray(); } + private void addImportedSubmodules(PyExpression location, Set names_already, List result) { + PsiFile file = location.getContainingFile(); + if (file instanceof PyFile) { + PyFile pyFile = (PyFile)file; + PsiElement moduleBase = myModule.getName().equals(PyNames.INIT_DOT_PY) ? myModule.getContainingDirectory() : myModule; + for (PyImportElement importElement : pyFile.getImportTargets()) { + PsiElement target = ResolveImportUtil.resolveImportElement(importElement); + if (target != null && PsiTreeUtil.isAncestor(moduleBase, target, true)) { + LookupElement element = null; + if (target instanceof PsiFileSystemItem) { + element = buildFileLookupElement(location, names_already, (PsiFileSystemItem) target); + } + if (element == null && target instanceof PsiNamedElement) { + element = LookupElementBuilder.create((PsiNamedElement)target).setIcon(target.getIcon(0)); + } + if (element != null) { + result.add(element); + } + } + } + } + } + public static List getSubmoduleVariants(final PsiDirectory directory, PsiElement location, Set names_already) { List result = new ArrayList(); for (PsiFileSystemItem pfsi : getSubmodulesList(directory)) { - if (pfsi == location.getContainingFile().getOriginalFile()) continue; - String s = pfsi.getName(); - int pos = s.lastIndexOf('.'); // it may not contain a dot, except in extension; cut it off. - if (pos > 0) s = s.substring(0, pos); - if (!PyNames.isIdentifier(s)) continue; - if (names_already != null) { - if (names_already.contains(s)) continue; - else names_already.add(s); + LookupElement lookupElement = buildFileLookupElement(location, names_already, pfsi); + if (lookupElement != null) { + result.add(lookupElement); } - result.add(LookupElementBuilder.create(pfsi, s) - .setTypeText(getPresentablePath(directory)) - .setPresentableText(s) - .setIcon(pfsi.getIcon(0))); } return result; } + private static LookupElement buildFileLookupElement(PsiElement location, + Set names_already, + PsiFileSystemItem pfsi) { + if (pfsi == location.getContainingFile().getOriginalFile()) return null; + String s = pfsi.getName(); + int pos = s.lastIndexOf('.'); // it may not contain a dot, except in extension; cut it off. + if (pos > 0) s = s.substring(0, pos); + if (!PyNames.isIdentifier(s)) return null; + if (names_already != null) { + if (names_already.contains(s)) return null; + else names_already.add(s); + } + return LookupElementBuilder.create(pfsi, s) + .setTypeText(getPresentablePath((PsiDirectory)pfsi.getParent())) + .setPresentableText(s) + .setIcon(pfsi.getIcon(0)); + } + private static String getPresentablePath(PsiDirectory directory) { if (directory == null) { return ""; diff --git a/python/testData/completion/submodules/__init__.py b/python/testData/completion/submodules/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/testData/completion/submodules/bar/__init__.py b/python/testData/completion/submodules/bar/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/testData/completion/submodules/bar/baz.py b/python/testData/completion/submodules/bar/baz.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/testData/completion/submodules/foo.after.py b/python/testData/completion/submodules/foo.after.py new file mode 100644 index 000000000000..3d2165708691 --- /dev/null +++ b/python/testData/completion/submodules/foo.after.py @@ -0,0 +1,3 @@ +import bar +import bar.baz +bar.baz \ No newline at end of file diff --git a/python/testData/completion/submodules/foo.py b/python/testData/completion/submodules/foo.py new file mode 100644 index 000000000000..16725dd2fc03 --- /dev/null +++ b/python/testData/completion/submodules/foo.py @@ -0,0 +1,3 @@ +import bar +import bar.baz +bar.b \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java index afe6abd1459b..25ffb7d9c1ae 100644 --- a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java +++ b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java @@ -359,4 +359,11 @@ public class PythonCompletionTest extends PyLightFixtureTestCase { myFixture.completeBasic(); myFixture.checkResultByFile("completion/pep328/package/subpackage1/moduleX.after.py"); } + + public void testImportedSubmoduleCompletion() { // PY-3227 + myFixture.copyDirectoryToProject("completion/submodules", "submodules"); + myFixture.configureByFile("submodules/foo.py"); + myFixture.completeBasic(); + myFixture.checkResultByFile("completion/submodules/foo.after.py"); + } }