From a66ef47887a6808a6a52edd042ba0310534f970e Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Tue, 28 Jul 2015 21:28:39 +0300 Subject: [PATCH] Push Down: Initial Support --- .../kotlin/generators/tests/GenerateTests.kt | 5 + .../jetbrains/kotlin/util/descriptorUtils.kt | 1 + .../classInheritorsSearch.kt | 2 +- .../AbstractPullPushMembersHandler.kt | 103 +++++++++ .../KotlinRefactoringSupportProvider.kt | 3 + .../refactoring/pullUp/KotlinPullUpHandler.kt | 84 ++------ .../refactoring/pullUp/KotlinPullUpHelper.kt | 201 +---------------- .../idea/refactoring/pullUp/markingUtils.kt | 167 +++++++++++++++ .../idea/refactoring/pullUp/pullUpUtils.kt | 82 +++++++ .../pushDown/KotlinPushDownDialog.kt | 104 +++++++++ .../pushDown/KotlinPushDownHandler.kt | 89 ++++++++ .../pushDown/KotlinPushDownProcessor.kt | 202 ++++++++++++++++++ .../refactoring/pushDown/finalClass.kt | 8 + .../pushDown/finalClass.kt.messages | 2 + .../pushDown/implicitCompanionUsages.kt | 12 ++ .../pushDown/implicitCompanionUsages.kt.after | 13 ++ .../refactoring/pushDown/liftPrivate.kt | 12 ++ .../refactoring/pushDown/liftPrivate.kt.after | 10 + idea/testData/refactoring/pushDown/noCaret.kt | 4 + .../refactoring/pushDown/noCaret.kt.messages | 2 + .../refactoring/pushDown/objectDeclaration.kt | 8 + .../pushDown/objectDeclaration.kt.messages | 2 + .../refactoring/pushDown/outsideOfClass.kt | 3 + .../pushDown/outsideOfClass.kt.messages | 2 + .../refactoring/pushDown/pushClassMembers.kt | 34 +++ .../pushDown/pushClassMembers.kt.after | 55 +++++ .../pushClassMembersAndMakeAbstract.kt | 34 +++ .../pushClassMembersAndMakeAbstract.kt.after | 64 ++++++ .../pushDown/pushClassMembersWithGenerics.kt | 29 +++ .../pushClassMembersWithGenerics.kt.after | 47 ++++ .../pushDown/pushInterfaceMembers.kt | 31 +++ .../pushDown/pushInterfaceMembers.kt.after | 69 ++++++ .../pushInterfaceMembersAndMakeAbstract.kt | 31 +++ ...shInterfaceMembersAndMakeAbstract.kt.after | 74 +++++++ .../pushDown/pushSuperInterfaces.kt | 12 ++ .../pushDown/pushSuperInterfaces.kt.after | 12 ++ .../pushSuperInterfacesWithGenerics.kt | 14 ++ .../pushSuperInterfacesWithGenerics.kt.after | 14 ++ .../refactoring/AbstractMemberPullPushTest.kt | 110 ++++++++++ .../refactoring/pullUp/AbstractPullUpTest.kt | 95 +------- .../pushDown/AbstractPushDownTest.kt | 33 +++ .../pushDown/PushDownTestGenerated.java | 115 ++++++++++ 42 files changed, 1638 insertions(+), 356 deletions(-) create mode 100644 idea/src/org/jetbrains/kotlin/idea/refactoring/AbstractPullPushMembersHandler.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/markingUtils.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/refactoring/pushDown/KotlinPushDownDialog.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/refactoring/pushDown/KotlinPushDownHandler.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/refactoring/pushDown/KotlinPushDownProcessor.kt create mode 100644 idea/testData/refactoring/pushDown/finalClass.kt create mode 100644 idea/testData/refactoring/pushDown/finalClass.kt.messages create mode 100644 idea/testData/refactoring/pushDown/implicitCompanionUsages.kt create mode 100644 idea/testData/refactoring/pushDown/implicitCompanionUsages.kt.after create mode 100644 idea/testData/refactoring/pushDown/liftPrivate.kt create mode 100644 idea/testData/refactoring/pushDown/liftPrivate.kt.after create mode 100644 idea/testData/refactoring/pushDown/noCaret.kt create mode 100644 idea/testData/refactoring/pushDown/noCaret.kt.messages create mode 100644 idea/testData/refactoring/pushDown/objectDeclaration.kt create mode 100644 idea/testData/refactoring/pushDown/objectDeclaration.kt.messages create mode 100644 idea/testData/refactoring/pushDown/outsideOfClass.kt create mode 100644 idea/testData/refactoring/pushDown/outsideOfClass.kt.messages create mode 100644 idea/testData/refactoring/pushDown/pushClassMembers.kt create mode 100644 idea/testData/refactoring/pushDown/pushClassMembers.kt.after create mode 100644 idea/testData/refactoring/pushDown/pushClassMembersAndMakeAbstract.kt create mode 100644 idea/testData/refactoring/pushDown/pushClassMembersAndMakeAbstract.kt.after create mode 100644 idea/testData/refactoring/pushDown/pushClassMembersWithGenerics.kt create mode 100644 idea/testData/refactoring/pushDown/pushClassMembersWithGenerics.kt.after create mode 100644 idea/testData/refactoring/pushDown/pushInterfaceMembers.kt create mode 100644 idea/testData/refactoring/pushDown/pushInterfaceMembers.kt.after create mode 100644 idea/testData/refactoring/pushDown/pushInterfaceMembersAndMakeAbstract.kt create mode 100644 idea/testData/refactoring/pushDown/pushInterfaceMembersAndMakeAbstract.kt.after create mode 100644 idea/testData/refactoring/pushDown/pushSuperInterfaces.kt create mode 100644 idea/testData/refactoring/pushDown/pushSuperInterfaces.kt.after create mode 100644 idea/testData/refactoring/pushDown/pushSuperInterfacesWithGenerics.kt create mode 100644 idea/testData/refactoring/pushDown/pushSuperInterfacesWithGenerics.kt.after create mode 100644 idea/tests/org/jetbrains/kotlin/idea/refactoring/AbstractMemberPullPushTest.kt create mode 100644 idea/tests/org/jetbrains/kotlin/idea/refactoring/pushDown/AbstractPushDownTest.kt create mode 100644 idea/tests/org/jetbrains/kotlin/idea/refactoring/pushDown/PushDownTestGenerated.java diff --git a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index 5d4633626f3..0aba493d907 100644 --- a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -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) { model("refactoring/pullUp/j2k", extension = "java", singleClass = true, testClassName = "J2K", testMethod = "doJavaTest") } + testClass(javaClass()) { + model("refactoring/pushDown", extension = "kt", singleClass = true) + } + testClass(javaClass()) { model("debugger/selectExpression", recursive = false) model("debugger/selectExpression/disallowMethodCalls", testMethod = "doTestWoMethodCalls") diff --git a/idea/ide-common/src/org/jetbrains/kotlin/util/descriptorUtils.kt b/idea/ide-common/src/org/jetbrains/kotlin/util/descriptorUtils.kt index d21273a3c36..e64d0ab977e 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/util/descriptorUtils.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/util/descriptorUtils.kt @@ -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() .firstOrNull { it.getContainingDeclaration() == this && OverridingUtil.DEFAULT.isOverridableBy(it as CallableDescriptor, signature).getResult() == OVERRIDABLE diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/declarationsSearch/classInheritorsSearch.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/declarationsSearch/classInheritorsSearch.kt index d08b5440a37..9e63deef69b 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/declarationsSearch/classInheritorsSearch.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/declarationsSearch/classInheritorsSearch.kt @@ -26,7 +26,7 @@ import com.intellij.psi.PsiElement import com.intellij.psi.search.searches.ClassInheritorsSearch import com.intellij.util.EmptyQuery -public fun HierarchySearchRequest.searchInheritors(): Query { +public fun HierarchySearchRequest<*>.searchInheritors(): Query { val psiClass: PsiClass? = when (originalElement) { is JetClassOrObject -> LightClassUtil.getPsiClass(originalElement) is PsiClass -> originalElement diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/AbstractPullPushMembersHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/AbstractPullPushMembersHandler.kt new file mode 100644 index 00000000000..2155f56f2f8 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/AbstractPullPushMembersHandler.kt @@ -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, 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() 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): Boolean { + return elements.mapTo(HashSet()) { + when (it) { + is JetNamedFunction, is JetProperty -> + (it.parent as? JetClassBody)?.parent as? JetClassOrObject + is JetClassOrObject -> it + else -> null + } ?: return false + }.size() == 1 + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/KotlinRefactoringSupportProvider.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/KotlinRefactoringSupportProvider.kt index 2b06d2859c2..3568c9cc485 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/KotlinRefactoringSupportProvider.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/KotlinRefactoringSupportProvider.kt @@ -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() } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpHandler.kt index 866a55bd4ab..9a03be16200 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpHandler.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpHandler.kt @@ -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 } - 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, 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() 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): Boolean { - return elements.mapTo(HashSet()) { - 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" \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpHelper.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpHelper.kt index a3fe38a4abd..b711e8f358f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpHelper.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpHelper.kt @@ -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> { - 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() { @@ -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 { - val affectedElements = ArrayList() - - 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) { - 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() - .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 { diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/markingUtils.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/markingUtils.kt new file mode 100644 index 00000000000..5093f5b20d2 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/markingUtils.kt @@ -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 { + val affectedElements = ArrayList() + + 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) { + markedElements.forEach { + it.newFqName = null + it.newTypeText = null + it.replaceWithTargetThis = null + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/pullUpUtils.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/pullUpUtils.kt index 4d7e99a570d..6c5266f40b4 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/pullUpUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/pullUpUtils.kt @@ -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() + .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() } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/pushDown/KotlinPushDownDialog.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/pushDown/KotlinPushDownDialog.kt new file mode 100644 index 00000000000..744e611f652 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/pushDown/KotlinPushDownDialog.kt @@ -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, + private val sourceClass: JetClass +) : RefactoringDialog(project, true) { + init { + title = PUSH_MEMBERS_DOWN + init() + } + + private var memberInfoModel: AbstractMemberInfoModel? = null + + public val selectedMemberInfos: List + 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() { + 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)) + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/pushDown/KotlinPushDownHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/pushDown/KotlinPushDownHandler.kt new file mode 100644 index 00000000000..5cac22c17d6 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/pushDown/KotlinPushDownHandler.kt @@ -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): List + } + + 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() + } + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/pushDown/KotlinPushDownProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/pushDown/KotlinPushDownProcessor.kt new file mode 100644 index 00000000000..51fe12eda80 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/pushDown/KotlinPushDownProcessor.kt @@ -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 +) : 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) = UsageViewDescriptorImpl() + + override fun getBeforeData() = RefactoringEventData().apply { + addElement(sourceClass) + addElements(membersToMove.map { it.member }.toTypedArray()) + } + + override fun getAfterData(usages: Array) = RefactoringEventData().apply { + addElements(usages.map { it.element as? JetClassOrObject }.filterNotNull()) + } + + override fun findUsages(): Array { + 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) { + val markedElements = ArrayList() + try { + membersToMove.forEach { markedElements += markElements(it.member, sourceClassContext, sourceClassDescriptor, null) } + usages.forEach { (it.element as? JetClassOrObject)?.let { pushDownToClass(it) } } + removeOriginalMembers() + } + finally { + clearMarking(markedElements) + } + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pushDown/finalClass.kt b/idea/testData/refactoring/pushDown/finalClass.kt new file mode 100644 index 00000000000..2472bc6269f --- /dev/null +++ b/idea/testData/refactoring/pushDown/finalClass.kt @@ -0,0 +1,8 @@ +class A { + // INFO: {"checked": "true"} + val x = 1 + // INFO: {"checked": "true"} + fun foo() { + + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pushDown/finalClass.kt.messages b/idea/testData/refactoring/pushDown/finalClass.kt.messages new file mode 100644 index 00000000000..7ce0a117049 --- /dev/null +++ b/idea/testData/refactoring/pushDown/finalClass.kt.messages @@ -0,0 +1,2 @@ +Cannot perform refactoring. +Class A is final \ No newline at end of file diff --git a/idea/testData/refactoring/pushDown/implicitCompanionUsages.kt b/idea/testData/refactoring/pushDown/implicitCompanionUsages.kt new file mode 100644 index 00000000000..0bd2a18d9e3 --- /dev/null +++ b/idea/testData/refactoring/pushDown/implicitCompanionUsages.kt @@ -0,0 +1,12 @@ +class X { + open class A { + // INFO: {"checked": "true"} + fun foo(): Int = bar() + A.bar() + X.A.bar() + + companion object { + fun bar() = 1 + } + } +} + +class B : X.A \ No newline at end of file diff --git a/idea/testData/refactoring/pushDown/implicitCompanionUsages.kt.after b/idea/testData/refactoring/pushDown/implicitCompanionUsages.kt.after new file mode 100644 index 00000000000..033583e834c --- /dev/null +++ b/idea/testData/refactoring/pushDown/implicitCompanionUsages.kt.after @@ -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() +} \ No newline at end of file diff --git a/idea/testData/refactoring/pushDown/liftPrivate.kt b/idea/testData/refactoring/pushDown/liftPrivate.kt new file mode 100644 index 00000000000..a148509db7a --- /dev/null +++ b/idea/testData/refactoring/pushDown/liftPrivate.kt @@ -0,0 +1,12 @@ +open class A { + // INFO: {"checked": "true", "toAbstract": "true"} + private fun foo() { + + } +} + +class B : A { + fun foo() { + + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pushDown/liftPrivate.kt.after b/idea/testData/refactoring/pushDown/liftPrivate.kt.after new file mode 100644 index 00000000000..9b0715ff346 --- /dev/null +++ b/idea/testData/refactoring/pushDown/liftPrivate.kt.after @@ -0,0 +1,10 @@ +open class A { + // INFO: {"checked": "true", "toAbstract": "true"} + protected abstract fun foo() +} + +class B : A { + override fun foo() { + + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pushDown/noCaret.kt b/idea/testData/refactoring/pushDown/noCaret.kt new file mode 100644 index 00000000000..4982e2ddd21 --- /dev/null +++ b/idea/testData/refactoring/pushDown/noCaret.kt @@ -0,0 +1,4 @@ + +class A { + val x: Int = 1 +} \ No newline at end of file diff --git a/idea/testData/refactoring/pushDown/noCaret.kt.messages b/idea/testData/refactoring/pushDown/noCaret.kt.messages new file mode 100644 index 00000000000..00b333aa5d1 --- /dev/null +++ b/idea/testData/refactoring/pushDown/noCaret.kt.messages @@ -0,0 +1,2 @@ +Cannot perform refactoring. +Caret should be positioned inside a class to push members from \ No newline at end of file diff --git a/idea/testData/refactoring/pushDown/objectDeclaration.kt b/idea/testData/refactoring/pushDown/objectDeclaration.kt new file mode 100644 index 00000000000..6bed0490a62 --- /dev/null +++ b/idea/testData/refactoring/pushDown/objectDeclaration.kt @@ -0,0 +1,8 @@ +object O { + // INFO: {"checked": "true"} + val x = 1 + // INFO: {"checked": "true"} + fun foo() { + + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pushDown/objectDeclaration.kt.messages b/idea/testData/refactoring/pushDown/objectDeclaration.kt.messages new file mode 100644 index 00000000000..a8621af481a --- /dev/null +++ b/idea/testData/refactoring/pushDown/objectDeclaration.kt.messages @@ -0,0 +1,2 @@ +Cannot perform refactoring. +Object O is final \ No newline at end of file diff --git a/idea/testData/refactoring/pushDown/outsideOfClass.kt b/idea/testData/refactoring/pushDown/outsideOfClass.kt new file mode 100644 index 00000000000..7b729c2546f --- /dev/null +++ b/idea/testData/refactoring/pushDown/outsideOfClass.kt @@ -0,0 +1,3 @@ +fun test() { + +} \ No newline at end of file diff --git a/idea/testData/refactoring/pushDown/outsideOfClass.kt.messages b/idea/testData/refactoring/pushDown/outsideOfClass.kt.messages new file mode 100644 index 00000000000..00b333aa5d1 --- /dev/null +++ b/idea/testData/refactoring/pushDown/outsideOfClass.kt.messages @@ -0,0 +1,2 @@ +Cannot perform refactoring. +Caret should be positioned inside a class to push members from \ No newline at end of file diff --git a/idea/testData/refactoring/pushDown/pushClassMembers.kt b/idea/testData/refactoring/pushDown/pushClassMembers.kt new file mode 100644 index 00000000000..cd9dc3af746 --- /dev/null +++ b/idea/testData/refactoring/pushDown/pushClassMembers.kt @@ -0,0 +1,34 @@ +abstract class 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() +} \ No newline at end of file diff --git a/idea/testData/refactoring/pushDown/pushClassMembers.kt.after b/idea/testData/refactoring/pushDown/pushClassMembers.kt.after new file mode 100644 index 00000000000..3a955187e9c --- /dev/null +++ b/idea/testData/refactoring/pushDown/pushClassMembers.kt.after @@ -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 { + + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pushDown/pushClassMembersAndMakeAbstract.kt b/idea/testData/refactoring/pushDown/pushClassMembersAndMakeAbstract.kt new file mode 100644 index 00000000000..d8af9b895f8 --- /dev/null +++ b/idea/testData/refactoring/pushDown/pushClassMembersAndMakeAbstract.kt @@ -0,0 +1,34 @@ +abstract class 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() +} \ No newline at end of file diff --git a/idea/testData/refactoring/pushDown/pushClassMembersAndMakeAbstract.kt.after b/idea/testData/refactoring/pushDown/pushClassMembersAndMakeAbstract.kt.after new file mode 100644 index 00000000000..3160e0b041d --- /dev/null +++ b/idea/testData/refactoring/pushDown/pushClassMembersAndMakeAbstract.kt.after @@ -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 { + + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pushDown/pushClassMembersWithGenerics.kt b/idea/testData/refactoring/pushDown/pushClassMembersWithGenerics.kt new file mode 100644 index 00000000000..7fd9445f5a8 --- /dev/null +++ b/idea/testData/refactoring/pushDown/pushClassMembersWithGenerics.kt @@ -0,0 +1,29 @@ +interface Z + +open class A { + // INFO: {"checked": "true"} + val t1: T + // INFO: {"checked": "true"} + val t2: Z + + // INFO: {"checked": "true"} + fun foo(t1: T, t2: Z, s1: S, s2: Z): Boolean = true + + // INFO: {"checked": "true"} + inner class X : Z { + + } + + // INFO: {"checked": "true"} + class Y : Z { + + } +} + +class B : A>() { + +} + +class C : A>() { + +} \ No newline at end of file diff --git a/idea/testData/refactoring/pushDown/pushClassMembersWithGenerics.kt.after b/idea/testData/refactoring/pushDown/pushClassMembersWithGenerics.kt.after new file mode 100644 index 00000000000..b8ddd79f29c --- /dev/null +++ b/idea/testData/refactoring/pushDown/pushClassMembersWithGenerics.kt.after @@ -0,0 +1,47 @@ +interface Z + +open class A { + +} + +class B : A>() { + // INFO: {"checked": "true"} + val t1: Z + // INFO: {"checked": "true"} + val t2: Z> + + // INFO: {"checked": "true"} + fun foo(t1: Z, t2: Z>, s1: S, s2: Z): Boolean = true + + // INFO: {"checked": "true"} + inner class X : Z> { + + } + + // INFO: {"checked": "true"} + class Y : Z { + + } + +} + +class C : A>() { + // INFO: {"checked": "true"} + val t1: B + // INFO: {"checked": "true"} + val t2: Z> + + // INFO: {"checked": "true"} + fun foo(t1: B, t2: Z>, s1: S, s2: Z): Boolean = true + + // INFO: {"checked": "true"} + inner class X : Z> { + + } + + // INFO: {"checked": "true"} + class Y : Z { + + } + +} \ No newline at end of file diff --git a/idea/testData/refactoring/pushDown/pushInterfaceMembers.kt b/idea/testData/refactoring/pushDown/pushInterfaceMembers.kt new file mode 100644 index 00000000000..4701100867a --- /dev/null +++ b/idea/testData/refactoring/pushDown/pushInterfaceMembers.kt @@ -0,0 +1,31 @@ +interface 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() +} \ No newline at end of file diff --git a/idea/testData/refactoring/pushDown/pushInterfaceMembers.kt.after b/idea/testData/refactoring/pushDown/pushInterfaceMembers.kt.after new file mode 100644 index 00000000000..57b34ac24d0 --- /dev/null +++ b/idea/testData/refactoring/pushDown/pushInterfaceMembers.kt.after @@ -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 { + + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pushDown/pushInterfaceMembersAndMakeAbstract.kt b/idea/testData/refactoring/pushDown/pushInterfaceMembersAndMakeAbstract.kt new file mode 100644 index 00000000000..11484351247 --- /dev/null +++ b/idea/testData/refactoring/pushDown/pushInterfaceMembersAndMakeAbstract.kt @@ -0,0 +1,31 @@ +interface 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() +} \ No newline at end of file diff --git a/idea/testData/refactoring/pushDown/pushInterfaceMembersAndMakeAbstract.kt.after b/idea/testData/refactoring/pushDown/pushInterfaceMembersAndMakeAbstract.kt.after new file mode 100644 index 00000000000..2b062a3b600 --- /dev/null +++ b/idea/testData/refactoring/pushDown/pushInterfaceMembersAndMakeAbstract.kt.after @@ -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 { + + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pushDown/pushSuperInterfaces.kt b/idea/testData/refactoring/pushDown/pushSuperInterfaces.kt new file mode 100644 index 00000000000..2e4ca3a39ab --- /dev/null +++ b/idea/testData/refactoring/pushDown/pushSuperInterfaces.kt @@ -0,0 +1,12 @@ +// INFO: {"checked": "true"} +interface X + +// INFO: {"checked": "true"} +interface Y + +// INFO: {"checked": "true"} +interface Z + +open class A : X, Y, Z + +class B : A(), Z \ No newline at end of file diff --git a/idea/testData/refactoring/pushDown/pushSuperInterfaces.kt.after b/idea/testData/refactoring/pushDown/pushSuperInterfaces.kt.after new file mode 100644 index 00000000000..fe61f753891 --- /dev/null +++ b/idea/testData/refactoring/pushDown/pushSuperInterfaces.kt.after @@ -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 \ No newline at end of file diff --git a/idea/testData/refactoring/pushDown/pushSuperInterfacesWithGenerics.kt b/idea/testData/refactoring/pushDown/pushSuperInterfacesWithGenerics.kt new file mode 100644 index 00000000000..40a4a8e8650 --- /dev/null +++ b/idea/testData/refactoring/pushDown/pushSuperInterfacesWithGenerics.kt @@ -0,0 +1,14 @@ +interface I + +// INFO: {"checked": "true"} +interface X + +// INFO: {"checked": "true"} +interface Y + +// INFO: {"checked": "true"} +interface Z + +open class A : X, Y>, Z> + +class B : A, Y>(), Z \ No newline at end of file diff --git a/idea/testData/refactoring/pushDown/pushSuperInterfacesWithGenerics.kt.after b/idea/testData/refactoring/pushDown/pushSuperInterfacesWithGenerics.kt.after new file mode 100644 index 00000000000..80681776a33 --- /dev/null +++ b/idea/testData/refactoring/pushDown/pushSuperInterfacesWithGenerics.kt.after @@ -0,0 +1,14 @@ +interface I + +// INFO: {"checked": "true"} +interface X + +// INFO: {"checked": "true"} +interface Y + +// INFO: {"checked": "true"} +interface Z + +open class A + +class B : A, Y>(), Z, X>, Y> \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/AbstractMemberPullPushTest.kt b/idea/tests/org/jetbrains/kotlin/idea/refactoring/AbstractMemberPullPushTest.kt new file mode 100644 index 00000000000..f80e4926842 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/AbstractMemberPullPushTest.kt @@ -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>(members: List): List { + members.forEach { + val info = it.member.elementInfo + it.isChecked = info.checked + it.isToAbstract = info.toAbstract + } + return members.filter { it.isChecked } + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/pullUp/AbstractPullUpTest.kt b/idea/tests/org/jetbrains/kotlin/idea/refactoring/pullUp/AbstractPullUpTest.kt index cbe626141a6..446ee015823 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/pullUp/AbstractPullUpTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/pullUp/AbstractPullUpTest.kt @@ -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>(members: List): List { - 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): List { - return chooseMembers(members) - } + override fun adjustMembers(members: List) = chooseMembers(members) override fun chooseSuperClass(superClasses: List): 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 } } } diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/pushDown/AbstractPushDownTest.kt b/idea/tests/org/jetbrains/kotlin/idea/refactoring/pushDown/AbstractPushDownTest.kt new file mode 100644 index 00000000000..8579f8995ed --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/pushDown/AbstractPushDownTest.kt @@ -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) = chooseMembers(members) + } + KotlinPushDownHandler().invoke(getProject(), getEditor(), file) { + if (it == KotlinPushDownHandler.PUSH_DOWN_TEST_HELPER_KEY) helper else null + } + } + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/pushDown/PushDownTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/pushDown/PushDownTestGenerated.java new file mode 100644 index 00000000000..217ce594a47 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/pushDown/PushDownTestGenerated.java @@ -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); + } +}