AA: migrate PSI declaration provider (from IJ)
This commit is contained in:
committed by
Ilya Kirillov
parent
3f6b386681
commit
55e24451a4
@@ -5,6 +5,9 @@ plugins {
|
||||
|
||||
dependencies {
|
||||
api(project(":compiler:psi"))
|
||||
api(project(":analysis:analysis-api"))
|
||||
api(project(":analysis:analysis-api-providers"))
|
||||
api(project(":analysis:project-structure"))
|
||||
api(project(":analysis:decompiled:decompiler-to-psi"))
|
||||
api(project(":compiler:light-classes"))
|
||||
implementation(intellijCore())
|
||||
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.analysis.providers
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.*
|
||||
|
||||
public object DecompiledPsiDeclarationProvider {
|
||||
public fun findPsi(ktSymbol: KtSymbol, project: Project): PsiElement? {
|
||||
return when (ktSymbol) {
|
||||
is KtConstructorSymbol -> providePsiForConstructor(ktSymbol, project)
|
||||
is KtFunctionLikeSymbol -> providePsiForFunction(ktSymbol, project)
|
||||
is KtEnumEntrySymbol -> providePsiForEnumEntry(ktSymbol, project)
|
||||
is KtVariableLikeSymbol -> providePsiForProperty(ktSymbol, project)
|
||||
is KtClassLikeSymbol -> providePsiForClass(ktSymbol, project)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun providePsiForConstructor(
|
||||
constructorSymbol: KtConstructorSymbol,
|
||||
project: Project
|
||||
): PsiElement? {
|
||||
val classId = constructorSymbol.containingClassIdIfNonLocal ?: return null
|
||||
val psiClass = project.createPsiDeclarationProvider(constructorSymbol.scope(project))
|
||||
?.getClassesByClassId(classId)
|
||||
?.firstOrNull() ?: return null
|
||||
return psiClass.constructors.firstOrNull()
|
||||
}
|
||||
|
||||
private fun providePsiForFunction(
|
||||
functionLikeSymbol: KtFunctionLikeSymbol,
|
||||
project: Project
|
||||
): PsiElement? {
|
||||
return functionLikeSymbol.callableIdIfNonLocal?.let {
|
||||
project.createPsiDeclarationProvider(functionLikeSymbol.scope(project))
|
||||
?.getFunctions(it)
|
||||
?.firstOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
private fun providePsiForProperty(
|
||||
variableLikeSymbol: KtVariableLikeSymbol,
|
||||
project: Project
|
||||
): PsiElement? {
|
||||
return variableLikeSymbol.callableIdIfNonLocal?.let {
|
||||
project.createPsiDeclarationProvider(variableLikeSymbol.scope(project))
|
||||
?.getProperties(it)
|
||||
// TODO: needs to pick field/getter/setter accordingly?
|
||||
?.firstOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
private fun providePsiForClass(
|
||||
classLikeSymbol: KtClassLikeSymbol,
|
||||
project: Project
|
||||
): PsiElement? {
|
||||
return classLikeSymbol.classIdIfNonLocal?.let {
|
||||
project.createPsiDeclarationProvider(classLikeSymbol.scope(project))
|
||||
?.getClassesByClassId(it)
|
||||
?.firstOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
private fun providePsiForEnumEntry(
|
||||
enumEntrySymbol: KtEnumEntrySymbol,
|
||||
project: Project
|
||||
): PsiElement? {
|
||||
val classId = enumEntrySymbol.containingEnumClassIdIfNonLocal ?: return null
|
||||
val psiClass = project.createPsiDeclarationProvider(enumEntrySymbol.scope(project))
|
||||
?.getClassesByClassId(classId)
|
||||
?.firstOrNull() ?: return null
|
||||
return psiClass.fields.find {
|
||||
it.name == enumEntrySymbol.name.asString()
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtSymbol.scope(project: Project): GlobalSearchScope {
|
||||
// TODO: finding containing module and use a narrower scope?
|
||||
return GlobalSearchScope.allScope(project)
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.analysis.providers
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiMember
|
||||
import com.intellij.psi.PsiMethod
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.name.CallableId
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
|
||||
/**
|
||||
* A [PsiMember] declaration provider for a given scope. Can be created via [KotlinPsiDeclarationProviderFactory].
|
||||
*/
|
||||
public abstract class KotlinPsiDeclarationProvider {
|
||||
/**
|
||||
* Gets a collection of [PsiClass] by [ClassId]
|
||||
*
|
||||
* In standalone mode, this is simply [PsiClassStub]-based [PsiClass]
|
||||
*/
|
||||
public abstract fun getClassesByClassId(classId: ClassId): Collection<PsiClass>
|
||||
|
||||
public abstract fun getProperties(callableId: CallableId): Collection<PsiMember>
|
||||
public abstract fun getFunctions(callableId: CallableId): Collection<PsiMethod>
|
||||
}
|
||||
|
||||
public abstract class KotlinPsiDeclarationProviderFactory {
|
||||
public abstract fun createPsiDeclarationProvider(searchScope: GlobalSearchScope): KotlinPsiDeclarationProvider
|
||||
}
|
||||
|
||||
public fun Project.createPsiDeclarationProvider(searchScope: GlobalSearchScope): KotlinPsiDeclarationProvider? =
|
||||
// TODO: avoid using fail-safe service loading once the factory has an easy-to-register ctor.
|
||||
getServiceIfCreated(KotlinPsiDeclarationProviderFactory::class.java)
|
||||
?.createPsiDeclarationProvider(searchScope)
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.analysis.providers.impl
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.impl.jar.CoreJarFileSystem
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.impl.compiled.ClsClassImpl
|
||||
import com.intellij.psi.impl.compiled.ClsFileImpl
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.analysis.decompiled.light.classes.ClsJavaStubByVirtualFileCache
|
||||
import org.jetbrains.kotlin.analysis.project.structure.KtBinaryModule
|
||||
import org.jetbrains.kotlin.analysis.providers.KotlinPsiDeclarationProvider
|
||||
import org.jetbrains.kotlin.analysis.providers.KotlinPsiDeclarationProviderFactory
|
||||
import org.jetbrains.kotlin.asJava.builder.ClsWrapperStubPsiFactory
|
||||
import org.jetbrains.kotlin.name.CallableId
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeSmart
|
||||
|
||||
private class KotlinStaticPsiDeclarationFromBinaryModuleProvider(
|
||||
private val project: Project,
|
||||
override val scope: GlobalSearchScope,
|
||||
private val binaryModules: Collection<KtBinaryModule>,
|
||||
override val jarFileSystem: CoreJarFileSystem,
|
||||
) : KotlinPsiDeclarationProvider(), AbstractDeclarationFromBinaryModuleProvider {
|
||||
private val psiManager by lazy { PsiManager.getInstance(project) }
|
||||
|
||||
private fun clsClassImplsByFqName(
|
||||
fqName: FqName,
|
||||
isPackageName: Boolean = true,
|
||||
): Collection<ClsClassImpl> {
|
||||
return binaryModules
|
||||
.flatMap {
|
||||
virtualFilesFromModule(it, fqName, isPackageName)
|
||||
}
|
||||
.mapNotNull {
|
||||
createClsJavaClassFromVirtualFile(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createClsJavaClassFromVirtualFile(
|
||||
classFile: VirtualFile,
|
||||
): ClsClassImpl? {
|
||||
val javaFileStub = ClsJavaStubByVirtualFileCache.getInstance(project).get(classFile) ?: return null
|
||||
javaFileStub.psiFactory = ClsWrapperStubPsiFactory.INSTANCE
|
||||
val fakeFile = object : ClsFileImpl(ClassFileViewProvider(psiManager, classFile)) {
|
||||
override fun getStub() = javaFileStub
|
||||
|
||||
override fun isPhysical() = false
|
||||
}
|
||||
javaFileStub.psi = fakeFile
|
||||
return fakeFile.classes.single() as ClsClassImpl
|
||||
}
|
||||
|
||||
override fun getClassesByClassId(classId: ClassId): Collection<ClsClassImpl> {
|
||||
return clsClassImplsByFqName(classId.asSingleFqName(), isPackageName = false)
|
||||
}
|
||||
|
||||
override fun getProperties(callableId: CallableId): Collection<PsiMember> {
|
||||
val classes = callableId.classId?.let { classId ->
|
||||
getClassesByClassId(classId)
|
||||
} ?: clsClassImplsByFqName(callableId.packageName)
|
||||
return classes.flatMap { psiClass ->
|
||||
psiClass.children
|
||||
.filterIsInstance<PsiMember>()
|
||||
.filter { psiMember ->
|
||||
if (psiMember !is PsiMethod && psiMember !is PsiField) return@filter false
|
||||
val name = psiMember.name ?: return@filter false
|
||||
// PsiField a.k.a. backing field
|
||||
name == callableId.callableName.identifier ||
|
||||
// PsiMethod, i.e., accessors
|
||||
(name.startsWith("get") || name.startsWith("set")) &&
|
||||
// E.g., getFooBar -> FooBar -> fooBar
|
||||
(name.substring(3).decapitalizeSmart().endsWith(callableId.callableName.identifier))
|
||||
|
||||
}
|
||||
}.toList()
|
||||
}
|
||||
|
||||
override fun getFunctions(callableId: CallableId): Collection<PsiMethod> {
|
||||
val classes = callableId.classId?.let { classId ->
|
||||
getClassesByClassId(classId)
|
||||
} ?: clsClassImplsByFqName(callableId.packageName)
|
||||
return classes.flatMap { psiClass ->
|
||||
psiClass.methods.filter { psiMethod ->
|
||||
psiMethod.name == callableId.callableName.identifier
|
||||
}
|
||||
}.toList()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: we can't register this in IDE yet due to non-trivial parameters: lib modules and jar file system.
|
||||
// We need a session or facade that maintains such information
|
||||
public class KotlinStaticPsiDeclarationProviderFactory(
|
||||
private val project: Project,
|
||||
private val binaryModules: Collection<KtBinaryModule>,
|
||||
private val jarFileSystem: CoreJarFileSystem,
|
||||
) : KotlinPsiDeclarationProviderFactory() {
|
||||
override fun createPsiDeclarationProvider(searchScope: GlobalSearchScope): KotlinPsiDeclarationProvider {
|
||||
return KotlinStaticPsiDeclarationFromBinaryModuleProvider(project, searchScope, binaryModules, jarFileSystem)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user