diff --git a/java/java-structure-view/src/com/intellij/ide/structureView/impl/java/JavaClassTreeElementBase.java b/java/java-structure-view/src/com/intellij/ide/structureView/impl/java/JavaClassTreeElementBase.java index 9c3bfd8d84ad..c17df7d7b71e 100644 --- a/java/java-structure-view/src/com/intellij/ide/structureView/impl/java/JavaClassTreeElementBase.java +++ b/java/java-structure-view/src/com/intellij/ide/structureView/impl/java/JavaClassTreeElementBase.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.ide.structureView.impl.java; import com.intellij.ide.structureView.impl.common.PsiTreeElementBase; @@ -27,9 +13,9 @@ import com.intellij.psi.*; import com.intellij.psi.util.PsiUtil; import com.intellij.util.ui.UIUtil; -public abstract class JavaClassTreeElementBase extends PsiTreeElementBase implements AccessLevelProvider, - ColoredItemPresentation, - LocationPresentation { +public abstract class JavaClassTreeElementBase extends PsiTreeElementBase + implements AccessLevelProvider, ColoredItemPresentation, LocationPresentation { + private final boolean myIsInherited; protected String myLocation; @@ -44,14 +30,14 @@ public abstract class JavaClassTreeElementBase extends public boolean isPublic() { Value element = getElement(); - return !(element instanceof PsiModifierListOwner) || ((PsiModifierListOwner)element).hasModifierProperty(PsiModifier.PUBLIC); + return !(element instanceof PsiModifierListOwner owner) || owner.hasModifierProperty(PsiModifier.PUBLIC); } @Override public int getAccessLevel() { Value element = getElement(); - if (!(element instanceof PsiModifierListOwner)) return PsiUtil.ACCESS_LEVEL_PUBLIC; - final PsiModifierList modifierList = ((PsiModifierListOwner)element).getModifierList(); + if (!(element instanceof PsiModifierListOwner owner)) return PsiUtil.ACCESS_LEVEL_PUBLIC; + final PsiModifierList modifierList = owner.getModifierList(); if (modifierList == null) { return PsiUtil.ACCESS_LEVEL_PUBLIC; } @@ -69,8 +55,8 @@ public abstract class JavaClassTreeElementBase extends if (isInherited()) { if (myLocation == null) { final Value element = getElement(); - if (element instanceof PsiMember) { - final PsiClass cls = ((PsiMember)element).getContainingClass(); + if (element instanceof PsiMember member) { + final PsiClass cls = member.getContainingClass(); if (cls == null) { myLocation = ""; } else { @@ -97,17 +83,16 @@ public abstract class JavaClassTreeElementBase extends } @Override - public boolean equals(final Object o) { + public boolean equals(Object o) { if (!super.equals(o)) return false; - final JavaClassTreeElementBase that = (JavaClassTreeElementBase)o; + final JavaClassTreeElementBase that = (JavaClassTreeElementBase)o; - if (myIsInherited != that.myIsInherited) return false; - - return true; + return myIsInherited == that.myIsInherited; } @Override public TextAttributesKey getTextAttributesKey() { + if (isInherited()) return CodeInsightColors.NOT_USED_ELEMENT_ATTRIBUTES; try { return isDeprecated() ? CodeInsightColors.DEPRECATED_ATTRIBUTES : null; } @@ -118,6 +103,6 @@ public abstract class JavaClassTreeElementBase extends private boolean isDeprecated(){ final Value element = getElement(); - return element instanceof PsiDocCommentOwner && ((PsiDocCommentOwner)element).isDeprecated(); + return element instanceof PsiDocCommentOwner owner && owner.isDeprecated(); } } diff --git a/java/java-structure-view/src/com/intellij/ide/structureView/impl/java/PsiMethodTreeElement.java b/java/java-structure-view/src/com/intellij/ide/structureView/impl/java/PsiMethodTreeElement.java index 331f6401fd84..643700323441 100644 --- a/java/java-structure-view/src/com/intellij/ide/structureView/impl/java/PsiMethodTreeElement.java +++ b/java/java-structure-view/src/com/intellij/ide/structureView/impl/java/PsiMethodTreeElement.java @@ -1,10 +1,8 @@ -// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.ide.structureView.impl.java; import com.intellij.ide.structureView.StructureViewTreeElement; import com.intellij.ide.util.treeView.smartTree.SortableTreeElement; -import com.intellij.openapi.editor.colors.CodeInsightColors; -import com.intellij.openapi.editor.colors.TextAttributesKey; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.IndexNotReadyException; import com.intellij.openapi.util.registry.Registry; @@ -99,12 +97,6 @@ public class PsiMethodTreeElement extends JavaClassTreeElementBase im return StringUtil.isEmpty(myLocation) ? null : myLocation; } - @Override - public TextAttributesKey getTextAttributesKey() { - if (isInherited()) return CodeInsightColors.NOT_USED_ELEMENT_ATTRIBUTES; - return super.getTextAttributesKey(); - } - public PsiMethod getMethod() { return getElement(); } diff --git a/java/java-structure-view/src/com/intellij/ide/structureView/impl/java/SuperTypesGrouper.java b/java/java-structure-view/src/com/intellij/ide/structureView/impl/java/SuperTypesGrouper.java index a82bef34b4e1..1d4df437ed19 100644 --- a/java/java-structure-view/src/com/intellij/ide/structureView/impl/java/SuperTypesGrouper.java +++ b/java/java-structure-view/src/com/intellij/ide/structureView/impl/java/SuperTypesGrouper.java @@ -1,4 +1,4 @@ -// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.ide.structureView.impl.java; import com.intellij.icons.AllIcons; @@ -7,6 +7,7 @@ import com.intellij.ide.util.treeView.AbstractTreeNode; import com.intellij.ide.util.treeView.smartTree.*; import com.intellij.openapi.util.Key; import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMember; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiModifier; import com.intellij.util.ArrayUtil; @@ -19,7 +20,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; -public final class SuperTypesGrouper implements Grouper{ +public final class SuperTypesGrouper implements Grouper { public static final Key> SUPER_METHOD_KEY = Key.create("StructureTreeBuilder.SUPER_METHOD_KEY"); public static final @NonNls String ID = "SHOW_INTERFACES"; @@ -29,45 +30,49 @@ public final class SuperTypesGrouper implements Grouper{ Map groups = new HashMap<>(); for (TreeElement child : children) { - if (child instanceof PsiMethodTreeElement element) { - PsiMethod method = element.getMethod(); - if (element.isInherited()) { - PsiClass groupClass = method.getContainingClass(); + if (child instanceof JavaClassTreeElementBase treeElement + && treeElement.isInherited() + && treeElement.getElement() instanceof PsiMember member) { + PsiClass groupClass = member.getContainingClass(); + if (groupClass != null) { final SuperTypeGroup group = getOrCreateGroup(groupClass, SuperTypeGroup.OwnershipType.INHERITS, groups); group.addMethod(child); } - else { - PsiMethod[] superMethods = method.findSuperMethods(); - - if (superMethods.length > 0) { - //prefer interface, if there are any - for (int i = 1; i < superMethods.length; i++) { - PsiMethod superMethod = superMethods[i]; - PsiClass containingClass = superMethod.getContainingClass(); - if (containingClass != null && containingClass.isInterface()) { - ArrayUtil.swap(superMethods, 0, i); - break; - } + continue; + } + if (child instanceof PsiMethodTreeElement element) { + PsiMethod method = element.getMethod(); + + PsiMethod[] superMethods = method.findSuperMethods(); + if (superMethods.length > 0) { + //prefer interface, if there are any + for (int i = 1; i < superMethods.length; i++) { + PsiMethod superMethod = superMethods[i]; + PsiClass containingClass = superMethod.getContainingClass(); + if (containingClass != null && containingClass.isInterface()) { + ArrayUtil.swap(superMethods, 0, i); + break; } - - PsiMethod superMethod = superMethods[0]; - method.putUserData(SUPER_METHOD_KEY, new WeakReference<>(superMethod)); - PsiClass groupClass = superMethod.getContainingClass(); - boolean overrides = methodOverridesSuper(method, superMethod); - final SuperTypeGroup.OwnershipType ownershipType = - overrides ? SuperTypeGroup.OwnershipType.OVERRIDES : SuperTypeGroup.OwnershipType.IMPLEMENTS; - SuperTypeGroup group = getOrCreateGroup(groupClass, ownershipType, groups); - group.addMethod(child); } + + PsiMethod superMethod = superMethods[0]; + method.putUserData(SUPER_METHOD_KEY, new WeakReference<>(superMethod)); + PsiClass groupClass = superMethod.getContainingClass(); + boolean overrides = methodOverridesSuper(method, superMethod); + final SuperTypeGroup.OwnershipType ownershipType = + overrides ? SuperTypeGroup.OwnershipType.OVERRIDES : SuperTypeGroup.OwnershipType.IMPLEMENTS; + SuperTypeGroup group = getOrCreateGroup(groupClass, ownershipType, groups); + group.addMethod(child); } } } return groups.keySet(); } - private static SuperTypeGroup getOrCreateGroup(final PsiClass groupClass, final SuperTypeGroup.OwnershipType ownershipType, final Map groups) { - SuperTypeGroup superTypeGroup = - new SuperTypeGroup(groupClass, ownershipType); + private static SuperTypeGroup getOrCreateGroup(PsiClass groupClass, + SuperTypeGroup.OwnershipType ownershipType, + Map groups) { + SuperTypeGroup superTypeGroup = new SuperTypeGroup(groupClass, ownershipType); SuperTypeGroup existing = groups.get(superTypeGroup); if (existing == null) { groups.put(superTypeGroup, superTypeGroup); @@ -85,12 +90,7 @@ public final class SuperTypesGrouper implements Grouper{ } private static boolean methodOverridesSuper(PsiMethod method, PsiMethod superMethod) { - boolean overrides = false; - if (method.hasModifierProperty(PsiModifier.ABSTRACT) || !superMethod.hasModifierProperty(PsiModifier.ABSTRACT)){ - overrides = true; - } - return overrides; - + return method.hasModifierProperty(PsiModifier.ABSTRACT) || !superMethod.hasModifierProperty(PsiModifier.ABSTRACT); } @Override diff --git a/java/java-tests/testSrc/com/intellij/java/structureView/JavaStructureViewTest.java b/java/java-tests/testSrc/com/intellij/java/structureView/JavaStructureViewTest.java index 09903d182a67..e29ae43768bd 100644 --- a/java/java-tests/testSrc/com/intellij/java/structureView/JavaStructureViewTest.java +++ b/java/java-tests/testSrc/com/intellij/java/structureView/JavaStructureViewTest.java @@ -1,4 +1,4 @@ -// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.java.structureView; import com.intellij.ide.structureView.impl.java.*; @@ -61,8 +61,8 @@ public class JavaStructureViewTest extends LightJavaStructureViewTestCaseBase { JBTreeTraverser> traverser = JBTreeTraverser.from(AbstractTreeNode::getChildren); List> roots = new ArrayList<>(); for (Object element : getElements()) { - if (element instanceof AbstractTreeNode) { - roots.add((AbstractTreeNode)element); + if (element instanceof AbstractTreeNode node) { + roots.add(node); } } return traverser.withRoots(roots) @@ -84,7 +84,7 @@ public class JavaStructureViewTest extends LightJavaStructureViewTestCaseBase { TreeElementWrapper last = (TreeElementWrapper)elements[elements.length - 1]; Collection> children = last.getChildren(); assertEquals(1, children.size()); - assertEquals(3, ((AbstractTreeNode)((List)children).get(0)).getChildren().size()); + assertEquals(3, ((AbstractTreeNode)((List)children).getFirst()).getChildren().size()); } private Object[] getElements() { @@ -115,11 +115,13 @@ public class JavaStructureViewTest extends LightJavaStructureViewTestCaseBase { public void testSuperTypeGrouping() { doTest(""" abstract class Abstract { - abstract void toImplement(); - void toOverride(){}} + abstract void toImplement(); + void toOverride(){} + } class aClass extends Abstract { - void toImplement(){}; - void toOverride(){};}""", + void toImplement(){}; + void toOverride(){}; + }""", """ -Test.java @@ -131,7 +133,77 @@ public class JavaStructureViewTest extends LightJavaStructureViewTestCaseBase { toImplement(): void -Abstract toOverride(): void""" - , true, false); + , true, false, false); + } + + public void testSuperTypeGrouping2() { + doTest(""" + abstract class Abstract { + int field; + class Inner {} + } + class aClass extends Abstract { + int moreField; + }""", + + """ + -Test.java + -Abstract + -Inner + -Object + getClass(): Class + hashCode(): int + equals(Object): boolean + clone(): Object + toString(): String + notify(): void + notifyAll(): void + wait(long): void + wait(long, int): void + wait(): void + finalize(): void + -Object + getClass(): Class + hashCode(): int + equals(Object): boolean + clone(): Object + toString(): String + notify(): void + notifyAll(): void + wait(long): void + wait(long, int): void + wait(): void + finalize(): void + field: int + -aClass + -Abstract + -Inner + getClass(): Class + hashCode(): int + equals(Object): boolean + clone(): Object + toString(): String + notify(): void + notifyAll(): void + wait(long): void + wait(long, int): void + wait(): void + finalize(): void + field: int + -Object + getClass(): Class + hashCode(): int + equals(Object): boolean + clone(): Object + toString(): String + notify(): void + notifyAll(): void + wait(long): void + wait(long, int): void + wait(): void + finalize(): void + moreField: int""" + , true, false, true); } public void testPropertiesGrouping1() { @@ -368,10 +440,11 @@ public class JavaStructureViewTest extends LightJavaStructureViewTestCaseBase { } public void testRecursive() { - myFixture.configureByText("I.java", "interface I {" + - " class Impl implements I {" + - " }" + - "};"); + myFixture.configureByText("I.java", """ + interface I { + class Impl implements I { + } + }"""); myFixture.testStructureView(component -> { component.setActionActive(InheritedMembersNodeProvider.ID, true); PlatformTestUtil.assertTreeEqual(component.getTree(), @@ -690,21 +763,23 @@ public class JavaStructureViewTest extends LightJavaStructureViewTestCaseBase { } private void doTest(String classText, @Language("TEXT") String expected) { - doTest(classText, expected, false, false); + doTest(classText, expected, false, false, false); } private void doPropertiesTest(String classText, String expected) { - doTest(classText, expected, false, true); + doTest(classText, expected, false, true, false); } private void doTest(String classText, String expected, boolean showInterfaces, - boolean showProperties) { + boolean showProperties, + boolean showInherited) { myFixture.configureByText("Test.java", classText); myFixture.testStructureView(svc -> { svc.setActionActive(SuperTypesGrouper.ID, showInterfaces); svc.setActionActive(PropertiesGrouper.ID, showProperties); + svc.setActionActive(InheritedMembersNodeProvider.ID, showInherited); svc.setActionActive(JavaAnonymousClassesNodeProvider.ID, true); JTree tree = svc.getTree(); PlatformTestUtil.waitWhileBusy(tree); diff --git a/platform/structure-view-impl/resources/messages/StructureViewBundle.properties b/platform/structure-view-impl/resources/messages/StructureViewBundle.properties index ad1b3fdfb3fe..4d48c7d2c94c 100644 --- a/platform/structure-view-impl/resources/messages/StructureViewBundle.properties +++ b/platform/structure-view-impl/resources/messages/StructureViewBundle.properties @@ -1,5 +1,5 @@ action.sort.by.type=By Type -action.structureview.group.methods.by.defining.type=Methods by Defining Type +action.structureview.group.methods.by.defining.type=Members by Defining Type action.structureview.show.fields=Fields action.structureview.show.inherited=Inherited action.structureview.sort.by.visibility=By Visibility