From b849f3989b7fc5ba3ed4bbf6e00994c532c08fbd Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Fri, 27 May 2011 19:03:09 +0400 Subject: [PATCH] Show instance attributes in structure view (PY-3371) --- .../structureView/PyStructureViewElement.java | 74 ++++++++++++------- python/testData/structureView/attributes.py | 32 ++++++++ .../testData/structureView/baseClassNames.py | 2 +- .../jetbrains/python/PyStructureViewTest.java | 52 ++++++++++--- 4 files changed, 120 insertions(+), 40 deletions(-) create mode 100644 python/testData/structureView/attributes.py diff --git a/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java b/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java index ffdb88b155bb..ef06a5df0b87 100644 --- a/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java +++ b/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java @@ -17,9 +17,7 @@ import com.jetbrains.python.psi.impl.PyBuiltinCache; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import java.util.Arrays; -import java.util.LinkedHashSet; -import java.util.Set; +import java.util.*; import static com.intellij.openapi.util.text.StringUtil.join; import static com.intellij.openapi.util.text.StringUtil.notNullize; @@ -75,7 +73,8 @@ public class PyStructureViewElement implements StructureViewTreeElement { myElement.acceptChildren(new PyElementVisitor() { @Override public void visitElement(PsiElement element) { - if (isWorthyClassItem(element)) { + if (element instanceof PyClass || element instanceof PyFunction || + (!(myElement instanceof PyClass) && isWorthyItem(element))) { childrenElements.add((PyElement)element); } else { @@ -83,47 +82,68 @@ public class PyStructureViewElement implements StructureViewTreeElement { } } }); - - StructureViewTreeElement[] children = new StructureViewTreeElement[childrenElements.size()]; - int i = 0; + final Collection children = new ArrayList(); for (PyElement element : childrenElements) { - // look at functions and predefined __names__ - Visibility vis = Visibility.NORMAL; + final Visibility vis; if (PsiTreeUtil.getParentOfType(element, PyFunction.class) != null) { // whatever is defined inside a def, is hidden vis = Visibility.INVISIBLE; } else { - String name = element.getName(); - if (name != null && name.startsWith("__")) { - if (PyNames.UnderscoredAttributes.contains(name)) { - vis = Visibility.PREDEFINED; - } - else { - vis = Visibility.PRIVATE; - } - } + vis = getVisibilityByName(element.getName()); } - children[i] = new PyStructureViewElement(element, vis); + 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) { - ((PyStructureViewElement)(children[i])).setIcon(Icons.EXCEPTION_CLASS_ICON); + e.setIcon(Icons.EXCEPTION_CLASS_ICON); break; } } } - i += 1; } - - return children; + final Collection attrs = new ArrayList(); + if (myElement instanceof PyClass) { + final PyClass c = (PyClass)myElement; + final Comparator comparator = new Comparator() { + @Override + public int compare(PyTargetExpression e1, PyTargetExpression e2) { + final String n1 = e1.getName(); + final String n2 = e2.getName(); + return (n1 != null && n2 != null) ? n1.compareTo(n2) : 0; + } + }; + final List instanceAttrs = c.getInstanceAttributes(); + final List classAttrs = c.getClassAttributes(); + Collections.sort(instanceAttrs, comparator); + Collections.sort(classAttrs, comparator); + attrs.addAll(classAttrs); + attrs.addAll(instanceAttrs); + } + for (PyTargetExpression e : attrs) { + if (e.isValid()) { + children.add(new PyStructureViewElement(e, getVisibilityByName(e.getName()))); + } + } + return children.toArray(new StructureViewTreeElement[children.size()]); } - static boolean isWorthyClassItem(PsiElement element) { - if (element instanceof PyClass) return true; - if (element instanceof PyFunction) return true; + private static Visibility getVisibilityByName(@Nullable String name) { + if (name != null && name.startsWith("__")) { + if (PyNames.UnderscoredAttributes.contains(name)) { + return Visibility.PREDEFINED; + } + else { + return Visibility.PRIVATE; + } + } + return Visibility.NORMAL; + } + + private static boolean isWorthyItem(PsiElement element) { if ((element instanceof PyTargetExpression) && ((PyTargetExpression)element).getQualifier() == null) { PsiElement e = element.getParent(); if (e instanceof PyAssignmentStatement) { @@ -142,8 +162,6 @@ public class PyStructureViewElement implements StructureViewTreeElement { return false; } - - public ItemPresentation getPresentation() { return new ItemPresentation() { public String getPresentableText() { diff --git a/python/testData/structureView/attributes.py b/python/testData/structureView/attributes.py new file mode 100644 index 000000000000..f7bb766fe1a9 --- /dev/null +++ b/python/testData/structureView/attributes.py @@ -0,0 +1,32 @@ +class B(object): + c1 = 0 + + def f(self, x): + self.i1 = x + l1 = self.i1 + self.i2 = l1 + + def __init__(self, x, y): + self.i2 = x + self.i3 = y + + @classmethod + def g(cls, x): + cls.c2 = cls.c1 + +g1 = "foo" + +class C(B): + c2 = -1 + + def __init__(self, x, y): + super(C, self).__init__(x, y) + self.i3 = -2 + self.i4 = -3 + + def h(self): + self.i5 = self.i6 + + c3 = -4 + +g2 = "bar" diff --git a/python/testData/structureView/baseClassNames.py b/python/testData/structureView/baseClassNames.py index ea4975f96699..b6ab4b132741 100644 --- a/python/testData/structureView/baseClassNames.py +++ b/python/testData/structureView/baseClassNames.py @@ -2,7 +2,7 @@ import lib1 class B1: def f(self, x): - return x + self.y + return x class B2(object): @staticmethod diff --git a/python/testSrc/com/jetbrains/python/PyStructureViewTest.java b/python/testSrc/com/jetbrains/python/PyStructureViewTest.java index fbb042cea039..d4a4a2cededa 100644 --- a/python/testSrc/com/jetbrains/python/PyStructureViewTest.java +++ b/python/testSrc/com/jetbrains/python/PyStructureViewTest.java @@ -10,21 +10,51 @@ import static com.intellij.testFramework.PlatformTestUtil.assertTreeEqual; * @author vlan */ public class PyStructureViewTest extends PyLightFixtureTestCase { + private static String TEST_DIRECTORY = "structureView/"; + public void testBaseClassNames() { - myFixture.configureByFiles("structureView/baseClassNames.py", - "structureView/lib1.py"); + myFixture.configureByFiles(TEST_DIRECTORY + "baseClassNames.py", + TEST_DIRECTORY + "lib1.py"); + doTest("-baseClassNames.py\n" + + " -B1\n" + + " f(self, x)\n" + + " -B2(object)\n" + + " g(x)\n" + + " C(B1, B2)\n" + + " D1(C)\n" + + " D2(C)\n" + + " D3(lib1.C)\n"); + } + + public void testAttributes() { // PY-3371 + myFixture.configureByFile(TEST_DIRECTORY + "attributes.py"); + doTest("-attributes.py\n" + + " -B(object)\n" + + " f(self, x)\n" + + " __init__(self, x, y)\n" + + " g(cls, x)\n" + + " c1\n" + + " c2\n" + + " i1\n" + + " i2\n" + + " i3\n" + + " g1\n" + + " -C(B)\n" + + " __init__(self, x, y)\n" + + " h(self)\n" + + " c2\n" + + " c3\n" + + " i3\n" + + " i4\n" + + " i5\n" + + " g2\n"); + } + + private void doTest(final String expected) { myFixture.testStructureView(new Consumer() { @Override public void consume(StructureViewComponent component) { - assertTreeEqual(component.getTree(), "-baseClassNames.py\n" + - " -B1\n" + - " f(self, x)\n" + - " -B2(object)\n" + - " g(x)\n" + - " C(B1, B2)\n" + - " D1(C)\n" + - " D2(C)\n" + - " D3(lib1.C)\n"); + assertTreeEqual(component.getTree(), expected); } }); }