From 91b9bdf25f1e97a0c583a49dba4bd439fac89507 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Tue, 13 Dec 2011 13:38:26 +0400 Subject: [PATCH 01/35] Fixed NPE in PySkeletonRefresher (EA-32558) --- python/src/com/jetbrains/python/sdk/PySkeletonRefresher.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/src/com/jetbrains/python/sdk/PySkeletonRefresher.java b/python/src/com/jetbrains/python/sdk/PySkeletonRefresher.java index f53a5536f471..c55013a32ddb 100644 --- a/python/src/com/jetbrains/python/sdk/PySkeletonRefresher.java +++ b/python/src/com/jetbrains/python/sdk/PySkeletonRefresher.java @@ -103,7 +103,7 @@ public class PySkeletonRefresher { } } - private static String getExtraSysPath(Sdk sdk, String skeletonsPath) { + private static String getExtraSysPath(@NotNull Sdk sdk, @Nullable String skeletonsPath) { final VirtualFile[] classDirs = sdk.getRootProvider().getFiles(OrderRootType.CLASSES); final StringBuilder builder = new StringBuilder("\""); int i = 0; @@ -113,7 +113,7 @@ public class PySkeletonRefresher { } if (classDirs[i].isInLocalFileSystem()) { final String pathname = classDirs[i].getPath(); - if (!skeletonsPath.equals(pathname)) { + if (pathname != null && !pathname.equals(skeletonsPath)) { builder.append(pathname); } } From 45403796673c7a70ddb3fe1bc93289a5d0d956cb Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 13 Dec 2011 12:09:16 +0100 Subject: [PATCH 02/35] EA-32548 - PIEAE: PsiElementBase.getContainingFile --- .../jetbrains/python/structureView/PyStructureViewElement.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java b/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java index e383389310fa..5f7aa526e43f 100644 --- a/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java +++ b/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java @@ -139,6 +139,9 @@ public class PyStructureViewElement implements StructureViewTreeElement { private Collection getElementChildren(final PyElement element) { final Collection children = new ArrayList(); + if (!element.isValid()) { + return children; + } element.acceptChildren(new PyElementVisitor() { @Override public void visitElement(PsiElement e) { From 1cbf057a81d799025a4b547640855113927d3c8d Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 13 Dec 2011 12:13:19 +0100 Subject: [PATCH 03/35] EA-32562 - NPE: CreateClassQuickFix.applyFix --- .../src/com/jetbrains/python/actions/CreateClassQuickFix.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python/src/com/jetbrains/python/actions/CreateClassQuickFix.java b/python/src/com/jetbrains/python/actions/CreateClassQuickFix.java index 5542c5e9245e..0d81dd177bb2 100644 --- a/python/src/com/jetbrains/python/actions/CreateClassQuickFix.java +++ b/python/src/com/jetbrains/python/actions/CreateClassQuickFix.java @@ -41,6 +41,9 @@ public class CreateClassQuickFix implements LocalQuickFix { public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { PsiElement anchor = myAnchor; + if (!anchor.isValid()) { + return; + } if (!(anchor instanceof PyFile)) { while(!(anchor.getParent() instanceof PyFile)) { anchor = anchor.getParent(); From cb37e2ae7fa38c3acba7ca45dffc50a124989643 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 13 Dec 2011 12:19:23 +0100 Subject: [PATCH 04/35] EA-32578 - NPE: SkeletonErrorsDialog.getHTMLColor --- .../jetbrains/python/sdk/SkeletonErrorsDialog.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/python/src/com/jetbrains/python/sdk/SkeletonErrorsDialog.java b/python/src/com/jetbrains/python/sdk/SkeletonErrorsDialog.java index 7880cea65399..21ef573b85d2 100644 --- a/python/src/com/jetbrains/python/sdk/SkeletonErrorsDialog.java +++ b/python/src/com/jetbrains/python/sdk/SkeletonErrorsDialog.java @@ -1,6 +1,5 @@ package com.jetbrains.python.sdk; -import com.intellij.openapi.util.Disposer; import com.intellij.ui.components.JBScrollPane; import com.jetbrains.python.PyBundle; @@ -12,8 +11,6 @@ import java.awt.event.ActionListener; import java.util.List; import java.util.Map; -import static java.lang.Math.*; - public class SkeletonErrorsDialog extends JDialog { private JPanel contentPane; private JButton buttonOK; @@ -35,8 +32,13 @@ public class SkeletonErrorsDialog extends JDialog { myMessagePane.setContentType("text/html"); myMessagePane.setBorder(new EmptyBorder(0, 0, 0, 0)); StringBuilder sb = new StringBuilder(""); + final Color foreground = getParent().getForeground(); + final Color background = getParent().getBackground(); + if (foreground != null && background != null) { + sb.append("text='").append(getHTMLColor(foreground)).append("' "); + sb.append("bgcolor='").append(getHTMLColor(background)).append("'"); + } + sb.append(">"); if (failed_sdks.size() > 0) { sb.append("

").append(PyBundle.message("sdk.error.dialog.failed.sdks")).append("

"); From 86811f08fed02e57ad1d7ca3e88e4ff36903b5f0 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Tue, 13 Dec 2011 16:46:26 +0400 Subject: [PATCH 05/35] Cleanup --- .../python/documentation/PythonDocumentationProvider.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java b/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java index f962b341405f..5d2fc7188a39 100644 --- a/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java +++ b/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java @@ -81,8 +81,6 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i /** * Creates a HTML description of function definition. * @param fun the function - * @param deco_name_wrapper puts a tag around decorator name - * @param deco_separator is added between decorators * @param func_name_wrapper puts a tag around the function name * @param escaper sanitizes values that come directly from doc string or code * @return chain of strings for further chaining @@ -117,7 +115,7 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i } } if (originalElement instanceof PyExpression && originalElement.isValid()) { - result.append("\n").append(describeExpressionType((PyExpression)originalElement)); + result.append("\n").append(describeExpressionType((PyExpression)originalElement)); } return result.toString(); } From 5f1e612fd10f403017b0da2e77a6de1a14fcd960 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Tue, 13 Dec 2011 16:48:43 +0400 Subject: [PATCH 06/35] Fixed SOE in PythonDocumentationProvider (EA-32588) --- .../PythonDocumentationProvider.java | 19 +++++++--- .../python/psi/types/PyUnionType.java | 37 +++++++++---------- .../jetbrains/python/PyTypeParserTest.java | 11 ++++-- 3 files changed, 39 insertions(+), 28 deletions(-) diff --git a/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java b/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java index 5d2fc7188a39..ea4b3937601e 100644 --- a/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java +++ b/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java @@ -151,14 +151,21 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i } public static String getTypeName(@Nullable PyType type, @NotNull final TypeEvalContext context) { - Set visited = new HashSet(); + return getTypeName(type, context, new HashSet()); + } + + private static String getTypeName(@Nullable PyType type, @NotNull final TypeEvalContext context, @NotNull final Set visited) { + if (visited.contains(type)) { + return UNKNOWN; + } + visited.add(type); if (type == null) { return UNKNOWN; } if (type instanceof PyTypeReference) { final PyType resolved = ((PyTypeReference)type).resolve(null, context); if (resolved != null) { - return getTypeName(resolved, context); + return getTypeName(resolved, context, visited); } return UNKNOWN; } @@ -166,19 +173,19 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i if (type instanceof PyCollectionType) { final PyType elementType = ((PyCollectionType)type).getElementType(context); if (elementType != null) { - return String.format("%s of %s", name, getTypeName(elementType, context)); + return String.format("%s of %s", name, getTypeName(elementType, context, visited)); } } if (type instanceof PyUnionType) { - return String.format("one of (%s)", StringUtil.join(((PyUnionType)type).getResolvedMembers(context), + return String.format("one of (%s)", StringUtil.join(((PyUnionType)type).getMembers(), new Function() { @Override public String fun(PyType t) { - return getTypeName(t, context); + return getTypeName(t, context, visited); } }, ", ")); } - return name; + return name != null ? name : UNKNOWN; } static ChainIterable describeDecorators( diff --git a/python/src/com/jetbrains/python/psi/types/PyUnionType.java b/python/src/com/jetbrains/python/psi/types/PyUnionType.java index 783470bce64a..7a37b1b4cd75 100644 --- a/python/src/com/jetbrains/python/psi/types/PyUnionType.java +++ b/python/src/com/jetbrains/python/psi/types/PyUnionType.java @@ -16,10 +16,10 @@ import java.util.*; * @author yole */ public class PyUnionType implements PyType { - private final List myMembers; + private final Set myMembers; private PyUnionType(Collection members) { - myMembers = new ArrayList(members); + myMembers = new LinkedHashSet(members); } @Nullable @@ -132,26 +132,11 @@ public class PyUnionType implements PyType { return false; } - public List getMembers() { + public Collection getMembers() { return myMembers; } - - public List getResolvedMembers(TypeEvalContext context) { - List result = new ArrayList(); - for (PyType member : myMembers) { - if (member instanceof PyTypeReference) { - final PyType resolved = ((PyTypeReference)member).resolve(null, context); - if (resolved != null && !resolved.equals(this)) { - result.add(resolved); - } - } - else { - result.add(member); - } - } - return result; - } + @Nullable public PyType exclude(PyType t, TypeEvalContext context) { final List members = new ArrayList(); for (PyType m : getMembers()) { @@ -172,4 +157,18 @@ public class PyUnionType implements PyType { return new PyUnionType(Collections.singletonList(type)); } } + + @Override + public boolean equals(Object other) { + if (other instanceof PyUnionType) { + final PyUnionType otherType = (PyUnionType)other; + return myMembers.equals(otherType.myMembers); + } + return false; + } + + @Override + public int hashCode() { + return myMembers.hashCode(); + } } diff --git a/python/testSrc/com/jetbrains/python/PyTypeParserTest.java b/python/testSrc/com/jetbrains/python/PyTypeParserTest.java index fac35cef9905..f966b0da1c8b 100644 --- a/python/testSrc/com/jetbrains/python/PyTypeParserTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypeParserTest.java @@ -3,7 +3,10 @@ package com.jetbrains.python; import com.jetbrains.python.fixtures.PyTestCase; import com.jetbrains.python.psi.types.*; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; +import java.util.Set; /** * @author yole @@ -44,10 +47,12 @@ public class PyTypeParserTest extends PyTestCase { public void testUnionType() { myFixture.configureByFile("typeParser/typeParser.py"); final PyUnionType type = (PyUnionType)PyTypeParser.getTypeByName(myFixture.getFile(), "MyObject or str"); - final List members = type.getMembers(); + assertNotNull(type); + final Collection members = type.getMembers(); assertEquals(2, members.size()); - assertClassType(members.get(0), "MyObject"); - assertClassType(members.get(1), "str"); + final List list = new ArrayList(members); + assertClassType(list.get(0), "MyObject"); + assertClassType(list.get(1), "str"); } public void testTypeSubparts() { From 99169466d2246b91299aa855524ab13a716f4c84 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Tue, 13 Dec 2011 19:59:34 +0400 Subject: [PATCH 07/35] Fixed test of call-by-class Python inspection --- .../testData/inspections/PyCallByClassInspection/test.py | 9 ++++----- .../com/jetbrains/python/PythonInspectionsTest.java | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/python/testData/inspections/PyCallByClassInspection/test.py b/python/testData/inspections/PyCallByClassInspection/test.py index cf72257f7603..e346a34686cf 100644 --- a/python/testData/inspections/PyCallByClassInspection/test.py +++ b/python/testData/inspections/PyCallByClassInspection/test.py @@ -4,9 +4,9 @@ class Z(object): class A(object): def method(self, *args, **kwargs): - Z.method(self) # passing wrong instance - Z.method(Z) # passing class instead of instance - Z.method(A) # passing class instead of instance AND wrong class + Z.method(self) # passing wrong instance + Z.method(Z) # passing class instead of instance + Z.method(A) # passing class instead of instance AND wrong class Z.method(Z()) #pass def __init__(self): @@ -16,5 +16,4 @@ class B(A): def __init__(self): A.__init__(self) # pass -A.method(B()) # pass - +A.method(B()) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java b/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java index c570c89a73a5..8e7815764422 100644 --- a/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java +++ b/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java @@ -219,7 +219,7 @@ public class PythonInspectionsTest extends PyTestCase { private void doHighlightingTest(final Class inspectionClass) { myFixture.configureByFile("inspections/" + getTestName(false) + "/test.py"); myFixture.enableInspections(inspectionClass); - myFixture.checkHighlighting(true, false, false); + myFixture.checkHighlighting(true, false, true); } private void doHighlightingTest(final Class inspectionClass, final LanguageLevel languageLevel) { @@ -248,7 +248,7 @@ public class PythonInspectionsTest extends PyTestCase { public void testInconsistentIndentation() { doHighlightingTest(PyInconsistentIndentationInspection.class, LanguageLevel.PYTHON26); } - + public void testPyChainedComparisonsInspection() { doHighlightingTest(PyChainedComparisonsInspection.class); } From 70eea82a8934b5e121dd785252acbbfe860886de Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Tue, 13 Dec 2011 20:00:19 +0400 Subject: [PATCH 08/35] Changed priority of some inspections from warning to weak warning --- python/src/META-INF/python-plugin-common.xml | 26 ++++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/python/src/META-INF/python-plugin-common.xml b/python/src/META-INF/python-plugin-common.xml index 876e5c9e1dac..88670bff3802 100644 --- a/python/src/META-INF/python-plugin-common.xml +++ b/python/src/META-INF/python-plugin-common.xml @@ -231,8 +231,8 @@ - - + + @@ -240,12 +240,12 @@ - + - + - + @@ -256,17 +256,17 @@ - - - - + + + + - + - + - + @@ -742,7 +742,7 @@ - + From 81139a069f182ee44cc79044ad82299a351e419c Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 13 Dec 2011 18:42:18 +0100 Subject: [PATCH 09/35] generate python spellchecker dict -> internal --- python/src/META-INF/python-plugin-common.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/src/META-INF/python-plugin-common.xml b/python/src/META-INF/python-plugin-common.xml index 88670bff3802..93bbaa12b698 100644 --- a/python/src/META-INF/python-plugin-common.xml +++ b/python/src/META-INF/python-plugin-common.xml @@ -950,7 +950,7 @@ - + From 5c866def18e1ec4a21678bbb8b134e01a96c650b Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 14 Dec 2011 13:31:19 +0100 Subject: [PATCH 10/35] disable native mac clipboard in PyCharm --- python/build/pycharm_build.gant | 1 + 1 file changed, 1 insertion(+) diff --git a/python/build/pycharm_build.gant b/python/build/pycharm_build.gant index a9ccb8e0dc1b..c86ab2998d14 100644 --- a/python/build/pycharm_build.gant +++ b/python/build/pycharm_build.gant @@ -515,6 +515,7 @@ def layoutMac(Map args, String target) { args["bundleIdentifier"] = "com.jetbrains.pycharm" args["platform_prefix"] = "Python" args["idea.properties.path"] = "$home/python/build/idea.properties" + args["idea.properties"] = ["ide.mac.useNativeClipboard":"false"]; layoutMacApp(target, "$home/community", args) From 21d063d58ea0cd619ab1210b2271dfb1fa3c45ad Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 14 Dec 2011 14:55:19 +0100 Subject: [PATCH 11/35] honor fileOnly flag correctly when resolving import references (PY-1896) --- .../PyUnresolvedReferencesInspection.java | 20 +++++++++---------- .../psi/impl/PyImportReferenceImpl.java | 10 ++++++++++ .../python/psi/resolve/ResolveImportUtil.java | 7 +++++-- .../importFunction.py | 1 + .../unresolvedImport.py | 2 +- .../unresolvedImportedModule.py | 2 +- .../python/PyMultiFileResolveTest.java | 9 --------- .../PyUnresolvedReferencesInspectionTest.java | 4 ++++ 8 files changed, 32 insertions(+), 23 deletions(-) create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection/importFunction.py diff --git a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java index 13a9c1ee892b..321569cb010d 100644 --- a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java @@ -323,7 +323,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection { } private void registerUnresolvedReferenceProblem(final PyElement node, final PsiReference reference, HighlightSeverity severity) { - final StringBuilder description_buf = new StringBuilder(""); // TODO: clear description_buf logic. maybe a flag is needed instead. + final StringBuilder descriptionBuf = new StringBuilder(""); // TODO: clear descriptionBuf logic. maybe a flag is needed instead. final String text = reference.getElement().getText(); final String ref_text = reference.getRangeInElement().substring(text); // text of the part we're working with final PsiElement element = reference.getElement(); @@ -393,7 +393,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection { )) { severity = HighlightSeverity.WEAK_WARNING; String errmsg = PyBundle.message("INSP.module.$0.not.found", ref_text); - description_buf.append(errmsg); + descriptionBuf.append(errmsg); // TODO: mark the node so that future references pointing to it won't result in a error, but in a warning } } @@ -401,11 +401,11 @@ public class PyUnresolvedReferencesInspection extends PyInspection { if (myIgnoredIdentifiers.contains(reference.getCanonicalText())) return; } - if (reference instanceof PsiReferenceEx) { + if (reference instanceof PsiReferenceEx && descriptionBuf.length() == 0) { final String s = ((PsiReferenceEx)reference).getUnresolvedDescription(); - if (s != null) description_buf.append(s); + if (s != null) descriptionBuf.append(s); } - if (description_buf.length() == 0) { + if (descriptionBuf.length() == 0) { boolean marked_qualified = false; if (element instanceof PyQualifiedExpression) { final PyQualifiedExpression qexpr = (PyQualifiedExpression)element; @@ -441,12 +441,12 @@ public class PyUnresolvedReferencesInspection extends PyInspection { } } if (reference instanceof PyOperatorReferenceImpl) { - description_buf.append(PyBundle.message("INSP.unresolved.operator.ref", + descriptionBuf.append(PyBundle.message("INSP.unresolved.operator.ref", qtype.getName(), refname, ((PyOperatorReferenceImpl)reference).getReadableOperatorName())); } else { - description_buf.append(PyBundle.message("INSP.unresolved.ref.$0.for.class.$1", ref_text, qtype.getName())); + descriptionBuf.append(PyBundle.message("INSP.unresolved.ref.$0.for.class.$1", ref_text, qtype.getName())); } marked_qualified = true; } @@ -461,14 +461,14 @@ public class PyUnresolvedReferencesInspection extends PyInspection { return; } else { - description_buf.append(PyBundle.message("INSP.cannot.find.$0.in.$1", ref_text, qtype.getName())); + descriptionBuf.append(PyBundle.message("INSP.cannot.find.$0.in.$1", ref_text, qtype.getName())); marked_qualified = true; } } } } if (! marked_qualified) { - description_buf.append(PyBundle.message("INSP.unresolved.ref.$0", ref_text)); + descriptionBuf.append(PyBundle.message("INSP.unresolved.ref.$0", ref_text)); if (ref_text.equals("true") || ref_text.equals("false")) actions.add(new UnresolvedRefTrueFalseQuickFix(element)); @@ -509,7 +509,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection { } } } - String description = description_buf.toString(); + String description = descriptionBuf.toString(); ProblemHighlightType hl_type; if (severity == HighlightSeverity.WARNING) { hl_type = ProblemHighlightType.GENERIC_ERROR_OR_WARNING; diff --git a/python/src/com/jetbrains/python/psi/impl/PyImportReferenceImpl.java b/python/src/com/jetbrains/python/psi/impl/PyImportReferenceImpl.java index 63d79ee80359..b63cfd09a901 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyImportReferenceImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyImportReferenceImpl.java @@ -37,6 +37,16 @@ public class PyImportReferenceImpl extends PyReferenceImpl { myElement = element; } + + @Override + public String getUnresolvedDescription() { + final PyImportStatement importStatement = PsiTreeUtil.getParentOfType(myElement, PyImportStatement.class); + if (importStatement != null) { + return "No module named " + myElement.getReferencedName(); + } + return super.getUnresolvedDescription(); + } + @NotNull @Override protected List resolveInner() { diff --git a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java index 506f66ee2ee8..3b3bb4689d61 100644 --- a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java +++ b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java @@ -615,7 +615,7 @@ public class ResolveImportUtil { module = null; break; } - module = resolveChild(module, component, foothold_file, root, false, checkForPackage); // only files, we want a module + module = resolveChild(module, component, foothold_file, root, true, checkForPackage); // only files, we want a module } return module; } @@ -685,7 +685,10 @@ public class ResolveImportUtil { // OTOH, quite often a module named foo exports a class or function named foo, which is used as a fallback // by a module one level higher (e.g. curses.set_key). Prefer it to submodule if possible. - ret = ((PyFileImpl)parent).getElementNamed(referencedName, false); + PsiElement elementNamed = ((PyFileImpl)parent).getElementNamed(referencedName, false); + if (!fileOnly || PyUtil.instanceOf(elementNamed, PsiFile.class, PsiDirectory.class)) { + ret = elementNamed; + } if (ret != null && !PyUtil.instanceOf(ret, PsiFile.class, PsiDirectory.class) && PsiTreeUtil.getStubOrPsiParentOfType(ret, PyExceptPart.class) == null) { return ret; diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/importFunction.py b/python/testData/inspections/PyUnresolvedReferencesInspection/importFunction.py new file mode 100644 index 000000000000..2bfbaa3d0ee9 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/importFunction.py @@ -0,0 +1 @@ +import collections.OrderedDict diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/unresolvedImport.py b/python/testData/inspections/PyUnresolvedReferencesInspection/unresolvedImport.py index 853423274bc8..6920e61d6f58 100644 --- a/python/testData/inspections/PyUnresolvedReferencesInspection/unresolvedImport.py +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/unresolvedImport.py @@ -1 +1 @@ -import wurm +import wurm diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/unresolvedImportedModule.py b/python/testData/inspections/PyUnresolvedReferencesInspection/unresolvedImportedModule.py index 9a9bd1864596..536498f81fad 100644 --- a/python/testData/inspections/PyUnresolvedReferencesInspection/unresolvedImportedModule.py +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/unresolvedImportedModule.py @@ -1,2 +1,2 @@ -import deliverance.proxycommand +import deliverance.proxycommand deliverance.proxycommand.main() diff --git a/python/testSrc/com/jetbrains/python/PyMultiFileResolveTest.java b/python/testSrc/com/jetbrains/python/PyMultiFileResolveTest.java index 196c45ead729..053781491a75 100644 --- a/python/testSrc/com/jetbrains/python/PyMultiFileResolveTest.java +++ b/python/testSrc/com/jetbrains/python/PyMultiFileResolveTest.java @@ -1,7 +1,6 @@ package com.jetbrains.python; import com.intellij.openapi.fileTypes.FileType; -import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileFilter; import com.intellij.psi.*; @@ -210,14 +209,6 @@ public class PyMultiFileResolveTest extends PyResolveTestCase { assertResolvesTo(PyTargetExpression.class, "__all__"); } - public void testDunderAllImport() { - assertResolvesTo(PyTargetExpression.class, "__all__"); - } - - public void testDunderAllImportResolve() { - assertResolvesTo(PyTargetExpression.class, "__all__"); - } - public void testDunderAllConflict() { assertResolvesTo(PyFunction.class, "do_stuff", "/src/mypackage1.py"); } diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java index fc8bf9acbbbb..33a3e270252d 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java @@ -73,6 +73,10 @@ public class PyUnresolvedReferencesInspectionTest extends PyTestCase { doTest(); } + public void testImportFunction() { // PY-1896 + doTest(); + } + public void testImportToContainingFile() { // PY-4372 myFixture.copyFileToProject("inspections/PyUnresolvedReferencesInspection/__init__.py", "PyUnresolvedReferencesInspection/__init__.py"); myFixture.copyFileToProject("inspections/PyUnresolvedReferencesInspection/importToContainingFile.py", "PyUnresolvedReferencesInspection/importToContainingFile.py"); From 3acfd908e163e0412591f551b7917c149384001f Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 14 Dec 2011 17:06:59 +0100 Subject: [PATCH 12/35] refactor PyUnresolvedReferencesInspection.registerUnresolvedReferenceProblem() to a more manageable size --- .../imports/PythonReferenceImporter.java | 23 +- .../PyUnresolvedReferencesInspection.java | 248 ++++++++++-------- .../inspections/AddFunctionToModule.py | 2 +- 3 files changed, 151 insertions(+), 122 deletions(-) diff --git a/python/src/com/jetbrains/python/codeInsight/imports/PythonReferenceImporter.java b/python/src/com/jetbrains/python/codeInsight/imports/PythonReferenceImporter.java index 5cd1f6138b3d..4f0c0df3dc78 100644 --- a/python/src/com/jetbrains/python/codeInsight/imports/PythonReferenceImporter.java +++ b/python/src/com/jetbrains/python/codeInsight/imports/PythonReferenceImporter.java @@ -52,7 +52,7 @@ public class PythonReferenceImporter implements ReferenceImporter { if (refExpr.getQualifier() == null) { final PsiPolyVariantReference reference = refExpr.getReference(); if (reference.resolve() == null) { - AutoImportQuickFix fix = proposeImportFix(refExpr, reference, refExpr.getText()); + AutoImportQuickFix fix = proposeImportFix(refExpr, reference); if (fix != null && fix.getCandidatesCount() == 1) { fix.invoke(file); } @@ -67,14 +67,17 @@ public class PythonReferenceImporter implements ReferenceImporter { private static TokenSet IS_IMPORT_STATEMENT = TokenSet.create(PyElementTypes.IMPORT_STATEMENT); @Nullable - public static AutoImportQuickFix proposeImportFix(final PyElement node, PsiReference reference, String ref_text) { + public static AutoImportQuickFix proposeImportFix(final PyElement node, PsiReference reference) { + final String text = reference.getElement().getText(); + final String refText = reference.getRangeInElement().substring(text); // text of the part we're working with + // don't propose meaningless auto imports if no interpreter is configured final Module module = ModuleUtil.findModuleForPsiElement(node); if (module != null && PythonSdkType.findPythonSdk(module) == null) { return null; } PsiFile existing_import_file = null; // if there's a matching existing import, this it the file it imports - AutoImportQuickFix fix = new AutoImportQuickFix(node, reference, ref_text, !PyCodeInsightSettings.getInstance().PREFER_FROM_IMPORT); + AutoImportQuickFix fix = new AutoImportQuickFix(node, reference, refText, !PyCodeInsightSettings.getInstance().PREFER_FROM_IMPORT); Set seen_file_names = new HashSet(); // true import names // maybe the name is importable via some existing 'import foo' statement, and only needs a qualifier. // walk up collecting all such statements and analyzing @@ -91,7 +94,7 @@ public class PythonReferenceImporter implements ReferenceImporter { PyFile dst_file = (PyFile)dst; String name = ielt.getImportReference().getReferencedName(); // ref is ok or matching would fail seen_file_names.add(name); - PsiElement res = dst_file.findExportedName(ref_text); + PsiElement res = dst_file.findExportedName(refText); if (res != null && !(res instanceof PyFile) && !(res instanceof PyImportElement) && dst_file.equals(res.getContainingFile())) { existing_import_file = dst_file; fix.addImport(res, dst_file, ielt); @@ -107,14 +110,14 @@ public class PythonReferenceImporter implements ReferenceImporter { // NOTE: current indices have limitations, only finding direct definitions of classes and functions. Project project = node.getProject(); List symbols = new ArrayList(); - symbols.addAll(PyClassNameIndex.find(ref_text, project, true)); + symbols.addAll(PyClassNameIndex.find(refText, project, true)); GlobalSearchScope scope = PyClassNameIndex.projectWithLibrariesScope(project); if (!isQualifier(node)) { - symbols.addAll(PyFunctionNameIndex.find(ref_text, project, scope)); + symbols.addAll(PyFunctionNameIndex.find(refText, project, scope)); } - symbols.addAll(PyVariableNameIndex.find(ref_text, project, scope)); + symbols.addAll(PyVariableNameIndex.find(refText, project, scope)); if (!isCall(node)) { - symbols.addAll(findImportableModules(node.getContainingFile(), ref_text, project, scope)); + symbols.addAll(findImportableModules(node.getContainingFile(), refText, project, scope)); } if (symbols.size() > 0) { for (PsiElement symbol : symbols) { @@ -125,7 +128,7 @@ public class PythonReferenceImporter implements ReferenceImporter { PyQualifiedName import_path = ResolveImportUtil.findCanonicalImportPath(srcfile, node); if (import_path != null && !seen_file_names.contains(import_path.toString())) { // a new, valid hit - fix.addImport(symbol, srcfile, import_path, proposeAsName(node.getContainingFile(), ref_text, import_path)); + fix.addImport(symbol, srcfile, import_path, proposeAsName(node.getContainingFile(), refText, import_path)); seen_file_names.add(import_path.toString()); // just in case, again } } @@ -133,7 +136,7 @@ public class PythonReferenceImporter implements ReferenceImporter { } } for(PyImportCandidateProvider provider: Extensions.getExtensions(PyImportCandidateProvider.EP_NAME)) { - provider.addImportCandidates(reference, ref_text, fix); + provider.addImportCandidates(reference, refText, fix); } if (fix.getCandidatesCount() > 0) { fix.sortCandidates(); diff --git a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java index 321569cb010d..49f3f24e1720 100644 --- a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java @@ -323,11 +323,10 @@ public class PyUnresolvedReferencesInspection extends PyInspection { } private void registerUnresolvedReferenceProblem(final PyElement node, final PsiReference reference, HighlightSeverity severity) { - final StringBuilder descriptionBuf = new StringBuilder(""); // TODO: clear descriptionBuf logic. maybe a flag is needed instead. + String description = null; final String text = reference.getElement().getText(); final String ref_text = reference.getRangeInElement().substring(text); // text of the part we're working with final PsiElement element = reference.getElement(); - final boolean ref_is_importable = PythonReferenceImporter.isImportable(element); final List actions = new ArrayList(2); if (ref_text.length() <= 0) return; // empty text, nothing to highlight final String refname = (element instanceof PyQualifiedExpression) ? ((PyQualifiedExpression)element).getReferencedName() : ref_text; @@ -342,42 +341,16 @@ public class PyUnresolvedReferencesInspection extends PyInspection { if (refex.getQualifier() != null) { final PyClassType object_type = PyBuiltinCache.getInstance(node).getObjectType(); if ((object_type != null) && object_type.getPossibleInstanceMembers().contains(refname)) return; - } else { if (LanguageLevel.forElement(node).isOlderThan(LanguageLevel.PYTHON26)) { - if (refname.equals("with")) { + if ("with".equals(refname)) { actions.add(new UnresolvedRefAddFutureImportQuickFix(refex)); } } - PyClass containedClass = PsiTreeUtil.getParentOfType(node, PyClass.class); - if (containedClass != null) { - for (PyTargetExpression target : containedClass.getInstanceAttributes()) { - if (Comparing.strEqual(node.getName(), target.getName())) { - actions.add(new UnresolvedReferenceAddSelfQuickFix(refex)); - } - } - for (PyStatement statement : containedClass.getStatementList().getStatements()) { - if (statement instanceof PyAssignmentStatement) { - if (((PyAssignmentStatement)statement).getLeftHandSideExpression().getText().equals(refex.getText())) { - PyExpression callexpr = ((PyAssignmentStatement)statement).getAssignedValue(); - if (callexpr instanceof PyCallExpression) { - PyType type = myTypeEvalContext.getType(callexpr); - if (type != null && type instanceof PyClassType) { - String name = ((PyCallExpression)callexpr).getCallee().getText(); - if (name != null && name.equals("property")) - actions.add(new UnresolvedReferenceAddSelfQuickFix(refex)); - } - } - } - } - } - for (PyFunction method : containedClass.getMethods()) { - if (refex.getText().equals(method.getName())) { - actions.add(new UnresolvedReferenceAddSelfQuickFix(refex)); - } - } - } + if (ref_text.equals("true") || ref_text.equals("false")) + actions.add(new UnresolvedRefTrueFalseQuickFix(element)); + addAddSelfFix(node, refex, actions); PyCallExpression callExpression = PsiTreeUtil.getParentOfType(element, PyCallExpression.class); if (callExpression != null) actions.add(new UnresolvedRefCreateFunctionQuickFix(callExpression, refex)); @@ -392,8 +365,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection { ) != null )) { severity = HighlightSeverity.WEAK_WARNING; - String errmsg = PyBundle.message("INSP.module.$0.not.found", ref_text); - descriptionBuf.append(errmsg); + description = PyBundle.message("INSP.module.$0.not.found", ref_text); // TODO: mark the node so that future references pointing to it won't result in a error, but in a warning } } @@ -401,11 +373,10 @@ public class PyUnresolvedReferencesInspection extends PyInspection { if (myIgnoredIdentifiers.contains(reference.getCanonicalText())) return; } - if (reference instanceof PsiReferenceEx && descriptionBuf.length() == 0) { - final String s = ((PsiReferenceEx)reference).getUnresolvedDescription(); - if (s != null) descriptionBuf.append(s); + if (reference instanceof PsiReferenceEx && description == null) { + description = ((PsiReferenceEx)reference).getUnresolvedDescription(); } - if (descriptionBuf.length() == 0) { + if (description == null) { boolean marked_qualified = false; if (element instanceof PyQualifiedExpression) { final PyQualifiedExpression qexpr = (PyQualifiedExpression)element; @@ -416,100 +387,39 @@ public class PyUnresolvedReferencesInspection extends PyInspection { if (qualifier != null) { PyType qtype = myTypeEvalContext.getType(qualifier); if (qtype != null) { - if (qtype instanceof PyNoneType || qtype instanceof PyTypeReference || - (qtype instanceof PyUnionType && ((PyUnionType) qtype).isWeak()) || - (qtype instanceof PyImportedModuleType)) { - // this almost always means that we don't know the type, so don't show an error in this case + if (ignoreUnresolvedMemberForType(qtype, reference, ref_text)) { return; } + addCreateMemberFromUsageFixes(qtype, reference, ref_text, actions); if (qtype instanceof PyClassType) { - PyClass cls = ((PyClassType)qtype).getPyClass(); - if (cls != null) { - if (overridesGetAttr(cls)) { - return; - } - if (cls.findProperty(ref_text) != null) { - return; // a property exists but accessor is not found; other inspections handle this - } - if (! PyBuiltinCache.getInstance(node).hasInBuiltins(cls)) { - if (element.getParent() instanceof PyCallExpression) { - actions.add(new AddMethodQuickFix(ref_text, (PyClassType)qtype)); - } - else if (!(reference instanceof PyOperatorReferenceImpl)) { - actions.add(new AddFieldQuickFix(ref_text, cls, "None")); - } - } - } if (reference instanceof PyOperatorReferenceImpl) { - descriptionBuf.append(PyBundle.message("INSP.unresolved.operator.ref", - qtype.getName(), refname, - ((PyOperatorReferenceImpl)reference).getReadableOperatorName())); + description = PyBundle.message("INSP.unresolved.operator.ref", + qtype.getName(), refname, + ((PyOperatorReferenceImpl)reference).getReadableOperatorName()); } else { - descriptionBuf.append(PyBundle.message("INSP.unresolved.ref.$0.for.class.$1", ref_text, qtype.getName())); + description = PyBundle.message("INSP.unresolved.ref.$0.for.class.$1", ref_text, qtype.getName()); } marked_qualified = true; } - else if (qtype instanceof PyModuleType) { - PsiFile file = ((PyModuleType)qtype).getModule(); - if (file instanceof PyFile) { - actions.add(new AddFunctionQuickFix(ref_text, (PyFile)file)); - } - } - else if (qtype instanceof CythonBuiltinType || - (qtype instanceof CythonType && reference instanceof PyOperatorReferenceImpl)) { - return; - } else { - descriptionBuf.append(PyBundle.message("INSP.cannot.find.$0.in.$1", ref_text, qtype.getName())); + description = PyBundle.message("INSP.cannot.find.$0.in.$1", ref_text, qtype.getName()); marked_qualified = true; } } } } - if (! marked_qualified) { - descriptionBuf.append(PyBundle.message("INSP.unresolved.ref.$0", ref_text)); - if (ref_text.equals("true") || ref_text.equals("false")) - actions.add(new UnresolvedRefTrueFalseQuickFix(element)); + if (!marked_qualified) { + description = PyBundle.message("INSP.unresolved.ref.$0", ref_text); // look in other imported modules for this whole name - if (ref_is_importable) { - AutoImportQuickFix importFix = PythonReferenceImporter.proposeImportFix(node, reference, ref_text); - if (importFix != null) { - // if the context doesn't look like a function call and we only found imports of functions, suggest auto-import - // as a quickfix but no popup balloon (PY-2312) - if ((isCall(node) || !importFix.hasOnlyFunctions()) && PyCodeInsightSettings.getInstance().SHOW_IMPORT_POPUP) { - final AutoImportHintAction autoImportHintAction = new AutoImportHintAction(importFix); - actions.add(autoImportHintAction); - } - else { - actions.add(importFix); - } - } + if (PythonReferenceImporter.isImportable(element)) { + addAutoImportFix(node, reference, actions); } - if (ref_text.length() > 2 && Character.isUpperCase(ref_text.charAt(0)) && !Character.isUpperCase(ref_text.charAt(1)) && - PsiTreeUtil.getParentOfType(element, PyImportStatementBase.class) == null) { - PsiElement anchor = element; - if (element instanceof PyQualifiedExpression) { - final PyExpression qexpr = ((PyQualifiedExpression)element).getQualifier(); - if (qexpr != null) { - final PyType type = myTypeEvalContext.getType(qexpr); - if (type instanceof PyModuleType) { - anchor = ((PyModuleType) type).getModule(); - } - else { - anchor = null; - } - } - if (anchor != null) { - actions.add(new CreateClassQuickFix(ref_text, anchor)); - } - } - } + addCreateClassFix(ref_text, element, actions); } } - String description = descriptionBuf.toString(); ProblemHighlightType hl_type; if (severity == HighlightSeverity.WARNING) { hl_type = ProblemHighlightType.GENERIC_ERROR_OR_WARNING; @@ -535,6 +445,122 @@ public class PyUnresolvedReferencesInspection extends PyInspection { registerProblem(point, description, hl_type, null, range, actions.toArray(new LocalQuickFix[actions.size()])); } + private static boolean ignoreUnresolvedMemberForType(PyType qtype, PsiReference reference, String refText) { + if (qtype instanceof PyNoneType || qtype instanceof PyTypeReference || + (qtype instanceof PyUnionType && ((PyUnionType)qtype).isWeak()) || + (qtype instanceof PyImportedModuleType)) { + // this almost always means that we don't know the type, so don't show an error in this case + return true; + } + if (qtype instanceof PyClassType) { + PyClass cls = ((PyClassType)qtype).getPyClass(); + if (cls != null) { + if (overridesGetAttr(cls)) { + return true; + } + if (cls.findProperty(refText) != null) { + return true; + } + } + } + if (qtype instanceof CythonBuiltinType || + (qtype instanceof CythonType && reference instanceof PyOperatorReferenceImpl)) { + return true; + } + return false; + } + + private static void addCreateMemberFromUsageFixes(PyType qtype, PsiReference reference, String refText, List actions) { + PsiElement element = reference.getElement(); + if (qtype instanceof PyClassType) { + PyClass cls = ((PyClassType)qtype).getPyClass(); + if (cls != null) { + if (!PyBuiltinCache.getInstance(element).hasInBuiltins(cls)) { + if (element.getParent() instanceof PyCallExpression) { + actions.add(new AddMethodQuickFix(refText, (PyClassType)qtype)); + } + else if (!(reference instanceof PyOperatorReferenceImpl)) { + actions.add(new AddFieldQuickFix(refText, cls, "None")); + } + } + } + } + else if (qtype instanceof PyModuleType) { + PsiFile file = ((PyModuleType)qtype).getModule(); + if (file instanceof PyFile) { + actions.add(new AddFunctionQuickFix(refText, (PyFile)file)); + } + } + } + + private void addAddSelfFix(PyElement node, PyReferenceExpression refex, List actions) { + PyClass containedClass = PsiTreeUtil.getParentOfType(node, PyClass.class); + if (containedClass != null) { + for (PyTargetExpression target : containedClass.getInstanceAttributes()) { + if (Comparing.strEqual(node.getName(), target.getName())) { + actions.add(new UnresolvedReferenceAddSelfQuickFix(refex)); + } + } + for (PyStatement statement : containedClass.getStatementList().getStatements()) { + if (statement instanceof PyAssignmentStatement) { + if (((PyAssignmentStatement)statement).getLeftHandSideExpression().getText().equals(refex.getText())) { + PyExpression callexpr = ((PyAssignmentStatement)statement).getAssignedValue(); + if (callexpr instanceof PyCallExpression) { + PyType type = myTypeEvalContext.getType(callexpr); + if (type != null && type instanceof PyClassType) { + String name = ((PyCallExpression)callexpr).getCallee().getText(); + if (name != null && name.equals("property")) + actions.add(new UnresolvedReferenceAddSelfQuickFix(refex)); + } + } + } + } + } + for (PyFunction method : containedClass.getMethods()) { + if (refex.getText().equals(method.getName())) { + actions.add(new UnresolvedReferenceAddSelfQuickFix(refex)); + } + } + } + } + + private static void addAutoImportFix(PyElement node, PsiReference reference, List actions) { + AutoImportQuickFix importFix = PythonReferenceImporter.proposeImportFix(node, reference); + if (importFix != null) { + // if the context doesn't look like a function call and we only found imports of functions, suggest auto-import + // as a quickfix but no popup balloon (PY-2312) + if ((isCall(node) || !importFix.hasOnlyFunctions()) && PyCodeInsightSettings.getInstance().SHOW_IMPORT_POPUP) { + final AutoImportHintAction autoImportHintAction = new AutoImportHintAction(importFix); + actions.add(autoImportHintAction); + } + else { + actions.add(importFix); + } + } + } + + private void addCreateClassFix(String refText, PsiElement element, List actions) { + if (refText.length() > 2 && Character.isUpperCase(refText.charAt(0)) && !Character.isUpperCase(refText.charAt(1)) && + PsiTreeUtil.getParentOfType(element, PyImportStatementBase.class) == null) { + PsiElement anchor = element; + if (element instanceof PyQualifiedExpression) { + final PyExpression qexpr = ((PyQualifiedExpression)element).getQualifier(); + if (qexpr != null) { + final PyType type = myTypeEvalContext.getType(qexpr); + if (type instanceof PyModuleType) { + anchor = ((PyModuleType) type).getModule(); + } + else { + anchor = null; + } + } + if (anchor != null) { + actions.add(new CreateClassQuickFix(refText, anchor)); + } + } + } + } + private static boolean isCall(PyElement node) { final PyCallExpression callExpression = PsiTreeUtil.getParentOfType(node, PyCallExpression.class); return callExpression != null && node == callExpression.getCallee(); diff --git a/python/testData/inspections/AddFunctionToModule.py b/python/testData/inspections/AddFunctionToModule.py index d0426f1db205..db6c9bb36946 100644 --- a/python/testData/inspections/AddFunctionToModule.py +++ b/python/testData/inspections/AddFunctionToModule.py @@ -2,4 +2,4 @@ import AddFunctionToModu # do something -AddFunctionToModule.frob(1) +AddFunctionToModule.frob(1) From 2d78fea31661695c81bba4c4446ec4ffa7f7885d Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 14 Dec 2011 17:56:22 +0100 Subject: [PATCH 13/35] classmethod/staticmethod refactoring: set -> single value, Flag -> Modifier, static method in PyUtil -> instance method on PyFunction --- .../findUsages/PyUsageTypeProvider.java | 6 +- .../inspections/PyCallByClassInspection.java | 8 +- .../PyMethodFirstArgAssignmentInspection.java | 5 +- .../PyUnusedLocalInspectionVisitor.java | 2 +- .../python/psi/PyCallExpression.java | 15 +-- .../com/jetbrains/python/psi/PyFunction.java | 10 +- .../src/com/jetbrains/python/psi/PyUtil.java | 124 +----------------- .../psi/impl/PyCallExpressionHelper.java | 30 ++--- .../python/psi/impl/PyDecoratorImpl.java | 2 +- .../python/psi/impl/PyFunctionImpl.java | 117 +++++++++++++++++ .../python/psi/impl/PyNamedParameterImpl.java | 7 +- .../field/PyIntroduceFieldHandler.java | 8 +- .../validation/HighlightingAnnotator.java | 6 +- .../jetbrains/python/PyResolveCalleeTest.java | 8 +- .../com/jetbrains/python/PyStubsTest.java | 6 +- 15 files changed, 175 insertions(+), 179 deletions(-) diff --git a/python/src/com/jetbrains/python/findUsages/PyUsageTypeProvider.java b/python/src/com/jetbrains/python/findUsages/PyUsageTypeProvider.java index 14c695dab65e..cc8a6f19ac26 100644 --- a/python/src/com/jetbrains/python/findUsages/PyUsageTypeProvider.java +++ b/python/src/com/jetbrains/python/findUsages/PyUsageTypeProvider.java @@ -14,8 +14,6 @@ import com.jetbrains.python.psi.types.PyTypeReference; import com.jetbrains.python.psi.types.TypeEvalContext; import org.jetbrains.annotations.Nullable; -import java.util.Set; - /** * @author yole */ @@ -79,8 +77,8 @@ public class PyUsageTypeProvider implements UsageTypeProviderEx { final PsiElement element = ((PsiElementUsageTarget)targets[0]).getElement(); if (element instanceof PyFunction) { PyFunction function = (PyFunction) element; - final Set flags = PyUtil.detectDecorationsAndWrappersOf(function); - PyCallExpression.PyMarkedCallee callee = new PyCallExpression.PyMarkedCallee(function, flags, 1, true); + final PyFunction.Modifier modifier = function.getModifier(); + PyCallExpression.PyMarkedCallee callee = new PyCallExpression.PyMarkedCallee(function, modifier, 1, true); CallArgumentsMappingImpl mapping = new CallArgumentsMappingImpl(call.getArgumentList()); mapping.mapArguments(callee, null); if (mapping.hasProblems()) { diff --git a/python/src/com/jetbrains/python/inspections/PyCallByClassInspection.java b/python/src/com/jetbrains/python/inspections/PyCallByClassInspection.java index 3938df79eefe..1d3b033b62d9 100644 --- a/python/src/com/jetbrains/python/inspections/PyCallByClassInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyCallByClassInspection.java @@ -14,8 +14,8 @@ import org.jetbrains.annotations.NotNull; import java.util.Map; -import static com.jetbrains.python.psi.PyFunction.Flag.CLASSMETHOD; -import static com.jetbrains.python.psi.PyFunction.Flag.STATICMETHOD; +import static com.jetbrains.python.psi.PyFunction.Modifier.CLASSMETHOD; +import static com.jetbrains.python.psi.PyFunction.Modifier.STATICMETHOD; /** * Checks for for calls like X.method(y,...), where y is not an instance of X. @@ -77,7 +77,7 @@ public class PyCallByClassInspection extends PyInspection { if (arglist != null) { CallArgumentsMapping analysis = arglist.analyzeCall(resolveWithoutImplicits()); final PyCallExpression.PyMarkedCallee markedCallee = analysis.getMarkedCallee(); - if (markedCallee != null && !markedCallee.getFlags().contains(STATICMETHOD)) { + if (markedCallee != null && markedCallee.getModifier() != STATICMETHOD) { PyParameter[] params = markedCallee.getCallable().getParameterList().getParameters(); if (params.length > 0 && params[0] instanceof PyNamedParameter) { PyNamedParameter first_param = (PyNamedParameter)params[0]; @@ -89,7 +89,7 @@ public class PyCallByClassInspection extends PyInspection { PyType first_arg_type = myTypeEvalContext.getType(first_arg); if (first_arg_type instanceof PyClassType) { final PyClassType first_arg_class_type = (PyClassType)first_arg_type; - if (first_arg_class_type.isDefinition() && !markedCallee.getFlags().contains(CLASSMETHOD)) { + if (first_arg_class_type.isDefinition() && markedCallee.getModifier() != CLASSMETHOD) { registerProblem( first_arg, PyBundle.message("INSP.instance.of.$0.excpected", qual_class.getQualifiedName()) diff --git a/python/src/com/jetbrains/python/inspections/PyMethodFirstArgAssignmentInspection.java b/python/src/com/jetbrains/python/inspections/PyMethodFirstArgAssignmentInspection.java index 0716e5c352cc..0b86d1ad3f55 100644 --- a/python/src/com/jetbrains/python/inspections/PyMethodFirstArgAssignmentInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyMethodFirstArgAssignmentInspection.java @@ -12,7 +12,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; -import java.util.Set; /** * Reports assignment to 'self' or 'cls'. @@ -64,8 +63,8 @@ public class PyMethodFirstArgAssignmentInspection extends PyInspection { final String first_param_name = first_parm.getName(); if (first_param_name == null || first_param_name.length() < 1) return null; // ignore cases of incorrect code // is it a static method? - Set flagSet = PyUtil.detectDecorationsAndWrappersOf(method); - if (flagSet.contains(PyFunction.Flag.STATICMETHOD)) return null; // these may do whatever they please + PyFunction.Modifier modifier = method.getModifier(); + if (modifier == PyFunction.Modifier.STATICMETHOD) return null; // these may do whatever they please return first_param_name; } diff --git a/python/src/com/jetbrains/python/inspections/PyUnusedLocalInspectionVisitor.java b/python/src/com/jetbrains/python/inspections/PyUnusedLocalInspectionVisitor.java index 7e3aebbff87a..072998afa52a 100644 --- a/python/src/com/jetbrains/python/inspections/PyUnusedLocalInspectionVisitor.java +++ b/python/src/com/jetbrains/python/inspections/PyUnusedLocalInspectionVisitor.java @@ -245,7 +245,7 @@ public class PyUnusedLocalInspectionVisitor extends PyInspectionVisitor { if (PsiTreeUtil.getParentOfType(element, PyClass.class) != null) { // When function is inside a class, first parameter may be either self or cls which is always 'used'. final PyFunction method = PsiTreeUtil.getParentOfType(element, PyFunction.class); - if (method != null && ! PyNames.STATICMETHOD.equals(PyUtil.getClassOrStaticMethodDecorator(method))) { + if (method != null && method.getModifier() != PyFunction.Modifier.STATICMETHOD) { final PsiElement parent = namedParameter.getParent(); if (parent instanceof PyParameterList && ((PyParameterList)parent).getParameters()[0] == namedParameter) { continue; diff --git a/python/src/com/jetbrains/python/psi/PyCallExpression.java b/python/src/com/jetbrains/python/psi/PyCallExpression.java index eb453c5828fe..78e725f654c6 100644 --- a/python/src/com/jetbrains/python/psi/PyCallExpression.java +++ b/python/src/com/jetbrains/python/psi/PyCallExpression.java @@ -5,9 +5,6 @@ import com.jetbrains.python.psi.resolve.PyResolveContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.EnumSet; -import java.util.Set; - /** * Represents an entire call expression, like foo() or foo.bar[1]('x'). */ @@ -85,7 +82,7 @@ public interface PyCallExpression extends PyExpression { */ class PyMarkedCallee { Callable myCallable; - Set myFlags; + PyFunction.Modifier myModifier; int myImplicitOffset; boolean myImplicitlyResolved; @@ -97,16 +94,16 @@ public interface PyCallExpression extends PyExpression { * @param offset implicit argument offset; parameters up to this are implicitly filled in the call. * @param implicitlyResolved value for {@link #isImplicitlyResolved()} */ - public PyMarkedCallee(@NotNull Callable function, Set flags, int offset, boolean implicitlyResolved) { + public PyMarkedCallee(@NotNull Callable function, PyFunction.Modifier modifier, int offset, boolean implicitlyResolved) { myCallable = function; - myFlags = flags; + myModifier = modifier; myImplicitOffset = offset; myImplicitlyResolved = implicitlyResolved; } public PyMarkedCallee(Callable callable, boolean implicitlyResolved) { myCallable = callable; - myFlags = EnumSet.noneOf(PyFunction.Flag.class); + myModifier = null; myImplicitOffset = 0; myImplicitlyResolved = implicitlyResolved; } @@ -115,8 +112,8 @@ public interface PyCallExpression extends PyExpression { return myCallable; } - public Set getFlags() { - return myFlags; + public PyFunction.Modifier getModifier() { + return myModifier; } /** diff --git a/python/src/com/jetbrains/python/psi/PyFunction.java b/python/src/com/jetbrains/python/psi/PyFunction.java index 441c0d7c19a1..51512d11faae 100644 --- a/python/src/com/jetbrains/python/psi/PyFunction.java +++ b/python/src/com/jetbrains/python/psi/PyFunction.java @@ -63,10 +63,18 @@ extends */ String getDeprecationMessage(); + /** + * Looks for two standard decorators to a function, or a wrapping assignment that closely follows it. + * + * @return a flag describing what was detected. + */ + @Nullable + Modifier getModifier(); + /** * Flags that mark common alterations of a function: decoration by and wrapping in classmethod() and staticmethod(). */ - enum Flag { + enum Modifier { /** * Function is decorated with @classmethod, its first param is the class. */ diff --git a/python/src/com/jetbrains/python/psi/PyUtil.java b/python/src/com/jetbrains/python/psi/PyUtil.java index f46bda03189e..369501beb556 100644 --- a/python/src/com/jetbrains/python/psi/PyUtil.java +++ b/python/src/com/jetbrains/python/psi/PyUtil.java @@ -17,13 +17,11 @@ import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.ui.popup.Balloon; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.util.Condition; -import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.WindowManager; import com.intellij.psi.*; -import com.intellij.psi.stubs.StubElement; import com.intellij.psi.tree.TokenSet; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.ui.awt.RelativePoint; @@ -39,9 +37,6 @@ import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.codeInsight.stdlib.PyNamedTupleType; import com.jetbrains.python.documentation.EpydocUtil; import com.jetbrains.python.psi.impl.PyBuiltinCache; -import com.jetbrains.python.psi.impl.PyQualifiedName; -import com.jetbrains.python.psi.stubs.PyFunctionStub; -import com.jetbrains.python.psi.stubs.PyTargetExpressionStub; import com.jetbrains.python.psi.types.PyClassType; import com.jetbrains.python.psi.types.PyTupleType; import com.jetbrains.python.psi.types.PyType; @@ -58,9 +53,8 @@ import java.io.IOException; import java.util.*; import java.util.List; -import static com.jetbrains.python.psi.PyFunction.Flag.CLASSMETHOD; -import static com.jetbrains.python.psi.PyFunction.Flag.STATICMETHOD; -import static com.jetbrains.python.psi.impl.PyCallExpressionHelper.interpretAsStaticmethodOrClassmethodWrappingCall; +import static com.jetbrains.python.psi.PyFunction.Modifier.CLASSMETHOD; +import static com.jetbrains.python.psi.PyFunction.Modifier.STATICMETHOD; public class PyUtil { private PyUtil() { @@ -443,42 +437,6 @@ public class PyUtil { if (!thing) throw new IncorrectOperationException(); } - /** - * When a function is decorated many decorators, finds the deepest builtin decorator: - *
-   * @foo
-   * @classmethod # <-- that's it
-   * @bar
-   * def moo(cls):
-   *   pass
-   * 
- * @param node the allegedly decorated function - * @return name of the built-in decorator, or null (even if there are non-built-in decorators). - */ - @Nullable - public static String getClassOrStaticMethodDecorator(@NotNull final PyFunction node) { - PyDecoratorList decolist = node.getDecoratorList(); - if (decolist != null) { - PyDecorator[] decos = decolist.getDecorators(); - if (decos.length > 0) { - for (int i = decos.length - 1; i >= 0; i -= 1) { - PyDecorator deco = decos[i]; - String deconame = deco.getName(); - if (PyNames.CLASSMETHOD.equals(deconame) || PyNames.STATICMETHOD.equals(deconame)) { - return deconame; - } - for(PyKnownDecoratorProvider provider: KnownDecoratorProviderHolder.KNOWN_DECORATOR_PROVIDERS) { - String name = provider.toKnownDecorator(deconame); - if (name != null) { - return name; - } - } - } - } - } - return null; - } - public static boolean isInstanceAttribute(PyExpression target) { if (!(target instanceof PyTargetExpression)) { return false; @@ -612,80 +570,6 @@ public class PyUtil { } } - /** - * Looks for two standard decorators to a function, or a wrapping assignment that closely follows it. - * - * @param function what to analyze - * @return a set of flags describing what was detected. - */ - @NotNull - public static Set detectDecorationsAndWrappersOf(PyFunction function) { - Set flags = EnumSet.noneOf(PyFunction.Flag.class); - String deconame = getClassOrStaticMethodDecorator(function); - if (PyNames.CLASSMETHOD.equals(deconame)) { - flags.add(CLASSMETHOD); - } - else if (PyNames.STATICMETHOD.equals(deconame)) flags.add(STATICMETHOD); - // implicit staticmethod __new__ - PyClass cls = function.getContainingClass(); - if (cls != null && PyNames.NEW.equals(function.getName()) && cls.isNewStyleClass()) flags.add(STATICMETHOD); - // - if (!flags.contains(CLASSMETHOD) && !flags.contains(STATICMETHOD)) { // not set by decos, look for reassignment - final PyFunctionStub stub = function.getStub(); - if (stub != null) { - return getWrappersFromStub(stub); - } - String func_name = function.getName(); - if (func_name != null) { - PyAssignmentStatement assignment = PsiTreeUtil.getNextSiblingOfType(function, PyAssignmentStatement.class); - if (assignment != null) { - for (Pair pair : assignment.getTargetsToValuesMapping()) { - PyExpression value = pair.getSecond(); - if (value instanceof PyCallExpression) { - PyExpression target = pair.getFirst(); - if (target instanceof PyTargetExpression && func_name.equals(target.getName())) { - Pair interpreted = interpretAsStaticmethodOrClassmethodWrappingCall((PyCallExpression)value, function); - if (interpreted != null) { - PyFunction original = interpreted.getSecond(); - if (original == function) { - String wrapper_name = interpreted.getFirst(); - if (PyNames.CLASSMETHOD.equals(wrapper_name)) { - flags.add(CLASSMETHOD); - } - else if (PyNames.STATICMETHOD.equals(wrapper_name)) flags.add(STATICMETHOD); - } - } - } - } - } - } - } - } - return flags; - } - - private static Set getWrappersFromStub(PyFunctionStub stub) { - final StubElement parentStub = stub.getParentStub(); - final List childrenStubs = parentStub.getChildrenStubs(); - int index = childrenStubs.indexOf(stub); - if (index >= 0 && index < childrenStubs.size() - 1) { - StubElement nextStub = (StubElement) childrenStubs.get(index+1); - if (nextStub instanceof PyTargetExpressionStub) { - final PyTargetExpressionStub targetExpressionStub = (PyTargetExpressionStub)nextStub; - if (targetExpressionStub.getInitializerType() == PyTargetExpressionStub.InitializerType.CallExpression) { - final PyQualifiedName qualifiedName = targetExpressionStub.getInitializer(); - if (PyQualifiedName.fromComponents(PyNames.CLASSMETHOD).equals(qualifiedName)) { - return EnumSet.of(PyFunction.Flag.CLASSMETHOD); - } - if (PyQualifiedName.fromComponents(PyNames.STATICMETHOD).equals(qualifiedName)) { - return EnumSet.of(PyFunction.Flag.STATICMETHOD); - } - } - } - } - return EnumSet.noneOf(PyFunction.Flag.class); - } - /** * Returns child element in the psi tree * @@ -999,7 +883,7 @@ public class PyUtil { public static MethodFlags of(@NotNull PyFunction node) { PyClass cls = node.getContainingClass(); if (cls != null) { - Set flags = detectDecorationsAndWrappersOf(node); + PyFunction.Modifier modifier = node.getModifier(); boolean isMetaclassMethod = false; PyClass type_cls = PyBuiltinCache.getInstance(node).getClass("type"); for (PyClass ancestor_cls : cls.iterateAncestorClasses()) { @@ -1010,7 +894,7 @@ public class PyUtil { } final String method_name = node.getName(); boolean isSpecialMetaclassMethod = isMetaclassMethod && method_name != null && among(method_name, PyNames.INIT, "__call__"); - return new MethodFlags(flags.contains(CLASSMETHOD), flags.contains(STATICMETHOD), isMetaclassMethod, isSpecialMetaclassMethod); + return new MethodFlags(modifier == CLASSMETHOD, modifier == STATICMETHOD, isMetaclassMethod, isSpecialMetaclassMethod); } return null; } diff --git a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java index 0d8e6e51c88d..c33a5de1aaeb 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java +++ b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java @@ -139,7 +139,7 @@ public class PyCallExpressionHelper { @Nullable public static PyCallExpression.PyMarkedCallee resolveCallee(PyCallExpression us, PyResolveContext resolveContext, int implicitOffset) { - PyFunction.Flag wrappedFlag = null; + PyFunction.Modifier wrappedModifier = null; boolean isConstructorCall = false; PyExpression callee = us.getCallee(); @@ -167,17 +167,17 @@ public class PyCallExpressionHelper { resolved = wrapperInfo.getSecond(); String wrapper_name = wrapperInfo.getFirst(); if (PyNames.CLASSMETHOD.equals(wrapper_name)) { - wrappedFlag = PyFunction.Flag.CLASSMETHOD; + wrappedModifier = PyFunction.Modifier.CLASSMETHOD; } - else if (PyNames.STATICMETHOD.equals(wrapper_name)) wrappedFlag = PyFunction.Flag.STATICMETHOD; + else if (PyNames.STATICMETHOD.equals(wrapper_name)) wrappedModifier = PyFunction.Modifier.STATICMETHOD; } } if (resolved instanceof Callable) { - Set flags = resolved instanceof PyFunction - ? PyUtil.detectDecorationsAndWrappersOf((PyFunction) resolved) - : EnumSet.noneOf(PyFunction.Flag.class); - if (wrappedFlag != null) { - flags.add(wrappedFlag); + PyFunction.Modifier modifier = resolved instanceof PyFunction + ? ((PyFunction)resolved).getModifier() + : null; + if (modifier == null && wrappedModifier != null) { + modifier = wrappedModifier; } List qualifiers = resolveResult != null ? resolveResult.getQualifiers() : Collections.emptyList(); boolean isByInstance = isConstructorCall || @@ -187,16 +187,16 @@ public class PyCallExpressionHelper { boolean isByClass = lastQualifier == null ? false : isQualifiedByClass((Callable)resolved, lastQualifier, resolveContext.getTypeEvalContext()); final Callable callable = (Callable)resolved; - implicitOffset += getImplicitArgumentCount(callable, flags, isConstructorCall, isByInstance, isByClass); + implicitOffset += getImplicitArgumentCount(callable, modifier, isConstructorCall, isByInstance, isByClass); implicitOffset = implicitOffset < 0? 0: implicitOffset; // wrong source can trigger strange behaviour - return new PyCallExpression.PyMarkedCallee(callable, flags, implicitOffset, + return new PyCallExpression.PyMarkedCallee(callable, modifier, implicitOffset, resolveResult != null ? resolveResult.isImplicit() : false); } return null; } /** - * Calls the {@link #getImplicitArgumentCount(PyExpression, Callable, PyFunction.Flag, EnumSet, boolean) full version} + * Calls the {@link #getImplicitArgumentCount(PyExpression, Callable, com.jetbrains.python.psi.PyFunction.Modifier, EnumSet< com.jetbrains.python.psi.PyFunction.Modifier >, boolean) full version} * with null flags and with isByInstance inferred directly from call site (won't work with reassigned bound methods). * * @param callReference the call site, where arguments are given. @@ -215,7 +215,7 @@ public class PyCallExpressionHelper { QualifiedResolveResult followed = callReference.followAssignmentsChain(resolveContext); boolean isByInstance = isQualifiedByInstance(functionBeingCalled, followed.getQualifiers(), resolveContext.getTypeEvalContext()); boolean isByClass = isQualifiedByInstance(functionBeingCalled, followed.getQualifiers(), resolveContext.getTypeEvalContext()); - return getImplicitArgumentCount(functionBeingCalled, PyUtil.detectDecorationsAndWrappersOf(functionBeingCalled), false, isByInstance, isByClass); + return getImplicitArgumentCount(functionBeingCalled, functionBeingCalled.getModifier(), false, isByInstance, isByClass); } /** @@ -229,7 +229,7 @@ public class PyCallExpressionHelper { */ private static int getImplicitArgumentCount( Callable callable, - Set flags, + PyFunction.Modifier modifier, boolean isConstructorCall, boolean isByInstance, boolean isByClass @@ -247,10 +247,10 @@ public class PyCallExpressionHelper { } // decorators? - if (flags.contains(PyFunction.Flag.STATICMETHOD)) { + if (modifier == PyFunction.Modifier.STATICMETHOD) { if (isByInstance && implicit_offset > 0) implicit_offset -= 1; // might have marked it as implicit 'self' } - else if (flags.contains(PyFunction.Flag.CLASSMETHOD)) { + else if (modifier == PyFunction.Modifier.CLASSMETHOD) { if (!isByInstance) implicit_offset += 1; // Both Foo.method() and foo.method() have implicit the first arg } return implicit_offset; diff --git a/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java b/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java index d123a6a0486f..fe444fc6b4af 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java @@ -119,7 +119,7 @@ public class PyDecoratorImpl extends PyPresentableElementImpl i if (callee == null) return null; if (!hasArgumentList()) { // NOTE: that +1 thing looks fishy - callee = new PyMarkedCallee(callee.getCallable(), callee.getFlags(), callee.getImplicitOffset() + 1, callee.isImplicitlyResolved()); + callee = new PyMarkedCallee(callee.getCallable(), callee.getModifier(), callee.getImplicitOffset() + 1, callee.isImplicitlyResolved()); } return callee; } diff --git a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java index ccf5a72267b1..fa0452ee37c6 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java @@ -2,6 +2,7 @@ package com.jetbrains.python.psi.impl; import com.intellij.lang.ASTNode; import com.intellij.openapi.extensions.Extensions; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; @@ -25,6 +26,7 @@ import com.jetbrains.python.documentation.StructuredDocString; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.stubs.PyClassStub; import com.jetbrains.python.psi.stubs.PyFunctionStub; +import com.jetbrains.python.psi.stubs.PyTargetExpressionStub; import com.jetbrains.python.psi.types.*; import com.jetbrains.python.sdk.PythonSdkType; import org.jetbrains.annotations.NotNull; @@ -33,6 +35,10 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.*; +import static com.jetbrains.python.psi.PyFunction.Modifier.CLASSMETHOD; +import static com.jetbrains.python.psi.PyFunction.Modifier.STATICMETHOD; +import static com.jetbrains.python.psi.impl.PyCallExpressionHelper.interpretAsStaticmethodOrClassmethodWrappingCall; + /** * Implements PyFunction. */ @@ -416,4 +422,115 @@ public class PyFunctionImpl extends PyPresentableElementImpl imp } return super.getUseScope(); } + + /** + * Looks for two standard decorators to a function, or a wrapping assignment that closely follows it. + * + * @return a flag describing what was detected. + */ + @Nullable + public Modifier getModifier() { + String deconame = getClassOrStaticMethodDecorator(); + if (PyNames.CLASSMETHOD.equals(deconame)) { + return CLASSMETHOD; + } + else if (PyNames.STATICMETHOD.equals(deconame)) { + return STATICMETHOD; + } + // implicit staticmethod __new__ + PyClass cls = getContainingClass(); + if (cls != null && PyNames.NEW.equals(getName()) && cls.isNewStyleClass()) { + return STATICMETHOD; + } + // + if (getStub() != null) { + return getWrappersFromStub(); + } + String func_name = getName(); + if (func_name != null) { + PyAssignmentStatement assignment = PsiTreeUtil.getNextSiblingOfType(this, PyAssignmentStatement.class); + if (assignment != null) { + for (Pair pair : assignment.getTargetsToValuesMapping()) { + PyExpression value = pair.getSecond(); + if (value instanceof PyCallExpression) { + PyExpression target = pair.getFirst(); + if (target instanceof PyTargetExpression && func_name.equals(target.getName())) { + Pair interpreted = interpretAsStaticmethodOrClassmethodWrappingCall((PyCallExpression)value, this); + if (interpreted != null) { + PyFunction original = interpreted.getSecond(); + if (original == this) { + String wrapper_name = interpreted.getFirst(); + if (PyNames.CLASSMETHOD.equals(wrapper_name)) { + return CLASSMETHOD; + } + else if (PyNames.STATICMETHOD.equals(wrapper_name)) { + return STATICMETHOD; + } + } + } + } + } + } + } + } + return null; + } + + @Nullable + private Modifier getWrappersFromStub() { + final StubElement parentStub = getStub().getParentStub(); + final List childrenStubs = parentStub.getChildrenStubs(); + int index = childrenStubs.indexOf(getStub()); + if (index >= 0 && index < childrenStubs.size() - 1) { + StubElement nextStub = (StubElement) childrenStubs.get(index+1); + if (nextStub instanceof PyTargetExpressionStub) { + final PyTargetExpressionStub targetExpressionStub = (PyTargetExpressionStub)nextStub; + if (targetExpressionStub.getInitializerType() == PyTargetExpressionStub.InitializerType.CallExpression) { + final PyQualifiedName qualifiedName = targetExpressionStub.getInitializer(); + if (PyQualifiedName.fromComponents(PyNames.CLASSMETHOD).equals(qualifiedName)) { + return Modifier.CLASSMETHOD; + } + if (PyQualifiedName.fromComponents(PyNames.STATICMETHOD).equals(qualifiedName)) { + return Modifier.STATICMETHOD; + } + } + } + } + return null; + } + + /** + * When a function is decorated many decorators, finds the deepest builtin decorator: + *
+   * @foo
+   * @classmethod # <-- that's it
+   * @bar
+   * def moo(cls):
+   *   pass
+   * 
+ * @return name of the built-in decorator, or null (even if there are non-built-in decorators). + */ + @Nullable + private String getClassOrStaticMethodDecorator() { + PyDecoratorList decolist = getDecoratorList(); + if (decolist != null) { + PyDecorator[] decos = decolist.getDecorators(); + if (decos.length > 0) { + for (int i = decos.length - 1; i >= 0; i -= 1) { + PyDecorator deco = decos[i]; + String deconame = deco.getName(); + if (PyNames.CLASSMETHOD.equals(deconame) || PyNames.STATICMETHOD.equals(deconame)) { + return deconame; + } + for(PyKnownDecoratorProvider provider: PyUtil.KnownDecoratorProviderHolder.KNOWN_DECORATOR_PROVIDERS) { + String name = provider.toKnownDecorator(deconame); + if (name != null) { + return name; + } + } + } + } + } + return null; + } } diff --git a/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java b/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java index b6fed8d9a065..589a7a541035 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java @@ -24,7 +24,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import java.util.Set; /** * @author yole @@ -157,12 +156,12 @@ public class PyNamedParameterImpl extends PyPresentableElementImpl flags = PyUtil.detectDecorationsAndWrappersOf(func); - if (params [0] == this && !flags.contains(PyFunction.Flag.STATICMETHOD)) { + final PyFunction.Modifier modifier = func.getModifier(); + if (params [0] == this && modifier != PyFunction.Modifier.STATICMETHOD) { // must be 'self' or 'cls' final PyClass containingClass = func.getContainingClass(); if (containingClass != null) { - return new PyClassType(containingClass, flags.contains(PyFunction.Flag.CLASSMETHOD)); + return new PyClassType(containingClass, modifier == PyFunction.Modifier.CLASSMETHOD); } } if (isKeywordContainer()) { diff --git a/python/src/com/jetbrains/python/refactoring/introduce/field/PyIntroduceFieldHandler.java b/python/src/com/jetbrains/python/refactoring/introduce/field/PyIntroduceFieldHandler.java index 1a3547657246..9b2181b05aea 100644 --- a/python/src/com/jetbrains/python/refactoring/introduce/field/PyIntroduceFieldHandler.java +++ b/python/src/com/jetbrains/python/refactoring/introduce/field/PyIntroduceFieldHandler.java @@ -122,8 +122,8 @@ public class PyIntroduceFieldHandler extends IntroduceHandler { final PyFunction function = (PyFunction)myScope; final PyParameter[] parameters = function.getParameterList().getParameters(); if (parameters.length > 0 && result == parameters[0]) { - final Set flags = PyUtil.detectDecorationsAndWrappersOf(function); - if (!(flags.contains(PyFunction.Flag.STATICMETHOD))) { + final PyFunction.Modifier modifier = function.getModifier(); + if (modifier != PyFunction.Modifier.STATICMETHOD) { // 'self' is not a local scope dependency return; } @@ -247,8 +247,8 @@ public class PyIntroduceFieldHandler extends IntroduceHandler { private static boolean isInStaticMethod(PsiElement element) { PyFunction containingMethod = PsiTreeUtil.getParentOfType(element, PyFunction.class, false, PyClass.class); if (containingMethod != null) { - final Set flags = PyUtil.detectDecorationsAndWrappersOf(containingMethod); - return flags.contains(PyFunction.Flag.STATICMETHOD); + final PyFunction.Modifier modifier = containingMethod.getModifier(); + return modifier == PyFunction.Modifier.STATICMETHOD; } return false; } diff --git a/python/src/com/jetbrains/python/validation/HighlightingAnnotator.java b/python/src/com/jetbrains/python/validation/HighlightingAnnotator.java index 4dbe62cf561a..16009aad9984 100644 --- a/python/src/com/jetbrains/python/validation/HighlightingAnnotator.java +++ b/python/src/com/jetbrains/python/validation/HighlightingAnnotator.java @@ -6,8 +6,6 @@ import com.intellij.util.ArrayUtil; import com.jetbrains.python.highlighting.PyHighlighter; import com.jetbrains.python.psi.*; -import java.util.Set; - /** * @author yole */ @@ -39,8 +37,8 @@ public class HighlightingAnnotator extends PyAnnotator { boolean isSelf = false; final int index = ArrayUtil.find(function.getParameterList().getParameters(), node); if (function.getContainingClass() != null && index == 0) { - final Set flags = PyUtil.detectDecorationsAndWrappersOf(function); - if (!flags.contains(PyFunction.Flag.CLASSMETHOD) && !flags.contains(PyFunction.Flag.STATICMETHOD)) { + final PyFunction.Modifier modifier = function.getModifier(); + if (modifier != PyFunction.Modifier.CLASSMETHOD && modifier != PyFunction.Modifier.STATICMETHOD) { isSelf = true; } } diff --git a/python/testSrc/com/jetbrains/python/PyResolveCalleeTest.java b/python/testSrc/com/jetbrains/python/PyResolveCalleeTest.java index 5fc6feac9798..11f591c8b9b4 100644 --- a/python/testSrc/com/jetbrains/python/PyResolveCalleeTest.java +++ b/python/testSrc/com/jetbrains/python/PyResolveCalleeTest.java @@ -7,8 +7,6 @@ import com.jetbrains.python.psi.PyCallExpression; import com.jetbrains.python.psi.PyFunction; import com.jetbrains.python.psi.resolve.PyResolveContext; -import java.util.EnumSet; - /** * Tests callee resolution in PyCallExpressionImpl. * User: dcheryasov @@ -31,7 +29,7 @@ public class PyResolveCalleeTest extends PyTestCase { public void testClassCall() { PyCallExpression.PyMarkedCallee resolved = resolveCallee(); assertNotNull(resolved.getCallable()); - assertTrue(resolved.getFlags().equals(EnumSet.noneOf(PyFunction.Flag.class))); + assertEquals(null, resolved.getModifier()); } public void testDecoCall() { @@ -43,13 +41,13 @@ public class PyResolveCalleeTest extends PyTestCase { public void testDecoParamCall() { PyCallExpression.PyMarkedCallee resolved = resolveCallee(); assertNotNull(resolved.getCallable()); - assertTrue(resolved.getFlags().equals(EnumSet.noneOf(PyFunction.Flag.class))); + assertNull(resolved.getModifier()); } public void testWrappedStaticMethod() { PyCallExpression.PyMarkedCallee resolved = resolveCallee(); assertNotNull(resolved.getCallable()); assertEquals(0, resolved.getImplicitOffset()); - assertEquals(resolved.getFlags(), EnumSet.of(PyFunction.Flag.STATICMETHOD)); + assertEquals(resolved.getModifier(), PyFunction.Modifier.STATICMETHOD); } } diff --git a/python/testSrc/com/jetbrains/python/PyStubsTest.java b/python/testSrc/com/jetbrains/python/PyStubsTest.java index 7c069f4cc23d..2abfbea5891c 100644 --- a/python/testSrc/com/jetbrains/python/PyStubsTest.java +++ b/python/testSrc/com/jetbrains/python/PyStubsTest.java @@ -21,9 +21,7 @@ import com.jetbrains.python.psi.stubs.PyVariableNameIndex; import com.jetbrains.python.toolbox.Maybe; import java.util.Collection; -import java.util.EnumSet; import java.util.List; -import java.util.Set; /** * @author max @@ -335,8 +333,8 @@ public class PyStubsTest extends PyTestCase { final PyClass pyClass = file.getTopLevelClasses().get(0); final PyFunction[] methods = pyClass.getMethods(); assertEquals(1, methods.length); - final Set flags = PyUtil.detectDecorationsAndWrappersOf(methods[0]); - assertEquals(EnumSet.of(PyFunction.Flag.STATICMETHOD), flags); + final PyFunction.Modifier modifier = methods[0].getModifier(); + assertEquals(PyFunction.Modifier.STATICMETHOD, modifier); assertNotParsed(file); } From afb47cce55ae0c176b78c6e6f32ce12fe9424c23 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 14 Dec 2011 18:39:51 +0100 Subject: [PATCH 14/35] cleanup --- .../psi/impl/PyStarImportElementImpl.java | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/python/src/com/jetbrains/python/psi/impl/PyStarImportElementImpl.java b/python/src/com/jetbrains/python/psi/impl/PyStarImportElementImpl.java index 32e16b817902..f0c96b4a1ac5 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyStarImportElementImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyStarImportElementImpl.java @@ -2,7 +2,6 @@ package com.jetbrains.python.psi.impl; import com.intellij.lang.ASTNode; import com.intellij.navigation.ItemPresentation; -import com.intellij.openapi.editor.colors.TextAttributesKey; import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.containers.HashSet; @@ -29,9 +28,9 @@ public class PyStarImportElementImpl extends PyElementImpl implements PyStarImpo @NotNull public Iterable iterateNames() { if (getParent() instanceof PyFromImportStatement) { - PyFromImportStatement import_from_stmt = (PyFromImportStatement)getParent(); - PyReferenceExpression from_src = import_from_stmt.getImportSource(); - final List importedFiles = ResolveImportUtil.resolveImportReference(from_src); + PyFromImportStatement fromImportStatement = (PyFromImportStatement)getParent(); + final List importedFiles = ResolveImportUtil.resolveFromOrForeignImport(fromImportStatement, + fromImportStatement.getImportSourceQName()); ChainIterable chain = new ChainIterable(); for (PsiElement importedFile : new HashSet(importedFiles)) { // resolver gives lots of duplicates final PsiElement source = PyUtil.turnDirIntoInit(importedFile); @@ -50,9 +49,9 @@ public class PyStarImportElementImpl extends PyElementImpl implements PyStarImpo return null; } if (getParent() instanceof PyFromImportStatement) { - PyFromImportStatement import_from_stmt = (PyFromImportStatement)getParent(); - PyReferenceExpression from_src = import_from_stmt.getImportSource(); - final List importedFiles = ResolveImportUtil.resolveImportReference(from_src); + PyFromImportStatement fromImportStatement = (PyFromImportStatement)getParent(); + final List importedFiles = ResolveImportUtil.resolveFromOrForeignImport(fromImportStatement, + fromImportStatement.getImportSourceQName()); for (PsiElement importedFile : new HashSet(importedFiles)) { // resolver gives lots of duplicates final PsiElement source = PyUtil.turnDirIntoInit(importedFile); if (source instanceof PyFile) { @@ -80,9 +79,9 @@ public class PyStarImportElementImpl extends PyElementImpl implements PyStarImpo return new ItemPresentation() { private String getName() { - PyElement elt = PsiTreeUtil.getParentOfType(PyStarImportElementImpl.this, PyFromImportStatement.class); - if (elt instanceof PyFromImportStatement) { // always? who knows :) - PyReferenceExpression imp_src = ((PyFromImportStatement)elt).getImportSource(); + PyFromImportStatement elt = PsiTreeUtil.getParentOfType(PyStarImportElementImpl.this, PyFromImportStatement.class); + if (elt != null) { // always? who knows :) + PyReferenceExpression imp_src = elt.getImportSource(); if (imp_src != null) { return PyResolveUtil.toPath(imp_src, "."); } @@ -95,7 +94,7 @@ public class PyStarImportElementImpl extends PyElementImpl implements PyStarImpo } public String getLocationString() { - StringBuffer buf = new StringBuffer("| "); + StringBuilder buf = new StringBuilder("| "); buf.append("from ").append(getName()).append(" import *"); return buf.toString(); } From ba9c13d0644f02cecc8dc3f0d73acfa29de6e1c7 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 14 Dec 2011 18:47:17 +0100 Subject: [PATCH 15/35] cleanup --- .../ImportFromToImportIntention.java | 6 +- .../ImportToImportFromIntention.java | 6 +- .../ImportToggleAliasIntention.java | 4 +- .../python/psi/impl/PyImportElementImpl.java | 2 +- .../psi/impl/PyStarImportElementImpl.java | 2 +- .../python/psi/resolve/PyResolveUtil.java | 56 ++++--------------- 6 files changed, 21 insertions(+), 55 deletions(-) diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/ImportFromToImportIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/ImportFromToImportIntention.java index 51e4c2c22de5..02d8c4111efa 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/ImportFromToImportIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/ImportFromToImportIntention.java @@ -72,7 +72,7 @@ public class ImportFromToImportIntention implements IntentionAction { ret.myModuleReference = ret.myFromImportStatement.getImportSource(); } if (ret.myModuleReference != null) { - ret.myModuleName = PyResolveUtil.toPath(ret.myModuleReference, "."); + ret.myModuleName = PyResolveUtil.toPath(ret.myModuleReference); } return ret; } @@ -106,7 +106,7 @@ public class ImportFromToImportIntention implements IntentionAction { if (info.myModuleReference != null) { PyExpression remaining_module = info.myModuleReference.getQualifier(); if (remaining_module instanceof PyQualifiedExpression) { - remaining_name = PyResolveUtil.toPath((PyQualifiedExpression)remaining_module, "."); + remaining_name = PyResolveUtil.toPath((PyQualifiedExpression)remaining_module); } else remaining_name = ""; // unqualified name: "...module" separated_name = info.myModuleReference.getReferencedName(); @@ -152,7 +152,7 @@ public class ImportFromToImportIntention implements IntentionAction { } } if (info.myModuleReference != null) { - info.myModuleName = PyResolveUtil.toPath(info.myModuleReference, "."); + info.myModuleName = PyResolveUtil.toPath(info.myModuleReference); } if (info.myModuleReference != null && info.myModuleName != null && info.myFromImportStatement != null) { myText = info.getText(); diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/ImportToImportFromIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/ImportToImportFromIntention.java index ca041cd159e0..eef94956d87e 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/ImportToImportFromIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/ImportToImportFromIntention.java @@ -79,7 +79,7 @@ public class ImportToImportFromIntention implements IntentionAction { // usages of imported name are qualifiers; what they refer to? PyReferenceExpression reference = myImportElement.getImportReference(); if (reference != null) { - myModuleName = PyResolveUtil.toPath(reference, "."); + myModuleName = PyResolveUtil.toPath(reference); myQualifierName = myImportElement.getVisibleName(); myReferee = reference.getReference().resolve(); myHasModuleReference = false; @@ -89,7 +89,7 @@ public class ImportToImportFromIntention implements IntentionAction { public boolean execute(@NotNull PsiElement element) { if (element instanceof PyReferenceExpression && PsiTreeUtil.getParentOfType(element, PyImportElement.class) == null) { PyReferenceExpression ref = (PyReferenceExpression)element; - if (myQualifierName.equals(PyResolveUtil.toPath(ref, "."))) { // filter out other names that might resolve to our target + if (myQualifierName.equals(PyResolveUtil.toPath(ref))) { // filter out other names that might resolve to our target PsiElement parent_elt = ref.getParent(); if (parent_elt instanceof PyQualifiedExpression) { // really qualified by us, not just referencing? PsiElement resolved = ref.getReference().resolve(); @@ -182,7 +182,7 @@ public class ImportToImportFromIntention implements IntentionAction { String module_name = "?"; if (myImportElement != null) { PyReferenceExpression reference = myImportElement.getImportReference(); - if (reference != null) module_name = PyResolveUtil.toPath(reference, "."); + if (reference != null) module_name = PyResolveUtil.toPath(reference); } return PyBundle.message("INTN.convert.to.from.$0.import.$1", getDots()+module_name, "..."); } diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/ImportToggleAliasIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/ImportToggleAliasIntention.java index 184e71e284e6..1ec789b6a42f 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/ImportToggleAliasIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/ImportToggleAliasIntention.java @@ -111,7 +111,7 @@ public class ImportToggleAliasIntention implements IntentionAction { PyReferenceExpression reference = sure(state.myImportElement.getImportReference()); // search for references to us with the right name try { - String imported_name = PyResolveUtil.toPath(reference, "."); + String imported_name = PyResolveUtil.toPath(reference); if (state.myAlias != null) { // have to remove alias, rename everything to original target_name = imported_name; @@ -150,7 +150,7 @@ public class ImportToggleAliasIntention implements IntentionAction { public boolean execute(@NotNull PsiElement element) { if (element instanceof PyReferenceExpression && PsiTreeUtil.getParentOfType(element, PyImportElement.class) == null) { PyReferenceExpression ref = (PyReferenceExpression)element; - if (remove_name.equals(PyResolveUtil.toPath(ref, "."))) { // filter out other names that might resolve to our target + if (remove_name.equals(PyResolveUtil.toPath(ref))) { // filter out other names that might resolve to our target PsiElement resolved = ref.getReference().resolve(); if (resolved == referee) references.add(ref.getReference()); } diff --git a/python/src/com/jetbrains/python/psi/impl/PyImportElementImpl.java b/python/src/com/jetbrains/python/psi/impl/PyImportElementImpl.java index 4973c5469e47..4bc70c36a76e 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyImportElementImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyImportElementImpl.java @@ -156,7 +156,7 @@ public class PyImportElementImpl extends PyBaseElementImpl buf.append("from "); PyReferenceExpression imp_src = ((PyFromImportStatement)elt).getImportSource(); if (imp_src != null) { - buf.append(PyResolveUtil.toPath(imp_src, ".")); + buf.append(PyResolveUtil.toPath(imp_src)); } else { buf.append(""); diff --git a/python/src/com/jetbrains/python/psi/impl/PyStarImportElementImpl.java b/python/src/com/jetbrains/python/psi/impl/PyStarImportElementImpl.java index f0c96b4a1ac5..c8d4e4fab387 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyStarImportElementImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyStarImportElementImpl.java @@ -83,7 +83,7 @@ public class PyStarImportElementImpl extends PyElementImpl implements PyStarImpo if (elt != null) { // always? who knows :) PyReferenceExpression imp_src = elt.getImportSource(); if (imp_src != null) { - return PyResolveUtil.toPath(imp_src, "."); + return PyResolveUtil.toPath(imp_src); } } return ""; diff --git a/python/src/com/jetbrains/python/psi/resolve/PyResolveUtil.java b/python/src/com/jetbrains/python/psi/resolve/PyResolveUtil.java index ce561f20aab1..f4de9fe4ff9f 100644 --- a/python/src/com/jetbrains/python/psi/resolve/PyResolveUtil.java +++ b/python/src/com/jetbrains/python/psi/resolve/PyResolveUtil.java @@ -15,6 +15,7 @@ import com.intellij.psi.util.PsiTreeUtil; import com.jetbrains.python.PyElementTypes; import com.jetbrains.python.PythonDialectsTokenSetProvider; import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.impl.PyQualifiedName; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -233,33 +234,6 @@ public class PyResolveUtil { } - /** - * Tries to match two [qualified] reference expression paths by names; target must be a 'sublist' of source to match. - * E.g., 'a.b.c.d' and 'a.b.c' would match, while 'a.b.c' and 'a.b.c.d' would not. Eqaully, 'a.b.c' and 'a.b.d' would not match. - * If either source or target is null, false is returned. - * - * @param source_path expression path to match (the longer list of qualifiers). - * @param target_path expression path to match against (hopeful sublist of qualifiers of source). - * @return true if source matches target. - * @see #unwindQualifiers(PyQualifiedExpression) . - */ - public static boolean pathsMatch(List source_path, List target_path) { - // turn qualifiers into lists - if ((source_path == null) || (target_path == null)) return false; - // compare until target is exhausted - Iterator source_iter = source_path.iterator(); - for (final T target_elt : target_path) { - if (source_iter.hasNext()) { - S source_elt = source_iter.next(); - if (!target_elt.getText().equals(source_elt.getText())) return false; - } - else { - return false; - } // source exhausted before target - } - return true; - } - public static boolean pathsMatchStr(List source_path, List target_path) { // turn qualifiers into lists if ((source_path == null) || (target_path == null)) return false; @@ -280,9 +254,10 @@ public class PyResolveUtil { /** * Unwinds a multi-level qualified expression into a path, as seen in source text, i.e. outermost qualifier first. * - * @param expr an experssion to unwind. + * @param expr an expression to unwind. * @return path as a list of ref expressions. */ + @NotNull public static List unwindQualifiers(final PyQualifiedExpression expr) { final List path = new LinkedList(); PyQualifiedExpression e = expr; @@ -305,27 +280,18 @@ public class PyResolveUtil { return path; } - public static String toPath(PyQualifiedExpression expr, String separator) { + public static String toPath(PyQualifiedExpression expr) { if (expr == null) return ""; List path = unwindQualifiers(expr); - if (path != null) { - StringBuilder buf = new StringBuilder(); - boolean is_not_first = false; - for (PyExpression ex : path) { - if (is_not_first) { - buf.append(separator); - } - else { - is_not_first = true; - } - buf.append(ex.getName()); - } - return buf.toString(); + final PyQualifiedName qName = PyQualifiedName.fromReferenceChain(path); + if (qName != null) { + return qName.toString(); } - else { - String s = expr.getName(); - return s != null ? s : ""; + String name = expr.getName(); + if (name != null) { + return name; } + return ""; } /** From 270e99c5f241c5040898638e4dc8bf408e18145f Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 14 Dec 2011 18:57:08 +0100 Subject: [PATCH 16/35] cleanup --- .../psi/impl/PyQualifiedReferenceImpl.java | 6 ++-- .../resolve/AssignmentCollectProcessor.java | 35 ++++++++++--------- .../python/psi/resolve/PyResolveUtil.java | 19 ---------- 3 files changed, 21 insertions(+), 39 deletions(-) diff --git a/python/src/com/jetbrains/python/psi/impl/PyQualifiedReferenceImpl.java b/python/src/com/jetbrains/python/psi/impl/PyQualifiedReferenceImpl.java index 6c01a18db934..3c73ae63ba46 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyQualifiedReferenceImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyQualifiedReferenceImpl.java @@ -344,9 +344,9 @@ public class PyQualifiedReferenceImpl extends PyReferenceImpl { } private static Collection collectAssignedAttributes(PyQualifiedExpression qualifier) { - List qualifier_path = PyResolveUtil.unwindQualifiersAsStrList(qualifier); - if (qualifier_path != null) { - AssignmentCollectProcessor proc = new AssignmentCollectProcessor(qualifier_path); + PyQualifiedName qualifierPath = PyQualifiedName.fromReferenceChain(PyResolveUtil.unwindQualifiers(qualifier)); + if (qualifierPath != null) { + AssignmentCollectProcessor proc = new AssignmentCollectProcessor(qualifierPath); PyResolveUtil.treeCrawlUp(proc, qualifier); return proc.getResult(); } diff --git a/python/src/com/jetbrains/python/psi/resolve/AssignmentCollectProcessor.java b/python/src/com/jetbrains/python/psi/resolve/AssignmentCollectProcessor.java index 1db3fcdad1d2..c3a17179a110 100644 --- a/python/src/com/jetbrains/python/psi/resolve/AssignmentCollectProcessor.java +++ b/python/src/com/jetbrains/python/psi/resolve/AssignmentCollectProcessor.java @@ -7,6 +7,7 @@ import com.intellij.psi.scope.PsiScopeProcessor; import com.jetbrains.python.psi.PyAssignmentStatement; import com.jetbrains.python.psi.PyExpression; import com.jetbrains.python.psi.PyTargetExpression; +import com.jetbrains.python.psi.impl.PyQualifiedName; import org.jetbrains.annotations.NotNull; import java.util.*; @@ -16,9 +17,9 @@ public class AssignmentCollectProcessor implements PsiScopeProcessor { * Collects all assignments in context above given element, if they match given naming pattern. * Used to track creation of attributes by assignment (e.g in constructor). */ - List my_qualifier; - List my_result; - Set my_seen_names; + private final PyQualifiedName myQualifier; + private final List myResult; + private final Set mySeenNames; /** * Creates an instance to collect assignments of attributes to the object identified by 'qualifier'. @@ -28,11 +29,11 @@ public class AssignmentCollectProcessor implements PsiScopeProcessor { * * @param qualifier qualifying names, outermost first; must not be empty. */ - public AssignmentCollectProcessor(@NotNull List qualifier) { - assert qualifier.size() > 0; - my_qualifier = qualifier; - my_result = new ArrayList(); - my_seen_names = new HashSet(); + public AssignmentCollectProcessor(@NotNull PyQualifiedName qualifier) { + assert qualifier.getComponentCount() > 0; + myQualifier = qualifier; + myResult = new ArrayList(); + mySeenNames = new HashSet(); } public boolean execute(final PsiElement element, final ResolveState state) { @@ -41,19 +42,19 @@ public class AssignmentCollectProcessor implements PsiScopeProcessor { for (PyExpression ex : assignment.getTargets()) { if (ex instanceof PyTargetExpression) { final PyTargetExpression target = (PyTargetExpression)ex; - List quals = PyResolveUtil.unwindQualifiersAsStrList(target); List qualsExpr = PyResolveUtil.unwindQualifiers(target); - if (quals != null) { - if (quals.size() == my_qualifier.size() + 1 && PyResolveUtil.pathsMatchStr(quals, my_qualifier)) { + PyQualifiedName qualifiedName = PyQualifiedName.fromReferenceChain(qualsExpr); + if (qualifiedName != null) { + if (qualifiedName.getComponentCount() == myQualifier.getComponentCount() + 1 && qualifiedName.matchesPrefix(myQualifier)) { // a new attribute follows last qualifier; collect it. PyExpression last_elt = qualsExpr.get(qualsExpr.size() - 1); // last item is the outermost, new, attribute. String last_elt_name = last_elt.getName(); - if (!my_seen_names.contains(last_elt_name)) { // no dupes, only remember the latest - my_result.add(last_elt); - my_seen_names.add(last_elt_name); + if (!mySeenNames.contains(last_elt_name)) { // no dupes, only remember the latest + myResult.add(last_elt); + mySeenNames.add(last_elt_name); } } - else if (quals.size() < my_qualifier.size() + 1 && PyResolveUtil.pathsMatchStr(my_qualifier, quals)) { + else if (qualifiedName.getComponentCount() < myQualifier.getComponentCount() + 1 && myQualifier.matchesPrefix(qualifiedName)) { // qualifier(s) get redefined; collect no more. return false; } @@ -66,12 +67,12 @@ public class AssignmentCollectProcessor implements PsiScopeProcessor { } /** - * @return a collection of exressions (parts of assignment expressions) where new attributes were defined. E.g. for "a.b.c = 1", + * @return a collection of expressions (parts of assignment expressions) where new attributes were defined. E.g. for "a.b.c = 1", * the expression for 'c' is in the result. */ @NotNull public Collection getResult() { - return my_result; + return myResult; } public T getHint(final Key hintKey) { diff --git a/python/src/com/jetbrains/python/psi/resolve/PyResolveUtil.java b/python/src/com/jetbrains/python/psi/resolve/PyResolveUtil.java index f4de9fe4ff9f..9df00aedcbf6 100644 --- a/python/src/com/jetbrains/python/psi/resolve/PyResolveUtil.java +++ b/python/src/com/jetbrains/python/psi/resolve/PyResolveUtil.java @@ -20,7 +20,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -233,24 +232,6 @@ public class PyResolveUtil { return treeCrawlUp(processor, false, elt); } - - public static boolean pathsMatchStr(List source_path, List target_path) { - // turn qualifiers into lists - if ((source_path == null) || (target_path == null)) return false; - // compare until target is exhausted - Iterator source_iter = source_path.iterator(); - for (final String target_elt : target_path) { - if (source_iter.hasNext()) { - String source_elt = source_iter.next(); - if (!target_elt.equals(source_elt)) return false; - } - else { - return false; - } // source exhausted before target - } - return true; - } - /** * Unwinds a multi-level qualified expression into a path, as seen in source text, i.e. outermost qualifier first. * From a6cc4be630674f981b6b285a25da68e7b08be0fe Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 14 Dec 2011 19:01:47 +0100 Subject: [PATCH 17/35] getImportReference() -> getImportReferenceExpression() --- .../python/actions/ReplaceBuiltinsQuickFix.java | 2 +- .../codeInsight/imports/PythonReferenceImporter.java | 4 ++-- .../intentions/ImportFromToImportIntention.java | 4 ++-- .../intentions/ImportToImportFromIntention.java | 6 +++--- .../intentions/ImportToggleAliasIntention.java | 6 +++--- .../src/com/jetbrains/python/psi/PyImportElement.java | 2 +- .../jetbrains/python/psi/impl/PyImportElementImpl.java | 10 +++++----- .../python/psi/impl/PyImportReferenceImpl.java | 2 +- .../python/psi/impl/PyImportStatementNavigator.java | 3 +-- 9 files changed, 19 insertions(+), 20 deletions(-) diff --git a/python/src/com/jetbrains/python/actions/ReplaceBuiltinsQuickFix.java b/python/src/com/jetbrains/python/actions/ReplaceBuiltinsQuickFix.java index b3bd943835fe..d9187a1f3aae 100644 --- a/python/src/com/jetbrains/python/actions/ReplaceBuiltinsQuickFix.java +++ b/python/src/com/jetbrains/python/actions/ReplaceBuiltinsQuickFix.java @@ -32,7 +32,7 @@ public class ReplaceBuiltinsQuickFix implements LocalQuickFix { PsiElement importStatement = descriptor.getPsiElement(); if (importStatement instanceof PyImportStatement) { for (PyImportElement importElement : ((PyImportStatement)importStatement).getImportElements()) { - PyReferenceExpression importReference = importElement.getImportReference(); + PyReferenceExpression importReference = importElement.getImportReferenceExpression(); if (importReference != null) { if ("__builtin__".equals(importReference.getName())) { importReference.replace(elementGenerator.createFromText(LanguageLevel.getDefault(), PyReferenceExpression.class, "builtins")); diff --git a/python/src/com/jetbrains/python/codeInsight/imports/PythonReferenceImporter.java b/python/src/com/jetbrains/python/codeInsight/imports/PythonReferenceImporter.java index 4f0c0df3dc78..d7527c6e8c97 100644 --- a/python/src/com/jetbrains/python/codeInsight/imports/PythonReferenceImporter.java +++ b/python/src/com/jetbrains/python/codeInsight/imports/PythonReferenceImporter.java @@ -87,12 +87,12 @@ public class PythonReferenceImporter implements ReferenceImporter { if (result.size() > 0) { for (PsiElement stmt : import_prc.getResult()) { for (PyImportElement ielt : ((PyImportStatement)stmt).getImportElements()) { - final PyReferenceExpression src = ielt.getImportReference(); + final PyReferenceExpression src = ielt.getImportReferenceExpression(); if (src != null) { PsiElement dst = src.getReference().resolve(); if (dst instanceof PyFile) { PyFile dst_file = (PyFile)dst; - String name = ielt.getImportReference().getReferencedName(); // ref is ok or matching would fail + String name = ielt.getImportReferenceExpression().getReferencedName(); // ref is ok or matching would fail seen_file_names.add(name); PsiElement res = dst_file.findExportedName(refText); if (res != null && !(res instanceof PyFile) && !(res instanceof PyImportElement) && dst_file.equals(res.getContainingFile())) { diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/ImportFromToImportIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/ImportFromToImportIntention.java index 02d8c4111efa..0700bdad3bda 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/ImportFromToImportIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/ImportFromToImportIntention.java @@ -142,7 +142,7 @@ public class ImportFromToImportIntention implements IntentionAction { if (info.myRelativeLevel > 0) { // make sure we aren't importing a module from the relative path for (PyImportElement import_element : info.myFromImportStatement.getImportElements()) { - PyReferenceExpression ref = import_element.getImportReference(); + PyReferenceExpression ref = import_element.getImportReferenceExpression(); if (ref != null && ref.isValid()) { PsiElement target = ref.getReference().resolve(); if (target instanceof PyExpression && ((PyExpression)target).getType(TypeEvalContext.fast()) instanceof PyModuleType) return false; @@ -246,7 +246,7 @@ public class ImportFromToImportIntention implements IntentionAction { PyImportElement ielt = entry.getValue(); if (ielt.getAsNameElement() != null) { // we have an alias, replace it with real name - PyReferenceExpression refex = ielt.getImportReference(); + PyReferenceExpression refex = ielt.getImportReferenceExpression(); assert refex != null; // else we won't resolve to this ielt String real_name = refex.getReferencedName(); ASTNode new_qualifier = generator.createExpressionFromText(real_name).getNode(); diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/ImportToImportFromIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/ImportToImportFromIntention.java index eef94956d87e..7a7767b6d8b8 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/ImportToImportFromIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/ImportToImportFromIntention.java @@ -77,7 +77,7 @@ public class ImportToImportFromIntention implements IntentionAction { assert myImportElement != null : "isAvailable() must have returned true, but myImportElement is null"; // usages of imported name are qualifiers; what they refer to? - PyReferenceExpression reference = myImportElement.getImportReference(); + PyReferenceExpression reference = myImportElement.getImportReferenceExpression(); if (reference != null) { myModuleName = PyResolveUtil.toPath(reference); myQualifierName = myImportElement.getVisibleName(); @@ -108,7 +108,7 @@ public class ImportToImportFromIntention implements IntentionAction { public void invoke() { assert myImportElement != null : "isAvailable() must have returned true, but myImportElement is null"; - PyUtil.sure(myImportElement.getImportReference()); + PyUtil.sure(myImportElement.getImportReferenceExpression()); Project project = myImportElement.getProject(); // usages of imported name are qualifiers; what they refer to? @@ -181,7 +181,7 @@ public class ImportToImportFromIntention implements IntentionAction { public String getText() { String module_name = "?"; if (myImportElement != null) { - PyReferenceExpression reference = myImportElement.getImportReference(); + PyReferenceExpression reference = myImportElement.getImportReferenceExpression(); if (reference != null) module_name = PyResolveUtil.toPath(reference); } return PyBundle.message("INTN.convert.to.from.$0.import.$1", getDots()+module_name, "..."); diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/ImportToggleAliasIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/ImportToggleAliasIntention.java index 1ec789b6a42f..94b67f5d0283 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/ImportToggleAliasIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/ImportToggleAliasIntention.java @@ -64,7 +64,7 @@ public class ImportToggleAliasIntention implements IntentionAction { return false; } } - final PyReferenceExpression referenceExpression = myImportElement.getImportReference(); + final PyReferenceExpression referenceExpression = myImportElement.getImportReferenceExpression(); if (referenceExpression == null || referenceExpression.getReference().resolve() == null) { return false; } @@ -74,7 +74,7 @@ public class ImportToggleAliasIntention implements IntentionAction { public String getText() { String add_name = "Add alias"; if (myImportElement != null) { - PyReferenceExpression refex = myImportElement.getImportReference(); + PyReferenceExpression refex = myImportElement.getImportReferenceExpression(); if (refex != null) { add_name = PyBundle.message("INTN.add.alias.for.import.$0", refex.getText()); } @@ -108,7 +108,7 @@ public class ImportToggleAliasIntention implements IntentionAction { // final String target_name; // we set in in the source final String remove_name; // we replace it in the source - PyReferenceExpression reference = sure(state.myImportElement.getImportReference()); + PyReferenceExpression reference = sure(state.myImportElement.getImportReferenceExpression()); // search for references to us with the right name try { String imported_name = PyResolveUtil.toPath(reference); diff --git a/python/src/com/jetbrains/python/psi/PyImportElement.java b/python/src/com/jetbrains/python/psi/PyImportElement.java index 47601ac1a5fa..25fdb6f72746 100644 --- a/python/src/com/jetbrains/python/psi/PyImportElement.java +++ b/python/src/com/jetbrains/python/psi/PyImportElement.java @@ -11,7 +11,7 @@ import org.jetbrains.annotations.Nullable; */ public interface PyImportElement extends PyElement, NameDefiner, StubBasedPsiElement { @Nullable - PyReferenceExpression getImportReference(); + PyReferenceExpression getImportReferenceExpression(); @Nullable PyQualifiedName getImportedQName(); diff --git a/python/src/com/jetbrains/python/psi/impl/PyImportElementImpl.java b/python/src/com/jetbrains/python/psi/impl/PyImportElementImpl.java index 4bc70c36a76e..cdb3760c52e7 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyImportElementImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyImportElementImpl.java @@ -42,7 +42,7 @@ public class PyImportElementImpl extends PyBaseElementImpl } @Nullable - public PyReferenceExpression getImportReference() { + public PyReferenceExpression getImportReferenceExpression() { final ASTNode node = getNode().findChildByType(PyElementTypes.REFERENCE_EXPRESSION); return node == null ? null : (PyReferenceExpression) node.getPsi(); } @@ -52,7 +52,7 @@ public class PyImportElementImpl extends PyBaseElementImpl if (stub != null) { return stub.getImportedQName(); } - final PyReferenceExpression importReference = getImportReference(); + final PyReferenceExpression importReference = getImportReferenceExpression(); return importReference != null ? importReference.asQualifiedName() : null; } @@ -120,7 +120,7 @@ public class PyImportElementImpl extends PyBaseElementImpl if (place.getContainingFile() != getContainingFile()) { return true; } - final PyReferenceExpression importRef = getImportReference(); + final PyReferenceExpression importRef = getImportReferenceExpression(); if (importRef != null) { final PsiElement element = importRef.getReference().resolve(); if (element != null) { @@ -136,7 +136,7 @@ public class PyImportElementImpl extends PyBaseElementImpl @NotNull private String getRefName(String default_name) { - PyReferenceExpression ref = getImportReference(); + PyReferenceExpression ref = getImportReferenceExpression(); if (ref != null) { String refname = ref.getName(); if (refname != null) return refname; @@ -189,7 +189,7 @@ public class PyImportElementImpl extends PyBaseElementImpl public Iterable iterateNames() { PyElement ret = getAsNameElement(); if (ret == null) { - List unwound_path = PyResolveUtil.unwindQualifiers(getImportReference()); + List unwound_path = PyResolveUtil.unwindQualifiers(getImportReferenceExpression()); if ((unwound_path != null) && (unwound_path.size() > 0)) ret = unwound_path.get(0); } if (ret == null) { diff --git a/python/src/com/jetbrains/python/psi/impl/PyImportReferenceImpl.java b/python/src/com/jetbrains/python/psi/impl/PyImportReferenceImpl.java index b63cfd09a901..51e6dce897db 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyImportReferenceImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyImportReferenceImpl.java @@ -270,7 +270,7 @@ public class PyImportReferenceImpl extends PyReferenceImpl { private void addImportedNames(@NotNull PyImportElement[] import_elts) { for (PyImportElement ielt : import_elts) { - PyReferenceExpression ref = ielt.getImportReference(); + PyReferenceExpression ref = ielt.getImportReferenceExpression(); if (ref != null) { String s = ref.getReferencedName(); if (s != null) myNamesAlready.add(s); diff --git a/python/src/com/jetbrains/python/psi/impl/PyImportStatementNavigator.java b/python/src/com/jetbrains/python/psi/impl/PyImportStatementNavigator.java index e6a3fd01ba4a..352c6d960b72 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyImportStatementNavigator.java +++ b/python/src/com/jetbrains/python/psi/impl/PyImportStatementNavigator.java @@ -3,7 +3,6 @@ package com.jetbrains.python.psi.impl; import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; import com.jetbrains.python.psi.PyImportElement; -import com.jetbrains.python.psi.PyImportStatement; import com.jetbrains.python.psi.PyImportStatementBase; import org.jetbrains.annotations.Nullable; @@ -21,7 +20,7 @@ public class PyImportStatementNavigator { return null; } for (PyImportElement importElement : statement.getImportElements()) { - if (element == importElement || element == importElement.getImportReference()){ + if (element == importElement || element == importElement.getImportReferenceExpression()){ return statement; } } From 8e786aca3daf3d56a8f336a8c94163d22081dc32 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 14 Dec 2011 19:05:33 +0100 Subject: [PATCH 18/35] no point in having Impl as part of class name when there is no interface class --- .../com/jetbrains/python/actions/GenerateBinaryStubsFix.java | 4 ++-- .../python/inspections/PyUnresolvedReferencesInspection.java | 4 ++-- .../{PyImportReferenceImpl.java => PyImportReference.java} | 4 ++-- ...yQualifiedReferenceImpl.java => PyQualifiedReference.java} | 4 ++-- .../jetbrains/python/psi/impl/PyReferenceExpressionImpl.java | 4 ++-- .../com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) rename python/src/com/jetbrains/python/psi/impl/{PyImportReferenceImpl.java => PyImportReference.java} (98%) rename python/src/com/jetbrains/python/psi/impl/{PyQualifiedReferenceImpl.java => PyQualifiedReference.java} (99%) diff --git a/python/src/com/jetbrains/python/actions/GenerateBinaryStubsFix.java b/python/src/com/jetbrains/python/actions/GenerateBinaryStubsFix.java index 579e0a87156b..b04e96a1d5a3 100644 --- a/python/src/com/jetbrains/python/actions/GenerateBinaryStubsFix.java +++ b/python/src/com/jetbrains/python/actions/GenerateBinaryStubsFix.java @@ -13,7 +13,7 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiReference; import com.jetbrains.python.psi.*; -import com.jetbrains.python.psi.impl.PyImportReferenceImpl; +import com.jetbrains.python.psi.impl.PyImportReference; import com.jetbrains.python.sdk.IronPythonSdkFlavor; import com.jetbrains.python.sdk.PySkeletonRefresher; import com.jetbrains.python.sdk.PythonSdkFlavor; @@ -92,7 +92,7 @@ public class GenerateBinaryStubsFix implements LocalQuickFix { } public static boolean isApplicable(PsiReference ref) { - if (!(ref instanceof PyImportReferenceImpl)) { + if (!(ref instanceof PyImportReference)) { return false; } final Sdk sdk = getPythonSdk(ref); diff --git a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java index 49f3f24e1720..1044bcda2e61 100644 --- a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java @@ -40,7 +40,7 @@ import com.jetbrains.python.console.PydevConsoleRunner; import com.jetbrains.python.documentation.DocStringParameterReference; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyBuiltinCache; -import com.jetbrains.python.psi.impl.PyImportReferenceImpl; +import com.jetbrains.python.psi.impl.PyImportReference; import com.jetbrains.python.psi.impl.PyImportStatementNavigator; import com.jetbrains.python.psi.impl.PyOperatorReferenceImpl; import com.jetbrains.python.psi.resolve.ImportedResolveResult; @@ -290,7 +290,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection { myAllImports.remove(node.getParent()); } } - else if (reference instanceof PyImportReferenceImpl && + else if (reference instanceof PyImportReference && target == reference.getElement().getContainingFile()) { final boolean insideFromImport = PsiTreeUtil.getParentOfType(node, PyFromImportStatement.class) != null; if ((insideFromImport && PyImportStatementNavigator.getImportStatementByElement(node) != null) || !insideFromImport) { diff --git a/python/src/com/jetbrains/python/psi/impl/PyImportReferenceImpl.java b/python/src/com/jetbrains/python/psi/impl/PyImportReference.java similarity index 98% rename from python/src/com/jetbrains/python/psi/impl/PyImportReferenceImpl.java rename to python/src/com/jetbrains/python/psi/impl/PyImportReference.java index 51e6dce897db..ad09a86a9833 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyImportReferenceImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyImportReference.java @@ -29,10 +29,10 @@ import java.util.*; /** * @author yole */ -public class PyImportReferenceImpl extends PyReferenceImpl { +public class PyImportReference extends PyReferenceImpl { private final PyReferenceExpressionImpl myElement; - public PyImportReferenceImpl(PyReferenceExpressionImpl element, PyResolveContext context) { + public PyImportReference(PyReferenceExpressionImpl element, PyResolveContext context) { super(element, context); myElement = element; } diff --git a/python/src/com/jetbrains/python/psi/impl/PyQualifiedReferenceImpl.java b/python/src/com/jetbrains/python/psi/impl/PyQualifiedReference.java similarity index 99% rename from python/src/com/jetbrains/python/psi/impl/PyQualifiedReferenceImpl.java rename to python/src/com/jetbrains/python/psi/impl/PyQualifiedReference.java index 3c73ae63ba46..c0be37ce5362 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyQualifiedReferenceImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyQualifiedReference.java @@ -36,8 +36,8 @@ import java.util.*; /** * @author yole */ -public class PyQualifiedReferenceImpl extends PyReferenceImpl { - public PyQualifiedReferenceImpl(PyQualifiedExpression element, PyResolveContext context) { +public class PyQualifiedReference extends PyReferenceImpl { + public PyQualifiedReference(PyQualifiedExpression element, PyResolveContext context) { super(element, context); } diff --git a/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java index 4ac10960986a..fed3dace6786 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java @@ -59,7 +59,7 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere } } if (PsiTreeUtil.getParentOfType(this, PyImportElement.class, PyFromImportStatement.class) != null) { - return new PyImportReferenceImpl(this, context); + return new PyImportReference(this, context); } if (file != null) { @@ -74,7 +74,7 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere } if (qualifier != null) { - return new PyQualifiedReferenceImpl(this, context); + return new PyQualifiedReference(this, context); } return new PyReferenceImpl(this, context); diff --git a/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java index 603cab69adb3..f02b38a3cdb5 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java @@ -402,7 +402,7 @@ public class PyTargetExpressionImpl extends PyPresentableElementImpl Date: Wed, 14 Dec 2011 19:20:17 +0100 Subject: [PATCH 19/35] shuffling some code around --- .../python/psi/impl/PyImportReference.java | 26 ++++++++++++++----- .../python/psi/resolve/ResolveImportUtil.java | 24 +---------------- 2 files changed, 21 insertions(+), 29 deletions(-) diff --git a/python/src/com/jetbrains/python/psi/impl/PyImportReference.java b/python/src/com/jetbrains/python/psi/impl/PyImportReference.java index ad09a86a9833..2bb16733654c 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyImportReference.java +++ b/python/src/com/jetbrains/python/psi/impl/PyImportReference.java @@ -54,21 +54,37 @@ public class PyImportReference extends PyReferenceImpl { final String referencedName = myElement.getReferencedName(); if (referencedName == null) return ret; - int default_submodule_rate = RatedResolveResult.RATE_HIGH; + int defaultSubmoduleRate = RatedResolveResult.RATE_HIGH; // names inside module take precedence over submodules final PyImportElement import_elt = PsiTreeUtil.getParentOfType(myElement, PyImportElement.class); if (import_elt != null) { if (ret.poke(ResolveImportUtil.findImportedNameInsideModule(import_elt, referencedName), RatedResolveResult.RATE_HIGH)) { - default_submodule_rate = RatedResolveResult.RATE_NORMAL; + defaultSubmoduleRate = RatedResolveResult.RATE_NORMAL; } } - List targets = ResolveImportUtil.resolveImportReference(myElement); + final PyElement parent = PsiTreeUtil.getParentOfType(myElement, PyImportElement.class, PyFromImportStatement.class); //importRef.getParent(); + List targets; + final PyQualifiedName qname = myElement.asQualifiedName(); + if (parent instanceof PyImportElement) { + targets = ResolveImportUtil.multiResolveImportElement((PyImportElement)parent, qname); + } + else if (parent instanceof PyFromImportStatement) { // "from foo import" + targets = ResolveImportUtil.resolveFromOrForeignImport((PyFromImportStatement)parent, qname); + } + else { + return ret; + } + addRatedResults(ret, defaultSubmoduleRate, targets); + return ret; + } + + private static void addRatedResults(ResolveResultList ret, int defaultSubmoduleRate, List targets) { for (PsiElement target : targets) { target = PyUtil.turnDirIntoInit(target); if (target != null) { // ignore dirs without __init__.py, worthless - int rate = default_submodule_rate; + int rate = defaultSubmoduleRate; if (target instanceof PyFile) { VirtualFile vFile = ((PyFile)target).getVirtualFile(); if (vFile != null && vFile.getLength() == 0) { @@ -78,8 +94,6 @@ public class PyImportReference extends PyReferenceImpl { ret.poke(target, rate); } } - - return ret; } @NotNull diff --git a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java index 3b3bb4689d61..77e0d661c90f 100644 --- a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java +++ b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java @@ -113,7 +113,7 @@ public class ResolveImportUtil { } @NotNull - private static List multiResolveImportElement(PyImportElement import_element, final PyQualifiedName qName) { + public static List multiResolveImportElement(PyImportElement import_element, final PyQualifiedName qName) { if (qName == null) return Collections.emptyList(); // TODO: search for entire names, not for first component only! @@ -160,28 +160,6 @@ public class ResolveImportUtil { return result != null ? Collections.singletonList(result) : Collections.emptyList(); } - @NotNull - public static List resolveImportReference(final PyReferenceExpression importRef) { - // prerequisites - if (importRef == null) return Collections.emptyList(); - if (!importRef.isValid()) return Collections.emptyList(); // we often catch a reparse while in a process of resolution - final String referencedName = importRef.getReferencedName(); // it will be the "foo" in later comments - if (referencedName == null) return Collections.emptyList(); - final PsiFile file = importRef.getContainingFile(); - if (file == null || !file.isValid()) return Collections.emptyList(); - - final PyElement parent = PsiTreeUtil.getParentOfType(importRef, PyImportElement.class, PyFromImportStatement.class); //importRef.getParent(); - final PyQualifiedName qname = importRef.asQualifiedName(); - if (parent instanceof PyImportElement) { - PyImportElement import_element = (PyImportElement)parent; - return multiResolveImportElement(import_element, qname); - } - else if (parent instanceof PyFromImportStatement) { // "from foo import" - return resolveFromOrForeignImport((PyFromImportStatement)parent, qname); - } - return Collections.emptyList(); - } - public static List resolveFromOrForeignImport(PyFromImportStatement fromImportStatement, PyQualifiedName qname) { final List results = resolveFromImportStatementSource(fromImportStatement, qname); if (results.isEmpty() && qname != null && qname.getComponentCount() > 0) { From b11b58de6275e66202bd9b7d3b6efa9166ba58ae Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 14 Dec 2011 19:32:01 +0100 Subject: [PATCH 20/35] move code related to accepting RootVisitors from ResolveImportUtil to a separate class --- .../documentation/DocumentationBuilder.java | 7 +- .../python/psi/resolve/ResolveImportUtil.java | 118 +---------------- .../python/psi/resolve/RootVisitorHost.java | 124 ++++++++++++++++++ 3 files changed, 133 insertions(+), 116 deletions(-) create mode 100644 python/src/com/jetbrains/python/psi/resolve/RootVisitorHost.java diff --git a/python/src/com/jetbrains/python/documentation/DocumentationBuilder.java b/python/src/com/jetbrains/python/documentation/DocumentationBuilder.java index 2507436b7238..1c2ba537f885 100644 --- a/python/src/com/jetbrains/python/documentation/DocumentationBuilder.java +++ b/python/src/com/jetbrains/python/documentation/DocumentationBuilder.java @@ -19,10 +19,7 @@ import com.jetbrains.python.console.PyConsoleUtil; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.impl.PyCallExpressionHelper; -import com.jetbrains.python.psi.resolve.PyResolveContext; -import com.jetbrains.python.psi.resolve.QualifiedResolveResult; -import com.jetbrains.python.psi.resolve.ResolveImportUtil; -import com.jetbrains.python.psi.resolve.RootVisitor; +import com.jetbrains.python.psi.resolve.*; import com.jetbrains.python.psi.types.PyClassType; import com.jetbrains.python.psi.types.PyType; import com.jetbrains.python.psi.types.TypeEvalContext; @@ -498,7 +495,7 @@ class DocumentationBuilder { } else { RootFinder finder = new RootFinder(path); - ResolveImportUtil.visitRoots(followed, finder); + RootVisitorHost.visitRoots(followed, finder); final String root_path = finder.getResult(); if (root_path != null) { String after_part = path.substring(root_path.length()); diff --git a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java index 77e0d661c90f..1403c1a23a04 100644 --- a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java +++ b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java @@ -15,7 +15,6 @@ import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.util.Processor; import com.intellij.util.containers.HashSet; import com.jetbrains.django.facet.DjangoFacetType; import com.jetbrains.python.PyNames; @@ -329,11 +328,11 @@ public class ResolveImportUtil { visitor = new ResolveInRootVisitor(moduleQualifiedName, manager, footholdFile, checkForPackage); } if (module != null) { - visitRoots(module, visitor); + RootVisitorHost.visitRoots(module, visitor); return visitor.resultsAsList(); } else if (foothold != null) { - visitSdkRoots(foothold, visitor); + RootVisitorHost.visitSdkRoots(foothold, visitor); return visitor.resultsAsList(); } else { @@ -376,7 +375,7 @@ public class ResolveImportUtil { @NotNull PyQualifiedName moduleQualifiedName) { ResolveInRootVisitor visitor = new ResolveInRootVisitor(moduleQualifiedName, PsiManager.getInstance(module.getProject()), null, true); - if (visitModuleContentEntries(ModuleRootManager.getInstance(module), visitor)) { + if (RootVisitorHost.visitModuleContentEntries(ModuleRootManager.getInstance(module), visitor)) { for (VirtualFile file : rootProvider.getFiles(OrderRootType.CLASSES)) { visitor.visitRoot(file); } @@ -384,109 +383,6 @@ public class ResolveImportUtil { return visitor.resultsAsList(); } - public static void visitRoots(@NotNull final PsiElement elt, @NotNull final RootVisitor visitor) { - // real search - final Module module = ModuleUtil.findModuleForPsiElement(elt); - if (module != null) { - visitRoots(module, visitor); - } - else { - visitSdkRoots(elt, visitor); - } - } - - public static void visitRoots(@NotNull Module module, final RootVisitor visitor) { - OrderEnumerator.orderEntries(module).recursively().forEach(new Processor() { - @Override - public boolean process(OrderEntry orderEntry) { - if (orderEntry instanceof ModuleSourceOrderEntry) { - return visitModuleContentEntries(((ModuleSourceOrderEntry)orderEntry).getRootModel(), visitor); - } - return visitOrderEntryRoots(visitor, orderEntry); - } - }); - } - - /** - * Visits module content, sdk roots and libraries - */ - public static void visitRoots(@NotNull Module module, @NotNull Sdk sdk, RootVisitor visitor) { - if (!visitModuleContentEntries(ModuleRootManager.getInstance(module), visitor)) return; - // else look in SDK roots - if (visitSdkRoots(visitor, sdk)) return; - - //look in libraries - ModuleRootManager rootManager = ModuleRootManager.getInstance(module); - rootManager.orderEntries().process(new LibraryRootVisitingPolicy(visitor), null); - } - - private static void visitSdkRoots(PsiElement elt, RootVisitor visitor) { - // no module, another way to look in SDK roots - final PsiFile elt_psifile = elt.getContainingFile(); - if (elt_psifile != null) { // formality - final VirtualFile elt_vfile = elt_psifile.getOriginalFile().getVirtualFile(); - List orderEntries = null; - if (elt_vfile != null) { // reality - final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(elt.getProject()).getFileIndex(); - orderEntries = fileIndex.getOrderEntriesForFile(elt_vfile); - if (orderEntries.size() > 0) { - for (OrderEntry entry : orderEntries) { - if (!visitOrderEntryRoots(visitor, entry)) break; - } - } - else { - orderEntries = null; - } - } - - // out-of-project file or non-file(e.g. console) - use roots of SDK assigned to project - if (orderEntries == null) { - final Sdk sdk = ProjectRootManager.getInstance(elt.getProject()).getProjectSdk(); - if (sdk != null) { - visitSdkRoots(visitor, sdk); - } - } - } - } - - private static boolean visitSdkRoots(@NotNull RootVisitor visitor, @NotNull Sdk sdk) { - final VirtualFile[] roots = sdk.getRootProvider().getFiles(OrderRootType.CLASSES); - for (VirtualFile root : roots) { - if (!visitor.visitRoot(root)) { - return true; - } - } - return false; - } - - - private static boolean visitModuleContentEntries(ModuleRootModel rootModel, RootVisitor visitor) { - // look in module sources - Set contentRoots = Sets.newHashSet(); - for (ContentEntry entry : rootModel.getContentEntries()) { - VirtualFile rootFile = entry.getFile(); - - if (rootFile != null && !visitor.visitRoot(rootFile)) return false; - contentRoots.add(rootFile); - for (VirtualFile folder : entry.getSourceFolderFiles()) { - if (!visitor.visitRoot(folder)) return false; - } - } - return true; - } - - private static boolean visitOrderEntryRoots(RootVisitor visitor, OrderEntry entry) { - Set allRoots = new LinkedHashSet(); - Collections.addAll(allRoots, entry.getFiles(OrderRootType.SOURCES)); - Collections.addAll(allRoots, entry.getFiles(OrderRootType.CLASSES)); - for (VirtualFile root : allRoots) { - if (!visitor.visitRoot(root)) { - return false; - } - } - return true; - } - @Nullable public static PsiElement resolveInRoots(@NotNull final PsiElement context, final String name) { return resolveInRoots(context, PyQualifiedName.fromDottedString(name)); @@ -542,7 +438,7 @@ public class ResolveImportUtil { public static boolean findInRoots(Module module, Sdk pythonSdk, String name) { ResolveNameVisitor visitor = new ResolveNameVisitor(PsiManager.getInstance(module.getProject()), name); - visitRoots(module, pythonSdk, visitor); + RootVisitorHost.visitRoots(module, pythonSdk, visitor); return visitor.isFound(); } @@ -846,7 +742,7 @@ public class ResolveImportUtil { return name; } PathChoosingVisitor visitor = new PathChoosingVisitor(vfile); - visitRoots(foothold, visitor); + RootVisitorHost.visitRoots(foothold, visitor); final PyQualifiedName result = visitor.getResult(); if (cache != null) { cache.putName(vfile, result); @@ -862,7 +758,7 @@ public class ResolveImportUtil { return name.toString(); } PathChoosingVisitor visitor = new PathChoosingVisitor(vfile); - visitRoots(module, visitor); + RootVisitorHost.visitRoots(module, visitor); final PyQualifiedName result = visitor.getResult(); cache.putName(vfile, result); return result == null ? null : result.toString(); @@ -961,7 +857,7 @@ public class ResolveImportUtil { @Override public PsiElement visitLibraryOrderEntry(LibraryOrderEntry libraryOrderEntry, PsiElement value) { if (value != null) return value; // for chaining in processOrder() - visitOrderEntryRoots(myVisitor, libraryOrderEntry); + RootVisitorHost.visitOrderEntryRoots(myVisitor, libraryOrderEntry); return null; } } diff --git a/python/src/com/jetbrains/python/psi/resolve/RootVisitorHost.java b/python/src/com/jetbrains/python/psi/resolve/RootVisitorHost.java new file mode 100644 index 000000000000..094e21ee1f5f --- /dev/null +++ b/python/src/com/jetbrains/python/psi/resolve/RootVisitorHost.java @@ -0,0 +1,124 @@ +package com.jetbrains.python.psi.resolve; + +import com.google.common.collect.Sets; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleUtil; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.roots.*; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.util.Processor; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** + * @author yole + */ +public class RootVisitorHost { + public static void visitRoots(@NotNull final PsiElement elt, @NotNull final RootVisitor visitor) { + // real search + final Module module = ModuleUtil.findModuleForPsiElement(elt); + if (module != null) { + visitRoots(module, visitor); + } + else { + visitSdkRoots(elt, visitor); + } + } + + public static void visitRoots(@NotNull Module module, final RootVisitor visitor) { + OrderEnumerator.orderEntries(module).recursively().forEach(new Processor() { + @Override + public boolean process(OrderEntry orderEntry) { + if (orderEntry instanceof ModuleSourceOrderEntry) { + return visitModuleContentEntries(((ModuleSourceOrderEntry)orderEntry).getRootModel(), visitor); + } + return visitOrderEntryRoots(visitor, orderEntry); + } + }); + } + + /** + * Visits module content, sdk roots and libraries + */ + public static void visitRoots(@NotNull Module module, @NotNull Sdk sdk, RootVisitor visitor) { + if (!visitModuleContentEntries(ModuleRootManager.getInstance(module), visitor)) return; + // else look in SDK roots + if (visitSdkRoots(visitor, sdk)) return; + + //look in libraries + ModuleRootManager rootManager = ModuleRootManager.getInstance(module); + rootManager.orderEntries().process(new ResolveImportUtil.LibraryRootVisitingPolicy(visitor), null); + } + + static void visitSdkRoots(PsiElement elt, RootVisitor visitor) { + // no module, another way to look in SDK roots + final PsiFile elt_psifile = elt.getContainingFile(); + if (elt_psifile != null) { // formality + final VirtualFile elt_vfile = elt_psifile.getOriginalFile().getVirtualFile(); + List orderEntries = null; + if (elt_vfile != null) { // reality + final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(elt.getProject()).getFileIndex(); + orderEntries = fileIndex.getOrderEntriesForFile(elt_vfile); + if (orderEntries.size() > 0) { + for (OrderEntry entry : orderEntries) { + if (!visitOrderEntryRoots(visitor, entry)) break; + } + } + else { + orderEntries = null; + } + } + + // out-of-project file or non-file(e.g. console) - use roots of SDK assigned to project + if (orderEntries == null) { + final Sdk sdk = ProjectRootManager.getInstance(elt.getProject()).getProjectSdk(); + if (sdk != null) { + visitSdkRoots(visitor, sdk); + } + } + } + } + + private static boolean visitSdkRoots(@NotNull RootVisitor visitor, @NotNull Sdk sdk) { + final VirtualFile[] roots = sdk.getRootProvider().getFiles(OrderRootType.CLASSES); + for (VirtualFile root : roots) { + if (!visitor.visitRoot(root)) { + return true; + } + } + return false; + } + + static boolean visitModuleContentEntries(ModuleRootModel rootModel, RootVisitor visitor) { + // look in module sources + Set contentRoots = Sets.newHashSet(); + for (ContentEntry entry : rootModel.getContentEntries()) { + VirtualFile rootFile = entry.getFile(); + + if (rootFile != null && !visitor.visitRoot(rootFile)) return false; + contentRoots.add(rootFile); + for (VirtualFile folder : entry.getSourceFolderFiles()) { + if (!visitor.visitRoot(folder)) return false; + } + } + return true; + } + + static boolean visitOrderEntryRoots(RootVisitor visitor, OrderEntry entry) { + Set allRoots = new LinkedHashSet(); + Collections.addAll(allRoots, entry.getFiles(OrderRootType.SOURCES)); + Collections.addAll(allRoots, entry.getFiles(OrderRootType.CLASSES)); + for (VirtualFile root : allRoots) { + if (!visitor.visitRoot(root)) { + return false; + } + } + return true; + } +} From e81efffef737a42a4987c75c0c515890e275be16 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 14 Dec 2011 19:43:00 +0100 Subject: [PATCH 21/35] extract ImportResolver class --- .../python/psi/resolve/ImportResolver.java | 101 ++++++++++++++ .../python/psi/resolve/ResolveImportUtil.java | 123 ++---------------- 2 files changed, 109 insertions(+), 115 deletions(-) create mode 100644 python/src/com/jetbrains/python/psi/resolve/ImportResolver.java diff --git a/python/src/com/jetbrains/python/psi/resolve/ImportResolver.java b/python/src/com/jetbrains/python/psi/resolve/ImportResolver.java new file mode 100644 index 000000000000..b75df50227f5 --- /dev/null +++ b/python/src/com/jetbrains/python/psi/resolve/ImportResolver.java @@ -0,0 +1,101 @@ +package com.jetbrains.python.psi.resolve; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.intellij.facet.FacetManager; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import com.jetbrains.django.facet.DjangoFacetType; +import com.jetbrains.python.psi.impl.PyQualifiedName; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Set; + +/** +* @author yole +*/ +public class ImportResolver implements RootVisitor { + final PsiFile myFootholdFile; + final boolean myCheckForPackage; + @Nullable private final Module myModule; + private final PsiElement myFoothold; + final @NotNull PyQualifiedName myQualifiedName; + final @NotNull PsiManager myPsiManager; + final Set results = Sets.newLinkedHashSet(); + private boolean myAcceptRootAsTopLevelPackage; + + public ImportResolver(@Nullable Module module, + PsiElement foothold, + @NotNull PyQualifiedName qName, + @NotNull PsiManager psiManager, + boolean checkForPackage) { + myModule = module; + myFoothold = foothold; + myQualifiedName = qName; + myPsiManager = psiManager; + myFootholdFile = foothold != null ? foothold.getContainingFile() : null; + myCheckForPackage = checkForPackage; + if (module != null && FacetManager.getInstance(module).getFacetByType(DjangoFacetType.ID) != null) { + myAcceptRootAsTopLevelPackage = true; + } + } + + public boolean visitRoot(final VirtualFile root) { + if (!root.isValid()) { + return true; + } + PsiElement module = resolveInRoot(root, myQualifiedName, myPsiManager, myFootholdFile, myCheckForPackage); + if (module != null) { + results.add(module); + } + + if (myAcceptRootAsTopLevelPackage && myQualifiedName.matchesPrefix(PyQualifiedName.fromDottedString(root.getName()))) { + module = resolveInRoot(root.getParent(), myQualifiedName, myPsiManager, myFootholdFile, myCheckForPackage); + if (module != null) { + results.add(module); + } + } + + return true; + } + + @NotNull + public List resultsAsList() { + return Lists.newArrayList(results); + } + + public void go() { + if (myModule != null) { + RootVisitorHost.visitRoots(myModule, this); + } + else if (myFoothold != null) { + RootVisitorHost.visitSdkRoots(myFoothold, this); + } + else { + throw new IllegalStateException(); + } + } + + @Nullable + protected static PsiElement resolveInRoot(VirtualFile root, + PyQualifiedName qualifiedName, + PsiManager psiManager, + @Nullable PsiFile foothold_file, + boolean checkForPackage) { + PsiElement module = root.isDirectory() ? psiManager.findDirectory(root) : psiManager.findFile(root); + if (module == null) return null; + for (String component : qualifiedName.getComponents()) { + if (component == null) { + module = null; + break; + } + module = ResolveImportUtil.resolveChild(module, component, foothold_file, root, true, checkForPackage); // only files, we want a module + } + return module; + } +} diff --git a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java index 1403c1a23a04..c1cc835dd882 100644 --- a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java +++ b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java @@ -2,7 +2,6 @@ package com.jetbrains.python.psi.resolve; import com.google.common.collect.Lists; import com.google.common.collect.Sets; -import com.intellij.facet.FacetManager; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; @@ -16,7 +15,6 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.containers.HashSet; -import com.jetbrains.django.facet.DjangoFacetType; import com.jetbrains.python.PyNames; import com.jetbrains.python.console.PydevConsoleRunner; import com.jetbrains.python.psi.*; @@ -28,7 +26,10 @@ import com.jetbrains.python.sdk.PythonSdkType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; import static com.jetbrains.python.psi.FutureFeature.ABSOLUTE_IMPORT; @@ -314,30 +315,9 @@ public class ResolveImportUtil { @Nullable PsiElement foothold, boolean checkForPackage) { - PsiFile footholdFile = foothold != null ? foothold.getContainingFile() : null; - boolean has_djando_facet = false; - if (module != null) { - has_djando_facet = FacetManager.getInstance(module).getFacetByType(DjangoFacetType.ID) != null; - } - ResolveInRootVisitor visitor; - // resolve the name considering every source root as a package dir, as if it's a deployed package. django console does so. - if (has_djando_facet) { - visitor = new ResolveInRootAsTopPackageVisitor(moduleQualifiedName, manager, footholdFile, checkForPackage); - } - else { - visitor = new ResolveInRootVisitor(moduleQualifiedName, manager, footholdFile, checkForPackage); - } - if (module != null) { - RootVisitorHost.visitRoots(module, visitor); - return visitor.resultsAsList(); - } - else if (foothold != null) { - RootVisitorHost.visitSdkRoots(foothold, visitor); - return visitor.resultsAsList(); - } - else { - throw new IllegalStateException(); - } + ImportResolver visitor = new ImportResolver(module, foothold, moduleQualifiedName, manager, checkForPackage); + visitor.go(); + return visitor.resultsAsList(); } @Nullable @@ -373,8 +353,7 @@ public class ResolveImportUtil { public static List resolveModulesInRootProvider(@NotNull RootProvider rootProvider, @NotNull Module module, @NotNull PyQualifiedName moduleQualifiedName) { - ResolveInRootVisitor visitor = new ResolveInRootVisitor(moduleQualifiedName, PsiManager.getInstance(module.getProject()), null, - true); + ImportResolver visitor = new ImportResolver(module, null, moduleQualifiedName, PsiManager.getInstance(module.getProject()), true); if (RootVisitorHost.visitModuleContentEntries(ModuleRootManager.getInstance(module), visitor)) { for (VirtualFile file : rootProvider.getFiles(OrderRootType.CLASSES)) { visitor.visitRoot(file); @@ -442,92 +421,6 @@ public class ResolveImportUtil { return visitor.isFound(); } - static class ResolveInRootVisitor implements RootVisitor { - final PsiFile myFootholdFile; - final boolean myCheckForPackage; - final @NotNull PyQualifiedName myQualifiedName; - final @NotNull PsiManager myPsiManager; - final Set results = Sets.newLinkedHashSet(); - - public ResolveInRootVisitor(@NotNull PyQualifiedName qName, - @NotNull PsiManager psiManager, - @Nullable PsiFile foothold_file, - boolean checkForPackage) { - myQualifiedName = qName; - myPsiManager = psiManager; - myFootholdFile = foothold_file; - myCheckForPackage = checkForPackage; - } - - public boolean visitRoot(final VirtualFile root) { - if (!root.isValid()) { - return true; - } - PsiElement module = resolveInRoot(root, myQualifiedName, myPsiManager, myFootholdFile, myCheckForPackage); - if (module != null) { - results.add(module); - } - - return true; - } - - @NotNull - public List resultsAsList() { - return Lists.newArrayList(results); - } - - @Nullable - protected static PsiElement resolveInRoot(VirtualFile root, - PyQualifiedName qualifiedName, - PsiManager psiManager, - @Nullable PsiFile foothold_file, - boolean checkForPackage) { - PsiElement module = root.isDirectory() ? psiManager.findDirectory(root) : psiManager.findFile(root); - if (module == null) return null; - for (String component : qualifiedName.getComponents()) { - if (component == null) { - module = null; - break; - } - module = resolveChild(module, component, foothold_file, root, true, checkForPackage); // only files, we want a module - } - return module; - } - } - - /** - * Visits roots and detects if qName is a name of top package coincinding with a root: - * that is, tha package is not one of root's children, but the root itself. - */ - private static class ResolveInRootAsTopPackageVisitor extends ResolveInRootVisitor { - public ResolveInRootAsTopPackageVisitor(@NotNull PyQualifiedName qName, - @NotNull PsiManager psiManager, - @Nullable PsiFile foothold_file, - boolean checkForPackage) { - super(qName, psiManager, foothold_file, checkForPackage); - } - - @Override - public boolean visitRoot(VirtualFile root) { - if (!root.isValid()) { - return true; - } - PsiElement module = resolveInRoot(root, myQualifiedName, myPsiManager, myFootholdFile, myCheckForPackage); - if (module != null) { - results.add(module); - } - - if (myQualifiedName.matchesPrefix(PyQualifiedName.fromDottedString(root.getName()))) { - module = resolveInRoot(root.getParent(), myQualifiedName, myPsiManager, myFootholdFile, myCheckForPackage); - if (module != null) { - results.add(module); - } - } - - return true; - } - } - /** * Tries to find referencedName under the parent element. Used to resolve any names that look imported. * Parent might happen to be a PyFile(__init__.py), then it is treated both as a file and as ist base dir. From 47dc6dba9e3c2fb71c93b52c7937c2a2d4aeb717 Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Wed, 14 Dec 2011 20:19:03 +0100 Subject: [PATCH 22/35] Better error messages. --- .../python/parsing/StatementParsing.java | 94 +++++++++++-------- 1 file changed, 57 insertions(+), 37 deletions(-) diff --git a/python/src/com/jetbrains/python/parsing/StatementParsing.java b/python/src/com/jetbrains/python/parsing/StatementParsing.java index 895a5d5e3249..dc4f780f44d0 100644 --- a/python/src/com/jetbrains/python/parsing/StatementParsing.java +++ b/python/src/com/jetbrains/python/parsing/StatementParsing.java @@ -35,11 +35,16 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper { @NonNls protected static final String TOK_NONLOCAL = "nonlocal"; @NonNls protected static final String TOK_EXEC = "exec"; + private static final String EXPRESSION_EXPECTED = "Expression expected"; + public static final String IDENTIFIER_EXPECTED = "Identifier expected"; + protected enum Phase {NONE, FROM, FUTURE, IMPORT} // 'from __future__ import' phase + private Phase myFutureImportPhase = Phase.NONE; private boolean myExpectAsKeyword = false; public enum FUTURE {ABSOLUTE_IMPORT, DIVISION, GENERATORS, NESTED_SCOPES, WITH_STATEMENT, PRINT_FUNCTION} + protected Set myFutureFlags = EnumSet.noneOf(FUTURE.class); public static class ImportTypes { @@ -179,7 +184,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper { statementType = PyElementTypes.AUG_ASSIGNMENT_STATEMENT; builder.advanceLexer(); if (!getExpressionParser().parseYieldOrTupleExpression(false)) { - builder.error("expression expected"); + builder.error(EXPRESSION_EXPECTED); } } else if (builder.getTokenType() == PyTokenTypes.EQ) { @@ -194,7 +199,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper { PsiBuilder.Marker maybeExprMarker = builder.mark(); if (!getExpressionParser().parseYieldOrTupleExpression(false)) { maybeExprMarker.drop(); - builder.error("expression expected"); + builder.error(EXPRESSION_EXPECTED); break; } if (builder.getTokenType() == PyTokenTypes.EQ) { @@ -220,10 +225,13 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper { builder.advanceLexer(); if (firstToken == PyTokenTypes.INCONSISTENT_DEDENT) { - builder.error("unindent does not match any outer indentation level"); + builder.error("Unindent does not match any outer indentation level"); + } + else if (firstToken == PyTokenTypes.INDENT) { + builder.error("Unexpected indent"); } else { - builder.error("statement expected, found " + firstToken.toString()); + builder.error("Statement expected, found " + firstToken.toString()); } } @@ -245,7 +253,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper { } } else if (!builder.eof()) { - builder.error("end of statement expected"); + builder.error("End of statement expected"); } } @@ -296,13 +304,13 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper { final PsiBuilder.Marker delStatement = myBuilder.mark(); myBuilder.advanceLexer(); if (!getExpressionParser().parseSingleExpression(false)) { - myBuilder.error("expression expected"); + myBuilder.error("Expression expected"); } while (myBuilder.getTokenType() == PyTokenTypes.COMMA) { myBuilder.advanceLexer(); if (!getEndOfStatementsTokens().contains(myBuilder.getTokenType())) { if (!getExpressionParser().parseSingleExpression(false)) { - myBuilder.error("expression expected"); + myBuilder.error("Expression expected"); } } } @@ -342,12 +350,14 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper { if (myBuilder.getTokenType() == PyTokenTypes.COMMA) { myBuilder.advanceLexer(); if (!getExpressionParser().parseSingleExpression(false)) { - myContext.getBuilder().error("Expression expected"); + myContext.getBuilder().error(EXPRESSION_EXPECTED); } } checkEndOfStatement(scope); } - else myContext.getBuilder().error("Expression expected"); + else { + myContext.getBuilder().error(EXPRESSION_EXPECTED); + } assertStatement.done(PyElementTypes.ASSERT_STATEMENT); } @@ -413,6 +423,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper { /** * Parses option dots before imported name. + * * @return true iff there were dots. */ private boolean parseRelativeImportDots() { @@ -431,7 +442,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper { final PsiBuilder.Marker asMarker = builder.mark(); if (is_module_import) { // import _ if (!parseDottedNameAsAware(true, false)) { - asMarker.drop(); + asMarker.drop(); break; } } @@ -480,7 +491,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper { return id_text; } else { - myBuilder.error("identifier expected"); + myBuilder.error(IDENTIFIER_EXPECTED); idMarker.drop(); } return null; @@ -498,7 +509,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper { protected boolean parseDottedNameAsAware(boolean expect_as, boolean optional) { if (myBuilder.getTokenType() != PyTokenTypes.IDENTIFIER) { if (optional) return true; - myBuilder.error("identifier expected"); + myBuilder.error(IDENTIFIER_EXPECTED); return false; } PsiBuilder.Marker marker = myBuilder.mark(); @@ -509,7 +520,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper { while (myBuilder.getTokenType() == PyTokenTypes.DOT) { marker = marker.precede(); myBuilder.advanceLexer(); - checkMatches(PyTokenTypes.IDENTIFIER, "identifier expected"); + checkMatches(PyTokenTypes.IDENTIFIER, IDENTIFIER_EXPECTED); marker.done(getReferenceType()); } myExpectAsKeyword = old_expect_AS_kwd; @@ -569,7 +580,9 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper { parseColonAndSuite(scope); elsePart.done(PyElementTypes.ELSE_PART); } - else elsePart.drop(); + else { + elsePart.drop(); + } ifStatement.done(elementType); } @@ -582,7 +595,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper { if (myBuilder.getTokenType() == PyTokenTypes.STATEMENT_BREAK) { myBuilder.advanceLexer(); } - marker.error("colon expected"); + marker.error("Colon expected"); return false; } @@ -596,7 +609,9 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper { parseColonAndSuite(scope); elsePart.done(PyElementTypes.ELSE_PART); } - else elsePart.drop(); + else { + elsePart.drop(); + } statement.done(PyElementTypes.FOR_STATEMENT); } @@ -616,7 +631,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper { final PsiBuilder.Marker whilePart = myBuilder.mark(); myBuilder.advanceLexer(); if (!getExpressionParser().parseSingleExpression(false)) { - myBuilder.error("expression expected"); + myBuilder.error(EXPRESSION_EXPECTED); } parseColonAndSuite(scope); whilePart.done(PyElementTypes.WHILE_PART); @@ -626,7 +641,9 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper { parseColonAndSuite(scope); elsePart.done(PyElementTypes.ELSE_PART); } - else elsePart.drop(); + else { + elsePart.drop(); + } statement.done(PyElementTypes.WHILE_STATEMENT); } @@ -645,13 +662,13 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper { myBuilder.advanceLexer(); if (myBuilder.getTokenType() != PyTokenTypes.COLON) { if (!getExpressionParser().parseSingleExpression(false)) { - myBuilder.error("expression expected"); + myBuilder.error(EXPRESSION_EXPECTED); } myExpectAsKeyword = true; if (myBuilder.getTokenType() == PyTokenTypes.COMMA || myBuilder.getTokenType() == PyTokenTypes.AS_KEYWORD) { myBuilder.advanceLexer(); if (!getExpressionParser().parseSingleExpression(true)) { - myBuilder.error("expression expected"); + myBuilder.error(EXPRESSION_EXPECTED); } } } @@ -664,7 +681,9 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper { parseColonAndSuite(scope); elsePart.done(PyElementTypes.ELSE_PART); } - else elsePart.drop(); + else { + elsePart.drop(); + } } final PsiBuilder.Marker finallyPart = myBuilder.mark(); if (myBuilder.getTokenType() == PyTokenTypes.FINALLY_KEYWORD) { @@ -697,7 +716,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper { assertCurrentToken(PyTokenTypes.WITH_KEYWORD); final PsiBuilder.Marker statement = myBuilder.mark(); myBuilder.advanceLexer(); - while(true) { + while (true) { PsiBuilder.Marker withItem = myBuilder.mark(); getExpressionParser().parseExpression(); myExpectAsKeyword = true; @@ -722,7 +741,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper { public void parseClassDeclaration(PsiBuilder.Marker classMarker, ParsingScope scope) { assertCurrentToken(PyTokenTypes.CLASS_KEYWORD); myBuilder.advanceLexer(); - checkMatches(PyTokenTypes.IDENTIFIER, "identifier expected"); + checkMatches(PyTokenTypes.IDENTIFIER, IDENTIFIER_EXPECTED); if (myBuilder.getTokenType() == PyTokenTypes.LPAR) { getExpressionParser().parseArgumentList(); } @@ -744,12 +763,12 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper { final PsiBuilder.Marker marker = myBuilder.mark(); if (myBuilder.getTokenType() != PyTokenTypes.INDENT) { - myBuilder.error("indent expected"); + myBuilder.error("Indent expected"); } else { myBuilder.advanceLexer(); if (myBuilder.eof()) { - myBuilder.error("indented block expected"); + myBuilder.error("Indented block expected"); } else { while (!myBuilder.eof() && myBuilder.getTokenType() != PyTokenTypes.DEDENT) { @@ -764,7 +783,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper { endMarker.done(elType); } if (!myBuilder.eof()) { - checkMatches(PyTokenTypes.DEDENT, "dedent expected"); + checkMatches(PyTokenTypes.DEDENT, "Dedent expected"); } // NOTE: the following line advances the PsiBuilder lexer and thus // ensures that the whitespace following the statement list is included @@ -774,13 +793,14 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper { else { final PsiBuilder.Marker marker = myBuilder.mark(); if (myBuilder.eof()) { - myBuilder.error("statement expected"); + myBuilder.error("Statement expected"); } else { parseSimpleStatement(scope.withSuite(true)); while (matchToken(PyTokenTypes.SEMICOLON)) { - if (matchToken(PyTokenTypes.STATEMENT_BREAK)) + if (matchToken(PyTokenTypes.STATEMENT_BREAK)) { break; + } parseSimpleStatement(scope.withSuite(true)); } } @@ -795,22 +815,22 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper { if ( (myExpectAsKeyword || myContext.getLanguageLevel().hasWithStatement()) && source == PyTokenTypes.IDENTIFIER && isWordAtPosition(text, start, end, TOK_AS) - ) { + ) { return PyTokenTypes.AS_KEYWORD; } else if ( // filter - (myFutureImportPhase == Phase.FROM) && - source == PyTokenTypes.IDENTIFIER && - isWordAtPosition(text, start, end, TOK_FUTURE_IMPORT) - ) { + (myFutureImportPhase == Phase.FROM) && + source == PyTokenTypes.IDENTIFIER && + isWordAtPosition(text, start, end, TOK_FUTURE_IMPORT) + ) { myFutureImportPhase = Phase.FUTURE; return source; } else if ( - hasWithStatement() && - source == PyTokenTypes.IDENTIFIER && - isWordAtPosition(text, start, end, TOK_WITH) - ) { + hasWithStatement() && + source == PyTokenTypes.IDENTIFIER && + isWordAtPosition(text, start, end, TOK_WITH) + ) { return PyTokenTypes.WITH_KEYWORD; } else if (hasPrintStatement() && source == PyTokenTypes.IDENTIFIER && From 719be97a8d85155971ab48f6aff26459a636af79 Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Wed, 14 Dec 2011 20:19:40 +0100 Subject: [PATCH 23/35] Fixed console indentation parse error (PY-5333, PY-4493) --- .../python/console/PyConsoleUtil.java | 30 ++++--- .../console/PydevConsoleCommunication.java | 8 +- .../PydevConsoleExecuteActionHandler.java | 32 ++++++-- .../python/console/PydevConsoleRunner.java | 13 ++- .../python/console/parsing/IPythonData.java | 40 ---------- .../console/parsing/PyConsoleParser.java | 15 ++-- .../parsing/PyConsoleParsingContext.java | 80 +++++++++++-------- .../console/parsing/PythonConsoleData.java | 49 ++++++++++++ .../python/psi/PyFileElementType.java | 72 +++++++++++------ 9 files changed, 203 insertions(+), 136 deletions(-) delete mode 100644 python/src/com/jetbrains/python/console/parsing/IPythonData.java create mode 100644 python/src/com/jetbrains/python/console/parsing/PythonConsoleData.java diff --git a/python/src/com/jetbrains/python/console/PyConsoleUtil.java b/python/src/com/jetbrains/python/console/PyConsoleUtil.java index 96711e32fedc..affe7d06f30e 100644 --- a/python/src/com/jetbrains/python/console/PyConsoleUtil.java +++ b/python/src/com/jetbrains/python/console/PyConsoleUtil.java @@ -6,7 +6,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.util.Key; import com.intellij.openapi.vfs.VirtualFile; -import com.jetbrains.python.console.parsing.IPythonData; +import com.jetbrains.python.console.parsing.PythonConsoleData; import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; @@ -30,7 +30,8 @@ public class PyConsoleUtil { }; public static final String DOUBLE_QUOTE_MULTILINE = "\"\"\""; public static final String SINGLE_QUOTE_MULTILINE = "'''"; - static final Key IPYTHON = Key.create("ipython"); + + static final Key PYTHON_CONSOLE_DATA = Key.create("python-console-data"); private PyConsoleUtil() { } @@ -119,23 +120,28 @@ public class PyConsoleUtil { } public static void markIPython(@NotNull VirtualFile file) { - IPythonData data = getOrCreateIPythonData(file); - data.setEnabled(true); + PythonConsoleData consoleData = getOrCreateIPythonData(file); + consoleData.setIPythonEnabled(true); } @NotNull - public static IPythonData getOrCreateIPythonData(@NotNull VirtualFile file) { - IPythonData data = file.getUserData(IPYTHON); - if (data == null) { - data = new IPythonData(); - file.putUserData(IPYTHON, data); + public static PythonConsoleData getOrCreateIPythonData(@NotNull VirtualFile file) { + PythonConsoleData consoleData = file.getUserData(PYTHON_CONSOLE_DATA); + if (consoleData == null) { + consoleData = new PythonConsoleData(); + file.putUserData(PYTHON_CONSOLE_DATA, consoleData); } - return data; + return consoleData; } public static void setIPythonAutomagic(@NotNull VirtualFile file, boolean detected) { - IPythonData data = getOrCreateIPythonData(file); - data.setAutomagic(detected); + PythonConsoleData consoleData = getOrCreateIPythonData(file); + consoleData.setIPythonAutomagic(detected); + } + + public static void setCurrentIndentSize(@NotNull VirtualFile file, int indentSize) { + PythonConsoleData consoleData = getOrCreateIPythonData(file); + consoleData.setIndentSize(indentSize); } } diff --git a/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java b/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java index 58f4eeb7b980..574d0a3a6017 100644 --- a/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java +++ b/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java @@ -10,7 +10,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; import com.jetbrains.django.util.VirtualFileUtil; -import com.jetbrains.python.console.parsing.IPythonData; +import com.jetbrains.python.console.parsing.PythonConsoleData; import com.jetbrains.python.console.pydev.*; import com.jetbrains.python.debugger.PydevXmlUtils; import org.apache.xmlrpc.WebServer; @@ -159,9 +159,9 @@ public class PydevConsoleCommunication extends AbstractConsoleCommunication impl boolean isAutoMagic = (Boolean)params.get(1); if (getConsoleFile() != null) { - IPythonData data = PyConsoleUtil.getOrCreateIPythonData(getConsoleFile()); - data.setAutomagic(isAutoMagic); - data.setMagicCommands(commands); + PythonConsoleData consoleData = PyConsoleUtil.getOrCreateIPythonData(getConsoleFile()); + consoleData.setIPythonAutomagic(isAutoMagic); + consoleData.setIPythonMagicCommands(commands); } return ""; diff --git a/python/src/com/jetbrains/python/console/PydevConsoleExecuteActionHandler.java b/python/src/com/jetbrains/python/console/PydevConsoleExecuteActionHandler.java index af3e56fbccd9..c54f6521bc69 100644 --- a/python/src/com/jetbrains/python/console/PydevConsoleExecuteActionHandler.java +++ b/python/src/com/jetbrains/python/console/PydevConsoleExecuteActionHandler.java @@ -13,6 +13,7 @@ import com.intellij.openapi.editor.EditorModificationUtil; import com.intellij.openapi.fileTypes.PlainTextLanguage; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.codeStyle.CodeStyleSettingsManager; import com.intellij.psi.impl.source.codeStyle.IndentHelperImpl; import com.jetbrains.python.PythonFileType; @@ -21,6 +22,7 @@ import com.jetbrains.python.console.pydev.ConsoleCommunication; import com.jetbrains.python.console.pydev.ConsoleCommunicationListener; import com.jetbrains.python.console.pydev.ICallback; import com.jetbrains.python.console.pydev.InterpreterResponse; +import org.jetbrains.annotations.Nullable; import java.util.Scanner; @@ -63,7 +65,7 @@ public class PydevConsoleExecuteActionHandler extends ConsoleExecuteActionHandle processOneLine(line); } } - if (execAnyway && myCurrentIndentSize>0) { + if (execAnyway && myCurrentIndentSize > 0) { finishExecution(); } } @@ -142,7 +144,7 @@ public class PydevConsoleExecuteActionHandler extends ConsoleExecuteActionHandle flag = true; } if ((myCurrentIndentSize > 0 && indent > 0) || flag) { - myCurrentIndentSize = indent; + setCurrentIndentSize(indent); indentEditor(currentEditor, indent); more(console, currentEditor); @@ -170,13 +172,13 @@ public class PydevConsoleExecuteActionHandler extends ConsoleExecuteActionHandle console.setPrompt(PyConsoleUtil.INPUT_PROMPT); PyConsoleUtil.scrollDown(currentEditor); } - myCurrentIndentSize = -1; + setCurrentIndentSize(1); } else if (interpreterResponse.more) { more(console, currentEditor); if (myCurrentIndentSize == -1) { // compute current indentation - myCurrentIndentSize = IndentHelperImpl.getIndent(getProject(), PythonFileType.INSTANCE, line, false) + getPythonIndent(); + setCurrentIndentSize(IndentHelperImpl.getIndent(getProject(), PythonFileType.INSTANCE, line, false) + getPythonIndent()); // In this case we can insert indent automatically indentEditor(currentEditor, myCurrentIndentSize); } @@ -185,7 +187,7 @@ public class PydevConsoleExecuteActionHandler extends ConsoleExecuteActionHandle if (!myConsoleCommunication.isWaitingForInput()) { ordinaryPrompt(console, currentEditor); } - myCurrentIndentSize = -1; + setCurrentIndentSize(-1); } return null; @@ -250,7 +252,25 @@ public class PydevConsoleExecuteActionHandler extends ConsoleExecuteActionHandle return myCurrentIndentSize; } - private boolean shouldIndent(String line) { + public void setCurrentIndentSize(int currentIndentSize) { + myCurrentIndentSize = currentIndentSize; + VirtualFile file = getConsoleFile(); + if (file != null) { + PyConsoleUtil.setCurrentIndentSize(file, currentIndentSize); + } + } + + @Nullable + private VirtualFile getConsoleFile() { + if (myConsoleView != null && myConsoleView.getConsole().getFile() != null) { + return myConsoleView.getConsole().getFile().getVirtualFile(); + } + else { + return null; + } + } + + private static boolean shouldIndent(String line) { return line.endsWith(":"); } diff --git a/python/src/com/jetbrains/python/console/PydevConsoleRunner.java b/python/src/com/jetbrains/python/console/PydevConsoleRunner.java index 6c1056845bfa..36a050ee01d1 100644 --- a/python/src/com/jetbrains/python/console/PydevConsoleRunner.java +++ b/python/src/com/jetbrains/python/console/PydevConsoleRunner.java @@ -42,7 +42,7 @@ import com.intellij.util.net.NetUtils; import com.jetbrains.django.run.Runner; import com.jetbrains.python.PythonHelpersLocator; import com.jetbrains.python.console.completion.PydevConsoleElement; -import com.jetbrains.python.console.parsing.IPythonData; +import com.jetbrains.python.console.parsing.PythonConsoleData; import com.jetbrains.python.console.pydev.ConsoleCommunication; import com.jetbrains.python.run.PythonCommandLineState; import com.jetbrains.python.run.PythonTracebackFilter; @@ -419,15 +419,12 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory myMagicCommands = Sets.newHashSet(); - - public boolean isEnabled() { - return myEnabled; - } - - public void setEnabled(boolean enabled) { - myEnabled = enabled; - } - - public boolean isAutomagic() { - return myAutomagic; - } - - public void setAutomagic(boolean automagic) { - myAutomagic = automagic; - } - - public void setMagicCommands(List magicCommands) { - myMagicCommands.clear(); - myMagicCommands.addAll(magicCommands); - } - - public boolean isMagicCommand(String command) { - return myMagicCommands.contains(command); - } -} diff --git a/python/src/com/jetbrains/python/console/parsing/PyConsoleParser.java b/python/src/com/jetbrains/python/console/parsing/PyConsoleParser.java index 52c59e8df194..5774f46dd680 100644 --- a/python/src/com/jetbrains/python/console/parsing/PyConsoleParser.java +++ b/python/src/com/jetbrains/python/console/parsing/PyConsoleParser.java @@ -2,8 +2,6 @@ package com.jetbrains.python.console.parsing; import com.intellij.lang.ASTNode; import com.intellij.lang.PsiBuilder; -import com.intellij.psi.PlainTextTokenTypes; -import com.intellij.psi.PsiElement; import com.intellij.psi.tree.IElementType; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.parsing.ParsingContext; @@ -18,11 +16,11 @@ import org.jetbrains.annotations.NotNull; public class PyConsoleParser extends PyParser{ private LanguageLevel myLanguageLevel; private StatementParsing.FUTURE myFutureFlag; - private IPythonData myIPythonData; + private PythonConsoleData myPythonConsoleData; private boolean myIPythonStartSymbol; - public PyConsoleParser(IPythonData iPythonData) { - myIPythonData = iPythonData; + public PyConsoleParser(PythonConsoleData pythonConsoleData) { + myPythonConsoleData = pythonConsoleData; myLanguageLevel = LanguageLevel.getDefault(); } @@ -35,7 +33,7 @@ public class PyConsoleParser extends PyParser{ public ASTNode parse(IElementType root, PsiBuilder builder) { final PsiBuilder.Marker rootMarker = builder.mark(); - myIPythonStartSymbol = startsWithIPythonSpecialSymbol(builder); + myIPythonStartSymbol = myPythonConsoleData.isIPythonEnabled() && startsWithIPythonSpecialSymbol(builder); ParsingContext context = createParsingContext(builder, myLanguageLevel, myFutureFlag); @@ -46,8 +44,7 @@ public class PyConsoleParser extends PyParser{ stmt_parser.parseStatement(context.emptyParsingScope()); } rootMarker.done(root); - ASTNode ast = builder.getTreeBuilt(); - return ast; + return builder.getTreeBuilt(); } private static boolean startsWithIPythonSpecialSymbol(PsiBuilder builder) { @@ -59,6 +56,6 @@ public class PyConsoleParser extends PyParser{ @Override protected ParsingContext createParsingContext(PsiBuilder builder, LanguageLevel languageLevel, StatementParsing.FUTURE futureFlag) { - return new PyConsoleParsingContext(builder, languageLevel, futureFlag, myIPythonData, myIPythonStartSymbol); + return new PyConsoleParsingContext(builder, languageLevel, futureFlag, myPythonConsoleData, myIPythonStartSymbol); } } diff --git a/python/src/com/jetbrains/python/console/parsing/PyConsoleParsingContext.java b/python/src/com/jetbrains/python/console/parsing/PyConsoleParsingContext.java index 7fb4a079fb08..153fff94810d 100644 --- a/python/src/com/jetbrains/python/console/parsing/PyConsoleParsingContext.java +++ b/python/src/com/jetbrains/python/console/parsing/PyConsoleParsingContext.java @@ -1,7 +1,6 @@ package com.jetbrains.python.console.parsing; import com.intellij.lang.PsiBuilder; -import com.intellij.psi.PsiElement; import com.jetbrains.python.PyElementTypes; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.parsing.ExpressionParsing; @@ -17,17 +16,19 @@ import org.jetbrains.annotations.Nullable; public class PyConsoleParsingContext extends ParsingContext { private final StatementParsing stmtParser; private final ExpressionParsing expressionParser; - private boolean myStartsWithIPythonSymbol; - private IPythonData myIPythonData; public PyConsoleParsingContext(final PsiBuilder builder, LanguageLevel languageLevel, StatementParsing.FUTURE futureFlag, - IPythonData iPythonData, boolean startsWithIPythonSymbol) { + PythonConsoleData pythonConsoleData, boolean startsWithIPythonSymbol) { super(builder, languageLevel, futureFlag); - myStartsWithIPythonSymbol = startsWithIPythonSymbol; - stmtParser = new ConsoleStatementParsing(this, futureFlag, myStartsWithIPythonSymbol, iPythonData); - expressionParser = new ConsoleExpressionParsing(this); + stmtParser = new ConsoleStatementParsing(this, futureFlag, startsWithIPythonSymbol, pythonConsoleData); + if (pythonConsoleData.isIPythonEnabled()) { + expressionParser = new ConsoleExpressionParsing(this); + } + else { + expressionParser = new ExpressionParsing(this); + } } @Override @@ -43,12 +44,15 @@ public class PyConsoleParsingContext extends ParsingContext { private static class ConsoleStatementParsing extends StatementParsing { private boolean myStartsWithIPythonSymbol; - private IPythonData myIPythonData; + private PythonConsoleData myPythonConsoleData; - protected ConsoleStatementParsing(ParsingContext context, @Nullable FUTURE futureFlag, boolean startsWithIPythonSymbol, IPythonData iPythonData) { + protected ConsoleStatementParsing(ParsingContext context, + @Nullable FUTURE futureFlag, + boolean startsWithIPythonSymbol, + PythonConsoleData pythonConsoleData) { super(context, futureFlag); myStartsWithIPythonSymbol = startsWithIPythonSymbol; - myIPythonData = iPythonData; + myPythonConsoleData = pythonConsoleData; } @@ -58,9 +62,16 @@ public class PyConsoleParsingContext extends ParsingContext { parseIPythonCommand(); } else { - if (myIPythonData.isAutomagic()) { - if (myIPythonData.isMagicCommand(myBuilder.getTokenText())) { - parseIPythonCommand(); + if (myPythonConsoleData.isIPythonEnabled()) { + if (myPythonConsoleData.isIPythonAutomagic()) { + if (myPythonConsoleData.isMagicCommand(myBuilder.getTokenText())) { + parseIPythonCommand(); + } + } + } + if (myPythonConsoleData.getIndentSize() > 0) { + if (myBuilder.getTokenType() == PyTokenTypes.INDENT) { + myBuilder.advanceLexer(); } } super.parseStatement(scope); @@ -76,31 +87,36 @@ public class PyConsoleParsingContext extends ParsingContext { } protected void checkEndOfStatement(ParsingScope scope) { - PsiBuilder builder = myContext.getBuilder(); - if (builder.getTokenType() == PyTokenTypes.STATEMENT_BREAK) { - builder.advanceLexer(); - } - else if (builder.getTokenType() == PyTokenTypes.SEMICOLON) { - if (!scope.isSuite()) { + if (myPythonConsoleData.isIPythonEnabled()) { + PsiBuilder builder = myContext.getBuilder(); + if (builder.getTokenType() == PyTokenTypes.STATEMENT_BREAK) { builder.advanceLexer(); - if (builder.getTokenType() == PyTokenTypes.STATEMENT_BREAK) { + } + else if (builder.getTokenType() == PyTokenTypes.SEMICOLON) { + if (!scope.isSuite()) { builder.advanceLexer(); + if (builder.getTokenType() == PyTokenTypes.STATEMENT_BREAK) { + builder.advanceLexer(); + } } } - } - else if (builder.eof()) { - return; - } - else { - if (builder.getTokenType() == PyConsoleTokenTypes.PLING || builder.getTokenType() == PyConsoleTokenTypes.QUESTION_MARK) { - builder.advanceLexer(); - if (builder.getTokenType() == PyConsoleTokenTypes.PLING || builder.getTokenType() == PyConsoleTokenTypes.QUESTION_MARK) { - builder.advanceLexer(); - } - + else if (builder.eof()) { return; } - builder.error("end of statement expected"); + else { + if (builder.getTokenType() == PyConsoleTokenTypes.PLING || builder.getTokenType() == PyConsoleTokenTypes.QUESTION_MARK) { + builder.advanceLexer(); + if (builder.getTokenType() == PyConsoleTokenTypes.PLING || builder.getTokenType() == PyConsoleTokenTypes.QUESTION_MARK) { + builder.advanceLexer(); + } + + return; + } + builder.error("end of statement expected"); + } + } + else { + super.checkEndOfStatement(scope); } } } diff --git a/python/src/com/jetbrains/python/console/parsing/PythonConsoleData.java b/python/src/com/jetbrains/python/console/parsing/PythonConsoleData.java new file mode 100644 index 000000000000..25f33ba30228 --- /dev/null +++ b/python/src/com/jetbrains/python/console/parsing/PythonConsoleData.java @@ -0,0 +1,49 @@ +package com.jetbrains.python.console.parsing; + +import com.google.common.collect.Sets; + +import java.util.List; +import java.util.Set; + +/** + * @author traff + */ +public class PythonConsoleData { + private boolean myIPythonEnabled; + private boolean myIPythonAutomagic; + private Set myIPythonMagicCommands = Sets.newHashSet(); + private int myIndentSize = -1; + + public boolean isIPythonEnabled() { + return myIPythonEnabled; + } + + public void setIPythonEnabled(boolean IPythonEnabled) { + myIPythonEnabled = IPythonEnabled; + } + + public boolean isIPythonAutomagic() { + return myIPythonAutomagic; + } + + public void setIPythonAutomagic(boolean IPythonAutomagic) { + myIPythonAutomagic = IPythonAutomagic; + } + + public void setIPythonMagicCommands(List IPythonMagicCommands) { + myIPythonMagicCommands.clear(); + myIPythonMagicCommands.addAll(IPythonMagicCommands); + } + + public boolean isMagicCommand(String command) { + return myIPythonMagicCommands.contains(command); + } + + public int getIndentSize() { + return myIndentSize; + } + + public void setIndentSize(int indentSize) { + myIndentSize = indentSize; + } +} diff --git a/python/src/com/jetbrains/python/psi/PyFileElementType.java b/python/src/com/jetbrains/python/psi/PyFileElementType.java index 5a1a5cd0979a..c83d30fcf99f 100644 --- a/python/src/com/jetbrains/python/psi/PyFileElementType.java +++ b/python/src/com/jetbrains/python/psi/PyFileElementType.java @@ -12,7 +12,7 @@ import com.intellij.psi.stubs.StubInputStream; import com.intellij.psi.stubs.StubOutputStream; import com.intellij.psi.tree.IStubFileElementType; import com.intellij.util.io.StringRef; -import com.jetbrains.python.console.parsing.IPythonData; +import com.jetbrains.python.console.parsing.PythonConsoleData; import com.jetbrains.python.console.parsing.PyConsoleParser; import com.jetbrains.python.console.PydevConsoleRunner; import com.jetbrains.python.console.parsing.PythonConsoleLexer; @@ -52,47 +52,69 @@ public class PyFileElementType extends IStubFileElementType { public ASTNode parseContents(ASTNode chameleon) { final FileElement node = (FileElement)chameleon; final LanguageLevel languageLevel = getLanguageLevel(node.getPsi()); - if (PydevConsoleRunner.isIPythonConsole(node)) { - return parseConsoleCode(node, PydevConsoleRunner.getIPythonData(node)); + if (PydevConsoleRunner.isPythonConsole(node)) { + return parseConsoleCode(node, PydevConsoleRunner.getPythonConsoleData(node)); } - final PsiElement psi = node.getPsi(); - if (psi != null) { - final Project project = psi.getProject(); - final PsiBuilderFactory factory = PsiBuilderFactory.getInstance(); - final Language language = getLanguage(); - final ParserDefinition parserDefinition = LanguageParserDefinitions.INSTANCE.forLanguage(language); - if (parserDefinition == null) { - return null; - } - final Lexer lexer = parserDefinition.createLexer(project); - final PsiParser parser = parserDefinition.createParser(project); - final PsiBuilder builder = factory.createBuilder(project, node, lexer, language, node.getChars()); - if (parser instanceof PyParser) { - final PyParser pythonParser = (PyParser)parser; - pythonParser.setLanguageLevel(languageLevel); - if (languageLevel == LanguageLevel.PYTHON26 && psi.getContainingFile().getName().equals("__builtin__.py")) { - pythonParser.setFutureFlag(StatementParsing.FUTURE.PRINT_FUNCTION); + else { + final PsiElement psi = node.getPsi(); + if (psi != null) { + final Project project = psi.getProject(); + final PsiBuilderFactory factory = PsiBuilderFactory.getInstance(); + final Language language = getLanguage(); + final ParserDefinition parserDefinition = LanguageParserDefinitions.INSTANCE.forLanguage(language); + if (parserDefinition == null) { + return null; } + final Lexer lexer = parserDefinition.createLexer(project); + final PsiParser parser = parserDefinition.createParser(project); + final PsiBuilder builder = factory.createBuilder(project, node, lexer, language, node.getChars()); + if (parser instanceof PyParser) { + final PyParser pythonParser = (PyParser)parser; + pythonParser.setLanguageLevel(languageLevel); + if (languageLevel == LanguageLevel.PYTHON26 && psi.getContainingFile().getName().equals("__builtin__.py")) { + pythonParser.setFutureFlag(StatementParsing.FUTURE.PRINT_FUNCTION); + } + } + return parser.parse(this, builder).getFirstChildNode(); } - return parser.parse(this, builder).getFirstChildNode(); + return null; } - return null; } - private ASTNode parseConsoleCode(@NotNull FileElement node, IPythonData data) { - final Lexer lexer = new PythonConsoleLexer(); + @Nullable + private ASTNode parseConsoleCode(@NotNull FileElement node, PythonConsoleData consoleData) { + final Lexer lexer = createConsoleLexer(node, consoleData); final PsiElement psi = node.getPsi(); if (psi != null) { final Project project = psi.getProject(); final PsiBuilderFactory factory = PsiBuilderFactory.getInstance(); final PsiBuilder builder = factory.createBuilder(project, node, lexer, getLanguage(), node.getChars()); - final PyParser parser = new PyConsoleParser(data); + final PyParser parser = new PyConsoleParser(consoleData); return parser.parse(this, builder).getFirstChildNode(); } return null; } + @Nullable + private Lexer createConsoleLexer(FileElement node, PythonConsoleData consoleData) { + if (consoleData.isIPythonEnabled()) { + return new PythonConsoleLexer(); + } + else { + final ParserDefinition parserDefinition = LanguageParserDefinitions.INSTANCE.forLanguage(getLanguage()); + if (parserDefinition == null) { + return null; + } + final PsiElement psi = node.getPsi(); + if (psi == null) { + return null; + } + final Project project = psi.getProject(); + return parserDefinition.createLexer(project); + } + } + private static LanguageLevel getLanguageLevel(PsiElement psi) { final PsiFile file = psi.getContainingFile(); if (!(file instanceof PyFile)) { From e5312fef893c4575c85cb2abf22ba8b3afc8797c Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 14 Dec 2011 20:35:14 +0100 Subject: [PATCH 24/35] a smarter ImportResolver; use it directly when convenient --- python/src/com/jetbrains/pyqt/QtFileType.java | 2 +- .../python/codeInsight/PyDynamicMember.java | 4 +- .../python/codeInsight/PyPsiPath.java | 8 +- .../python/psi/impl/PyImportReference.java | 3 +- .../python/psi/resolve/ImportResolver.java | 141 ++++++++++++++---- .../python/psi/resolve/ResolveImportUtil.java | 84 ++--------- .../python/psi/resolve/RootVisitorHost.java | 16 +- .../psi/types/PyClassMembersProviderBase.java | 7 +- 8 files changed, 144 insertions(+), 121 deletions(-) diff --git a/python/src/com/jetbrains/pyqt/QtFileType.java b/python/src/com/jetbrains/pyqt/QtFileType.java index 8aeeb99cc653..72ace770c198 100644 --- a/python/src/com/jetbrains/pyqt/QtFileType.java +++ b/python/src/com/jetbrains/pyqt/QtFileType.java @@ -109,7 +109,7 @@ public abstract class QtFileType implements FileType, INativeFileType { } private static String findToolInPackage(String toolName, Module module, Sdk sdk, String name) { - List elements = ResolveImportUtil.resolveModulesInRootProvider(sdk.getRootProvider(), module, PyQualifiedName.fromComponents(name)); + List elements = ResolveImportUtil.resolveModulesInSdk(sdk, module, PyQualifiedName.fromComponents(name)); for (PsiElement psiElement : elements) { if (psiElement instanceof PsiDirectory) { VirtualFile tool = ((PsiDirectory)psiElement).getVirtualFile().findChild(toolName + ".exe"); diff --git a/python/src/com/jetbrains/python/codeInsight/PyDynamicMember.java b/python/src/com/jetbrains/python/codeInsight/PyDynamicMember.java index dec572fb8f8d..c1577ab11da4 100644 --- a/python/src/com/jetbrains/python/codeInsight/PyDynamicMember.java +++ b/python/src/com/jetbrains/python/codeInsight/PyDynamicMember.java @@ -108,7 +108,7 @@ public class PyDynamicMember { } @Nullable - public PsiElement resolve(PsiElement context) { + public PsiElement resolve(@NotNull PsiElement context) { if (myTarget != null) { return myTarget; } @@ -124,7 +124,7 @@ public class PyDynamicMember { } @Nullable - private PsiElement findResolveTarget(PsiElement context) { + private PsiElement findResolveTarget(@NotNull PsiElement context) { if (myPsiPath != null) { return myPsiPath.resolve(context); } diff --git a/python/src/com/jetbrains/python/codeInsight/PyPsiPath.java b/python/src/com/jetbrains/python/codeInsight/PyPsiPath.java index 2ee5ac0fe4db..d2ad7e75907c 100644 --- a/python/src/com/jetbrains/python/codeInsight/PyPsiPath.java +++ b/python/src/com/jetbrains/python/codeInsight/PyPsiPath.java @@ -3,12 +3,10 @@ package com.jetbrains.python.codeInsight; import com.intellij.psi.PsiElement; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyQualifiedName; -import com.jetbrains.python.psi.resolve.ResolveImportUtil; +import com.jetbrains.python.psi.resolve.ImportResolver; import com.jetbrains.python.psi.stubs.PyClassNameIndex; import org.jetbrains.annotations.Nullable; -import java.util.List; - /** * @author yole */ @@ -26,8 +24,8 @@ public abstract class PyPsiPath { @Nullable @Override public PsiElement resolve(PsiElement context) { - final List elements = ResolveImportUtil.resolveModulesInRoots(myQualifiedName, context); - return elements.size() > 0 ? elements.get(0) : null; + ImportResolver visitor = new ImportResolver(myQualifiedName, true).fromElement(context); + return visitor.firstResult(); } } diff --git a/python/src/com/jetbrains/python/psi/impl/PyImportReference.java b/python/src/com/jetbrains/python/psi/impl/PyImportReference.java index 2bb16733654c..cd0f8415f693 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyImportReference.java +++ b/python/src/com/jetbrains/python/psi/impl/PyImportReference.java @@ -274,7 +274,8 @@ public class PyImportReference extends PyReferenceImpl { } private void fillFromQName(PyQualifiedName thisQName, InsertHandler insertHandler) { - final List dirs = ResolveImportUtil.resolveModulesInRoots(thisQName, myCurrentFile); + ImportResolver visitor = new ImportResolver(thisQName, true).fromElement(myCurrentFile); + final List dirs = visitor.resultsAsList(); for (PsiElement dir : dirs) { if (dir instanceof PsiDirectory) { fillFromDir((PsiDirectory)dir, insertHandler); diff --git a/python/src/com/jetbrains/python/psi/resolve/ImportResolver.java b/python/src/com/jetbrains/python/psi/resolve/ImportResolver.java index b75df50227f5..2679175d95ec 100644 --- a/python/src/com/jetbrains/python/psi/resolve/ImportResolver.java +++ b/python/src/com/jetbrains/python/psi/resolve/ImportResolver.java @@ -4,15 +4,23 @@ import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.intellij.facet.FacetManager; import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.module.ModuleUtil; +import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.jetbrains.django.facet.DjangoFacetType; +import com.jetbrains.python.console.PydevConsoleRunner; +import com.jetbrains.python.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.impl.PyQualifiedName; +import com.jetbrains.python.sdk.PythonSdkType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Set; @@ -20,42 +28,67 @@ import java.util.Set; * @author yole */ public class ImportResolver implements RootVisitor { - final PsiFile myFootholdFile; final boolean myCheckForPackage; - @Nullable private final Module myModule; - private final PsiElement myFoothold; - final @NotNull PyQualifiedName myQualifiedName; - final @NotNull PsiManager myPsiManager; + @Nullable private Module myModule; + private PsiElement myFoothold; + private PsiFile myFootholdFile; + private final @NotNull PyQualifiedName myQualifiedName; + @NotNull PsiManager myPsiManager; final Set results = Sets.newLinkedHashSet(); private boolean myAcceptRootAsTopLevelPackage; + private boolean myVisitAllModules = false; + private Sdk myWithSdk; - public ImportResolver(@Nullable Module module, - PsiElement foothold, - @NotNull PyQualifiedName qName, - @NotNull PsiManager psiManager, - boolean checkForPackage) { - myModule = module; - myFoothold = foothold; + public ImportResolver(@NotNull PyQualifiedName qName, boolean checkForPackage) { myQualifiedName = qName; - myPsiManager = psiManager; - myFootholdFile = foothold != null ? foothold.getContainingFile() : null; myCheckForPackage = checkForPackage; - if (module != null && FacetManager.getInstance(module).getFacetByType(DjangoFacetType.ID) != null) { - myAcceptRootAsTopLevelPackage = true; + } + + public ImportResolver fromElement(@NotNull PsiElement foothold) { + myFoothold = foothold; + myFootholdFile = foothold.getContainingFile(); + myPsiManager = PsiManager.getInstance(foothold.getProject()); + setModule(ModuleUtil.findModuleForPsiElement(myFoothold)); + if (PydevConsoleRunner.isInPydevConsole(foothold)) { + withAllModules(); } + return this; + } + + public ImportResolver fromModule(@NotNull Module module) { + setModule(module); + myPsiManager = PsiManager.getInstance(module.getProject()); + return this; + } + + private void setModule(Module module) { + myModule = module; + if (module != null && FacetManager.getInstance(module).getFacetByType(DjangoFacetType.ID) != null) { + myAcceptRootAsTopLevelPackage = true; + } + } + + public ImportResolver withAllModules() { + myVisitAllModules = true; + return this; + } + + public ImportResolver withSdk(Sdk sdk) { + myWithSdk = sdk; + return this; } public boolean visitRoot(final VirtualFile root) { if (!root.isValid()) { return true; } - PsiElement module = resolveInRoot(root, myQualifiedName, myPsiManager, myFootholdFile, myCheckForPackage); + PsiElement module = resolveInRoot(root); if (module != null) { results.add(module); } if (myAcceptRootAsTopLevelPackage && myQualifiedName.matchesPrefix(PyQualifiedName.fromDottedString(root.getName()))) { - module = resolveInRoot(root.getParent(), myQualifiedName, myPsiManager, myFootholdFile, myCheckForPackage); + module = resolveInRoot(root.getParent()); if (module != null) { results.add(module); } @@ -66,12 +99,29 @@ public class ImportResolver implements RootVisitor { @NotNull public List resultsAsList() { - return Lists.newArrayList(results); - } + if (myFoothold != null && !myFoothold.isValid()) { + return Collections.emptyList(); + } - public void go() { - if (myModule != null) { - RootVisitorHost.visitRoots(myModule, this); + PythonPathCache cache = findMyCache(); + if (cache != null) { + final List cachedResults = cache.get(myQualifiedName); + if (cachedResults != null) { + return cachedResults; + } + } + + if (myVisitAllModules) { + for (Module mod : ModuleManager.getInstance(myPsiManager.getProject()).getModules()) { + RootVisitorHost.visitRoots(mod, false, this); + } + } + else if (myModule != null) { + final boolean otherSdk = withOtherSdk(); + RootVisitorHost.visitRoots(myModule, otherSdk, this); + if (otherSdk) { + RootVisitorHost.visitSdkRoots(myWithSdk, this); + } } else if (myFoothold != null) { RootVisitorHost.visitSdkRoots(myFoothold, this); @@ -79,22 +129,51 @@ public class ImportResolver implements RootVisitor { else { throw new IllegalStateException(); } + + final ArrayList resultList = Lists.newArrayList(results); + if (cache != null) { + cache.put(myQualifiedName, resultList); + } + return resultList; + } + + @Nullable + public PsiElement firstResult() { + final List results = resultsAsList(); + return results.size() > 0 ? results.get(0) : null; + } + + private boolean withOtherSdk() { + return myWithSdk != null && myWithSdk != PythonSdkType.findPythonSdk(myModule); } @Nullable - protected static PsiElement resolveInRoot(VirtualFile root, - PyQualifiedName qualifiedName, - PsiManager psiManager, - @Nullable PsiFile foothold_file, - boolean checkForPackage) { - PsiElement module = root.isDirectory() ? psiManager.findDirectory(root) : psiManager.findFile(root); + private PythonPathCache findMyCache() { + if (myVisitAllModules) { + return null; + } + if (myModule != null) { + return withOtherSdk() ? null : PythonModulePathCache.getInstance(myModule); + } + if (myFootholdFile != null) { + final Sdk sdk = PyBuiltinCache.findSdkForFile(myFootholdFile); + if (sdk != null) { + return PythonSdkPathCache.getInstance(myPsiManager.getProject(), sdk); + } + } + return null; + } + + @Nullable + private PsiElement resolveInRoot(VirtualFile root) { + PsiElement module = root.isDirectory() ? myPsiManager.findDirectory(root) : myPsiManager.findFile(root); if (module == null) return null; - for (String component : qualifiedName.getComponents()) { + for (String component : myQualifiedName.getComponents()) { if (component == null) { module = null; break; } - module = ResolveImportUtil.resolveChild(module, component, foothold_file, root, true, checkForPackage); // only files, we want a module + module = ResolveImportUtil.resolveChild(module, component, myFootholdFile, root, true, myCheckForPackage); // only files, we want a module } return module; } diff --git a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java index c1cc835dd882..3b99bde8e553 100644 --- a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java +++ b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java @@ -1,10 +1,7 @@ package com.jetbrains.python.psi.resolve; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.module.Module; -import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.module.ModuleUtil; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.roots.*; @@ -16,7 +13,6 @@ import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.containers.HashSet; import com.jetbrains.python.PyNames; -import com.jetbrains.python.console.PydevConsoleRunner; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.impl.PyFileImpl; @@ -259,8 +255,9 @@ public class ResolveImportUtil { @Nullable public static PsiElement resolveModuleInRoots(@NotNull PyQualifiedName moduleQualifiedName, @Nullable PsiElement foothold) { - final List candidates = resolveModulesInRoots(moduleQualifiedName, foothold); - return candidates.isEmpty() ? null : candidates.get(0); + if (foothold == null) return null; + ImportResolver visitor = new ImportResolver(moduleQualifiedName, true).fromElement(foothold); + return visitor.firstResult(); } /** @@ -272,51 +269,8 @@ public class ResolveImportUtil { */ @NotNull public static List resolveModulesInRoots(@NotNull PyQualifiedName moduleQualifiedName, @Nullable PsiElement foothold) { - if (foothold == null || !foothold.isValid()) return Collections.emptyList(); - PsiFile footholdFile = foothold.getContainingFile(); - if (footholdFile == null || !footholdFile.isValid()) return Collections.emptyList(); - - PythonPathCache cache = getPathCache(foothold); - if (cache != null) { - final List cachedResults = cache.get(moduleQualifiedName); - if (cachedResults != null) { - return cachedResults; - } - } - - final Module module = ModuleUtil.findModuleForPsiElement(foothold); - - List results; - - if (PydevConsoleRunner.isInPydevConsole(foothold)) { - results = visitRootsInAllModules(moduleQualifiedName, foothold, footholdFile); - } - else { - results = visitRoots(moduleQualifiedName, foothold.getManager(), module, foothold, true); - } - - if (cache != null) { - cache.put(moduleQualifiedName, results); - } - return results; - } - - private static List visitRootsInAllModules(PyQualifiedName moduleQualifiedName, PsiElement foothold, PsiFile footholdFile) { - Set res = Sets.newHashSet(); - for (Module mod : ModuleManager.getInstance(footholdFile.getProject()).getModules()) { - res.addAll(visitRoots(moduleQualifiedName, foothold.getManager(), mod, foothold, true)); - } - return Lists.newArrayList(res); - } - - private static List visitRoots(@NotNull PyQualifiedName moduleQualifiedName, - @NotNull PsiManager manager, - @Nullable Module module, - @Nullable PsiElement foothold, - boolean checkForPackage) { - - ImportResolver visitor = new ImportResolver(module, foothold, moduleQualifiedName, manager, checkForPackage); - visitor.go(); + if (foothold == null) return Collections.emptyList(); + ImportResolver visitor = new ImportResolver(moduleQualifiedName, true).fromElement(foothold); return visitor.resultsAsList(); } @@ -337,28 +291,10 @@ public class ResolveImportUtil { } @NotNull - public static List resolveModulesInRoots(@NotNull Module module, @NotNull PyQualifiedName moduleQualifiedName, - boolean checkForPackage) { - PythonPathCache cache = PythonModulePathCache.getInstance(module); - final List cachedResults = cache.get(moduleQualifiedName); - if (cachedResults != null) { - return cachedResults; - } - List results = visitRoots(moduleQualifiedName, PsiManager.getInstance(module.getProject()), module, null, checkForPackage); - cache.put(moduleQualifiedName, results); - return results; - } - - @NotNull - public static List resolveModulesInRootProvider(@NotNull RootProvider rootProvider, - @NotNull Module module, - @NotNull PyQualifiedName moduleQualifiedName) { - ImportResolver visitor = new ImportResolver(module, null, moduleQualifiedName, PsiManager.getInstance(module.getProject()), true); - if (RootVisitorHost.visitModuleContentEntries(ModuleRootManager.getInstance(module), visitor)) { - for (VirtualFile file : rootProvider.getFiles(OrderRootType.CLASSES)) { - visitor.visitRoot(file); - } - } + public static List resolveModulesInSdk(@NotNull Sdk sdk, + @NotNull Module module, + @NotNull PyQualifiedName moduleQualifiedName) { + ImportResolver visitor = new ImportResolver(moduleQualifiedName, true).fromModule(module).withSdk(sdk); return visitor.resultsAsList(); } @@ -651,7 +587,7 @@ public class ResolveImportUtil { return name.toString(); } PathChoosingVisitor visitor = new PathChoosingVisitor(vfile); - RootVisitorHost.visitRoots(module, visitor); + RootVisitorHost.visitRoots(module, false, visitor); final PyQualifiedName result = visitor.getResult(); cache.putName(vfile, result); return result == null ? null : result.toString(); diff --git a/python/src/com/jetbrains/python/psi/resolve/RootVisitorHost.java b/python/src/com/jetbrains/python/psi/resolve/RootVisitorHost.java index 094e21ee1f5f..c5b24859cc5f 100644 --- a/python/src/com/jetbrains/python/psi/resolve/RootVisitorHost.java +++ b/python/src/com/jetbrains/python/psi/resolve/RootVisitorHost.java @@ -24,15 +24,19 @@ public class RootVisitorHost { // real search final Module module = ModuleUtil.findModuleForPsiElement(elt); if (module != null) { - visitRoots(module, visitor); + visitRoots(module, false, visitor); } else { visitSdkRoots(elt, visitor); } } - public static void visitRoots(@NotNull Module module, final RootVisitor visitor) { - OrderEnumerator.orderEntries(module).recursively().forEach(new Processor() { + public static void visitRoots(@NotNull Module module, final boolean skipSdk, final RootVisitor visitor) { + OrderEnumerator enumerator = OrderEnumerator.orderEntries(module).recursively(); + if (skipSdk) { + enumerator = enumerator.withoutSdk(); + } + enumerator.forEach(new Processor() { @Override public boolean process(OrderEntry orderEntry) { if (orderEntry instanceof ModuleSourceOrderEntry) { @@ -49,7 +53,7 @@ public class RootVisitorHost { public static void visitRoots(@NotNull Module module, @NotNull Sdk sdk, RootVisitor visitor) { if (!visitModuleContentEntries(ModuleRootManager.getInstance(module), visitor)) return; // else look in SDK roots - if (visitSdkRoots(visitor, sdk)) return; + if (visitSdkRoots(sdk, visitor)) return; //look in libraries ModuleRootManager rootManager = ModuleRootManager.getInstance(module); @@ -79,13 +83,13 @@ public class RootVisitorHost { if (orderEntries == null) { final Sdk sdk = ProjectRootManager.getInstance(elt.getProject()).getProjectSdk(); if (sdk != null) { - visitSdkRoots(visitor, sdk); + visitSdkRoots(sdk, visitor); } } } } - private static boolean visitSdkRoots(@NotNull RootVisitor visitor, @NotNull Sdk sdk) { + public static boolean visitSdkRoots(@NotNull Sdk sdk, @NotNull RootVisitor visitor) { final VirtualFile[] roots = sdk.getRootProvider().getFiles(OrderRootType.CLASSES); for (VirtualFile root : roots) { if (!visitor.visitRoot(root)) { diff --git a/python/src/com/jetbrains/python/psi/types/PyClassMembersProviderBase.java b/python/src/com/jetbrains/python/psi/types/PyClassMembersProviderBase.java index cb8a86967412..db6ef38cb0b4 100644 --- a/python/src/com/jetbrains/python/psi/types/PyClassMembersProviderBase.java +++ b/python/src/com/jetbrains/python/psi/types/PyClassMembersProviderBase.java @@ -2,6 +2,7 @@ package com.jetbrains.python.psi.types; import com.intellij.psi.PsiElement; import com.jetbrains.python.codeInsight.PyDynamicMember; +import com.jetbrains.python.psi.PyClass; import org.jetbrains.annotations.Nullable; import java.util.Collection; @@ -26,9 +27,13 @@ public class PyClassMembersProviderBase implements PyClassMembersProvider { public static PsiElement resolveMemberByName(Collection members, PyClassType clazz, String name) { + final PyClass pyClass = clazz.getPyClass(); + if (pyClass == null) { + return null; + } for (PyDynamicMember member : members) { if (member.getName().equals(name)) { - return member.resolve(clazz.getPyClass()); + return member.resolve(pyClass); } } return null; From 65bea983a33f27021db71a7eb22c3a7f4f9b0e5a Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 14 Dec 2011 20:37:01 +0100 Subject: [PATCH 25/35] fix accidental usage of old method --- .../python/inspections/PyMethodParametersInspection.java | 3 ++- .../com/jetbrains/python/psi/resolve/ResolveImportUtil.java | 5 ----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/python/src/com/jetbrains/python/inspections/PyMethodParametersInspection.java b/python/src/com/jetbrains/python/inspections/PyMethodParametersInspection.java index d8b6f449bc43..2954872e93c0 100644 --- a/python/src/com/jetbrains/python/inspections/PyMethodParametersInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyMethodParametersInspection.java @@ -13,6 +13,7 @@ import com.jetbrains.python.PyNames; import com.jetbrains.python.actions.AddSelfQuickFix; import com.jetbrains.python.actions.RenameParameterQuickFix; import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.impl.PyQualifiedName; import com.jetbrains.python.psi.resolve.ResolveImportUtil; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NonNls; @@ -56,7 +57,7 @@ public class PyMethodParametersInspection extends PyInspection { synchronized (this) { // other threads would wait as long in resolveInRoots() anyway if (myPossibleZopeRef == null) { myPossibleZopeRef = new Ref(); - ret = ResolveImportUtil.resolveInRoots(foothold, "zope.interface.Interface"); + ret = ResolveImportUtil.resolveModuleInRoots(PyQualifiedName.fromComponents("zope.interface.Interface"), foothold); myPossibleZopeRef.set(ret); // null is OK } else ret = myPossibleZopeRef.get(); diff --git a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java index 3b99bde8e553..fc8094c6806e 100644 --- a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java +++ b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java @@ -298,11 +298,6 @@ public class ResolveImportUtil { return visitor.resultsAsList(); } - @Nullable - public static PsiElement resolveInRoots(@NotNull final PsiElement context, final String name) { - return resolveInRoots(context, PyQualifiedName.fromDottedString(name)); - } - /** * Looks for a name among element's module's roots; if there's no module, then among project's roots. * From e37c497e00d6e3c5059b9db8955c69eeca33b368 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 14 Dec 2011 20:44:44 +0100 Subject: [PATCH 26/35] some more code which is not really needed --- .../documentation/DocStringTypeReference.java | 2 +- .../python/psi/resolve/ResolveImportUtil.java | 34 ------------------- 2 files changed, 1 insertion(+), 35 deletions(-) diff --git a/python/src/com/jetbrains/python/documentation/DocStringTypeReference.java b/python/src/com/jetbrains/python/documentation/DocStringTypeReference.java index d34e80e7f49f..a6fdae0442ea 100644 --- a/python/src/com/jetbrains/python/documentation/DocStringTypeReference.java +++ b/python/src/com/jetbrains/python/documentation/DocStringTypeReference.java @@ -78,7 +78,7 @@ public class DocStringTypeReference extends PsiReferenceBase { } if (myType instanceof PyImportedModuleType) { final PyImportedModule module = ((PyImportedModuleType)myType).getImportedModule(); - return PyUtil.turnDirIntoInit(ResolveImportUtil.resolveInRoots(module.getContainingFile(), module.getImportedPrefix())); + return PyUtil.turnDirIntoInit(ResolveImportUtil.resolveModuleInRoots(module.getImportedPrefix(), module.getContainingFile())); } return null; } diff --git a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java index fc8094c6806e..f017ad73aa30 100644 --- a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java +++ b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java @@ -298,40 +298,6 @@ public class ResolveImportUtil { return visitor.resultsAsList(); } - /** - * Looks for a name among element's module's roots; if there's no module, then among project's roots. - * - * @param context PSI element that defines the module and/or the project. - * @param qualifiedName module name to be found among roots. - * @return a PsiFile, a child of a root. - */ - @Nullable - public static PsiElement resolveInRoots(@NotNull final PsiElement context, final PyQualifiedName qualifiedName) { - // NOTE: a quick and dirty temporary fix for "current dir" root path, which is assumed to be present first (but may be not). - if (qualifiedName.getComponentCount() == 1) { - PsiElement res = resolveInCurrentDir(context, qualifiedName.getLastComponent()); - if (res != null) { - return res; - } - } - return resolveModuleInRoots(qualifiedName, context); - } - - @Nullable - public static PsiElement resolveInCurrentDir(@NotNull final PsiElement elt, final String refName) { - PsiFile pfile = elt.getContainingFile(); - VirtualFile vfile = pfile.getVirtualFile(); - if (vfile == null) { // we're probably within a copy, e.g. for completion; get the real thing - pfile = pfile.getOriginalFile(); - } - PsiDirectory pdir = pfile.getContainingDirectory(); - if (pdir != null) { - PsiElement child_elt = resolveChild(pdir, refName, pfile, null, true, true); - if (child_elt != null) return child_elt; - } - return null; - } - @Nullable private static PsiElement resolveForeignImport(@NotNull final PyElement importElement, @NotNull final PyQualifiedName importText, From cf193b7bd1673dc382ec7a67a9bb2ad36be6a390 Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Wed, 14 Dec 2011 20:44:55 +0100 Subject: [PATCH 27/35] crlf to messages. --- python/helpers/pydev/pydevd.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/helpers/pydev/pydevd.py b/python/helpers/pydev/pydevd.py index 021d6e0809e9..ddb0e72b7772 100644 --- a/python/helpers/pydev/pydevd.py +++ b/python/helpers/pydev/pydevd.py @@ -1299,19 +1299,19 @@ if __name__ == '__main__': port = setup['port'] if setup['multiproc']: - pydev_log.debug("Started in multiproc mode") + pydev_log.debug("Started in multiproc mode\n") dispatcher = Dispatcher() dispatcher.connect(setup) if dispatcher.port is not None: port = dispatcher.port - pydev_log.debug("Received port %d" %port) + pydev_log.debug("Received port %d\n" %port) pydev_log.info("pydev debugger: process %d is connecting\n"% os.getpid()) import pydev_monkey try: pydev_monkey.patch_new_process_functions() except: - logger.error("Error patching process functions") + logger.error("Error patching process functions\n") else: pydev_log.error("pydev debugger: couldn't get port for new debug process\n") else: From 08839156de93d080837e168a64cec25142d09674 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 14 Dec 2011 21:03:53 +0100 Subject: [PATCH 28/35] push relative resolve logic into ImportResolver --- .../python/psi/resolve/ImportResolver.java | 116 +++++++++++++----- .../python/psi/resolve/ResolveImportUtil.java | 54 ++------ .../python/psi/resolve/RootVisitorHost.java | 46 +++---- 3 files changed, 119 insertions(+), 97 deletions(-) diff --git a/python/src/com/jetbrains/python/psi/resolve/ImportResolver.java b/python/src/com/jetbrains/python/psi/resolve/ImportResolver.java index 2679175d95ec..fa011c6507c9 100644 --- a/python/src/com/jetbrains/python/psi/resolve/ImportResolver.java +++ b/python/src/com/jetbrains/python/psi/resolve/ImportResolver.java @@ -8,6 +8,7 @@ import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.module.ModuleUtil; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; @@ -30,13 +31,14 @@ import java.util.Set; public class ImportResolver implements RootVisitor { final boolean myCheckForPackage; @Nullable private Module myModule; - private PsiElement myFoothold; private PsiFile myFootholdFile; private final @NotNull PyQualifiedName myQualifiedName; @NotNull PsiManager myPsiManager; final Set results = Sets.newLinkedHashSet(); private boolean myAcceptRootAsTopLevelPackage; private boolean myVisitAllModules = false; + private int myRelativeLevel = -1; + private boolean myWithoutRoots; private Sdk myWithSdk; public ImportResolver(@NotNull PyQualifiedName qName, boolean checkForPackage) { @@ -45,10 +47,9 @@ public class ImportResolver implements RootVisitor { } public ImportResolver fromElement(@NotNull PsiElement foothold) { - myFoothold = foothold; - myFootholdFile = foothold.getContainingFile(); + myFootholdFile = foothold.getContainingFile().getOriginalFile(); myPsiManager = PsiManager.getInstance(foothold.getProject()); - setModule(ModuleUtil.findModuleForPsiElement(myFoothold)); + setModule(ModuleUtil.findModuleForPsiElement(myFootholdFile)); if (PydevConsoleRunner.isInPydevConsole(foothold)) { withAllModules(); } @@ -77,6 +78,22 @@ public class ImportResolver implements RootVisitor { myWithSdk = sdk; return this; } + + /** + * Specifies whether we should attempt to resolve imports relative to the current file. + * + * @param relativeLevel if >= 0, we try to resolve at the specified number of levels above the current file. + * @return this + */ + public ImportResolver withRelative(int relativeLevel) { + myRelativeLevel = relativeLevel; + return this; + } + + public ImportResolver withoutRoots() { + myWithoutRoots = true; + return this; + } public boolean visitRoot(final VirtualFile root) { if (!root.isValid()) { @@ -99,42 +116,59 @@ public class ImportResolver implements RootVisitor { @NotNull public List resultsAsList() { - if (myFoothold != null && !myFoothold.isValid()) { + if (myFootholdFile != null && !myFootholdFile.isValid()) { return Collections.emptyList(); } - PythonPathCache cache = findMyCache(); - if (cache != null) { - final List cachedResults = cache.get(myQualifiedName); - if (cachedResults != null) { - return cachedResults; + if (myRelativeLevel >= 0) { + assert myFootholdFile != null; + PsiDirectory dir = myFootholdFile.getContainingDirectory(); + if (myRelativeLevel > 0) { + dir = ResolveImportUtil.stepBackFrom(myFootholdFile, myRelativeLevel); + + } + PsiElement module = resolveModuleAt(dir); + if (module != null) { + results.add(module); } } - if (myVisitAllModules) { - for (Module mod : ModuleManager.getInstance(myPsiManager.getProject()).getModules()) { - RootVisitorHost.visitRoots(mod, false, this); + if (!myWithoutRoots) { + PythonPathCache cache = findMyCache(); + if (cache != null) { + final List cachedResults = cache.get(myQualifiedName); + if (cachedResults != null) { + return cachedResults; + } } - } - else if (myModule != null) { - final boolean otherSdk = withOtherSdk(); - RootVisitorHost.visitRoots(myModule, otherSdk, this); - if (otherSdk) { - RootVisitorHost.visitSdkRoots(myWithSdk, this); + + if (myVisitAllModules) { + for (Module mod : ModuleManager.getInstance(myPsiManager.getProject()).getModules()) { + RootVisitorHost.visitRoots(mod, false, this); + } } - } - else if (myFoothold != null) { - RootVisitorHost.visitSdkRoots(myFoothold, this); - } - else { - throw new IllegalStateException(); + else if (myModule != null) { + final boolean otherSdk = withOtherSdk(); + RootVisitorHost.visitRoots(myModule, otherSdk, this); + if (otherSdk) { + RootVisitorHost.visitSdkRoots(myWithSdk, this); + } + } + else if (myFootholdFile != null) { + RootVisitorHost.visitSdkRoots(myFootholdFile, this); + } + else { + throw new IllegalStateException(); + } + + final ArrayList resultList = Lists.newArrayList(results); + if (cache != null) { + cache.put(myQualifiedName, resultList); + } + return resultList; } - final ArrayList resultList = Lists.newArrayList(results); - if (cache != null) { - cache.put(myQualifiedName, resultList); - } - return resultList; + return Lists.newArrayList(results); } @Nullable @@ -177,4 +211,26 @@ public class ImportResolver implements RootVisitor { } return module; } + + /** + * Searches for a module at given directory, unwinding qualifiers and traversing directories as needed. + * + * @param directory where to start from; top qualifier will be searched for here. + * @return module's file, or null. + */ + @Nullable + private PsiElement resolveModuleAt(@Nullable PsiDirectory directory) { + // prerequisites + if (directory == null || !directory.isValid()) return null; + + PsiElement seeker = directory; + for (String name : myQualifiedName.getComponents()) { + if (name == null) { + return null; + } + seeker = ResolveImportUtil.resolveChild(seeker, name, myFootholdFile, null, true, true); + } + return seeker; + } + } diff --git a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java index f017ad73aa30..270e80340544 100644 --- a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java +++ b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java @@ -189,70 +189,36 @@ public class ResolveImportUtil { * * @param qualifiedName qualified name of the module reference to resolve * @param source_file where that reference resides; serves as PSI foothold to determine module, project, etc. - * @param import_is_absolute if false, try old python 2.x's "relative first, absolute next" approach. - * @param relative_level if > 0, step back from source_file and resolve from there (even if import_is_absolute is false!). + * @param importIsAbsolute if false, try old python 2.x's "relative first, absolute next" approach. + * @param relativeLevel if > 0, step back from source_file and resolve from there (even if importIsAbsolute is false!). * @return list of possible candidates */ @NotNull public static List resolveModule(@Nullable PyQualifiedName qualifiedName, PsiFile source_file, - boolean import_is_absolute, int relative_level) { + boolean importIsAbsolute, int relativeLevel) { if (qualifiedName == null) return Collections.emptyList(); - String marker = StringUtil.join(qualifiedName.getComponents(), ".") + "#" + Integer.toString(relative_level); + String marker = StringUtil.join(qualifiedName.getComponents(), ".") + "#" + Integer.toString(relativeLevel); Set being_imported = ourBeingImported.get(); if (being_imported.contains(marker)) return Collections.emptyList(); // break endless loop in import try { being_imported.add(marker); - if (relative_level > 0) { + ImportResolver visitor = new ImportResolver(qualifiedName, true).fromElement(source_file); + if (relativeLevel > 0) { // "from ...module import" - final PsiElement module = resolveModuleAt(stepBackFrom(source_file, relative_level), source_file, qualifiedName); - return module != null ? Collections.singletonList(module) : Collections.emptyList(); + visitor.withRelative(relativeLevel).withoutRoots(); } else { // "from module import" - if (import_is_absolute) { - return resolveModulesInRoots(qualifiedName, source_file); - } - else { - final PsiDirectory dir = source_file.getOriginalFile().getContainingDirectory(); - PsiElement module = resolveModuleAt(dir, source_file, qualifiedName); - if (module != null) { - return Collections.singletonList(module); - } - List found_in_roots = resolveModulesInRoots(qualifiedName, source_file); - if (found_in_roots.size() > 0) return found_in_roots; - - return Collections.emptyList(); + if (!importIsAbsolute) { + visitor.withRelative(0); } } + return visitor.resultsAsList(); } finally { being_imported.remove(marker); } } - /** - * Searches for a module at given directory, unwinding qualifiers and traversing directories as needed. - * - * @param directory where to start from; top qualifier will be searched for here. - * @param sourceFile the file containing the import statement being resolved - * @param qualifiedName the qualified name of the module to search - * @return module's file, or null. - */ - @Nullable - private static PsiElement resolveModuleAt(PsiDirectory directory, PsiFile sourceFile, PyQualifiedName qualifiedName) { - // prerequisites - if (directory == null || !directory.isValid()) return null; - if (sourceFile == null || !sourceFile.isValid()) return null; - - PsiElement seeker = directory; - for (String name : qualifiedName.getComponents()) { - if (name == null) { - return null; - } - seeker = resolveChild(seeker, name, sourceFile, null, true, true); - } - return seeker; - } - @Nullable public static PsiElement resolveModuleInRoots(@NotNull PyQualifiedName moduleQualifiedName, @Nullable PsiElement foothold) { if (foothold == null) return null; diff --git a/python/src/com/jetbrains/python/psi/resolve/RootVisitorHost.java b/python/src/com/jetbrains/python/psi/resolve/RootVisitorHost.java index c5b24859cc5f..82a2ab61b51c 100644 --- a/python/src/com/jetbrains/python/psi/resolve/RootVisitorHost.java +++ b/python/src/com/jetbrains/python/psi/resolve/RootVisitorHost.java @@ -27,7 +27,10 @@ public class RootVisitorHost { visitRoots(module, false, visitor); } else { - visitSdkRoots(elt, visitor); + final PsiFile containingFile = elt.getContainingFile(); + if (containingFile != null) { + visitSdkRoots(containingFile, visitor); + } } } @@ -60,31 +63,28 @@ public class RootVisitorHost { rootManager.orderEntries().process(new ResolveImportUtil.LibraryRootVisitingPolicy(visitor), null); } - static void visitSdkRoots(PsiElement elt, RootVisitor visitor) { - // no module, another way to look in SDK roots - final PsiFile elt_psifile = elt.getContainingFile(); - if (elt_psifile != null) { // formality - final VirtualFile elt_vfile = elt_psifile.getOriginalFile().getVirtualFile(); - List orderEntries = null; - if (elt_vfile != null) { // reality - final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(elt.getProject()).getFileIndex(); - orderEntries = fileIndex.getOrderEntriesForFile(elt_vfile); - if (orderEntries.size() > 0) { - for (OrderEntry entry : orderEntries) { - if (!visitOrderEntryRoots(visitor, entry)) break; - } - } - else { - orderEntries = null; + static void visitSdkRoots(PsiFile file, RootVisitor visitor) { + // formality + final VirtualFile elt_vfile = file.getOriginalFile().getVirtualFile(); + List orderEntries = null; + if (elt_vfile != null) { // reality + final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(file.getProject()).getFileIndex(); + orderEntries = fileIndex.getOrderEntriesForFile(elt_vfile); + if (orderEntries.size() > 0) { + for (OrderEntry entry : orderEntries) { + if (!visitOrderEntryRoots(visitor, entry)) break; } } + else { + orderEntries = null; + } + } - // out-of-project file or non-file(e.g. console) - use roots of SDK assigned to project - if (orderEntries == null) { - final Sdk sdk = ProjectRootManager.getInstance(elt.getProject()).getProjectSdk(); - if (sdk != null) { - visitSdkRoots(sdk, visitor); - } + // out-of-project file or non-file(e.g. console) - use roots of SDK assigned to project + if (orderEntries == null) { + final Sdk sdk = ProjectRootManager.getInstance(file.getProject()).getProjectSdk(); + if (sdk != null) { + visitSdkRoots(sdk, visitor); } } } From 8a1f2668d09c4faed35d6aacc9c3abac7ef85b23 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 14 Dec 2011 21:11:45 +0100 Subject: [PATCH 29/35] use standard ImportResolver for checking django/coverage presence, avoid code duplication --- .../python/psi/resolve/ImportResolver.java | 5 +++ .../python/psi/resolve/ResolveImportUtil.java | 37 ------------------- 2 files changed, 5 insertions(+), 37 deletions(-) diff --git a/python/src/com/jetbrains/python/psi/resolve/ImportResolver.java b/python/src/com/jetbrains/python/psi/resolve/ImportResolver.java index fa011c6507c9..693a51e652b7 100644 --- a/python/src/com/jetbrains/python/psi/resolve/ImportResolver.java +++ b/python/src/com/jetbrains/python/psi/resolve/ImportResolver.java @@ -41,6 +41,11 @@ public class ImportResolver implements RootVisitor { private boolean myWithoutRoots; private Sdk myWithSdk; + public ImportResolver(@NotNull String qNameString, boolean checkForPackage) { + myQualifiedName = PyQualifiedName.fromComponents(qNameString); + myCheckForPackage = checkForPackage; + } + public ImportResolver(@NotNull PyQualifiedName qName, boolean checkForPackage) { myQualifiedName = qName; myCheckForPackage = checkForPackage; diff --git a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java index 270e80340544..85dc6d4e7168 100644 --- a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java +++ b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java @@ -277,13 +277,6 @@ public class ResolveImportUtil { return null; } - public static boolean findInRoots(Module module, Sdk pythonSdk, String name) { - ResolveNameVisitor - visitor = new ResolveNameVisitor(PsiManager.getInstance(module.getProject()), name); - RootVisitorHost.visitRoots(module, pythonSdk, visitor); - return visitor.isFound(); - } - /** * Tries to find referencedName under the parent element. Used to resolve any names that look imported. * Parent might happen to be a PyFile(__init__.py), then it is treated both as a file and as ist base dir. @@ -684,34 +677,4 @@ public class ResolveImportUtil { } return PointInImport.NONE; } - - public static class ResolveNameVisitor implements RootVisitor { - final @NotNull PsiManager myPsiManager; - private boolean myFound = false; - private final String myName; - - - public ResolveNameVisitor(@NotNull PsiManager psiManager, String name) { - myPsiManager = psiManager; - myName = name; - } - - public boolean visitRoot(final VirtualFile root) { - if (!root.isValid()) { - return true; - } - PsiElement module = root.isDirectory() ? myPsiManager.findDirectory(root) : myPsiManager.findFile(root); - module = resolveChild(module, myName, null, root, false, false); - if (module != null) { - myFound = true; - return false; - } - - return true; - } - - public boolean isFound() { - return myFound; - } - } } From 115350a837dce2dedc609ed79dbb061d87634638 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 14 Dec 2011 21:35:18 +0100 Subject: [PATCH 30/35] nicer API for ImportResolver --- python/src/com/jetbrains/pyqt/QtFileType.java | 19 ++++--- .../python/codeInsight/PyPsiPath.java | 2 +- .../python/psi/impl/PyImportReference.java | 9 ++-- .../python/psi/resolve/ImportResolver.java | 52 ++++++++++++++++--- .../python/psi/resolve/ResolveImportUtil.java | 26 +--------- 5 files changed, 60 insertions(+), 48 deletions(-) diff --git a/python/src/com/jetbrains/pyqt/QtFileType.java b/python/src/com/jetbrains/pyqt/QtFileType.java index 72ace770c198..98cb00aa4f58 100644 --- a/python/src/com/jetbrains/pyqt/QtFileType.java +++ b/python/src/com/jetbrains/pyqt/QtFileType.java @@ -10,11 +10,10 @@ import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; -import com.intellij.psi.PsiElement; -import com.jetbrains.python.psi.impl.PyQualifiedName; -import com.jetbrains.python.psi.resolve.ResolveImportUtil; +import com.jetbrains.python.psi.resolve.ImportResolver; import com.jetbrains.python.sdk.PythonSdkType; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.io.IOException; @@ -108,14 +107,14 @@ public abstract class QtFileType implements FileType, INativeFileType { return null; } + @Nullable private static String findToolInPackage(String toolName, Module module, Sdk sdk, String name) { - List elements = ResolveImportUtil.resolveModulesInSdk(sdk, module, PyQualifiedName.fromComponents(name)); - for (PsiElement psiElement : elements) { - if (psiElement instanceof PsiDirectory) { - VirtualFile tool = ((PsiDirectory)psiElement).getVirtualFile().findChild(toolName + ".exe"); - if (tool != null) { - return tool.getPath(); - } + ImportResolver visitor = new ImportResolver(name).fromModule(module).withSdk(sdk); + List elements = visitor.resultsOfType(PsiDirectory.class); + for (PsiDirectory directory : elements) { + VirtualFile tool = directory.getVirtualFile().findChild(toolName + ".exe"); + if (tool != null) { + return tool.getPath(); } } return null; diff --git a/python/src/com/jetbrains/python/codeInsight/PyPsiPath.java b/python/src/com/jetbrains/python/codeInsight/PyPsiPath.java index d2ad7e75907c..a4b17d7b5022 100644 --- a/python/src/com/jetbrains/python/codeInsight/PyPsiPath.java +++ b/python/src/com/jetbrains/python/codeInsight/PyPsiPath.java @@ -24,7 +24,7 @@ public abstract class PyPsiPath { @Nullable @Override public PsiElement resolve(PsiElement context) { - ImportResolver visitor = new ImportResolver(myQualifiedName, true).fromElement(context); + ImportResolver visitor = new ImportResolver(myQualifiedName).fromElement(context); return visitor.firstResult(); } } diff --git a/python/src/com/jetbrains/python/psi/impl/PyImportReference.java b/python/src/com/jetbrains/python/psi/impl/PyImportReference.java index cd0f8415f693..2165564b7869 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyImportReference.java +++ b/python/src/com/jetbrains/python/psi/impl/PyImportReference.java @@ -274,12 +274,9 @@ public class PyImportReference extends PyReferenceImpl { } private void fillFromQName(PyQualifiedName thisQName, InsertHandler insertHandler) { - ImportResolver visitor = new ImportResolver(thisQName, true).fromElement(myCurrentFile); - final List dirs = visitor.resultsAsList(); - for (PsiElement dir : dirs) { - if (dir instanceof PsiDirectory) { - fillFromDir((PsiDirectory)dir, insertHandler); - } + ImportResolver visitor = new ImportResolver(thisQName).fromElement(myCurrentFile); + for (PsiDirectory dir : visitor.resultsOfType(PsiDirectory.class)) { + fillFromDir(dir, insertHandler); } } diff --git a/python/src/com/jetbrains/python/psi/resolve/ImportResolver.java b/python/src/com/jetbrains/python/psi/resolve/ImportResolver.java index 693a51e652b7..4e9824752f55 100644 --- a/python/src/com/jetbrains/python/psi/resolve/ImportResolver.java +++ b/python/src/com/jetbrains/python/psi/resolve/ImportResolver.java @@ -29,7 +29,7 @@ import java.util.Set; * @author yole */ public class ImportResolver implements RootVisitor { - final boolean myCheckForPackage; + boolean myCheckForPackage = true; @Nullable private Module myModule; private PsiFile myFootholdFile; private final @NotNull PyQualifiedName myQualifiedName; @@ -41,14 +41,12 @@ public class ImportResolver implements RootVisitor { private boolean myWithoutRoots; private Sdk myWithSdk; - public ImportResolver(@NotNull String qNameString, boolean checkForPackage) { - myQualifiedName = PyQualifiedName.fromComponents(qNameString); - myCheckForPackage = checkForPackage; + public ImportResolver(@NotNull String qNameString) { + myQualifiedName = PyQualifiedName.fromDottedString(qNameString); } - public ImportResolver(@NotNull PyQualifiedName qName, boolean checkForPackage) { + public ImportResolver(@NotNull PyQualifiedName qName) { myQualifiedName = qName; - myCheckForPackage = checkForPackage; } public ImportResolver fromElement(@NotNull PsiElement foothold) { @@ -79,6 +77,12 @@ public class ImportResolver implements RootVisitor { return this; } + /** + * Specifies that we need to look for the name in the specified SDK (instead of the SDK assigned to the module, if any). + * + * @param sdk the SDK in which the name should be searched. + * @return this + */ public ImportResolver withSdk(Sdk sdk) { myWithSdk = sdk; return this; @@ -94,11 +98,27 @@ public class ImportResolver implements RootVisitor { myRelativeLevel = relativeLevel; return this; } - + + /** + * Specifies that we should only try to resolve relative to the current file, not in roots. + * + * @return this + */ public ImportResolver withoutRoots() { myWithoutRoots = true; return this; } + + /** + * Specifies that we're looking for a file in a directory hierarchy, not a module in the Python package hierarchy + * (so we don't need to check for existence of __init__.py) + * + * @return + */ + public ImportResolver withPlainDirectories() { + myCheckForPackage = false; + return this; + } public boolean visitRoot(final VirtualFile root) { if (!root.isValid()) { @@ -180,6 +200,24 @@ public class ImportResolver implements RootVisitor { public PsiElement firstResult() { final List results = resultsAsList(); return results.size() > 0 ? results.get(0) : null; + } + + @NotNull + public List resultsOfType(Class clazz) { + List result = new ArrayList(); + for (PsiElement element : resultsAsList()) { + if (clazz.isInstance(element)) { + //noinspection unchecked + result.add((T) element); + } + } + return result; + } + + @Nullable + public T firstResultOfType(Class clazz) { + final List list = resultsOfType(clazz); + return list.size() > 0 ? list.get(0) : null; } private boolean withOtherSdk() { diff --git a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java index 85dc6d4e7168..7376f1c72e50 100644 --- a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java +++ b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java @@ -202,7 +202,7 @@ public class ResolveImportUtil { if (being_imported.contains(marker)) return Collections.emptyList(); // break endless loop in import try { being_imported.add(marker); - ImportResolver visitor = new ImportResolver(qualifiedName, true).fromElement(source_file); + ImportResolver visitor = new ImportResolver(qualifiedName).fromElement(source_file); if (relativeLevel > 0) { // "from ...module import" visitor.withRelative(relativeLevel).withoutRoots(); @@ -222,24 +222,10 @@ public class ResolveImportUtil { @Nullable public static PsiElement resolveModuleInRoots(@NotNull PyQualifiedName moduleQualifiedName, @Nullable PsiElement foothold) { if (foothold == null) return null; - ImportResolver visitor = new ImportResolver(moduleQualifiedName, true).fromElement(foothold); + ImportResolver visitor = new ImportResolver(moduleQualifiedName).fromElement(foothold); return visitor.firstResult(); } - /** - * Returns the list of directories/files under different project roots which match the specified qualified name. - * - * @param moduleQualifiedName the qualified name to find - * @param foothold the PSI element in the context of which the search is performed - * @return the list of matching directories or files, or an empty list if nothing was found - */ - @NotNull - public static List resolveModulesInRoots(@NotNull PyQualifiedName moduleQualifiedName, @Nullable PsiElement foothold) { - if (foothold == null) return Collections.emptyList(); - ImportResolver visitor = new ImportResolver(moduleQualifiedName, true).fromElement(foothold); - return visitor.resultsAsList(); - } - @Nullable private static PythonPathCache getPathCache(PsiElement foothold) { PythonPathCache cache = null; @@ -256,14 +242,6 @@ public class ResolveImportUtil { return cache; } - @NotNull - public static List resolveModulesInSdk(@NotNull Sdk sdk, - @NotNull Module module, - @NotNull PyQualifiedName moduleQualifiedName) { - ImportResolver visitor = new ImportResolver(moduleQualifiedName, true).fromModule(module).withSdk(sdk); - return visitor.resultsAsList(); - } - @Nullable private static PsiElement resolveForeignImport(@NotNull final PyElement importElement, @NotNull final PyQualifiedName importText, From eceb8e0367b1e5e9ffec1cfe8a7d3c3d2f9d9b9a Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 14 Dec 2011 21:44:00 +0100 Subject: [PATCH 31/35] rename ImportResolver to QualifiedNameResolver (to match what it actually does); delete some more code which is thankfully not used anymore --- python/src/com/jetbrains/pyqt/QtFileType.java | 4 +-- .../python/codeInsight/PyPsiPath.java | 4 +-- .../python/psi/impl/PyImportReference.java | 2 +- ...solver.java => QualifiedNameResolver.java} | 26 ++++++++++--------- .../python/psi/resolve/ResolveImportUtil.java | 25 ++---------------- .../python/psi/resolve/RootVisitorHost.java | 13 ---------- 6 files changed, 21 insertions(+), 53 deletions(-) rename python/src/com/jetbrains/python/psi/resolve/{ImportResolver.java => QualifiedNameResolver.java} (91%) diff --git a/python/src/com/jetbrains/pyqt/QtFileType.java b/python/src/com/jetbrains/pyqt/QtFileType.java index 98cb00aa4f58..0a5c3817291e 100644 --- a/python/src/com/jetbrains/pyqt/QtFileType.java +++ b/python/src/com/jetbrains/pyqt/QtFileType.java @@ -10,7 +10,7 @@ import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; -import com.jetbrains.python.psi.resolve.ImportResolver; +import com.jetbrains.python.psi.resolve.QualifiedNameResolver; import com.jetbrains.python.sdk.PythonSdkType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -109,7 +109,7 @@ public abstract class QtFileType implements FileType, INativeFileType { @Nullable private static String findToolInPackage(String toolName, Module module, Sdk sdk, String name) { - ImportResolver visitor = new ImportResolver(name).fromModule(module).withSdk(sdk); + QualifiedNameResolver visitor = new QualifiedNameResolver(name).fromModule(module).withSdk(sdk); List elements = visitor.resultsOfType(PsiDirectory.class); for (PsiDirectory directory : elements) { VirtualFile tool = directory.getVirtualFile().findChild(toolName + ".exe"); diff --git a/python/src/com/jetbrains/python/codeInsight/PyPsiPath.java b/python/src/com/jetbrains/python/codeInsight/PyPsiPath.java index a4b17d7b5022..c772e3fcd6ed 100644 --- a/python/src/com/jetbrains/python/codeInsight/PyPsiPath.java +++ b/python/src/com/jetbrains/python/codeInsight/PyPsiPath.java @@ -3,7 +3,7 @@ package com.jetbrains.python.codeInsight; import com.intellij.psi.PsiElement; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyQualifiedName; -import com.jetbrains.python.psi.resolve.ImportResolver; +import com.jetbrains.python.psi.resolve.QualifiedNameResolver; import com.jetbrains.python.psi.stubs.PyClassNameIndex; import org.jetbrains.annotations.Nullable; @@ -24,7 +24,7 @@ public abstract class PyPsiPath { @Nullable @Override public PsiElement resolve(PsiElement context) { - ImportResolver visitor = new ImportResolver(myQualifiedName).fromElement(context); + QualifiedNameResolver visitor = new QualifiedNameResolver(myQualifiedName).fromElement(context); return visitor.firstResult(); } } diff --git a/python/src/com/jetbrains/python/psi/impl/PyImportReference.java b/python/src/com/jetbrains/python/psi/impl/PyImportReference.java index 2165564b7869..8393d095c98c 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyImportReference.java +++ b/python/src/com/jetbrains/python/psi/impl/PyImportReference.java @@ -274,7 +274,7 @@ public class PyImportReference extends PyReferenceImpl { } private void fillFromQName(PyQualifiedName thisQName, InsertHandler insertHandler) { - ImportResolver visitor = new ImportResolver(thisQName).fromElement(myCurrentFile); + QualifiedNameResolver visitor = new QualifiedNameResolver(thisQName).fromElement(myCurrentFile); for (PsiDirectory dir : visitor.resultsOfType(PsiDirectory.class)) { fillFromDir(dir, insertHandler); } diff --git a/python/src/com/jetbrains/python/psi/resolve/ImportResolver.java b/python/src/com/jetbrains/python/psi/resolve/QualifiedNameResolver.java similarity index 91% rename from python/src/com/jetbrains/python/psi/resolve/ImportResolver.java rename to python/src/com/jetbrains/python/psi/resolve/QualifiedNameResolver.java index 4e9824752f55..d4e2859a88ef 100644 --- a/python/src/com/jetbrains/python/psi/resolve/ImportResolver.java +++ b/python/src/com/jetbrains/python/psi/resolve/QualifiedNameResolver.java @@ -26,9 +26,11 @@ import java.util.List; import java.util.Set; /** -* @author yole -*/ -public class ImportResolver implements RootVisitor { + * Resolves the specified qualified name in the specified context (module, all modules or a file) to a file or directory. + * + * @author yole + */ +public class QualifiedNameResolver implements RootVisitor { boolean myCheckForPackage = true; @Nullable private Module myModule; private PsiFile myFootholdFile; @@ -41,15 +43,15 @@ public class ImportResolver implements RootVisitor { private boolean myWithoutRoots; private Sdk myWithSdk; - public ImportResolver(@NotNull String qNameString) { + public QualifiedNameResolver(@NotNull String qNameString) { myQualifiedName = PyQualifiedName.fromDottedString(qNameString); } - public ImportResolver(@NotNull PyQualifiedName qName) { + public QualifiedNameResolver(@NotNull PyQualifiedName qName) { myQualifiedName = qName; } - public ImportResolver fromElement(@NotNull PsiElement foothold) { + public QualifiedNameResolver fromElement(@NotNull PsiElement foothold) { myFootholdFile = foothold.getContainingFile().getOriginalFile(); myPsiManager = PsiManager.getInstance(foothold.getProject()); setModule(ModuleUtil.findModuleForPsiElement(myFootholdFile)); @@ -59,7 +61,7 @@ public class ImportResolver implements RootVisitor { return this; } - public ImportResolver fromModule(@NotNull Module module) { + public QualifiedNameResolver fromModule(@NotNull Module module) { setModule(module); myPsiManager = PsiManager.getInstance(module.getProject()); return this; @@ -72,7 +74,7 @@ public class ImportResolver implements RootVisitor { } } - public ImportResolver withAllModules() { + public QualifiedNameResolver withAllModules() { myVisitAllModules = true; return this; } @@ -83,7 +85,7 @@ public class ImportResolver implements RootVisitor { * @param sdk the SDK in which the name should be searched. * @return this */ - public ImportResolver withSdk(Sdk sdk) { + public QualifiedNameResolver withSdk(Sdk sdk) { myWithSdk = sdk; return this; } @@ -94,7 +96,7 @@ public class ImportResolver implements RootVisitor { * @param relativeLevel if >= 0, we try to resolve at the specified number of levels above the current file. * @return this */ - public ImportResolver withRelative(int relativeLevel) { + public QualifiedNameResolver withRelative(int relativeLevel) { myRelativeLevel = relativeLevel; return this; } @@ -104,7 +106,7 @@ public class ImportResolver implements RootVisitor { * * @return this */ - public ImportResolver withoutRoots() { + public QualifiedNameResolver withoutRoots() { myWithoutRoots = true; return this; } @@ -115,7 +117,7 @@ public class ImportResolver implements RootVisitor { * * @return */ - public ImportResolver withPlainDirectories() { + public QualifiedNameResolver withPlainDirectories() { myCheckForPackage = false; return this; } diff --git a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java index 7376f1c72e50..39ec7d59ce81 100644 --- a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java +++ b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java @@ -202,7 +202,7 @@ public class ResolveImportUtil { if (being_imported.contains(marker)) return Collections.emptyList(); // break endless loop in import try { being_imported.add(marker); - ImportResolver visitor = new ImportResolver(qualifiedName).fromElement(source_file); + QualifiedNameResolver visitor = new QualifiedNameResolver(qualifiedName).fromElement(source_file); if (relativeLevel > 0) { // "from ...module import" visitor.withRelative(relativeLevel).withoutRoots(); @@ -222,7 +222,7 @@ public class ResolveImportUtil { @Nullable public static PsiElement resolveModuleInRoots(@NotNull PyQualifiedName moduleQualifiedName, @Nullable PsiElement foothold) { if (foothold == null) return null; - ImportResolver visitor = new ImportResolver(moduleQualifiedName).fromElement(foothold); + QualifiedNameResolver visitor = new QualifiedNameResolver(moduleQualifiedName).fromElement(foothold); return visitor.firstResult(); } @@ -568,27 +568,6 @@ public class ResolveImportUtil { return null; } - public static class LibraryRootVisitingPolicy extends RootPolicy { - private final RootVisitor myVisitor; - - public LibraryRootVisitingPolicy(RootVisitor visitor) { - myVisitor = visitor; - } - - @Nullable - public PsiElement visitJdkOrderEntry(final JdkOrderEntry jdkOrderEntry, final PsiElement value) { - return null; - } - - @Nullable - @Override - public PsiElement visitLibraryOrderEntry(LibraryOrderEntry libraryOrderEntry, PsiElement value) { - if (value != null) return value; // for chaining in processOrder() - RootVisitorHost.visitOrderEntryRoots(myVisitor, libraryOrderEntry); - return null; - } - } - /** * When a name is imported from a module, tries to find the definition of that name inside the module, * as opposed to looking for submodules. diff --git a/python/src/com/jetbrains/python/psi/resolve/RootVisitorHost.java b/python/src/com/jetbrains/python/psi/resolve/RootVisitorHost.java index 82a2ab61b51c..c6490d97878f 100644 --- a/python/src/com/jetbrains/python/psi/resolve/RootVisitorHost.java +++ b/python/src/com/jetbrains/python/psi/resolve/RootVisitorHost.java @@ -50,19 +50,6 @@ public class RootVisitorHost { }); } - /** - * Visits module content, sdk roots and libraries - */ - public static void visitRoots(@NotNull Module module, @NotNull Sdk sdk, RootVisitor visitor) { - if (!visitModuleContentEntries(ModuleRootManager.getInstance(module), visitor)) return; - // else look in SDK roots - if (visitSdkRoots(sdk, visitor)) return; - - //look in libraries - ModuleRootManager rootManager = ModuleRootManager.getInstance(module); - rootManager.orderEntries().process(new ResolveImportUtil.LibraryRootVisitingPolicy(visitor), null); - } - static void visitSdkRoots(PsiFile file, RootVisitor visitor) { // formality final VirtualFile elt_vfile = file.getOriginalFile().getVirtualFile(); From d376e3568c8f42d4208873a26dc7d5d2e389ae47 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 14 Dec 2011 21:50:42 +0100 Subject: [PATCH 32/35] kill some code duplication --- .../psi/resolve/QualifiedNameResolver.java | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/python/src/com/jetbrains/python/psi/resolve/QualifiedNameResolver.java b/python/src/com/jetbrains/python/psi/resolve/QualifiedNameResolver.java index d4e2859a88ef..b57d36ec28d2 100644 --- a/python/src/com/jetbrains/python/psi/resolve/QualifiedNameResolver.java +++ b/python/src/com/jetbrains/python/psi/resolve/QualifiedNameResolver.java @@ -67,7 +67,7 @@ public class QualifiedNameResolver implements RootVisitor { return this; } - private void setModule(Module module) { + private void setModule(@Nullable Module module) { myModule = module; if (module != null && FacetManager.getInstance(module).getFacetByType(DjangoFacetType.ID) != null) { myAcceptRootAsTopLevelPackage = true; @@ -154,7 +154,7 @@ public class QualifiedNameResolver implements RootVisitor { dir = ResolveImportUtil.stepBackFrom(myFootholdFile, myRelativeLevel); } - PsiElement module = resolveModuleAt(dir); + PsiElement module = resolveModuleAt(dir, null); if (module != null) { results.add(module); } @@ -245,26 +245,22 @@ public class QualifiedNameResolver implements RootVisitor { @Nullable private PsiElement resolveInRoot(VirtualFile root) { - PsiElement module = root.isDirectory() ? myPsiManager.findDirectory(root) : myPsiManager.findFile(root); - if (module == null) return null; - for (String component : myQualifiedName.getComponents()) { - if (component == null) { - module = null; - break; - } - module = ResolveImportUtil.resolveChild(module, component, myFootholdFile, root, true, myCheckForPackage); // only files, we want a module + if (!root.isDirectory()) { + // if we have added a file as a root, it's unlikely that we'll be able to resolve anything under it in 'files only' resolve mode + return null; } - return module; + return resolveModuleAt(myPsiManager.findDirectory(root), root); } /** * Searches for a module at given directory, unwinding qualifiers and traversing directories as needed. * - * @param directory where to start from; top qualifier will be searched for here. + * @param directory where to start from; top qualifier will be searched for here. + * @param root an SDK, library or content root from which we're searching, or null if we're searching relatively * @return module's file, or null. */ @Nullable - private PsiElement resolveModuleAt(@Nullable PsiDirectory directory) { + private PsiElement resolveModuleAt(@Nullable PsiDirectory directory, @Nullable VirtualFile root) { // prerequisites if (directory == null || !directory.isValid()) return null; @@ -273,7 +269,7 @@ public class QualifiedNameResolver implements RootVisitor { if (name == null) { return null; } - seeker = ResolveImportUtil.resolveChild(seeker, name, myFootholdFile, null, true, true); + seeker = ResolveImportUtil.resolveChild(seeker, name, myFootholdFile, root, true, myCheckForPackage); } return seeker; } From 035ec1f14066f538d938f91a3b320a02ed62fbda Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Wed, 14 Dec 2011 22:04:28 +0100 Subject: [PATCH 33/35] Fixed tests. --- .../PyCompatibilityInspection/numericLiteralExpression.py | 2 +- python/testData/inspections/UnresolvedWith.py | 4 ++-- python/testData/psi/ColonBeforeEof.txt | 2 +- python/testData/psi/IncompleteFor.txt | 2 +- python/testData/psi/IncompleteStatementList.txt | 2 +- python/testData/psi/InconsistentDedent.txt | 2 +- python/testData/psi/MissingParenInCall.txt | 2 +- python/testData/psi/WithStatement2.txt | 4 ++-- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/python/testData/inspections/PyCompatibilityInspection/numericLiteralExpression.py b/python/testData/inspections/PyCompatibilityInspection/numericLiteralExpression.py index d10bf20270fe..e0bfc8945642 100644 --- a/python/testData/inspections/PyCompatibilityInspection/numericLiteralExpression.py +++ b/python/testData/inspections/PyCompatibilityInspection/numericLiteralExpression.py @@ -1,2 +1,2 @@ a = 12l -v = 048 \ No newline at end of file +v = 048 \ No newline at end of file diff --git a/python/testData/inspections/UnresolvedWith.py b/python/testData/inspections/UnresolvedWith.py index 135ac77300fa..0f1ee191bdf0 100644 --- a/python/testData/inspections/UnresolvedWith.py +++ b/python/testData/inspections/UnresolvedWith.py @@ -1,2 +1,2 @@ -with open("x.txt") as f: - data = f.read() \ No newline at end of file +with open("x.txt") as f: + data = f.read() \ No newline at end of file diff --git a/python/testData/psi/ColonBeforeEof.txt b/python/testData/psi/ColonBeforeEof.txt index 246f155b21dd..7ec42342726a 100644 --- a/python/testData/psi/ColonBeforeEof.txt +++ b/python/testData/psi/ColonBeforeEof.txt @@ -12,5 +12,5 @@ PyFile:ColonBeforeEof.py PsiElement(Py:IDENTIFIER)('b') PsiElement(Py:COLON)(':') PyStatementList - PsiErrorElement:statement expected + PsiErrorElement:Statement expected \ No newline at end of file diff --git a/python/testData/psi/IncompleteFor.txt b/python/testData/psi/IncompleteFor.txt index f785bb5d0cc0..3b833e59a2b1 100644 --- a/python/testData/psi/IncompleteFor.txt +++ b/python/testData/psi/IncompleteFor.txt @@ -20,7 +20,7 @@ PyFile:IncompleteFor.py PsiWhiteSpace(' ') PyReferenceExpression: ba PsiElement(Py:IDENTIFIER)('ba') - PsiErrorElement:colon expected + PsiErrorElement:Colon expected PyStatementList diff --git a/python/testData/psi/IncompleteStatementList.txt b/python/testData/psi/IncompleteStatementList.txt index 89aa12e134d4..69240089bb12 100644 --- a/python/testData/psi/IncompleteStatementList.txt +++ b/python/testData/psi/IncompleteStatementList.txt @@ -15,7 +15,7 @@ PyFile:IncompleteStatementList.py PsiWhiteSpace(' ') PyReferenceExpression: ba PsiElement(Py:IDENTIFIER)('ba') - PsiErrorElement:colon expected + PsiErrorElement:Colon expected PyStatementList diff --git a/python/testData/psi/InconsistentDedent.txt b/python/testData/psi/InconsistentDedent.txt index 1ee44c60498d..2c346adca39b 100644 --- a/python/testData/psi/InconsistentDedent.txt +++ b/python/testData/psi/InconsistentDedent.txt @@ -11,7 +11,7 @@ PyFile:InconsistentDedent.py PyStatementList PyPassStatement PsiElement(Py:PASS_KEYWORD)('pass') - PsiErrorElement:unindent does not match any outer indentation level + PsiErrorElement:Unindent does not match any outer indentation level PsiWhiteSpace('\n ') PyPrintStatement diff --git a/python/testData/psi/MissingParenInCall.txt b/python/testData/psi/MissingParenInCall.txt index 01190ffc3dc4..1021a7a2cb29 100644 --- a/python/testData/psi/MissingParenInCall.txt +++ b/python/testData/psi/MissingParenInCall.txt @@ -26,7 +26,7 @@ PyFile:MissingParenInCall.py PsiElement(Py:COMMA)(',') PsiErrorElement:expression expected - PsiErrorElement:colon expected + PsiErrorElement:Colon expected PsiWhiteSpace('\n\n') PyStatementList diff --git a/python/testData/psi/WithStatement2.txt b/python/testData/psi/WithStatement2.txt index e40654dfa532..b5a8e6236750 100644 --- a/python/testData/psi/WithStatement2.txt +++ b/python/testData/psi/WithStatement2.txt @@ -72,12 +72,12 @@ PyFile:WithStatement2.py PyWithItem PsiErrorElement:expression expected - PsiErrorElement:colon expected + PsiErrorElement:Colon expected PyStatementList PsiElement(Py:EQ)('=') - PsiErrorElement:statement expected, found Py:EQ + PsiErrorElement:Statement expected, found Py:EQ PsiWhiteSpace(' ') PyExpressionStatement From 9adab52d368ad0d1b9f05155c1c19190f44379c3 Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 14 Dec 2011 22:10:58 +0100 Subject: [PATCH 34/35] an action to copy PSI to clipboard from PsiViewer, removed similar action in python --- python/src/META-INF/python-plugin-common.xml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/python/src/META-INF/python-plugin-common.xml b/python/src/META-INF/python-plugin-common.xml index 93bbaa12b698..452b506dd0db 100644 --- a/python/src/META-INF/python-plugin-common.xml +++ b/python/src/META-INF/python-plugin-common.xml @@ -811,15 +811,6 @@ - - - - From 7df9c94cf016786e2947921878516017571eaa67 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 14 Dec 2011 18:05:48 +0100 Subject: [PATCH 35/35] Ignore commented lines in .vmoptions --- python/build/pycharm.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/build/pycharm.sh b/python/build/pycharm.sh index 4ef59e500fbc..1ed235f26b03 100644 --- a/python/build/pycharm.sh +++ b/python/build/pycharm.sh @@ -129,7 +129,7 @@ fi # if VM options file exists - use it if [ -r "$VM_OPTIONS_FILE" ]; then - JVM_ARGS=`tr '\n' ' ' < "$VM_OPTIONS_FILE"` + JVM_ARGS=`cat "$VM_OPTIONS_FILE" | grep -ve "^#.*" | tr '\n' ' '` JVM_ARGS="$JVM_ARGS -Djb.vmOptionsFile=\"$VM_OPTIONS_FILE\"" # only extract properties (not VM options) from Info.plist INFO_PLIST_PARSER_OPTIONS=""