Implement light classes for Kotlin scripts
This commit is contained in:
+8
-4
@@ -22,12 +22,10 @@ import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.asJava.builder.*
|
||||
import org.jetbrains.kotlin.asJava.classes.KtLightClass
|
||||
import org.jetbrains.kotlin.asJava.classes.KtLightClassForScript
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
|
||||
typealias LightClassBuilder = (LightClassConstructionContext) -> LightClassBuilderResult
|
||||
@@ -38,6 +36,8 @@ abstract class LightClassGenerationSupport {
|
||||
|
||||
abstract fun createDataHolderForFacade(files: Collection<KtFile>, builder: LightClassBuilder): LightClassDataHolder.ForFacade
|
||||
|
||||
abstract fun createDataHolderForScript(script: KtScript, builder: LightClassBuilder): LightClassDataHolder.ForScript
|
||||
|
||||
abstract fun findClassOrObjectDeclarations(fqName: FqName, searchScope: GlobalSearchScope): Collection<KtClassOrObject>
|
||||
|
||||
/*
|
||||
@@ -61,6 +61,8 @@ abstract class LightClassGenerationSupport {
|
||||
|
||||
abstract fun getLightClass(classOrObject: KtClassOrObject): KtLightClass?
|
||||
|
||||
abstract fun getLightClassForScript(script: KtScript): KtLightClassForScript?
|
||||
|
||||
abstract fun resolveToDescriptor(declaration: KtDeclaration): DeclarationDescriptor?
|
||||
|
||||
abstract fun analyze(element: KtElement): BindingContext
|
||||
@@ -69,6 +71,8 @@ abstract class LightClassGenerationSupport {
|
||||
|
||||
abstract fun getFacadeClasses(facadeFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass>
|
||||
|
||||
abstract fun getScriptClasses(scriptFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass>
|
||||
|
||||
abstract fun getKotlinInternalClasses(fqName: FqName, scope: GlobalSearchScope): Collection<PsiClass>
|
||||
|
||||
abstract fun getFacadeClassesInPackage(packageFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass>
|
||||
|
||||
+5
-1
@@ -53,6 +53,10 @@ interface LightClassDataHolder {
|
||||
interface ForFacade : LightClassDataHolder {
|
||||
fun findDataForFacade(classFqName: FqName): LightClassData = findData { it.findDelegate(classFqName) }
|
||||
}
|
||||
|
||||
interface ForScript : ForClass {
|
||||
fun findDataForScript(scriptFqName: FqName): LightClassData = findData { it.findDelegate(scriptFqName) }
|
||||
}
|
||||
}
|
||||
|
||||
interface LightClassData {
|
||||
@@ -85,7 +89,7 @@ object InvalidLightClassDataHolder : LightClassDataHolder.ForClass {
|
||||
class LightClassDataHolderImpl(
|
||||
override val javaFileStub: PsiJavaFileStub,
|
||||
override val extraDiagnostics: Diagnostics
|
||||
) : LightClassDataHolder.ForClass, LightClassDataHolder.ForFacade {
|
||||
) : LightClassDataHolder.ForClass, LightClassDataHolder.ForFacade, LightClassDataHolder.ForScript {
|
||||
override fun findData(findDelegate: (PsiJavaFileStub) -> PsiClass) = findDelegate(javaFileStub).let(::LightClassDataImpl)
|
||||
}
|
||||
|
||||
|
||||
+39
@@ -128,6 +128,31 @@ sealed class LightClassDataProviderForFileFacade constructor(
|
||||
}
|
||||
|
||||
|
||||
class LightClassDataProviderForScript(private val script: KtScript) : CachedValueProvider<LightClassDataHolder.ForScript> {
|
||||
private fun computeLightClassData(): LightClassDataHolder.ForScript {
|
||||
return LightClassGenerationSupport.getInstance(script.project).createDataHolderForScript(script) {
|
||||
constructionContext ->
|
||||
buildLightClass(
|
||||
script.fqName.parent(),
|
||||
listOf(script.containingKtFile),
|
||||
ClassFilterForScript(script),
|
||||
constructionContext
|
||||
) generate@ {
|
||||
state, files ->
|
||||
val scriptFile = files.first()
|
||||
val codegen = state.factory.forPackage(scriptFile.packageFqName, files)
|
||||
codegen.generate(CompilationErrorHandler.THROW_EXCEPTION)
|
||||
state.factory.done()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun compute(): CachedValueProvider.Result<LightClassDataHolder.ForScript>? =
|
||||
CachedValueProvider.Result.create(computeLightClassData(), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT)
|
||||
|
||||
override fun toString(): String = this::class.java.name + " for ${script.fqName}"
|
||||
}
|
||||
|
||||
interface StubComputationTracker {
|
||||
fun onStubComputed(javaFileStub: PsiJavaFileStub, context: LightClassConstructionContext)
|
||||
}
|
||||
@@ -181,3 +206,17 @@ object ClassFilterForFacade : GenerationState.GenerateClassFilter() {
|
||||
override fun shouldGeneratePackagePart(ktFile: KtFile) = true
|
||||
override fun shouldGenerateScript(script: KtScript) = false
|
||||
}
|
||||
|
||||
private class ClassFilterForScript(val script: KtScript) : GenerationState.GenerateClassFilter() {
|
||||
override fun shouldAnnotateClass(processingClassOrObject: KtClassOrObject): Boolean =
|
||||
shouldGenerateClass(processingClassOrObject)
|
||||
|
||||
override fun shouldGenerateClass(processingClassOrObject: KtClassOrObject): Boolean =
|
||||
processingClassOrObject.isAncestor(script, true)
|
||||
|
||||
override fun shouldGenerateClassMembers(processingClassOrObject: KtClassOrObject): Boolean = true
|
||||
|
||||
override fun shouldGeneratePackagePart(ktFile: KtFile): Boolean = script.containingKtFile === ktFile
|
||||
|
||||
override fun shouldGenerateScript(script: KtScript): Boolean = this.script === script
|
||||
}
|
||||
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright 2010-2017 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.asJava.classes
|
||||
|
||||
import com.intellij.openapi.util.Comparing
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.impl.light.LightEmptyImplementsList
|
||||
import com.intellij.psi.impl.light.LightModifierList
|
||||
import com.intellij.psi.util.CachedValue
|
||||
import com.intellij.psi.util.CachedValuesManager
|
||||
import org.jetbrains.annotations.NonNls
|
||||
import org.jetbrains.kotlin.asJava.builder.LightClassDataHolder
|
||||
import org.jetbrains.kotlin.asJava.builder.LightClassDataProviderForScript
|
||||
import org.jetbrains.kotlin.asJava.elements.FakeFileForLightClass
|
||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||
import org.jetbrains.kotlin.load.java.structure.LightClassOriginKind
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtScript
|
||||
import javax.swing.Icon
|
||||
|
||||
class KtLightClassForScript private constructor(
|
||||
val script: KtScript,
|
||||
private val lightClassDataCache: CachedValue<LightClassDataHolder.ForScript>
|
||||
) : KtLazyLightClass(script.manager) {
|
||||
|
||||
private val hashCode: Int = computeHashCode()
|
||||
|
||||
private val packageFqName: FqName = script.fqName.parent()
|
||||
|
||||
private val modifierList: PsiModifierList = LightModifierList(manager, KotlinLanguage.INSTANCE, PsiModifier.PUBLIC)
|
||||
|
||||
private val implementsList: LightEmptyImplementsList = LightEmptyImplementsList(manager)
|
||||
|
||||
private val _containingFile by lazyPub {
|
||||
FakeFileForLightClass(
|
||||
script.containingKtFile,
|
||||
lightClass = { this },
|
||||
stub = { lightClassDataCache.value.javaFileStub },
|
||||
packageFqName = packageFqName)
|
||||
}
|
||||
|
||||
override val kotlinOrigin: KtClassOrObject? get() = null
|
||||
|
||||
val fqName: FqName = script.fqName
|
||||
|
||||
override fun getModifierList() = modifierList
|
||||
|
||||
override fun hasModifierProperty(@NonNls name: String) = modifierList.hasModifierProperty(name)
|
||||
|
||||
override fun isDeprecated() = false
|
||||
|
||||
override fun isInterface() = false
|
||||
|
||||
override fun isAnnotationType() = false
|
||||
|
||||
override fun isEnum() = false
|
||||
|
||||
override fun getContainingClass() = null
|
||||
|
||||
override fun getContainingFile() = _containingFile
|
||||
|
||||
override fun hasTypeParameters() = false
|
||||
|
||||
override fun getTypeParameters(): Array<PsiTypeParameter> = PsiTypeParameter.EMPTY_ARRAY
|
||||
|
||||
override fun getTypeParameterList() = null
|
||||
|
||||
override fun getDocComment() = null
|
||||
|
||||
override fun getImplementsList() = implementsList
|
||||
|
||||
override fun getImplementsListTypes(): Array<PsiClassType> = PsiClassType.EMPTY_ARRAY
|
||||
|
||||
override fun getInterfaces(): Array<PsiClass> = PsiClass.EMPTY_ARRAY
|
||||
|
||||
override fun getOwnInnerClasses(): List<PsiClass> {
|
||||
return script.declarations.filterIsInstance<KtClassOrObject>()
|
||||
// workaround for ClassInnerStuffCache not supporting classes with null names, see KT-13927
|
||||
// inner classes with null names can't be searched for and can't be used from java anyway
|
||||
// we can't prohibit creating light classes with null names either since they can contain members
|
||||
.filter { it.name != null }
|
||||
.mapNotNull { KtLightClassForSourceDeclaration.create(it) }
|
||||
}
|
||||
|
||||
override fun getInitializers(): Array<PsiClassInitializer> = PsiClassInitializer.EMPTY_ARRAY
|
||||
|
||||
override fun getName() = script.fqName.shortName().asString()
|
||||
|
||||
override fun getQualifiedName() = script.fqName.asString()
|
||||
|
||||
override fun isValid() = script.isValid
|
||||
|
||||
override fun copy() = KtLightClassForScript(script, lightClassDataCache)
|
||||
|
||||
override val lightClassData = lightClassDataCache.value.findDataForScript(script.fqName)
|
||||
|
||||
override fun getNavigationElement() = script
|
||||
|
||||
override fun isEquivalentTo(another: PsiElement?): Boolean =
|
||||
another is PsiClass && Comparing.equal(another.qualifiedName, qualifiedName)
|
||||
|
||||
override fun getElementIcon(flags: Int): Icon? = throw UnsupportedOperationException("This should be done by JetIconProvider")
|
||||
|
||||
override fun hashCode() = hashCode
|
||||
|
||||
private fun computeHashCode(): Int {
|
||||
var result = manager.hashCode()
|
||||
result = 31 * result + script.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other == null || this::class.java != other::class.java) {
|
||||
return false
|
||||
}
|
||||
|
||||
val lightClass = other as KtLightClassForScript
|
||||
if (this === other) return true
|
||||
|
||||
if (this.hashCode != lightClass.hashCode) return false
|
||||
if (manager != lightClass.manager) return false
|
||||
if (script != lightClass.script) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun toString() = "${KtLightClassForScript::class.java.simpleName}:${script.fqName}"
|
||||
|
||||
companion object {
|
||||
|
||||
private val JAVA_API_STUB_FOR_SCRIPT = Key.create<CachedValue<LightClassDataHolder.ForScript>>("JAVA_API_STUB_FOR_SCRIPT")
|
||||
|
||||
fun create(script: KtScript): KtLightClassForScript = KtLightClassForScript(script, getLightClassCachedValue(script))
|
||||
|
||||
fun getLightClassCachedValue(script: KtScript): CachedValue<LightClassDataHolder.ForScript> {
|
||||
return script.getUserData(JAVA_API_STUB_FOR_SCRIPT) ?:
|
||||
createCachedValueForScript(script).also { script.putUserData(JAVA_API_STUB_FOR_SCRIPT, it) }
|
||||
}
|
||||
|
||||
private fun createCachedValueForScript(script: KtScript): CachedValue<LightClassDataHolder.ForScript> =
|
||||
CachedValuesManager.getManager(script.project).createCachedValue(LightClassDataProviderForScript(script), false)
|
||||
}
|
||||
|
||||
override val originKind: LightClassOriginKind get() = LightClassOriginKind.SOURCE
|
||||
}
|
||||
+5
-7
@@ -39,10 +39,7 @@ import org.jetbrains.kotlin.asJava.ImpreciseResolveResult
|
||||
import org.jetbrains.kotlin.asJava.ImpreciseResolveResult.UNSURE
|
||||
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
|
||||
import org.jetbrains.kotlin.asJava.LightClassUtil
|
||||
import org.jetbrains.kotlin.asJava.builder.InvalidLightClassDataHolder
|
||||
import org.jetbrains.kotlin.asJava.builder.LightClassData
|
||||
import org.jetbrains.kotlin.asJava.builder.LightClassDataHolder
|
||||
import org.jetbrains.kotlin.asJava.builder.LightClassDataProviderForClassOrObject
|
||||
import org.jetbrains.kotlin.asJava.builder.*
|
||||
import org.jetbrains.kotlin.asJava.elements.FakeFileForLightClass
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightIdentifier
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightModifierList
|
||||
@@ -334,7 +331,7 @@ abstract class KtLightClassForSourceDeclaration(protected val classOrObject: KtC
|
||||
|
||||
|
||||
fun create(classOrObject: KtClassOrObject): KtLightClassForSourceDeclaration? {
|
||||
if (classOrObject.containingKtFile.isScript() || classOrObject.hasModifier(KtTokens.HEADER_KEYWORD)) {
|
||||
if (classOrObject.hasModifier(KtTokens.HEADER_KEYWORD)) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -366,10 +363,11 @@ abstract class KtLightClassForSourceDeclaration(protected val classOrObject: KtC
|
||||
}
|
||||
|
||||
fun getLightClassDataHolder(classOrObject: KtClassOrObject): LightClassDataHolder.ForClass {
|
||||
return getLightClassCachedValue(classOrObject).value
|
||||
return classOrObject.containingKtFile.script?.let { KtLightClassForScript.getLightClassCachedValue(it).value } ?:
|
||||
getLightClassCachedValue(classOrObject).value
|
||||
}
|
||||
|
||||
fun getLightClassCachedValue(classOrObject: KtClassOrObject): CachedValue<LightClassDataHolder.ForClass> {
|
||||
private fun getLightClassCachedValue(classOrObject: KtClassOrObject): CachedValue<LightClassDataHolder.ForClass> {
|
||||
var value =
|
||||
getOutermostClassOrObject(classOrObject).getUserData(JAVA_API_STUB) // stub computed for outer class can be used for inner/nested
|
||||
?: classOrObject.getUserData(JAVA_API_STUB)
|
||||
|
||||
+1
-5
@@ -33,10 +33,7 @@ import org.jetbrains.kotlin.asJava.LightClassGenerationSupport;
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.name.FqNamesUtilKt;
|
||||
import org.jetbrains.kotlin.psi.KtClass;
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject;
|
||||
import org.jetbrains.kotlin.psi.KtEnumEntry;
|
||||
import org.jetbrains.kotlin.psi.KtFile;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.resolve.jvm.KotlinFinderMarker;
|
||||
|
||||
import java.util.Collection;
|
||||
@@ -90,7 +87,6 @@ public class JavaElementFinder extends PsiElementFinder implements KotlinFinderM
|
||||
FqName qualifiedName = new FqName(qualifiedNameString);
|
||||
|
||||
findClassesAndObjects(qualifiedName, scope, answer);
|
||||
|
||||
answer.addAll(lightClassGenerationSupport.getFacadeClasses(qualifiedName, scope));
|
||||
answer.addAll(lightClassGenerationSupport.getKotlinInternalClasses(qualifiedName, scope));
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.intellij.psi.*
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.asJava.classes.KtLightClass
|
||||
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
|
||||
import org.jetbrains.kotlin.asJava.classes.KtLightClassForScript
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightAnnotationForSourceEntry
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightElement
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightIdentifier
|
||||
@@ -52,6 +53,8 @@ fun KtFile.findFacadeClass(): KtLightClass? {
|
||||
.firstOrNull { it is KtLightClassForFacade && this in it.files } as? KtLightClass
|
||||
}
|
||||
|
||||
fun KtScript.toLightClass(): KtLightClassForScript? = LightClassGenerationSupport.getInstance(project).getLightClassForScript(this)
|
||||
|
||||
fun KtElement.toLightElements(): List<PsiNamedElement> =
|
||||
when (this) {
|
||||
is KtClassOrObject -> listOfNotNull(toLightClass())
|
||||
|
||||
Reference in New Issue
Block a user