Minor: refactoring 'refactoring' package

This commit is contained in:
Dmitry Gridin
2019-05-13 18:08:10 +07:00
parent 98a4c4588e
commit 379152303c
11 changed files with 197 additions and 187 deletions
@@ -25,12 +25,12 @@ abstract class AbstractIntroduceAction : BasePlatformRefactoringAction() {
setInjectedContext(true) setInjectedContext(true)
} }
override final fun setInjectedContext(worksInInjected: Boolean) { final override fun setInjectedContext(worksInInjected: Boolean) {
super.setInjectedContext(worksInInjected) super.setInjectedContext(worksInInjected)
} }
override fun isAvailableInEditorOnly(): Boolean = true override fun isAvailableInEditorOnly(): Boolean = true
override fun isEnabledOnElements(elements: Array<out PsiElement>): Boolean = override fun isEnabledOnElements(elements: Array<out PsiElement>): Boolean =
elements.all { it is KtElement } elements.all { it is KtElement }
} }
@@ -33,14 +33,14 @@ import org.jetbrains.kotlin.psi.psiUtil.isIdentifier
import org.jetbrains.kotlin.psi.psiUtil.quoteIfNeeded import org.jetbrains.kotlin.psi.psiUtil.quoteIfNeeded
import java.awt.BorderLayout import java.awt.BorderLayout
abstract class AbstractKotlinInplaceIntroducer<D: KtNamedDeclaration>( abstract class AbstractKotlinInplaceIntroducer<D : KtNamedDeclaration>(
localVariable: D?, localVariable: D?,
expression: KtExpression?, expression: KtExpression?,
occurrences: Array<KtExpression>, occurrences: Array<KtExpression>,
title: String, title: String,
project: Project, project: Project,
editor: Editor editor: Editor
): AbstractInplaceIntroducer<D, KtExpression>(project, editor, expression, localVariable, occurrences, title, KotlinFileType.INSTANCE) { ) : AbstractInplaceIntroducer<D, KtExpression>(project, editor, expression, localVariable, occurrences, title, KotlinFileType.INSTANCE) {
protected fun initFormComponents(init: FormBuilder.() -> Unit) { protected fun initFormComponents(init: FormBuilder.() -> Unit) {
myWholePanel.layout = BorderLayout() myWholePanel.layout = BorderLayout()
@@ -58,8 +58,7 @@ abstract class AbstractKotlinInplaceIntroducer<D: KtNamedDeclaration>(
// myExprMarker was invalidated by stopIntroduce() // myExprMarker was invalidated by stopIntroduce()
myExprMarker = myExpr?.let { createMarker(it) } myExprMarker = myExpr?.let { createMarker(it) }
startInplaceIntroduceTemplate() startInplaceIntroduceTemplate()
} } finally {
finally {
myEditor.putUserData(InplaceRefactoring.INTRODUCE_RESTART, false) myEditor.putUserData(InplaceRefactoring.INTRODUCE_RESTART, false)
} }
} }
@@ -74,10 +73,10 @@ abstract class AbstractKotlinInplaceIntroducer<D: KtNamedDeclaration>(
override fun getActionName(): String? = null override fun getActionName(): String? = null
override fun restoreExpression( override fun restoreExpression(
containingFile: PsiFile, containingFile: PsiFile,
declaration: D, declaration: D,
marker: RangeMarker, marker: RangeMarker,
exprText: String? exprText: String?
): KtExpression? { ): KtExpression? {
if (exprText == null || !declaration.isValid) return null if (exprText == null || !declaration.isValid) return null
@@ -89,8 +88,8 @@ abstract class AbstractKotlinInplaceIntroducer<D: KtNamedDeclaration>(
val occurrenceExprText = (myExpr as? KtProperty)?.name ?: exprText val occurrenceExprText = (myExpr as? KtProperty)?.name ?: exprText
return leaf return leaf
.getNonStrictParentOfType<KtSimpleNameExpression>() .getNonStrictParentOfType<KtSimpleNameExpression>()
?.replaced(KtPsiFactory(myProject).createExpression(occurrenceExprText)) ?.replaced(KtPsiFactory(myProject).createExpression(occurrenceExprText))
} }
override fun updateTitle(declaration: D?) = updateTitle(declaration, null) override fun updateTitle(declaration: D?) = updateTitle(declaration, null)
@@ -72,23 +72,23 @@ import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier
import java.util.* import java.util.*
data class ExtractSuperInfo( data class ExtractSuperInfo(
val originalClass: KtClassOrObject, val originalClass: KtClassOrObject,
val memberInfos: Collection<KotlinMemberInfo>, val memberInfos: Collection<KotlinMemberInfo>,
val targetParent: PsiElement, val targetParent: PsiElement,
val targetFileName: String, val targetFileName: String,
val newClassName: String, val newClassName: String,
val isInterface: Boolean, val isInterface: Boolean,
val docPolicy: DocCommentPolicy<*> val docPolicy: DocCommentPolicy<*>
) )
class ExtractSuperRefactoring( class ExtractSuperRefactoring(
private var extractInfo: ExtractSuperInfo private var extractInfo: ExtractSuperInfo
) { ) {
companion object { companion object {
private fun getElementsToMove( private fun getElementsToMove(
memberInfos: Collection<KotlinMemberInfo>, memberInfos: Collection<KotlinMemberInfo>,
originalClass: KtClassOrObject, originalClass: KtClassOrObject,
isExtractInterface: Boolean isExtractInterface: Boolean
): Map<KtElement, KotlinMemberInfo?> { ): Map<KtElement, KotlinMemberInfo?> {
val project = originalClass.project val project = originalClass.project
val elementsToMove = LinkedHashMap<KtElement, KotlinMemberInfo?>() val elementsToMove = LinkedHashMap<KtElement, KotlinMemberInfo?>()
@@ -98,8 +98,7 @@ class ExtractSuperRefactoring(
val member = memberInfo.member ?: continue val member = memberInfo.member ?: continue
if (memberInfo.isSuperClass) { if (memberInfo.isSuperClass) {
superInterfacesToMove += member superInterfacesToMove += member
} } else {
else {
elementsToMove[member] = memberInfo elementsToMove[member] = memberInfo
} }
} }
@@ -107,8 +106,9 @@ class ExtractSuperRefactoring(
val superTypeList = originalClass.getSuperTypeList() val superTypeList = originalClass.getSuperTypeList()
if (superTypeList != null) { if (superTypeList != null) {
for (superTypeListEntry in originalClass.superTypeListEntries) { for (superTypeListEntry in originalClass.superTypeListEntries) {
val superType = superTypeListEntry.analyze(BodyResolveMode.PARTIAL)[BindingContext.TYPE, superTypeListEntry.typeReference] val superType =
?: continue superTypeListEntry.analyze(BodyResolveMode.PARTIAL)[BindingContext.TYPE, superTypeListEntry.typeReference]
?: continue
val superClassDescriptor = superType.constructor.declarationDescriptor ?: continue val superClassDescriptor = superType.constructor.declarationDescriptor ?: continue
val superClass = DescriptorToSourceUtilsIde.getAnyDeclaration(project, superClassDescriptor) as? KtClass ?: continue val superClass = DescriptorToSourceUtilsIde.getAnyDeclaration(project, superClassDescriptor) as? KtClass ?: continue
if ((!isExtractInterface && !superClass.isInterface()) || superClass in superInterfacesToMove) { if ((!isExtractInterface && !superClass.isInterface()) || superClass in superInterfacesToMove) {
@@ -121,11 +121,11 @@ class ExtractSuperRefactoring(
} }
fun collectConflicts( fun collectConflicts(
originalClass: KtClassOrObject, originalClass: KtClassOrObject,
memberInfos: List<KotlinMemberInfo>, memberInfos: List<KotlinMemberInfo>,
targetParent: PsiElement, targetParent: PsiElement,
newClassName: String, newClassName: String,
isExtractInterface: Boolean isExtractInterface: Boolean
): MultiMap<PsiElement, String> { ): MultiMap<PsiElement, String> {
val conflicts = MultiMap<PsiElement, String>() val conflicts = MultiMap<PsiElement, String>()
@@ -134,9 +134,9 @@ class ExtractSuperRefactoring(
if (targetParent is KtElement) { if (targetParent is KtElement) {
val targetSibling = originalClass.parentsWithSelf.first { it.parent == targetParent } as KtElement val targetSibling = originalClass.parentsWithSelf.first { it.parent == targetParent } as KtElement
targetSibling.getResolutionScope() targetSibling.getResolutionScope()
.findClassifier(Name.identifier(newClassName), NoLookupLocation.FROM_IDE) .findClassifier(Name.identifier(newClassName), NoLookupLocation.FROM_IDE)
?.let { DescriptorToSourceUtilsIde.getAnyDeclaration(project, it) } ?.let { DescriptorToSourceUtilsIde.getAnyDeclaration(project, it) }
?.let { conflicts.putValue(it, "Class $newClassName already exists in the target scope") } ?.let { conflicts.putValue(it, "Class $newClassName already exists in the target scope") }
} }
val elementsToMove = getElementsToMove(memberInfos, originalClass, isExtractInterface).keys val elementsToMove = getElementsToMove(memberInfos, originalClass, isExtractInterface).keys
@@ -144,16 +144,15 @@ class ExtractSuperRefactoring(
val moveTarget = if (targetParent is PsiDirectory) { val moveTarget = if (targetParent is PsiDirectory) {
val targetPackage = targetParent.getPackage() ?: return conflicts val targetPackage = targetParent.getPackage() ?: return conflicts
KotlinMoveTargetForDeferredFile(FqName(targetPackage.qualifiedName), targetParent) { null } KotlinMoveTargetForDeferredFile(FqName(targetPackage.qualifiedName), targetParent) { null }
} } else {
else {
KotlinMoveTargetForExistingElement(targetParent as KtElement) KotlinMoveTargetForExistingElement(targetParent as KtElement)
} }
val conflictChecker = MoveConflictChecker( val conflictChecker = MoveConflictChecker(
project, project,
elementsToMove, elementsToMove,
moveTarget, moveTarget,
originalClass, originalClass,
memberInfos.asSequence().filter { it.isToAbstract }.mapNotNull { it.member }.toList() memberInfos.asSequence().filter { it.isToAbstract }.mapNotNull { it.member }.toList()
) )
project.runSynchronouslyWithProgress(RefactoringBundle.message("detecting.possible.conflicts"), true) { project.runSynchronouslyWithProgress(RefactoringBundle.message("detecting.possible.conflicts"), true) {
@@ -163,11 +162,11 @@ class ExtractSuperRefactoring(
ReferencesSearch.search(element).mapTo(usages) { MoveRenameUsageInfo(it, element) } ReferencesSearch.search(element).mapTo(usages) { MoveRenameUsageInfo(it, element) }
if (element is KtCallableDeclaration) { if (element is KtCallableDeclaration) {
element.toLightMethods().flatMapTo(usages) { element.toLightMethods().flatMapTo(usages) {
MethodReferencesSearch.search(it).map { MoveRenameUsageInfo(it, element) } MethodReferencesSearch.search(it).map { reference -> MoveRenameUsageInfo(reference, element) }
} }
} }
} }
conflictChecker.checkAllConflicts(usages, LinkedHashSet<UsageInfo>(), conflicts) conflictChecker.checkAllConflicts(usages, LinkedHashSet(), conflicts)
if (targetParent is PsiDirectory) { if (targetParent is PsiDirectory) {
ExtractSuperClassUtil.checkSuperAccessible(targetParent, conflicts, originalClass.toLightClass()) ExtractSuperClassUtil.checkSuperAccessible(targetParent, conflicts, originalClass.toLightClass())
} }
@@ -190,11 +189,11 @@ class ExtractSuperRefactoring(
if (refTarget is KtTypeParameter && refTarget.getStrictParentOfType<KtTypeParameterListOwner>() == extractInfo.originalClass) { if (refTarget is KtTypeParameter && refTarget.getStrictParentOfType<KtTypeParameterListOwner>() == extractInfo.originalClass) {
typeParameters += refTarget typeParameters += refTarget
refTarget.accept( refTarget.accept(
object : KtTreeVisitorVoid() { object : KtTreeVisitorVoid() {
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) { override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
(expression.mainReference.resolve() as? KtTypeParameter)?.let { typeParameters += it } (expression.mainReference.resolve() as? KtTypeParameter)?.let { typeParameters += it }
}
} }
}
) )
} }
} }
@@ -207,12 +206,12 @@ class ExtractSuperRefactoring(
} }
} }
getElementsToMove(extractInfo.memberInfos, extractInfo.originalClass, extractInfo.isInterface) getElementsToMove(extractInfo.memberInfos, extractInfo.originalClass, extractInfo.isInterface)
.asSequence() .asSequence()
.flatMap { .flatMap {
val (element, info) = it val (element, info) = it
info?.getChildrenToAnalyze()?.asSequence() ?: sequenceOf(element) info?.getChildrenToAnalyze()?.asSequence() ?: sequenceOf(element)
} }
.forEach { it.accept(visitor) } .forEach { it.accept(visitor) }
} }
private fun createClass(superClassEntry: KtSuperTypeListEntry?): KtClass? { private fun createClass(superClassEntry: KtSuperTypeListEntry?): KtClass? {
@@ -228,8 +227,7 @@ class ExtractSuperRefactoring(
NewKotlinFileAction.createFileFromTemplate(extractInfo.targetFileName, template, targetParent) ?: return null NewKotlinFileAction.createFileFromTemplate(extractInfo.targetFileName, template, targetParent) ?: return null
} }
file.add(prototype) as KtClass file.add(prototype) as KtClass
} } else {
else {
val targetSibling = originalClass.parentsWithSelf.first { it.parent == targetParent } val targetSibling = originalClass.parentsWithSelf.first { it.parent == targetParent }
insertDeclaration(prototype, targetSibling) insertDeclaration(prototype, targetSibling)
} }
@@ -256,13 +254,12 @@ class ExtractSuperRefactoring(
} }
} }
val needSuperCall = !extractInfo.isInterface val needSuperCall = !extractInfo.isInterface
&& (superClassEntry is KtSuperTypeCallEntry && (superClassEntry is KtSuperTypeCallEntry
|| originalClass.hasPrimaryConstructor() || originalClass.hasPrimaryConstructor()
|| originalClass.secondaryConstructors.isEmpty()) || originalClass.secondaryConstructors.isEmpty())
val newSuperTypeListEntry = if (needSuperCall) { val newSuperTypeListEntry = if (needSuperCall) {
psiFactory.createSuperTypeCallEntry("$superTypeText()") psiFactory.createSuperTypeCallEntry("$superTypeText()")
} } else {
else {
psiFactory.createSuperTypeEntry(superTypeText) psiFactory.createSuperTypeEntry(superTypeText)
} }
if (superClassEntry != null) { if (superClassEntry != null) {
@@ -271,12 +268,10 @@ class ExtractSuperRefactoring(
} }
val superClassEntryToAdd = if (qualifiedTypeRefText != null) { val superClassEntryToAdd = if (qualifiedTypeRefText != null) {
superClassEntry.copied().apply { typeReference?.replace(psiFactory.createType(qualifiedTypeRefText)) } superClassEntry.copied().apply { typeReference?.replace(psiFactory.createType(qualifiedTypeRefText)) }
} } else superClassEntry
else superClassEntry
newClass.addSuperTypeListEntry(superClassEntryToAdd) newClass.addSuperTypeListEntry(superClassEntryToAdd)
ShortenReferences.DEFAULT.process(superClassEntry.replaced(newSuperTypeListEntry)) ShortenReferences.DEFAULT.process(superClassEntry.replaced(newSuperTypeListEntry))
} } else {
else {
ShortenReferences.DEFAULT.process(originalClass.addSuperTypeListEntry(newSuperTypeListEntry)) ShortenReferences.DEFAULT.process(originalClass.addSuperTypeListEntry(newSuperTypeListEntry))
} }
@@ -297,8 +292,7 @@ class ExtractSuperRefactoring(
originalClass.superTypeListEntries.firstOrNull { originalClass.superTypeListEntries.firstOrNull {
bindingContext[BindingContext.TYPE, it.typeReference]?.constructor?.declarationDescriptor == superClassDescriptor bindingContext[BindingContext.TYPE, it.typeReference]?.constructor?.declarationDescriptor == superClassDescriptor
} }
} } else null
else null
project.runSynchronouslyWithProgress(RefactoringBundle.message("progress.text"), true) { runReadAction { analyzeContext() } } project.runSynchronouslyWithProgress(RefactoringBundle.message("progress.text"), true) { runReadAction { analyzeContext() } }
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtClassOrObject
object KotlinExtractInterfaceHandler : KotlinExtractSuperHandlerBase(true) { object KotlinExtractInterfaceHandler : KotlinExtractSuperHandlerBase(true) {
val REFACTORING_NAME = "Extract Interface" const val REFACTORING_NAME = "Extract Interface"
override fun getErrorMessage(klass: KtClassOrObject): String? { override fun getErrorMessage(klass: KtClassOrObject): String? {
val superMessage = super.getErrorMessage(klass) val superMessage = super.getErrorMessage(klass)
@@ -33,9 +33,9 @@ object KotlinExtractInterfaceHandler : KotlinExtractSuperHandlerBase(true) {
override fun createDialog(klass: KtClassOrObject, targetParent: PsiElement) = override fun createDialog(klass: KtClassOrObject, targetParent: PsiElement) =
KotlinExtractInterfaceDialog( KotlinExtractInterfaceDialog(
originalClass = klass, originalClass = klass,
targetParent = targetParent, targetParent = targetParent,
conflictChecker = { checkConflicts(klass, it) }, conflictChecker = { checkConflicts(klass, it) },
refactoring = { ExtractSuperRefactoring(it).performRefactoring() } refactoring = { ExtractSuperRefactoring(it).performRefactoring() }
) )
} }
@@ -68,11 +68,11 @@ abstract class KotlinExtractSuperHandlerBase(private val isExtractInterface: Boo
getErrorMessage(klass)?.let { getErrorMessage(klass)?.let {
CommonRefactoringUtil.showErrorHint( CommonRefactoringUtil.showErrorHint(
project, project,
editor, editor,
RefactoringBundle.getCannotRefactorMessage(it), RefactoringBundle.getCannotRefactorMessage(it),
KotlinExtractSuperclassHandler.REFACTORING_NAME, KotlinExtractSuperclassHandler.REFACTORING_NAME,
HelpID.EXTRACT_SUPERCLASS HelpID.EXTRACT_SUPERCLASS
) )
return false return false
} }
@@ -90,22 +90,22 @@ abstract class KotlinExtractSuperHandlerBase(private val isExtractInterface: Boo
if (editor == null) return doInvoke(klass, containers.first()) if (editor == null) return doInvoke(klass, containers.first())
chooseContainerElementIfNecessary( chooseContainerElementIfNecessary(
containers, containers,
editor, editor,
if (containers.first() is KtFile) "Select target file" else "Select target code block / file", if (containers.first() is KtFile) "Select target file" else "Select target code block / file",
true, true,
{ it }, { it },
{ doInvoke(klass, if (it is SeparateFileWrapper) klass.containingFile.parent!! else it) } { doInvoke(klass, if (it is SeparateFileWrapper) klass.containingFile.parent!! else it) }
) )
} }
protected fun checkConflicts(originalClass: KtClassOrObject, dialog: KotlinExtractSuperDialogBase): Boolean { protected fun checkConflicts(originalClass: KtClassOrObject, dialog: KotlinExtractSuperDialogBase): Boolean {
val conflicts = ExtractSuperRefactoring.collectConflicts( val conflicts = ExtractSuperRefactoring.collectConflicts(
originalClass, originalClass,
dialog.selectedMembers, dialog.selectedMembers,
dialog.selectedTargetParent, dialog.selectedTargetParent,
dialog.extractedSuperName, dialog.extractedSuperName,
isExtractInterface isExtractInterface
) )
return ExtractSuperClassUtil.showConflicts(dialog, conflicts, originalClass.project) return ExtractSuperClassUtil.showConflicts(dialog, conflicts, originalClass.project)
} }
@@ -23,7 +23,7 @@ import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtClassOrObject
object KotlinExtractSuperclassHandler : KotlinExtractSuperHandlerBase(false) { object KotlinExtractSuperclassHandler : KotlinExtractSuperHandlerBase(false) {
val REFACTORING_NAME = "Extract Superclass" const val REFACTORING_NAME = "Extract Superclass"
override fun getErrorMessage(klass: KtClassOrObject): String? { override fun getErrorMessage(klass: KtClassOrObject): String? {
val superMessage = super.getErrorMessage(klass) val superMessage = super.getErrorMessage(klass)
@@ -38,9 +38,9 @@ object KotlinExtractSuperclassHandler : KotlinExtractSuperHandlerBase(false) {
override fun createDialog(klass: KtClassOrObject, targetParent: PsiElement) = override fun createDialog(klass: KtClassOrObject, targetParent: PsiElement) =
KotlinExtractSuperclassDialog( KotlinExtractSuperclassDialog(
originalClass = klass, originalClass = klass,
targetParent = targetParent, targetParent = targetParent,
conflictChecker = { checkConflicts(klass, it) }, conflictChecker = { checkConflicts(klass, it) },
refactoring = { ExtractSuperRefactoring(it).performRefactoring() } refactoring = { ExtractSuperRefactoring(it).performRefactoring() }
) )
} }
@@ -36,13 +36,20 @@ import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
class KotlinExtractInterfaceDialog( class KotlinExtractInterfaceDialog(
originalClass: KtClassOrObject, originalClass: KtClassOrObject,
targetParent: PsiElement, targetParent: PsiElement,
conflictChecker: (KotlinExtractSuperDialogBase) -> Boolean, conflictChecker: (KotlinExtractSuperDialogBase) -> Boolean,
refactoring: (ExtractSuperInfo) -> Unit refactoring: (ExtractSuperInfo) -> Unit
) : KotlinExtractSuperDialogBase(originalClass, targetParent, conflictChecker, true, KotlinExtractInterfaceHandler.REFACTORING_NAME, refactoring) { ) : KotlinExtractSuperDialogBase(
originalClass,
targetParent,
conflictChecker,
true,
KotlinExtractInterfaceHandler.REFACTORING_NAME,
refactoring
) {
companion object { companion object {
private val DESTINATION_PACKAGE_RECENT_KEY = "KotlinExtractInterfaceDialog.RECENT_KEYS" private const val DESTINATION_PACKAGE_RECENT_KEY = "KotlinExtractInterfaceDialog.RECENT_KEYS"
} }
init { init {
@@ -44,12 +44,12 @@ import java.awt.BorderLayout
import javax.swing.* import javax.swing.*
abstract class KotlinExtractSuperDialogBase( abstract class KotlinExtractSuperDialogBase(
protected val originalClass: KtClassOrObject, protected val originalClass: KtClassOrObject,
protected val targetParent: PsiElement, protected val targetParent: PsiElement,
private val conflictChecker: (KotlinExtractSuperDialogBase) -> Boolean, private val conflictChecker: (KotlinExtractSuperDialogBase) -> Boolean,
private val isExtractInterface: Boolean, private val isExtractInterface: Boolean,
refactoringName: String, refactoringName: String,
private val refactoring: (ExtractSuperInfo) -> Unit private val refactoring: (ExtractSuperInfo) -> Unit
) : JavaExtractSuperBaseDialog(originalClass.project, originalClass.toLightClass()!!, emptyList(), refactoringName) { ) : JavaExtractSuperBaseDialog(originalClass.project, originalClass.toLightClass()!!, emptyList(), refactoringName) {
private var initComplete: Boolean = false private var initComplete: Boolean = false
@@ -61,10 +61,15 @@ abstract class KotlinExtractSuperDialogBase(
private val fileNameField = JTextField() private val fileNameField = JTextField()
open class MemberInfoModelBase( open class MemberInfoModelBase(
originalClass: KtClassOrObject, originalClass: KtClassOrObject,
val memberInfos: List<KotlinMemberInfo>, val memberInfos: List<KotlinMemberInfo>,
interfaceContainmentVerifier: (KtNamedDeclaration) -> Boolean interfaceContainmentVerifier: (KtNamedDeclaration) -> Boolean
) : KotlinUsesAndInterfacesDependencyMemberInfoModel<KtNamedDeclaration, KotlinMemberInfo>(originalClass, null, false, interfaceContainmentVerifier) { ) : KotlinUsesAndInterfacesDependencyMemberInfoModel<KtNamedDeclaration, KotlinMemberInfo>(
originalClass,
null,
false,
interfaceContainmentVerifier
) {
override fun isMemberEnabled(member: KotlinMemberInfo): Boolean { override fun isMemberEnabled(member: KotlinMemberInfo): Boolean {
val declaration = member.member ?: return false val declaration = member.member ?: return false
return !declaration.hasModifier(KtTokens.CONST_KEYWORD) return !declaration.hasModifier(KtTokens.CONST_KEYWORD)
@@ -73,8 +78,8 @@ abstract class KotlinExtractSuperDialogBase(
override fun isAbstractEnabled(memberInfo: KotlinMemberInfo): Boolean { override fun isAbstractEnabled(memberInfo: KotlinMemberInfo): Boolean {
val member = memberInfo.member val member = memberInfo.member
return !(member.hasModifier(KtTokens.INLINE_KEYWORD) || return !(member.hasModifier(KtTokens.INLINE_KEYWORD) ||
member.hasModifier(KtTokens.EXTERNAL_KEYWORD) || member.hasModifier(KtTokens.EXTERNAL_KEYWORD) ||
member.hasModifier(KtTokens.LATEINIT_KEYWORD)) member.hasModifier(KtTokens.LATEINIT_KEYWORD))
} }
override fun isFixedAbstract(memberInfo: KotlinMemberInfo?) = true override fun isFixedAbstract(memberInfo: KotlinMemberInfo?) = true
@@ -140,9 +145,9 @@ abstract class KotlinExtractSuperDialogBase(
return JPanel(BorderLayout()).apply { return JPanel(BorderLayout()).apply {
val memberSelectionPanel = KotlinMemberSelectionPanel( val memberSelectionPanel = KotlinMemberSelectionPanel(
RefactoringBundle.message(if (isExtractInterface) "members.to.form.interface" else "members.to.form.superclass"), RefactoringBundle.message(if (isExtractInterface) "members.to.form.interface" else "members.to.form.superclass"),
memberInfoModel.memberInfos, memberInfoModel.memberInfos,
RefactoringBundle.message("make.abstract") RefactoringBundle.message("make.abstract")
) )
memberSelectionPanel.table.memberInfoModel = memberInfoModel memberSelectionPanel.table.memberInfoModel = memberInfoModel
memberSelectionPanel.table.addMemberInfoChangeListener(memberInfoModel) memberSelectionPanel.table.addMemberInfoChangeListener(memberInfoModel)
@@ -178,13 +183,13 @@ abstract class KotlinExtractSuperDialogBase(
override fun executeRefactoring() { override fun executeRefactoring() {
val extractInfo = ExtractSuperInfo( val extractInfo = ExtractSuperInfo(
mySourceClass.unwrapped as KtClassOrObject, mySourceClass.unwrapped as KtClassOrObject,
selectedMembers, selectedMembers,
if (targetParent is PsiDirectory) targetDirectory else targetParent, if (targetParent is PsiDirectory) targetDirectory else targetParent,
targetFileName, targetFileName,
extractedSuperName.quoteIfNeeded(), extractedSuperName.quoteIfNeeded(),
isExtractInterface, isExtractInterface,
DocCommentPolicy<PsiComment>(docCommentPolicy) DocCommentPolicy<PsiComment>(docCommentPolicy)
) )
refactoring(extractInfo) refactoring(extractInfo)
} }
@@ -31,13 +31,20 @@ import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.KtProperty
class KotlinExtractSuperclassDialog( class KotlinExtractSuperclassDialog(
originalClass: KtClassOrObject, originalClass: KtClassOrObject,
targetParent: PsiElement, targetParent: PsiElement,
conflictChecker: (KotlinExtractSuperDialogBase) -> Boolean, conflictChecker: (KotlinExtractSuperDialogBase) -> Boolean,
refactoring: (ExtractSuperInfo) -> Unit refactoring: (ExtractSuperInfo) -> Unit
) : KotlinExtractSuperDialogBase(originalClass, targetParent, conflictChecker, false, KotlinExtractSuperclassHandler.REFACTORING_NAME, refactoring) { ) : KotlinExtractSuperDialogBase(
originalClass,
targetParent,
conflictChecker,
false,
KotlinExtractSuperclassHandler.REFACTORING_NAME,
refactoring
) {
companion object { companion object {
private val DESTINATION_PACKAGE_RECENT_KEY = "KotlinExtractSuperclassDialog.RECENT_KEYS" private const val DESTINATION_PACKAGE_RECENT_KEY = "KotlinExtractSuperclassDialog.RECENT_KEYS"
} }
init { init {
@@ -46,9 +53,9 @@ class KotlinExtractSuperclassDialog(
override fun createMemberInfoModel(): MemberInfoModelBase { override fun createMemberInfoModel(): MemberInfoModelBase {
return object : MemberInfoModelBase( return object : MemberInfoModelBase(
originalClass, originalClass,
extractClassMembers(originalClass), extractClassMembers(originalClass),
getInterfaceContainmentVerifier { selectedMembers } getInterfaceContainmentVerifier { selectedMembers }
) { ) {
override fun isAbstractEnabled(memberInfo: KotlinMemberInfo): Boolean { override fun isAbstractEnabled(memberInfo: KotlinMemberInfo): Boolean {
if (!super.isAbstractEnabled(memberInfo)) return false if (!super.isAbstractEnabled(memberInfo)) return false
@@ -42,19 +42,19 @@ fun showErrorHintByKey(project: Project, editor: Editor, messageKey: String, tit
} }
fun selectElementsWithTargetSibling( fun selectElementsWithTargetSibling(
operationName: String, operationName: String,
editor: Editor, editor: Editor,
file: KtFile, file: KtFile,
title: String, title: String,
elementKinds: Collection<CodeInsightUtils.ElementKind>, elementKinds: Collection<CodeInsightUtils.ElementKind>,
elementValidator: (List<PsiElement>) -> String?, elementValidator: (List<PsiElement>) -> String?,
getContainers: (elements: List<PsiElement>, commonParent: PsiElement) -> List<PsiElement>, getContainers: (elements: List<PsiElement>, commonParent: PsiElement) -> List<PsiElement>,
continuation: (elements: List<PsiElement>, targetSibling: PsiElement) -> Unit continuation: (elements: List<PsiElement>, targetSibling: PsiElement) -> Unit
) { ) {
fun onSelectionComplete(elements: List<PsiElement>, targetContainer: PsiElement) { fun onSelectionComplete(elements: List<PsiElement>, targetContainer: PsiElement) {
val physicalElements = elements.map { it.substringContextOrThis } val physicalElements = elements.map { it.substringContextOrThis }
val parent = PsiTreeUtil.findCommonParent(physicalElements) val parent = PsiTreeUtil.findCommonParent(physicalElements)
?: throw AssertionError("Should have at least one parent: ${physicalElements.joinToString("\n")}") ?: throw AssertionError("Should have at least one parent: ${physicalElements.joinToString("\n")}")
if (parent == targetContainer) { if (parent == targetContainer) {
continuation(elements, physicalElements.first()) continuation(elements, physicalElements.first())
@@ -74,14 +74,14 @@ fun selectElementsWithTargetSibling(
} }
fun selectElementsWithTargetParent( fun selectElementsWithTargetParent(
operationName: String, operationName: String,
editor: Editor, editor: Editor,
file: KtFile, file: KtFile,
title: String, title: String,
elementKinds: Collection<CodeInsightUtils.ElementKind>, elementKinds: Collection<CodeInsightUtils.ElementKind>,
elementValidator: (List<PsiElement>) -> String?, elementValidator: (List<PsiElement>) -> String?,
getContainers: (elements: List<PsiElement>, commonParent: PsiElement) -> List<PsiElement>, getContainers: (elements: List<PsiElement>, commonParent: PsiElement) -> List<PsiElement>,
continuation: (elements: List<PsiElement>, targetParent: PsiElement) -> Unit continuation: (elements: List<PsiElement>, targetParent: PsiElement) -> Unit
) { ) {
fun showErrorHintByKey(key: String) { fun showErrorHintByKey(key: String) {
showErrorHintByKey(file.project, editor, key, operationName) showErrorHintByKey(file.project, editor, key, operationName)
@@ -95,7 +95,7 @@ fun selectElementsWithTargetParent(
val physicalElements = elements.map { it.substringContextOrThis } val physicalElements = elements.map { it.substringContextOrThis }
val parent = PsiTreeUtil.findCommonParent(physicalElements) val parent = PsiTreeUtil.findCommonParent(physicalElements)
?: throw AssertionError("Should have at least one parent: ${physicalElements.joinToString("\n")}") ?: throw AssertionError("Should have at least one parent: ${physicalElements.joinToString("\n")}")
val containers = getContainers(physicalElements, parent) val containers = getContainers(physicalElements, parent)
if (containers.isEmpty()) { if (containers.isEmpty()) {
@@ -104,12 +104,12 @@ fun selectElementsWithTargetParent(
} }
chooseContainerElementIfNecessary( chooseContainerElementIfNecessary(
containers, containers,
editor, editor,
title, title,
true, true,
{ it }, { it },
{ continuation(elements, it) } { continuation(elements, it) }
) )
} }
@@ -133,8 +133,7 @@ fun selectElementsWithTargetParent(
selectElement(editor, file, false, elementKinds) { expr -> selectElement(editor, file, false, elementKinds) { expr ->
if (expr != null) { if (expr != null) {
selectTargetContainer(listOf(expr)) selectTargetContainer(listOf(expr))
} } else {
else {
if (!editor.selectionModel.hasSelection()) { if (!editor.selectionModel.hasSelection()) {
if (elementKinds.singleOrNull() == CodeInsightUtils.ElementKind.EXPRESSION) { if (elementKinds.singleOrNull() == CodeInsightUtils.ElementKind.EXPRESSION) {
val elementAtCaret = file.findElementAt(editor.caretModel.offset) val elementAtCaret = file.findElementAt(editor.caretModel.offset)
@@ -221,7 +220,7 @@ fun ExtractableSubstringInfo.replaceWith(replacement: KtExpression): KtExpressio
fun KtExpression.mustBeParenthesizedInInitializerPosition(): Boolean { fun KtExpression.mustBeParenthesizedInInitializerPosition(): Boolean {
if (this !is KtBinaryExpression) return false if (this !is KtBinaryExpression) return false
if (left?.mustBeParenthesizedInInitializerPosition() ?: false) return true if (left?.mustBeParenthesizedInInitializerPosition() == true) return true
return PsiChildRange(left, operationReference).any { (it is PsiWhiteSpace) && it.textContains('\n') } return PsiChildRange(left, operationReference).any { (it is PsiWhiteSpace) && it.textContains('\n') }
} }
@@ -36,9 +36,9 @@ import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes
import java.util.* import java.util.*
class KotlinMemberInfoStorage( class KotlinMemberInfoStorage(
classOrObject: KtClassOrObject, classOrObject: KtClassOrObject,
filter: (KtNamedDeclaration) -> Boolean = { true } filter: (KtNamedDeclaration) -> Boolean = { true }
): AbstractMemberInfoStorage<KtNamedDeclaration, PsiNamedElement, KotlinMemberInfo>(classOrObject, filter) { ) : AbstractMemberInfoStorage<KtNamedDeclaration, PsiNamedElement, KotlinMemberInfo>(classOrObject, filter) {
override fun memberConflict(member1: KtNamedDeclaration, member: KtNamedDeclaration): Boolean { override fun memberConflict(member1: KtNamedDeclaration, member: KtNamedDeclaration): Boolean {
val descriptor1 = member1.resolveToDescriptorWrapperAware() val descriptor1 = member1.resolveToDescriptorWrapperAware()
val descriptor = member.resolveToDescriptorWrapperAware() val descriptor = member.resolveToDescriptorWrapperAware()
@@ -50,7 +50,7 @@ class KotlinMemberInfoStorage(
!overloadUtil.isOverloadable(descriptor1, descriptor) !overloadUtil.isOverloadable(descriptor1, descriptor)
} }
descriptor1 is PropertyDescriptor && descriptor is PropertyDescriptor || descriptor1 is PropertyDescriptor && descriptor is PropertyDescriptor ||
descriptor1 is ClassDescriptor && descriptor is ClassDescriptor -> true descriptor1 is ClassDescriptor && descriptor is ClassDescriptor -> true
else -> false else -> false
} }
} }
@@ -81,14 +81,14 @@ class KotlinMemberInfoStorage(
} }
fun extractClassMembers( fun extractClassMembers(
aClass: KtClassOrObject, aClass: KtClassOrObject,
collectSuperTypeEntries: Boolean = true, collectSuperTypeEntries: Boolean = true,
filter: ((KtNamedDeclaration) -> Boolean)? = null filter: ((KtNamedDeclaration) -> Boolean)? = null
): List<KotlinMemberInfo> { ): List<KotlinMemberInfo> {
fun KtClassOrObject.extractFromClassBody( fun KtClassOrObject.extractFromClassBody(
filter: ((KtNamedDeclaration) -> Boolean)?, filter: ((KtNamedDeclaration) -> Boolean)?,
isCompanion: Boolean, isCompanion: Boolean,
result: MutableCollection<KotlinMemberInfo> result: MutableCollection<KotlinMemberInfo>
) { ) {
declarations declarations
.asSequence() .asSequence()
@@ -98,7 +98,7 @@ fun extractClassMembers(
&& !(it is KtObjectDeclaration && it.isCompanion()) && !(it is KtObjectDeclaration && it.isCompanion())
&& (filter == null || filter(it)) && (filter == null || filter(it))
} }
.mapTo(result) { KotlinMemberInfo(it as KtNamedDeclaration, isCompanionMember = isCompanion) } .mapTo(result) { KotlinMemberInfo(it as KtNamedDeclaration, isCompanionMember = isCompanion) }
} }
val result = ArrayList<KotlinMemberInfo>() val result = ArrayList<KotlinMemberInfo>()
@@ -107,26 +107,25 @@ fun extractClassMembers(
aClass.superTypeListEntries aClass.superTypeListEntries
.asSequence() .asSequence()
.filterIsInstance<KtSuperTypeEntry>() .filterIsInstance<KtSuperTypeEntry>()
.mapNotNull { .mapNotNull {
val typeReference = it.typeReference ?: return@mapNotNull null val typeReference = it.typeReference ?: return@mapNotNull null
val type = typeReference.analyze(BodyResolveMode.PARTIAL)[BindingContext.TYPE, typeReference] val type = typeReference.analyze(BodyResolveMode.PARTIAL)[BindingContext.TYPE, typeReference]
val classDescriptor = type?.constructor?.declarationDescriptor as? ClassDescriptor val classDescriptor = type?.constructor?.declarationDescriptor as? ClassDescriptor
val classPsi = classDescriptor?.source?.getPsi() when (val classPsi = classDescriptor?.source?.getPsi()) {
when (classPsi) { is KtClass -> classPsi
is KtClass -> classPsi is PsiClass -> KtPsiClassWrapper(classPsi)
is PsiClass -> KtPsiClassWrapper(classPsi) else -> null
else -> null
}
} }
.filter { it.isInterfaceClass() } }
.mapTo(result) { KotlinMemberInfo(it, true) } .filter { it.isInterfaceClass() }
.mapTo(result) { KotlinMemberInfo(it, true) }
} }
aClass.primaryConstructor aClass.primaryConstructor
?.valueParameters ?.valueParameters
?.asSequence() ?.asSequence()
?.filter { it.hasValOrVar() } ?.filter { it.hasValOrVar() }
?.mapTo(result) { KotlinMemberInfo(it) } ?.mapTo(result) { KotlinMemberInfo(it) }
aClass.extractFromClassBody(filter, false, result) aClass.extractFromClassBody(filter, false, result)
(aClass as? KtClass)?.companionObjects?.firstOrNull()?.extractFromClassBody(filter, true, result) (aClass as? KtClass)?.companionObjects?.firstOrNull()?.extractFromClassBody(filter, true, result)