diff --git a/python/src/META-INF/python-plugin-common.xml b/python/src/META-INF/python-plugin-common.xml
index e18df4974244..084e536a71a2 100644
--- a/python/src/META-INF/python-plugin-common.xml
+++ b/python/src/META-INF/python-plugin-common.xml
@@ -55,7 +55,7 @@
serviceImplementation="com.jetbrains.python.psi.impl.PyBuiltinCache"/>
-
+
diff --git a/python/src/com/jetbrains/python/PyLineMarkerNavigator.java b/python/src/com/jetbrains/python/PyLineMarkerNavigator.java
deleted file mode 100644
index e25f31123074..000000000000
--- a/python/src/com/jetbrains/python/PyLineMarkerNavigator.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package com.jetbrains.python;
-
-import com.intellij.codeInsight.daemon.impl.GutterIconNavigationHandler;
-import com.intellij.codeInsight.daemon.impl.PsiElementListNavigator;
-import com.intellij.ide.util.DefaultPsiElementCellRenderer;
-import com.intellij.psi.NavigatablePsiElement;
-import com.intellij.psi.PsiElement;
-import com.intellij.util.Processor;
-import com.jetbrains.python.psi.PyFunction;
-import com.jetbrains.python.psi.search.PySuperMethodsSearch;
-
-import java.awt.event.MouseEvent;
-import java.util.List;
-import java.util.ArrayList;
-
-/**
- * @author yole
- */
-public class PyLineMarkerNavigator implements GutterIconNavigationHandler {
- public void navigate(final MouseEvent e, final PsiElement elt) {
- if (elt.getParent() instanceof PyFunction) {
- final PyFunction function = (PyFunction)elt.getParent();
- final List navElements = new ArrayList();
- PySuperMethodsSearch.search(function).forEach(new Processor() {
- public boolean process(final PsiElement psiElement) {
- if (psiElement instanceof NavigatablePsiElement) {
- navElements.add((NavigatablePsiElement) psiElement);
- }
- return true;
- }
- });
- final NavigatablePsiElement[] methods = navElements.toArray(new NavigatablePsiElement[navElements.size()]);
- PsiElementListNavigator.openTargets(e, methods, "Choose Super Method of " + function.getName(),
- new DefaultPsiElementCellRenderer());
- }
- }
-}
diff --git a/python/src/com/jetbrains/python/PyLineMarkerProvider.java b/python/src/com/jetbrains/python/PyLineMarkerProvider.java
deleted file mode 100644
index d805884b8957..000000000000
--- a/python/src/com/jetbrains/python/PyLineMarkerProvider.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.jetbrains.python;
-
-import com.intellij.codeHighlighting.Pass;
-import com.intellij.codeInsight.daemon.impl.LineMarkerInfo;
-import com.intellij.codeInsight.daemon.impl.LineMarkerProvider;
-import com.intellij.lang.ASTNode;
-import com.intellij.openapi.util.IconLoader;
-import com.intellij.psi.PsiElement;
-import com.jetbrains.python.psi.PyFunction;
-import com.jetbrains.python.psi.search.PySuperMethodsSearch;
-import org.jetbrains.annotations.Nullable;
-
-import javax.swing.*;
-import java.util.List;
-import java.util.Collection;
-
-/**
- * @author yole
- */
-public class PyLineMarkerProvider implements LineMarkerProvider {
- private static final Icon OVERRIDING_METHOD_ICON = IconLoader.getIcon("/gutter/overridingMethod.png");
-
- public LineMarkerInfo getLineMarkerInfo(final PsiElement element) {
- final ASTNode node = element.getNode();
- if (node != null && node.getElementType() == PyTokenTypes.IDENTIFIER && element.getParent() instanceof PyFunction) {
- final PyFunction function = (PyFunction)element.getParent();
- return getMethodMarker(element, function);
- }
- return null;
- }
-
- @Nullable
- 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
- LineMarkerInfo info = new LineMarkerInfo(element, element.getTextRange().getStartOffset(),
- OVERRIDING_METHOD_ICON, Pass.UPDATE_ALL, null, new PyLineMarkerNavigator());
- return info;
- }
- return null;
- }
-
- public void collectSlowLineMarkers(final List elements, final Collection result) {
- }
-}
diff --git a/python/src/com/jetbrains/python/codeInsight/PyLineMarkerNavigator.java b/python/src/com/jetbrains/python/codeInsight/PyLineMarkerNavigator.java
new file mode 100644
index 000000000000..7d63bbd6c253
--- /dev/null
+++ b/python/src/com/jetbrains/python/codeInsight/PyLineMarkerNavigator.java
@@ -0,0 +1,40 @@
+package com.jetbrains.python.codeInsight;
+
+import com.intellij.codeInsight.daemon.impl.GutterIconNavigationHandler;
+import com.intellij.codeInsight.daemon.impl.PsiElementListNavigator;
+import com.intellij.ide.util.DefaultPsiElementCellRenderer;
+import com.intellij.psi.NavigatablePsiElement;
+import com.intellij.psi.PsiElement;
+import com.intellij.util.Processor;
+import com.intellij.util.Query;
+import org.jetbrains.annotations.Nullable;
+
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author yole
+ */
+public abstract class PyLineMarkerNavigator implements GutterIconNavigationHandler {
+ public void navigate(final MouseEvent e, final PsiElement elt) {
+ final List navElements = new ArrayList();
+ Query elementQuery = search(elt);
+ if (elementQuery == null) return;
+ elementQuery.forEach(new Processor() {
+ public boolean process(final T psiElement) {
+ if (psiElement instanceof NavigatablePsiElement) {
+ navElements.add((NavigatablePsiElement)psiElement);
+ }
+ return true;
+ }
+ });
+ final NavigatablePsiElement[] methods = navElements.toArray(new NavigatablePsiElement[navElements.size()]);
+ PsiElementListNavigator.openTargets(e, methods, getTitle(elt), new DefaultPsiElementCellRenderer());
+ }
+
+ protected abstract String getTitle(PsiElement elt);
+
+ @Nullable
+ protected abstract Query search(PsiElement elt);
+}
diff --git a/python/src/com/jetbrains/python/codeInsight/PyLineMarkerProvider.java b/python/src/com/jetbrains/python/codeInsight/PyLineMarkerProvider.java
new file mode 100644
index 000000000000..a28eae9994c9
--- /dev/null
+++ b/python/src/com/jetbrains/python/codeInsight/PyLineMarkerProvider.java
@@ -0,0 +1,81 @@
+package com.jetbrains.python.codeInsight;
+
+import com.intellij.codeHighlighting.Pass;
+import com.intellij.codeInsight.daemon.impl.LineMarkerInfo;
+import com.intellij.codeInsight.daemon.impl.LineMarkerProvider;
+import com.intellij.lang.ASTNode;
+import com.intellij.openapi.util.IconLoader;
+import com.intellij.psi.PsiElement;
+import com.intellij.util.Query;
+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 org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author yole
+ */
+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");
+
+ public LineMarkerInfo getLineMarkerInfo(final PsiElement element) {
+ final ASTNode node = element.getNode();
+ if (node != null && node.getElementType() == PyTokenTypes.IDENTIFIER && element.getParent() instanceof PyFunction) {
+ final PyFunction function = (PyFunction)element.getParent();
+ return getMethodMarker(element, function);
+ }
+ return null;
+ }
+
+ @Nullable
+ 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);
+ }
+ return null;
+ }
+
+ public void collectSlowLineMarkers(final List elements, final Collection result) {
+ for(PsiElement element: elements) {
+ if (element instanceof PyClass) {
+ collectInheritingClasses((PyClass) element, 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));
+ }
+ }
+}