Fix for KT-14819 Quick documentation for special Enum functions doesn't work

#KT-14819 fixed
This commit is contained in:
Simon Ogorodnik
2016-12-15 19:44:25 +03:00
committed by Simon Ogorodnik
parent fa01a4efad
commit d0cf1b0c5f
10 changed files with 153 additions and 9 deletions
+12
View File
@@ -46,4 +46,16 @@ public abstract class Enum<E : Enum<E>>(name: String, ordinal: Int): Comparable<
public override final fun equals(other: Any?): Boolean
public override final fun hashCode(): Int
public override fun toString(): String
/**
* Returns an array containing the constants of this enum type, in the order they're declared.
* This method may be used to iterate over the constants.
* @values
*/
/**
* Returns the enum constant of this type with the specified name. The string must match exactly an identifier used to declare an enum constant in this type. (Extraneous whitespace characters are not permitted.)
* @throws IllegalArgumentException if this enum type has no constant with the specified name
* @valueOf
*/
}
@@ -27,14 +27,13 @@ import com.intellij.psi.PsiManager
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject
import org.jetbrains.kotlin.idea.decompiler.navigation.SourceNavigationHelper
import org.jetbrains.kotlin.idea.kdoc.KDocRenderer
import org.jetbrains.kotlin.idea.kdoc.findKDoc
import org.jetbrains.kotlin.idea.kdoc.isBoringBuiltinClass
@@ -42,15 +41,18 @@ import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink
import org.jetbrains.kotlin.idea.project.languageVersionSettings
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
import org.jetbrains.kotlin.kdoc.psi.api.KDoc
import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.renderer.ClassifierNamePolicy
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.deprecatedByAnnotationReplaceWithExpression
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.resolve.getDeprecations
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.utils.addToStdlib.constant
@@ -102,8 +104,60 @@ class KotlinQuickDocumentationProvider : AbstractDocumentationProvider() {
renderCompanionObjectName = true
}
private fun renderEnumSpecialFunction(element: KtClass, functionDescriptor: FunctionDescriptor, quickNavigation: Boolean): String {
var renderedDecl = DESCRIPTOR_RENDERER.render(functionDescriptor)
if (quickNavigation) return renderedDecl
val declarationDescriptor = element.resolveToDescriptorIfAny()
val enumDescriptor = (declarationDescriptor as? ClassDescriptor)?.getSuperClassNotAny() ?: return renderedDecl
val enumDeclaration =
DescriptorToSourceUtilsIde.getAnyDeclaration(element.project, enumDescriptor) as? KtDeclaration ?: return renderedDecl
val enumSource = SourceNavigationHelper.getNavigationElement(enumDeclaration)
val functionName = functionDescriptor.fqNameSafe.shortName().asString()
val kdoc = enumSource.findDescendantOfType<KDoc> {
it.getChildrenOfType<KDocSection>().any { it.findTagByName(functionName) != null }
}
if (kdoc != null) {
val renderedComment = KDocRenderer.renderKDoc(kdoc.getDefaultSection())
if (renderedComment.startsWith("<p>")) {
renderedDecl += renderedComment
}
else {
renderedDecl = "$renderedDecl<br/>$renderedComment"
}
}
return renderedDecl
}
private fun renderEnum(element: KtClass, originalElement: PsiElement?, quickNavigation: Boolean): String? {
val referenceExpression = originalElement?.getNonStrictParentOfType<KtReferenceExpression>()
if (referenceExpression != null) {
// When caret on special enum function (e.g SomeEnum.values<caret>())
// element is not an KtReferenceExpression, but KtClass of enum
// so reference extracted from originalElement
val context = referenceExpression.analyze(BodyResolveMode.PARTIAL)
(context[BindingContext.REFERENCE_TARGET, referenceExpression] ?:
context[BindingContext.REFERENCE_TARGET, referenceExpression.getChildOfType<KtReferenceExpression>()])?.let {
if (it is FunctionDescriptor) // To protect from Some<caret>Enum.values()
return renderEnumSpecialFunction(element, it, quickNavigation)
}
}
return renderKotlinDeclaration(element, quickNavigation)
}
private fun getText(element: PsiElement, originalElement: PsiElement?, quickNavigation: Boolean): String? {
if (element is KtDeclaration) {
if (element is KtClass && element.isEnum()) {
// When caret on special enum function (e.g SomeEnum.values<caret>())
// element is not an KtReferenceExpression, but KtClass of enum
return renderEnum(element, originalElement, quickNavigation)
}
else if (element is KtDeclaration) {
return renderKotlinDeclaration(element, quickNavigation)
}
else if (element is KtNameReferenceExpression && element.getReferencedName() == "it") {
+10
View File
@@ -0,0 +1,10 @@
/**
* Useless one
*/
enum class SomeEnum
fun use() {
Some<caret>Enum::class
}
//INFO: <pre><b>public</b> <b>final</b> <b>enum class</b> SomeEnum : <a href="psi_element://kotlin.Enum">Enum</a>&lt;<a href="psi_element://SomeEnum">SomeEnum</a>&gt; <i>defined in</i> root package</pre><p>Useless one</p>
+6
View File
@@ -0,0 +1,6 @@
/**
* Useless one
*/
enum class SomeEnum<caret>
//INFO: <pre><b>public</b> <b>final</b> <b>enum class</b> SomeEnum : <a href="psi_element://kotlin.Enum">Enum</a>&lt;<a href="psi_element://SomeEnum">SomeEnum</a>&gt; <i>defined in</i> root package</pre><p>Useless one</p>
+12
View File
@@ -0,0 +1,12 @@
/**
* Enum of 1, 2
*/
enum class SomeEnum(val i: Int) {
One(1), Two(2);
}
fun use() {
Some<caret>Enum.One
}
//INFO: <pre><b>public</b> <b>final</b> <b>enum class</b> SomeEnum : <a href="psi_element://kotlin.Enum">Enum</a>&lt;<a href="psi_element://SomeEnum">SomeEnum</a>&gt; <i>defined in</i> root package</pre><p>Enum of 1, 2</p>
+11
View File
@@ -0,0 +1,11 @@
enum class E {
A
}
fun use() {
E.valueOf<caret>("A")
}
//INFO: <b>public</b> <b>final</b> <b>fun</b> valueOf(value: String): <a href="psi_element://E">E</a> <i>defined in</i> E<p>Returns the enum constant of this type with the specified name. The string must match exactly an identifier used to declare an enum constant in this type. (Extraneous whitespace characters are not permitted.)</p>
//INFO: <dl><dt><b>Throws:</b></dt><dd><code>IllegalArgumentException</code> - if this enum type has no constant with the specified name</dd></dl>
+9
View File
@@ -0,0 +1,9 @@
enum class E {
}
fun use() {
E.values<caret>()
}
//INFO: <b>public</b> <b>final</b> <b>fun</b> values(): Array&lt;<a href="psi_element://E">E</a>&gt; <i>defined in</i> E<p>Returns an array containing the constants of this enum type, in the order they're declared. This method may be used to iterate over the constants.</p>
@@ -144,6 +144,36 @@ public class QuickDocProviderTestGenerated extends AbstractQuickDocProviderTest
doTest(fileName);
}
@TestMetadata("OnEnumClassReference.kt")
public void testOnEnumClassReference() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/editor/quickDoc/OnEnumClassReference.kt");
doTest(fileName);
}
@TestMetadata("OnEnumDeclaration.kt")
public void testOnEnumDeclaration() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/editor/quickDoc/OnEnumDeclaration.kt");
doTest(fileName);
}
@TestMetadata("OnEnumUsage.kt")
public void testOnEnumUsage() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/editor/quickDoc/OnEnumUsage.kt");
doTest(fileName);
}
@TestMetadata("OnEnumValueOfFunction.kt")
public void testOnEnumValueOfFunction() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/editor/quickDoc/OnEnumValueOfFunction.kt");
doTest(fileName);
}
@TestMetadata("OnEnumValuesFunction.kt")
public void testOnEnumValuesFunction() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/editor/quickDoc/OnEnumValuesFunction.kt");
doTest(fileName);
}
@TestMetadata("OnFunctionDeclarationWithPackage.kt")
public void testOnFunctionDeclarationWithPackage() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/editor/quickDoc/OnFunctionDeclarationWithPackage.kt");
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
* Copyright 2010-2016 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
* Copyright 2010-2016 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.