mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
python overriding method navigation
This commit is contained in:
@@ -65,10 +65,12 @@
|
||||
<extensionPoint qualifiedName="Pythonid.typeProvider" interface="com.jetbrains.python.psi.impl.PyTypeProvider"/>
|
||||
<extensionPoint qualifiedName="Pythonid.pySuperMethodsSearch" interface="com.intellij.util.QueryExecutor"/>
|
||||
<extensionPoint qualifiedName="Pythonid.pyClassInheritorsSearch" interface="com.intellij.util.QueryExecutor"/>
|
||||
<extensionPoint qualifiedName="Pythonid.pyOverridingMethodsSearch" interface="com.intellij.util.QueryExecutor"/>
|
||||
</extensionPoints>
|
||||
|
||||
<extensions defaultExtensionNs="Pythonid">
|
||||
<pySuperMethodsSearch implementation="com.jetbrains.python.psi.search.PySuperMethodsSearchExecutor"/>
|
||||
<pyClassInheritorsSearch implementation="com.jetbrains.python.psi.search.PyClassInheritorsSearchExecutor"/>
|
||||
<pyOverridingMethodsSearch implementation="com.jetbrains.python.psi.search.PyOverridingMethodsSearchExecutor"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
|
||||
@@ -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<PsiElement>() {
|
||||
protected String getTitle(final PsiElement elt) {
|
||||
return "Choose Super Method of " + ((PyFunction)elt.getParent()).getName();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected Query<PsiElement> search(final PsiElement elt) {
|
||||
if (!(elt.getParent() instanceof PyFunction)) return null;
|
||||
return PySuperMethodsSearch.search((PyFunction)elt.getParent());
|
||||
}
|
||||
};
|
||||
|
||||
private static final PyLineMarkerNavigator ourSubclassNavigator = new PyLineMarkerNavigator<PyClass>() {
|
||||
protected String getTitle(final PsiElement elt) {
|
||||
return "Choose Subclass of "+ ((PyClass) elt).getName();
|
||||
}
|
||||
|
||||
protected Query<PyClass> search(final PsiElement elt) {
|
||||
return PyClassInheritorsSearch.search((PyClass) elt, true);
|
||||
}
|
||||
};
|
||||
|
||||
private static final PyLineMarkerNavigator ourOverridingMethodNavigator = new PyLineMarkerNavigator<PyFunction>() {
|
||||
protected String getTitle(final PsiElement elt) {
|
||||
return "Choose Overriding Method of " + ((PyFunction) elt).getName();
|
||||
}
|
||||
|
||||
protected Query<PyFunction> 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<PsiElement>() {
|
||||
protected String getTitle(final PsiElement elt) {
|
||||
return "Choose Super Method of " + ((PyFunction)elt.getParent()).getName();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected Query<PsiElement> 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<PsiElement> elements, final Collection<LineMarkerInfo> result) {
|
||||
Set<PyFunction> functions = new HashSet<PyFunction>();
|
||||
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<LineMarkerInfo> result) {
|
||||
if (PyClassInheritorsSearch.search(element, false).findFirst() != null) {
|
||||
PyLineMarkerNavigator navigator = new PyLineMarkerNavigator<PyClass>() {
|
||||
protected String getTitle(final PsiElement elt) {
|
||||
return "Choose Subclass of "+ ((PyClass) elt).getName();
|
||||
}
|
||||
|
||||
protected Query<PyClass> 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<PyFunction> functions, final Collection<LineMarkerInfo> result) {
|
||||
Set<PyClass> classes = new HashSet<PyClass>();
|
||||
final Set<PyFunction> candidates = new HashSet<PyFunction>(functions);
|
||||
for(PyFunction function: functions) {
|
||||
PyClass pyClass = function.getContainingClass();
|
||||
if (pyClass != null && function.getName() != null) {
|
||||
classes.add(pyClass);
|
||||
}
|
||||
else {
|
||||
candidates.remove(function);
|
||||
}
|
||||
}
|
||||
final Set<PyFunction> overridden = new HashSet<PyFunction>();
|
||||
for(PyClass pyClass: classes) {
|
||||
PyClassInheritorsSearch.search(pyClass, true).forEach(new Processor<PyClass>() {
|
||||
public boolean process(final PyClass pyClass) {
|
||||
for (Iterator<PyFunction> 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<PyFunction, PyOverridingMethodsSearch.SearchParameters> {
|
||||
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<PyFunction> search(PyFunction function, boolean checkDeep) {
|
||||
if (function.getContainingClass() == null) return EmptyQuery.getEmptyQuery();
|
||||
return INSTANCE.createUniqueResultsQuery(new SearchParameters(function, checkDeep));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<PyFunction, PyOverridingMethodsSearch.SearchParameters> {
|
||||
public boolean execute(final PyOverridingMethodsSearch.SearchParameters queryParameters, final Processor<PyFunction> consumer) {
|
||||
final PyFunction baseMethod = queryParameters.getFunction();
|
||||
PyClass containingClass = baseMethod.getContainingClass();
|
||||
return PyClassInheritorsSearch.search(containingClass, queryParameters.isCheckDeep()).forEach(new Processor<PyClass>() {
|
||||
public boolean process(final PyClass pyClass) {
|
||||
PyFunction overridingMethod = pyClass.findMethodByName(baseMethod.getName());
|
||||
//noinspection SimplifiableIfStatement
|
||||
if (overridingMethod != null) {
|
||||
return consumer.process(overridingMethod);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user