Pull Up: Initial support
#KT-7330 Fixed
This commit is contained in:
@@ -21,9 +21,11 @@ import com.intellij.lang.ASTNode
|
||||
import com.intellij.navigation.ItemPresentation
|
||||
import com.intellij.navigation.ItemPresentationProviders
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiWhiteSpace
|
||||
import com.intellij.psi.impl.CheckUtil
|
||||
import com.intellij.psi.impl.source.codeStyle.CodeEditUtil
|
||||
import com.intellij.psi.stubs.IStubElementType
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.JetNodeTypes
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.lexer.JetTokens
|
||||
@@ -46,6 +48,22 @@ abstract public class JetClassOrObject : JetTypeParameterListOwnerStub<KotlinCla
|
||||
|
||||
public fun getOrCreateBody(): JetClassBody = getBody() ?: add(JetPsiFactory(this).createEmptyClassBody()) as JetClassBody
|
||||
|
||||
public fun addDeclaration(declaration: JetDeclaration): JetDeclaration {
|
||||
val body = getOrCreateBody()
|
||||
val anchor = PsiTreeUtil.skipSiblingsBackward(body.getRBrace() ?: body.getLastChild()!!, javaClass<PsiWhiteSpace>())
|
||||
return body.addAfter(declaration, anchor) as JetDeclaration
|
||||
}
|
||||
|
||||
public fun addDeclarationAfter(declaration: JetDeclaration, anchor: PsiElement?): JetDeclaration {
|
||||
val anchorBefore = anchor ?: getDeclarations().lastOrNull() ?: return addDeclaration(declaration)
|
||||
return getOrCreateBody().addAfter(declaration, anchorBefore) as JetDeclaration
|
||||
}
|
||||
|
||||
public fun addDeclarationBefore(declaration: JetDeclaration, anchor: PsiElement?): JetDeclaration {
|
||||
val anchorAfter = anchor ?: getDeclarations().firstOrNull() ?: return addDeclaration(declaration)
|
||||
return getOrCreateBody().addBefore(declaration, anchorAfter) as JetDeclaration
|
||||
}
|
||||
|
||||
public fun isTopLevel(): Boolean = getStub()?.isTopLevel() ?: (getParent() is JetFile)
|
||||
|
||||
public fun isLocal(): Boolean = getStub()?.isLocal() ?: JetPsiUtil.isLocal(this)
|
||||
|
||||
@@ -157,6 +157,11 @@ public class JetProperty extends JetTypeParameterListOwnerStub<KotlinPropertyStu
|
||||
return findChildByType(JetTokens.COLON);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PsiElement getEqualsToken() {
|
||||
return findChildByType(JetTokens.EQ);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<JetPropertyAccessor> getAccessors() {
|
||||
return getStubOrPsiChildrenAsList(JetStubElementTypes.PROPERTY_ACCESSOR);
|
||||
|
||||
@@ -134,6 +134,15 @@ public fun JetExpression.getQualifiedExpressionForSelectorOrThis(): JetExpressio
|
||||
return getQualifiedExpressionForSelector() ?: this
|
||||
}
|
||||
|
||||
public fun JetExpression.getQualifiedExpressionForReceiver(): JetQualifiedExpression? {
|
||||
val parent = getParent()
|
||||
return if (parent is JetQualifiedExpression && parent.getReceiverExpression() == this) parent else null
|
||||
}
|
||||
|
||||
public fun JetExpression.getQualifiedExpressionForReceiverOrThis(): JetExpression {
|
||||
return getQualifiedExpressionForReceiver() ?: this
|
||||
}
|
||||
|
||||
public fun JetExpression.isDotReceiver(): Boolean =
|
||||
(getParent() as? JetDotQualifiedExpression)?.getReceiverExpression() == this
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.psi
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.UserDataHolder
|
||||
import com.intellij.psi.PsiElement
|
||||
|
||||
public class UserDataProperty<in R: UserDataHolder, T : Any>(val key: Key<T>, val default: T? = null) : ReadWriteProperty<R, T?> {
|
||||
override fun get(thisRef: R, desc: kotlin.PropertyMetadata): T? {
|
||||
@@ -39,3 +40,13 @@ public class NotNullableUserDataProperty<in R: UserDataHolder, T : Any>(val key:
|
||||
thisRef.putUserData(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
public class CopyableUserDataProperty<in R: PsiElement, T : Any>(val key: Key<T>, val default: T? = null) : ReadWriteProperty<R, T?> {
|
||||
override fun get(thisRef: R, property: PropertyMetadata): T? {
|
||||
return thisRef.getCopyableUserData(key)
|
||||
}
|
||||
|
||||
override fun set(thisRef: R, property: PropertyMetadata, value: T?) {
|
||||
thisRef.putCopyableUserData(key, value)
|
||||
}
|
||||
}
|
||||
@@ -16,14 +16,17 @@
|
||||
|
||||
package org.jetbrains.kotlin.test.util
|
||||
|
||||
import com.intellij.codeInspection.SmartHashMap
|
||||
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.psi.PsiComment
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiWhiteSpace
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.util.SmartFMap
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
public fun String.trimTrailingWhitespacesAndAddNewlineAtEOF(): String =
|
||||
this.split('\n').map { it.trimEnd() }.joinToString(separator = "\n").let {
|
||||
@@ -74,28 +77,29 @@ public fun String.trimIndent(): String {
|
||||
}.joinToString(separator = "\n")
|
||||
}
|
||||
|
||||
public fun JetFile.findElementByComment(commentText: String): JetElement? {
|
||||
var result: JetElement? = null
|
||||
public fun JetFile.findElementByCommentPrefix(commentText: String): JetElement? =
|
||||
findElementsByCommentPrefix(commentText).keySet().singleOrNull()
|
||||
|
||||
public fun JetFile.findElementsByCommentPrefix(prefix: String): Map<JetElement, String> {
|
||||
var result = SmartFMap.emptyMap<JetElement, String>()
|
||||
accept(
|
||||
object : JetTreeVisitorVoid() {
|
||||
override fun visitComment(comment: PsiComment) {
|
||||
if (comment.getText() == commentText) {
|
||||
val commentText = comment.getText()
|
||||
if (commentText.startsWith(prefix)) {
|
||||
val parent = comment.getParent()
|
||||
if (parent is JetDeclaration) {
|
||||
result = parent
|
||||
}
|
||||
else {
|
||||
result = PsiTreeUtil.skipSiblingsForward(
|
||||
val elementToAdd = when (parent) {
|
||||
is JetDeclaration -> parent
|
||||
else -> PsiTreeUtil.skipSiblingsForward(
|
||||
comment,
|
||||
javaClass<PsiWhiteSpace>(),
|
||||
javaClass<PsiComment>(),
|
||||
javaClass<JetPackageDirective>()
|
||||
) as? JetElement
|
||||
}
|
||||
javaClass<PsiWhiteSpace>(), javaClass<PsiComment>(), javaClass<JetPackageDirective>()
|
||||
)
|
||||
} as? JetElement ?: return
|
||||
|
||||
result = result.plus(elementToAdd, commentText.substring(prefix.length()).trim())
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -83,6 +83,7 @@ import org.jetbrains.kotlin.idea.quickfix.AbstractQuickFixTest
|
||||
import org.jetbrains.kotlin.idea.refactoring.inline.AbstractInlineTest
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.AbstractJetExtractionTest
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.AbstractJetMoveTest
|
||||
import org.jetbrains.kotlin.idea.refactoring.pullUp.AbstractPullUpTest
|
||||
import org.jetbrains.kotlin.idea.refactoring.rename.AbstractRenameTest
|
||||
import org.jetbrains.kotlin.idea.resolve.*
|
||||
import org.jetbrains.kotlin.idea.structureView.AbstractKotlinFileStructureTest
|
||||
@@ -653,6 +654,10 @@ fun main(args: Array<String>) {
|
||||
model("refactoring/introduceJavaParameter", extension = "java", testMethod = "doIntroduceJavaParameterTest")
|
||||
}
|
||||
|
||||
testClass(javaClass<AbstractPullUpTest>()) {
|
||||
model("refactoring/pullUp", extension = "kt", singleClass = true)
|
||||
}
|
||||
|
||||
testClass(javaClass<AbstractSelectExpressionForDebuggerTest>()) {
|
||||
model("debugger/selectExpression", recursive = false)
|
||||
model("debugger/selectExpression/disallowMethodCalls", testMethod = "doTestWoMethodCalls")
|
||||
|
||||
@@ -16,11 +16,13 @@
|
||||
|
||||
package org.jetbrains.kotlin.util
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||
import org.jetbrains.kotlin.resolve.OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
import org.jetbrains.kotlin.types.TypeConstructor
|
||||
import org.jetbrains.kotlin.types.checker.JetTypeChecker
|
||||
import org.jetbrains.kotlin.types.checker.TypeCheckingProcedure
|
||||
import org.jetbrains.kotlin.types.typeUtil.equalTypesOrNulls
|
||||
|
||||
public fun descriptorsEqualWithSubstitution(descriptor1: DeclarationDescriptor?, descriptor2: DeclarationDescriptor?): Boolean {
|
||||
@@ -56,3 +58,12 @@ public fun descriptorsEqualWithSubstitution(descriptor1: DeclarationDescriptor?,
|
||||
return true
|
||||
}
|
||||
|
||||
public fun ClassDescriptor.findCallableMemberBySignature(signature: CallableMemberDescriptor): CallableMemberDescriptor? {
|
||||
val descriptorKind = if (signature is FunctionDescriptor) DescriptorKindFilter.FUNCTIONS else DescriptorKindFilter.VARIABLES
|
||||
return getDefaultType().getMemberScope()
|
||||
.getDescriptors(descriptorKind)
|
||||
.firstOrNull {
|
||||
it.getContainingDeclaration() == this
|
||||
&& OverridingUtil.DEFAULT.isOverridableBy(it as CallableDescriptor, signature).getResult() == OVERRIDABLE
|
||||
} as? CallableMemberDescriptor
|
||||
}
|
||||
|
||||
@@ -527,6 +527,8 @@
|
||||
language="jet"
|
||||
implementationClass="org.jetbrains.kotlin.idea.search.ideaExtensions.JetTargetElementEvaluator" />
|
||||
|
||||
<refactoring.pullUpHelperFactory language="jet" implementationClass="org.jetbrains.kotlin.idea.refactoring.pullUp.KotlinPullUpHelperFactory"/>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.idea.intentions.IfNullToElvisIntention</className>
|
||||
<category>Kotlin</category>
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.idea.refactoring.introduce.introduceParameter.Kotlin
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceParameter.KotlinIntroduceParameterHandler
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceProperty.KotlinIntroducePropertyHandler
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinIntroduceVariableHandler
|
||||
import org.jetbrains.kotlin.idea.refactoring.pullUp.KotlinPullUpHandler
|
||||
import org.jetbrains.kotlin.idea.refactoring.safeDelete.*
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
|
||||
@@ -70,4 +71,6 @@ public class KotlinRefactoringSupportProvider : RefactoringSupportProvider() {
|
||||
}
|
||||
|
||||
override fun getChangeSignatureHandler() = JetChangeSignatureHandler()
|
||||
|
||||
override fun getPullUpHandler() = KotlinPullUpHandler()
|
||||
}
|
||||
|
||||
+33
@@ -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.memberInfo
|
||||
|
||||
import com.intellij.openapi.util.Iconable
|
||||
import com.intellij.ui.ListCellRendererWrapper
|
||||
import org.jetbrains.kotlin.psi.JetClassOrObject
|
||||
import javax.swing.JList
|
||||
|
||||
public class JetClassOrObjectCellRenderer: ListCellRendererWrapper<JetClassOrObject>() {
|
||||
override fun customize(list: JList, value: JetClassOrObject?, index: Int, selected: Boolean, hasFocus: Boolean) {
|
||||
if (value == null) return
|
||||
|
||||
setText(value.qualifiedNameForRendering())
|
||||
value.getIcon(Iconable.ICON_FLAG_VISIBILITY or Iconable.ICON_FLAG_READ_STATUS)?.let { setIcon(it) }
|
||||
}
|
||||
}
|
||||
|
||||
public fun JetClassOrObject.qualifiedNameForRendering(): String = getFqName()?.asString() ?: getName() ?: "[Anonymous]"
|
||||
@@ -16,13 +16,16 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.refactoring.memberInfo
|
||||
|
||||
import com.intellij.psi.PsiMember
|
||||
import com.intellij.refactoring.classMembers.MemberInfoBase
|
||||
import com.intellij.refactoring.util.classMembers.MemberInfo
|
||||
import org.jetbrains.kotlin.asJava.getRepresentativeLightMethod
|
||||
import org.jetbrains.kotlin.asJava.toLightClass
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.psi.JetFile
|
||||
import org.jetbrains.kotlin.psi.JetNamedDeclaration
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
|
||||
public class KotlinMemberInfo(member: JetNamedDeclaration) : MemberInfoBase<JetNamedDeclaration>(member) {
|
||||
init {
|
||||
@@ -36,3 +39,17 @@ public class KotlinMemberInfo(member: JetNamedDeclaration) : MemberInfoBase<JetN
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public fun KotlinMemberInfo.toJavaMemberInfo(): MemberInfo? {
|
||||
val declaration = getMember()
|
||||
val psiMember: PsiMember? = when (declaration) {
|
||||
is JetNamedFunction, is JetProperty -> declaration.getRepresentativeLightMethod()
|
||||
is JetClassOrObject -> declaration.toLightClass()
|
||||
else -> null
|
||||
}
|
||||
if (psiMember == null) return null
|
||||
|
||||
val info = MemberInfo(psiMember)
|
||||
info.setToAbstract(isToAbstract())
|
||||
return info
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.psi.PsiElement
|
||||
import com.intellij.psi.PsiField
|
||||
import com.intellij.psi.PsiMember
|
||||
import com.intellij.psi.PsiSubstitutor
|
||||
import com.intellij.refactoring.classMembers.MemberInfoBase
|
||||
import com.intellij.refactoring.memberPullUp.PullUpData
|
||||
import com.intellij.refactoring.memberPullUp.PullUpHelper
|
||||
import com.intellij.refactoring.memberPullUp.PullUpHelperFactory
|
||||
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
|
||||
import org.jetbrains.kotlin.asJava.unwrapped
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.psi.JetClassOrObject
|
||||
import org.jetbrains.kotlin.psi.JetNamedDeclaration
|
||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||
import org.jetbrains.kotlin.types.substitutions.getTypeSubstitutor
|
||||
import java.util.LinkedHashSet
|
||||
|
||||
object EmptyPullUpHelper : PullUpHelper<MemberInfoBase<PsiMember>> {
|
||||
override fun postProcessMember(member: PsiMember) {
|
||||
|
||||
}
|
||||
|
||||
override fun moveFieldInitializations(movedFields: LinkedHashSet<PsiField>) {
|
||||
|
||||
}
|
||||
|
||||
override fun encodeContextInfo(info: MemberInfoBase<PsiMember>) {
|
||||
|
||||
}
|
||||
|
||||
override fun setCorrectVisibility(info: MemberInfoBase<PsiMember>) {
|
||||
|
||||
}
|
||||
|
||||
override fun move(info: MemberInfoBase<PsiMember>, substitutor: PsiSubstitutor) {
|
||||
|
||||
}
|
||||
|
||||
override fun updateUsage(element: PsiElement) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.refactoring.memberPullUp.PullUpData
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.core.getResolutionScope
|
||||
import org.jetbrains.kotlin.psi.JetClass
|
||||
import org.jetbrains.kotlin.psi.JetClassOrObject
|
||||
import org.jetbrains.kotlin.psi.JetNamedDeclaration
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.substitutions.getTypeSubstitution
|
||||
import org.jetbrains.kotlin.utils.keysToMap
|
||||
import java.util.*
|
||||
|
||||
class KotlinPullUpData(val sourceClass: JetClassOrObject,
|
||||
val targetClass: JetClass,
|
||||
val membersToMove: Collection<JetNamedDeclaration>) {
|
||||
val resolutionFacade = sourceClass.getResolutionFacade()
|
||||
|
||||
val sourceClassContext = resolutionFacade.analyzeFullyAndGetResult(listOf(sourceClass)).bindingContext
|
||||
|
||||
val sourceClassDescriptor = sourceClassContext[BindingContext.DECLARATION_TO_DESCRIPTOR, sourceClass] as ClassDescriptor
|
||||
|
||||
val memberDescriptors = membersToMove.keysToMap { sourceClassContext[BindingContext.DECLARATION_TO_DESCRIPTOR, it]!! }
|
||||
|
||||
val targetClassDescriptor = resolutionFacade.resolveToDescriptor(targetClass) as ClassDescriptor
|
||||
|
||||
val typeParametersInSourceClassContext by lazy {
|
||||
sourceClassDescriptor.getTypeConstructor().getParameters() + sourceClass.getResolutionScope(sourceClassContext, resolutionFacade)
|
||||
.getDescriptors(DescriptorKindFilter.NON_SINGLETON_CLASSIFIERS)
|
||||
.filterIsInstance<TypeParameterDescriptor>()
|
||||
}
|
||||
|
||||
val sourceToTargetClassSubstitutor: TypeSubstitutor by lazy {
|
||||
val substitution = LinkedHashMap<TypeConstructor, TypeProjection>()
|
||||
|
||||
typeParametersInSourceClassContext.forEach {
|
||||
substitution[it.getTypeConstructor()] = TypeProjectionImpl(it.getUpperBoundsAsType())
|
||||
}
|
||||
|
||||
val superClassSubstitution = getTypeSubstitution(targetClassDescriptor.getDefaultType(), sourceClassDescriptor.getDefaultType())
|
||||
?: emptyMap<TypeConstructor, TypeProjection>()
|
||||
for ((typeConstructor, typeProjection) in superClassSubstitution) {
|
||||
val subClassTypeParameter = typeProjection.getType().getConstructor().getDeclarationDescriptor() as? TypeParameterDescriptor
|
||||
?: continue
|
||||
val superClassTypeParameter = typeConstructor.getDeclarationDescriptor()
|
||||
?: continue
|
||||
substitution[subClassTypeParameter.getTypeConstructor()] = TypeProjectionImpl(superClassTypeParameter.getDefaultType())
|
||||
}
|
||||
|
||||
TypeSubstitutor.create(substitution)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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.project.Project
|
||||
import com.intellij.psi.PsiComment
|
||||
import com.intellij.refactoring.JavaRefactoringSettings
|
||||
import com.intellij.refactoring.classMembers.AbstractMemberInfoModel
|
||||
import com.intellij.refactoring.memberPullUp.PullUpDialogBase
|
||||
import com.intellij.refactoring.memberPullUp.PullUpProcessor
|
||||
import com.intellij.refactoring.util.DocCommentPolicy
|
||||
import org.jetbrains.kotlin.asJava.toLightClass
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.*
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import java.awt.event.ItemEvent
|
||||
import javax.swing.JComboBox
|
||||
|
||||
public class KotlinPullUpDialog(
|
||||
project: Project,
|
||||
private val classOrObject: JetClassOrObject,
|
||||
superClasses: List<JetClass>,
|
||||
memberInfoStorage: KotlinMemberInfoStorage
|
||||
) : PullUpDialogBase<KotlinMemberInfoStorage, KotlinMemberInfo, JetNamedDeclaration, JetClassOrObject>(
|
||||
project, classOrObject, superClasses, memberInfoStorage, PULL_MEMBERS_UP
|
||||
) {
|
||||
init {
|
||||
init()
|
||||
}
|
||||
|
||||
private inner class MemberInfoModelImpl : AbstractMemberInfoModel<JetNamedDeclaration, KotlinMemberInfo>() {
|
||||
// Abstract members remain abstract
|
||||
override fun isFixedAbstract(memberInfo: KotlinMemberInfo?) = true
|
||||
|
||||
/*
|
||||
* Any non-abstract function can change abstractness.
|
||||
*
|
||||
* Non-abstract property with initializer or delegate is always made abstract.
|
||||
* Any other non-abstract property can change abstractness.
|
||||
*
|
||||
* Classes do not have abstractness
|
||||
*/
|
||||
override fun isAbstractEnabled(memberInfo: KotlinMemberInfo): Boolean {
|
||||
val superClass = getSuperClass() ?: return false
|
||||
if (!superClass.isInterface()) return true
|
||||
|
||||
val member = memberInfo.getMember()
|
||||
return member is JetNamedFunction || (member is JetProperty && !member.mustBeAbstractInInterface())
|
||||
}
|
||||
|
||||
override fun isAbstractWhenDisabled(memberInfo: KotlinMemberInfo): Boolean {
|
||||
return memberInfo.getMember() is JetProperty
|
||||
}
|
||||
|
||||
override fun isMemberEnabled(memberInfo: KotlinMemberInfo): Boolean {
|
||||
val superClass = getSuperClass() ?: return false
|
||||
if (memberInfo in memberInfoStorage.getDuplicatedMemberInfos(superClass)) return false
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
protected val memberInfoStorage: KotlinMemberInfoStorage get() = myMemberInfoStorage
|
||||
|
||||
override fun getDimensionServiceKey() = "#" + javaClass.getName()
|
||||
|
||||
override fun getSuperClass() = super.getSuperClass() as? JetClass
|
||||
|
||||
override fun initClassCombo(classCombo: JComboBox) {
|
||||
classCombo.setRenderer(JetClassOrObjectCellRenderer())
|
||||
classCombo.addItemListener { event ->
|
||||
if (event.getStateChange() == ItemEvent.SELECTED) {
|
||||
myMemberSelectionPanel?.getTable()?.let {
|
||||
it.setMemberInfos(myMemberInfos)
|
||||
it.fireExternalDataChange()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun createMemberInfoModel() = MemberInfoModelImpl()
|
||||
|
||||
override fun getPreselection() = mySuperClasses.firstOrNull()
|
||||
|
||||
override fun createMemberSelectionTable(infos: MutableList<KotlinMemberInfo>) =
|
||||
KotlinMemberSelectionTable(infos, null, "Make abstract")
|
||||
|
||||
override fun doAction() {
|
||||
invokeRefactoring(createProcessor(myClass, getSuperClass()!!, getSelectedMemberInfos()))
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun createProcessor(sourceClass: JetClassOrObject,
|
||||
targetClass: JetClass,
|
||||
memberInfos: List<KotlinMemberInfo>): PullUpProcessor {
|
||||
return PullUpProcessor(sourceClass.toLightClass(),
|
||||
targetClass.toLightClass(),
|
||||
memberInfos.map { it.toJavaMemberInfo() }.filterNotNull().toTypedArray(),
|
||||
DocCommentPolicy<PsiComment>(JavaRefactoringSettings.getInstance().PULL_UP_MEMBERS_JAVADOC))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* 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.actionSystem.CommonDataKeys
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.ScrollType
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.refactoring.HelpID
|
||||
import com.intellij.refactoring.RefactoringActionHandler
|
||||
import com.intellij.refactoring.RefactoringBundle
|
||||
import com.intellij.refactoring.lang.ElementsHandler
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.idea.core.refactoring.canRefactor
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfoStorage
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.qualifiedNameForRendering
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.types.typeUtil.supertypes
|
||||
import java.util.HashSet
|
||||
|
||||
public class KotlinPullUpHandler : RefactoringActionHandler, ElementsHandler {
|
||||
companion object {
|
||||
val PULLUP_TEST_HELPER_KEY = "PULLUP_TEST_HELPER_KEY"
|
||||
}
|
||||
|
||||
interface TestHelper {
|
||||
fun adjustMembers(members: List<KotlinMemberInfo>): List<KotlinMemberInfo>
|
||||
fun chooseSuperClass(superClasses: List<JetClass>): JetClass
|
||||
}
|
||||
|
||||
private fun reportWrongPosition(project: Project, editor: Editor?) {
|
||||
val message = RefactoringBundle.getCannotRefactorMessage(
|
||||
RefactoringBundle.message("the.caret.should.be.positioned.inside.a.class.to.pull.members.from")
|
||||
)
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, message, PULL_MEMBERS_UP, "refactoring.pullMembersUp")
|
||||
}
|
||||
|
||||
private fun reportWrongContext(project: Project, editor: Editor?) {
|
||||
val message = RefactoringBundle.getCannotRefactorMessage(
|
||||
RefactoringBundle.message("is.not.supported.in.the.current.context", PULL_MEMBERS_UP)
|
||||
)
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, message, PULL_MEMBERS_UP, HelpID.MEMBERS_PULL_UP)
|
||||
}
|
||||
|
||||
private fun reportNoSuperClasses(project: Project, editor: Editor?, classOrObject: JetClassOrObject) {
|
||||
val message = RefactoringBundle.getCannotRefactorMessage(
|
||||
RefactoringBundle.message("class.does.not.have.base.classes.interfaces.in.current.project",
|
||||
classOrObject.qualifiedNameForRendering())
|
||||
)
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, message, PULL_MEMBERS_UP, HelpID.MEMBERS_PULL_UP)
|
||||
}
|
||||
|
||||
override fun invoke(project: Project, editor: Editor, file: PsiFile, dataContext: DataContext?) {
|
||||
val offset = editor.getCaretModel().getOffset()
|
||||
editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE)
|
||||
|
||||
val target = (file.findElementAt(offset) ?: return).parentsWithSelf.firstOrNull {
|
||||
it is JetClassOrObject || ((it is JetNamedFunction || it is JetProperty) && it.getParent() 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?,
|
||||
classOrObject: JetClassOrObject?,
|
||||
member: JetNamedDeclaration?,
|
||||
dataContext: DataContext?) {
|
||||
if (classOrObject == null) {
|
||||
reportWrongContext(project, editor)
|
||||
return
|
||||
}
|
||||
|
||||
val classDescriptor = classOrObject.resolveToDescriptor() as ClassDescriptor
|
||||
val superClasses = classDescriptor.getDefaultType()
|
||||
.supertypes()
|
||||
.asSequence()
|
||||
.map {
|
||||
val descriptor = it.getConstructor().getDeclarationDescriptor()
|
||||
val declaration = descriptor?.let { DescriptorToSourceUtilsIde.getAnyDeclaration(project, it) }
|
||||
if (declaration is JetClass && declaration.canRefactor()) declaration else null
|
||||
}
|
||||
.filterNotNull()
|
||||
.toSortedListBy { it.qualifiedNameForRendering() }
|
||||
|
||||
if (superClasses.isEmpty()) {
|
||||
val containingClass = classOrObject.getStrictParentOfType<JetClassOrObject>()
|
||||
if (containingClass != null) {
|
||||
invoke(project, editor, containingClass, classOrObject, dataContext)
|
||||
}
|
||||
else {
|
||||
reportNoSuperClasses(project, editor, classOrObject)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
val memberInfoStorage = KotlinMemberInfoStorage(classOrObject)
|
||||
val members = memberInfoStorage.getClassMemberInfos(classOrObject)
|
||||
|
||||
if (ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
val helper = dataContext?.getData(PULLUP_TEST_HELPER_KEY) as TestHelper
|
||||
KotlinPullUpDialog.createProcessor(classOrObject,
|
||||
helper.chooseSuperClass(superClasses),
|
||||
helper.adjustMembers(members)).run()
|
||||
}
|
||||
else {
|
||||
val manager = classOrObject.getManager()
|
||||
members.filter { manager.areElementsEquivalent(it.getMember(), member) }.forEach { it.setChecked(true) }
|
||||
|
||||
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.getParent() as? JetClassBody)?.getParent() as? JetClassOrObject
|
||||
is JetClassOrObject -> it
|
||||
else -> null
|
||||
} ?: return false
|
||||
}.size() == 1
|
||||
}
|
||||
}
|
||||
|
||||
val PULL_MEMBERS_UP = "Pull Members Up"
|
||||
@@ -0,0 +1,393 @@
|
||||
/*
|
||||
* 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 com.intellij.psi.PsiField
|
||||
import com.intellij.psi.PsiMember
|
||||
import com.intellij.psi.PsiSubstitutor
|
||||
import com.intellij.psi.search.LocalSearchScope
|
||||
import com.intellij.psi.search.searches.ReferencesSearch
|
||||
import com.intellij.refactoring.classMembers.MemberInfoBase
|
||||
import com.intellij.refactoring.memberPullUp.PullUpData
|
||||
import com.intellij.refactoring.memberPullUp.PullUpHelper
|
||||
import org.jetbrains.kotlin.asJava.getRepresentativeLightMethod
|
||||
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
|
||||
import org.jetbrains.kotlin.asJava.toLightClass
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.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.imports.importableFqName
|
||||
import org.jetbrains.kotlin.idea.refactoring.safeDelete.removeOverrideModifier
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.idea.util.ShortenReferences
|
||||
import org.jetbrains.kotlin.lexer.JetModifierKeywordToken
|
||||
import org.jetbrains.kotlin.lexer.JetTokens
|
||||
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.psi.psiUtil.parentsWithSelf
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getExplicitReceiverValue
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||
import org.jetbrains.kotlin.util.findCallableMemberBySignature
|
||||
import java.util.ArrayList
|
||||
import java.util.LinkedHashSet
|
||||
|
||||
class KotlinPullUpHelper(
|
||||
private val javaData: PullUpData,
|
||||
private val data: KotlinPullUpData
|
||||
) : PullUpHelper<MemberInfoBase<PsiMember>> {
|
||||
companion object {
|
||||
private var JetElement.newFqName: FqName? by CopyableUserDataProperty(Key.create("NEW_FQ_NAME"))
|
||||
private var JetElement.replaceWithTargetThis: Boolean? by CopyableUserDataProperty(Key.create("REPLACE_WITH_TARGET_THIS"))
|
||||
private var JetElement.newTypeText: String? by CopyableUserDataProperty(Key.create("NEW_TYPE_TEXT"))
|
||||
}
|
||||
|
||||
private fun willBeUsedInSourceClass(member: PsiElement): Boolean {
|
||||
return !ReferencesSearch
|
||||
.search(member, LocalSearchScope(data.sourceClass), false)
|
||||
.all { it.getElement().parentsWithSelf.any { it in data.membersToMove } }
|
||||
}
|
||||
|
||||
private fun liftToProtected(declaration: JetNamedDeclaration, ignoreUsages: Boolean = false) {
|
||||
if (!declaration.hasModifier(JetTokens.PRIVATE_KEYWORD)) return
|
||||
if (ignoreUsages || willBeUsedInSourceClass(declaration)) declaration.addModifierWithSpace(JetTokens.PROTECTED_KEYWORD)
|
||||
}
|
||||
|
||||
override fun setCorrectVisibility(info: MemberInfoBase<PsiMember>) {
|
||||
val member = info.getMember().namedUnwrappedElement as? JetNamedDeclaration ?: return
|
||||
|
||||
if (data.targetClass.isInterface()) {
|
||||
member.removeModifier(JetTokens.PUBLIC_KEYWORD)
|
||||
return
|
||||
}
|
||||
|
||||
if (member.hasModifier(JetTokens.PRIVATE_KEYWORD)) {
|
||||
member.accept(
|
||||
object: JetVisitorVoid() {
|
||||
override fun visitNamedDeclaration(declaration: JetNamedDeclaration) {
|
||||
when (declaration) {
|
||||
is JetClass -> {
|
||||
liftToProtected(declaration)
|
||||
declaration.getDeclarations().forEach { it.accept(this) }
|
||||
}
|
||||
is JetNamedFunction, is JetProperty -> {
|
||||
liftToProtected(declaration, declaration == member && info.isToAbstract())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun encodeContextInfo(info: MemberInfoBase<PsiMember>) {
|
||||
|
||||
}
|
||||
|
||||
private fun getClashingMemberInTargetClass(memberDescriptor: CallableMemberDescriptor): CallableMemberDescriptor? {
|
||||
val memberInSuper = memberDescriptor.substitute(data.sourceToTargetClassSubstitutor) ?: return null
|
||||
return data.targetClassDescriptor.findCallableMemberBySignature(memberInSuper as CallableMemberDescriptor)
|
||||
}
|
||||
|
||||
private fun fixOverrideAndGetClashingSuper(sourceMember: JetCallableDeclaration,
|
||||
targetMember: JetCallableDeclaration): JetCallableDeclaration? {
|
||||
val memberDescriptor = data.memberDescriptors[sourceMember] as CallableMemberDescriptor
|
||||
|
||||
if (memberDescriptor.getOverriddenDescriptors().isEmpty()) {
|
||||
targetMember.removeOverrideModifier()
|
||||
return null
|
||||
}
|
||||
|
||||
val clashingSuperDescriptor = getClashingMemberInTargetClass(memberDescriptor) ?: return null
|
||||
if (clashingSuperDescriptor.getOverriddenDescriptors().isEmpty()) {
|
||||
targetMember.removeOverrideModifier()
|
||||
}
|
||||
return clashingSuperDescriptor.getSource().getPsi() as? JetCallableDeclaration
|
||||
}
|
||||
|
||||
private fun makeAbstract(sourceMember: JetCallableDeclaration, targetMember: JetCallableDeclaration) {
|
||||
if (!data.targetClass.isInterface()) {
|
||||
targetMember.addModifierWithSpace(JetTokens.ABSTRACT_KEYWORD)
|
||||
}
|
||||
|
||||
if (sourceMember.getTypeReference() == null) {
|
||||
var type = (data.memberDescriptors[sourceMember] as CallableMemberDescriptor).getReturnType()
|
||||
if (type == null || type.isError()) {
|
||||
type = KotlinBuiltIns.getInstance().getNullableAnyType()
|
||||
}
|
||||
else {
|
||||
type = data.sourceToTargetClassSubstitutor.substitute(type, Variance.INVARIANT)
|
||||
?: KotlinBuiltIns.getInstance().getNullableAnyType()
|
||||
}
|
||||
|
||||
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.getAccessors()
|
||||
targetMember.getEqualsToken() ?: targetMember.getDelegate() ?: accessors.firstOrNull()
|
||||
}
|
||||
|
||||
is JetNamedFunction -> {
|
||||
targetMember as JetNamedFunction
|
||||
targetMember.getEqualsToken() ?: targetMember.getBodyExpression()
|
||||
}
|
||||
|
||||
else -> null
|
||||
}
|
||||
|
||||
if (deleteFrom != null) {
|
||||
targetMember.deleteChildRange(deleteFrom, targetMember.getLastChild())
|
||||
}
|
||||
}
|
||||
|
||||
private fun addMemberToTarget(targetMember: JetNamedDeclaration): JetNamedDeclaration {
|
||||
val anchor = data.targetClass.getDeclarations().filterIsInstance(targetMember.javaClass).lastOrNull()
|
||||
val movedMember = when {
|
||||
anchor == null && targetMember is JetProperty -> data.targetClass.addDeclarationBefore(targetMember, null)
|
||||
else -> data.targetClass.addDeclarationAfter(targetMember, anchor)
|
||||
}
|
||||
return movedMember as JetNamedDeclaration
|
||||
}
|
||||
|
||||
private fun doAddCallableMember(memberCopy: JetCallableDeclaration, clashingSuper: JetCallableDeclaration?): JetCallableDeclaration {
|
||||
if (clashingSuper != null && clashingSuper.hasModifier(JetTokens.ABSTRACT_KEYWORD)) {
|
||||
return clashingSuper.replaced(memberCopy)
|
||||
}
|
||||
return addMemberToTarget(memberCopy) as JetCallableDeclaration
|
||||
}
|
||||
|
||||
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.getInstanceReference()]
|
||||
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.getExtensionReceiver()
|
||||
}
|
||||
if (!receiver.exists()) {
|
||||
receiver = resolvedCall.getDispatchReceiver()
|
||||
}
|
||||
if (!receiver.exists()) return
|
||||
|
||||
val implicitThis = receiver.getType().getConstructor().getDeclarationDescriptor() 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.getName().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
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Formatting rules don't apply here for some reason
|
||||
private fun JetNamedDeclaration.addModifierWithSpace(modifier: JetModifierKeywordToken) {
|
||||
addModifier(modifier)
|
||||
addAfter(JetPsiFactory(this).createWhiteSpace(), getModifierList())
|
||||
}
|
||||
|
||||
override fun move(info: MemberInfoBase<PsiMember>, substitutor: PsiSubstitutor) {
|
||||
val member = info.getMember().namedUnwrappedElement as? JetNamedDeclaration ?: return
|
||||
val markedElements = markElements(member)
|
||||
val memberCopy = member.copy() as JetNamedDeclaration
|
||||
|
||||
try {
|
||||
var movedMember: JetNamedDeclaration
|
||||
when (member) {
|
||||
is JetCallableDeclaration -> {
|
||||
val clashingSuper = fixOverrideAndGetClashingSuper(member, memberCopy as JetCallableDeclaration)
|
||||
|
||||
val originalIsAbstract = member.hasModifier(JetTokens.ABSTRACT_KEYWORD)
|
||||
val toAbstract = when {
|
||||
info.isToAbstract() -> true
|
||||
!data.targetClass.isInterface() -> false
|
||||
member is JetProperty -> member.mustBeAbstractInInterface()
|
||||
else -> false
|
||||
}
|
||||
if (toAbstract) {
|
||||
if (!originalIsAbstract) {
|
||||
makeAbstract(member, memberCopy)
|
||||
}
|
||||
|
||||
movedMember = doAddCallableMember(memberCopy, clashingSuper)
|
||||
if (member.getTypeReference() == null) {
|
||||
movedMember.getTypeReference()?.addToShorteningWaitSet()
|
||||
}
|
||||
if (originalIsAbstract) {
|
||||
member.delete()
|
||||
}
|
||||
else if (!member.hasModifier(JetTokens.OVERRIDE_KEYWORD)) {
|
||||
member.addModifierWithSpace(JetTokens.OVERRIDE_KEYWORD)
|
||||
}
|
||||
}
|
||||
else {
|
||||
movedMember = doAddCallableMember(memberCopy, clashingSuper)
|
||||
member.delete()
|
||||
}
|
||||
|
||||
if (originalIsAbstract && data.targetClass.isInterface()) {
|
||||
movedMember.removeModifier(JetTokens.ABSTRACT_KEYWORD)
|
||||
}
|
||||
|
||||
if (!data.targetClass.isInterface()
|
||||
&& !data.targetClass.hasModifier(JetTokens.ABSTRACT_KEYWORD)
|
||||
&& (movedMember.hasModifier(JetTokens.ABSTRACT_KEYWORD))) {
|
||||
data.targetClass.addModifierWithSpace(JetTokens.ABSTRACT_KEYWORD)
|
||||
}
|
||||
|
||||
movedMember.getRepresentativeLightMethod()?.let { javaData.getMovedMembers().add(it) }
|
||||
}
|
||||
|
||||
is JetClassOrObject -> {
|
||||
if (data.targetClass.isInterface()) {
|
||||
memberCopy.removeModifier(JetTokens.INNER_KEYWORD)
|
||||
}
|
||||
movedMember = addMemberToTarget(memberCopy) as JetClassOrObject
|
||||
member.delete()
|
||||
movedMember.toLightClass()?.let { javaData.getMovedMembers().add(it) }
|
||||
}
|
||||
|
||||
else -> return
|
||||
}
|
||||
|
||||
processMarkedElements(movedMember)
|
||||
}
|
||||
finally {
|
||||
clearMarking(markedElements)
|
||||
}
|
||||
}
|
||||
|
||||
override fun postProcessMember(member: PsiMember) {
|
||||
|
||||
}
|
||||
|
||||
override fun moveFieldInitializations(movedFields: LinkedHashSet<PsiField>) {
|
||||
|
||||
}
|
||||
|
||||
override fun updateUsage(element: PsiElement) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.refactoring.memberPullUp.PullUpData
|
||||
import com.intellij.refactoring.memberPullUp.PullUpHelper
|
||||
import com.intellij.refactoring.memberPullUp.PullUpHelperFactory
|
||||
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
|
||||
import org.jetbrains.kotlin.asJava.unwrapped
|
||||
import org.jetbrains.kotlin.psi.JetClass
|
||||
import org.jetbrains.kotlin.psi.JetClassOrObject
|
||||
import org.jetbrains.kotlin.psi.JetNamedDeclaration
|
||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
|
||||
public class KotlinPullUpHelperFactory : PullUpHelperFactory {
|
||||
private fun PullUpData.toKotlinPullUpData(): KotlinPullUpData? {
|
||||
if (!getSourceClass().isInheritor(getTargetClass(), true)) return null
|
||||
val sourceClass = getSourceClass().unwrapped as? JetClassOrObject ?: return null
|
||||
val targetClass = getTargetClass().unwrapped as? JetClass ?: return null
|
||||
val membersToMove = getMembersToMove()
|
||||
.map { it.namedUnwrappedElement as? JetNamedDeclaration }
|
||||
.filterNotNull()
|
||||
.sortBy { it.startOffset }
|
||||
return KotlinPullUpData(sourceClass, targetClass, membersToMove)
|
||||
}
|
||||
|
||||
override fun createPullUpHelper(data: PullUpData): PullUpHelper<*> {
|
||||
val kotlinPullUpData = data.toKotlinPullUpData() ?: return EmptyPullUpHelper
|
||||
return KotlinPullUpHelper(data, kotlinPullUpData)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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 org.jetbrains.kotlin.psi.JetProperty
|
||||
|
||||
fun JetProperty.mustBeAbstractInInterface() =
|
||||
hasInitializer() || hasDelegate() || (!hasInitializer() && !hasDelegate() && getAccessors().isEmpty())
|
||||
@@ -0,0 +1,27 @@
|
||||
// WITH_RUNTIME
|
||||
open class A
|
||||
|
||||
abstract class <caret>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 {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// WITH_RUNTIME
|
||||
abstract class A {
|
||||
// INFO: {"checked": "true"}
|
||||
val x = 1
|
||||
// INFO: {"checked": "true"}
|
||||
val y: Int get() = 2
|
||||
// INFO: {"checked": "true"}
|
||||
val z: Int by lazy { 3 }
|
||||
// INFO: {"checked": "true"}
|
||||
abstract val t: Int
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
fun foo(n: Int): Boolean = n > 0
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
abstract fun bar(s: String)
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
inner class X {
|
||||
|
||||
}
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
class Y {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
abstract class B: A() {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// WITH_RUNTIME
|
||||
open class A
|
||||
|
||||
abstract class <caret>B: 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 {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// WITH_RUNTIME
|
||||
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 val t: Int
|
||||
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
abstract fun foo(n: Int): Boolean
|
||||
|
||||
// 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() {
|
||||
// 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"}
|
||||
override fun foo(n: Int): Boolean = n > 0
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// WITH_RUNTIME
|
||||
interface I
|
||||
|
||||
interface Z<T>
|
||||
|
||||
open class A<T: I, U: I, V>
|
||||
|
||||
class C<W: I> {
|
||||
inner class <caret>B<X: I, Y>(x: X, y: Y): A<X, I, Z<Y>>() {
|
||||
// INFO: {"checked": "true"}
|
||||
fun foo<S>(x1: X, x2: Z<X>, y1: Y, y2: Z<Y>, w1: W, w2: Z<W>, s1: S, s2: Z<S>) {
|
||||
|
||||
}
|
||||
// INFO: {"checked": "true"}
|
||||
inner class Foo<S>(x1: X, x2: Z<X>, y1: Y, y2: Z<Y>, w1: W, w2: Z<W>, s1: S, s2: Z<S>): A<X, I, Z<Y>>(), Z<W> {
|
||||
|
||||
}
|
||||
// INFO: {"checked": "true"}
|
||||
val foo1: X
|
||||
// INFO: {"checked": "true"}
|
||||
val foo2: Z<X>
|
||||
// INFO: {"checked": "true"}
|
||||
val foo3: Y
|
||||
// INFO: {"checked": "true"}
|
||||
val foo4: Z<Y>
|
||||
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
val foo5 = x
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
val foo6 = object: Z<X> {}
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
val foo7 = y
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
val foo8 = object: Z<Y> {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// WITH_RUNTIME
|
||||
interface I
|
||||
|
||||
interface Z<T>
|
||||
|
||||
abstract class A<T: I, U: I, V> {
|
||||
// INFO: {"checked": "true"}
|
||||
val foo1: T
|
||||
// INFO: {"checked": "true"}
|
||||
val foo2: Z<T>
|
||||
// INFO: {"checked": "true"}
|
||||
val foo3: Any?
|
||||
// INFO: {"checked": "true"}
|
||||
val foo4: Z<Any?>
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
abstract val foo5: T
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
abstract val foo6: Z<T>
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
abstract val foo7: Any?
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
abstract val foo8: Z<Any?>
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
fun foo<S>(x1: T, x2: Z<T>, y1: Any?, y2: Z<Any?>, w1: I, w2: Z<I>, s1: S, s2: Z<S>) {
|
||||
|
||||
}
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
inner class Foo<S>(x1: T, x2: Z<T>, y1: Any?, y2: Z<Any?>, w1: I, w2: Z<I>, s1: S, s2: Z<S>): A<T, I, Z<Any?>>(), Z<I> {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class C<W: I> {
|
||||
inner class B<X: I, Y>(x: X, y: Y): A<X, I, Z<Y>>() {
|
||||
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
override val foo5 = x
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
override val foo6 = object: Z<X> {}
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
override val foo7 = y
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
override val foo8 = object: Z<Y> {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// WITH_RUNTIME
|
||||
interface T
|
||||
|
||||
abstract class <caret>B: T {
|
||||
// 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 {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// WITH_RUNTIME
|
||||
interface T {
|
||||
// INFO: {"checked": "true"}
|
||||
val x: Int
|
||||
// INFO: {"checked": "true"}
|
||||
val y: Int get() = 2
|
||||
// INFO: {"checked": "true"}
|
||||
val z: Int
|
||||
// INFO: {"checked": "true"}
|
||||
val t: Int
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
fun foo(n: Int): Boolean = n > 0
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
fun bar(s: String)
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
class X {
|
||||
|
||||
}
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
class Y {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
abstract class B: T {
|
||||
// INFO: {"checked": "true"}
|
||||
override val x = 1
|
||||
// INFO: {"checked": "true"}
|
||||
override val z: Int by lazy { 3 }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// WITH_RUNTIME
|
||||
interface T
|
||||
|
||||
abstract class <caret>B: T {
|
||||
// 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 {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// WITH_RUNTIME
|
||||
interface T {
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
val x: Int
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
val y: Int
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
val z: Int
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
val t: Int
|
||||
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
fun foo(n: Int): Boolean
|
||||
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
fun bar(s: String)
|
||||
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
class X {
|
||||
|
||||
}
|
||||
|
||||
// INFO: {"checked": "true", "toAbstract": "true"}
|
||||
class Y {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
abstract class B: T {
|
||||
// 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"}
|
||||
override fun foo(n: Int): Boolean = n > 0
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
open class A
|
||||
|
||||
class X {
|
||||
class <caret>B: A() {
|
||||
// INFO: {"checked": "true"}
|
||||
fun foo(): Int = bar() + B.bar() + X.B.bar()
|
||||
|
||||
companion object {
|
||||
fun bar() = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
open class A {
|
||||
// INFO: {"checked": "true"}
|
||||
fun foo(): Int = X.B.bar() + X.B.bar() + X.B.bar()
|
||||
}
|
||||
|
||||
class X {
|
||||
class B: A() {
|
||||
|
||||
companion object {
|
||||
fun bar() = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
@@ -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 pull members from
|
||||
@@ -0,0 +1,3 @@
|
||||
class <caret>A {
|
||||
val x: Int = 1
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
Cannot perform refactoring.
|
||||
A does not have base classes/interfaces in current project
|
||||
@@ -0,0 +1,3 @@
|
||||
fun <caret>test() {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
Cannot perform refactoring.
|
||||
Caret should be positioned inside a class to pull members from
|
||||
@@ -0,0 +1,6 @@
|
||||
interface I
|
||||
|
||||
class <caret>A: I {
|
||||
// INFO: {"checked": "true"}
|
||||
public fun foo(): Int = 1
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
interface I {
|
||||
// INFO: {"checked": "true"}
|
||||
fun foo(): Int = 1
|
||||
}
|
||||
|
||||
class A: I {
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
open class A {
|
||||
open fun bar() = 1
|
||||
}
|
||||
|
||||
class <caret>B: A() {
|
||||
override fun bar() = 1
|
||||
|
||||
// INFO: {"checked": "true"}
|
||||
fun foo() = super.bar()
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
open class A {
|
||||
open fun bar() = 1
|
||||
// INFO: {"checked": "true"}
|
||||
fun foo() = bar()
|
||||
}
|
||||
|
||||
class B: A() {
|
||||
override fun bar() = 1
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// TARGET_CLASS: A
|
||||
open class A {
|
||||
|
||||
}
|
||||
|
||||
open class B: A() {
|
||||
|
||||
}
|
||||
|
||||
class <caret>C: B() {
|
||||
// INFO: {"checked": "true"}
|
||||
val x: Int
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// TARGET_CLASS: A
|
||||
open class A {
|
||||
// INFO: {"checked": "true"}
|
||||
val x: Int
|
||||
|
||||
}
|
||||
|
||||
open class B: A() {
|
||||
|
||||
}
|
||||
|
||||
class C: B() {
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
open class A
|
||||
|
||||
class <caret>B: A() {
|
||||
// INFO: {"checked": "true"}
|
||||
private fun foo(): Int = 1
|
||||
|
||||
fun bar() = foo() + 1
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
open class A {
|
||||
// INFO: {"checked": "true"}
|
||||
protected fun foo(): Int = 1
|
||||
}
|
||||
|
||||
class B: A() {
|
||||
|
||||
fun bar() = foo() + 1
|
||||
}
|
||||
+4
-4
@@ -52,7 +52,7 @@ import org.jetbrains.kotlin.psi.JetNamedDeclaration
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import org.jetbrains.kotlin.test.JetTestUtils
|
||||
import org.jetbrains.kotlin.test.util.findElementByComment
|
||||
import org.jetbrains.kotlin.test.util.findElementByCommentPrefix
|
||||
import org.jetbrains.kotlin.utils.emptyOrSingletonList
|
||||
import java.io.File
|
||||
import java.util.Collections
|
||||
@@ -106,7 +106,7 @@ public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTe
|
||||
KotlinIntroduceParameterHandler(HelperImpl())
|
||||
}
|
||||
with (handler) {
|
||||
val target = (file as JetFile).findElementByComment("// TARGET:") as? JetNamedDeclaration
|
||||
val target = (file as JetFile).findElementByCommentPrefix("// TARGET:") as? JetNamedDeclaration
|
||||
if (target != null) {
|
||||
JetRefactoringUtil.selectExpression(fixture.getEditor(), file, true) { expression ->
|
||||
invoke(fixture.getProject(), fixture.getEditor(), expression!!, target)
|
||||
@@ -208,7 +208,7 @@ public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTe
|
||||
val extractionTarget = propertyTargets.single {
|
||||
it.name == InTextDirectivesUtils.findStringWithPrefixes(file.getText(), "// EXTRACTION_TARGET: ")
|
||||
}
|
||||
val explicitPreviousSibling = file.findElementByComment("// SIBLING:")
|
||||
val explicitPreviousSibling = file.findElementByCommentPrefix("// SIBLING:")
|
||||
val helper = object : ExtractionEngineHelper(INTRODUCE_PROPERTY) {
|
||||
override fun configureAndRun(
|
||||
project: Project,
|
||||
@@ -237,7 +237,7 @@ public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTe
|
||||
doTest(path) { file ->
|
||||
file as JetFile
|
||||
|
||||
val explicitPreviousSibling = file.findElementByComment("// SIBLING:")
|
||||
val explicitPreviousSibling = file.findElementByCommentPrefix("// SIBLING:")
|
||||
val fileText = file.getText() ?: ""
|
||||
val expectedNames = InTextDirectivesUtils.findListWithPrefixes(fileText, "// SUGGESTED_NAMES: ")
|
||||
val expectedReturnTypes = InTextDirectivesUtils.findListWithPrefixes(fileText, "// SUGGESTED_RETURN_TYPES: ")
|
||||
|
||||
@@ -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.pullUp
|
||||
|
||||
import com.google.gson.JsonParser
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.refactoring.BaseRefactoringProcessor
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil
|
||||
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture
|
||||
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
|
||||
import org.jetbrains.kotlin.idea.stubindex.JetFullClassNameIndex
|
||||
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
|
||||
import org.jetbrains.kotlin.idea.test.JetLightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.kotlin.idea.test.KotlinMultiFileTestCase
|
||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import org.jetbrains.kotlin.test.JetTestUtils
|
||||
import org.jetbrains.kotlin.test.util.findElementByCommentPrefix
|
||||
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 JetElement.elementInfo: ElementInfo
|
||||
by NotNullableUserDataProperty(Key.create("ELEMENT_INFO"), ElementInfo(false, false))
|
||||
}
|
||||
|
||||
override fun getProjectDescriptor() = LightCodeInsightFixtureTestCase.JAVA_LATEST
|
||||
|
||||
val fixture: JavaCodeInsightTestFixture get() = myFixture
|
||||
|
||||
protected override fun getTestDataPath() = PluginTestCaseBase.getTestDataPathBase()
|
||||
|
||||
protected fun doTest(path: String) {
|
||||
val mainFile = File(path)
|
||||
val afterFile = File("$path.after")
|
||||
val conflictFile = File("$path.messages")
|
||||
|
||||
fixture.setTestDataPath("${JetTestUtils.getHomeDirectory()}/${mainFile.getParent()}")
|
||||
|
||||
val mainFileName = mainFile.getName()
|
||||
val file = fixture.configureByFile(mainFileName) as JetFile
|
||||
|
||||
val addKotlinRuntime = InTextDirectivesUtils.findStringWithPrefixes(file.getText(), "// WITH_RUNTIME") != null
|
||||
if (addKotlinRuntime) {
|
||||
ConfigLibraryUtil.configureKotlinRuntimeAndSdk(myModule, PluginTestCaseBase.mockJdk())
|
||||
}
|
||||
|
||||
try {
|
||||
for ((element, info) in file.findElementsByCommentPrefix("// INFO: ")) {
|
||||
val parsedInfo = JsonParser().parse(info).getAsJsonObject()
|
||||
element.elementInfo = ElementInfo(parsedInfo["checked"]?.getAsBoolean() ?: false,
|
||||
parsedInfo["toAbstract"]?.getAsBoolean() ?: false)
|
||||
}
|
||||
val targetClassName = InTextDirectivesUtils.findStringWithPrefixes(file.getText(), "// TARGET_CLASS: ")
|
||||
|
||||
val helper = object: KotlinPullUpHandler.TestHelper {
|
||||
override fun adjustMembers(members: List<KotlinMemberInfo>): List<KotlinMemberInfo> {
|
||||
members.forEach {
|
||||
val info = it.getMember().elementInfo
|
||||
it.setChecked(info.checked)
|
||||
it.setToAbstract(info.toAbstract)
|
||||
}
|
||||
return members.filter { it.isChecked() }
|
||||
}
|
||||
|
||||
override fun chooseSuperClass(superClasses: List<JetClass>): JetClass {
|
||||
if (targetClassName != null) {
|
||||
return superClasses.single { it.getFqName()?.asString() ?: it.getName() == targetClassName }
|
||||
}
|
||||
return superClasses.first()
|
||||
}
|
||||
}
|
||||
KotlinPullUpHandler().invoke(getProject(), getEditor(), file) {
|
||||
if (it == KotlinPullUpHandler.PULLUP_TEST_HELPER_KEY) helper else null
|
||||
}
|
||||
|
||||
assert(!conflictFile.exists()) { "Conflict file $conflictFile should not exist" }
|
||||
JetTestUtils.assertEqualsToFile(afterFile, file.getText()!!)
|
||||
}
|
||||
catch(e: Exception) {
|
||||
val message = when (e) {
|
||||
is BaseRefactoringProcessor.ConflictsInTestsException -> e.getMessages().sort().joinToString("\n")
|
||||
is CommonRefactoringUtil.RefactoringErrorHintException -> e.getMessage()!!
|
||||
else -> throw e
|
||||
}
|
||||
JetTestUtils.assertEqualsToFile(conflictFile, message)
|
||||
}
|
||||
finally {
|
||||
if (addKotlinRuntime) {
|
||||
ConfigLibraryUtil.unConfigureKotlinRuntimeAndSdk(myModule, PluginTestCaseBase.mockJdk())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.pullUp;
|
||||
|
||||
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/pullUp")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class PullUpTestGenerated extends AbstractPullUpTest {
|
||||
public void testAllFilesPresentInPullUp() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentInSingleGeneratedClass(this.getClass(), new File("idea/testData/refactoring/pullUp"), Pattern.compile("^(.+)\\.kt$"));
|
||||
}
|
||||
|
||||
@TestMetadata("fromClassToClass.kt")
|
||||
public void testFromClassToClass() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/fromClassToClass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("fromClassToClassMakeAbstract.kt")
|
||||
public void testFromClassToClassMakeAbstract() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/fromClassToClassMakeAbstract.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("fromClassToClassWithGenerics.kt")
|
||||
public void testFromClassToClassWithGenerics() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/fromClassToClassWithGenerics.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("fromClassToInterface.kt")
|
||||
public void testFromClassToInterface() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/fromClassToInterface.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("fromClassToInterfaceMakeAbstract.kt")
|
||||
public void testFromClassToInterfaceMakeAbstract() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/fromClassToInterfaceMakeAbstract.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("implicitCompanionUsages.kt")
|
||||
public void testImplicitCompanionUsages() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/implicitCompanionUsages.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("noCaret.kt")
|
||||
public void testNoCaret() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/noCaret.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("noSuperClass.kt")
|
||||
public void testNoSuperClass() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/noSuperClass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("outsideOfClass.kt")
|
||||
public void testOutsideOfClass() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/outsideOfClass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("publicToInterface.kt")
|
||||
public void testPublicToInterface() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/publicToInterface.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("superToThis.kt")
|
||||
public void testSuperToThis() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/superToThis.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("toIndirectSuperClass.kt")
|
||||
public void testToIndirectSuperClass() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/toIndirectSuperClass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("usedPrivateToClass.kt")
|
||||
public void testUsedPrivateToClass() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/usedPrivateToClass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user