Fix for KT-7995 Quick doc should show deprecation info and ReplaceWith

This commit is contained in:
Simon Ogorodnik
2016-11-12 01:13:30 +03:00
committed by Simon Ogorodnik
parent 1a211ee7b0
commit 3376dbb10d
4 changed files with 64 additions and 5 deletions
@@ -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>) : 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
@@ -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 = "<pre>$renderedDecl</pre>"
}
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>", "</$tag>", body)
}
private fun mixKotlinToJava(declarationDescriptor: DeclarationDescriptor, element: PsiElement, originalElement: PsiElement?): String? {
val originalInfo = JavaDocumentationProvider().getQuickNavigateInfo(element, originalElement)
if (originalInfo != null) {
@@ -0,0 +1,6 @@
@Deprecated("lol no more mainstream", replaceWith = ReplaceWith(expression = "kek()"))
fun <caret>lol() {
println("lol")
}
//INFO: @<a href="psi_element://kotlin.Deprecated">Deprecated</a> <b>public</b> <b>fun</b> lol(): Unit <i>defined in</i> root package<DL><DT><b>Deprecated:</b></DT><DD>lol no more mainstream</DD><DT><b>Replace with:</b></DT><DD><code>kek()</code></DD></DL>
@@ -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");