move KDoc-related logic shared between IDEA and Dokka to ide-common module

This commit is contained in:
Dmitry Jemerov
2016-05-10 14:07:53 +02:00
parent 07207949ba
commit 42ba14328c
7 changed files with 238 additions and 220 deletions
@@ -0,0 +1,76 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.kdoc
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag
import org.jetbrains.kotlin.kdoc.psi.api.KDoc
import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtPrimaryConstructor
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
fun DeclarationDescriptor.findKDoc(): KDocTag? {
if (this is DeclarationDescriptorWithSource) {
var psiDeclaration = (this.source as? PsiSourceElement)?.psi?.navigationElement
// KDoc for primary constructor is located inside of its class KDoc
if (psiDeclaration is KtPrimaryConstructor) {
psiDeclaration = psiDeclaration.getContainingClassOrObject()
}
if (psiDeclaration is KtDeclaration) {
val kdoc = psiDeclaration.docComment
if (kdoc != null) {
if (this is ConstructorDescriptor) {
// ConstructorDescriptor resolves to the same JetDeclaration
val constructorSection = kdoc.findSectionByTag(KDocKnownTag.CONSTRUCTOR)
if (constructorSection != null) {
return constructorSection
}
}
return kdoc.getDefaultSection()
}
}
}
if (this is PropertyDescriptor) {
val containingClassDescriptor = this.containingDeclaration as? ClassDescriptor
if (containingClassDescriptor != null) {
val classKDoc = containingClassDescriptor.findKDoc()?.getParentOfType<KDoc>(false)
if (classKDoc != null) {
val propertySection = classKDoc.findSectionByTag(KDocKnownTag.PROPERTY,
getName().asString())
if (propertySection != null) {
return propertySection
}
}
}
}
if (this is CallableDescriptor) {
for (baseDescriptor in this.overriddenDescriptors) {
val baseKDoc = baseDescriptor.original.findKDoc()
if (baseKDoc != null) {
return baseKDoc
}
}
}
return null
}
@@ -0,0 +1,152 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.kdoc
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.getFileResolutionScope
import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag
import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.FunctionDescriptorUtil
import org.jetbrains.kotlin.resolve.scopes.*
import org.jetbrains.kotlin.resolve.scopes.utils.collectDescriptorsFiltered
import org.jetbrains.kotlin.resolve.scopes.utils.memberScopeAsImportingScope
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
fun resolveKDocLink(resolutionFacade: ResolutionFacade,
fromDescriptor: DeclarationDescriptor,
fromSubjectOfTag: KDocTag?,
qualifiedName: List<String>): Collection<DeclarationDescriptor> {
if (fromSubjectOfTag?.knownTag == KDocKnownTag.PARAM) {
return resolveParamLink(fromDescriptor, qualifiedName)
}
// Try to find a matching local descriptor (parameter or type parameter) first.
if (qualifiedName.size == 1) {
val localResult = resolveInLocalScope(fromDescriptor, qualifiedName.single(), resolutionFacade)
if (!localResult.isEmpty()) {
return localResult
}
}
var result: Collection<DeclarationDescriptor> = listOf(fromDescriptor)
qualifiedName.forEach { nameComponent ->
val scope = getKDocLinkResolutionScope(resolutionFacade, result.singleOrNull() ?: return emptyList())
result = scope.collectDescriptorsFiltered(nameFilter = { it.asString() == nameComponent})
}
return result
}
private fun resolveInLocalScope(fromDescriptor: DeclarationDescriptor,
name: String,
resolutionFacade: ResolutionFacade): List<DeclarationDescriptor> {
val scope = getKDocLinkResolutionScope(resolutionFacade, fromDescriptor)
return scope.collectDescriptorsFiltered(nameFilter = { it.asString() == name }).filter {
it.containingDeclaration == fromDescriptor
}
}
fun getParamDescriptors(fromDescriptor: DeclarationDescriptor): List<DeclarationDescriptor> {
// TODO resolve parameters of functions passed as parameters
when (fromDescriptor) {
is CallableDescriptor ->
return fromDescriptor.valueParameters + fromDescriptor.typeParameters
is ClassifierDescriptor -> {
val typeParams = fromDescriptor.typeConstructor.parameters
if (fromDescriptor is ClassDescriptor) {
val constructorDescriptor = fromDescriptor.unsubstitutedPrimaryConstructor
if (constructorDescriptor != null) {
return typeParams + constructorDescriptor.valueParameters
}
}
return typeParams
}
}
return listOf()
}
private fun resolveParamLink(fromDescriptor: DeclarationDescriptor, qualifiedName: List<String>): List<DeclarationDescriptor> {
val name = qualifiedName.singleOrNull() ?: return listOf()
return getParamDescriptors(fromDescriptor).filter { it.name.asString() == name }
}
private fun getPackageInnerScope(descriptor: PackageFragmentDescriptor): MemberScope {
return descriptor.containingDeclaration.getPackage(descriptor.fqName).memberScope
}
private fun getClassInnerScope(outerScope: LexicalScope, descriptor: ClassDescriptor): LexicalScope {
val headerScope = LexicalScopeImpl(outerScope, descriptor, false, descriptor.thisAsReceiverParameter,
LexicalScopeKind.SYNTHETIC) {
for (typeParameter in descriptor.declaredTypeParameters) {
addClassifierDescriptor(typeParameter)
}
for (constructor in descriptor.constructors) {
addFunctionDescriptor(constructor)
}
}
val scopeChain = arrayListOf(descriptor.defaultType.memberScope,
descriptor.staticScope)
descriptor.companionObjectDescriptor?.let {
scopeChain.add(it.defaultType.memberScope)
}
return LexicalChainedScope(headerScope, descriptor, false, null,
LexicalScopeKind.SYNTHETIC,
scopeChain)
}
fun getKDocLinkResolutionScope(resolutionFacade: ResolutionFacade, descriptor: DeclarationDescriptor): LexicalScope {
return when (descriptor) {
is PackageFragmentDescriptor ->
LexicalScope.empty(getPackageInnerScope(descriptor).memberScopeAsImportingScope(), descriptor)
is PackageViewDescriptor ->
LexicalScope.empty(descriptor.memberScope.memberScopeAsImportingScope(), descriptor)
is ClassDescriptor ->
getClassInnerScope(getOuterScope(descriptor, resolutionFacade), descriptor)
is FunctionDescriptor ->
FunctionDescriptorUtil.getFunctionInnerScope(getOuterScope(descriptor, resolutionFacade),
descriptor, LocalRedeclarationChecker.DO_NOTHING)
is PropertyDescriptor ->
ScopeUtils.makeScopeForPropertyHeader(getOuterScope(descriptor, resolutionFacade), descriptor)
is DeclarationDescriptorNonRoot ->
getOuterScope(descriptor, resolutionFacade)
else -> throw IllegalArgumentException("Cannot find resolution scope for root $descriptor")
}
}
private fun getOuterScope(descriptor: DeclarationDescriptorWithSource, resolutionFacade: ResolutionFacade): LexicalScope {
val parent = descriptor.containingDeclaration
if (parent is PackageFragmentDescriptor) {
val containingFile = (descriptor.source as? PsiSourceElement)?.psi?.containingFile as? KtFile
if (containingFile != null) {
return resolutionFacade.getFileResolutionScope(containingFile)
}
}
return getKDocLinkResolutionScope(resolutionFacade, parent!!)
}
@@ -1,77 +0,0 @@
/*
* Copyright 2010-2015 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.kdoc
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag
import org.jetbrains.kotlin.kdoc.psi.api.KDoc
import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtPrimaryConstructor
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
object KDocFinder {
fun findKDoc(declaration: DeclarationDescriptor): KDocTag? {
if (declaration is DeclarationDescriptorWithSource) {
var psiDeclaration = (declaration.source as? PsiSourceElement)?.psi?.navigationElement
// KDoc for primary constructor is located inside of its class KDoc
if (psiDeclaration is KtPrimaryConstructor) {
psiDeclaration = psiDeclaration.getContainingClassOrObject()
}
if (psiDeclaration is KtDeclaration) {
val kdoc = psiDeclaration.docComment
if (kdoc != null) {
if (declaration is ConstructorDescriptor) {
// ConstructorDescriptor resolves to the same JetDeclaration
val constructorSection = kdoc.findSectionByTag(KDocKnownTag.CONSTRUCTOR)
if (constructorSection != null) {
return constructorSection
}
}
return kdoc.getDefaultSection()
}
}
}
if (declaration is PropertyDescriptor) {
val containingClassDescriptor = declaration.containingDeclaration as? ClassDescriptor
if (containingClassDescriptor != null) {
val classKDoc = findKDoc(containingClassDescriptor)?.getParentOfType<KDoc>(false)
if (classKDoc != null) {
val propertySection = classKDoc.findSectionByTag(KDocKnownTag.PROPERTY,
declaration.getName().asString())
if (propertySection != null) {
return propertySection
}
}
}
}
if (declaration is CallableDescriptor) {
for (baseDescriptor in declaration.overriddenDescriptors) {
val baseKDoc = findKDoc(baseDescriptor.original)
if (baseKDoc != null) {
return baseKDoc
}
}
}
return null
}
}
@@ -18,23 +18,13 @@ package org.jetbrains.kotlin.idea.kdoc
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.references.KtMultiReference
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.getFileResolutionScope
import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag
import org.jetbrains.kotlin.kdoc.psi.impl.KDocLink
import org.jetbrains.kotlin.kdoc.psi.impl.KDocName
import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.FunctionDescriptorUtil
import org.jetbrains.kotlin.resolve.scopes.*
import org.jetbrains.kotlin.resolve.scopes.utils.collectDescriptorsFiltered
import org.jetbrains.kotlin.resolve.scopes.utils.memberScopeAsImportingScope
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
class KDocReference(element: KDocName): KtMultiReference<KDocName>(element) {
override fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor> {
@@ -63,126 +53,3 @@ class KDocReference(element: KDocName): KtMultiReference<KDocName>(element) {
override fun getCanonicalText(): String = element.getNameText()
}
fun resolveKDocLink(resolutionFacade: ResolutionFacade,
fromDescriptor: DeclarationDescriptor,
fromSubjectOfTag: KDocTag?,
qualifiedName: List<String>): Collection<DeclarationDescriptor> {
if (fromSubjectOfTag?.knownTag == KDocKnownTag.PARAM) {
return resolveParamLink(fromDescriptor, qualifiedName)
}
// Try to find a matching local descriptor (parameter or type parameter) first.
if (qualifiedName.size == 1) {
val localResult = resolveInLocalScope(fromDescriptor, qualifiedName.single(), resolutionFacade)
if (!localResult.isEmpty()) {
return localResult
}
}
var result: Collection<DeclarationDescriptor> = listOf(fromDescriptor)
qualifiedName.forEach { nameComponent ->
val scope = getResolutionScope(resolutionFacade, result.singleOrNull() ?: return emptyList())
result = scope.collectDescriptorsFiltered(nameFilter = { it.asString() == nameComponent})
}
return result
}
private fun resolveInLocalScope(fromDescriptor: DeclarationDescriptor,
name: String,
resolutionFacade: ResolutionFacade): List<DeclarationDescriptor> {
val scope = getResolutionScope(resolutionFacade, fromDescriptor)
return scope.collectDescriptorsFiltered(nameFilter = { it.asString() == name }).filter {
it.containingDeclaration == fromDescriptor
}
}
fun getParamDescriptors(fromDescriptor: DeclarationDescriptor): List<DeclarationDescriptor> {
// TODO resolve parameters of functions passed as parameters
when (fromDescriptor) {
is CallableDescriptor ->
return fromDescriptor.valueParameters + fromDescriptor.typeParameters
is ClassifierDescriptor -> {
val typeParams = fromDescriptor.typeConstructor.parameters
if (fromDescriptor is ClassDescriptor) {
val constructorDescriptor = fromDescriptor.unsubstitutedPrimaryConstructor
if (constructorDescriptor != null) {
return typeParams + constructorDescriptor.valueParameters
}
}
return typeParams
}
}
return listOf()
}
private fun resolveParamLink(fromDescriptor: DeclarationDescriptor, qualifiedName: List<String>): List<DeclarationDescriptor> {
val name = qualifiedName.singleOrNull() ?: return listOf()
return getParamDescriptors(fromDescriptor).filter { it.name.asString() == name }
}
private fun getPackageInnerScope(descriptor: PackageFragmentDescriptor): MemberScope {
return descriptor.containingDeclaration.getPackage(descriptor.fqName).memberScope
}
private fun getClassInnerScope(outerScope: LexicalScope, descriptor: ClassDescriptor): LexicalScope {
val headerScope = LexicalScopeImpl(outerScope, descriptor, false, descriptor.thisAsReceiverParameter,
LexicalScopeKind.SYNTHETIC) {
for (typeParameter in descriptor.declaredTypeParameters) {
addClassifierDescriptor(typeParameter)
}
for (constructor in descriptor.constructors) {
addFunctionDescriptor(constructor)
}
}
val scopeChain = arrayListOf(descriptor.defaultType.memberScope,
descriptor.staticScope)
descriptor.companionObjectDescriptor?.let {
scopeChain.add(it.defaultType.memberScope)
}
return LexicalChainedScope(headerScope, descriptor, false, null,
LexicalScopeKind.SYNTHETIC,
scopeChain)
}
fun getResolutionScope(resolutionFacade: ResolutionFacade, descriptor: DeclarationDescriptor): LexicalScope {
return when (descriptor) {
is PackageFragmentDescriptor ->
LexicalScope.empty(getPackageInnerScope(descriptor).memberScopeAsImportingScope(), descriptor)
is PackageViewDescriptor ->
LexicalScope.empty(descriptor.memberScope.memberScopeAsImportingScope(), descriptor)
is ClassDescriptor ->
getClassInnerScope(getOuterScope(descriptor, resolutionFacade), descriptor)
is FunctionDescriptor ->
FunctionDescriptorUtil.getFunctionInnerScope(getOuterScope(descriptor, resolutionFacade),
descriptor, LocalRedeclarationChecker.DO_NOTHING)
is PropertyDescriptor ->
ScopeUtils.makeScopeForPropertyHeader(getOuterScope(descriptor, resolutionFacade), descriptor)
is DeclarationDescriptorNonRoot ->
getOuterScope(descriptor, resolutionFacade)
else -> throw IllegalArgumentException("Cannot find resolution scope for root $descriptor")
}
}
private fun getOuterScope(descriptor: DeclarationDescriptorWithSource, resolutionFacade: ResolutionFacade): LexicalScope {
val parent = descriptor.containingDeclaration
if (parent is PackageFragmentDescriptor) {
val containingFile = (descriptor.source as? PsiSourceElement)?.psi?.containingFile as? KtFile
if (containingFile != null) {
return resolutionFacade.getFileResolutionScope(containingFile)
}
}
return getResolutionScope(resolutionFacade, parent!!)
}
@@ -27,7 +27,7 @@ import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.core.ExpectedInfo
import org.jetbrains.kotlin.idea.kdoc.getParamDescriptors
import org.jetbrains.kotlin.idea.kdoc.getResolutionScope
import org.jetbrains.kotlin.idea.kdoc.getKDocLinkResolutionScope
import org.jetbrains.kotlin.idea.util.CallType
import org.jetbrains.kotlin.idea.util.substituteExtensionIfCallable
import org.jetbrains.kotlin.kdoc.lexer.KDocTokens
@@ -101,7 +101,7 @@ class KDocNameCompletionSession(
}
private fun addLinkCompletions(declarationDescriptor: DeclarationDescriptor) {
val scope = getResolutionScope(resolutionFacade, declarationDescriptor)
val scope = getKDocLinkResolutionScope(resolutionFacade, declarationDescriptor)
val implicitReceivers = scope.getImplicitReceiversHierarchy().map { it.value }
fun isApplicable(descriptor: DeclarationDescriptor): Boolean {
@@ -29,16 +29,16 @@ import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject
import org.jetbrains.kotlin.idea.kdoc.KDocFinder
import org.jetbrains.kotlin.idea.kdoc.KDocRenderer
import org.jetbrains.kotlin.idea.kdoc.findKDoc
import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtReferenceExpression
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.renderer.ClassifierNamePolicy
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
@@ -134,7 +134,7 @@ class KotlinQuickDocumentationProvider : AbstractDocumentationProvider() {
if (!quickNavigation) {
renderedDecl = "<pre>" + renderedDecl + "</pre>"
}
val comment = KDocFinder.findKDoc(declarationDescriptor)
val comment = declarationDescriptor.findKDoc()
if (comment != null) {
val renderedComment = KDocRenderer.renderKDoc(comment)
if (renderedComment.startsWith("<p>")) {
@@ -35,7 +35,7 @@ class KDocFinderTest() : LightPlatformCodeInsightFixtureTestCase() {
val declaration = (myFixture.file as KtFile).declarations[0]
val descriptor = declaration.resolveToDescriptor() as ClassDescriptor
val constructorDescriptor = descriptor.unsubstitutedPrimaryConstructor!!
val doc = KDocFinder.findKDoc(constructorDescriptor)
val doc = constructorDescriptor.findKDoc()
Assert.assertEquals("Doc for constructor of class C.", doc!!.getContent())
}
@@ -44,7 +44,7 @@ class KDocFinderTest() : LightPlatformCodeInsightFixtureTestCase() {
val declaration = (myFixture.file as KtFile).declarations.single { it.name == "Bar" }
val descriptor = declaration.resolveToDescriptor() as ClassDescriptor
val overriddenFunctionDescriptor = descriptor.defaultType.memberScope.getContributedFunctions(Name.identifier("xyzzy"), NoLookupLocation.FROM_TEST).single()
val doc = KDocFinder.findKDoc(overriddenFunctionDescriptor)
val doc = overriddenFunctionDescriptor.findKDoc()
Assert.assertEquals("Doc for method xyzzy", doc!!.getContent())
}
@@ -53,7 +53,7 @@ class KDocFinderTest() : LightPlatformCodeInsightFixtureTestCase() {
val declaration = (myFixture.file as KtFile).declarations.single { it.name == "Bar" }
val descriptor = declaration.resolveToDescriptor() as ClassDescriptor
val overriddenFunctionDescriptor = descriptor.defaultType.memberScope.getContributedFunctions(Name.identifier("xyzzy"), NoLookupLocation.FROM_TEST).single()
val doc = KDocFinder.findKDoc(overriddenFunctionDescriptor)
val doc = overriddenFunctionDescriptor.findKDoc()
Assert.assertEquals("Doc for method xyzzy", doc!!.getContent())
}
@@ -62,7 +62,7 @@ class KDocFinderTest() : LightPlatformCodeInsightFixtureTestCase() {
val declaration = (myFixture.file as KtFile).declarations.single { it.name == "Foo" }
val descriptor = declaration.resolveToDescriptor() as ClassDescriptor
val propertyDescriptor = descriptor.defaultType.memberScope.getContributedVariables(Name.identifier("xyzzy"), NoLookupLocation.FROM_TEST).single()
val doc = KDocFinder.findKDoc(propertyDescriptor)
val doc = propertyDescriptor.findKDoc()
Assert.assertEquals("Doc for property xyzzy", doc!!.getContent())
}
}