Extract Class: Implement 'Extract Interface' refactoring
#KT-11017 Fixed
This commit is contained in:
@@ -144,6 +144,7 @@ These artifacts include extensions for the types available in the latter JDKs, s
|
||||
|
||||
- [`KT-13155`](https://youtrack.jetbrains.com/issue/KT-13155) Implement "Introduce Type Parameter" refactoring
|
||||
- [`KT-11017`](https://youtrack.jetbrains.com/issue/KT-11017) Implement "Extract Superclass" refactoring
|
||||
- [`KT-11017`](https://youtrack.jetbrains.com/issue/KT-11017) Implement "Extract Interface" refactoring
|
||||
|
||||
#### Android Lint
|
||||
|
||||
|
||||
@@ -799,6 +799,7 @@ fun main(args: Array<String>) {
|
||||
model("refactoring/introduceTypeParameter", pattern = KT_OR_KTS, testMethod = "doIntroduceTypeParameterTest")
|
||||
model("refactoring/introduceTypeAlias", pattern = KT_OR_KTS, testMethod = "doIntroduceTypeAliasTest")
|
||||
model("refactoring/extractSuperclass", pattern = KT_OR_KTS, testMethod = "doExtractSuperclassTest")
|
||||
model("refactoring/extractInterface", pattern = KT_OR_KTS, testMethod = "doExtractInterfaceTest")
|
||||
}
|
||||
|
||||
testClass<AbstractPullUpTest>() {
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiNameIdentifierOwner
|
||||
import com.intellij.refactoring.RefactoringActionHandler
|
||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinChangeSignatureHandler
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractClass.KotlinExtractInterfaceHandler
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractFunction.ExtractKotlinFunctionHandler
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractClass.KotlinExtractSuperclassHandler
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceParameter.KotlinIntroduceLambdaParameterHandler
|
||||
@@ -81,6 +82,8 @@ class KotlinRefactoringSupportProvider : RefactoringSupportProvider() {
|
||||
override fun getPushDownHandler() = KotlinPushDownHandler()
|
||||
|
||||
override fun getExtractSuperClassHandler() = KotlinExtractSuperclassHandler
|
||||
|
||||
override fun getExtractInterfaceHandler() = KotlinExtractInterfaceHandler
|
||||
}
|
||||
|
||||
class KotlinVetoRenameCondition: Condition<PsiElement> {
|
||||
|
||||
+33
-23
@@ -68,22 +68,24 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier
|
||||
import java.util.*
|
||||
|
||||
data class ExtractSuperclassInfo(
|
||||
data class ExtractSuperInfo(
|
||||
val originalClass: KtClassOrObject,
|
||||
val memberInfos: Collection<KotlinMemberInfo>,
|
||||
val targetParent: PsiElement,
|
||||
val targetFileName: String,
|
||||
val newClassName: String,
|
||||
val isInterface: Boolean,
|
||||
val docPolicy: DocCommentPolicy<*>
|
||||
)
|
||||
|
||||
class ExtractSuperclassRefactoring(
|
||||
private var extractInfo: ExtractSuperclassInfo
|
||||
class ExtractSuperRefactoring(
|
||||
private var extractInfo: ExtractSuperInfo
|
||||
) {
|
||||
companion object {
|
||||
private fun getElementsToMove(
|
||||
memberInfos: Collection<KotlinMemberInfo>,
|
||||
originalClass: KtClassOrObject
|
||||
originalClass: KtClassOrObject,
|
||||
isExtractInterface: Boolean
|
||||
): Map<KtElement, KotlinMemberInfo?> {
|
||||
val project = originalClass.project
|
||||
val elementsToMove = LinkedHashMap<KtElement, KotlinMemberInfo?>()
|
||||
@@ -106,7 +108,7 @@ class ExtractSuperclassRefactoring(
|
||||
?: continue
|
||||
val superClassDescriptor = superType.constructor.declarationDescriptor ?: continue
|
||||
val superClass = DescriptorToSourceUtilsIde.getAnyDeclaration(project, superClassDescriptor) as? KtClass ?: continue
|
||||
if (!superClass.isInterface() || superClass in superInterfacesToMove) {
|
||||
if ((!isExtractInterface && !superClass.isInterface()) || superClass in superInterfacesToMove) {
|
||||
elementsToMove[superTypeListEntry] = null
|
||||
}
|
||||
}
|
||||
@@ -119,7 +121,8 @@ class ExtractSuperclassRefactoring(
|
||||
originalClass: KtClassOrObject,
|
||||
memberInfos: List<KotlinMemberInfo>,
|
||||
targetParent: PsiElement,
|
||||
newClassName: String
|
||||
newClassName: String,
|
||||
isExtractInterface: Boolean
|
||||
): MultiMap<PsiElement, String> {
|
||||
val conflicts = MultiMap<PsiElement, String>()
|
||||
|
||||
@@ -133,7 +136,7 @@ class ExtractSuperclassRefactoring(
|
||||
?.let { conflicts.putValue(it, "Class $newClassName already exists in the target scope") }
|
||||
}
|
||||
|
||||
val elementsToMove = getElementsToMove(memberInfos, originalClass).keys
|
||||
val elementsToMove = getElementsToMove(memberInfos, originalClass, isExtractInterface).keys
|
||||
|
||||
val moveTarget = if (targetParent is PsiDirectory) {
|
||||
val targetPackage = targetParent.getPackage() ?: return conflicts
|
||||
@@ -192,7 +195,7 @@ class ExtractSuperclassRefactoring(
|
||||
collectTypeParameters(refTarget)
|
||||
}
|
||||
}
|
||||
getElementsToMove(extractInfo.memberInfos, extractInfo.originalClass)
|
||||
getElementsToMove(extractInfo.memberInfos, extractInfo.originalClass, extractInfo.isInterface)
|
||||
.asSequence()
|
||||
.flatMap {
|
||||
val (element, info) = it
|
||||
@@ -206,18 +209,22 @@ class ExtractSuperclassRefactoring(
|
||||
val newClassName = extractInfo.newClassName
|
||||
val originalClass = extractInfo.originalClass
|
||||
|
||||
val kind = if (extractInfo.isInterface) "interface" else "class"
|
||||
val prototype = psiFactory.createClass("$kind $newClassName")
|
||||
val newClass = if (targetParent is PsiDirectory) {
|
||||
val template = FileTemplateManager.getInstance(project).getInternalTemplate("Kotlin File")
|
||||
val newFile = NewKotlinFileAction.createFileFromTemplate(extractInfo.targetFileName, template, targetParent) as KtFile
|
||||
newFile.add(psiFactory.createClass("class $newClassName")) as KtClass
|
||||
newFile.add(prototype) as KtClass
|
||||
}
|
||||
else {
|
||||
val targetSibling = originalClass.parentsWithSelf.first { it.parent == targetParent }
|
||||
insertDeclaration(psiFactory.createClass("class $newClassName"), targetSibling)
|
||||
insertDeclaration(prototype, targetSibling)
|
||||
}
|
||||
|
||||
val shouldBeAbstract = extractInfo.memberInfos.any { it.isToAbstract }
|
||||
newClass.addModifier(if (shouldBeAbstract) KtTokens.ABSTRACT_KEYWORD else KtTokens.OPEN_KEYWORD)
|
||||
if (!extractInfo.isInterface) {
|
||||
newClass.addModifier(if (shouldBeAbstract) KtTokens.ABSTRACT_KEYWORD else KtTokens.OPEN_KEYWORD)
|
||||
}
|
||||
|
||||
if (typeParameters.isNotEmpty()) {
|
||||
val typeParameterListText = typeParameters.sortedBy { it.startOffset }.map { it.text }.joinToString(prefix = "<", postfix = ">")
|
||||
@@ -235,10 +242,11 @@ class ExtractSuperclassRefactoring(
|
||||
append(typeParameters.sortedBy { it.startOffset }.map { it.name }.joinToString(prefix = "<", postfix = ">"))
|
||||
}
|
||||
}
|
||||
val needSuperCall = superClassEntry is KtSuperTypeCallEntry
|
||||
val needSuperCall = !extractInfo.isInterface
|
||||
&& (superClassEntry is KtSuperTypeCallEntry
|
||||
|| originalClass.hasPrimaryConstructor()
|
||||
|| originalClass.getSecondaryConstructors().isEmpty()
|
||||
val newSuperTypeCallEntry = if (needSuperCall) {
|
||||
|| originalClass.getSecondaryConstructors().isEmpty())
|
||||
val newSuperTypeListEntry = if (needSuperCall) {
|
||||
psiFactory.createSuperTypeCallEntry("$superTypeText()")
|
||||
}
|
||||
else {
|
||||
@@ -253,10 +261,10 @@ class ExtractSuperclassRefactoring(
|
||||
}
|
||||
else superClassEntry
|
||||
newClass.addSuperTypeListEntry(superClassEntryToAdd)
|
||||
ShortenReferences.DEFAULT.process(superClassEntry.replaced(newSuperTypeCallEntry))
|
||||
ShortenReferences.DEFAULT.process(superClassEntry.replaced(newSuperTypeListEntry))
|
||||
}
|
||||
else {
|
||||
ShortenReferences.DEFAULT.process(originalClass.addSuperTypeListEntry(newSuperTypeCallEntry))
|
||||
ShortenReferences.DEFAULT.process(originalClass.addSuperTypeListEntry(newSuperTypeListEntry))
|
||||
}
|
||||
|
||||
ShortenReferences.DEFAULT.process(newClass)
|
||||
@@ -267,15 +275,17 @@ class ExtractSuperclassRefactoring(
|
||||
fun performRefactoring() {
|
||||
val originalClass = extractInfo.originalClass
|
||||
|
||||
KotlinExtractSuperclassHandler.getErrorMessage(originalClass)?.let {
|
||||
throw CommonRefactoringUtil.RefactoringErrorHintException(it)
|
||||
}
|
||||
val handler = if (extractInfo.isInterface) KotlinExtractInterfaceHandler else KotlinExtractSuperclassHandler
|
||||
handler.getErrorMessage(originalClass)?.let { throw CommonRefactoringUtil.RefactoringErrorHintException(it) }
|
||||
|
||||
val originalClassDescriptor = originalClass.resolveToDescriptor() as ClassDescriptor
|
||||
val superClassDescriptor = originalClassDescriptor.getSuperClassNotAny()
|
||||
val superClassEntry = originalClass.getSuperTypeListEntries().firstOrNull {
|
||||
bindingContext[BindingContext.TYPE, it.typeReference]?.constructor?.declarationDescriptor == superClassDescriptor
|
||||
val superClassEntry = if (!extractInfo.isInterface) {
|
||||
val originalClassDescriptor = originalClass.resolveToDescriptor() as ClassDescriptor
|
||||
val superClassDescriptor = originalClassDescriptor.getSuperClassNotAny()
|
||||
originalClass.getSuperTypeListEntries().firstOrNull {
|
||||
bindingContext[BindingContext.TYPE, it.typeReference]?.constructor?.declarationDescriptor == superClassDescriptor
|
||||
}
|
||||
}
|
||||
else null
|
||||
|
||||
project.runSynchronouslyWithProgress(RefactoringBundle.message("progress.text"), true) { runReadAction { analyzeContext() } }
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.introduce.extractClass
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractClass.ui.KotlinExtractInterfaceDialog
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
|
||||
object KotlinExtractInterfaceHandler : KotlinExtractSuperHandlerBase(true) {
|
||||
val REFACTORING_NAME = "Extract Interface"
|
||||
|
||||
override fun getErrorMessage(klass: KtClassOrObject): String? {
|
||||
if (klass is KtClass && klass.isAnnotation()) return "Interface cannot be extracted from an annotation class"
|
||||
return null
|
||||
}
|
||||
|
||||
override fun doInvoke(klass: KtClassOrObject, targetParent: PsiElement, project: Project, editor: Editor?) {
|
||||
if (!CommonRefactoringUtil.checkReadOnlyStatus(project, klass)) return
|
||||
|
||||
KotlinExtractInterfaceDialog(
|
||||
originalClass = klass,
|
||||
targetParent = targetParent,
|
||||
conflictChecker = { checkConflicts(klass, it) },
|
||||
refactoring = { ExtractSuperRefactoring(it).performRefactoring() }
|
||||
).show()
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.introduce.extractClass
|
||||
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.ScrollType
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.refactoring.RefactoringActionHandler
|
||||
import com.intellij.refactoring.extractSuperclass.ExtractSuperClassUtil
|
||||
import com.intellij.refactoring.lang.ElementsHandler
|
||||
import org.jetbrains.kotlin.idea.refactoring.SeparateFileWrapper
|
||||
import org.jetbrains.kotlin.idea.refactoring.chooseContainerElementIfNecessary
|
||||
import org.jetbrains.kotlin.idea.refactoring.getExtractionContainers
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractClass.ui.KotlinExtractSuperDialogBase
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||
|
||||
abstract class KotlinExtractSuperHandlerBase(private val isExtractInterface: Boolean) : RefactoringActionHandler, ElementsHandler {
|
||||
override fun isEnabledOnElements(elements: Array<out PsiElement>) = elements.singleOrNull() is KtClassOrObject
|
||||
|
||||
override fun invoke(project: Project, editor: Editor, file: PsiFile, dataContext: DataContext?) {
|
||||
val offset = editor.caretModel.offset
|
||||
val element = file.findElementAt(offset) ?: return
|
||||
val klass = element.getNonStrictParentOfType<KtClassOrObject>() ?: return
|
||||
editor.scrollingModel.scrollToCaret(ScrollType.MAKE_VISIBLE)
|
||||
selectElements(klass, project, editor)
|
||||
}
|
||||
|
||||
override fun invoke(project: Project, elements: Array<out PsiElement>, dataContext: DataContext?) {
|
||||
if (dataContext == null) return
|
||||
val editor = CommonDataKeys.EDITOR.getData(dataContext)
|
||||
val klass = PsiTreeUtil.findCommonParent(*elements)?.getNonStrictParentOfType<KtClassOrObject>() ?: return
|
||||
selectElements(klass, project, editor)
|
||||
}
|
||||
|
||||
fun selectElements(klass: KtClassOrObject, project: Project, editor: Editor?) {
|
||||
val containers = klass.getExtractionContainers(strict = true, includeAll = true) + SeparateFileWrapper(klass.manager)
|
||||
|
||||
if (editor == null) return doInvoke(klass, containers.first(), project, editor)
|
||||
|
||||
chooseContainerElementIfNecessary(
|
||||
containers,
|
||||
editor,
|
||||
if (containers.first() is KtFile) "Select target file" else "Select target code block / file",
|
||||
true,
|
||||
{ it },
|
||||
{ doInvoke(klass, if (it is SeparateFileWrapper) klass.containingFile.parent!! else it, project, editor) }
|
||||
)
|
||||
}
|
||||
|
||||
protected fun checkConflicts(originalClass: KtClassOrObject, dialog: KotlinExtractSuperDialogBase): Boolean {
|
||||
val conflicts = ExtractSuperRefactoring.collectConflicts(
|
||||
originalClass,
|
||||
dialog.selectedMembers,
|
||||
dialog.selectedTargetParent,
|
||||
dialog.extractedSuperName,
|
||||
isExtractInterface
|
||||
)
|
||||
return ExtractSuperClassUtil.showConflicts(dialog, conflicts, originalClass.project)
|
||||
}
|
||||
|
||||
internal abstract fun getErrorMessage(klass: KtClassOrObject): String?
|
||||
|
||||
protected abstract fun doInvoke(klass: KtClassOrObject, targetParent: PsiElement, project: Project, editor: Editor?)
|
||||
}
|
||||
+4
-61
@@ -16,65 +16,20 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.refactoring.introduce.extractClass
|
||||
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.ScrollType
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.refactoring.HelpID
|
||||
import com.intellij.refactoring.RefactoringActionHandler
|
||||
import com.intellij.refactoring.RefactoringBundle
|
||||
import com.intellij.refactoring.extractSuperclass.ExtractSuperClassUtil
|
||||
import com.intellij.refactoring.lang.ElementsHandler
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil
|
||||
import org.jetbrains.kotlin.idea.refactoring.SeparateFileWrapper
|
||||
import org.jetbrains.kotlin.idea.refactoring.chooseContainerElementIfNecessary
|
||||
import org.jetbrains.kotlin.idea.refactoring.getExtractionContainers
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractClass.ui.KotlinExtractSuperclassDialog
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||
|
||||
object KotlinExtractSuperclassHandler : RefactoringActionHandler, ElementsHandler {
|
||||
object KotlinExtractSuperclassHandler : KotlinExtractSuperHandlerBase(false) {
|
||||
val REFACTORING_NAME = "Extract Superclass"
|
||||
|
||||
override fun isEnabledOnElements(elements: Array<out PsiElement>) = elements.singleOrNull() is KtClassOrObject
|
||||
|
||||
override fun invoke(project: Project, editor: Editor, file: PsiFile, dataContext: DataContext?) {
|
||||
val offset = editor.caretModel.offset
|
||||
val element = file.findElementAt(offset) ?: return
|
||||
val klass = element.getNonStrictParentOfType<KtClassOrObject>() ?: return
|
||||
editor.scrollingModel.scrollToCaret(ScrollType.MAKE_VISIBLE)
|
||||
selectElements(klass, project, editor)
|
||||
}
|
||||
|
||||
override fun invoke(project: Project, elements: Array<out PsiElement>, dataContext: DataContext?) {
|
||||
if (dataContext == null) return
|
||||
val editor = CommonDataKeys.EDITOR.getData(dataContext)
|
||||
val klass = PsiTreeUtil.findCommonParent(*elements)?.getNonStrictParentOfType<KtClassOrObject>() ?: return
|
||||
selectElements(klass, project, editor)
|
||||
}
|
||||
|
||||
fun selectElements(klass: KtClassOrObject, project: Project, editor: Editor?) {
|
||||
val containers = klass.getExtractionContainers(strict = true, includeAll = true) + SeparateFileWrapper(klass.manager)
|
||||
|
||||
if (editor == null) return doInvoke(klass, containers.first(), project, editor)
|
||||
|
||||
chooseContainerElementIfNecessary(
|
||||
containers,
|
||||
editor,
|
||||
if (containers.first() is KtFile) "Select target file" else "Select target code block / file",
|
||||
true,
|
||||
{ it },
|
||||
{ doInvoke(klass, it, project, editor) }
|
||||
)
|
||||
}
|
||||
|
||||
fun getErrorMessage(klass: KtClassOrObject): String? {
|
||||
override fun getErrorMessage(klass: KtClassOrObject): String? {
|
||||
if (klass is KtClass) {
|
||||
if (klass.isInterface()) return RefactoringBundle.message("superclass.cannot.be.extracted.from.an.interface")
|
||||
if (klass.isEnum()) return RefactoringBundle.message("superclass.cannot.be.extracted.from.an.enum")
|
||||
@@ -83,30 +38,18 @@ object KotlinExtractSuperclassHandler : RefactoringActionHandler, ElementsHandle
|
||||
return null
|
||||
}
|
||||
|
||||
private fun checkConflicts(originalClass: KtClassOrObject, dialog: KotlinExtractSuperclassDialog): Boolean {
|
||||
val conflicts = ExtractSuperclassRefactoring.collectConflicts(
|
||||
originalClass,
|
||||
dialog.selectedMembers,
|
||||
dialog.selectedTargetParent,
|
||||
dialog.extractedSuperName
|
||||
)
|
||||
return ExtractSuperClassUtil.showConflicts(dialog, conflicts, originalClass.project)
|
||||
}
|
||||
|
||||
private fun doInvoke(klass: KtClassOrObject, container: PsiElement, project: Project, editor: Editor?) {
|
||||
override fun doInvoke(klass: KtClassOrObject, targetParent: PsiElement, project: Project, editor: Editor?) {
|
||||
if (!CommonRefactoringUtil.checkReadOnlyStatus(project, klass)) return
|
||||
|
||||
getErrorMessage(klass)?.let {
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, RefactoringBundle.getCannotRefactorMessage(it), REFACTORING_NAME, HelpID.EXTRACT_SUPERCLASS)
|
||||
}
|
||||
|
||||
val targetParent = (if (container is SeparateFileWrapper) klass.containingFile.parent else container) ?: return
|
||||
|
||||
KotlinExtractSuperclassDialog(
|
||||
originalClass = klass,
|
||||
targetParent = targetParent,
|
||||
conflictChecker = { checkConflicts(klass, it) },
|
||||
refactoring = { ExtractSuperclassRefactoring(it).performRefactoring() }
|
||||
refactoring = { ExtractSuperRefactoring(it).performRefactoring() }
|
||||
).show()
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.introduce.extractClass.ui
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.refactoring.HelpID
|
||||
import com.intellij.refactoring.JavaRefactoringSettings
|
||||
import com.intellij.refactoring.RefactoringBundle
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractClass.ExtractSuperInfo
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractClass.KotlinExtractInterfaceHandler
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.extractClassMembers
|
||||
import org.jetbrains.kotlin.idea.refactoring.pullUp.mustBeAbstractInInterface
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||
import org.jetbrains.kotlin.psi.KtProperty
|
||||
|
||||
class KotlinExtractInterfaceDialog(
|
||||
originalClass: KtClassOrObject,
|
||||
targetParent: PsiElement,
|
||||
conflictChecker: (KotlinExtractSuperDialogBase) -> Boolean,
|
||||
refactoring: (ExtractSuperInfo) -> Unit
|
||||
) : KotlinExtractSuperDialogBase(originalClass, targetParent, conflictChecker, true, KotlinExtractInterfaceHandler.REFACTORING_NAME, refactoring) {
|
||||
companion object {
|
||||
private val DESTINATION_PACKAGE_RECENT_KEY = "KotlinExtractInterfaceDialog.RECENT_KEYS"
|
||||
}
|
||||
|
||||
init {
|
||||
init()
|
||||
}
|
||||
|
||||
override fun createMemberInfoModel(): MemberInfoModelBase {
|
||||
val extractableMemberInfos = extractClassMembers(originalClass).filterNot {
|
||||
val member = it.member
|
||||
member is KtClass && member.hasModifier(KtTokens.INNER_KEYWORD)
|
||||
}
|
||||
extractableMemberInfos.forEach { it.isToAbstract = true }
|
||||
return object : MemberInfoModelBase(extractableMemberInfos) {
|
||||
override fun isAbstractEnabled(memberInfo: KotlinMemberInfo): Boolean {
|
||||
val member = memberInfo.member
|
||||
return member is KtNamedFunction || (member is KtProperty && !member.mustBeAbstractInInterface())
|
||||
}
|
||||
|
||||
override fun isAbstractWhenDisabled(member: KotlinMemberInfo) = member.member is KtProperty
|
||||
}
|
||||
}
|
||||
|
||||
override fun getDestinationPackageRecentKey() = DESTINATION_PACKAGE_RECENT_KEY
|
||||
|
||||
override fun getClassNameLabelText() = RefactoringBundle.message("interface.name.prompt")!!
|
||||
|
||||
override fun getPackageNameLabelText() = RefactoringBundle.message("package.for.new.interface")!!
|
||||
|
||||
override fun getEntityName() = RefactoringBundle.message("extractSuperInterface.interface")!!
|
||||
|
||||
override fun getTopLabelText() = RefactoringBundle.message("extract.interface.from")!!
|
||||
|
||||
override fun getDocCommentPolicySetting() = JavaRefactoringSettings.getInstance().EXTRACT_INTERFACE_JAVADOC
|
||||
|
||||
override fun setDocCommentPolicySetting(policy: Int) {
|
||||
JavaRefactoringSettings.getInstance().EXTRACT_INTERFACE_JAVADOC = policy
|
||||
}
|
||||
|
||||
override fun getExtractedSuperNameNotSpecifiedMessage() = RefactoringBundle.message("no.interface.name.specified")!!
|
||||
|
||||
override fun getHelpId() = HelpID.EXTRACT_INTERFACE
|
||||
|
||||
override fun createExtractedSuperNameField() = super.createExtractedSuperNameField()!!.apply { text = "I${originalClass.name}" }
|
||||
}
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.introduce.extractClass.ui
|
||||
|
||||
import com.intellij.psi.PsiComment
|
||||
import com.intellij.psi.PsiDirectory
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.refactoring.RefactoringBundle
|
||||
import com.intellij.refactoring.classMembers.AbstractMemberInfoModel
|
||||
import com.intellij.refactoring.classMembers.MemberInfoChange
|
||||
import com.intellij.refactoring.extractSuperclass.JavaExtractSuperBaseDialog
|
||||
import com.intellij.refactoring.util.DocCommentPolicy
|
||||
import com.intellij.refactoring.util.RefactoringMessageUtil
|
||||
import org.jetbrains.kotlin.asJava.toLightClass
|
||||
import org.jetbrains.kotlin.asJava.unwrapped
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
|
||||
import org.jetbrains.kotlin.idea.core.quoteIfNeeded
|
||||
import org.jetbrains.kotlin.idea.core.unquote
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractClass.ExtractSuperInfo
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberSelectionPanel
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
||||
import java.awt.BorderLayout
|
||||
import javax.swing.Box
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JPanel
|
||||
import javax.swing.JTextField
|
||||
|
||||
abstract class KotlinExtractSuperDialogBase(
|
||||
protected val originalClass: KtClassOrObject,
|
||||
protected val targetParent: PsiElement,
|
||||
private val conflictChecker: (KotlinExtractSuperDialogBase) -> Boolean,
|
||||
private val isExtractInterface: Boolean,
|
||||
refactoringName: String,
|
||||
private val refactoring: (ExtractSuperInfo) -> Unit
|
||||
) : JavaExtractSuperBaseDialog(originalClass.project, originalClass.toLightClass()!!, emptyList(), refactoringName) {
|
||||
private lateinit var memberInfoModel: MemberInfoModelBase
|
||||
|
||||
val selectedMembers: List<KotlinMemberInfo>
|
||||
get() = memberInfoModel.memberInfos.filter { it.isChecked }
|
||||
|
||||
private val fileNameField = JTextField()
|
||||
|
||||
open class MemberInfoModelBase(
|
||||
val memberInfos: List<KotlinMemberInfo>
|
||||
) : AbstractMemberInfoModel<KtNamedDeclaration, KotlinMemberInfo>() {
|
||||
override fun isFixedAbstract(memberInfo: KotlinMemberInfo?) = true
|
||||
}
|
||||
|
||||
val selectedTargetParent: PsiElement
|
||||
get() = if (targetParent is PsiDirectory) targetDirectory else targetParent
|
||||
|
||||
val targetFileName: String
|
||||
get() = fileNameField.text
|
||||
|
||||
protected abstract fun createMemberInfoModel(): MemberInfoModelBase
|
||||
|
||||
override fun getDocCommentPanelName() = "KDoc for abstracts"
|
||||
|
||||
override fun checkConflicts() = conflictChecker(this)
|
||||
|
||||
override fun createActionComponent() = Box.createHorizontalBox()!!
|
||||
|
||||
override fun createDestinationRootPanel() = if (targetParent is PsiDirectory) super.createDestinationRootPanel() else null
|
||||
|
||||
override fun createNorthPanel(): JComponent? {
|
||||
return super.createNorthPanel().apply {
|
||||
if (targetParent !is PsiDirectory) {
|
||||
myPackageNameLabel.parent.remove(myPackageNameLabel)
|
||||
myPackageNameField.parent.remove(myPackageNameField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun createCenterPanel(): JComponent? {
|
||||
memberInfoModel = createMemberInfoModel().apply {
|
||||
memberInfoChanged(MemberInfoChange(memberInfos))
|
||||
}
|
||||
|
||||
return JPanel(BorderLayout()).apply {
|
||||
val memberSelectionPanel = KotlinMemberSelectionPanel(
|
||||
RefactoringBundle.message(if (isExtractInterface) "members.to.form.interface" else "members.to.form.superclass"),
|
||||
memberInfoModel.memberInfos,
|
||||
RefactoringBundle.message("make.abstract")
|
||||
)
|
||||
memberSelectionPanel.table.memberInfoModel = memberInfoModel
|
||||
memberSelectionPanel.table.addMemberInfoChangeListener(memberInfoModel)
|
||||
add(memberSelectionPanel, BorderLayout.CENTER)
|
||||
|
||||
add(myDocCommentPanel, BorderLayout.EAST)
|
||||
}
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
|
||||
fileNameField.text = "$extractedSuperName.${KotlinFileType.EXTENSION}"
|
||||
}
|
||||
|
||||
override fun preparePackage() {
|
||||
if (targetParent is PsiDirectory) super.preparePackage()
|
||||
}
|
||||
|
||||
override fun isExtractSuperclass() = true
|
||||
|
||||
override fun validateName(name: String): String? {
|
||||
return when {
|
||||
!KotlinNameSuggester.isIdentifier(name.quoteIfNeeded()) -> RefactoringMessageUtil.getIncorrectIdentifierMessage(name)
|
||||
name.unquote() == mySourceClass.name -> "Different name expected"
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
override fun createProcessor() = null
|
||||
|
||||
override fun executeRefactoring() {
|
||||
val extractInfo = ExtractSuperInfo(
|
||||
mySourceClass.unwrapped as KtClassOrObject,
|
||||
selectedMembers,
|
||||
if (targetParent is PsiDirectory) targetDirectory else targetParent,
|
||||
targetFileName,
|
||||
extractedSuperName.quoteIfNeeded(),
|
||||
isExtractInterface,
|
||||
DocCommentPolicy<PsiComment>(docCommentPolicy)
|
||||
)
|
||||
refactoring(extractInfo)
|
||||
}
|
||||
}
|
||||
+13
-142
@@ -16,81 +16,39 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.refactoring.introduce.extractClass.ui
|
||||
|
||||
import com.intellij.psi.PsiComment
|
||||
import com.intellij.psi.PsiDirectory
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.refactoring.HelpID
|
||||
import com.intellij.refactoring.JavaRefactoringSettings
|
||||
import com.intellij.refactoring.RefactoringBundle
|
||||
import com.intellij.refactoring.classMembers.AbstractMemberInfoModel
|
||||
import com.intellij.refactoring.classMembers.MemberInfoChange
|
||||
import com.intellij.refactoring.extractSuperclass.ExtractSuperBaseDialog
|
||||
import com.intellij.refactoring.extractSuperclass.JavaExtractSuperBaseDialog
|
||||
import com.intellij.refactoring.util.DocCommentPolicy
|
||||
import com.intellij.refactoring.util.RefactoringMessageUtil
|
||||
import com.intellij.ui.components.JBLabel
|
||||
import com.intellij.util.ui.FormBuilder
|
||||
import org.jetbrains.kotlin.asJava.toLightClass
|
||||
import org.jetbrains.kotlin.asJava.unwrapped
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
|
||||
import org.jetbrains.kotlin.idea.core.quoteIfNeeded
|
||||
import org.jetbrains.kotlin.idea.core.unquote
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractClass.ExtractSuperclassInfo
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractClass.ExtractSuperInfo
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractClass.KotlinExtractSuperclassHandler
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberSelectionPanel
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.extractClassMembers
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||
import org.jetbrains.kotlin.psi.KtProperty
|
||||
import java.awt.BorderLayout
|
||||
import javax.swing.*
|
||||
|
||||
class KotlinExtractSuperclassDialog(
|
||||
originalClass: KtClassOrObject,
|
||||
private val targetParent: PsiElement,
|
||||
private val conflictChecker: (KotlinExtractSuperclassDialog) -> Boolean,
|
||||
private val refactoring: (ExtractSuperclassInfo) -> Unit
|
||||
) : JavaExtractSuperBaseDialog(
|
||||
originalClass.project,
|
||||
originalClass.toLightClass()!!,
|
||||
emptyList(),
|
||||
KotlinExtractSuperclassHandler.REFACTORING_NAME
|
||||
) {
|
||||
targetParent: PsiElement,
|
||||
conflictChecker: (KotlinExtractSuperDialogBase) -> Boolean,
|
||||
refactoring: (ExtractSuperInfo) -> Unit
|
||||
) : KotlinExtractSuperDialogBase(originalClass, targetParent, conflictChecker, false, KotlinExtractSuperclassHandler.REFACTORING_NAME, refactoring) {
|
||||
companion object {
|
||||
private val DESTINATION_PACKAGE_RECENT_KEY = "KotlinExtractSuperclassDialog.RECENT_KEYS"
|
||||
}
|
||||
|
||||
val kotlinMemberInfos = extractClassMembers(originalClass)
|
||||
|
||||
val selectedMembers: List<KotlinMemberInfo>
|
||||
get() = kotlinMemberInfos.filter { it.isChecked }
|
||||
|
||||
private val fileNameField = JTextField()
|
||||
|
||||
private val memberInfoModel = object : AbstractMemberInfoModel<KtNamedDeclaration, KotlinMemberInfo>() {
|
||||
override fun isFixedAbstract(memberInfo: KotlinMemberInfo?) = true
|
||||
|
||||
override fun isAbstractEnabled(memberInfo: KotlinMemberInfo): Boolean {
|
||||
val member = memberInfo.member
|
||||
return member is KtNamedFunction || member is KtProperty
|
||||
}
|
||||
}.apply {
|
||||
memberInfoChanged(MemberInfoChange(kotlinMemberInfos))
|
||||
}
|
||||
|
||||
val selectedTargetParent: PsiElement
|
||||
get() = if (targetParent is PsiDirectory) targetDirectory else targetParent
|
||||
|
||||
val targetFileName: String
|
||||
get() = fileNameField.text
|
||||
|
||||
init {
|
||||
init()
|
||||
}
|
||||
|
||||
fileNameField.text = "$extractedSuperName.${KotlinFileType.EXTENSION}"
|
||||
override fun createMemberInfoModel(): MemberInfoModelBase {
|
||||
return object : MemberInfoModelBase(extractClassMembers(originalClass)) {
|
||||
override fun isAbstractEnabled(memberInfo: KotlinMemberInfo): Boolean {
|
||||
val member = memberInfo.member
|
||||
return member is KtNamedFunction || member is KtProperty
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getDestinationPackageRecentKey() = DESTINATION_PACKAGE_RECENT_KEY
|
||||
@@ -109,94 +67,7 @@ class KotlinExtractSuperclassDialog(
|
||||
JavaRefactoringSettings.getInstance().EXTRACT_SUPERCLASS_JAVADOC = policy
|
||||
}
|
||||
|
||||
override fun getDocCommentPanelName() = "KDoc for abstracts"
|
||||
|
||||
override fun getExtractedSuperNameNotSpecifiedMessage() = RefactoringBundle.message("no.superclass.name.specified")!!
|
||||
|
||||
override fun getHelpId() = HelpID.EXTRACT_SUPERCLASS
|
||||
|
||||
override fun validateName(name: String): String? {
|
||||
return when {
|
||||
!KotlinNameSuggester.isIdentifier(name.quoteIfNeeded()) -> RefactoringMessageUtil.getIncorrectIdentifierMessage(name)
|
||||
name.unquote() == mySourceClass.name -> "Different name expected"
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
override fun checkConflicts() = conflictChecker(this)
|
||||
|
||||
override fun createActionComponent() = Box.createHorizontalBox()!!
|
||||
|
||||
override fun createDestinationRootPanel(): JPanel? {
|
||||
if (targetParent !is PsiDirectory) return null
|
||||
|
||||
val targetDirectoryPanel = super.createDestinationRootPanel()
|
||||
val targetFileNamePanel = JPanel(BorderLayout()).apply {
|
||||
border = BorderFactory.createEmptyBorder(10, 0, 0, 0)
|
||||
val label = JBLabel("Target file name:")
|
||||
add(label, BorderLayout.NORTH)
|
||||
label.labelFor = fileNameField
|
||||
add(fileNameField, BorderLayout.CENTER)
|
||||
}
|
||||
|
||||
return FormBuilder
|
||||
.createFormBuilder()
|
||||
.addComponent(targetDirectoryPanel)
|
||||
.addComponent(targetFileNamePanel)
|
||||
.panel
|
||||
}
|
||||
|
||||
override fun createNorthPanel(): JComponent? {
|
||||
return super.createNorthPanel().apply {
|
||||
if (targetParent !is PsiDirectory) {
|
||||
myPackageNameLabel.parent.remove(myPackageNameLabel)
|
||||
myPackageNameField.parent.remove(myPackageNameField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun createCenterPanel(): JComponent? {
|
||||
return JPanel(BorderLayout()).apply {
|
||||
val memberSelectionPanel = KotlinMemberSelectionPanel(
|
||||
RefactoringBundle.message("members.to.form.superclass"),
|
||||
kotlinMemberInfos,
|
||||
RefactoringBundle.message("make.abstract")
|
||||
)
|
||||
memberSelectionPanel.table.memberInfoModel = memberInfoModel
|
||||
memberSelectionPanel.table.addMemberInfoChangeListener(memberInfoModel)
|
||||
add(memberSelectionPanel, BorderLayout.CENTER)
|
||||
|
||||
add(myDocCommentPanel, BorderLayout.EAST)
|
||||
}
|
||||
}
|
||||
|
||||
override fun isExtractSuperclass() = true
|
||||
|
||||
override fun preparePackage() {
|
||||
if (targetParent !is PsiDirectory) return
|
||||
|
||||
super.preparePackage()
|
||||
|
||||
val fileName = targetFileName
|
||||
if (!fileName.endsWith(".${KotlinFileType.EXTENSION}")) {
|
||||
throw ExtractSuperBaseDialog.OperationFailedException("Invalid Kotlin file name: $fileName")
|
||||
}
|
||||
RefactoringMessageUtil.checkCanCreateFile(myTargetDirectory, fileName)?.let {
|
||||
throw ExtractSuperBaseDialog.OperationFailedException(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun createProcessor() = null
|
||||
|
||||
override fun executeRefactoring() {
|
||||
val extractInfo = ExtractSuperclassInfo(
|
||||
mySourceClass.unwrapped as KtClassOrObject,
|
||||
selectedMembers,
|
||||
if (targetParent is PsiDirectory) targetDirectory else targetParent,
|
||||
targetFileName,
|
||||
extractedSuperName.quoteIfNeeded(),
|
||||
DocCommentPolicy<PsiComment>(docCommentPolicy)
|
||||
)
|
||||
refactoring(extractInfo)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// NAME: X
|
||||
// INFO: {checked: "true"}
|
||||
interface T {}
|
||||
|
||||
open class A
|
||||
|
||||
// SIBLING:
|
||||
class <caret>B : A(), T {
|
||||
// INFO: {checked: "true"}
|
||||
fun foo() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// NAME: X
|
||||
// INFO: {checked: "true"}
|
||||
interface T {}
|
||||
|
||||
open class A
|
||||
|
||||
interface X : T {
|
||||
// INFO: {checked: "true"}
|
||||
fun foo() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// SIBLING:
|
||||
class B : A(), X {
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// NAME: X
|
||||
// INFO: {checked: "true"}
|
||||
interface T {}
|
||||
|
||||
open class A
|
||||
|
||||
// SIBLING:
|
||||
class <caret>B : A(), T {
|
||||
// INFO: {checked: "true", toAbstract: "true"}
|
||||
fun foo() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// NAME: X
|
||||
// INFO: {checked: "true"}
|
||||
interface T {}
|
||||
|
||||
open class A
|
||||
|
||||
interface X : T {
|
||||
// INFO: {checked: "true", toAbstract: "true"}
|
||||
fun foo()
|
||||
}
|
||||
|
||||
// SIBLING:
|
||||
class B : A(), X {
|
||||
// INFO: {checked: "true", toAbstract: "true"}
|
||||
override fun foo() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// NAME: B
|
||||
|
||||
// INFO: {checked: "true"}
|
||||
interface I<T>
|
||||
|
||||
open class J<T>
|
||||
|
||||
// SIBLING:
|
||||
class <caret>A<T, U : List<T>, V, W, X> : J<X>(), I<W> {
|
||||
// INFO: {checked: "true"}
|
||||
fun foo(u: U) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// NAME: B
|
||||
|
||||
// INFO: {checked: "true"}
|
||||
interface I<T>
|
||||
|
||||
open class J<T>
|
||||
|
||||
interface B<T, U : List<T>, W> : I<W> {
|
||||
// INFO: {checked: "true"}
|
||||
fun foo(u: U) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// SIBLING:
|
||||
class A<T, U : List<T>, V, W, X> : J<X>(), B<T, U, W> {
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// NAME: B
|
||||
|
||||
// INFO: {checked: "true"}
|
||||
interface I<T>
|
||||
|
||||
open class J<T>
|
||||
|
||||
// SIBLING:
|
||||
class <caret>A<T, U : List<T>, V, W, X> : J<X>(), I<W> {
|
||||
// INFO: {checked: "true", toAbstract: "true"}
|
||||
fun foo() {
|
||||
val u: U
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// NAME: B
|
||||
|
||||
// INFO: {checked: "true"}
|
||||
interface I<T>
|
||||
|
||||
open class J<T>
|
||||
|
||||
interface B<W> : I<W> {
|
||||
// INFO: {checked: "true", toAbstract: "true"}
|
||||
fun foo()
|
||||
}
|
||||
|
||||
// SIBLING:
|
||||
class A<T, U : List<T>, V, W, X> : J<X>(), B<W> {
|
||||
// INFO: {checked: "true", toAbstract: "true"}
|
||||
override fun foo() {
|
||||
val u: U
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
// NAME: B
|
||||
// SIBLING:
|
||||
annotation class <caret>A
|
||||
@@ -0,0 +1 @@
|
||||
Interface cannot be extracted from an annotation class
|
||||
+11
-6
@@ -42,8 +42,8 @@ import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
|
||||
import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively
|
||||
import org.jetbrains.kotlin.idea.refactoring.chooseMembers
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractClass.ExtractSuperclassInfo
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractClass.ExtractSuperclassRefactoring
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractClass.ExtractSuperInfo
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractClass.ExtractSuperRefactoring
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractFunction.EXTRACT_FUNCTION
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractFunction.ExtractKotlinFunctionHandler
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.*
|
||||
@@ -354,7 +354,7 @@ abstract class AbstractExtractionTest() : KotlinLightCodeInsightFixtureTestCase(
|
||||
}
|
||||
}
|
||||
|
||||
protected fun doExtractSuperclassTest(path: String) {
|
||||
protected fun doExtractSuperTest(path: String, isInterface: Boolean) {
|
||||
doTest(path) { file ->
|
||||
file as KtFile
|
||||
|
||||
@@ -366,21 +366,26 @@ abstract class AbstractExtractionTest() : KotlinLightCodeInsightFixtureTestCase(
|
||||
val editor = fixture.editor
|
||||
val originalClass = file.findElementAt(editor.caretModel.offset)?.getStrictParentOfType<KtClassOrObject>()!!
|
||||
val memberInfos = chooseMembers(extractClassMembers(originalClass))
|
||||
val conflicts = ExtractSuperclassRefactoring.collectConflicts(originalClass, memberInfos, targetParent, className)
|
||||
val conflicts = ExtractSuperRefactoring.collectConflicts(originalClass, memberInfos, targetParent, className, isInterface)
|
||||
project.checkConflictsInteractively(conflicts) {
|
||||
val extractInfo = ExtractSuperclassInfo(
|
||||
val extractInfo = ExtractSuperInfo(
|
||||
originalClass,
|
||||
memberInfos,
|
||||
targetParent,
|
||||
"$className.${KotlinFileType.EXTENSION}",
|
||||
className,
|
||||
isInterface,
|
||||
DocCommentPolicy<PsiComment>(DocCommentPolicy.ASIS)
|
||||
)
|
||||
ExtractSuperclassRefactoring(extractInfo).performRefactoring()
|
||||
ExtractSuperRefactoring(extractInfo).performRefactoring()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected fun doExtractSuperclassTest(path: String) = doExtractSuperTest(path, false)
|
||||
|
||||
protected fun doExtractInterfaceTest(path: String) = doExtractSuperTest(path, true)
|
||||
|
||||
protected fun doTest(path: String, checkAdditionalAfterdata: Boolean = false, action: (PsiFile) -> Unit) {
|
||||
val mainFile = File(path)
|
||||
val afterFile = File("$path.after")
|
||||
|
||||
+39
@@ -4258,4 +4258,43 @@ public class ExtractionTestGenerated extends AbstractExtractionTest {
|
||||
doExtractSuperclassTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/refactoring/extractInterface")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class ExtractInterface extends AbstractExtractionTest {
|
||||
@TestMetadata("addInterface.kt")
|
||||
public void testAddInterface() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/extractInterface/addInterface.kt");
|
||||
doExtractInterfaceTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("addInterfaceWithAbstract.kt")
|
||||
public void testAddInterfaceWithAbstract() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/extractInterface/addInterfaceWithAbstract.kt");
|
||||
doExtractInterfaceTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("addTypeParameters.kt")
|
||||
public void testAddTypeParameters() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/extractInterface/addTypeParameters.kt");
|
||||
doExtractInterfaceTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("addTypeParametersWithAbstract.kt")
|
||||
public void testAddTypeParametersWithAbstract() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/extractInterface/addTypeParametersWithAbstract.kt");
|
||||
doExtractInterfaceTest(fileName);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInExtractInterface() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/refactoring/extractInterface"), Pattern.compile("^(.+)\\.(kt|kts)$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("annotation.kt")
|
||||
public void testAnnotation() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/extractInterface/annotation.kt");
|
||||
doExtractInterfaceTest(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user