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