From 3376dbb10dc5b8982b2ddf798cc822a595f0cdb4 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Sat, 12 Nov 2016 01:13:30 +0300 Subject: [PATCH] Fix for KT-7995 Quick doc should show deprecation info and ReplaceWith --- .../kotlin/resolve/deprecationUtil.kt | 18 ++++++--- .../idea/KotlinQuickDocumentationProvider.kt | 39 +++++++++++++++++++ .../quickDoc/DeprecationWithReplaceInfo.kt | 6 +++ .../QuickDocProviderTestGenerated.java | 6 +++ 4 files changed, 64 insertions(+), 5 deletions(-) create mode 100644 idea/testData/editor/quickDoc/DeprecationWithReplaceInfo.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecationUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecationUtil.kt index 3104f7a0b0a..ed9205a6f6c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecationUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecationUtil.kt @@ -36,13 +36,21 @@ private val JAVA_DEPRECATED = FqName("java.lang.Deprecated") interface Deprecation { val deprecationLevel: DeprecationLevelValue - val message: String + val message: String? val target: DeclarationDescriptor } fun Deprecation.deprecatedByOverriddenMessage(): String? = (this as? DeprecatedByOverridden)?.additionalMessage() -private data class DeprecatedByAnnotation(private val annotation: AnnotationDescriptor, override val target: DeclarationDescriptor) : Deprecation { +fun Deprecation.deprecatedByAnnotationReplaceWithExpression(): String? { + val annotation = (this as? DeprecatedByAnnotation)?.annotation ?: return null + val replaceWithAnnotation = annotation.argumentValue(kotlin.Deprecated::replaceWith.name) + as? AnnotationDescriptor ?: return null + + return replaceWithAnnotation.argumentValue(kotlin.ReplaceWith::expression.name) as String +} + +private data class DeprecatedByAnnotation(val annotation: AnnotationDescriptor, override val target: DeclarationDescriptor) : Deprecation { override val deprecationLevel: DeprecationLevelValue get() { val level = annotation.argumentValue("level") as? ClassDescriptor @@ -55,8 +63,8 @@ private data class DeprecatedByAnnotation(private val annotation: AnnotationDesc } } - override val message: String - get() = annotation.argumentValue("message") as? String ?: "" + override val message: String? + get() = annotation.argumentValue("message") as? String } private data class DeprecatedByOverridden(private val deprecations: Collection) : Deprecation { @@ -184,7 +192,7 @@ internal fun createDeprecationDiagnostic(element: PsiElement, deprecation: Depre ERROR -> Errors.DEPRECATION_ERROR HIDDEN -> Errors.DEPRECATION_ERROR } - return diagnosticFactory.on(element, targetOriginal, deprecation.message) + return diagnosticFactory.on(element, targetOriginal, deprecation.message ?: "") } // values from kotlin.DeprecationLevel diff --git a/idea/src/org/jetbrains/kotlin/idea/KotlinQuickDocumentationProvider.kt b/idea/src/org/jetbrains/kotlin/idea/KotlinQuickDocumentationProvider.kt index ec88231d69a..504331de00b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/KotlinQuickDocumentationProvider.kt +++ b/idea/src/org/jetbrains/kotlin/idea/KotlinQuickDocumentationProvider.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.idea +import com.google.common.html.HtmlEscapers import com.intellij.codeInsight.documentation.DocumentationManagerUtil import com.intellij.lang.documentation.AbstractDocumentationProvider import com.intellij.lang.java.JavaDocumentationProvider @@ -41,7 +42,9 @@ import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import org.jetbrains.kotlin.renderer.ClassifierNamePolicy import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.deprecatedByAnnotationReplaceWithExpression import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe +import org.jetbrains.kotlin.resolve.getDeprecation import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.utils.addToStdlib.constant @@ -153,6 +156,9 @@ class KotlinQuickDocumentationProvider : AbstractDocumentationProvider() { if (!quickNavigation) { renderedDecl = "
$renderedDecl
" } + + renderedDecl += renderDeprecationInfo(declarationDescriptor) + val comment = declarationDescriptor.findKDoc() if (comment != null) { val renderedComment = KDocRenderer.renderKDoc(comment) @@ -167,6 +173,39 @@ class KotlinQuickDocumentationProvider : AbstractDocumentationProvider() { return renderedDecl } + private fun renderDeprecationInfo(declarationDescriptor: DeclarationDescriptor): String { + val deprecation = declarationDescriptor.getDeprecation() ?: return "" + + return buildString { + wrapTag("DL") { + deprecation.message?.let { message -> + wrapTag("DT") { wrapTag("b") { append("Deprecated:") } } + wrapTag("DD") { + append(message.htmlEscape()) + } + } + deprecation.deprecatedByAnnotationReplaceWithExpression()?.let { replaceWith -> + wrapTag("DT") { wrapTag("b") { append("Replace with:") } } + wrapTag("DD") { + wrapTag("code") { append(replaceWith.htmlEscape()) } + } + } + } + } + } + + fun String.htmlEscape(): String = HtmlEscapers.htmlEscaper().escape(this) + + private inline fun StringBuilder.wrap(prefix: String, postfix: String, crossinline body: () -> Unit) { + this.append(prefix) + body() + this.append(postfix) + } + + private inline fun StringBuilder.wrapTag(tag: String, crossinline body: () -> Unit) { + wrap("<$tag>", "", body) + } + private fun mixKotlinToJava(declarationDescriptor: DeclarationDescriptor, element: PsiElement, originalElement: PsiElement?): String? { val originalInfo = JavaDocumentationProvider().getQuickNavigateInfo(element, originalElement) if (originalInfo != null) { diff --git a/idea/testData/editor/quickDoc/DeprecationWithReplaceInfo.kt b/idea/testData/editor/quickDoc/DeprecationWithReplaceInfo.kt new file mode 100644 index 00000000000..0261aea5cf2 --- /dev/null +++ b/idea/testData/editor/quickDoc/DeprecationWithReplaceInfo.kt @@ -0,0 +1,6 @@ +@Deprecated("lol no more mainstream", replaceWith = ReplaceWith(expression = "kek()")) +fun lol() { + println("lol") +} + +//INFO: @Deprecated public fun lol(): Unit defined in root package
Deprecated:
lol no more mainstream
Replace with:
kek()
diff --git a/idea/tests/org/jetbrains/kotlin/idea/editor/quickDoc/QuickDocProviderTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/editor/quickDoc/QuickDocProviderTestGenerated.java index 20e39bcc053..2bfec0700d0 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/editor/quickDoc/QuickDocProviderTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/editor/quickDoc/QuickDocProviderTestGenerated.java @@ -72,6 +72,12 @@ public class QuickDocProviderTestGenerated extends AbstractQuickDocProviderTest doTest(fileName); } + @TestMetadata("DeprecationWithReplaceInfo.kt") + public void testDeprecationWithReplaceInfo() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/editor/quickDoc/DeprecationWithReplaceInfo.kt"); + doTest(fileName); + } + @TestMetadata("EscapeHtmlInsideCodeBlocks.kt") public void testEscapeHtmlInsideCodeBlocks() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/editor/quickDoc/EscapeHtmlInsideCodeBlocks.kt");