Support for import aliases in both resolve and completion in KDoc
This commit is contained in:
@@ -21,9 +21,11 @@ import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
|
|||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||||
import org.jetbrains.kotlin.idea.util.getFileResolutionScope
|
import org.jetbrains.kotlin.idea.util.getFileResolutionScope
|
||||||
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag
|
import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag
|
||||||
import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
|
import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||||
import org.jetbrains.kotlin.psi.KtQualifiedExpression
|
import org.jetbrains.kotlin.psi.KtQualifiedExpression
|
||||||
@@ -34,9 +36,10 @@ import org.jetbrains.kotlin.resolve.FunctionDescriptorUtil
|
|||||||
import org.jetbrains.kotlin.resolve.QualifiedExpressionResolver
|
import org.jetbrains.kotlin.resolve.QualifiedExpressionResolver
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||||
import org.jetbrains.kotlin.resolve.scopes.*
|
import org.jetbrains.kotlin.resolve.scopes.*
|
||||||
import org.jetbrains.kotlin.resolve.scopes.utils.collectDescriptorsFiltered
|
import org.jetbrains.kotlin.resolve.scopes.utils.*
|
||||||
import org.jetbrains.kotlin.resolve.scopes.utils.memberScopeAsImportingScope
|
|
||||||
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
|
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
|
||||||
|
import org.jetbrains.kotlin.utils.SmartList
|
||||||
|
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||||
|
|
||||||
fun resolveKDocLink(context: BindingContext,
|
fun resolveKDocLink(context: BindingContext,
|
||||||
resolutionFacade: ResolutionFacade,
|
resolutionFacade: ResolutionFacade,
|
||||||
@@ -86,31 +89,27 @@ fun resolveKDocSampleLink(context: BindingContext,
|
|||||||
return resolveDefaultKDocLink(context, resolutionFacade, fromDescriptor, qualifiedName)
|
return resolveDefaultKDocLink(context, resolutionFacade, fromDescriptor, qualifiedName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun resolveDefaultKDocLink(
|
||||||
|
context: BindingContext,
|
||||||
|
resolutionFacade: ResolutionFacade,
|
||||||
private fun resolveDefaultKDocLink(context: BindingContext,
|
fromDescriptor: DeclarationDescriptor,
|
||||||
resolutionFacade: ResolutionFacade,
|
qualifiedName: List<String>
|
||||||
fromDescriptor: DeclarationDescriptor,
|
): Collection<DeclarationDescriptor> {
|
||||||
qualifiedName: List<String>): Collection<DeclarationDescriptor> {
|
|
||||||
|
|
||||||
val scope = getKDocLinkResolutionScope(resolutionFacade, fromDescriptor)
|
val scope = getKDocLinkResolutionScope(resolutionFacade, fromDescriptor)
|
||||||
|
|
||||||
if (qualifiedName.size == 1) {
|
if (qualifiedName.size == 1) {
|
||||||
val shortName = qualifiedName.single()
|
val shortName = Name.identifier(qualifiedName.single())
|
||||||
val descriptorsByName = scope.collectDescriptorsFiltered(nameFilter = { it.asString() == shortName })
|
val descriptorsByName = SmartList<DeclarationDescriptor>()
|
||||||
// Try to find a matching local descriptor (parameter or type parameter) first.
|
scope.collectAllByName(shortName, descriptorsByName)
|
||||||
|
|
||||||
|
// Try to find a matching local descriptor (parameter or type parameter) first
|
||||||
val localDescriptors = descriptorsByName.filter { it.containingDeclaration == fromDescriptor }
|
val localDescriptors = descriptorsByName.filter { it.containingDeclaration == fromDescriptor }
|
||||||
|
|
||||||
if (localDescriptors.isNotEmpty()) return localDescriptors
|
if (localDescriptors.isNotEmpty()) return localDescriptors
|
||||||
|
|
||||||
val moduleDescriptor = fromDescriptor.module
|
descriptorsByName.addIfNotNull(fromDescriptor.module.getPackage(FqName.topLevel(shortName)))
|
||||||
|
|
||||||
val packagesByName = moduleDescriptor.getSubPackagesOf(FqName.ROOT, { true })
|
return descriptorsByName
|
||||||
.filter { it.asString() == shortName }
|
|
||||||
.map { moduleDescriptor.getPackage(it) }
|
|
||||||
|
|
||||||
return descriptorsByName + packagesByName
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val moduleDescriptor = fromDescriptor.module
|
val moduleDescriptor = fromDescriptor.module
|
||||||
@@ -125,11 +124,19 @@ private fun resolveDefaultKDocLink(context: BindingContext,
|
|||||||
if (descriptor == null) return emptyList()
|
if (descriptor == null) return emptyList()
|
||||||
if (memberName != null) {
|
if (memberName != null) {
|
||||||
val memberScope = getKDocLinkResolutionScope(resolutionFacade, descriptor)
|
val memberScope = getKDocLinkResolutionScope(resolutionFacade, descriptor)
|
||||||
return memberScope.collectDescriptorsFiltered(nameFilter = { it == memberName })
|
return memberScope.collectAllByName(memberName)
|
||||||
}
|
}
|
||||||
return listOf(descriptor)
|
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 {
|
private fun getPackageInnerScope(descriptor: PackageFragmentDescriptor): MemberScope {
|
||||||
return descriptor.containingDeclaration.getPackage(descriptor.fqName).memberScope
|
return descriptor.containingDeclaration.getPackage(descriptor.fqName).memberScope
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-18
@@ -48,13 +48,12 @@ import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
|||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtensionProperty
|
|
||||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||||
import org.jetbrains.kotlin.resolve.scopes.utils.collectDescriptorsFiltered
|
import org.jetbrains.kotlin.resolve.scopes.utils.collectDescriptorsFiltered
|
||||||
import org.jetbrains.kotlin.resolve.scopes.utils.getImplicitReceiversHierarchy
|
import org.jetbrains.kotlin.resolve.scopes.utils.getImplicitReceiversHierarchy
|
||||||
|
|
||||||
class KDocCompletionContributor() : CompletionContributor() {
|
class KDocCompletionContributor : CompletionContributor() {
|
||||||
init {
|
init {
|
||||||
extend(CompletionType.BASIC, psiElement().inside(KDocName::class.java),
|
extend(CompletionType.BASIC, psiElement().inside(KDocName::class.java),
|
||||||
KDocNameCompletionProvider)
|
KDocNameCompletionProvider)
|
||||||
@@ -109,13 +108,13 @@ class KDocNameCompletionSession(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun collectPackageViewDescriptors(qualifiedLink: List<String>, nameFilter: (Name) -> Boolean): Sequence<PackageViewDescriptor> {
|
private fun collectPackageViewDescriptors(qualifiedLink: List<String>, nameFilter: (Name) -> Boolean): Sequence<PackageViewDescriptor> {
|
||||||
val fqName = if (qualifiedLink.isEmpty()) FqName.ROOT else FqName.fromSegments(qualifiedLink)
|
val fqName = FqName.fromSegments(qualifiedLink)
|
||||||
return moduleDescriptor.getSubPackagesOf(fqName, nameFilter).asSequence()
|
return moduleDescriptor.getSubPackagesOf(fqName, nameFilter).asSequence()
|
||||||
.map { moduleDescriptor.getPackage(it) }
|
.map { moduleDescriptor.getPackage(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun collectDescriptorsFromScope(scope: LexicalScope, nameFilter: (Name) -> Boolean, collectFormParentScopes: Boolean): Sequence<DeclarationDescriptor> {
|
private fun collectDescriptorsFromScope(scope: LexicalScope, nameFilter: (Name) -> Boolean, collectFromParentScopes: Boolean): Sequence<DeclarationDescriptor> {
|
||||||
val implicitReceivers = scope.getImplicitReceiversHierarchy().map { it.value }
|
val implicitReceivers = scope.getImplicitReceiversHierarchy().map { it.value }
|
||||||
|
|
||||||
fun isApplicable(descriptor: DeclarationDescriptor): Boolean {
|
fun isApplicable(descriptor: DeclarationDescriptor): Boolean {
|
||||||
@@ -130,21 +129,19 @@ class KDocNameCompletionSession(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("IfThenToElvis")
|
val sequence = when {
|
||||||
return (
|
collectFromParentScopes -> scope.collectDescriptorsFiltered(nameFilter = nameFilter, changeNamesForAliased = true).asSequence()
|
||||||
if (collectFormParentScopes)
|
|
||||||
scope.collectDescriptorsFiltered(nameFilter = nameFilter).asSequence()
|
scope is LexicalScope.Base -> scope.parent.getContributedDescriptors(nameFilter = nameFilter).asSequence()
|
||||||
else if (scope is LexicalScope.Base)
|
|
||||||
scope.parent.getContributedDescriptors(nameFilter = nameFilter).asSequence()
|
else -> scope.getContributedDescriptors(nameFilter = nameFilter).asSequence() +
|
||||||
else
|
scope.parent.collectDescriptorsFiltered(nameFilter = nameFilter).asSequence().filter { it.isExtension }
|
||||||
(scope.getContributedDescriptors(nameFilter = nameFilter).asSequence()
|
}
|
||||||
+ scope.parent.collectDescriptorsFiltered(nameFilter = nameFilter).asSequence()
|
return sequence.filter(::isApplicable)
|
||||||
.filter { it.isExtension || it.isExtensionProperty })
|
|
||||||
).filter(::isApplicable)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun collectDescriptorsForLinkCompletion(declarationDescriptor: DeclarationDescriptor, kDocLink: KDocLink): Sequence<DeclarationDescriptor> {
|
private fun collectDescriptorsForLinkCompletion(declarationDescriptor: DeclarationDescriptor, kDocLink: KDocLink): Sequence<DeclarationDescriptor> {
|
||||||
val qualifiedLink = kDocLink.getLinkText().split('.').dropLast(1)
|
val qualifiedLink = kDocLink.getLinkText().split('.').dropLast(1)
|
||||||
val nameFilter = descriptorNameFilter.toNameFilter()
|
val nameFilter = descriptorNameFilter.toNameFilter()
|
||||||
return if (qualifiedLink.isNotEmpty()) {
|
return if (qualifiedLink.isNotEmpty()) {
|
||||||
@@ -184,7 +181,7 @@ object KDocTagCompletionProvider : CompletionProvider<CompletionParameters>() {
|
|||||||
StandardPatterns.character().javaIdentifierPart() or singleCharPattern('@'),
|
StandardPatterns.character().javaIdentifierPart() or singleCharPattern('@'),
|
||||||
StandardPatterns.character().javaIdentifierStart() or singleCharPattern('@'))
|
StandardPatterns.character().javaIdentifierStart() or singleCharPattern('@'))
|
||||||
|
|
||||||
if (prefix.length > 0 && !prefix.startsWith('@')) {
|
if (prefix.isNotEmpty() && !prefix.startsWith('@')) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val kdocOwner = parameters.position.getNonStrictParentOfType<KDoc>()?.getOwner()
|
val kdocOwner = parameters.position.getNonStrictParentOfType<KDoc>()?.getOwner()
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import java.sql.Date as SqlDate
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [Sql<caret>]
|
||||||
|
*/
|
||||||
|
fun foo(){}
|
||||||
|
|
||||||
|
// EXIST: { lookupString: "SqlDate", itemText: "SqlDate", tailText: " (java.sql.Date)" }
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import java.sql.Date as SqlDate
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [Sql<caret>]
|
||||||
|
*/
|
||||||
|
fun foo(){}
|
||||||
|
|
||||||
|
// ELEMENT: SqlDate
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import java.sql.Date as SqlDate
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [SqlDate<caret>]
|
||||||
|
*/
|
||||||
|
fun foo(){}
|
||||||
|
|
||||||
|
// ELEMENT: SqlDate
|
||||||
+6
@@ -3040,6 +3040,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("KDoc.kt")
|
||||||
|
public void testKDoc() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/java/importAliases/KDoc.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("PrefixUsed.kt")
|
@TestMetadata("PrefixUsed.kt")
|
||||||
public void testPrefixUsed() throws Exception {
|
public void testPrefixUsed() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/java/importAliases/PrefixUsed.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/java/importAliases/PrefixUsed.kt");
|
||||||
|
|||||||
+6
@@ -488,6 +488,12 @@ public class BasicCompletionHandlerTestGenerated extends AbstractBasicCompletion
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("KDoc.kt")
|
||||||
|
public void testKDoc() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/importAliases/KDoc.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("TopLevelFun.kt")
|
@TestMetadata("TopLevelFun.kt")
|
||||||
public void testTopLevelFun() throws Exception {
|
public void testTopLevelFun() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/importAliases/TopLevelFun.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/importAliases/TopLevelFun.kt");
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import java.sql.Date as SqlDate
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is [<caret>SqlDate]
|
||||||
|
*/
|
||||||
|
class Foo {
|
||||||
|
}
|
||||||
|
|
||||||
|
// REF: (java.sql).Date
|
||||||
@@ -73,6 +73,12 @@ public class KdocResolveTestGenerated extends AbstractReferenceResolveTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("ImportAliasClass.kt")
|
||||||
|
public void testImportAliasClass() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kdoc/resolve/ImportAliasClass.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("ImportedClassReference.kt")
|
@TestMetadata("ImportedClassReference.kt")
|
||||||
public void testImportedClassReference() throws Exception {
|
public void testImportedClassReference() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kdoc/resolve/ImportedClassReference.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/kdoc/resolve/ImportedClassReference.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user