diff --git a/python/src/com/jetbrains/python/structureView/PyInheritedMembersFilter.java b/python/src/com/jetbrains/python/structureView/PyInheritedMembersFilter.java new file mode 100644 index 000000000000..ae89e4c39316 --- /dev/null +++ b/python/src/com/jetbrains/python/structureView/PyInheritedMembersFilter.java @@ -0,0 +1,50 @@ +package com.jetbrains.python.structureView; + +import com.intellij.ide.IdeBundle; +import com.intellij.ide.util.treeView.smartTree.ActionPresentation; +import com.intellij.ide.util.treeView.smartTree.ActionPresentationData; +import com.intellij.ide.util.treeView.smartTree.Filter; +import com.intellij.ide.util.treeView.smartTree.TreeElement; +import com.intellij.openapi.util.IconLoader; +import com.jetbrains.python.psi.PyElement; +import org.jetbrains.annotations.NotNull; + +/** + * @author vlan + */ +public class PyInheritedMembersFilter implements Filter { + private static final String ID = "SHOW_INHERITED"; + + @Override + public boolean isReverted() { + return true; + } + + @Override + public boolean isVisible(TreeElement treeNode) { + if (treeNode instanceof PyStructureViewElement) { + final PyStructureViewElement sve = (PyStructureViewElement)treeNode; + return !sve.isInherited(); + } + return true; + } + + @NotNull + @Override + public String getName() { + return ID; + } + + @Override + public String toString() { + return getName(); + } + + @NotNull + @Override + public ActionPresentation getPresentation() { + return new ActionPresentationData(IdeBundle.message("action.structureview.show.inherited"), + null, + IconLoader.getIcon("/hierarchy/supertypes.png")); + } +} diff --git a/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java b/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java index 9ecb0eb5b8a3..fa7a06b61da2 100644 --- a/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java +++ b/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java @@ -2,18 +2,17 @@ package com.jetbrains.python.structureView; import com.intellij.ide.structureView.StructureViewTreeElement; import com.intellij.navigation.ItemPresentation; +import com.intellij.openapi.editor.colors.CodeInsightColors; import com.intellij.openapi.editor.colors.TextAttributesKey; import com.intellij.openapi.util.Iconable; import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.ui.LayeredIcon; import com.intellij.util.Function; -import com.intellij.util.PlatformIcons; import com.jetbrains.python.PyIcons; import com.jetbrains.python.PyNames; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.ParamHelper; -import com.jetbrains.python.psi.impl.PyBuiltinCache; import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -39,20 +38,26 @@ public class PyStructureViewElement implements StructureViewTreeElement { private PyElement myElement; private Visibility myVisibility; private Icon myIcon; + private boolean myInherited; - public PyStructureViewElement(PyElement element, Visibility vis) { + public PyStructureViewElement(PyElement element, Visibility vis, boolean inherited) { myElement = element; myVisibility = vis; + myInherited = inherited; } public PyStructureViewElement(PyElement element) { - this(element, Visibility.NORMAL); + this(element, Visibility.NORMAL, false); } public PyElement getValue() { return myElement; } + public boolean isInherited() { + return myInherited; + } + public void navigate(boolean requestFocus) { myElement.navigate(requestFocus); } @@ -69,46 +74,65 @@ public class PyStructureViewElement implements StructureViewTreeElement { myIcon = icon; } + @Override + public boolean equals(Object o) { + if (o instanceof StructureViewTreeElement) { + final Object value = ((StructureViewTreeElement)o).getValue(); + final String name = myElement.getName(); + if (value instanceof PyElement && name != null) { + return name.equals(((PyElement)value).getName()); + } + } + return false; + } + + @Override + public int hashCode() { + final String name = myElement.getName(); + return name != null ? name.hashCode() : 0; + } + public StructureViewTreeElement[] getChildren() { - final Set childrenElements = new LinkedHashSet(); - myElement.acceptChildren(new PyElementVisitor() { - @Override - public void visitElement(PsiElement element) { - if (element instanceof PyClass || element instanceof PyFunction || - (!(myElement instanceof PyClass) && isWorthyItem(element))) { - childrenElements.add((PyElement)element); - } - else { - element.acceptChildren(this); - } - } - }); - final Collection children = new ArrayList(); - for (PyElement element : childrenElements) { - final Visibility vis; - if (PsiTreeUtil.getParentOfType(element, PyFunction.class) != null) { - // whatever is defined inside a def, is hidden - vis = Visibility.INVISIBLE; - } - else { - vis = getVisibilityByName(element.getName()); - } - final PyStructureViewElement e = new PyStructureViewElement(element, vis); - children.add(e); - if (element instanceof PyClass && element.isValid()) { - PyClass the_exception = PyBuiltinCache.getInstance(element).getClass("Exception"); - final PyClass cls = (PyClass)element; - for (PyClass anc : cls.iterateAncestorClasses()) { - if (anc == the_exception) { - e.setIcon(PlatformIcons.EXCEPTION_CLASS_ICON); - break; - } + final Collection children = new LinkedHashSet(); + for (PyElement e : getElementChildren(myElement)) { + children.add(new PyStructureViewElement(e, getElementVisibility(e), false)); + } + if (myElement instanceof PyClass) { + for (PyClass c : ((PyClass)myElement).iterateAncestorClasses()) { + for (PyElement e: getElementChildren(c)) { + children.add(new PyStructureViewElement(e, getElementVisibility(e), true)); } } } + return children.toArray(new StructureViewTreeElement[children.size()]); + } + + private static Visibility getElementVisibility(PyElement element) { + if (!(element instanceof PyTargetExpression) && PsiTreeUtil.getParentOfType(element, PyFunction.class) != null) { + return Visibility.INVISIBLE; + } + else { + return getVisibilityByName(element.getName()); + } + } + + private static Collection getElementChildren(final PyElement element) { + final Collection children = new ArrayList(); + element.acceptChildren(new PyElementVisitor() { + @Override + public void visitElement(PsiElement e) { + if (e instanceof PyClass || e instanceof PyFunction || + (!(element instanceof PyClass) && isWorthyItem(e))) { + children.add((PyElement)e); + } + else { + e.acceptChildren(this); + } + } + }); final Collection attrs = new ArrayList(); - if (myElement instanceof PyClass) { - final PyClass c = (PyClass)myElement; + if (element instanceof PyClass) { + final PyClass c = (PyClass)element; final Comparator comparator = new Comparator() { @Override public int compare(PyTargetExpression e1, PyTargetExpression e2) { @@ -126,10 +150,10 @@ public class PyStructureViewElement implements StructureViewTreeElement { } for (PyTargetExpression e : attrs) { if (e.isValid()) { - children.add(new PyStructureViewElement(e, getVisibilityByName(e.getName()))); + children.add(e); } } - return children.toArray(new StructureViewTreeElement[children.size()]); + return children; } private static Visibility getVisibilityByName(@Nullable String name) { @@ -199,6 +223,9 @@ public class PyStructureViewElement implements StructureViewTreeElement { @Nullable public TextAttributesKey getTextAttributesKey() { + if (isInherited()) { + return CodeInsightColors.NOT_USED_ELEMENT_ATTRIBUTES; + } return null; } diff --git a/python/src/com/jetbrains/python/structureView/PyStructureViewModel.java b/python/src/com/jetbrains/python/structureView/PyStructureViewModel.java index 01d2970dbab7..2ca802c0ecf3 100644 --- a/python/src/com/jetbrains/python/structureView/PyStructureViewModel.java +++ b/python/src/com/jetbrains/python/structureView/PyStructureViewModel.java @@ -3,6 +3,7 @@ package com.jetbrains.python.structureView; import com.intellij.ide.structureView.StructureViewModel; import com.intellij.ide.structureView.StructureViewModelBase; import com.intellij.ide.structureView.StructureViewTreeElement; +import com.intellij.ide.util.treeView.smartTree.Filter; import com.intellij.ide.util.treeView.smartTree.Sorter; import com.intellij.psi.PsiFile; import com.jetbrains.python.psi.*; @@ -34,6 +35,14 @@ public class PyStructureViewModel extends StructureViewModelBase implements Stru return element instanceof PyClass; } + @NotNull + @Override + public Filter[] getFilters() { + return new Filter[] { + new PyInheritedMembersFilter(), + }; + } + @Override public boolean isAutoExpand(StructureViewTreeElement element) { return element.getValue() instanceof PsiFile; diff --git a/python/testData/structureView/inherited.py b/python/testData/structureView/inherited.py new file mode 100644 index 000000000000..4d456332117c --- /dev/null +++ b/python/testData/structureView/inherited.py @@ -0,0 +1,6 @@ +class C(object): + def f(self, x): + self.x = x + + def __str__(self): + return self.x \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyStructureViewTest.java b/python/testSrc/com/jetbrains/python/PyStructureViewTest.java index fac9f3eed1ee..f1c4292c0b51 100644 --- a/python/testSrc/com/jetbrains/python/PyStructureViewTest.java +++ b/python/testSrc/com/jetbrains/python/PyStructureViewTest.java @@ -24,10 +24,12 @@ public class PyStructureViewTest extends PyLightFixtureTestCase { " D1(C)\n" + " D2(C)\n" + " D3(lib1.C)\n" + - " D4(foo.bar.C)\n"); + " D4(foo.bar.C)\n", + false); } - public void testAttributes() { // PY-3371 + // PY-3371 + public void testAttributes() { myFixture.configureByFile(TEST_DIRECTORY + "attributes.py"); doTest("-attributes.py\n" + " -B(object)\n" + @@ -48,13 +50,38 @@ public class PyStructureViewTest extends PyLightFixtureTestCase { " i3\n" + " i4\n" + " i5\n" + - " g2\n"); + " g2\n", + false); } - private void doTest(final String expected) { + // PY-3936 + public void testInherited() { + myFixture.configureByFile(TEST_DIRECTORY + "inherited.py"); + doTest("-inherited.py\n" + + " -C(object)\n" + + " f(self, x)\n" + + " __str__(self)\n" + + " x\n" + + " __delattr__(self, name)\n" + + " __getattribute__(self, name)\n" + + " __hash__(self)\n" + + " __init__(self)\n" + + " __new__(cls, *more)\n" + + " __reduce_ex__(self, *args, **kwargs)\n" + + " __reduce__(self, *args, **kwargs)\n" + + " __repr__(self)\n" + + " __setattr__(self, name, value)\n" + + " __class__\n" + + " __dict__\n" + + " __doc__\n", + true); + } + + private void doTest(final String expected, final boolean inherited) { myFixture.testStructureView(new Consumer() { @Override public void consume(StructureViewComponent component) { + component.setActionActive("SHOW_INHERITED", !inherited); assertTreeEqual(component.getTree(), expected); } });