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();
+ }
+}