Skip to content

Commit

Permalink
Fix for KT-14819 Quick documentation for special Enum functions doesn…
Browse files Browse the repository at this point in the history
…'t work

 #KT-14819 fixed
  • Loading branch information
semoro committed Jan 10, 2017
1 parent fa01a4e commit d0cf1b0
Show file tree
Hide file tree
Showing 10 changed files with 153 additions and 9 deletions.
12 changes: 12 additions & 0 deletions core/builtins/native/kotlin/Enum.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,30 +27,32 @@ 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
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
Expand Down Expand Up @@ -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") {
Expand Down
10 changes: 10 additions & 0 deletions idea/testData/editor/quickDoc/OnEnumClassReference.kt
Original file line number Diff line number Diff line change
@@ -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 changes: 6 additions & 0 deletions idea/testData/editor/quickDoc/OnEnumDeclaration.kt
Original file line number Diff line number Diff line change
@@ -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 changes: 12 additions & 0 deletions idea/testData/editor/quickDoc/OnEnumUsage.kt
Original file line number Diff line number Diff line change
@@ -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 changes: 11 additions & 0 deletions idea/testData/editor/quickDoc/OnEnumValueOfFunction.kt
Original file line number Diff line number Diff line change
@@ -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 changes: 9 additions & 0 deletions idea/testData/editor/quickDoc/OnEnumValuesFunction.kt
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,36 @@ public void testOnClassDeclarationWithNoPackage() throws Exception {
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");
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down

0 comments on commit d0cf1b0

Please sign in to comment.