From cbcd35e2f505536a94f05ced7c4cb1a63ece8a6c Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Sat, 9 Jun 2018 13:29:13 +0300 Subject: [PATCH] Implement Kotlin sub-classes tooltip to handle MPP correctly Before this commit, Java tooltip was in use which could not work correctly with expect / actual classes So #KT-21011 Fixed --- idea/src/META-INF/plugin.xml | 1 + idea/src/META-INF/plugin.xml.172 | 2 + idea/src/META-INF/plugin.xml.173 | 2 + idea/src/META-INF/plugin.xml.182 | 2 + idea/src/META-INF/plugin.xml.as31 | 2 + idea/src/META-INF/plugin.xml.as32 | 2 + .../markers/KotlinClassTooltipLinkHandler.kt | 36 ++++++++++++ .../markers/KotlinLineMarkerProvider.kt | 2 +- .../markers/KotlinLineMarkerProvider.kt.172 | 2 +- .../markers/OverridenFunctionMarker.kt | 55 +++++++++++++++++-- .../actualDerived/common/common.kt | 6 ++ .../actualDerived/jvm/jvm.kt | 3 + .../MultiModuleLineMarkerTestGenerated.java | 5 ++ 13 files changed, 114 insertions(+), 6 deletions(-) create mode 100644 idea/src/org/jetbrains/kotlin/idea/highlighter/markers/KotlinClassTooltipLinkHandler.kt create mode 100644 idea/testData/multiModuleLineMarker/actualDerived/common/common.kt create mode 100644 idea/testData/multiModuleLineMarker/actualDerived/jvm/jvm.kt diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index f106da5296e..54a0964ae02 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -477,6 +477,7 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio. + diff --git a/idea/src/META-INF/plugin.xml.172 b/idea/src/META-INF/plugin.xml.172 index 5a0a47c4494..8676743050b 100644 --- a/idea/src/META-INF/plugin.xml.172 +++ b/idea/src/META-INF/plugin.xml.172 @@ -478,6 +478,8 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio. implementationClass="org.jetbrains.kotlin.idea.codeInsight.postfix.KtPostfixTemplateProvider"/> + + + + + + + + + + getOverriddenDeclarations(mappingToJava: MutableMap()) + ClassInheritorsSearch.search(klass).forEach(PsiElementProcessorAdapter(processor)) + + if (processor.isOverflow) { + return DaemonBundle.message(if (klass.isInterface) "interface.is.implemented.too.many" else "class.is.subclassed.too.many") + } + + val subclasses = processor.toArray(PsiClass.EMPTY_ARRAY) + if (subclasses.isEmpty()) { + val functionalImplementations = PsiElementProcessor.CollectElementsWithLimit(2, THashSet()) + FunctionalExpressionSearch.search(klass).forEach(PsiElementProcessorAdapter(functionalImplementations)) + return if (functionalImplementations.collection.isNotEmpty()) "Has functional implementations" else null + } + + val start = DaemonBundle.message(if (klass.isInterface) "interface.is.implemented.by.header" else "class.is.subclassed.by.header") + val shortcuts = ActionManager.getInstance().getAction(IdeActions.ACTION_GOTO_IMPLEMENTATION).shortcutSet.shortcuts + val shortcut = shortcuts.firstOrNull() + var postfix = "
Click" + if (shortcut != null) postfix += " or press " + KeymapUtil.getShortcutText(shortcut) + postfix += " to navigate
" + + val renderer = DeclarationByModuleRenderer() + val comparator = renderer.comparator + return subclasses.toList().sortedWith(comparator).joinToString( + prefix = "$start", postfix = "$postfix", separator = "
" + ) { + val moduleNameRequired = if (it is KtLightClass) { + val origin = it.kotlinOrigin + origin?.hasActualModifier() == true || origin?.isExpectDeclaration() == true + } else false + val moduleName = it.module?.name + val elementText = renderer.getElementText(it) + (moduleName?.takeIf { moduleNameRequired }?.let { " [$it]" } ?: "") + val refText = (moduleName?.let { "$it:" } ?: "") + ClassPresentationUtil.getNameForClass(it, /* qualified = */ true) + "    $elementText" + } +} + fun getOverriddenMethodTooltip(method: PsiMethod): String? { val processor = PsiElementProcessor.CollectElementsWithLimit(5) method.forEachOverridingMethod(processor = PsiElementProcessorAdapter(processor)::process) diff --git a/idea/testData/multiModuleLineMarker/actualDerived/common/common.kt b/idea/testData/multiModuleLineMarker/actualDerived/common/common.kt new file mode 100644 index 00000000000..13b3b1fe791 --- /dev/null +++ b/idea/testData/multiModuleLineMarker/actualDerived/common/common.kt @@ -0,0 +1,6 @@ + +open class ExpectedChild [testModule_Common]
    ExpectedChild [testModule_JVM]
    SimpleChild
Click or press Ctrl+Alt+B to navigate
">SimpleParent
+ +expect class ExpectedChild : SimpleParent + +class SimpleChild : SimpleParent() \ No newline at end of file diff --git a/idea/testData/multiModuleLineMarker/actualDerived/jvm/jvm.kt b/idea/testData/multiModuleLineMarker/actualDerived/jvm/jvm.kt new file mode 100644 index 00000000000..3a4bd03e5ea --- /dev/null +++ b/idea/testData/multiModuleLineMarker/actualDerived/jvm/jvm.kt @@ -0,0 +1,3 @@ +// !CHECK_HIGHLIGHTING + +actual class ExpectedChild : SimpleParent() \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiModuleLineMarkerTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiModuleLineMarkerTestGenerated.java index 9798288dcde..1733b7d6321 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiModuleLineMarkerTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiModuleLineMarkerTestGenerated.java @@ -25,6 +25,11 @@ public class MultiModuleLineMarkerTestGenerated extends AbstractMultiModuleLineM KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); } + @TestMetadata("actualDerived") + public void testActualDerived() throws Exception { + runTest("idea/testData/multiModuleLineMarker/actualDerived/"); + } + @TestMetadata("actualEnumEntries") public void testActualEnumEntries() throws Exception { runTest("idea/testData/multiModuleLineMarker/actualEnumEntries/");