Pull Up: Support super-interfaces
This commit is contained in:
@@ -39,6 +39,29 @@ abstract public class JetClassOrObject : JetTypeParameterListOwnerStub<KotlinCla
|
||||
public fun getDelegationSpecifierList(): JetDelegationSpecifierList? = getStubOrPsiChild(JetStubElementTypes.DELEGATION_SPECIFIER_LIST)
|
||||
open public fun getDelegationSpecifiers(): List<JetDelegationSpecifier> = getDelegationSpecifierList()?.getDelegationSpecifiers().orEmpty()
|
||||
|
||||
public fun addDelegationSpecifier(delegationSpecifier: JetDelegationSpecifier): JetDelegationSpecifier {
|
||||
getDelegationSpecifierList()?.let {
|
||||
return EditCommaSeparatedListHelper.addItem(it, getDelegationSpecifiers(), delegationSpecifier)
|
||||
}
|
||||
|
||||
val psiFactory = JetPsiFactory(this)
|
||||
val specifierListToAdd = psiFactory.createDelegatorToSuperCall("A()").replace(delegationSpecifier).getParent()
|
||||
val colon = addBefore(psiFactory.createColon(), getBody())
|
||||
return (addAfter(specifierListToAdd, colon) as JetDelegationSpecifierList).getDelegationSpecifiers().first()
|
||||
}
|
||||
|
||||
public fun removeDelegationSpecifier(delegationSpecifier: JetDelegationSpecifier) {
|
||||
val specifierList = getDelegationSpecifierList() ?: return
|
||||
assert(delegationSpecifier.getParent() === specifierList)
|
||||
|
||||
if (specifierList.getDelegationSpecifiers().size() > 1) {
|
||||
EditCommaSeparatedListHelper.removeItem<JetElement>(delegationSpecifier)
|
||||
}
|
||||
else {
|
||||
deleteChildRange(findChildByType<PsiElement>(JetTokens.COLON) ?: specifierList, specifierList)
|
||||
}
|
||||
}
|
||||
|
||||
public fun getAnonymousInitializers(): List<JetClassInitializer> = getBody()?.getAnonymousInitializers().orEmpty()
|
||||
|
||||
public fun getNameAsDeclaration(): JetObjectDeclarationName? =
|
||||
|
||||
@@ -378,6 +378,10 @@ public class JetPsiFactory(private val project: Project) {
|
||||
return createClass("class A: $text").getDelegationSpecifiers().first() as JetDelegatorToSuperCall
|
||||
}
|
||||
|
||||
public fun createDelegatorToSuperClass(text: String): JetDelegatorToSuperClass {
|
||||
return createClass("class A: $text").getDelegationSpecifiers().first() as JetDelegatorToSuperClass
|
||||
}
|
||||
|
||||
public fun createConstructorDelegationCall(text: String): JetConstructorDelegationCall {
|
||||
val colonOrEmpty = if (text.isEmpty()) "" else ": "
|
||||
return createClass("class A { constructor()$colonOrEmpty$text {}").getSecondaryConstructors().first().getDelegationCall()
|
||||
|
||||
@@ -17,25 +17,40 @@
|
||||
package org.jetbrains.kotlin.idea.refactoring.memberInfo
|
||||
|
||||
import com.intellij.psi.PsiMember
|
||||
import com.intellij.refactoring.RefactoringBundle
|
||||
import com.intellij.refactoring.classMembers.MemberInfoBase
|
||||
import com.intellij.refactoring.util.classMembers.MemberInfo
|
||||
import org.jetbrains.kotlin.asJava.getRepresentativeLightMethod
|
||||
import org.jetbrains.kotlin.asJava.toLightClass
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
|
||||
public class KotlinMemberInfo(member: JetNamedDeclaration) : MemberInfoBase<JetNamedDeclaration>(member) {
|
||||
public class KotlinMemberInfo(member: JetNamedDeclaration, val isSuperClass: Boolean = false) : MemberInfoBase<JetNamedDeclaration>(member) {
|
||||
init {
|
||||
val memberDescriptor = member.resolveToDescriptor()
|
||||
isStatic = member.getParent() is JetFile
|
||||
displayName = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.render(memberDescriptor)
|
||||
|
||||
val overriddenDescriptors = (memberDescriptor as? CallableMemberDescriptor)?.getOverriddenDescriptors() ?: emptySet()
|
||||
if (overriddenDescriptors.isNotEmpty()) {
|
||||
overrides = overriddenDescriptors.any { it.getModality() != Modality.ABSTRACT }
|
||||
if (member is JetClass && isSuperClass) {
|
||||
if (member.isInterface()) {
|
||||
displayName = RefactoringBundle.message("member.info.implements.0", member.getName())
|
||||
overrides = false
|
||||
}
|
||||
else {
|
||||
displayName = RefactoringBundle.message("member.info.extends.0", member.getName())
|
||||
overrides = true
|
||||
}
|
||||
}
|
||||
else {
|
||||
displayName = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.render(memberDescriptor)
|
||||
|
||||
val overriddenDescriptors = (memberDescriptor as? CallableMemberDescriptor)?.getOverriddenDescriptors() ?: emptySet()
|
||||
if (overriddenDescriptors.isNotEmpty()) {
|
||||
overrides = overriddenDescriptors.any { it.getModality() != Modality.ABSTRACT }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,7 +64,7 @@ public fun KotlinMemberInfo.toJavaMemberInfo(): MemberInfo? {
|
||||
}
|
||||
if (psiMember == null) return null
|
||||
|
||||
val info = MemberInfo(psiMember)
|
||||
val info = MemberInfo(psiMember, isSuperClass, null)
|
||||
info.setToAbstract(isToAbstract())
|
||||
return info
|
||||
}
|
||||
+14
-4
@@ -18,14 +18,14 @@ package org.jetbrains.kotlin.idea.refactoring.memberInfo
|
||||
|
||||
import com.intellij.refactoring.classMembers.AbstractMemberInfoStorage
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.psi.JetClassOrObject
|
||||
import org.jetbrains.kotlin.psi.JetConstructor
|
||||
import org.jetbrains.kotlin.psi.JetNamedDeclaration
|
||||
import org.jetbrains.kotlin.psi.JetObjectDeclaration
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.OverloadUtil
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
@@ -69,11 +69,21 @@ public class KotlinMemberInfoStorage(
|
||||
}
|
||||
|
||||
override fun extractClassMembers(aClass: JetClassOrObject, temp: ArrayList<KotlinMemberInfo>) {
|
||||
val context = aClass.analyze()
|
||||
aClass.getDeclarations()
|
||||
.filter { it is JetNamedDeclaration
|
||||
&& it !is JetConstructor<*>
|
||||
&& !(it is JetObjectDeclaration && it.isCompanion())
|
||||
&& myFilter.includeMember(it) }
|
||||
.mapTo(temp) { KotlinMemberInfo(it as JetNamedDeclaration) }
|
||||
aClass.getDelegationSpecifiers()
|
||||
.filterIsInstance<JetDelegatorToSuperClass>()
|
||||
.map {
|
||||
val type = context[BindingContext.TYPE, it.getTypeReference()]
|
||||
val classDescriptor = type?.getConstructor()?.getDeclarationDescriptor() as? ClassDescriptor
|
||||
classDescriptor?.getSource()?.getPsi() as? JetClass
|
||||
}
|
||||
.filter { it != null && it.isInterface() }
|
||||
.mapTo(temp) { KotlinMemberInfo(it!!, true) }
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -160,7 +160,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
|
||||
new Function1<JetNamedDeclaration, KotlinMemberInfo>() {
|
||||
@Override
|
||||
public KotlinMemberInfo invoke(JetNamedDeclaration declaration) {
|
||||
KotlinMemberInfo memberInfo = new KotlinMemberInfo(declaration);
|
||||
KotlinMemberInfo memberInfo = new KotlinMemberInfo(declaration, false);
|
||||
memberInfo.setChecked(elementsToMove.contains(declaration));
|
||||
return memberInfo;
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ public class KotlinPullUpDialog(
|
||||
override fun isMemberEnabled(memberInfo: KotlinMemberInfo): Boolean {
|
||||
val superClass = getSuperClass() ?: return false
|
||||
if (memberInfo in memberInfoStorage.getDuplicatedMemberInfos(superClass)) return false
|
||||
if (memberInfo.getMember() in memberInfoStorage.getExtending(superClass)) return false
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,11 +36,9 @@ import org.jetbrains.kotlin.idea.core.refactoring.canRefactor
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfoStorage
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.qualifiedNameForRendering
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.types.typeUtil.supertypes
|
||||
import java.util.HashSet
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.refactoring.pullUp
|
||||
|
||||
import com.intellij.lang.Language
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.impl.light.LightField
|
||||
@@ -26,8 +25,9 @@ import com.intellij.refactoring.classMembers.MemberInfoBase
|
||||
import com.intellij.refactoring.memberPullUp.PullUpData
|
||||
import com.intellij.refactoring.memberPullUp.PullUpHelper
|
||||
import com.intellij.refactoring.util.RefactoringUtil
|
||||
import com.intellij.util.containers.MultiMap
|
||||
import org.jetbrains.kotlin.asJava.*
|
||||
import org.jetbrains.kotlin.asJava.getRepresentativeLightMethod
|
||||
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
|
||||
import org.jetbrains.kotlin.asJava.toLightClass
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.JetLanguage
|
||||
@@ -52,9 +52,10 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlin.utils.keysToMap
|
||||
import java.util.*
|
||||
import java.util.ArrayList
|
||||
import java.util.Comparator
|
||||
import java.util.LinkedHashMap
|
||||
import java.util.LinkedHashSet
|
||||
|
||||
class KotlinPullUpHelper(
|
||||
private val javaData: PullUpData,
|
||||
@@ -510,6 +511,35 @@ class KotlinPullUpHelper(
|
||||
|
||||
override fun move(info: MemberInfoBase<PsiMember>, substitutor: PsiSubstitutor) {
|
||||
val member = info.getMember().namedUnwrappedElement as? JetNamedDeclaration ?: return
|
||||
|
||||
if (member is JetClass && info.getOverrides() != null) {
|
||||
val psiFactory = JetPsiFactory(member)
|
||||
|
||||
val classDescriptor = data.memberDescriptors[member] as? ClassDescriptor ?: return
|
||||
|
||||
val currentSpecifier =
|
||||
data.sourceClass.getDelegationSpecifiers()
|
||||
.filterIsInstance<JetDelegatorToSuperClass>()
|
||||
.firstOrNull {
|
||||
val referencedType = data.sourceClassContext[BindingContext.TYPE, it.getTypeReference()]
|
||||
referencedType?.getConstructor()?.getDeclarationDescriptor() == classDescriptor
|
||||
} ?: return
|
||||
|
||||
data.sourceClass.removeDelegationSpecifier(currentSpecifier)
|
||||
|
||||
if (!DescriptorUtils.isSubclass(data.targetClassDescriptor, classDescriptor)) {
|
||||
val referencedType = data.sourceClassContext[BindingContext.TYPE, currentSpecifier.getTypeReference()]!!
|
||||
val typeInTargetClass = data.sourceToTargetClassSubstitutor.substitute(referencedType, Variance.INVARIANT)
|
||||
if (typeInTargetClass != null && !typeInTargetClass.isError) {
|
||||
val renderedType = IdeDescriptorRenderers.SOURCE_CODE.renderType(typeInTargetClass)
|
||||
data.targetClass.addDelegationSpecifier(psiFactory.createDelegatorToSuperClass(renderedType)).addToShorteningWaitSet()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
val markedElements = markElements(member)
|
||||
val memberCopy = member.copy() as JetNamedDeclaration
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
open class A: Z {
|
||||
|
||||
}
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface X
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface Y
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface Z
|
||||
|
||||
class <caret>B: A(), X, Y, Z
|
||||
@@ -0,0 +1,14 @@
|
||||
open class A: Z, X, Y {
|
||||
|
||||
}
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface X
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface Y
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface Z
|
||||
|
||||
class B: A()
|
||||
@@ -0,0 +1,14 @@
|
||||
open class A: Z<String> {
|
||||
|
||||
}
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface X<T, U>
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface Y<V : Any>
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface Z<T>
|
||||
|
||||
class <caret>B<T : Any, U, V>: A(), X<U, V>, Y<T>, Z<U>
|
||||
@@ -0,0 +1,14 @@
|
||||
open class A: Z<String>, X<Any?, Any?>, Y<Any> {
|
||||
|
||||
}
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface X<T, U>
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface Y<V : Any>
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface Z<T>
|
||||
|
||||
class B<T : Any, U, V>: A()
|
||||
@@ -0,0 +1,12 @@
|
||||
// TARGET_CLASS: X
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface X
|
||||
|
||||
// INFO: {"checked": "false"}
|
||||
interface Y
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface Z
|
||||
|
||||
class <caret>B: X, Y, Z
|
||||
@@ -0,0 +1,12 @@
|
||||
// TARGET_CLASS: X
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface X : Z
|
||||
|
||||
// INFO: {"checked": "false"}
|
||||
interface Y
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface Z
|
||||
|
||||
class B: Y
|
||||
@@ -0,0 +1,14 @@
|
||||
open class A: Z {
|
||||
|
||||
}
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface X
|
||||
|
||||
// INFO: {"checked": "false"}
|
||||
interface Y
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface Z
|
||||
|
||||
class <caret>B: A(), X, Y, Z
|
||||
@@ -0,0 +1,14 @@
|
||||
open class A: Z, X {
|
||||
|
||||
}
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface X
|
||||
|
||||
// INFO: {"checked": "false"}
|
||||
interface Y
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface Z
|
||||
|
||||
class B: A(), Y
|
||||
@@ -0,0 +1,14 @@
|
||||
open class A {
|
||||
|
||||
}
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface X
|
||||
|
||||
// INFO: {"checked": "false"}
|
||||
interface Y
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface Z
|
||||
|
||||
class <caret>B: A(), X, Y, Z
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
open class A : X, Z {
|
||||
|
||||
}
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface X
|
||||
|
||||
// INFO: {"checked": "false"}
|
||||
interface Y
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface Z
|
||||
|
||||
class B: A(), Y
|
||||
@@ -113,6 +113,36 @@ public class PullUpTestGenerated extends AbstractPullUpTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("moveAllSuperInterfaces.kt")
|
||||
public void testMoveAllSuperInterfaces() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/moveAllSuperInterfaces.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("moveAllSuperInterfacesWithGenerics.kt")
|
||||
public void testMoveAllSuperInterfacesWithGenerics() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/moveAllSuperInterfacesWithGenerics.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("moveSuperInterfaces.kt")
|
||||
public void testMoveSuperInterfaces() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/moveSuperInterfaces.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("moveSuperInterfacesToEmptySpecifierList.kt")
|
||||
public void testMoveSuperInterfacesToEmptySpecifierList() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/moveSuperInterfacesToEmptySpecifierList.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("moveSuperInterfaceToItSelf.kt")
|
||||
public void testMoveSuperInterfaceToItSelf() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/moveSuperInterfaceToItSelf.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("multipleInitializersInConstructorsEq.kt")
|
||||
public void testMultipleInitializersInConstructorsEq() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/multipleInitializersInConstructorsEq.kt");
|
||||
|
||||
Reference in New Issue
Block a user