Add location string showing where method or property is defined

This commit is contained in:
Nikolay Krasko
2014-05-27 16:08:27 +04:00
parent f1f13d1f2a
commit 3494853cc3
18 changed files with 281 additions and 60 deletions
@@ -54,7 +54,7 @@ public interface DescriptorRenderer extends Renderer<DeclarationDescriptor> {
.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<DeclarationDescriptor> {
.setWithoutFunctionParameterNames(true)
.setReceiverAfterName(true)
.setRenderClassObjectName(true)
.setWithoutSuperTypes(true)
.setStartFromName(true).build();
DescriptorRenderer FQ_NAMES_IN_TYPES = new DescriptorRendererBuilder().build();
@@ -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);
}
}
@@ -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<JetType> 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) {
@@ -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<String, String> 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<CallableMemberDescriptor> 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;
}
}
@@ -0,0 +1,5 @@
trait First
trait Second : First {
fun foo()
}
@@ -0,0 +1,6 @@
-DoNotShowParentsInLocationJava.kt
-Third
equals(Any?): Boolean location=→Any
foo(): Unit location=→Second
hashCode(): Int location=→Any
toString(): String location=→Any
@@ -0,0 +1,3 @@
trait Third: Second
// WITH_INHERITED
@@ -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
@@ -1,15 +1,15 @@
-InheritedJavaMembers.kt
-InheritedJavaMembers : Callable<String>
call(): String?
clone(): Any
equals(Any?): Boolean
finalize(): Unit
getClass(): Class<out Any?>
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<out Any?> 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
@@ -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
@@ -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
@@ -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
@@ -1,7 +1,7 @@
-InheritedMembersWithSubstitutedTypes.kt
-Third : Second<String>
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
@@ -20,9 +20,9 @@
a: Int
a: String on Comparable<T>
b: Any
-Enum1 : Enum<Enum1>
FIRST : Enum1
SECOND : Enum1
-Enum1
FIRST
SECOND
extension1() on String: Unit
extension2() on Comparable<T>: Unit
test1(): Unit
@@ -34,7 +34,7 @@
TestWithWhere
testWithWhere(): Unit
Trait
Trait1 : Trait
Trait1
withDefaulArgs(Int = ..., String = ...): Unit
-WithDefaultArgs
a: Int
@@ -1,5 +1,5 @@
-SeveralClasses.kt
A
B
Other : A
Some : B, A
Other
Some
@@ -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)
}
@@ -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")
@@ -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<String> nodePrintCondition) {
StringBuilder buffer = new StringBuilder();
Collection<String> strings = printAsList(tree, withSelection, printInfo, nodePrintCondition);
for (String string : strings) {
buffer.append(string).append("\n");
}
return buffer.toString();
}
public static Collection<String> printAsList(
JTree tree, boolean withSelection,
@Nullable Queryable.PrintInfo printInfo,
@Nullable Condition<String> nodePrintCondition) {
Collection<String> strings = new ArrayList<String>();
Object root = tree.getModel().getRoot();
printImpl(tree, root, strings, 0, withSelection, printInfo, nodePrintCondition);
return strings;
}
private static void printImpl(JTree tree,
Object root,
Collection<String> strings,
int level,
boolean withSelection,
@Nullable Queryable.PrintInfo printInfo,
@Nullable Condition<String> 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);
}
}
}
}