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