From 26a91ddec1bdd27ac6e155c5df5eb8fe9c5670dd Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Fri, 21 Mar 2008 14:26:32 +0300 Subject: [PATCH] python inheritors search (doesn't work yet: Max will investigate) --- python/pluginSrc/META-INF/plugin.xml | 4 +- python/src/META-INF/python-plugin-common.xml | 9 +++- .../src/com/jetbrains/python/psi/PyClass.java | 2 +- .../python/psi/impl/PyClassImpl.java | 23 +++++------ .../psi/impl/stubs/PyClassElementType.java | 24 ++++++++++- .../psi/impl/stubs/PyClassStubImpl.java | 8 +++- .../psi/search/PyClassInheritorsSearch.java | 33 +++++++++++++++ .../PyClassInheritorsSearchExecutor.java | 34 +++++++++++++++ .../python/psi/stubs/PyClassStub.java | 1 + .../python/psi/stubs/PySuperClassIndex.java | 16 ++++++++ python/testData/inheritors/simple/A.py | 9 ++++ .../python/PyInheritorsSearchTest.java | 41 +++++++++++++++++++ 12 files changed, 185 insertions(+), 19 deletions(-) create mode 100644 python/src/com/jetbrains/python/psi/search/PyClassInheritorsSearch.java create mode 100644 python/src/com/jetbrains/python/psi/search/PyClassInheritorsSearchExecutor.java create mode 100644 python/src/com/jetbrains/python/psi/stubs/PySuperClassIndex.java create mode 100644 python/testData/inheritors/simple/A.py create mode 100644 python/testSrc/com/jetbrains/python/PyInheritorsSearchTest.java diff --git a/python/pluginSrc/META-INF/plugin.xml b/python/pluginSrc/META-INF/plugin.xml index 7b161e73b4e9..7e05f111da55 100644 --- a/python/pluginSrc/META-INF/plugin.xml +++ b/python/pluginSrc/META-INF/plugin.xml @@ -1,4 +1,7 @@ + + Pythonid Pythonid @@ -6,7 +9,6 @@ - \ No newline at end of file diff --git a/python/src/META-INF/python-plugin-common.xml b/python/src/META-INF/python-plugin-common.xml index 7689c008605e..00f675c1ec0a 100644 --- a/python/src/META-INF/python-plugin-common.xml +++ b/python/src/META-INF/python-plugin-common.xml @@ -42,6 +42,7 @@ + @@ -60,6 +61,12 @@ - + + + + + + + diff --git a/python/src/com/jetbrains/python/psi/PyClass.java b/python/src/com/jetbrains/python/psi/PyClass.java index 33525351d31f..0c8fb8aa8aa9 100644 --- a/python/src/com/jetbrains/python/psi/PyClass.java +++ b/python/src/com/jetbrains/python/psi/PyClass.java @@ -34,7 +34,7 @@ public interface PyClass extends PsiNamedElement, PyElement, PyDocStringOwner, S @NotNull PyStatementList getStatementList(); - @Nullable + @NotNull PyExpression[] getSuperClassExpressions(); @Nullable diff --git a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java index 74f65cbc839c..ef131e01f113 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java @@ -91,7 +91,7 @@ public class PyClassImpl extends PyPresentableElementImpl implement return childToPsiNotNull(PyElementTypes.STATEMENT_LIST); } - @Nullable + @NotNull public PyExpression[] getSuperClassExpressions() { final PyParenthesizedExpression superExpression = PsiTreeUtil.getChildOfType(this, PyParenthesizedExpression.class); if (superExpression != null) { @@ -103,25 +103,22 @@ public class PyClassImpl extends PyPresentableElementImpl implement return new PyExpression[] { expr }; } } - return null; + return PyExpression.EMPTY_ARRAY; } public PsiElement[] getSuperClassElements() { final PyExpression[] superExpressions = getSuperClassExpressions(); - if (superExpressions != null) { - List superClasses = new ArrayList(); - for(PyExpression expr: superExpressions) { - if (expr instanceof PyReferenceExpression && !PyNames.OBJECT.equals(expr.getText())) { - PyReferenceExpression ref = (PyReferenceExpression) expr; - final PsiElement result = ref.resolve(); - if (result != null) { - superClasses.add(result); - } + List superClasses = new ArrayList(); + for(PyExpression expr: superExpressions) { + if (expr instanceof PyReferenceExpression && !PyNames.OBJECT.equals(expr.getText())) { + PyReferenceExpression ref = (PyReferenceExpression) expr; + final PsiElement result = ref.resolve(); + if (result != null) { + superClasses.add(result); } } - return superClasses.toArray(new PsiElement[superClasses.size()]); } - return null; + return superClasses.toArray(new PsiElement[superClasses.size()]); } public PyClass[] getSuperClasses() { diff --git a/python/src/com/jetbrains/python/psi/impl/stubs/PyClassElementType.java b/python/src/com/jetbrains/python/psi/impl/stubs/PyClassElementType.java index ebdbded4850a..6fe15c266618 100644 --- a/python/src/com/jetbrains/python/psi/impl/stubs/PyClassElementType.java +++ b/python/src/com/jetbrains/python/psi/impl/stubs/PyClassElementType.java @@ -10,10 +10,12 @@ import com.intellij.psi.stubs.StubElement; import com.intellij.util.io.DataInputOutputUtil; import com.intellij.util.io.PersistentStringEnumerator; import com.jetbrains.python.psi.PyClass; +import com.jetbrains.python.psi.PyExpression; import com.jetbrains.python.psi.PyStubElementType; import com.jetbrains.python.psi.impl.PyClassImpl; import com.jetbrains.python.psi.stubs.PyClassNameIndex; import com.jetbrains.python.psi.stubs.PyClassStub; +import com.jetbrains.python.psi.stubs.PySuperClassIndex; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -33,18 +35,33 @@ public class PyClassElementType extends PyStubElementType } public PyClassStub createStub(final PyClass psi, final StubElement parentStub) { - return new PyClassStubImpl(psi.getName(), parentStub); + final PyExpression[] exprs = psi.getSuperClassExpressions(); + String[] superClasses = new String[exprs.length]; + for(int i=0; i if (name != null) { sink.occurence(PyClassNameIndex.KEY, name); } + for(String s: stub.getSuperClasses()) { + sink.occurence(PySuperClassIndex.KEY, s); + } } } \ No newline at end of file diff --git a/python/src/com/jetbrains/python/psi/impl/stubs/PyClassStubImpl.java b/python/src/com/jetbrains/python/psi/impl/stubs/PyClassStubImpl.java index fb258d956105..c299603605c7 100644 --- a/python/src/com/jetbrains/python/psi/impl/stubs/PyClassStubImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/stubs/PyClassStubImpl.java @@ -11,13 +11,19 @@ import com.jetbrains.python.psi.stubs.PyClassStub; public class PyClassStubImpl extends StubBase implements PyClassStub { private final String myName; + private final String[] mySuperClasses; - public PyClassStubImpl(final String name, StubElement parentStub) { + public PyClassStubImpl(final String name, StubElement parentStub, final String[] superClasses) { super(parentStub, PyElementTypes.CLASS_DECLARATION); myName = name; + mySuperClasses = superClasses; } public String getName() { return myName; } + + public String[] getSuperClasses() { + return mySuperClasses; + } } \ No newline at end of file diff --git a/python/src/com/jetbrains/python/psi/search/PyClassInheritorsSearch.java b/python/src/com/jetbrains/python/psi/search/PyClassInheritorsSearch.java new file mode 100644 index 000000000000..a6aa88098d8c --- /dev/null +++ b/python/src/com/jetbrains/python/psi/search/PyClassInheritorsSearch.java @@ -0,0 +1,33 @@ +package com.jetbrains.python.psi.search; + +import com.intellij.psi.search.searches.ExtensibleQueryFactory; +import com.intellij.util.Query; +import com.jetbrains.python.psi.PyClass; + +/** + * @author yole + */ +public class PyClassInheritorsSearch extends ExtensibleQueryFactory { + public static PyClassInheritorsSearch INSTANCE = new PyClassInheritorsSearch(); + + public static class SearchParameters { + private PyClass mySuperClass; + + public SearchParameters(final PyClass superClass) { + mySuperClass = superClass; + } + + public PyClass getSuperClass() { + return mySuperClass; + } + } + + private PyClassInheritorsSearch() { + super("Pythonid"); + } + + public static Query search(final PyClass superClass) { + final SearchParameters parameters = new SearchParameters(superClass); + return INSTANCE.createUniqueResultsQuery(parameters); + } +} diff --git a/python/src/com/jetbrains/python/psi/search/PyClassInheritorsSearchExecutor.java b/python/src/com/jetbrains/python/psi/search/PyClassInheritorsSearchExecutor.java new file mode 100644 index 000000000000..7ab249e33bc7 --- /dev/null +++ b/python/src/com/jetbrains/python/psi/search/PyClassInheritorsSearchExecutor.java @@ -0,0 +1,34 @@ +package com.jetbrains.python.psi.search; + +import com.intellij.openapi.project.Project; +import com.intellij.psi.search.ProjectScope; +import com.intellij.psi.stubs.StubIndex; +import com.intellij.util.Processor; +import com.intellij.util.QueryExecutor; +import com.jetbrains.python.psi.PyClass; +import com.jetbrains.python.psi.stubs.PySuperClassIndex; + +import java.util.Collection; + +/** + * @author yole + */ +public class PyClassInheritorsSearchExecutor implements QueryExecutor { + public boolean execute(final PyClassInheritorsSearch.SearchParameters queryParameters, final Processor consumer) { + PyClass superClass = queryParameters.getSuperClass(); + Project project = superClass.getProject(); + final Collection candidates = StubIndex.getInstance().get(PySuperClassIndex.KEY, superClass.getName(), project, + ProjectScope.getAllScope(project)); + for(PyClass candidate: candidates) { + final PyClass[] classes = candidate.getSuperClasses(); + if (classes != null) { + for(PyClass superClassCandidate: classes) { + if (superClassCandidate.isEquivalentTo(superClass)) { + if (!consumer.process(superClassCandidate)) return false; + } + } + } + } + return true; + } +} diff --git a/python/src/com/jetbrains/python/psi/stubs/PyClassStub.java b/python/src/com/jetbrains/python/psi/stubs/PyClassStub.java index 27e318f23679..a1858191e422 100644 --- a/python/src/com/jetbrains/python/psi/stubs/PyClassStub.java +++ b/python/src/com/jetbrains/python/psi/stubs/PyClassStub.java @@ -7,4 +7,5 @@ import com.intellij.psi.stubs.NamedStub; import com.jetbrains.python.psi.PyClass; public interface PyClassStub extends NamedStub { + String[] getSuperClasses(); } \ No newline at end of file diff --git a/python/src/com/jetbrains/python/psi/stubs/PySuperClassIndex.java b/python/src/com/jetbrains/python/psi/stubs/PySuperClassIndex.java new file mode 100644 index 000000000000..0ff9d0a02c63 --- /dev/null +++ b/python/src/com/jetbrains/python/psi/stubs/PySuperClassIndex.java @@ -0,0 +1,16 @@ +package com.jetbrains.python.psi.stubs; + +import com.intellij.psi.stubs.StringStubIndexExtension; +import com.intellij.psi.stubs.StubIndexKey; +import com.jetbrains.python.psi.PyClass; + +/** + * @author yole + */ +public class PySuperClassIndex extends StringStubIndexExtension { + public static final StubIndexKey KEY = new StubIndexKey("Py.class.super"); + + public StubIndexKey getKey() { + return KEY; + } +} diff --git a/python/testData/inheritors/simple/A.py b/python/testData/inheritors/simple/A.py new file mode 100644 index 000000000000..49515667b75e --- /dev/null +++ b/python/testData/inheritors/simple/A.py @@ -0,0 +1,9 @@ +class A(object): + pass + +class B(A): + pass + +class C(A): + pass + \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyInheritorsSearchTest.java b/python/testSrc/com/jetbrains/python/PyInheritorsSearchTest.java new file mode 100644 index 000000000000..a3a1d90349c4 --- /dev/null +++ b/python/testSrc/com/jetbrains/python/PyInheritorsSearchTest.java @@ -0,0 +1,41 @@ +package com.jetbrains.python; + +import com.intellij.codeInsight.CodeInsightTestCase; +import com.intellij.openapi.application.PathManager; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.search.ProjectScope; +import com.intellij.psi.stubs.StubIndex; +import com.intellij.testFramework.PsiTestUtil; +import com.jetbrains.python.psi.PyClass; +import com.jetbrains.python.psi.search.PyClassInheritorsSearch; +import com.jetbrains.python.psi.stubs.PyClassNameIndex; + +import java.util.Collection; + +/** + * @author yole + */ +public class PyInheritorsSearchTest extends CodeInsightTestCase { + public void testSimple() throws Exception { + setupProject(); + final PyClass pyClass = findClass("A"); + Collection inheritors = PyClassInheritorsSearch.search(pyClass).findAll(); + assertEquals(2, inheritors.size()); + } + + private void setupProject() throws Exception { + String testName = getTestName(true); + String root = PathManager.getHomePath() + "/plugins/python/testData/inheritors/" + testName; + VirtualFile rootDir = PsiTestUtil.createTestProjectStructure(myProject, myModule, root, myFilesToDelete, false); + PsiTestUtil.addSourceContentToRoots(myModule, rootDir); + PsiDocumentManager.getInstance(myProject).commitAllDocuments(); + } + + private PyClass findClass(final String name) { + final Collection classes = StubIndex.getInstance().get(PyClassNameIndex.KEY, name, myProject, + ProjectScope.getAllScope(myProject)); + assert classes.size() == 1; + return classes.iterator().next(); + } +}