mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Java: include fields and inner classes when grouping by defining type in structure view (IDEA-169975)
GitOrigin-RevId: 930abc712605034fd916a7dff69a95d8ddb12299
This commit is contained in:
committed by
intellij-monorepo-bot
parent
d247f73910
commit
0244a62fc3
+14
-29
@@ -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<Value extends PsiElement> extends PsiTreeElementBase<Value> implements AccessLevelProvider,
|
||||
ColoredItemPresentation,
|
||||
LocationPresentation {
|
||||
public abstract class JavaClassTreeElementBase<Value extends PsiElement> extends PsiTreeElementBase<Value>
|
||||
implements AccessLevelProvider, ColoredItemPresentation, LocationPresentation {
|
||||
|
||||
private final boolean myIsInherited;
|
||||
protected String myLocation;
|
||||
|
||||
@@ -44,14 +30,14 @@ public abstract class JavaClassTreeElementBase<Value extends PsiElement> 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<Value extends PsiElement> 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<Value extends PsiElement> 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<Value extends PsiElement> extends
|
||||
|
||||
private boolean isDeprecated(){
|
||||
final Value element = getElement();
|
||||
return element instanceof PsiDocCommentOwner && ((PsiDocCommentOwner)element).isDeprecated();
|
||||
return element instanceof PsiDocCommentOwner owner && owner.isDeprecated();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-9
@@ -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<PsiMethod> 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();
|
||||
}
|
||||
|
||||
+36
-36
@@ -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<WeakReference<PsiMethod>> 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<Group, SuperTypeGroup> 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<Group, SuperTypeGroup> groups) {
|
||||
SuperTypeGroup superTypeGroup =
|
||||
new SuperTypeGroup(groupClass, ownershipType);
|
||||
private static SuperTypeGroup getOrCreateGroup(PsiClass groupClass,
|
||||
SuperTypeGroup.OwnershipType ownershipType,
|
||||
Map<Group, SuperTypeGroup> 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
|
||||
|
||||
+91
-16
@@ -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<AbstractTreeNode<?>> traverser = JBTreeTraverser.from(AbstractTreeNode::getChildren);
|
||||
List<AbstractTreeNode<?>> 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<AbstractTreeNode<?>> 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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user