Push Down: Initial Support
This commit is contained in:
@@ -84,6 +84,7 @@ import org.jetbrains.kotlin.idea.refactoring.inline.AbstractInlineTest
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.AbstractJetExtractionTest
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.AbstractJetMoveTest
|
||||
import org.jetbrains.kotlin.idea.refactoring.pullUp.AbstractPullUpTest
|
||||
import org.jetbrains.kotlin.idea.refactoring.pushDown.AbstractPushDownTest
|
||||
import org.jetbrains.kotlin.idea.refactoring.rename.AbstractRenameTest
|
||||
import org.jetbrains.kotlin.idea.resolve.*
|
||||
import org.jetbrains.kotlin.idea.structureView.AbstractKotlinFileStructureTest
|
||||
@@ -664,6 +665,10 @@ fun main(args: Array<String>) {
|
||||
model("refactoring/pullUp/j2k", extension = "java", singleClass = true, testClassName = "J2K", testMethod = "doJavaTest")
|
||||
}
|
||||
|
||||
testClass(javaClass<AbstractPushDownTest>()) {
|
||||
model("refactoring/pushDown", extension = "kt", singleClass = true)
|
||||
}
|
||||
|
||||
testClass(javaClass<AbstractSelectExpressionForDebuggerTest>()) {
|
||||
model("debugger/selectExpression", recursive = false)
|
||||
model("debugger/selectExpression/disallowMethodCalls", testMethod = "doTestWoMethodCalls")
|
||||
|
||||
@@ -62,6 +62,7 @@ public fun ClassDescriptor.findCallableMemberBySignature(signature: CallableMemb
|
||||
val descriptorKind = if (signature is FunctionDescriptor) DescriptorKindFilter.FUNCTIONS else DescriptorKindFilter.VARIABLES
|
||||
return getDefaultType().getMemberScope()
|
||||
.getDescriptors(descriptorKind)
|
||||
.filterIsInstance<CallableMemberDescriptor>()
|
||||
.firstOrNull {
|
||||
it.getContainingDeclaration() == this
|
||||
&& OverridingUtil.DEFAULT.isOverridableBy(it as CallableDescriptor, signature).getResult() == OVERRIDABLE
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.search.searches.ClassInheritorsSearch
|
||||
import com.intellij.util.EmptyQuery
|
||||
|
||||
public fun HierarchySearchRequest<PsiElement>.searchInheritors(): Query<PsiClass> {
|
||||
public fun HierarchySearchRequest<*>.searchInheritors(): Query<PsiClass> {
|
||||
val psiClass: PsiClass? = when (originalElement) {
|
||||
is JetClassOrObject -> LightClassUtil.getPsiClass(originalElement)
|
||||
is PsiClass -> originalElement
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.idea.refactoring
|
||||
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.ScrollType
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.refactoring.RefactoringActionHandler
|
||||
import com.intellij.refactoring.RefactoringBundle
|
||||
import com.intellij.refactoring.lang.ElementsHandler
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil
|
||||
import org.jetbrains.kotlin.idea.core.refactoring.canRefactor
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||
import java.util.HashSet
|
||||
|
||||
public abstract class AbstractPullPushMembersHandler(
|
||||
private val refactoringName: String,
|
||||
private val helpId: String,
|
||||
private val wrongPositionMessage: String
|
||||
) : RefactoringActionHandler, ElementsHandler {
|
||||
private fun reportWrongPosition(project: Project, editor: Editor?) {
|
||||
val message = RefactoringBundle.getCannotRefactorMessage(wrongPositionMessage)
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, message, refactoringName, helpId)
|
||||
}
|
||||
|
||||
protected fun reportWrongContext(project: Project, editor: Editor?) {
|
||||
val message = RefactoringBundle.getCannotRefactorMessage(
|
||||
RefactoringBundle.message("is.not.supported.in.the.current.context", refactoringName)
|
||||
)
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, message, refactoringName, helpId)
|
||||
}
|
||||
|
||||
protected abstract fun invoke(project: Project,
|
||||
editor: Editor?,
|
||||
classOrObject: JetClassOrObject?,
|
||||
member: JetNamedDeclaration?,
|
||||
dataContext: DataContext?)
|
||||
|
||||
override fun invoke(project: Project, editor: Editor, file: PsiFile, dataContext: DataContext?) {
|
||||
val offset = editor.caretModel.offset
|
||||
editor.scrollingModel.scrollToCaret(ScrollType.MAKE_VISIBLE)
|
||||
|
||||
val target = (file.findElementAt(offset) ?: return).parentsWithSelf.firstOrNull {
|
||||
it is JetClassOrObject || ((it is JetNamedFunction || it is JetProperty) && it.parent is JetClassBody)
|
||||
}
|
||||
|
||||
if (target == null) {
|
||||
reportWrongPosition(project, editor)
|
||||
return
|
||||
}
|
||||
if (!target.canRefactor()) return
|
||||
|
||||
invoke(project, arrayOf(target), dataContext)
|
||||
}
|
||||
|
||||
override fun invoke(project: Project, elements: Array<out PsiElement>, dataContext: DataContext?) {
|
||||
val element = elements.singleOrNull() ?: return
|
||||
|
||||
val editor = dataContext?.let { CommonDataKeys.EDITOR.getData(it) }
|
||||
|
||||
val (classOrObject, member) = when (element) {
|
||||
is JetNamedFunction, is JetProperty -> element.getStrictParentOfType<JetClassOrObject>() to element as JetNamedDeclaration?
|
||||
is JetClassOrObject -> element to null
|
||||
else -> {
|
||||
reportWrongPosition(project, editor)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
invoke(project, editor, classOrObject, member, dataContext)
|
||||
}
|
||||
|
||||
override fun isEnabledOnElements(elements: Array<out PsiElement>): Boolean {
|
||||
return elements.mapTo(HashSet<PsiElement>()) {
|
||||
when (it) {
|
||||
is JetNamedFunction, is JetProperty ->
|
||||
(it.parent as? JetClassBody)?.parent as? JetClassOrObject
|
||||
is JetClassOrObject -> it
|
||||
else -> null
|
||||
} ?: return false
|
||||
}.size() == 1
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.idea.refactoring.introduce.introduceParameter.Kotlin
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceProperty.KotlinIntroducePropertyHandler
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinIntroduceVariableHandler
|
||||
import org.jetbrains.kotlin.idea.refactoring.pullUp.KotlinPullUpHandler
|
||||
import org.jetbrains.kotlin.idea.refactoring.pushDown.KotlinPushDownHandler
|
||||
import org.jetbrains.kotlin.idea.refactoring.safeDelete.*
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
|
||||
@@ -73,4 +74,6 @@ public class KotlinRefactoringSupportProvider : RefactoringSupportProvider() {
|
||||
override fun getChangeSignatureHandler() = JetChangeSignatureHandler()
|
||||
|
||||
override fun getPullUpHandler() = KotlinPullUpHandler()
|
||||
|
||||
override fun getPushDownHandler() = KotlinPushDownHandler()
|
||||
}
|
||||
|
||||
@@ -16,37 +16,36 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.refactoring.pullUp
|
||||
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.ScrollType
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiNamedElement
|
||||
import com.intellij.refactoring.HelpID
|
||||
import com.intellij.refactoring.RefactoringActionHandler
|
||||
import com.intellij.refactoring.RefactoringBundle
|
||||
import com.intellij.refactoring.lang.ElementsHandler
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.idea.core.refactoring.canRefactor
|
||||
import org.jetbrains.kotlin.idea.refactoring.AbstractPullPushMembersHandler
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfoStorage
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.qualifiedClassNameForRendering
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.JetClass
|
||||
import org.jetbrains.kotlin.psi.JetClassOrObject
|
||||
import org.jetbrains.kotlin.psi.JetNamedDeclaration
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||
import org.jetbrains.kotlin.types.typeUtil.supertypes
|
||||
import java.util.HashSet
|
||||
|
||||
public class KotlinPullUpHandler : RefactoringActionHandler, ElementsHandler {
|
||||
public class KotlinPullUpHandler : AbstractPullPushMembersHandler(
|
||||
refactoringName = PULL_MEMBERS_UP,
|
||||
helpId = HelpID.MEMBERS_PULL_UP,
|
||||
wrongPositionMessage = RefactoringBundle.message("the.caret.should.be.positioned.inside.a.class.to.pull.members.from")
|
||||
) {
|
||||
companion object {
|
||||
val PULLUP_TEST_HELPER_KEY = "PULLUP_TEST_HELPER_KEY"
|
||||
val PULL_UP_TEST_HELPER_KEY = "PULL_UP_TEST_HELPER_KEY"
|
||||
}
|
||||
|
||||
interface TestHelper {
|
||||
@@ -54,20 +53,6 @@ public class KotlinPullUpHandler : RefactoringActionHandler, ElementsHandler {
|
||||
fun chooseSuperClass(superClasses: List<PsiNamedElement>): PsiNamedElement
|
||||
}
|
||||
|
||||
private fun reportWrongPosition(project: Project, editor: Editor?) {
|
||||
val message = RefactoringBundle.getCannotRefactorMessage(
|
||||
RefactoringBundle.message("the.caret.should.be.positioned.inside.a.class.to.pull.members.from")
|
||||
)
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, message, PULL_MEMBERS_UP, "refactoring.pullMembersUp")
|
||||
}
|
||||
|
||||
private fun reportWrongContext(project: Project, editor: Editor?) {
|
||||
val message = RefactoringBundle.getCannotRefactorMessage(
|
||||
RefactoringBundle.message("is.not.supported.in.the.current.context", PULL_MEMBERS_UP)
|
||||
)
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, message, PULL_MEMBERS_UP, HelpID.MEMBERS_PULL_UP)
|
||||
}
|
||||
|
||||
private fun reportNoSuperClasses(project: Project, editor: Editor?, classOrObject: JetClassOrObject) {
|
||||
val message = RefactoringBundle.getCannotRefactorMessage(
|
||||
RefactoringBundle.message("class.does.not.have.base.classes.interfaces.in.current.project",
|
||||
@@ -76,41 +61,7 @@ public class KotlinPullUpHandler : RefactoringActionHandler, ElementsHandler {
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, message, PULL_MEMBERS_UP, HelpID.MEMBERS_PULL_UP)
|
||||
}
|
||||
|
||||
override fun invoke(project: Project, editor: Editor, file: PsiFile, dataContext: DataContext?) {
|
||||
val offset = editor.caretModel.offset
|
||||
editor.scrollingModel.scrollToCaret(ScrollType.MAKE_VISIBLE)
|
||||
|
||||
val target = (file.findElementAt(offset) ?: return).parentsWithSelf.firstOrNull {
|
||||
it is JetClassOrObject || ((it is JetNamedFunction || it is JetProperty) && it.parent is JetClassBody)
|
||||
}
|
||||
|
||||
if (target == null) {
|
||||
reportWrongPosition(project, editor)
|
||||
return
|
||||
}
|
||||
if (!target.canRefactor()) return
|
||||
|
||||
invoke(project, arrayOf(target), dataContext)
|
||||
}
|
||||
|
||||
override fun invoke(project: Project, elements: Array<out PsiElement>, dataContext: DataContext?) {
|
||||
val element = elements.singleOrNull() ?: return
|
||||
|
||||
val editor = dataContext?.let { CommonDataKeys.EDITOR.getData(it) }
|
||||
|
||||
val (classOrObject, member) = when (element) {
|
||||
is JetNamedFunction, is JetProperty -> element.getStrictParentOfType<JetClassOrObject>() to element as JetNamedDeclaration?
|
||||
is JetClassOrObject -> element to null
|
||||
else -> {
|
||||
reportWrongPosition(project, editor)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
invoke(project, editor, classOrObject, member, dataContext)
|
||||
}
|
||||
|
||||
private fun invoke(project: Project,
|
||||
override fun invoke(project: Project,
|
||||
editor: Editor?,
|
||||
classOrObject: JetClassOrObject?,
|
||||
member: JetNamedDeclaration?,
|
||||
@@ -148,7 +99,7 @@ public class KotlinPullUpHandler : RefactoringActionHandler, ElementsHandler {
|
||||
val members = memberInfoStorage.getClassMemberInfos(classOrObject)
|
||||
|
||||
if (ApplicationManager.getApplication().isUnitTestMode) {
|
||||
val helper = dataContext?.getData(PULLUP_TEST_HELPER_KEY) as TestHelper
|
||||
val helper = dataContext?.getData(PULL_UP_TEST_HELPER_KEY) as TestHelper
|
||||
val selectedMembers = helper.adjustMembers(members)
|
||||
val targetClass = helper.chooseSuperClass(superClasses)
|
||||
checkConflicts(project, classOrObject, targetClass, selectedMembers) {
|
||||
@@ -162,17 +113,6 @@ public class KotlinPullUpHandler : RefactoringActionHandler, ElementsHandler {
|
||||
KotlinPullUpDialog(project, classOrObject, superClasses, memberInfoStorage).show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun isEnabledOnElements(elements: Array<out PsiElement>): Boolean {
|
||||
return elements.mapTo(HashSet<PsiElement>()) {
|
||||
when (it) {
|
||||
is JetNamedFunction, is JetProperty ->
|
||||
(it.parent as? JetClassBody)?.parent as? JetClassOrObject
|
||||
is JetClassOrObject -> it
|
||||
else -> null
|
||||
} ?: return false
|
||||
}.size() == 1
|
||||
}
|
||||
}
|
||||
|
||||
val PULL_MEMBERS_UP = "Pull Members Up"
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.refactoring.pullUp
|
||||
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager
|
||||
import com.intellij.psi.impl.light.LightField
|
||||
@@ -31,30 +30,25 @@ import org.jetbrains.kotlin.asJava.getRepresentativeLightMethod
|
||||
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
|
||||
import org.jetbrains.kotlin.asJava.toLightClass
|
||||
import org.jetbrains.kotlin.asJava.unwrapped
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.JetLanguage
|
||||
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
|
||||
import org.jetbrains.kotlin.idea.core.refactoring.createJavaField
|
||||
import org.jetbrains.kotlin.idea.core.refactoring.createJavaMethod
|
||||
import org.jetbrains.kotlin.idea.imports.importableFqName
|
||||
import org.jetbrains.kotlin.idea.refactoring.safeDelete.removeOverrideModifier
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.idea.util.ShortenReferences
|
||||
import org.jetbrains.kotlin.idea.util.psi.patternMatching.JetPsiUnifier
|
||||
import org.jetbrains.kotlin.lexer.JetTokens
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.allChildren
|
||||
import org.jetbrains.kotlin.psi.psiUtil.asAssignment
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getExplicitReceiverValue
|
||||
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 java.util.ArrayList
|
||||
import java.util.Comparator
|
||||
import java.util.LinkedHashMap
|
||||
@@ -64,12 +58,6 @@ class KotlinPullUpHelper(
|
||||
private val javaData: PullUpData,
|
||||
private val data: KotlinPullUpData
|
||||
) : PullUpHelper<MemberInfoBase<PsiMember>> {
|
||||
companion object {
|
||||
private var JetElement.newFqName: FqName? by CopyableUserDataProperty(Key.create("NEW_FQ_NAME"))
|
||||
private var JetElement.replaceWithTargetThis: Boolean? by CopyableUserDataProperty(Key.create("REPLACE_WITH_TARGET_THIS"))
|
||||
private var JetElement.newTypeText: String? by CopyableUserDataProperty(Key.create("NEW_TYPE_TEXT"))
|
||||
}
|
||||
|
||||
private fun JetExpression.isMovable(): Boolean {
|
||||
return accept(
|
||||
object: JetVisitor<Boolean, Nothing?>() {
|
||||
@@ -334,184 +322,13 @@ class KotlinPullUpHelper(
|
||||
return clashingSuperDescriptor.source.getPsi() as? JetCallableDeclaration
|
||||
}
|
||||
|
||||
private fun makeAbstract(sourceMember: JetCallableDeclaration, targetMember: JetCallableDeclaration) {
|
||||
if (!data.isInterfaceTarget) {
|
||||
targetMember.addModifierWithSpace(JetTokens.ABSTRACT_KEYWORD)
|
||||
}
|
||||
|
||||
if (sourceMember.typeReference == null) {
|
||||
var type = (data.memberDescriptors[sourceMember] as CallableMemberDescriptor).returnType
|
||||
if (type == null || type.isError) {
|
||||
type = KotlinBuiltIns.getInstance().nullableAnyType
|
||||
}
|
||||
else {
|
||||
type = data.sourceToTargetClassSubstitutor.substitute(type, Variance.INVARIANT)
|
||||
?: KotlinBuiltIns.getInstance().nullableAnyType
|
||||
}
|
||||
|
||||
if (sourceMember is JetProperty || !type.isUnit()) {
|
||||
val typeRef = JetPsiFactory(sourceMember).createType(IdeDescriptorRenderers.SOURCE_CODE.renderType(type))
|
||||
targetMember.setTypeReference(typeRef)
|
||||
}
|
||||
}
|
||||
|
||||
val deleteFrom = when (sourceMember) {
|
||||
is JetProperty -> {
|
||||
targetMember as JetProperty
|
||||
val accessors = targetMember.accessors
|
||||
targetMember.equalsToken ?: targetMember.delegate ?: accessors.firstOrNull()
|
||||
}
|
||||
|
||||
is JetNamedFunction -> {
|
||||
targetMember as JetNamedFunction
|
||||
targetMember.equalsToken ?: targetMember.bodyExpression
|
||||
}
|
||||
|
||||
else -> null
|
||||
}
|
||||
|
||||
if (deleteFrom != null) {
|
||||
targetMember.deleteChildRange(deleteFrom, targetMember.lastChild)
|
||||
}
|
||||
}
|
||||
|
||||
private fun markElements(member: JetNamedDeclaration): List<JetElement> {
|
||||
val affectedElements = ArrayList<JetElement>()
|
||||
|
||||
member.accept(
|
||||
object: JetVisitorVoid() {
|
||||
private fun visitSuperOrThis(expression: JetInstanceExpressionWithLabel) {
|
||||
val referenceTarget = data.sourceClassContext[BindingContext.REFERENCE_TARGET, expression.instanceReference]
|
||||
if (referenceTarget == data.targetClassDescriptor) {
|
||||
expression.replaceWithTargetThis = true
|
||||
affectedElements.add(expression)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitElement(element: PsiElement) {
|
||||
element.allChildren.forEach { it.accept(this) }
|
||||
}
|
||||
|
||||
override fun visitSimpleNameExpression(expression: JetSimpleNameExpression) {
|
||||
val resolvedCall = expression.getResolvedCall(data.sourceClassContext) ?: return
|
||||
var receiver = resolvedCall.getExplicitReceiverValue()
|
||||
if (!receiver.exists()) {
|
||||
receiver = resolvedCall.extensionReceiver
|
||||
}
|
||||
if (!receiver.exists()) {
|
||||
receiver = resolvedCall.dispatchReceiver
|
||||
}
|
||||
if (!receiver.exists()) return
|
||||
|
||||
val implicitThis = receiver.type.constructor.declarationDescriptor as? ClassDescriptor ?: return
|
||||
if (implicitThis.isCompanionObject
|
||||
&& DescriptorUtils.isAncestor(data.sourceClassDescriptor, implicitThis, true)) {
|
||||
val qualifierFqName = implicitThis.importableFqName ?: return
|
||||
|
||||
expression.newFqName = FqName("${qualifierFqName.asString()}.${expression.getReferencedName()}")
|
||||
affectedElements.add(expression)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitThisExpression(expression: JetThisExpression) {
|
||||
visitSuperOrThis(expression)
|
||||
}
|
||||
|
||||
override fun visitSuperExpression(expression: JetSuperExpression) {
|
||||
visitSuperOrThis(expression)
|
||||
}
|
||||
|
||||
override fun visitTypeReference(typeReference: JetTypeReference) {
|
||||
val oldType = data.sourceClassContext[BindingContext.TYPE, typeReference] ?: return
|
||||
val newType = data.sourceToTargetClassSubstitutor.substitute(oldType, Variance.INVARIANT) ?: return
|
||||
typeReference.newTypeText = IdeDescriptorRenderers.SOURCE_CODE.renderType(newType)
|
||||
affectedElements.add(typeReference)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
return affectedElements
|
||||
}
|
||||
|
||||
private fun processMarkedElements(member: JetNamedDeclaration) {
|
||||
val psiFactory = JetPsiFactory(member)
|
||||
val targetThis = psiFactory.createExpression("this@${data.targetClassDescriptor.name.asString()}")
|
||||
val shorteningOptionsForThis = ShortenReferences.Options(removeThisLabels = true, removeThis = true)
|
||||
|
||||
member.accept(
|
||||
object: JetVisitorVoid() {
|
||||
private fun visitSuperOrThis(expression: JetInstanceExpressionWithLabel) {
|
||||
expression.replaceWithTargetThis?.let {
|
||||
expression.replaceWithTargetThis = null
|
||||
|
||||
val newThisExpression = expression.replace(targetThis) as JetExpression
|
||||
newThisExpression.getQualifiedExpressionForReceiverOrThis().addToShorteningWaitSet(shorteningOptionsForThis)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitElement(element: PsiElement) {
|
||||
for (it in element.allChildren.toList()) {
|
||||
it.accept(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitSimpleNameExpression(expression: JetSimpleNameExpression) {
|
||||
expression.newFqName?.let {
|
||||
expression.newFqName = null
|
||||
|
||||
expression.mainReference.bindToFqName(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitThisExpression(expression: JetThisExpression) {
|
||||
visitSuperOrThis(expression)
|
||||
}
|
||||
|
||||
override fun visitSuperExpression(expression: JetSuperExpression) {
|
||||
visitSuperOrThis(expression)
|
||||
}
|
||||
|
||||
override fun visitTypeReference(typeReference: JetTypeReference) {
|
||||
typeReference.newTypeText?.let {
|
||||
typeReference.newTypeText = null
|
||||
|
||||
(typeReference.replace(psiFactory.createType(it)) as JetElement).addToShorteningWaitSet()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun clearMarking(markedElements: List<JetElement>) {
|
||||
markedElements.forEach {
|
||||
it.newFqName = null
|
||||
it.newTypeText = null
|
||||
it.replaceWithTargetThis = null
|
||||
}
|
||||
}
|
||||
|
||||
private fun moveSuperInterface(member: JetClass, substitutor: PsiSubstitutor) {
|
||||
val classDescriptor = data.memberDescriptors[member] as? ClassDescriptor ?: return
|
||||
val currentSpecifier =
|
||||
data.sourceClass.getDelegationSpecifiers()
|
||||
.filterIsInstance<JetDelegatorToSuperClass>()
|
||||
.firstOrNull {
|
||||
val referencedType = data.sourceClassContext[BindingContext.TYPE, it.typeReference]
|
||||
referencedType?.constructor?.declarationDescriptor == classDescriptor
|
||||
} ?: return
|
||||
val currentSpecifier = data.sourceClass.getDelegatorToSuperClassByDescriptor(classDescriptor, data.sourceClassContext) ?: return
|
||||
when (data.targetClass) {
|
||||
is JetClass -> {
|
||||
data.sourceClass.removeDelegationSpecifier(currentSpecifier)
|
||||
|
||||
if (DescriptorUtils.isSubclass(data.targetClassDescriptor, classDescriptor)) return
|
||||
|
||||
val referencedType = data.sourceClassContext[BindingContext.TYPE, currentSpecifier.typeReference]!!
|
||||
val typeInTargetClass = data.sourceToTargetClassSubstitutor.substitute(referencedType, Variance.INVARIANT)
|
||||
if (!(typeInTargetClass != null && !typeInTargetClass.isError)) return
|
||||
|
||||
val renderedType = IdeDescriptorRenderers.SOURCE_CODE.renderType(typeInTargetClass)
|
||||
val newSpecifier = JetPsiFactory(member).createDelegatorToSuperClass(renderedType)
|
||||
data.targetClass.addDelegationSpecifier(newSpecifier).addToShorteningWaitSet()
|
||||
addDelegatorToSuperClass(currentSpecifier, data.targetClass, data.targetClassDescriptor, data.sourceClassContext, data.sourceToTargetClassSubstitutor)
|
||||
}
|
||||
|
||||
is PsiClass -> {
|
||||
@@ -601,7 +418,7 @@ class KotlinPullUpHelper(
|
||||
return
|
||||
}
|
||||
|
||||
val markedElements = markElements(member)
|
||||
val markedElements = markElements(member, data.sourceClassContext, data.sourceClassDescriptor, data.targetClassDescriptor)
|
||||
val memberCopy = member.copy() as JetNamedDeclaration
|
||||
|
||||
fun moveClassOrObject(member: JetClassOrObject, memberCopy: JetClassOrObject): JetClassOrObject {
|
||||
@@ -629,7 +446,7 @@ class KotlinPullUpHelper(
|
||||
}
|
||||
if (toAbstract) {
|
||||
if (!originalIsAbstract) {
|
||||
makeAbstract(member, memberCopy)
|
||||
makeAbstract(memberCopy, data.memberDescriptors[member] as CallableMemberDescriptor, data.sourceToTargetClassSubstitutor, data.targetClass)
|
||||
}
|
||||
|
||||
movedMember = doAddCallableMember(memberCopy, clashingSuper, data.targetClass)
|
||||
@@ -661,7 +478,7 @@ class KotlinPullUpHelper(
|
||||
else -> return
|
||||
}
|
||||
|
||||
processMarkedElements(movedMember)
|
||||
applyMarking(movedMember, data.sourceToTargetClassSubstitutor, data.targetClassDescriptor)
|
||||
addMovedMember(movedMember)
|
||||
}
|
||||
finally {
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.idea.refactoring.pullUp
|
||||
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
|
||||
import org.jetbrains.kotlin.idea.imports.importableFqName
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.idea.util.ShortenReferences
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.allChildren
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForReceiverOrThis
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getExplicitReceiverValue
|
||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import java.util.*
|
||||
|
||||
private var JetElement.newFqName: FqName? by CopyableUserDataProperty(Key.create("NEW_FQ_NAME"))
|
||||
private var JetElement.replaceWithTargetThis: Boolean? by CopyableUserDataProperty(Key.create("REPLACE_WITH_TARGET_THIS"))
|
||||
private var JetElement.newTypeText: ((TypeSubstitutor) -> String?)? by CopyableUserDataProperty(Key.create("NEW_TYPE_TEXT"))
|
||||
|
||||
fun markElements(
|
||||
declaration: JetNamedDeclaration,
|
||||
context: BindingContext,
|
||||
sourceClassDescriptor: ClassDescriptor, targetClassDescriptor: ClassDescriptor?
|
||||
): List<JetElement> {
|
||||
val affectedElements = ArrayList<JetElement>()
|
||||
|
||||
declaration.accept(
|
||||
object : JetVisitorVoid() {
|
||||
private fun visitSuperOrThis(expression: JetInstanceExpressionWithLabel) {
|
||||
if (targetClassDescriptor == null) return
|
||||
|
||||
val referenceTarget = context[BindingContext.REFERENCE_TARGET, expression.instanceReference]
|
||||
if (referenceTarget == targetClassDescriptor) {
|
||||
expression.replaceWithTargetThis = true
|
||||
affectedElements.add(expression)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitElement(element: PsiElement) {
|
||||
element.allChildren.forEach { it.accept(this) }
|
||||
}
|
||||
|
||||
override fun visitSimpleNameExpression(expression: JetSimpleNameExpression) {
|
||||
val resolvedCall = expression.getResolvedCall(context) ?: return
|
||||
var receiver = resolvedCall.getExplicitReceiverValue()
|
||||
if (!receiver.exists()) {
|
||||
receiver = resolvedCall.extensionReceiver
|
||||
}
|
||||
if (!receiver.exists()) {
|
||||
receiver = resolvedCall.dispatchReceiver
|
||||
}
|
||||
if (!receiver.exists()) return
|
||||
|
||||
val implicitThis = receiver.type.constructor.declarationDescriptor as? ClassDescriptor ?: return
|
||||
if (implicitThis.isCompanionObject
|
||||
&& DescriptorUtils.isAncestor(sourceClassDescriptor, implicitThis, true)) {
|
||||
val qualifierFqName = implicitThis.importableFqName ?: return
|
||||
|
||||
expression.newFqName = FqName("${qualifierFqName.asString()}.${expression.getReferencedName()}")
|
||||
affectedElements.add(expression)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitThisExpression(expression: JetThisExpression) {
|
||||
visitSuperOrThis(expression)
|
||||
}
|
||||
|
||||
override fun visitSuperExpression(expression: JetSuperExpression) {
|
||||
visitSuperOrThis(expression)
|
||||
}
|
||||
|
||||
override fun visitTypeReference(typeReference: JetTypeReference) {
|
||||
val oldType = context[BindingContext.TYPE, typeReference] ?: return
|
||||
typeReference.newTypeText = f@ { substitutor ->
|
||||
substitutor.substitute(oldType, Variance.INVARIANT)?.let { IdeDescriptorRenderers.SOURCE_CODE.renderType(it) }
|
||||
}
|
||||
affectedElements.add(typeReference)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
return affectedElements
|
||||
}
|
||||
|
||||
fun applyMarking(
|
||||
declaration: JetNamedDeclaration,
|
||||
substitutor: TypeSubstitutor, targetClassDescriptor: ClassDescriptor
|
||||
) {
|
||||
val psiFactory = JetPsiFactory(declaration)
|
||||
val targetThis = psiFactory.createExpression("this@${targetClassDescriptor.name.asString()}")
|
||||
val shorteningOptionsForThis = ShortenReferences.Options(removeThisLabels = true, removeThis = true)
|
||||
|
||||
declaration.accept(
|
||||
object : JetVisitorVoid() {
|
||||
private fun visitSuperOrThis(expression: JetInstanceExpressionWithLabel) {
|
||||
expression.replaceWithTargetThis?.let {
|
||||
expression.replaceWithTargetThis = null
|
||||
|
||||
val newThisExpression = expression.replace(targetThis) as JetExpression
|
||||
newThisExpression.getQualifiedExpressionForReceiverOrThis().addToShorteningWaitSet(shorteningOptionsForThis)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitElement(element: PsiElement) {
|
||||
for (it in element.allChildren.toList()) {
|
||||
it.accept(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitSimpleNameExpression(expression: JetSimpleNameExpression) {
|
||||
expression.newFqName?.let {
|
||||
expression.newFqName = null
|
||||
|
||||
expression.mainReference.bindToFqName(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitThisExpression(expression: JetThisExpression) {
|
||||
this.visitSuperOrThis(expression)
|
||||
}
|
||||
|
||||
override fun visitSuperExpression(expression: JetSuperExpression) {
|
||||
this.visitSuperOrThis(expression)
|
||||
}
|
||||
|
||||
override fun visitTypeReference(typeReference: JetTypeReference) {
|
||||
typeReference.newTypeText?.let f@ {
|
||||
typeReference.newTypeText = null
|
||||
|
||||
val newTypeText = it(substitutor) ?: return@f
|
||||
(typeReference.replace(psiFactory.createType(newTypeText)) as JetElement).addToShorteningWaitSet()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun clearMarking(markedElements: List<JetElement>) {
|
||||
markedElements.forEach {
|
||||
it.newFqName = null
|
||||
it.newTypeText = null
|
||||
it.replaceWithTargetThis = null
|
||||
}
|
||||
}
|
||||
@@ -17,10 +17,20 @@
|
||||
package org.jetbrains.kotlin.idea.refactoring.pullUp
|
||||
|
||||
import com.intellij.psi.PsiClass
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
|
||||
import org.jetbrains.kotlin.idea.core.replaced
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.lexer.JetModifierKeywordToken
|
||||
import org.jetbrains.kotlin.lexer.JetTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||
|
||||
fun JetProperty.mustBeAbstractInInterface() =
|
||||
hasInitializer() || hasDelegate() || (!hasInitializer() && !hasDelegate() && accessors.isEmpty())
|
||||
@@ -74,4 +84,76 @@ fun JetClass.makeAbstract() {
|
||||
if (!isInterface()) {
|
||||
addModifierWithSpace(JetTokens.ABSTRACT_KEYWORD)
|
||||
}
|
||||
}
|
||||
|
||||
fun JetClassOrObject.getDelegatorToSuperClassByDescriptor(
|
||||
descriptor: ClassDescriptor,
|
||||
context: BindingContext
|
||||
): JetDelegatorToSuperClass? {
|
||||
return getDelegationSpecifiers()
|
||||
.filterIsInstance<JetDelegatorToSuperClass>()
|
||||
.firstOrNull {
|
||||
val referencedType = context[BindingContext.TYPE, it.typeReference]
|
||||
referencedType?.constructor?.declarationDescriptor == descriptor
|
||||
}
|
||||
}
|
||||
|
||||
fun makeAbstract(member: JetCallableDeclaration,
|
||||
originalMemberDescriptor: CallableMemberDescriptor,
|
||||
substitutor: TypeSubstitutor,
|
||||
targetClass: JetClass) {
|
||||
if (!targetClass.isInterface()) {
|
||||
member.addModifierWithSpace(JetTokens.ABSTRACT_KEYWORD)
|
||||
}
|
||||
|
||||
if (member.typeReference == null) {
|
||||
var type = originalMemberDescriptor.returnType
|
||||
if (type == null || type.isError) {
|
||||
type = KotlinBuiltIns.getInstance().nullableAnyType
|
||||
}
|
||||
else {
|
||||
type = substitutor.substitute(type, Variance.INVARIANT) ?: KotlinBuiltIns.getInstance().nullableAnyType
|
||||
}
|
||||
|
||||
if (member is JetProperty || !type.isUnit()) {
|
||||
val typeRef = JetPsiFactory(targetClass).createType(IdeDescriptorRenderers.SOURCE_CODE.renderType(type))
|
||||
member.setTypeReference(typeRef)
|
||||
}
|
||||
}
|
||||
|
||||
val deleteFrom = when (member) {
|
||||
is JetProperty -> {
|
||||
member.equalsToken ?: member.delegate ?: member.accessors.firstOrNull()
|
||||
}
|
||||
|
||||
is JetNamedFunction -> {
|
||||
member.equalsToken ?: member.bodyExpression
|
||||
}
|
||||
|
||||
else -> null
|
||||
}
|
||||
|
||||
if (deleteFrom != null) {
|
||||
member.deleteChildRange(deleteFrom, member.lastChild)
|
||||
}
|
||||
}
|
||||
|
||||
fun addDelegatorToSuperClass(
|
||||
delegator: JetDelegatorToSuperClass,
|
||||
targetClass: JetClassOrObject,
|
||||
targetClassDescriptor: ClassDescriptor,
|
||||
context: BindingContext,
|
||||
substitutor: TypeSubstitutor
|
||||
) {
|
||||
val referencedType = context[BindingContext.TYPE, delegator.typeReference]!!
|
||||
val referencedClass = referencedType.constructor.declarationDescriptor as? ClassDescriptor ?: return
|
||||
|
||||
if (targetClassDescriptor == referencedClass || DescriptorUtils.isDirectSubclass(targetClassDescriptor, referencedClass)) return
|
||||
|
||||
val typeInTargetClass = substitutor.substitute(referencedType, Variance.INVARIANT)
|
||||
if (!(typeInTargetClass != null && !typeInTargetClass.isError)) return
|
||||
|
||||
val renderedType = IdeDescriptorRenderers.SOURCE_CODE.renderType(typeInTargetClass)
|
||||
val newSpecifier = JetPsiFactory(targetClass).createDelegatorToSuperClass(renderedType)
|
||||
targetClass.addDelegationSpecifier(newSpecifier).addToShorteningWaitSet()
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.idea.refactoring.pushDown
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.refactoring.JavaRefactoringSettings
|
||||
import com.intellij.refactoring.RefactoringBundle
|
||||
import com.intellij.refactoring.classMembers.AbstractMemberInfoModel
|
||||
import com.intellij.refactoring.classMembers.MemberInfoChange
|
||||
import com.intellij.refactoring.ui.RefactoringDialog
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberSelectionPanel
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.qualifiedClassNameForRendering
|
||||
import org.jetbrains.kotlin.psi.JetClass
|
||||
import org.jetbrains.kotlin.psi.JetNamedDeclaration
|
||||
import org.jetbrains.kotlin.psi.JetNamedFunction
|
||||
import org.jetbrains.kotlin.psi.JetProperty
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.GridBagConstraints
|
||||
import java.awt.GridBagLayout
|
||||
import java.awt.Insets
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JLabel
|
||||
import javax.swing.JPanel
|
||||
|
||||
public class KotlinPushDownDialog(
|
||||
project: Project,
|
||||
private val memberInfos: List<KotlinMemberInfo>,
|
||||
private val sourceClass: JetClass
|
||||
) : RefactoringDialog(project, true) {
|
||||
init {
|
||||
title = PUSH_MEMBERS_DOWN
|
||||
init()
|
||||
}
|
||||
|
||||
private var memberInfoModel: AbstractMemberInfoModel<JetNamedDeclaration, KotlinMemberInfo>? = null
|
||||
|
||||
public val selectedMemberInfos: List<KotlinMemberInfo>
|
||||
get() = memberInfos.filter { it.isChecked && memberInfoModel?.isMemberEnabled(it) ?: false }
|
||||
|
||||
override fun getDimensionServiceKey() = "#" + javaClass.name
|
||||
|
||||
override fun createNorthPanel(): JComponent? {
|
||||
val gbConstraints = GridBagConstraints()
|
||||
|
||||
val panel = JPanel(GridBagLayout())
|
||||
|
||||
gbConstraints.insets = Insets(4, 0, 10, 8)
|
||||
gbConstraints.weighty = 1.0
|
||||
gbConstraints.weightx = 1.0
|
||||
gbConstraints.gridy = 0
|
||||
gbConstraints.gridwidth = GridBagConstraints.REMAINDER
|
||||
gbConstraints.fill = GridBagConstraints.BOTH
|
||||
gbConstraints.anchor = GridBagConstraints.WEST
|
||||
panel.add(JLabel(RefactoringBundle.message("push.members.from.0.down.label",
|
||||
sourceClass.qualifiedClassNameForRendering())), gbConstraints)
|
||||
return panel
|
||||
}
|
||||
|
||||
override fun createCenterPanel(): JComponent? {
|
||||
val panel = JPanel(BorderLayout())
|
||||
val memberSelectionPanel = KotlinMemberSelectionPanel(
|
||||
RefactoringBundle.message("members.to.be.pushed.down.panel.title"),
|
||||
memberInfos,
|
||||
RefactoringBundle.message("keep.abstract.column.header"))
|
||||
panel.add(memberSelectionPanel, BorderLayout.CENTER)
|
||||
|
||||
memberInfoModel = object : AbstractMemberInfoModel<JetNamedDeclaration, KotlinMemberInfo>() {
|
||||
override fun isFixedAbstract(member: KotlinMemberInfo?) = null
|
||||
|
||||
override fun isAbstractEnabled(memberInfo: KotlinMemberInfo): Boolean {
|
||||
val member = memberInfo.member
|
||||
return member is JetNamedFunction || member is JetProperty
|
||||
}
|
||||
}
|
||||
memberInfoModel!!.memberInfoChanged(MemberInfoChange(memberInfos))
|
||||
memberSelectionPanel.table.memberInfoModel = memberInfoModel
|
||||
memberSelectionPanel.table.addMemberInfoChangeListener(memberInfoModel)
|
||||
|
||||
return panel
|
||||
}
|
||||
|
||||
override fun doAction() {
|
||||
if (!isOKActionEnabled()) return
|
||||
|
||||
JavaRefactoringSettings.getInstance().PUSH_DOWN_PREVIEW_USAGES = isPreviewUsages
|
||||
|
||||
invokeRefactoring(KotlinPushDownProcessor(getProject(), sourceClass, selectedMemberInfos))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.idea.refactoring.pushDown
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.refactoring.HelpID
|
||||
import com.intellij.refactoring.RefactoringBundle
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil
|
||||
import com.intellij.refactoring.util.RefactoringUIUtil
|
||||
import org.jetbrains.kotlin.idea.refactoring.AbstractPullPushMembersHandler
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfoStorage
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.qualifiedClassNameForRendering
|
||||
import org.jetbrains.kotlin.idea.refactoring.pullUp.PULL_MEMBERS_UP
|
||||
import org.jetbrains.kotlin.psi.JetClass
|
||||
import org.jetbrains.kotlin.psi.JetClassOrObject
|
||||
import org.jetbrains.kotlin.psi.JetNamedDeclaration
|
||||
import org.jetbrains.kotlin.psi.JetObjectDeclaration
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.isInheritable
|
||||
|
||||
val PUSH_MEMBERS_DOWN = "Push Members Down"
|
||||
|
||||
public class KotlinPushDownHandler : AbstractPullPushMembersHandler(
|
||||
refactoringName = PUSH_MEMBERS_DOWN,
|
||||
helpId = HelpID.MEMBERS_PUSH_DOWN,
|
||||
wrongPositionMessage = RefactoringBundle.message("the.caret.should.be.positioned.inside.a.class.to.push.members.from")
|
||||
) {
|
||||
companion object {
|
||||
val PUSH_DOWN_TEST_HELPER_KEY = "PUSH_DOWN_TEST_HELPER_KEY"
|
||||
}
|
||||
|
||||
interface TestHelper {
|
||||
fun adjustMembers(members: List<KotlinMemberInfo>): List<KotlinMemberInfo>
|
||||
}
|
||||
|
||||
private fun reportFinalClassOrObject(project: Project, editor: Editor?, classOrObject: JetClassOrObject) {
|
||||
val message = RefactoringBundle.getCannotRefactorMessage(
|
||||
"${RefactoringUIUtil.getDescription(classOrObject, false)} is final".capitalize()
|
||||
)
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, message, PULL_MEMBERS_UP, HelpID.MEMBERS_PULL_UP)
|
||||
}
|
||||
|
||||
override fun invoke(project: Project,
|
||||
editor: Editor?,
|
||||
classOrObject: JetClassOrObject?,
|
||||
member: JetNamedDeclaration?,
|
||||
dataContext: DataContext?) {
|
||||
if (classOrObject == null) {
|
||||
reportWrongContext(project, editor)
|
||||
return
|
||||
}
|
||||
|
||||
if (!(classOrObject is JetClass && classOrObject.isInheritable())) {
|
||||
reportFinalClassOrObject(project, editor, classOrObject)
|
||||
return
|
||||
}
|
||||
|
||||
val members = KotlinMemberInfoStorage(classOrObject).getClassMemberInfos(classOrObject)
|
||||
if (ApplicationManager.getApplication().isUnitTestMode) {
|
||||
val helper = dataContext?.getData(PUSH_DOWN_TEST_HELPER_KEY) as TestHelper
|
||||
val selectedMembers = helper.adjustMembers(members)
|
||||
KotlinPushDownProcessor(project, classOrObject, selectedMembers).run()
|
||||
}
|
||||
else {
|
||||
val manager = PsiManager.getInstance(project)
|
||||
members.filter { manager.areElementsEquivalent(it.member, member) }.forEach { it.isChecked = true }
|
||||
KotlinPushDownDialog(project, members, classOrObject).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.idea.refactoring.pushDown
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.refactoring.BaseRefactoringProcessor
|
||||
import com.intellij.refactoring.RefactoringBundle
|
||||
import com.intellij.refactoring.listeners.RefactoringEventData
|
||||
import com.intellij.usageView.UsageInfo
|
||||
import com.intellij.usageView.UsageViewBundle
|
||||
import com.intellij.usageView.UsageViewDescriptor
|
||||
import org.jetbrains.kotlin.asJava.unwrapped
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
|
||||
import org.jetbrains.kotlin.idea.refactoring.pullUp.*
|
||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
|
||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.searchInheritors
|
||||
import org.jetbrains.kotlin.lexer.JetTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||
import org.jetbrains.kotlin.types.substitutions.getTypeSubstitutor
|
||||
import org.jetbrains.kotlin.util.findCallableMemberBySignature
|
||||
import org.jetbrains.kotlin.utils.keysToMap
|
||||
import java.util.ArrayList
|
||||
|
||||
public class KotlinPushDownProcessor(
|
||||
project: Project,
|
||||
private val sourceClass: JetClass,
|
||||
private val membersToMove: List<KotlinMemberInfo>
|
||||
) : BaseRefactoringProcessor(project) {
|
||||
private val resolutionFacade = sourceClass.getResolutionFacade()
|
||||
|
||||
private val sourceClassContext = resolutionFacade.analyzeFullyAndGetResult(listOf(sourceClass)).bindingContext
|
||||
|
||||
private val sourceClassDescriptor = sourceClassContext[BindingContext.DECLARATION_TO_DESCRIPTOR, sourceClass] as ClassDescriptor
|
||||
|
||||
private val memberDescriptors = membersToMove
|
||||
.map { it.member }
|
||||
.keysToMap { sourceClassContext[BindingContext.DECLARATION_TO_DESCRIPTOR, it]!! }
|
||||
|
||||
inner class UsageViewDescriptorImpl : UsageViewDescriptor {
|
||||
override fun getProcessedElementsHeader() = RefactoringBundle.message("push.down.members.elements.header")
|
||||
|
||||
override fun getElements() = arrayOf(sourceClass)
|
||||
|
||||
override fun getCodeReferencesText(usagesCount: Int, filesCount: Int) =
|
||||
RefactoringBundle.message("classes.to.push.down.members.to", UsageViewBundle.getReferencesString(usagesCount, filesCount))
|
||||
|
||||
override fun getCommentReferencesText(usagesCount: Int, filesCount: Int) = null
|
||||
}
|
||||
|
||||
class SubclassUsage(element: PsiElement) : UsageInfo(element)
|
||||
|
||||
override fun getCommandName() = PUSH_MEMBERS_DOWN
|
||||
|
||||
override fun createUsageViewDescriptor(usages: Array<out UsageInfo>) = UsageViewDescriptorImpl()
|
||||
|
||||
override fun getBeforeData() = RefactoringEventData().apply {
|
||||
addElement(sourceClass)
|
||||
addElements(membersToMove.map { it.member }.toTypedArray())
|
||||
}
|
||||
|
||||
override fun getAfterData(usages: Array<out UsageInfo>) = RefactoringEventData().apply {
|
||||
addElements(usages.map { it.element as? JetClassOrObject }.filterNotNull())
|
||||
}
|
||||
|
||||
override fun findUsages(): Array<out UsageInfo> {
|
||||
return HierarchySearchRequest(sourceClass, sourceClass.useScope, false)
|
||||
.searchInheritors()
|
||||
.map { it.unwrapped }
|
||||
.filterNotNull()
|
||||
.map { SubclassUsage(it) }
|
||||
.toTypedArray()
|
||||
}
|
||||
|
||||
private fun pushDownToClass(targetClass: JetClassOrObject) {
|
||||
val targetClassDescriptor = resolutionFacade.resolveToDescriptor(targetClass) as ClassDescriptor
|
||||
val substitutor = getTypeSubstitutor(sourceClassDescriptor.defaultType, targetClassDescriptor.defaultType)
|
||||
?: TypeSubstitutor.EMPTY
|
||||
members@ for (memberInfo in membersToMove) {
|
||||
val member = memberInfo.member
|
||||
val memberDescriptor = memberDescriptors[member] ?: continue
|
||||
|
||||
val movedMember = when (member) {
|
||||
is JetProperty, is JetNamedFunction -> {
|
||||
memberDescriptor as CallableMemberDescriptor
|
||||
|
||||
val targetMemberDescriptor = memberDescriptor.substitute(substitutor)?.let {
|
||||
targetClassDescriptor.findCallableMemberBySignature(it as CallableMemberDescriptor)
|
||||
}
|
||||
val targetMember = targetMemberDescriptor?.source?.getPsi() as? JetCallableDeclaration
|
||||
targetMember?.apply {
|
||||
if (memberDescriptor.modality != Modality.ABSTRACT && memberInfo.isToAbstract) {
|
||||
addModifierWithSpace(JetTokens.OVERRIDE_KEYWORD)
|
||||
}
|
||||
else if (memberDescriptor.overriddenDescriptors.isEmpty()) {
|
||||
removeModifier(JetTokens.OVERRIDE_KEYWORD)
|
||||
}
|
||||
else {
|
||||
addModifierWithSpace(JetTokens.OVERRIDE_KEYWORD)
|
||||
}
|
||||
} ?: addMemberToTarget(member, targetClass).apply {
|
||||
if (sourceClassDescriptor.kind == ClassKind.INTERFACE) {
|
||||
if (targetClassDescriptor.kind != ClassKind.INTERFACE && memberDescriptor.modality == Modality.ABSTRACT) {
|
||||
addModifierWithSpace(JetTokens.ABSTRACT_KEYWORD)
|
||||
}
|
||||
}
|
||||
if (memberDescriptor.modality != Modality.ABSTRACT && memberInfo.isToAbstract) {
|
||||
if (hasModifier(JetTokens.PRIVATE_KEYWORD)) {
|
||||
addModifierWithSpace(JetTokens.PROTECTED_KEYWORD)
|
||||
}
|
||||
addModifierWithSpace(JetTokens.OVERRIDE_KEYWORD)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is JetClassOrObject -> {
|
||||
if (memberInfo.overrides != null) {
|
||||
sourceClass.getDelegatorToSuperClassByDescriptor(memberDescriptor as ClassDescriptor, sourceClassContext)?.let {
|
||||
addDelegatorToSuperClass(it, targetClass, targetClassDescriptor, sourceClassContext, substitutor)
|
||||
}
|
||||
continue@members
|
||||
}
|
||||
else {
|
||||
addMemberToTarget(member, targetClass)
|
||||
}
|
||||
}
|
||||
|
||||
else -> continue@members
|
||||
}
|
||||
applyMarking(movedMember, substitutor, targetClassDescriptor)
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeOriginalMembers() {
|
||||
for (memberInfo in membersToMove) {
|
||||
val member = memberInfo.member
|
||||
val memberDescriptor = memberDescriptors[member] ?: continue
|
||||
when (member) {
|
||||
is JetProperty, is JetNamedFunction -> {
|
||||
member as JetCallableDeclaration
|
||||
memberDescriptor as CallableMemberDescriptor
|
||||
|
||||
if (memberDescriptor.modality != Modality.ABSTRACT && memberInfo.isToAbstract) {
|
||||
if (member.hasModifier(JetTokens.PRIVATE_KEYWORD)) {
|
||||
member.addModifierWithSpace(JetTokens.PROTECTED_KEYWORD)
|
||||
}
|
||||
makeAbstract(member, memberDescriptor, TypeSubstitutor.EMPTY, sourceClass)
|
||||
member.typeReference?.addToShorteningWaitSet()
|
||||
}
|
||||
else {
|
||||
member.delete()
|
||||
}
|
||||
}
|
||||
is JetClassOrObject -> {
|
||||
if (memberInfo.overrides != null) {
|
||||
sourceClass.getDelegatorToSuperClassByDescriptor(memberDescriptor as ClassDescriptor, sourceClassContext)?.let {
|
||||
sourceClass.removeDelegationSpecifier(it)
|
||||
}
|
||||
}
|
||||
else {
|
||||
member.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun performRefactoring(usages: Array<out UsageInfo>) {
|
||||
val markedElements = ArrayList<JetElement>()
|
||||
try {
|
||||
membersToMove.forEach { markedElements += markElements(it.member, sourceClassContext, sourceClassDescriptor, null) }
|
||||
usages.forEach { (it.element as? JetClassOrObject)?.let { pushDownToClass(it) } }
|
||||
removeOriginalMembers()
|
||||
}
|
||||
finally {
|
||||
clearMarking(markedElements)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
class <caret>A {
|
||||
// INFO: {"checked": "true"}
|
||||
val x = 1
|
||||
// INFO: {"checked": "true"}
|
||||
fun foo() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
Cannot perform refactoring.
|
||||
Class <b><code>A</code></b> is final
|
||||
@@ -0,0 +1,12 @@
|
||||
class X {
|
||||
open class <caret>A {
|
||||
// INFO: {"checked": "true"}
|
||||
fun foo(): Int = bar() + A.bar() + X.A.bar()
|
||||
|
||||
companion object {
|
||||
fun bar() = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class B : X.A
|
||||
@@ -0,0 +1,13 @@
|
||||
class X {
|
||||
open class A {
|
||||
|
||||
companion object {
|
||||
fun bar() = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class B : X.A {
|
||||
// INFO: {"checked": "true"}
|
||||
fun foo(): Int = X.A.bar() + X.A.bar() + X.A.bar()
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
open class <caret>A {
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
private fun foo() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class B : A {
|
||||
fun foo() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
open class A {
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
protected abstract fun foo()
|
||||
}
|
||||
|
||||
class B : A {
|
||||
override fun foo() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
class A {
|
||||
val x: Int = 1
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
Cannot perform refactoring.
|
||||
Caret should be positioned inside a class to push members from
|
||||
@@ -0,0 +1,8 @@
|
||||
object <caret>O {
|
||||
// INFO: {"checked": "true"}
|
||||
val x = 1
|
||||
// INFO: {"checked": "true"}
|
||||
fun foo() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
Cannot perform refactoring.
|
||||
Object <b><code>O</code></b> is final
|
||||
@@ -0,0 +1,3 @@
|
||||
fun <caret>test() {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
Cannot perform refactoring.
|
||||
Caret should be positioned inside a class to push members from
|
||||
@@ -0,0 +1,34 @@
|
||||
abstract class <caret>A {
|
||||
// INFO: {"checked": "true"}
|
||||
val x = 1
|
||||
// INFO: {"checked": "true"}
|
||||
val y: Int get() = 2
|
||||
// INFO: {"checked": "true"}
|
||||
val z: Int by lazy { 3 }
|
||||
// INFO: {"checked": "true"}
|
||||
abstract val t: Int
|
||||
// INFO: {"checked": "true"}
|
||||
fun foo(n: Int): Boolean = n > 0
|
||||
// INFO: {"checked": "true"}
|
||||
abstract fun bar(s: String)
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
inner class X {
|
||||
|
||||
}
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
class Y {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
abstract class B : A() {
|
||||
|
||||
}
|
||||
|
||||
class C : A() {
|
||||
override val t = 1
|
||||
|
||||
override fun bar(s: String) = s.length()
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
abstract class A {
|
||||
|
||||
}
|
||||
|
||||
abstract class B : A() {
|
||||
// INFO: {"checked": "true"}
|
||||
val x = 1
|
||||
// INFO: {"checked": "true"}
|
||||
val y: Int get() = 2
|
||||
// INFO: {"checked": "true"}
|
||||
val z: Int by lazy { 3 }
|
||||
// INFO: {"checked": "true"}
|
||||
abstract val t: Int
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
fun foo(n: Int): Boolean = n > 0
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
abstract fun bar(s: String)
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
inner class X {
|
||||
|
||||
}
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
class Y {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class C : A() {
|
||||
val t = 1
|
||||
// INFO: {"checked": "true"}
|
||||
val x = 1
|
||||
// INFO: {"checked": "true"}
|
||||
val y: Int get() = 2
|
||||
// INFO: {"checked": "true"}
|
||||
val z: Int by lazy { 3 }
|
||||
|
||||
fun bar(s: String) = s.length()
|
||||
// INFO: {"checked": "true"}
|
||||
fun foo(n: Int): Boolean = n > 0
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
inner class X {
|
||||
|
||||
}
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
class Y {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
abstract class <caret>A {
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
val x = 1
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
val y: Int get() = 2
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
val z: Int by lazy { 3 }
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
abstract val t: Int
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
fun foo(n: Int): Boolean = n > 0
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
abstract fun bar(s: String)
|
||||
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
inner class X {
|
||||
|
||||
}
|
||||
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
class Y {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
abstract class B : A() {
|
||||
|
||||
}
|
||||
|
||||
class C : A() {
|
||||
override val t = 1
|
||||
|
||||
override fun bar(s: String) = s.length()
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
abstract class A {
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
abstract val x: Int
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
abstract val y: Int
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
abstract val z: Int
|
||||
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
abstract fun foo(n: Int): Boolean
|
||||
|
||||
}
|
||||
|
||||
abstract class B : A() {
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
override val x = 1
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
override val y: Int get() = 2
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
override val z: Int by lazy { 3 }
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
abstract val t: Int
|
||||
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
override fun foo(n: Int): Boolean = n > 0
|
||||
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
abstract fun bar(s: String)
|
||||
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
inner class X {
|
||||
|
||||
}
|
||||
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
class Y {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class C : A() {
|
||||
val t = 1
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
override val x = 1
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
override val y: Int get() = 2
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
override val z: Int by lazy { 3 }
|
||||
|
||||
fun bar(s: String) = s.length()
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
override fun foo(n: Int): Boolean = n > 0
|
||||
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
inner class X {
|
||||
|
||||
}
|
||||
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
class Y {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
interface Z<T>
|
||||
|
||||
open class <caret>A<T> {
|
||||
// INFO: {"checked": "true"}
|
||||
val t1: T
|
||||
// INFO: {"checked": "true"}
|
||||
val t2: Z<T>
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
fun foo<S>(t1: T, t2: Z<T>, s1: S, s2: Z<S>): Boolean = true
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
inner class X : Z<T> {
|
||||
|
||||
}
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
class Y<U> : Z<U> {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class B<S> : A<Z<S>>() {
|
||||
|
||||
}
|
||||
|
||||
class C<K> : A<B<K>>() {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
interface Z<T>
|
||||
|
||||
open class A<T> {
|
||||
|
||||
}
|
||||
|
||||
class B<S> : A<Z<S>>() {
|
||||
// INFO: {"checked": "true"}
|
||||
val t1: Z<S>
|
||||
// INFO: {"checked": "true"}
|
||||
val t2: Z<Z<S>>
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
fun foo<S>(t1: Z<S>, t2: Z<Z<S>>, s1: S, s2: Z<S>): Boolean = true
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
inner class X : Z<Z<S>> {
|
||||
|
||||
}
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
class Y<U> : Z<U> {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class C<K> : A<B<K>>() {
|
||||
// INFO: {"checked": "true"}
|
||||
val t1: B<K>
|
||||
// INFO: {"checked": "true"}
|
||||
val t2: Z<B<K>>
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
fun foo<S>(t1: B<K>, t2: Z<B<K>>, s1: S, s2: Z<S>): Boolean = true
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
inner class X : Z<B<K>> {
|
||||
|
||||
}
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
class Y<U> : Z<U> {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
interface <caret>I {
|
||||
// INFO: {"checked": "true"}
|
||||
val x: Int get() = 2
|
||||
// INFO: {"checked": "true"}
|
||||
val y: Int
|
||||
// INFO: {"checked": "true"}
|
||||
fun foo(n: Int): Boolean = n > 0
|
||||
// INFO: {"checked": "true"}
|
||||
fun bar(s: String)
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
class Y {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
abstract class A : I
|
||||
|
||||
class B : I {
|
||||
override val y = 1
|
||||
|
||||
override fun bar(s: String) = s.length()
|
||||
}
|
||||
|
||||
interface J : I
|
||||
|
||||
interface K : I {
|
||||
override val y: Int get() = 1
|
||||
|
||||
override fun bar(s: String) = s.length()
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
interface I {
|
||||
|
||||
}
|
||||
|
||||
abstract class A : I {
|
||||
// INFO: {"checked": "true"}
|
||||
val x: Int get() = 2
|
||||
// INFO: {"checked": "true"}
|
||||
abstract val y: Int
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
fun foo(n: Int): Boolean = n > 0
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
abstract fun bar(s: String)
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
class Y {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class B : I {
|
||||
val y = 1
|
||||
// INFO: {"checked": "true"}
|
||||
val x: Int get() = 2
|
||||
|
||||
fun bar(s: String) = s.length()
|
||||
// INFO: {"checked": "true"}
|
||||
fun foo(n: Int): Boolean = n > 0
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
class Y {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
interface J : I {
|
||||
// INFO: {"checked": "true"}
|
||||
val x: Int get() = 2
|
||||
// INFO: {"checked": "true"}
|
||||
val y: Int
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
fun foo(n: Int): Boolean = n > 0
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
fun bar(s: String)
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
class Y {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
interface K : I {
|
||||
val y: Int get() = 1
|
||||
// INFO: {"checked": "true"}
|
||||
val x: Int get() = 2
|
||||
|
||||
fun bar(s: String) = s.length()
|
||||
// INFO: {"checked": "true"}
|
||||
fun foo(n: Int): Boolean = n > 0
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
class Y {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
interface <caret>I {
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
val x: Int get() = 2
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
val y: Int
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
fun foo(n: Int): Boolean = n > 0
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
fun bar(s: String)
|
||||
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
class Y {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
abstract class A : I
|
||||
|
||||
class B : I {
|
||||
override val y = 1
|
||||
|
||||
override fun bar(s: String) = s.length()
|
||||
}
|
||||
|
||||
interface J : I
|
||||
|
||||
interface K : I {
|
||||
override val y: Int get() = 1
|
||||
|
||||
override fun bar(s: String) = s.length()
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
interface I {
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
val x: Int
|
||||
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
fun foo(n: Int): Boolean
|
||||
|
||||
}
|
||||
|
||||
abstract class A : I {
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
override val x: Int get() = 2
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
abstract val y: Int
|
||||
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
override fun foo(n: Int): Boolean = n > 0
|
||||
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
abstract fun bar(s: String)
|
||||
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
class Y {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class B : I {
|
||||
val y = 1
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
override val x: Int get() = 2
|
||||
|
||||
fun bar(s: String) = s.length()
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
override fun foo(n: Int): Boolean = n > 0
|
||||
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
class Y {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
interface J : I {
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
override val x: Int get() = 2
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
val y: Int
|
||||
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
override fun foo(n: Int): Boolean = n > 0
|
||||
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
fun bar(s: String)
|
||||
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
class Y {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
interface K : I {
|
||||
val y: Int get() = 1
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
override val x: Int get() = 2
|
||||
|
||||
fun bar(s: String) = s.length()
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
override fun foo(n: Int): Boolean = n > 0
|
||||
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
class Y {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// INFO: {"checked": "true"}
|
||||
interface X
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface Y
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface Z
|
||||
|
||||
open class <caret>A : X, Y, Z
|
||||
|
||||
class B : A(), Z
|
||||
@@ -0,0 +1,12 @@
|
||||
// INFO: {"checked": "true"}
|
||||
interface X
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface Y
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface Z
|
||||
|
||||
open class A
|
||||
|
||||
class B : A(), Z, X, Y
|
||||
@@ -0,0 +1,14 @@
|
||||
interface I
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface X<T>
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface Y<T>
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface Z<T>
|
||||
|
||||
open class <caret>A<T, U> : X<T>, Y<X<I>>, Z<Y<U>>
|
||||
|
||||
class B<S> : A<X<S>, Y<S>>(), Z<I>
|
||||
@@ -0,0 +1,14 @@
|
||||
interface I
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface X<T>
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface Y<T>
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
interface Z<T>
|
||||
|
||||
open class A<T, U>
|
||||
|
||||
class B<S> : A<X<S>, Y<S>>(), Z<I>, X<X<S>>, Y<X<I>>
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.idea.refactoring
|
||||
|
||||
import com.google.gson.JsonParser
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.refactoring.BaseRefactoringProcessor
|
||||
import com.intellij.refactoring.classMembers.MemberInfoBase
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil
|
||||
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture
|
||||
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
|
||||
import org.jetbrains.kotlin.idea.test.JetLightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.kotlin.idea.test.JetWithJdkAndRuntimeLightProjectDescriptor
|
||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||
import org.jetbrains.kotlin.psi.NotNullableUserDataProperty
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import org.jetbrains.kotlin.test.JetTestUtils
|
||||
import org.jetbrains.kotlin.test.util.findElementsByCommentPrefix
|
||||
import java.io.File
|
||||
|
||||
public abstract class AbstractMemberPullPushTest : JetLightCodeInsightFixtureTestCase() {
|
||||
private data class ElementInfo(val checked: Boolean, val toAbstract: Boolean)
|
||||
|
||||
companion object {
|
||||
private var PsiElement.elementInfo: ElementInfo
|
||||
by NotNullableUserDataProperty(Key.create("ELEMENT_INFO"), ElementInfo(false, false))
|
||||
}
|
||||
|
||||
override fun getProjectDescriptor() = JetWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
|
||||
|
||||
val fixture: JavaCodeInsightTestFixture get() = myFixture
|
||||
|
||||
protected override fun getTestDataPath() = PluginTestCaseBase.getTestDataPathBase()
|
||||
|
||||
protected fun doTest(path: String, action: (mainFile: PsiFile) -> Unit) {
|
||||
val mainFile = File(path)
|
||||
val afterFile = File("$path.after")
|
||||
val conflictFile = File("$path.messages")
|
||||
|
||||
fixture.testDataPath = "${JetTestUtils.getHomeDirectory()}/${mainFile.getParent()}"
|
||||
|
||||
val mainFileName = mainFile.getName()
|
||||
val mainFileBaseName = FileUtil.getNameWithoutExtension(mainFileName)
|
||||
val extraFiles = mainFile.parentFile.listFiles { file, name ->
|
||||
name != mainFileName && name.startsWith("$mainFileBaseName.") && (name.endsWith(".kt") || name.endsWith(".java"))
|
||||
}
|
||||
val extraFilesToPsi = extraFiles.toMap { fixture.configureByFile(it.getName()) }
|
||||
val file = fixture.configureByFile(mainFileName)
|
||||
|
||||
val addKotlinRuntime = InTextDirectivesUtils.findStringWithPrefixes(file.text, "// WITH_RUNTIME") != null
|
||||
if (addKotlinRuntime) {
|
||||
ConfigLibraryUtil.configureKotlinRuntimeAndSdk(myModule, PluginTestCaseBase.mockJdk())
|
||||
}
|
||||
|
||||
try {
|
||||
for ((element, info) in file.findElementsByCommentPrefix("// INFO: ")) {
|
||||
val parsedInfo = JsonParser().parse(info).asJsonObject
|
||||
element.elementInfo = ElementInfo(parsedInfo["checked"]?.asBoolean ?: false,
|
||||
parsedInfo["toAbstract"]?.asBoolean ?: false)
|
||||
}
|
||||
|
||||
action(file)
|
||||
|
||||
assert(!conflictFile.exists()) { "Conflict file $conflictFile should not exist" }
|
||||
JetTestUtils.assertEqualsToFile(afterFile, file.text!!)
|
||||
for ((extraPsiFile, extraFile) in extraFilesToPsi) {
|
||||
JetTestUtils.assertEqualsToFile(File("${extraFile.getPath()}.after"), extraPsiFile.text)
|
||||
}
|
||||
}
|
||||
catch(e: Exception) {
|
||||
val message = when (e) {
|
||||
is BaseRefactoringProcessor.ConflictsInTestsException -> e.messages.sort().joinToString("\n")
|
||||
is CommonRefactoringUtil.RefactoringErrorHintException -> e.getMessage()!!
|
||||
else -> throw e
|
||||
}
|
||||
JetTestUtils.assertEqualsToFile(conflictFile, message)
|
||||
}
|
||||
finally {
|
||||
if (addKotlinRuntime) {
|
||||
ConfigLibraryUtil.unConfigureKotlinRuntimeAndSdk(myModule, PluginTestCaseBase.mockJdk())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected fun chooseMembers<T : MemberInfoBase<*>>(members: List<T>): List<T> {
|
||||
members.forEach {
|
||||
val info = it.member.elementInfo
|
||||
it.isChecked = info.checked
|
||||
it.isToAbstract = info.toAbstract
|
||||
}
|
||||
return members.filter { it.isChecked }
|
||||
}
|
||||
}
|
||||
@@ -16,116 +16,29 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.refactoring.pullUp
|
||||
|
||||
import com.google.gson.JsonParser
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.refactoring.BaseRefactoringProcessor
|
||||
import com.intellij.refactoring.classMembers.MemberInfoBase
|
||||
import com.intellij.refactoring.memberPullUp.PullUpConflictsUtil
|
||||
import com.intellij.refactoring.memberPullUp.PullUpProcessor
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil
|
||||
import com.intellij.refactoring.util.DocCommentPolicy
|
||||
import com.intellij.refactoring.util.RefactoringHierarchyUtil
|
||||
import com.intellij.refactoring.util.classMembers.MemberInfoStorage
|
||||
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import org.jetbrains.kotlin.idea.core.getPackage
|
||||
import org.jetbrains.kotlin.idea.refactoring.AbstractMemberPullPushTest
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.qualifiedClassNameForRendering
|
||||
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
|
||||
import org.jetbrains.kotlin.idea.test.JetLightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.kotlin.idea.test.JetWithJdkAndRuntimeLightProjectDescriptor
|
||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||
import org.jetbrains.kotlin.psi.NotNullableUserDataProperty
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import org.jetbrains.kotlin.test.JetTestUtils
|
||||
import org.jetbrains.kotlin.test.util.findElementsByCommentPrefix
|
||||
import java.io.File
|
||||
|
||||
public abstract class AbstractPullUpTest : JetLightCodeInsightFixtureTestCase() {
|
||||
private data class ElementInfo(val checked: Boolean, val toAbstract: Boolean)
|
||||
|
||||
companion object {
|
||||
private var PsiElement.elementInfo: ElementInfo
|
||||
by NotNullableUserDataProperty(Key.create("ELEMENT_INFO"), ElementInfo(false, false))
|
||||
}
|
||||
|
||||
override fun getProjectDescriptor() = JetWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
|
||||
|
||||
val fixture: JavaCodeInsightTestFixture get() = myFixture
|
||||
|
||||
protected override fun getTestDataPath() = PluginTestCaseBase.getTestDataPathBase()
|
||||
|
||||
protected fun doTest(path: String, action: (mainFile: PsiFile) -> Unit) {
|
||||
val mainFile = File(path)
|
||||
val afterFile = File("$path.after")
|
||||
val conflictFile = File("$path.messages")
|
||||
|
||||
fixture.testDataPath = "${JetTestUtils.getHomeDirectory()}/${mainFile.getParent()}"
|
||||
|
||||
val mainFileName = mainFile.getName()
|
||||
val mainFileBaseName = FileUtil.getNameWithoutExtension(mainFileName)
|
||||
val extraFiles = mainFile.parentFile.listFiles { file, name ->
|
||||
name != mainFileName && name.startsWith("$mainFileBaseName.") && (name.endsWith(".kt") || name.endsWith(".java"))
|
||||
}
|
||||
val extraFilesToPsi = extraFiles.toMap { fixture.configureByFile(it.getName()) }
|
||||
val file = fixture.configureByFile(mainFileName)
|
||||
|
||||
val addKotlinRuntime = InTextDirectivesUtils.findStringWithPrefixes(file.text, "// WITH_RUNTIME") != null
|
||||
if (addKotlinRuntime) {
|
||||
ConfigLibraryUtil.configureKotlinRuntimeAndSdk(myModule, PluginTestCaseBase.mockJdk())
|
||||
}
|
||||
|
||||
try {
|
||||
for ((element, info) in file.findElementsByCommentPrefix("// INFO: ")) {
|
||||
val parsedInfo = JsonParser().parse(info).asJsonObject
|
||||
element.elementInfo = ElementInfo(parsedInfo["checked"]?.asBoolean ?: false,
|
||||
parsedInfo["toAbstract"]?.asBoolean ?: false)
|
||||
}
|
||||
|
||||
action(file)
|
||||
|
||||
assert(!conflictFile.exists()) { "Conflict file $conflictFile should not exist" }
|
||||
JetTestUtils.assertEqualsToFile(afterFile, file.text!!)
|
||||
for ((extraPsiFile, extraFile) in extraFilesToPsi) {
|
||||
JetTestUtils.assertEqualsToFile(File("${extraFile.getPath()}.after"), extraPsiFile.text)
|
||||
}
|
||||
}
|
||||
catch(e: Exception) {
|
||||
val message = when (e) {
|
||||
is BaseRefactoringProcessor.ConflictsInTestsException -> e.messages.sort().joinToString("\n")
|
||||
is CommonRefactoringUtil.RefactoringErrorHintException -> e.getMessage()!!
|
||||
else -> throw e
|
||||
}
|
||||
JetTestUtils.assertEqualsToFile(conflictFile, message)
|
||||
}
|
||||
finally {
|
||||
if (addKotlinRuntime) {
|
||||
ConfigLibraryUtil.unConfigureKotlinRuntimeAndSdk(myModule, PluginTestCaseBase.mockJdk())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class AbstractPullUpTest : AbstractMemberPullPushTest() {
|
||||
private fun getTargetClassName(file: PsiFile) = InTextDirectivesUtils.findStringWithPrefixes(file.text, "// TARGET_CLASS: ")
|
||||
|
||||
private fun chooseMembers<T : MemberInfoBase<*>>(members: List<T>): List<T> {
|
||||
members.forEach {
|
||||
val info = it.member.elementInfo
|
||||
it.isChecked = info.checked
|
||||
it.isToAbstract = info.toAbstract
|
||||
}
|
||||
return members.filter { it.isChecked }
|
||||
}
|
||||
|
||||
protected fun doKotlinTest(path: String) {
|
||||
doTest(path) { file ->
|
||||
val targetClassName = getTargetClassName(file)
|
||||
val helper = object: KotlinPullUpHandler.TestHelper {
|
||||
override fun adjustMembers(members: List<KotlinMemberInfo>): List<KotlinMemberInfo> {
|
||||
return chooseMembers(members)
|
||||
}
|
||||
override fun adjustMembers(members: List<KotlinMemberInfo>) = chooseMembers(members)
|
||||
|
||||
override fun chooseSuperClass(superClasses: List<PsiNamedElement>): PsiNamedElement {
|
||||
if (targetClassName != null) {
|
||||
@@ -135,7 +48,7 @@ public abstract class AbstractPullUpTest : JetLightCodeInsightFixtureTestCase()
|
||||
}
|
||||
}
|
||||
KotlinPullUpHandler().invoke(getProject(), getEditor(), file) {
|
||||
if (it == KotlinPullUpHandler.PULLUP_TEST_HELPER_KEY) helper else null
|
||||
if (it == KotlinPullUpHandler.PULL_UP_TEST_HELPER_KEY) helper else null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.idea.refactoring.pushDown
|
||||
|
||||
import org.jetbrains.kotlin.idea.refactoring.AbstractMemberPullPushTest
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
|
||||
|
||||
public abstract class AbstractPushDownTest : AbstractMemberPullPushTest() {
|
||||
protected fun doTest(path: String) {
|
||||
doTest(path) { file ->
|
||||
val helper = object: KotlinPushDownHandler.TestHelper {
|
||||
override fun adjustMembers(members: List<KotlinMemberInfo>) = chooseMembers(members)
|
||||
}
|
||||
KotlinPushDownHandler().invoke(getProject(), getEditor(), file) {
|
||||
if (it == KotlinPushDownHandler.PUSH_DOWN_TEST_HELPER_KEY) helper else null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.idea.refactoring.pushDown;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.test.JetTestUtils;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("idea/testData/refactoring/pushDown")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class PushDownTestGenerated extends AbstractPushDownTest {
|
||||
public void testAllFilesPresentInPushDown() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentInSingleGeneratedClass(this.getClass(), new File("idea/testData/refactoring/pushDown"), Pattern.compile("^(.+)\\.kt$"));
|
||||
}
|
||||
|
||||
@TestMetadata("finalClass.kt")
|
||||
public void testFinalClass() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/finalClass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("implicitCompanionUsages.kt")
|
||||
public void testImplicitCompanionUsages() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/implicitCompanionUsages.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("liftPrivate.kt")
|
||||
public void testLiftPrivate() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/liftPrivate.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("noCaret.kt")
|
||||
public void testNoCaret() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/noCaret.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("objectDeclaration.kt")
|
||||
public void testObjectDeclaration() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/objectDeclaration.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("outsideOfClass.kt")
|
||||
public void testOutsideOfClass() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/outsideOfClass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("pushClassMembers.kt")
|
||||
public void testPushClassMembers() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/pushClassMembers.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("pushClassMembersAndMakeAbstract.kt")
|
||||
public void testPushClassMembersAndMakeAbstract() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/pushClassMembersAndMakeAbstract.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("pushClassMembersWithGenerics.kt")
|
||||
public void testPushClassMembersWithGenerics() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/pushClassMembersWithGenerics.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("pushInterfaceMembers.kt")
|
||||
public void testPushInterfaceMembers() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/pushInterfaceMembers.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("pushInterfaceMembersAndMakeAbstract.kt")
|
||||
public void testPushInterfaceMembersAndMakeAbstract() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/pushInterfaceMembersAndMakeAbstract.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("pushSuperInterfaces.kt")
|
||||
public void testPushSuperInterfaces() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/pushSuperInterfaces.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("pushSuperInterfacesWithGenerics.kt")
|
||||
public void testPushSuperInterfacesWithGenerics() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/pushSuperInterfacesWithGenerics.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user