From 3494853cc3f8816eda64283f9d18ce78f9d03ea7 Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Tue, 27 May 2014 16:08:27 +0400 Subject: [PATCH] Add location string showing where method or property is defined --- .../jet/renderer/DescriptorRenderer.java | 3 +- .../renderer/DescriptorRendererBuilder.java | 8 +- .../jet/renderer/DescriptorRendererImpl.java | 24 +++- .../JetStructureViewElement.java | 61 +++++++- .../DoNotShowParentsInLocationJava.Data.kt | 5 + .../DoNotShowParentsInLocationJava.after | 6 + .../DoNotShowParentsInLocationJava.kt | 3 + .../fileStructure/InheritedInnerClasses.after | 10 +- .../fileStructure/InheritedJavaMembers.after | 26 ++-- .../fileStructure/InheritedLocalKotlin.after | 14 +- .../fileStructure/InheritedLocalKotlin.kt | 3 + .../fileStructure/InheritedMembers.after | 6 +- ...InheritedMembersWithSubstitutedTypes.after | 12 +- .../structureView/fileStructure/Render.after | 8 +- .../fileStructure/SeveralClasses.after | 4 +- .../AbstractKotlinFileStructureTest.kt | 5 +- .../KotlinFileStructureTestGenerated.java | 9 +- .../structureView/StructureViewUtil.java | 134 ++++++++++++++++++ 18 files changed, 281 insertions(+), 60 deletions(-) create mode 100644 idea/testData/structureView/fileStructure/DoNotShowParentsInLocationJava.Data.kt create mode 100644 idea/testData/structureView/fileStructure/DoNotShowParentsInLocationJava.after create mode 100644 idea/testData/structureView/fileStructure/DoNotShowParentsInLocationJava.kt create mode 100644 idea/tests/org/jetbrains/jet/plugin/structureView/StructureViewUtil.java diff --git a/core/descriptors/src/org/jetbrains/jet/renderer/DescriptorRenderer.java b/core/descriptors/src/org/jetbrains/jet/renderer/DescriptorRenderer.java index a51522b548a..cf4b435dfa1 100644 --- a/core/descriptors/src/org/jetbrains/jet/renderer/DescriptorRenderer.java +++ b/core/descriptors/src/org/jetbrains/jet/renderer/DescriptorRenderer.java @@ -54,7 +54,7 @@ public interface DescriptorRenderer extends Renderer { .setModifiers() .setStartFromName(true).build(); - DescriptorRenderer STARTS_FROM_NAME_WITH_SHORT_TYPES = new DescriptorRendererBuilder() + DescriptorRenderer ONLY_NAMES_WITH_SHORT_TYPES = new DescriptorRendererBuilder() .setWithDefinedIn(false) .setModifiers() .setShortNames(true) @@ -62,6 +62,7 @@ public interface DescriptorRenderer extends Renderer { .setWithoutFunctionParameterNames(true) .setReceiverAfterName(true) .setRenderClassObjectName(true) + .setWithoutSuperTypes(true) .setStartFromName(true).build(); DescriptorRenderer FQ_NAMES_IN_TYPES = new DescriptorRendererBuilder().build(); diff --git a/core/descriptors/src/org/jetbrains/jet/renderer/DescriptorRendererBuilder.java b/core/descriptors/src/org/jetbrains/jet/renderer/DescriptorRendererBuilder.java index c4e0fb13b38..82dbdc85d49 100644 --- a/core/descriptors/src/org/jetbrains/jet/renderer/DescriptorRendererBuilder.java +++ b/core/descriptors/src/org/jetbrains/jet/renderer/DescriptorRendererBuilder.java @@ -41,6 +41,7 @@ public class DescriptorRendererBuilder { private boolean includeSynthesizedParameterNames = true; private boolean withoutFunctionParameterNames = false; private boolean withoutTypeParameters = false; + private boolean withoutSuperTypes = false; @NotNull private DescriptorRenderer.OverrideRenderingPolicy overrideRenderingPolicy = DescriptorRenderer.OverrideRenderingPolicy.RENDER_OPEN; @@ -187,6 +188,11 @@ public class DescriptorRendererBuilder { return this; } + public DescriptorRendererBuilder setWithoutSuperTypes(boolean withoutSuperTypes) { + this.withoutSuperTypes = withoutSuperTypes; + return this; + } + @NotNull public DescriptorRenderer build() { return new DescriptorRendererImpl( @@ -194,7 +200,7 @@ public class DescriptorRendererBuilder { normalizedVisibilities, showInternalKeyword, prettyFunctionTypes, uninferredTypeParameterAsName, overrideRenderingPolicy, valueParametersHandler, textFormat, excludedAnnotationClasses, includePropertyConstant, includeSynthesizedParameterNames, withoutFunctionParameterNames, withoutTypeParameters, receiverAfterName, - renderClassObjectName); + renderClassObjectName, withoutSuperTypes); } } diff --git a/core/descriptors/src/org/jetbrains/jet/renderer/DescriptorRendererImpl.java b/core/descriptors/src/org/jetbrains/jet/renderer/DescriptorRendererImpl.java index 8192d4f7260..41cc9e29297 100644 --- a/core/descriptors/src/org/jetbrains/jet/renderer/DescriptorRendererImpl.java +++ b/core/descriptors/src/org/jetbrains/jet/renderer/DescriptorRendererImpl.java @@ -42,7 +42,8 @@ import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; import java.util.*; import static org.jetbrains.jet.lang.types.ErrorUtils.UninferredParameterType; -import static org.jetbrains.jet.lang.types.TypeUtils.*; +import static org.jetbrains.jet.lang.types.TypeUtils.CANT_INFER_LAMBDA_PARAM_TYPE; +import static org.jetbrains.jet.lang.types.TypeUtils.DONT_CARE; public class DescriptorRendererImpl implements DescriptorRenderer { private final boolean shortNames; @@ -61,8 +62,9 @@ public class DescriptorRendererImpl implements DescriptorRenderer { private final boolean withoutFunctionParameterNames; private final boolean withoutTypeParameters; private final boolean renderClassObjectName; - + private final boolean withoutSuperTypes; private final boolean receiverAfterName; + @NotNull private final OverrideRenderingPolicy overrideRenderingPolicy; @NotNull @@ -95,7 +97,8 @@ public class DescriptorRendererImpl implements DescriptorRenderer { boolean withoutFunctionParameterNames, boolean withoutTypeParameters, boolean receiverAfterName, - boolean renderClassObjectName + boolean renderClassObjectName, + boolean withoutSuperTypes ) { this.shortNames = shortNames; this.withDefinedIn = withDefinedIn; @@ -119,6 +122,7 @@ public class DescriptorRendererImpl implements DescriptorRenderer { this.withoutTypeParameters = withoutTypeParameters; this.receiverAfterName = receiverAfterName; this.renderClassObjectName = renderClassObjectName; + this.withoutSuperTypes = withoutSuperTypes; } /* FORMATTING */ @@ -807,10 +811,18 @@ public class DescriptorRendererImpl implements DescriptorRenderer { } } + renderSuperTypes(klass, builder); + renderWhereSuffix(typeParameters, builder); + } + + private void renderSuperTypes(@NotNull ClassDescriptor klass, @NotNull StringBuilder builder) { + if (withoutSuperTypes) return; + if (!klass.equals(KotlinBuiltIns.getInstance().getNothing())) { Collection supertypes = klass.getTypeConstructor().getSupertypes(); - if (supertypes.isEmpty() || supertypes.size() == 1 && KotlinBuiltIns.getInstance().isAnyOrNullableAny( - supertypes.iterator().next())) { + + if (supertypes.isEmpty() || + supertypes.size() == 1 && KotlinBuiltIns.getInstance().isAnyOrNullableAny(supertypes.iterator().next())) { } else { builder.append(" : "); @@ -823,8 +835,6 @@ public class DescriptorRendererImpl implements DescriptorRenderer { } } } - - renderWhereSuffix(typeParameters, builder); } private void renderClassKindPrefix(ClassDescriptor klass, StringBuilder builder) { diff --git a/idea/src/org/jetbrains/jet/plugin/structureView/JetStructureViewElement.java b/idea/src/org/jetbrains/jet/plugin/structureView/JetStructureViewElement.java index 16c307a8249..02b4b3180bb 100644 --- a/idea/src/org/jetbrains/jet/plugin/structureView/JetStructureViewElement.java +++ b/idea/src/org/jetbrains/jet/plugin/structureView/JetStructureViewElement.java @@ -20,8 +20,10 @@ import com.intellij.ide.structureView.StructureViewTreeElement; import com.intellij.ide.util.treeView.smartTree.TreeElement; import com.intellij.navigation.ColoredItemPresentation; import com.intellij.navigation.ItemPresentation; +import com.intellij.navigation.LocationPresentation; import com.intellij.openapi.editor.colors.CodeInsightColors; import com.intellij.openapi.editor.colors.TextAttributesKey; +import com.intellij.openapi.ui.Queryable; import com.intellij.openapi.util.Iconable; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.NavigatablePsiElement; @@ -29,25 +31,28 @@ import com.intellij.util.ArrayUtil; import com.intellij.util.Function; import com.intellij.util.PsiIconUtil; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; +import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lang.resolve.OverrideResolver; import org.jetbrains.jet.plugin.JetDescriptorIconProvider; import org.jetbrains.jet.plugin.caches.resolve.ResolvePackage; import org.jetbrains.jet.renderer.DescriptorRenderer; import javax.swing.*; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.util.*; -public class JetStructureViewElement implements StructureViewTreeElement, ColoredItemPresentation { +public class JetStructureViewElement implements StructureViewTreeElement, ColoredItemPresentation, LocationPresentation, Queryable { private final NavigatablePsiElement element; + private final boolean isInherited; private String elementText; + private String locationString; private Icon icon; - private final boolean isInherited; public JetStructureViewElement(@NotNull NavigatablePsiElement element, @NotNull DeclarationDescriptor descriptor, boolean isInherited) { this.element = element; @@ -56,6 +61,7 @@ public class JetStructureViewElement implements StructureViewTreeElement, Colore if (!(element instanceof JetElement)) { // Avoid storing descriptor in fields elementText = getElementText(element, descriptor); + locationString = isInherited ? getElementLocationString(descriptor) : null; icon = getElementIcon(element, descriptor); } } @@ -141,7 +147,11 @@ public class JetStructureViewElement implements StructureViewTreeElement, Colore @Nullable @Override public String getLocationString() { - return null; + if (locationString == null) { + locationString = isInherited() ? getElementLocationString(getDescriptor()) : null; + } + + return locationString; } @Nullable @@ -154,6 +164,23 @@ public class JetStructureViewElement implements StructureViewTreeElement, Colore return icon; } + @Override + public String getLocationPrefix() { + return isInherited() ? " " : LocationPresentation.DEFAULT_LOCATION_PREFIX; + } + + @Override + public String getLocationSuffix() { + return isInherited() ? "" : LocationPresentation.DEFAULT_LOCATION_SUFFIX; + } + + @TestOnly + @Override + public void putInfo(@NotNull Map info) { + info.put("text", getPresentableText()); + info.put("location", getLocationString()); + } + public boolean isInherited() { return isInherited; } @@ -211,7 +238,7 @@ public class JetStructureViewElement implements StructureViewTreeElement, Colore @Nullable private static String getElementText(@NotNull NavigatablePsiElement navigatablePsiElement, @Nullable DeclarationDescriptor descriptor) { if (descriptor != null) { - return DescriptorRenderer.STARTS_FROM_NAME_WITH_SHORT_TYPES.render(descriptor); + return DescriptorRenderer.ONLY_NAMES_WITH_SHORT_TYPES.render(descriptor); } String text = navigatablePsiElement.getName(); @@ -225,4 +252,24 @@ public class JetStructureViewElement implements StructureViewTreeElement, Colore return null; } + + private static String getElementLocationString(@Nullable DeclarationDescriptor descriptor) { + if (descriptor instanceof CallableMemberDescriptor) { + Set baseCallableDescriptors = OverrideResolver.getDeepestSuperDeclarations((CallableMemberDescriptor) descriptor); + CallableMemberDescriptor first = ContainerUtil.getFirstItem(baseCallableDescriptors); + if (first != null) { + DeclarationDescriptor typeDescriptor = first.getContainingDeclaration(); + + String typeName = DescriptorRenderer.ONLY_NAMES_WITH_SHORT_TYPES.render(typeDescriptor); + return withRightArrow(typeName); + } + } + + return null; + } + + private static String withRightArrow(String str) { + char rightArrow = '\u2192'; + return UIUtil.getLabelFont().canDisplay(rightArrow) ? rightArrow + str : "->" + str; + } } diff --git a/idea/testData/structureView/fileStructure/DoNotShowParentsInLocationJava.Data.kt b/idea/testData/structureView/fileStructure/DoNotShowParentsInLocationJava.Data.kt new file mode 100644 index 00000000000..7f5b5d49cad --- /dev/null +++ b/idea/testData/structureView/fileStructure/DoNotShowParentsInLocationJava.Data.kt @@ -0,0 +1,5 @@ +trait First + +trait Second : First { + fun foo() +} \ No newline at end of file diff --git a/idea/testData/structureView/fileStructure/DoNotShowParentsInLocationJava.after b/idea/testData/structureView/fileStructure/DoNotShowParentsInLocationJava.after new file mode 100644 index 00000000000..8bcd449cfab --- /dev/null +++ b/idea/testData/structureView/fileStructure/DoNotShowParentsInLocationJava.after @@ -0,0 +1,6 @@ +-DoNotShowParentsInLocationJava.kt + -Third + equals(Any?): Boolean location=→Any + foo(): Unit location=→Second + hashCode(): Int location=→Any + toString(): String location=→Any diff --git a/idea/testData/structureView/fileStructure/DoNotShowParentsInLocationJava.kt b/idea/testData/structureView/fileStructure/DoNotShowParentsInLocationJava.kt new file mode 100644 index 00000000000..75a843444ee --- /dev/null +++ b/idea/testData/structureView/fileStructure/DoNotShowParentsInLocationJava.kt @@ -0,0 +1,3 @@ +trait Third: Second + +// WITH_INHERITED \ No newline at end of file diff --git a/idea/testData/structureView/fileStructure/InheritedInnerClasses.after b/idea/testData/structureView/fileStructure/InheritedInnerClasses.after index 86c0e71bd92..4c48eca7a87 100644 --- a/idea/testData/structureView/fileStructure/InheritedInnerClasses.after +++ b/idea/testData/structureView/fileStructure/InheritedInnerClasses.after @@ -1,6 +1,6 @@ -InheritedInnerClasses.kt - -B : A - equals(Any?): Boolean - foo(): Unit - hashCode(): Int - toString(): String + -B + equals(Any?): Boolean location=→Any + foo(): Unit location=→A + hashCode(): Int location=→Any + toString(): String location=→Any diff --git a/idea/testData/structureView/fileStructure/InheritedJavaMembers.after b/idea/testData/structureView/fileStructure/InheritedJavaMembers.after index 74b4f901f7e..012f703d5f2 100644 --- a/idea/testData/structureView/fileStructure/InheritedJavaMembers.after +++ b/idea/testData/structureView/fileStructure/InheritedJavaMembers.after @@ -1,15 +1,15 @@ -InheritedJavaMembers.kt - -InheritedJavaMembers : Callable - call(): String? - clone(): Any - equals(Any?): Boolean - finalize(): Unit - getClass(): Class - hashCode(): Int - notify(): Unit - notifyAll(): Unit + -InheritedJavaMembers + call(): String? location=→Callable + clone(): Any location=→Object + equals(Any?): Boolean location=→Any + finalize(): Unit location=→Object + getClass(): Class location=→Object + hashCode(): Int location=→Any + notify(): Unit location=→Object + notifyAll(): Unit location=→Object test(): Unit - toString(): String - wait(): Unit - wait(Long): Unit - wait(Long, Int): Unit + toString(): String location=→Any + wait(): Unit location=→Object + wait(Long): Unit location=→Object + wait(Long, Int): Unit location=→Object diff --git a/idea/testData/structureView/fileStructure/InheritedLocalKotlin.after b/idea/testData/structureView/fileStructure/InheritedLocalKotlin.after index 5e3e2954d37..e3683bbc9f2 100644 --- a/idea/testData/structureView/fileStructure/InheritedLocalKotlin.after +++ b/idea/testData/structureView/fileStructure/InheritedLocalKotlin.after @@ -1,8 +1,10 @@ -InheritedLocalKotlin.kt - -A : TA - equals(Any?): Boolean - hashCode(): Int - some(): Unit - toString(): String - -B : TA + -A + a: Int + equals(Any?): Boolean location=→Any + hashCode(): Int location=→Any some(): Unit + toString(): String location=→Any + -B + a: Int location=→TA + some(): Unit location=→TA diff --git a/idea/testData/structureView/fileStructure/InheritedLocalKotlin.kt b/idea/testData/structureView/fileStructure/InheritedLocalKotlin.kt index b1ac1b05ae6..bcc4716af7b 100644 --- a/idea/testData/structureView/fileStructure/InheritedLocalKotlin.kt +++ b/idea/testData/structureView/fileStructure/InheritedLocalKotlin.kt @@ -1,10 +1,13 @@ trait TA { fun some() + val a: Int } class A : TA { override fun some() { } + + override val a: Int = 0 } abstract class B : TA diff --git a/idea/testData/structureView/fileStructure/InheritedMembers.after b/idea/testData/structureView/fileStructure/InheritedMembers.after index 74d4879603e..310b334b451 100644 --- a/idea/testData/structureView/fileStructure/InheritedMembers.after +++ b/idea/testData/structureView/fileStructure/InheritedMembers.after @@ -1,6 +1,6 @@ -InheritedMembers.kt -Test - equals(Any?): Boolean - hashCode(): Int + equals(Any?): Boolean location=→Any + hashCode(): Int location=→Any test(): Unit - toString(): String + toString(): String location=→Any diff --git a/idea/testData/structureView/fileStructure/InheritedMembersWithSubstitutedTypes.after b/idea/testData/structureView/fileStructure/InheritedMembersWithSubstitutedTypes.after index db9dad579ec..63bc9b47f00 100644 --- a/idea/testData/structureView/fileStructure/InheritedMembersWithSubstitutedTypes.after +++ b/idea/testData/structureView/fileStructure/InheritedMembersWithSubstitutedTypes.after @@ -1,7 +1,7 @@ -InheritedMembersWithSubstitutedTypes.kt - -Third : Second - a: U - equals(Any?): Boolean - foo(T): Unit - hashCode(): Int - toString(): String + -Third + a: U location=→Second + equals(Any?): Boolean location=→Any + foo(T): Unit location=→First + hashCode(): Int location=→Any + toString(): String location=→Any diff --git a/idea/testData/structureView/fileStructure/Render.after b/idea/testData/structureView/fileStructure/Render.after index 2cf2e49dee6..71e3e7d0bc2 100644 --- a/idea/testData/structureView/fileStructure/Render.after +++ b/idea/testData/structureView/fileStructure/Render.after @@ -20,9 +20,9 @@ a: Int a: String on Comparable b: Any - -Enum1 : Enum - FIRST : Enum1 - SECOND : Enum1 + -Enum1 + FIRST + SECOND extension1() on String: Unit extension2() on Comparable: Unit test1(): Unit @@ -34,7 +34,7 @@ TestWithWhere testWithWhere(): Unit Trait - Trait1 : Trait + Trait1 withDefaulArgs(Int = ..., String = ...): Unit -WithDefaultArgs a: Int diff --git a/idea/testData/structureView/fileStructure/SeveralClasses.after b/idea/testData/structureView/fileStructure/SeveralClasses.after index 3841602e458..af2bcd8eaec 100644 --- a/idea/testData/structureView/fileStructure/SeveralClasses.after +++ b/idea/testData/structureView/fileStructure/SeveralClasses.after @@ -1,5 +1,5 @@ -SeveralClasses.kt A B - Other : A - Some : B, A + Other + Some diff --git a/idea/tests/org/jetbrains/jet/plugin/structureView/AbstractKotlinFileStructureTest.kt b/idea/tests/org/jetbrains/jet/plugin/structureView/AbstractKotlinFileStructureTest.kt index 43de5c30536..89afaa933c5 100644 --- a/idea/tests/org/jetbrains/jet/plugin/structureView/AbstractKotlinFileStructureTest.kt +++ b/idea/tests/org/jetbrains/jet/plugin/structureView/AbstractKotlinFileStructureTest.kt @@ -21,13 +21,13 @@ import org.jetbrains.jet.plugin.JetLightCodeInsightFixtureTestCase import com.intellij.util.ui.tree.TreeUtil import com.intellij.openapi.util.io.FileUtil import java.io.File -import com.intellij.testFramework.PlatformTestUtil import com.intellij.ui.treeStructure.filtered.FilteringTreeStructure import com.intellij.ide.util.FileStructurePopup import com.intellij.ide.actions.ViewStructureAction import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider import org.jetbrains.jet.JetTestUtils import org.jetbrains.jet.InTextDirectivesUtils +import com.intellij.openapi.ui.Queryable.PrintInfo import org.jetbrains.jet.test.util.configureWithExtraFile public abstract class AbstractKotlinFileStructureTest : JetLightCodeInsightFixtureTestCase() { @@ -47,7 +47,8 @@ public abstract class AbstractKotlinFileStructureTest : JetLightCodeInsightFixtu popup.setup() - val popupText = PlatformTestUtil.print(popup.getTree(), false) + val printInfo = PrintInfo(array("text"), array("location")) + val popupText = StructureViewUtil.print(popup.getTree(), false, printInfo, null) JetTestUtils.assertEqualsToFile(File("${FileUtil.getNameWithoutExtension(path)}.after"), popupText) } diff --git a/idea/tests/org/jetbrains/jet/plugin/structureView/KotlinFileStructureTestGenerated.java b/idea/tests/org/jetbrains/jet/plugin/structureView/KotlinFileStructureTestGenerated.java index 931c54fd516..017407514e9 100644 --- a/idea/tests/org/jetbrains/jet/plugin/structureView/KotlinFileStructureTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/plugin/structureView/KotlinFileStructureTestGenerated.java @@ -33,9 +33,12 @@ import org.jetbrains.jet.plugin.structureView.AbstractKotlinFileStructureTest; @TestMetadata("idea/testData/structureView/fileStructure") public class KotlinFileStructureTestGenerated extends AbstractKotlinFileStructureTest { public void testAllFilesPresentInFileStructure() throws Exception { - JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", - new File("idea/testData/structureView/fileStructure"), - Pattern.compile("^([^\\.]+)\\.kt$"), true); + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("idea/testData/structureView/fileStructure"), Pattern.compile("^([^\\.]+)\\.kt$"), true); + } + + @TestMetadata("DoNotShowParentsInLocationJava.kt") + public void testDoNotShowParentsInLocationJava() throws Exception { + doTest("idea/testData/structureView/fileStructure/DoNotShowParentsInLocationJava.kt"); } @TestMetadata("EmptyFile.kt") diff --git a/idea/tests/org/jetbrains/jet/plugin/structureView/StructureViewUtil.java b/idea/tests/org/jetbrains/jet/plugin/structureView/StructureViewUtil.java new file mode 100644 index 00000000000..a6204ae46a6 --- /dev/null +++ b/idea/tests/org/jetbrains/jet/plugin/structureView/StructureViewUtil.java @@ -0,0 +1,134 @@ +/* + * Copyright 2010-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. + */ + +package org.jetbrains.jet.plugin.structureView; + +import com.intellij.ide.util.treeView.AbstractTreeNode; +import com.intellij.openapi.ui.Queryable; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.ui.treeStructure.filtered.FilteringTreeStructure; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreePath; +import java.util.ArrayList; +import java.util.Collection; + +import static com.intellij.util.ObjectUtils.tryCast; + + +/** + * Extracted from PlatformTestUtil to print JTree with location string. + */ +public class StructureViewUtil { + @Nullable + protected static String toString(@Nullable Object node, @Nullable Queryable.PrintInfo printInfo) { + if (node instanceof AbstractTreeNode) { + return ((AbstractTreeNode) node).toTestString(printInfo); + } + + FilteringTreeStructure.FilteringNode filteringNode = tryCast(node, FilteringTreeStructure.FilteringNode.class); + if (filteringNode != null && filteringNode.getDelegate() instanceof AbstractTreeNode) { + return ((AbstractTreeNode) filteringNode.getDelegate()).toTestString(printInfo); + } + + if (node == null) { + return "NULL"; + } + + return node.toString(); + } + + @NotNull + public static String print(JTree tree, boolean withSelection) { + return print(tree, withSelection, null, null); + } + + @NotNull + public static String print( + JTree tree, boolean withSelection, + @Nullable Queryable.PrintInfo printInfo, + @Nullable Condition nodePrintCondition) { + StringBuilder buffer = new StringBuilder(); + Collection strings = printAsList(tree, withSelection, printInfo, nodePrintCondition); + for (String string : strings) { + buffer.append(string).append("\n"); + } + return buffer.toString(); + } + + public static Collection printAsList( + JTree tree, boolean withSelection, + @Nullable Queryable.PrintInfo printInfo, + @Nullable Condition nodePrintCondition) { + Collection strings = new ArrayList(); + Object root = tree.getModel().getRoot(); + printImpl(tree, root, strings, 0, withSelection, printInfo, nodePrintCondition); + return strings; + } + + private static void printImpl(JTree tree, + Object root, + Collection strings, + int level, + boolean withSelection, + @Nullable Queryable.PrintInfo printInfo, + @Nullable Condition nodePrintCondition) { + DefaultMutableTreeNode defaultMutableTreeNode = (DefaultMutableTreeNode)root; + + Object userObject = defaultMutableTreeNode.getUserObject(); + String nodeText; + if (userObject != null) { + nodeText = toString(userObject, printInfo); + } + else { + nodeText = "null"; + } + + if (nodePrintCondition != null && !nodePrintCondition.value(nodeText)) return; + + StringBuilder buff = new StringBuilder(); + StringUtil.repeatSymbol(buff, ' ', level); + + boolean expanded = tree.isExpanded(new TreePath(defaultMutableTreeNode.getPath())); + if (!defaultMutableTreeNode.isLeaf()) { + buff.append(expanded ? "-" : "+"); + } + + boolean selected = tree.getSelectionModel().isPathSelected(new TreePath(defaultMutableTreeNode.getPath())); + if (withSelection && selected) { + buff.append("["); + } + + buff.append(nodeText); + + if (withSelection && selected) { + buff.append("]"); + } + + strings.add(buff.toString()); + + int childCount = tree.getModel().getChildCount(root); + if (expanded) { + for (int i = 0; i < childCount; i++) { + printImpl(tree, tree.getModel().getChild(root, i), strings, level + 1, withSelection, printInfo, nodePrintCondition); + } + } + } +}