diff --git a/python/src/META-INF/python-plugin-common.xml b/python/src/META-INF/python-plugin-common.xml
index 084e536a71a2..e7f6009fdcd8 100644
--- a/python/src/META-INF/python-plugin-common.xml
+++ b/python/src/META-INF/python-plugin-common.xml
@@ -65,10 +65,12 @@
+
+
diff --git a/python/src/com/jetbrains/python/codeInsight/PyLineMarkerProvider.java b/python/src/com/jetbrains/python/codeInsight/PyLineMarkerProvider.java
index a28eae9994c9..5608435d1e73 100644
--- a/python/src/com/jetbrains/python/codeInsight/PyLineMarkerProvider.java
+++ b/python/src/com/jetbrains/python/codeInsight/PyLineMarkerProvider.java
@@ -7,16 +7,21 @@ import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.IconLoader;
import com.intellij.psi.PsiElement;
import com.intellij.util.Query;
+import com.intellij.util.Processor;
+import com.intellij.util.containers.HashSet;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.psi.search.PyClassInheritorsSearch;
import com.jetbrains.python.psi.search.PySuperMethodsSearch;
+import com.jetbrains.python.psi.search.PyOverridingMethodsSearch;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.Collection;
import java.util.List;
+import java.util.Set;
+import java.util.Iterator;
/**
* @author yole
@@ -25,6 +30,38 @@ public class PyLineMarkerProvider implements LineMarkerProvider {
private static final Icon OVERRIDING_METHOD_ICON = IconLoader.getIcon("/gutter/overridingMethod.png");
private static final Icon OVERRIDDEN_ICON = IconLoader.getIcon("/gutter/overridenMethod.png");
+ private static final PyLineMarkerNavigator ourSuperMethodNavigator = new PyLineMarkerNavigator() {
+ protected String getTitle(final PsiElement elt) {
+ return "Choose Super Method of " + ((PyFunction)elt.getParent()).getName();
+ }
+
+ @Nullable
+ protected Query search(final PsiElement elt) {
+ if (!(elt.getParent() instanceof PyFunction)) return null;
+ return PySuperMethodsSearch.search((PyFunction)elt.getParent());
+ }
+ };
+
+ private static final PyLineMarkerNavigator ourSubclassNavigator = new PyLineMarkerNavigator() {
+ protected String getTitle(final PsiElement elt) {
+ return "Choose Subclass of "+ ((PyClass) elt).getName();
+ }
+
+ protected Query search(final PsiElement elt) {
+ return PyClassInheritorsSearch.search((PyClass) elt, true);
+ }
+ };
+
+ private static final PyLineMarkerNavigator ourOverridingMethodNavigator = new PyLineMarkerNavigator() {
+ protected String getTitle(final PsiElement elt) {
+ return "Choose Overriding Method of " + ((PyFunction) elt).getName();
+ }
+
+ protected Query search(final PsiElement elt) {
+ return PyOverridingMethodsSearch.search((PyFunction) elt, true);
+ }
+ };
+
public LineMarkerInfo getLineMarkerInfo(final PsiElement element) {
final ASTNode node = element.getNode();
if (node != null && node.getElementType() == PyTokenTypes.IDENTIFIER && element.getParent() instanceof PyFunction) {
@@ -38,44 +75,63 @@ public class PyLineMarkerProvider implements LineMarkerProvider {
private static LineMarkerInfo getMethodMarker(final PsiElement element, final PyFunction function) {
if (PySuperMethodsSearch.search(function).findFirst() != null) {
// TODO: show "implementing" instead of "overriding" icon for Python implementations of Java interface methods
- PyLineMarkerNavigator markerNavigator = new PyLineMarkerNavigator() {
- protected String getTitle(final PsiElement elt) {
- return "Choose Super Method of " + ((PyFunction)elt.getParent()).getName();
- }
-
- @Nullable
- protected Query search(final PsiElement elt) {
- if (!(elt.getParent() instanceof PyFunction)) return null;
- return PySuperMethodsSearch.search((PyFunction)elt.getParent());
- }
- };
return new LineMarkerInfo(element, element.getTextRange().getStartOffset(), OVERRIDING_METHOD_ICON, Pass.UPDATE_ALL,
- null, markerNavigator);
+ null, ourSuperMethodNavigator);
}
return null;
}
public void collectSlowLineMarkers(final List elements, final Collection result) {
+ Set functions = new HashSet();
for(PsiElement element: elements) {
if (element instanceof PyClass) {
collectInheritingClasses((PyClass) element, result);
}
+ else if (element instanceof PyFunction) {
+ functions.add((PyFunction)element);
+ }
}
+ collectOverridingMethods(functions, result);
}
private static void collectInheritingClasses(final PyClass element, final Collection result) {
if (PyClassInheritorsSearch.search(element, false).findFirst() != null) {
- PyLineMarkerNavigator navigator = new PyLineMarkerNavigator() {
- protected String getTitle(final PsiElement elt) {
- return "Choose Subclass of "+ ((PyClass) elt).getName();
- }
-
- protected Query search(final PsiElement elt) {
- return PyClassInheritorsSearch.search((PyClass) elt, true);
- }
- };
result.add(new LineMarkerInfo(element, element.getTextOffset(), OVERRIDDEN_ICON, Pass.UPDATE_OVERRIDEN_MARKERS,
- null, navigator));
+ null, ourSubclassNavigator));
+ }
+ }
+
+ private static void collectOverridingMethods(final Set functions, final Collection result) {
+ Set classes = new HashSet();
+ final Set candidates = new HashSet(functions);
+ for(PyFunction function: functions) {
+ PyClass pyClass = function.getContainingClass();
+ if (pyClass != null && function.getName() != null) {
+ classes.add(pyClass);
+ }
+ else {
+ candidates.remove(function);
+ }
+ }
+ final Set overridden = new HashSet();
+ for(PyClass pyClass: classes) {
+ PyClassInheritorsSearch.search(pyClass, true).forEach(new Processor() {
+ public boolean process(final PyClass pyClass) {
+ for (Iterator it = candidates.iterator(); it.hasNext();) {
+ PyFunction func = it.next();
+ if (pyClass.findMethodByName(func.getName()) != null) {
+ overridden.add(func);
+ it.remove();
+ }
+ }
+ return !candidates.isEmpty();
+ }
+ });
+ if (candidates.isEmpty()) break;
+ }
+ for(PyFunction func: overridden) {
+ result.add(new LineMarkerInfo(func, func.getTextOffset(), OVERRIDDEN_ICON, Pass.UPDATE_OVERRIDEN_MARKERS, null,
+ ourOverridingMethodNavigator));
}
}
}
diff --git a/python/src/com/jetbrains/python/psi/search/PyOverridingMethodsSearch.java b/python/src/com/jetbrains/python/psi/search/PyOverridingMethodsSearch.java
new file mode 100644
index 000000000000..90dacec80eb3
--- /dev/null
+++ b/python/src/com/jetbrains/python/psi/search/PyOverridingMethodsSearch.java
@@ -0,0 +1,41 @@
+package com.jetbrains.python.psi.search;
+
+import com.intellij.psi.search.searches.ExtensibleQueryFactory;
+import com.intellij.util.Query;
+import com.intellij.util.EmptyQuery;
+import com.jetbrains.python.psi.PyFunction;
+
+/**
+ * @author yole
+ */
+public class PyOverridingMethodsSearch extends ExtensibleQueryFactory {
+ public static final PyOverridingMethodsSearch INSTANCE = new PyOverridingMethodsSearch();
+
+ public static class SearchParameters {
+ private PyFunction myFunction;
+ private boolean myCheckDeep;
+
+ public SearchParameters(final PyFunction function, final boolean checkDeep) {
+ myFunction = function;
+ myCheckDeep = checkDeep;
+ }
+
+ public PyFunction getFunction() {
+ return myFunction;
+ }
+
+ public boolean isCheckDeep() {
+ return myCheckDeep;
+ }
+ }
+
+ private PyOverridingMethodsSearch() {
+ super("Pythonid");
+ }
+
+ public static Query search(PyFunction function, boolean checkDeep) {
+ if (function.getContainingClass() == null) return EmptyQuery.getEmptyQuery();
+ return INSTANCE.createUniqueResultsQuery(new SearchParameters(function, checkDeep));
+ }
+}
+
diff --git a/python/src/com/jetbrains/python/psi/search/PyOverridingMethodsSearchExecutor.java b/python/src/com/jetbrains/python/psi/search/PyOverridingMethodsSearchExecutor.java
new file mode 100644
index 000000000000..34cba0623e34
--- /dev/null
+++ b/python/src/com/jetbrains/python/psi/search/PyOverridingMethodsSearchExecutor.java
@@ -0,0 +1,26 @@
+package com.jetbrains.python.psi.search;
+
+import com.intellij.util.Processor;
+import com.intellij.util.QueryExecutor;
+import com.jetbrains.python.psi.PyClass;
+import com.jetbrains.python.psi.PyFunction;
+
+/**
+ * @author yole
+ */
+public class PyOverridingMethodsSearchExecutor implements QueryExecutor {
+ public boolean execute(final PyOverridingMethodsSearch.SearchParameters queryParameters, final Processor consumer) {
+ final PyFunction baseMethod = queryParameters.getFunction();
+ PyClass containingClass = baseMethod.getContainingClass();
+ return PyClassInheritorsSearch.search(containingClass, queryParameters.isCheckDeep()).forEach(new Processor() {
+ public boolean process(final PyClass pyClass) {
+ PyFunction overridingMethod = pyClass.findMethodByName(baseMethod.getName());
+ //noinspection SimplifiableIfStatement
+ if (overridingMethod != null) {
+ return consumer.process(overridingMethod);
+ }
+ return true;
+ }
+ });
+ }
+}