diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KotlinSuperTypeListBuilder.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KotlinSuperTypeListBuilder.kt new file mode 100644 index 00000000000..9dd903fd818 --- /dev/null +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KotlinSuperTypeListBuilder.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2010-2019 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.asJava.classes + +import com.intellij.lang.Language +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiJavaCodeReferenceElement +import com.intellij.psi.PsiManager +import com.intellij.psi.PsiReferenceList +import org.jetbrains.kotlin.psi.KtSuperTypeList +import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext + +class KotlinSuperTypeListBuilder(kotlinOrigin: KtSuperTypeList?, manager: PsiManager, language: Language, role: PsiReferenceList.Role) : + KotlinLightReferenceListBuilder( + manager, + language, + role + ) { + + private val myKotlinOrigin: KtSuperTypeList? = kotlinOrigin + + inner class KotlinSuperTypeReference(private val element: PsiJavaCodeReferenceElement) : PsiJavaCodeReferenceElement by element { + + override fun getParent() = this@KotlinSuperTypeListBuilder + + val kotlinOrigin by lazyPub { + element.qualifiedName?.let { this@KotlinSuperTypeListBuilder.myKotlinOrigin?.findEntry(it) } + } + + override fun delete() { + val superTypeList = this@KotlinSuperTypeListBuilder.myKotlinOrigin ?: return + val entry = kotlinOrigin ?: return + superTypeList.removeEntry(entry) + } + } + + private val referenceElementsCache by lazyPub { + super.getReferenceElements().map { KotlinSuperTypeReference(it) }.toTypedArray() + } + + override fun getReferenceElements() = referenceElementsCache + + override fun add(element: PsiElement): PsiElement { + + if (element !is KotlinSuperTypeReference) throw UnsupportedOperationException("Unexpected element: ${element.getElementTextWithContext()}") + + val superTypeList = myKotlinOrigin ?: return element + val entry = element.kotlinOrigin ?: return element + + this.addSuperTypeEntry(superTypeList, entry, element) + + return element + } +} diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/implUtils.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/implUtils.kt index b90f66abe47..5d2f62ad1da 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/implUtils.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/implUtils.kt @@ -1,27 +1,52 @@ /* - * 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. + * Copyright 2010-2019 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.asJava.classes +import com.intellij.psi.PsiJavaCodeReferenceElement +import com.intellij.psi.PsiReferenceList import com.intellij.psi.impl.light.LightElement import com.intellij.util.IncorrectOperationException +import org.jetbrains.kotlin.asJava.LightClassGenerationSupport +import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.KtPsiFactory +import org.jetbrains.kotlin.psi.KtSuperTypeList +import org.jetbrains.kotlin.psi.KtSuperTypeListEntry +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe // NOTE: avoid using blocking lazy in light classes, it leads to deadlocks fun lazyPub(initializer: () -> T) = lazy(LazyThreadSafetyMode.PUBLICATION, initializer) fun LightElement.cannotModify(): Nothing { throw IncorrectOperationException("Modification not implemented.") +} + +fun KtSuperTypeList.findEntry(fqNameToFind: String): KtSuperTypeListEntry? { + val context = LightClassGenerationSupport.getInstance(project).analyzeWithContent(parent as KtClassOrObject) + return entries.firstOrNull { + val referencedType = context[BindingContext.TYPE, it.typeReference] + referencedType?.constructor?.declarationDescriptor?.fqNameUnsafe?.asString() == fqNameToFind + } +} + +fun PsiReferenceList.addSuperTypeEntry( + superTypeList: KtSuperTypeList, + entry: KtSuperTypeListEntry, + reference: PsiJavaCodeReferenceElement +) { + // Only classes may be mentioned in 'extends' list, thus create super call instead simple type reference + val entryToAdd = + if ((reference.parent as? PsiReferenceList)?.role == PsiReferenceList.Role.IMPLEMENTS_LIST && role == PsiReferenceList.Role.EXTENDS_LIST) { + KtPsiFactory(this).createSuperTypeCallEntry("${entry.text}()") + } else entry + // TODO: implement KtSuperListEntry qualification/shortening when inserting reference from another context + if (entry.parent != superTypeList) { + superTypeList.addEntry(entryToAdd) + } else { + // Preserve original entry order + entry.replace(entryToAdd) + } } \ No newline at end of file diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightClass.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightClass.kt index cdd0651e66a..4f68066f8e4 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightClass.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightClass.kt @@ -83,7 +83,8 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor override fun createExtendsList(): PsiReferenceList? = if (tooComplex) super.createExtendsList() - else KotlinLightReferenceListBuilder( + else KotlinSuperTypeListBuilder( + kotlinOrigin.getSuperTypeList(), manager, language, PsiReferenceList.Role.EXTENDS_LIST @@ -109,7 +110,8 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor override fun createImplementsList(): PsiReferenceList? = if (tooComplex) super.createImplementsList() - else KotlinLightReferenceListBuilder( + else KotlinSuperTypeListBuilder( + kotlinOrigin.getSuperTypeList(), manager, language, PsiReferenceList.Role.IMPLEMENTS_LIST diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightPsiReferenceList.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightPsiReferenceList.kt index f33395b3019..9a99c231f78 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightPsiReferenceList.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/KtLightPsiReferenceList.kt @@ -23,31 +23,24 @@ import com.intellij.psi.PsiReferenceList import com.intellij.psi.PsiReferenceList.Role import org.jetbrains.kotlin.asJava.LightClassGenerationSupport import org.jetbrains.kotlin.asJava.classes.KtLightClass +import org.jetbrains.kotlin.asJava.classes.addSuperTypeEntry +import org.jetbrains.kotlin.asJava.classes.findEntry import org.jetbrains.kotlin.asJava.classes.lazyPub -import org.jetbrains.kotlin.psi.KtClassOrObject -import org.jetbrains.kotlin.psi.KtPsiFactory -import org.jetbrains.kotlin.psi.KtSuperTypeList -import org.jetbrains.kotlin.psi.KtSuperTypeListEntry +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe -class KtLightPsiReferenceList ( - override val clsDelegate: PsiReferenceList, - private val owner: KtLightClass +class KtLightPsiReferenceList( + override val clsDelegate: PsiReferenceList, + private val owner: KtLightClass ) : KtLightElement, PsiReferenceList by clsDelegate { inner class KtLightSuperTypeReference( - override val clsDelegate: PsiJavaCodeReferenceElement + override val clsDelegate: PsiJavaCodeReferenceElement ) : KtLightElement, PsiJavaCodeReferenceElement by clsDelegate { override val kotlinOrigin by lazyPub { - val superTypeList = this@KtLightPsiReferenceList.kotlinOrigin ?: return@lazyPub null - val fqNameToFind = clsDelegate.qualifiedName ?: return@lazyPub null - val context = LightClassGenerationSupport.getInstance(project).analyzeWithContent(superTypeList.parent as KtClassOrObject) - superTypeList.entries.firstOrNull { - val referencedType = context[BindingContext.TYPE, it.typeReference] - referencedType?.constructor?.declarationDescriptor?.fqNameUnsafe?.asString() == fqNameToFind - } + clsDelegate.qualifiedName?.let { this@KtLightPsiReferenceList.kotlinOrigin?.findEntry(it) } } override fun getParent() = this@KtLightPsiReferenceList @@ -77,19 +70,9 @@ class KtLightPsiReferenceList ( val superTypeList = kotlinOrigin ?: return element val entry = element.kotlinOrigin ?: return element - // Only classes may be mentioned in 'extends' list, thus create super call instead simple type reference - val entryToAdd = if ((element.parent as? PsiReferenceList)?.role == Role.IMPLEMENTS_LIST && role == Role.EXTENDS_LIST) { - KtPsiFactory(this).createSuperTypeCallEntry("${entry.text}()") - } - else entry - // TODO: implement KtSuperListEntry qualification/shortening when inserting reference from another context - if (entry.parent != superTypeList) { - superTypeList.addEntry(entryToAdd) - } - else { - // Preserve original entry order - entry.replace(entryToAdd) - } + + this.addSuperTypeEntry(superTypeList, entry, element) + return element } } diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java index 756b473d328..05b19a2447a 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java @@ -1395,7 +1395,6 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes } @TestMetadata("kotlinInheritor.before.Main.java") - @Ignore("UltraLight classes does not support modification yet") public void testKotlinInheritor() throws Exception { runTest("idea/testData/quickfix/convertJavaInterfaceToClass/kotlinInheritor.before.Main.java"); }