diff --git a/annotations/com/intellij/ide/util/treeView/smartTree/annotations.xml b/annotations/com/intellij/ide/util/treeView/smartTree/annotations.xml new file mode 100644 index 00000000000..1b85de98c44 --- /dev/null +++ b/annotations/com/intellij/ide/util/treeView/smartTree/annotations.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/generators/src/org/jetbrains/jet/generators/tests/GenerateTests.kt b/generators/src/org/jetbrains/jet/generators/tests/GenerateTests.kt index 41579691c04..b3eaa649ce1 100644 --- a/generators/src/org/jetbrains/jet/generators/tests/GenerateTests.kt +++ b/generators/src/org/jetbrains/jet/generators/tests/GenerateTests.kt @@ -573,7 +573,7 @@ fun main(args: Array) { } testClass(javaClass()) { - model("structureView/fileStructure") + model("structureView/fileStructure", pattern = """^([^\.]+)\.kt$""") } testClass(javaClass()) { diff --git a/idea/src/org/jetbrains/jet/plugin/structureView/JetStructureViewElement.java b/idea/src/org/jetbrains/jet/plugin/structureView/JetStructureViewElement.java index 2843e914cc9..16c307a8249 100644 --- a/idea/src/org/jetbrains/jet/plugin/structureView/JetStructureViewElement.java +++ b/idea/src/org/jetbrains/jet/plugin/structureView/JetStructureViewElement.java @@ -47,13 +47,27 @@ public class JetStructureViewElement implements StructureViewTreeElement, Colore private String elementText; private Icon icon; + private final boolean isInherited; - public JetStructureViewElement(@NotNull NavigatablePsiElement element) { + public JetStructureViewElement(@NotNull NavigatablePsiElement element, @NotNull DeclarationDescriptor descriptor, boolean isInherited) { this.element = element; + this.isInherited = isInherited; + + if (!(element instanceof JetElement)) { + // Avoid storing descriptor in fields + elementText = getElementText(element, descriptor); + icon = getElementIcon(element, descriptor); + } + } + + public JetStructureViewElement(@NotNull NavigatablePsiElement element, boolean isInherited) { + this.element = element; + this.isInherited = isInherited; } public JetStructureViewElement(@NotNull JetFile fileElement) { element = fileElement; + isInherited = false; } @NotNull @@ -94,7 +108,7 @@ public class JetStructureViewElement implements StructureViewTreeElement, Colore return ArrayUtil.toObjectArray(ContainerUtil.map(childrenDeclarations, new Function() { @Override public TreeElement fun(JetDeclaration declaration) { - return new JetStructureViewElement(declaration); + return new JetStructureViewElement(declaration, false); } }), TreeElement.class); } @@ -102,9 +116,15 @@ public class JetStructureViewElement implements StructureViewTreeElement, Colore @Nullable @Override public TextAttributesKey getTextAttributesKey() { + if (isInherited()) { + return CodeInsightColors.NOT_USED_ELEMENT_ATTRIBUTES; + } + + if (element instanceof JetModifierListOwner && JetPsiUtil.isDeprecated((JetModifierListOwner) element)) { return CodeInsightColors.DEPRECATED_ATTRIBUTES; } + return null; } @@ -134,6 +154,10 @@ public class JetStructureViewElement implements StructureViewTreeElement, Colore return icon; } + public boolean isInherited() { + return isInherited; + } + @Nullable private DeclarationDescriptor getDescriptor() { if (!(element.isValid() && element instanceof JetDeclaration)) { @@ -175,6 +199,7 @@ public class JetStructureViewElement implements StructureViewTreeElement, Colore return Collections.emptyList(); } + @Nullable private static Icon getElementIcon(@NotNull NavigatablePsiElement navigatablePsiElement, @Nullable DeclarationDescriptor descriptor) { if (descriptor != null) { return JetDescriptorIconProvider.getIcon(descriptor, navigatablePsiElement, Iconable.ICON_FLAG_VISIBILITY); diff --git a/idea/src/org/jetbrains/jet/plugin/structureView/JetStructureViewModel.java b/idea/src/org/jetbrains/jet/plugin/structureView/JetStructureViewModel.java index 72a6fcd9dc8..827c1ce9dff 100644 --- a/idea/src/org/jetbrains/jet/plugin/structureView/JetStructureViewModel.java +++ b/idea/src/org/jetbrains/jet/plugin/structureView/JetStructureViewModel.java @@ -17,12 +17,19 @@ package org.jetbrains.jet.plugin.structureView; import com.intellij.ide.structureView.StructureViewModelBase; +import com.intellij.ide.util.treeView.smartTree.NodeProvider; import com.intellij.ide.util.treeView.smartTree.Sorter; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.psi.JetDeclaration; import org.jetbrains.jet.lang.psi.JetFile; +import java.util.Arrays; +import java.util.Collection; + public class JetStructureViewModel extends StructureViewModelBase { + private static final Collection NODE_PROVIDERS = Arrays.asList( + new KotlinInheritedMembersNodeProvider()); + private static final Sorter[] sorters = new Sorter[] {Sorter.ALPHA_SORTER}; public JetStructureViewModel(@NotNull JetFile jetFile) { @@ -30,6 +37,12 @@ public class JetStructureViewModel extends StructureViewModelBase { withSuitableClasses(JetDeclaration.class); } + @NotNull + @Override + public Collection getNodeProviders() { + return NODE_PROVIDERS; + } + @NotNull @Override public Sorter[] getSorters() { diff --git a/idea/src/org/jetbrains/jet/plugin/structureView/KotlinInheritedMembersNodeProvider.kt b/idea/src/org/jetbrains/jet/plugin/structureView/KotlinInheritedMembersNodeProvider.kt new file mode 100644 index 00000000000..55bffc226ec --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/structureView/KotlinInheritedMembersNodeProvider.kt @@ -0,0 +1,60 @@ +/* + * 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.smartTree.TreeElement +import com.intellij.ide.util.InheritedMembersNodeProvider +import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor +import org.jetbrains.jet.plugin.codeInsight.DescriptorToDeclarationUtil +import org.jetbrains.jet.lang.resolve.BindingContext +import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache +import java.util.ArrayList +import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor +import com.intellij.psi.NavigatablePsiElement +import org.jetbrains.jet.lang.psi.JetClassOrObject + +public class KotlinInheritedMembersNodeProvider: InheritedMembersNodeProvider() { + override fun provideNodes(node: TreeElement): Collection { + if (node !is JetStructureViewElement) return listOf() + + val element = node.getElement() + if (element !is JetClassOrObject) return listOf() + + [suppress("USELESS_CAST")] // KT-3996 Workaround + val project = (element as NavigatablePsiElement).getProject() + + val context = AnalyzerFacadeWithCache.getContextForElement(element) + val descriptor = context[BindingContext.DECLARATION_TO_DESCRIPTOR, element] + + if (descriptor !is ClassifierDescriptor) return listOf() + + val children = ArrayList() + + val defaultType = descriptor.getDefaultType() + for (memberDescriptor in defaultType.getMemberScope().getAllDescriptors()) { + if (memberDescriptor !is CallableMemberDescriptor) continue + if (memberDescriptor.getKind() != CallableMemberDescriptor.Kind.FAKE_OVERRIDE) continue + + val superTypeMember = DescriptorToDeclarationUtil.getDeclaration(project, memberDescriptor, context) + if (superTypeMember is NavigatablePsiElement) { + children.add(JetStructureViewElement(superTypeMember, memberDescriptor, true)) + } + } + + return children + } +} \ No newline at end of file diff --git a/idea/testData/structureView/fileStructure/InheritedInnerClasses.Data.kt b/idea/testData/structureView/fileStructure/InheritedInnerClasses.Data.kt new file mode 100644 index 00000000000..86c17c7db65 --- /dev/null +++ b/idea/testData/structureView/fileStructure/InheritedInnerClasses.Data.kt @@ -0,0 +1,6 @@ +open class A { + class I1 + inner class I2 + + fun foo() {} +} \ No newline at end of file diff --git a/idea/testData/structureView/fileStructure/InheritedInnerClasses.after b/idea/testData/structureView/fileStructure/InheritedInnerClasses.after new file mode 100644 index 00000000000..86c0e71bd92 --- /dev/null +++ b/idea/testData/structureView/fileStructure/InheritedInnerClasses.after @@ -0,0 +1,6 @@ +-InheritedInnerClasses.kt + -B : A + equals(Any?): Boolean + foo(): Unit + hashCode(): Int + toString(): String diff --git a/idea/testData/structureView/fileStructure/InheritedInnerClasses.kt b/idea/testData/structureView/fileStructure/InheritedInnerClasses.kt new file mode 100644 index 00000000000..45a3b2d02d2 --- /dev/null +++ b/idea/testData/structureView/fileStructure/InheritedInnerClasses.kt @@ -0,0 +1,5 @@ +class B: A() + +// Inner classes are not shown, same to Java + +// WITH_INHERITED \ No newline at end of file diff --git a/idea/testData/structureView/fileStructure/InheritedJavaMembers.after b/idea/testData/structureView/fileStructure/InheritedJavaMembers.after new file mode 100644 index 00000000000..74b4f901f7e --- /dev/null +++ b/idea/testData/structureView/fileStructure/InheritedJavaMembers.after @@ -0,0 +1,15 @@ +-InheritedJavaMembers.kt + -InheritedJavaMembers : Callable + call(): String? + clone(): Any + equals(Any?): Boolean + finalize(): Unit + getClass(): Class + hashCode(): Int + notify(): Unit + notifyAll(): Unit + test(): Unit + toString(): String + wait(): Unit + wait(Long): Unit + wait(Long, Int): Unit diff --git a/idea/testData/structureView/fileStructure/InheritedJavaMembers.kt b/idea/testData/structureView/fileStructure/InheritedJavaMembers.kt new file mode 100644 index 00000000000..e36c9969a43 --- /dev/null +++ b/idea/testData/structureView/fileStructure/InheritedJavaMembers.kt @@ -0,0 +1,8 @@ +import java.util.concurrent.Callable + +public abstract class InheritedJavaMembers() : Callable { + fun test() { + } +} + +// WITH_INHERITED \ No newline at end of file diff --git a/idea/testData/structureView/fileStructure/InheritedLocalKotlin.after b/idea/testData/structureView/fileStructure/InheritedLocalKotlin.after new file mode 100644 index 00000000000..5e3e2954d37 --- /dev/null +++ b/idea/testData/structureView/fileStructure/InheritedLocalKotlin.after @@ -0,0 +1,8 @@ +-InheritedLocalKotlin.kt + -A : TA + equals(Any?): Boolean + hashCode(): Int + some(): Unit + toString(): String + -B : TA + some(): Unit diff --git a/idea/testData/structureView/fileStructure/InheritedLocalKotlin.kt b/idea/testData/structureView/fileStructure/InheritedLocalKotlin.kt new file mode 100644 index 00000000000..b1ac1b05ae6 --- /dev/null +++ b/idea/testData/structureView/fileStructure/InheritedLocalKotlin.kt @@ -0,0 +1,12 @@ +trait TA { + fun some() +} + +class A : TA { + override fun some() { + } +} + +abstract class B : TA + +// WITH_INHERITED \ No newline at end of file diff --git a/idea/testData/structureView/fileStructure/InheritedMembers.after b/idea/testData/structureView/fileStructure/InheritedMembers.after index 4f4301722d1..74d4879603e 100644 --- a/idea/testData/structureView/fileStructure/InheritedMembers.after +++ b/idea/testData/structureView/fileStructure/InheritedMembers.after @@ -1,2 +1,6 @@ -InheritedMembers.kt - Test + -Test + equals(Any?): Boolean + hashCode(): Int + test(): Unit + toString(): String diff --git a/idea/testData/structureView/fileStructure/InheritedMembers.kt b/idea/testData/structureView/fileStructure/InheritedMembers.kt index 8fb2c8ad780..540c361d06a 100644 --- a/idea/testData/structureView/fileStructure/InheritedMembers.kt +++ b/idea/testData/structureView/fileStructure/InheritedMembers.kt @@ -1,3 +1,7 @@ -class Test { +class Test: Runable { + fun test() { -} \ No newline at end of file + } +} + +// WITH_INHERITED \ No newline at end of file diff --git a/idea/testData/structureView/fileStructure/InheritedMembersWithSubstitutedTypes.Data.kt b/idea/testData/structureView/fileStructure/InheritedMembersWithSubstitutedTypes.Data.kt new file mode 100644 index 00000000000..8b6a1428f40 --- /dev/null +++ b/idea/testData/structureView/fileStructure/InheritedMembersWithSubstitutedTypes.Data.kt @@ -0,0 +1,7 @@ +trait First { + fun foo(a: T) +} + +trait Second : First { + val a: U +} \ No newline at end of file diff --git a/idea/testData/structureView/fileStructure/InheritedMembersWithSubstitutedTypes.after b/idea/testData/structureView/fileStructure/InheritedMembersWithSubstitutedTypes.after new file mode 100644 index 00000000000..db9dad579ec --- /dev/null +++ b/idea/testData/structureView/fileStructure/InheritedMembersWithSubstitutedTypes.after @@ -0,0 +1,7 @@ +-InheritedMembersWithSubstitutedTypes.kt + -Third : Second + a: U + equals(Any?): Boolean + foo(T): Unit + hashCode(): Int + toString(): String diff --git a/idea/testData/structureView/fileStructure/InheritedMembersWithSubstitutedTypes.kt b/idea/testData/structureView/fileStructure/InheritedMembersWithSubstitutedTypes.kt new file mode 100644 index 00000000000..2162d6de334 --- /dev/null +++ b/idea/testData/structureView/fileStructure/InheritedMembersWithSubstitutedTypes.kt @@ -0,0 +1,5 @@ +trait Third: Second + +// Original type names are shown in members, behaviour is same to Java. + +// WITH_INHERITED \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/plugin/structureView/AbstractKotlinFileStructureTest.kt b/idea/tests/org/jetbrains/jet/plugin/structureView/AbstractKotlinFileStructureTest.kt index a4644f7ada1..43de5c30536 100644 --- a/idea/tests/org/jetbrains/jet/plugin/structureView/AbstractKotlinFileStructureTest.kt +++ b/idea/tests/org/jetbrains/jet/plugin/structureView/AbstractKotlinFileStructureTest.kt @@ -27,12 +27,14 @@ 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 org.jetbrains.jet.test.util.configureWithExtraFile public abstract class AbstractKotlinFileStructureTest : JetLightCodeInsightFixtureTestCase() { override fun getTestDataPath() = PluginTestCaseBase.getTestDataPathBase() + "/structureView/fileStructure" public fun doTest(path: String) { - myFixture.configureByFile(path) + myFixture.configureWithExtraFile(path) val textEditor = TextEditorProvider.getInstance()!!.getTextEditor(myFixture.getEditor()) val popup = ViewStructureAction.createPopup(myFixture.getProject(), textEditor) @@ -43,10 +45,19 @@ public abstract class AbstractKotlinFileStructureTest : JetLightCodeInsightFixtu popup.getTreeBuilder().getUi()!!.getUpdater()!!.setPassThroughMode(true) popup.update() + popup.setup() + val popupText = PlatformTestUtil.print(popup.getTree(), false) JetTestUtils.assertEqualsToFile(File("${FileUtil.getNameWithoutExtension(path)}.after"), popupText) } + protected fun FileStructurePopup.setup() { + val fileText = FileUtil.loadFile(File(getTestDataPath(), fileName()!!), true) + + val withInherited = InTextDirectivesUtils.isDirectiveDefined(fileText, "WITH_INHERITED") + setTreeActionState(javaClass(), withInherited) + } + public fun FileStructurePopup.update() { getTreeBuilder().refilter()!!.doWhenProcessed { getStructure().rebuild() diff --git a/idea/tests/org/jetbrains/jet/plugin/structureView/KotlinFileStructureTestGenerated.java b/idea/tests/org/jetbrains/jet/plugin/structureView/KotlinFileStructureTestGenerated.java index d283285b79b..931c54fd516 100644 --- a/idea/tests/org/jetbrains/jet/plugin/structureView/KotlinFileStructureTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/plugin/structureView/KotlinFileStructureTestGenerated.java @@ -33,7 +33,9 @@ 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("EmptyFile.kt") @@ -41,11 +43,31 @@ public class KotlinFileStructureTestGenerated extends AbstractKotlinFileStructur doTest("idea/testData/structureView/fileStructure/EmptyFile.kt"); } + @TestMetadata("InheritedInnerClasses.kt") + public void testInheritedInnerClasses() throws Exception { + doTest("idea/testData/structureView/fileStructure/InheritedInnerClasses.kt"); + } + + @TestMetadata("InheritedJavaMembers.kt") + public void testInheritedJavaMembers() throws Exception { + doTest("idea/testData/structureView/fileStructure/InheritedJavaMembers.kt"); + } + + @TestMetadata("InheritedLocalKotlin.kt") + public void testInheritedLocalKotlin() throws Exception { + doTest("idea/testData/structureView/fileStructure/InheritedLocalKotlin.kt"); + } + @TestMetadata("InheritedMembers.kt") public void testInheritedMembers() throws Exception { doTest("idea/testData/structureView/fileStructure/InheritedMembers.kt"); } + @TestMetadata("InheritedMembersWithSubstitutedTypes.kt") + public void testInheritedMembersWithSubstitutedTypes() throws Exception { + doTest("idea/testData/structureView/fileStructure/InheritedMembersWithSubstitutedTypes.kt"); + } + @TestMetadata("Render.kt") public void testRender() throws Exception { doTest("idea/testData/structureView/fileStructure/Render.kt");