Rewritten KDoc-link resolve and completion to work more reasonably
This commit is contained in:
@@ -20,11 +20,13 @@ import com.intellij.openapi.components.ServiceManager
|
||||
import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.util.CallType
|
||||
import org.jetbrains.kotlin.idea.util.getFileResolutionScope
|
||||
import org.jetbrains.kotlin.idea.util.substituteExtensionIfCallable
|
||||
import org.jetbrains.kotlin.incremental.components.LookupLocation
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag
|
||||
import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
@@ -34,18 +36,22 @@ import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
import org.jetbrains.kotlin.resolve.FunctionDescriptorUtil
|
||||
import org.jetbrains.kotlin.resolve.QualifiedExpressionResolver
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.scopes.*
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.*
|
||||
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
|
||||
import org.jetbrains.kotlin.utils.Printer
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
fun resolveKDocLink(context: BindingContext,
|
||||
resolutionFacade: ResolutionFacade,
|
||||
fromDescriptor: DeclarationDescriptor,
|
||||
fromSubjectOfTag: KDocTag?,
|
||||
qualifiedName: List<String>): Collection<DeclarationDescriptor> =
|
||||
fun resolveKDocLink(
|
||||
context: BindingContext,
|
||||
resolutionFacade: ResolutionFacade,
|
||||
fromDescriptor: DeclarationDescriptor,
|
||||
fromSubjectOfTag: KDocTag?,
|
||||
qualifiedName: List<String>
|
||||
): Collection<DeclarationDescriptor> =
|
||||
when (fromSubjectOfTag?.knownTag) {
|
||||
KDocKnownTag.PARAM -> resolveParamLink(fromDescriptor, qualifiedName)
|
||||
KDocKnownTag.SAMPLE -> resolveKDocSampleLink(context, resolutionFacade, fromDescriptor, qualifiedName)
|
||||
@@ -56,8 +62,10 @@ fun resolveKDocLink(context: BindingContext,
|
||||
fun getParamDescriptors(fromDescriptor: DeclarationDescriptor): List<DeclarationDescriptor> {
|
||||
// TODO resolve parameters of functions passed as parameters
|
||||
when (fromDescriptor) {
|
||||
is CallableDescriptor ->
|
||||
is CallableDescriptor -> {
|
||||
return fromDescriptor.valueParameters + fromDescriptor.typeParameters
|
||||
}
|
||||
|
||||
is ClassifierDescriptor -> {
|
||||
val typeParams = fromDescriptor.typeConstructor.parameters
|
||||
if (fromDescriptor is ClassDescriptor) {
|
||||
@@ -68,20 +76,24 @@ fun getParamDescriptors(fromDescriptor: DeclarationDescriptor): List<Declaration
|
||||
}
|
||||
return typeParams
|
||||
}
|
||||
}
|
||||
|
||||
return listOf()
|
||||
else -> {
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolveParamLink(fromDescriptor: DeclarationDescriptor, qualifiedName: List<String>): List<DeclarationDescriptor> {
|
||||
val name = qualifiedName.singleOrNull() ?: return listOf()
|
||||
val name = qualifiedName.singleOrNull() ?: return emptyList()
|
||||
return getParamDescriptors(fromDescriptor).filter { it.name.asString() == name }
|
||||
}
|
||||
|
||||
fun resolveKDocSampleLink(context: BindingContext,
|
||||
resolutionFacade: ResolutionFacade,
|
||||
fromDescriptor: DeclarationDescriptor,
|
||||
qualifiedName: List<String>): Collection<DeclarationDescriptor> {
|
||||
fun resolveKDocSampleLink(
|
||||
context: BindingContext,
|
||||
resolutionFacade: ResolutionFacade,
|
||||
fromDescriptor: DeclarationDescriptor,
|
||||
qualifiedName: List<String>
|
||||
): Collection<DeclarationDescriptor> {
|
||||
|
||||
val resolvedViaService = SampleResolutionService.resolveSample(context, fromDescriptor, resolutionFacade, qualifiedName)
|
||||
if (resolvedViaService.isNotEmpty()) return resolvedViaService
|
||||
@@ -96,19 +108,21 @@ private fun resolveDefaultKDocLink(
|
||||
qualifiedName: List<String>
|
||||
): Collection<DeclarationDescriptor> {
|
||||
|
||||
val scope = getKDocLinkResolutionScope(resolutionFacade, fromDescriptor)
|
||||
val contextScope = getKDocLinkResolutionScope(resolutionFacade, fromDescriptor)
|
||||
|
||||
if (qualifiedName.size == 1) {
|
||||
val shortName = Name.identifier(qualifiedName.single())
|
||||
|
||||
val descriptorsByName = SmartList<DeclarationDescriptor>()
|
||||
scope.collectAllByName(shortName, descriptorsByName)
|
||||
|
||||
descriptorsByName.addIfNotNull(contextScope.findClassifier(shortName, NoLookupLocation.FROM_IDE))
|
||||
descriptorsByName.addIfNotNull(contextScope.findPackage(shortName))
|
||||
descriptorsByName.addAll(contextScope.collectFunctions(shortName, NoLookupLocation.FROM_IDE))
|
||||
descriptorsByName.addAll(contextScope.collectVariables(shortName, NoLookupLocation.FROM_IDE))
|
||||
|
||||
// Try to find a matching local descriptor (parameter or type parameter) first
|
||||
val localDescriptors = descriptorsByName.filter { it.containingDeclaration == fromDescriptor }
|
||||
if (localDescriptors.isNotEmpty()) return localDescriptors
|
||||
|
||||
descriptorsByName.addIfNotNull(fromDescriptor.module.getPackage(FqName.topLevel(shortName)))
|
||||
|
||||
return descriptorsByName
|
||||
}
|
||||
|
||||
@@ -120,23 +134,17 @@ private fun resolveDefaultKDocLink(
|
||||
// TODO escape identifiers
|
||||
val codeFragment = factory.createExpressionCodeFragment(qualifiedName.joinToString("."), contextElement)
|
||||
val qualifiedExpression = codeFragment.findElementAt(codeFragment.textLength - 1)?.getStrictParentOfType<KtQualifiedExpression>() ?: return emptyList()
|
||||
val (descriptor, memberName) = qualifiedExpressionResolver.resolveClassOrPackageInQualifiedExpression(qualifiedExpression, scope, context)
|
||||
val (descriptor, memberName) = qualifiedExpressionResolver.resolveClassOrPackageInQualifiedExpression(qualifiedExpression, contextScope, context)
|
||||
if (descriptor == null) return emptyList()
|
||||
if (memberName != null) {
|
||||
val memberScope = getKDocLinkResolutionScope(resolutionFacade, descriptor)
|
||||
return memberScope.collectAllByName(memberName)
|
||||
val memberScope = getKDocLinkMemberScope(descriptor, contextScope)
|
||||
return memberScope.getContributedFunctions(memberName, NoLookupLocation.FROM_IDE) +
|
||||
memberScope.getContributedVariables(memberName, NoLookupLocation.FROM_IDE) +
|
||||
listOfNotNull(memberScope.getContributedClassifier(memberName, NoLookupLocation.FROM_IDE))
|
||||
}
|
||||
return listOf(descriptor)
|
||||
}
|
||||
|
||||
private fun LexicalScope.collectAllByName(shortName: Name, toCollection: MutableCollection<DeclarationDescriptor> = SmartList()): Collection<DeclarationDescriptor> {
|
||||
toCollection.addIfNotNull(findClassifier(shortName, NoLookupLocation.FROM_IDE))
|
||||
toCollection.addIfNotNull(findPackage(shortName))
|
||||
toCollection.addAll(collectFunctions(shortName, NoLookupLocation.FROM_IDE))
|
||||
toCollection.addAll(collectVariables(shortName, NoLookupLocation.FROM_IDE))
|
||||
return toCollection
|
||||
}
|
||||
|
||||
private fun getPackageInnerScope(descriptor: PackageFragmentDescriptor): MemberScope {
|
||||
return descriptor.containingDeclaration.getPackage(descriptor.fqName).memberScope
|
||||
}
|
||||
@@ -145,60 +153,108 @@ private fun getClassInnerScope(outerScope: LexicalScope, descriptor: ClassDescri
|
||||
|
||||
val headerScope = LexicalScopeImpl(outerScope, descriptor, false, descriptor.thisAsReceiverParameter,
|
||||
LexicalScopeKind.SYNTHETIC) {
|
||||
for (typeParameter in descriptor.declaredTypeParameters) {
|
||||
addClassifierDescriptor(typeParameter)
|
||||
}
|
||||
for (constructor in descriptor.constructors) {
|
||||
addFunctionDescriptor(constructor)
|
||||
}
|
||||
descriptor.declaredTypeParameters.forEach { addClassifierDescriptor(it) }
|
||||
descriptor.constructors.forEach { addFunctionDescriptor(it) }
|
||||
}
|
||||
|
||||
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)
|
||||
val scopeChain = listOfNotNull(
|
||||
descriptor.defaultType.memberScope,
|
||||
descriptor.staticScope,
|
||||
descriptor.companionObjectDescriptor?.defaultType?.memberScope
|
||||
)
|
||||
return LexicalChainedScope(headerScope, descriptor, false, null, LexicalScopeKind.SYNTHETIC, scopeChain)
|
||||
}
|
||||
|
||||
fun getKDocLinkResolutionScope(resolutionFacade: ResolutionFacade, descriptor: DeclarationDescriptor): LexicalScope {
|
||||
return when (descriptor) {
|
||||
is PackageFragmentDescriptor ->
|
||||
LexicalScope.Base(getPackageInnerScope(descriptor).memberScopeAsImportingScope(), descriptor)
|
||||
|
||||
is PackageViewDescriptor ->
|
||||
LexicalScope.Base(descriptor.memberScope.memberScopeAsImportingScope(), descriptor)
|
||||
|
||||
fun getKDocLinkResolutionScope(resolutionFacade: ResolutionFacade, contextDescriptor: DeclarationDescriptor): LexicalScope {
|
||||
return when (contextDescriptor) {
|
||||
is ClassDescriptor ->
|
||||
getClassInnerScope(getOuterScope(descriptor, resolutionFacade), descriptor)
|
||||
getClassInnerScope(getOuterScope(contextDescriptor, resolutionFacade), contextDescriptor)
|
||||
|
||||
is FunctionDescriptor ->
|
||||
FunctionDescriptorUtil.getFunctionInnerScope(getOuterScope(descriptor, resolutionFacade),
|
||||
descriptor, LocalRedeclarationChecker.DO_NOTHING)
|
||||
FunctionDescriptorUtil.getFunctionInnerScope(getOuterScope(contextDescriptor, resolutionFacade),
|
||||
contextDescriptor, LocalRedeclarationChecker.DO_NOTHING)
|
||||
|
||||
is PropertyDescriptor ->
|
||||
ScopeUtils.makeScopeForPropertyHeader(getOuterScope(descriptor, resolutionFacade), descriptor)
|
||||
ScopeUtils.makeScopeForPropertyHeader(getOuterScope(contextDescriptor, resolutionFacade), contextDescriptor)
|
||||
|
||||
is DeclarationDescriptorNonRoot ->
|
||||
getOuterScope(descriptor, resolutionFacade)
|
||||
getOuterScope(contextDescriptor, resolutionFacade)
|
||||
|
||||
else -> throw IllegalArgumentException("Cannot find resolution scope for root $descriptor")
|
||||
else -> throw IllegalArgumentException("Cannot find resolution scope for root $contextDescriptor")
|
||||
}
|
||||
}
|
||||
|
||||
private fun getOuterScope(descriptor: DeclarationDescriptorWithSource, resolutionFacade: ResolutionFacade): LexicalScope {
|
||||
val parent = descriptor.containingDeclaration
|
||||
val parent = descriptor.containingDeclaration!!
|
||||
if (parent is PackageFragmentDescriptor) {
|
||||
val containingFile = (descriptor.source as? PsiSourceElement)?.psi?.containingFile as? KtFile
|
||||
if (containingFile != null) {
|
||||
val kotlinCacheService = ServiceManager.getService(containingFile.project, KotlinCacheService::class.java)
|
||||
val facadeToUse = kotlinCacheService?.getResolutionFacade(listOf(containingFile)) ?: resolutionFacade
|
||||
return facadeToUse.getFileResolutionScope(containingFile)
|
||||
}
|
||||
?: return LexicalScope.Base(ImportingScope.Empty, parent)
|
||||
val kotlinCacheService = ServiceManager.getService(containingFile.project, KotlinCacheService::class.java)
|
||||
val facadeToUse = kotlinCacheService?.getResolutionFacade(listOf(containingFile)) ?: resolutionFacade
|
||||
return facadeToUse.getFileResolutionScope(containingFile)
|
||||
}
|
||||
else {
|
||||
return getKDocLinkResolutionScope(resolutionFacade, parent)
|
||||
}
|
||||
}
|
||||
|
||||
fun getKDocLinkMemberScope(descriptor: DeclarationDescriptor, contextScope: LexicalScope): MemberScope {
|
||||
return when (descriptor) {
|
||||
is PackageFragmentDescriptor -> getPackageInnerScope(descriptor)
|
||||
|
||||
is PackageViewDescriptor -> descriptor.memberScope
|
||||
|
||||
is ClassDescriptor -> {
|
||||
ChainedMemberScope("Member scope for KDoc resolve", listOfNotNull(
|
||||
descriptor.unsubstitutedMemberScope,
|
||||
descriptor.staticScope,
|
||||
descriptor.companionObjectDescriptor?.unsubstitutedMemberScope,
|
||||
ExtensionsScope(descriptor, contextScope)
|
||||
))
|
||||
}
|
||||
|
||||
else -> MemberScope.Empty
|
||||
}
|
||||
}
|
||||
|
||||
private class ExtensionsScope(
|
||||
private val receiverClass: ClassDescriptor,
|
||||
private val contextScope: LexicalScope
|
||||
) : MemberScope {
|
||||
private val receiverTypes = listOf(receiverClass.defaultType)
|
||||
|
||||
override fun getContributedFunctions(name: Name, location: LookupLocation): Collection<SimpleFunctionDescriptor> {
|
||||
return contextScope.collectFunctions(name, location)
|
||||
.flatMap { if (it is SimpleFunctionDescriptor && it.isExtension) it.substituteExtensionIfCallable(receiverTypes, CallType.DOT) else emptyList() }
|
||||
}
|
||||
|
||||
override fun getContributedVariables(name: Name, location: LookupLocation): Collection<PropertyDescriptor> {
|
||||
return contextScope.collectVariables(name, location)
|
||||
.flatMap { if (it is PropertyDescriptor && it.isExtension) it.substituteExtensionIfCallable(receiverTypes, CallType.DOT) else emptyList() }
|
||||
}
|
||||
|
||||
override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? = null
|
||||
|
||||
override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean): Collection<DeclarationDescriptor> {
|
||||
if (DescriptorKindExclude.Extensions in kindFilter.excludes) return emptyList()
|
||||
return contextScope.collectDescriptorsFiltered(kindFilter exclude DescriptorKindExclude.NonExtensions, nameFilter, changeNamesForAliased = true)
|
||||
.flatMap { if (it is CallableDescriptor && it.isExtension) it.substituteExtensionIfCallable(receiverTypes, CallType.DOT) else emptyList() }
|
||||
}
|
||||
|
||||
override fun getFunctionNames(): Set<Name> {
|
||||
return getContributedDescriptors(kindFilter = DescriptorKindFilter.FUNCTIONS)
|
||||
.map { it.name }
|
||||
.toSet()
|
||||
}
|
||||
|
||||
override fun getVariableNames(): Set<Name> {
|
||||
return getContributedDescriptors(kindFilter = DescriptorKindFilter.VARIABLES)
|
||||
.map { it.name }
|
||||
.toSet()
|
||||
}
|
||||
|
||||
override fun printScopeStructure(p: Printer) {
|
||||
p.println("Extensions for ${receiverClass.name} in:")
|
||||
contextScope.printStructure(p)
|
||||
}
|
||||
return getKDocLinkResolutionScope(resolutionFacade, parent!!)
|
||||
}
|
||||
|
||||
+15
-58
@@ -23,22 +23,17 @@ import com.intellij.codeInsight.lookup.LookupElementDecorator
|
||||
import com.intellij.patterns.PlatformPatterns.psiElement
|
||||
import com.intellij.patterns.StandardPatterns
|
||||
import com.intellij.util.ProcessingContext
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor
|
||||
import org.jetbrains.kotlin.idea.core.ExpectedInfo
|
||||
import org.jetbrains.kotlin.idea.kdoc.getKDocLinkMemberScope
|
||||
import org.jetbrains.kotlin.idea.kdoc.getKDocLinkResolutionScope
|
||||
import org.jetbrains.kotlin.idea.kdoc.getParamDescriptors
|
||||
import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink
|
||||
import org.jetbrains.kotlin.idea.util.CallType
|
||||
import org.jetbrains.kotlin.idea.util.substituteExtensionIfCallable
|
||||
import org.jetbrains.kotlin.kdoc.lexer.KDocTokens
|
||||
import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag
|
||||
import org.jetbrains.kotlin.kdoc.psi.api.KDoc
|
||||
import org.jetbrains.kotlin.kdoc.psi.impl.KDocLink
|
||||
import org.jetbrains.kotlin.kdoc.psi.impl.KDocName
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||
@@ -46,12 +41,8 @@ import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.collectDescriptorsFiltered
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.getImplicitReceiversHierarchy
|
||||
|
||||
class KDocCompletionContributor : CompletionContributor() {
|
||||
init {
|
||||
@@ -79,6 +70,7 @@ class KDocNameCompletionSession(
|
||||
toFromOriginalFileMapper: ToFromOriginalFileMapper,
|
||||
resultSet: CompletionResultSet
|
||||
) : CompletionSession(CompletionSessionConfiguration(parameters), parameters, toFromOriginalFileMapper, resultSet) {
|
||||
|
||||
override val descriptorKindFilter: DescriptorKindFilter? get() = null
|
||||
override val expectedInfos: Collection<ExpectedInfo> get() = emptyList()
|
||||
|
||||
@@ -100,63 +92,28 @@ class KDocNameCompletionSession(
|
||||
declarationDescriptor: DeclarationDescriptor) {
|
||||
val section = position.getContainingSection()
|
||||
val documentedParameters = section.findTagsByName("param").map { it.getSubjectName() }.toSet()
|
||||
val descriptors = getParamDescriptors(declarationDescriptor)
|
||||
getParamDescriptors(declarationDescriptor)
|
||||
.filter { it.name.asString() !in documentedParameters }
|
||||
|
||||
descriptors.forEach {
|
||||
collector.addElement(basicLookupElementFactory.createLookupElement(it, parametersAndTypeGrayed = true))
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectPackageViewDescriptors(qualifiedLink: List<String>, nameFilter: (Name) -> Boolean): Sequence<PackageViewDescriptor> {
|
||||
val fqName = FqName.fromSegments(qualifiedLink)
|
||||
return moduleDescriptor.getSubPackagesOf(fqName, nameFilter).asSequence()
|
||||
.map { moduleDescriptor.getPackage(it) }
|
||||
}
|
||||
|
||||
private fun collectDescriptorsFromScope(scope: LexicalScope, nameFilter: (Name) -> Boolean, collectFromParentScopes: Boolean): Sequence<DeclarationDescriptor> {
|
||||
val implicitReceivers = scope.getImplicitReceiversHierarchy().map { it.value }
|
||||
|
||||
fun isApplicable(descriptor: DeclarationDescriptor): Boolean {
|
||||
if (descriptor is CallableDescriptor) {
|
||||
val extensionReceiver = descriptor.extensionReceiverParameter
|
||||
if (extensionReceiver != null) {
|
||||
val substituted = descriptor.substituteExtensionIfCallable(implicitReceivers, bindingContext, DataFlowInfo.EMPTY,
|
||||
CallType.DEFAULT, moduleDescriptor)
|
||||
return substituted.isNotEmpty()
|
||||
.forEach {
|
||||
collector.addElement(basicLookupElementFactory.createLookupElement(it, parametersAndTypeGrayed = true))
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
val sequence = when {
|
||||
collectFromParentScopes -> scope.collectDescriptorsFiltered(nameFilter = nameFilter, changeNamesForAliased = true).asSequence()
|
||||
|
||||
scope is LexicalScope.Base -> scope.parent.getContributedDescriptors(nameFilter = nameFilter).asSequence()
|
||||
|
||||
else -> scope.getContributedDescriptors(nameFilter = nameFilter).asSequence() +
|
||||
scope.parent.collectDescriptorsFiltered(nameFilter = nameFilter).asSequence().filter { it.isExtension }
|
||||
}
|
||||
return sequence.filter(::isApplicable)
|
||||
}
|
||||
|
||||
private fun collectDescriptorsForLinkCompletion(declarationDescriptor: DeclarationDescriptor, kDocLink: KDocLink): Collection<DeclarationDescriptor> {
|
||||
val contextScope = getKDocLinkResolutionScope(resolutionFacade, declarationDescriptor)
|
||||
|
||||
private fun collectDescriptorsForLinkCompletion(declarationDescriptor: DeclarationDescriptor, kDocLink: KDocLink): Sequence<DeclarationDescriptor> {
|
||||
val qualifiedLink = kDocLink.getLinkText().split('.').dropLast(1)
|
||||
val nameFilter = descriptorNameFilter.toNameFilter()
|
||||
return if (qualifiedLink.isNotEmpty()) {
|
||||
if (qualifiedLink.isNotEmpty()) {
|
||||
val parentDescriptors = resolveKDocLink(bindingContext, resolutionFacade, declarationDescriptor, kDocLink.getTagIfSubject(), qualifiedLink)
|
||||
val childDescriptorsOfPartialLink = parentDescriptors.asSequence().flatMap {
|
||||
val scope = getKDocLinkResolutionScope(resolutionFacade, it)
|
||||
collectDescriptorsFromScope(scope, nameFilter, false)
|
||||
}
|
||||
|
||||
(collectPackageViewDescriptors(qualifiedLink, nameFilter) + childDescriptorsOfPartialLink)
|
||||
return parentDescriptors
|
||||
.flatMap {
|
||||
val scope = getKDocLinkMemberScope(it, contextScope)
|
||||
scope.getContributedDescriptors(nameFilter = nameFilter)
|
||||
}
|
||||
}
|
||||
else {
|
||||
val scope = getKDocLinkResolutionScope(resolutionFacade, declarationDescriptor)
|
||||
(collectDescriptorsFromScope(scope, nameFilter, true)
|
||||
+ collectPackageViewDescriptors(qualifiedLink, nameFilter))
|
||||
return contextScope.collectDescriptorsFiltered(DescriptorKindFilter.ALL, nameFilter, changeNamesForAliased = true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,7 +121,7 @@ class KDocNameCompletionSession(
|
||||
collectDescriptorsForLinkCompletion(declarationDescriptor, kDocLink).forEach {
|
||||
val element = basicLookupElementFactory.createLookupElement(it, parametersAndTypeGrayed = true)
|
||||
collector.addElement(object : LookupElementDecorator<LookupElement>(element) {
|
||||
override fun handleInsert(context: InsertionContext?) {
|
||||
override fun handleInsert(context: InsertionContext) {
|
||||
// insert only plain name here, no qualifier/parentheses/etc.
|
||||
}
|
||||
})
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import kotlin.text.capitalize as xxx
|
||||
|
||||
/**
|
||||
* [String.x<caret>]
|
||||
*/
|
||||
fun foo(){}
|
||||
|
||||
// EXIST: { lookupString: "xxx", itemText: "xxx" }
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* [kotlin.<caret>]
|
||||
*/
|
||||
fun foo(){}
|
||||
|
||||
// EXIST: io
|
||||
// EXIST: Int
|
||||
+16
-3
@@ -1,6 +1,10 @@
|
||||
package a
|
||||
|
||||
class B {
|
||||
interface I
|
||||
|
||||
class A : I
|
||||
|
||||
class B : I {
|
||||
/**
|
||||
* [a.B.<caret>]
|
||||
*/
|
||||
@@ -10,11 +14,20 @@ class B {
|
||||
}
|
||||
|
||||
fun B.ext() {
|
||||
|
||||
}
|
||||
|
||||
val B.extVal: String
|
||||
get() = ""
|
||||
|
||||
fun A.wrongExt(){}
|
||||
|
||||
val A.wrongExtVal: String
|
||||
get() = ""
|
||||
|
||||
fun I.extForSuper(){}
|
||||
|
||||
// EXIST: ext
|
||||
// EXIST: extVal
|
||||
// EXIST: extVal
|
||||
// ABSENT: wrongExt
|
||||
// ABSENT: wrongExtVal
|
||||
// EXIST: extForSuper
|
||||
|
||||
+8
-8
@@ -1,19 +1,19 @@
|
||||
package z
|
||||
|
||||
/**
|
||||
* [<caret>]
|
||||
* [a<caret>]
|
||||
*/
|
||||
fun f(xyzzy: String) {
|
||||
fun az(ap: String) {
|
||||
|
||||
}
|
||||
|
||||
fun bar() {
|
||||
fun aaa() {
|
||||
|
||||
}
|
||||
|
||||
class Z
|
||||
class aZ
|
||||
|
||||
// EXIST: z
|
||||
// EXIST: Z
|
||||
// EXIST: xyzzy
|
||||
// EXIST: bar
|
||||
// EXIST: az
|
||||
// EXIST: aZ
|
||||
// EXIST: ap
|
||||
// EXIST: aaa
|
||||
|
||||
+5
-4
@@ -1,8 +1,8 @@
|
||||
class Foo {
|
||||
/**
|
||||
* [<caret>]
|
||||
* [b<caret>]
|
||||
*/
|
||||
fun xyzzy() {
|
||||
fun baz() {
|
||||
|
||||
}
|
||||
|
||||
@@ -11,8 +11,9 @@ class Foo {
|
||||
}
|
||||
}
|
||||
|
||||
fun Foo.quux() {
|
||||
fun Foo.boo() {
|
||||
}
|
||||
|
||||
// EXIST: bar
|
||||
// EXIST: quux
|
||||
// EXIST: baz
|
||||
// EXIST: boo
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
class Foo
|
||||
fun Foo.ext() = ""
|
||||
|
||||
|
||||
/**
|
||||
* [Foo.ext.<caret>]
|
||||
*/
|
||||
fun test() {}
|
||||
|
||||
// NOTHING_ELSE
|
||||
@@ -0,0 +1,9 @@
|
||||
class Foo
|
||||
|
||||
/**
|
||||
* [Foo.<caret>]
|
||||
*/
|
||||
fun test() {}
|
||||
|
||||
|
||||
// ABSENT: test
|
||||
+6
@@ -3046,6 +3046,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("KDocExtension.kt")
|
||||
public void testKDocExtension() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/java/importAliases/KDocExtension.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("PrefixUsed.kt")
|
||||
public void testPrefixUsed() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/java/importAliases/PrefixUsed.kt");
|
||||
|
||||
+21
-3
@@ -32,6 +32,12 @@ import java.util.regex.Pattern;
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class KDocCompletionTestGenerated extends AbstractJvmBasicCompletionTest {
|
||||
@TestMetadata("AfterPackageName.kt")
|
||||
public void testAfterPackageName() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/kdoc/AfterPackageName.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInKdoc() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/kdoc"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
@@ -42,9 +48,9 @@ public class KDocCompletionTestGenerated extends AbstractJvmBasicCompletionTest
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ExtensionsForSubClassFQLink.kt")
|
||||
public void testExtensionsForSubClassFQLink() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/kdoc/ExtensionsForSubClassFQLink.kt");
|
||||
@TestMetadata("ExtensionsForNestedClassFQLink.kt")
|
||||
public void testExtensionsForNestedClassFQLink() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/kdoc/ExtensionsForNestedClassFQLink.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@@ -90,6 +96,18 @@ public class KDocCompletionTestGenerated extends AbstractJvmBasicCompletionTest
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("NoCompletionAfterFunName.kt")
|
||||
public void testNoCompletionAfterFunName() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/kdoc/NoCompletionAfterFunName.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("NoTopLevelForQualified.kt")
|
||||
public void testNoTopLevelForQualified() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/kdoc/NoTopLevelForQualified.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("NotTagName.kt")
|
||||
public void testNotTagName() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/kdoc/NotTagName.kt");
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
class Foo
|
||||
class Bar
|
||||
|
||||
fun Foo.foo(){}
|
||||
|
||||
fun foo(){}
|
||||
|
||||
/**
|
||||
* [Bar.<caret>foo]
|
||||
*/
|
||||
fun test() {}
|
||||
|
||||
// REF_EMPTY
|
||||
@@ -0,0 +1,10 @@
|
||||
import kotlin.io.extension
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* [File.<caret>extension]
|
||||
*/
|
||||
fun f() {
|
||||
}
|
||||
|
||||
// REF: (for java.io.File in kotlin.io).extension
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package a
|
||||
|
||||
class B {
|
||||
/**
|
||||
* [a.B.<caret>ext]
|
||||
*/
|
||||
fun member() {
|
||||
}
|
||||
}
|
||||
|
||||
fun B.ext() {
|
||||
}
|
||||
|
||||
// REF: (for B in a).ext()
|
||||
@@ -0,0 +1,10 @@
|
||||
class Foo
|
||||
|
||||
fun Foo.foo(){}
|
||||
|
||||
/**
|
||||
* [<caret>foo]
|
||||
*/
|
||||
fun test() {}
|
||||
|
||||
// REF: (for Foo in <root>).foo()
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package a
|
||||
|
||||
class B {
|
||||
/**
|
||||
* [a.B.<caret>extVal]
|
||||
*/
|
||||
fun member() {
|
||||
}
|
||||
}
|
||||
|
||||
val B.extVal: String
|
||||
get() = ""
|
||||
|
||||
// REF: (for B in a).extVal
|
||||
@@ -0,0 +1,8 @@
|
||||
class Foo
|
||||
|
||||
/**
|
||||
* [Foo.<caret>test]
|
||||
*/
|
||||
fun test() {}
|
||||
|
||||
// REF_EMPTY
|
||||
@@ -49,6 +49,12 @@ public class KdocResolveTestGenerated extends AbstractReferenceResolveTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("CheckExtensionReceiver.kt")
|
||||
public void testCheckExtensionReceiver() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kdoc/resolve/CheckExtensionReceiver.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ClassSelfReference.kt")
|
||||
public void testClassSelfReference() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kdoc/resolve/ClassSelfReference.kt");
|
||||
@@ -73,6 +79,30 @@ public class KdocResolveTestGenerated extends AbstractReferenceResolveTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ExtensionFromImports.kt")
|
||||
public void testExtensionFromImports() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kdoc/resolve/ExtensionFromImports.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ExtensionFun.kt")
|
||||
public void testExtensionFun() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kdoc/resolve/ExtensionFun.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ExtensionNonQualified.kt")
|
||||
public void testExtensionNonQualified() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kdoc/resolve/ExtensionNonQualified.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ExtensionVal.kt")
|
||||
public void testExtensionVal() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kdoc/resolve/ExtensionVal.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ImportAliasClass.kt")
|
||||
public void testImportAliasClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kdoc/resolve/ImportAliasClass.kt");
|
||||
@@ -85,6 +115,12 @@ public class KdocResolveTestGenerated extends AbstractReferenceResolveTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("OnlyMembersFromClass.kt")
|
||||
public void testOnlyMembersFromClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kdoc/resolve/OnlyMembersFromClass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("Overloads.kt")
|
||||
public void testOverloads() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kdoc/resolve/Overloads.kt");
|
||||
|
||||
Reference in New Issue
Block a user