Copy: Support class copying
#KT-8180 Fixed #KT-9054 Fixed
This commit is contained in:
@@ -115,6 +115,7 @@ import org.jetbrains.kotlin.idea.parameterInfo.AbstractParameterInfoTest
|
|||||||
import org.jetbrains.kotlin.idea.quickfix.AbstractQuickFixMultiFileTest
|
import org.jetbrains.kotlin.idea.quickfix.AbstractQuickFixMultiFileTest
|
||||||
import org.jetbrains.kotlin.idea.quickfix.AbstractQuickFixTest
|
import org.jetbrains.kotlin.idea.quickfix.AbstractQuickFixTest
|
||||||
import org.jetbrains.kotlin.idea.refactoring.AbstractNameSuggestionProviderTest
|
import org.jetbrains.kotlin.idea.refactoring.AbstractNameSuggestionProviderTest
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.copy.AbstractCopyTest
|
||||||
import org.jetbrains.kotlin.idea.refactoring.inline.AbstractInlineTest
|
import org.jetbrains.kotlin.idea.refactoring.inline.AbstractInlineTest
|
||||||
import org.jetbrains.kotlin.idea.refactoring.introduce.AbstractExtractionTest
|
import org.jetbrains.kotlin.idea.refactoring.introduce.AbstractExtractionTest
|
||||||
import org.jetbrains.kotlin.idea.refactoring.move.AbstractMoveTest
|
import org.jetbrains.kotlin.idea.refactoring.move.AbstractMoveTest
|
||||||
@@ -745,6 +746,10 @@ fun main(args: Array<String>) {
|
|||||||
model("refactoring/move", extension = "test", singleClass = true)
|
model("refactoring/move", extension = "test", singleClass = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testClass<AbstractCopyTest> {
|
||||||
|
model("refactoring/copy", extension = "test", singleClass = true)
|
||||||
|
}
|
||||||
|
|
||||||
testClass<AbstractMultiModuleMoveTest> {
|
testClass<AbstractMultiModuleMoveTest> {
|
||||||
model("refactoring/moveMultiModule", extension = "test", singleClass = true)
|
model("refactoring/moveMultiModule", extension = "test", singleClass = true)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -396,7 +396,14 @@
|
|||||||
<refactoring.moveInnerClassUsagesHandler
|
<refactoring.moveInnerClassUsagesHandler
|
||||||
implementationClass="org.jetbrains.kotlin.idea.refactoring.move.MoveJavaInnerClassKotlinUsagesHandler"
|
implementationClass="org.jetbrains.kotlin.idea.refactoring.move.MoveJavaInnerClassKotlinUsagesHandler"
|
||||||
language="kotlin" />
|
language="kotlin" />
|
||||||
<refactoring.copyHandler implementation="org.jetbrains.kotlin.idea.refactoring.copy.CopyKotlinFileHandler" order="first" />
|
<refactoring.copyHandler
|
||||||
|
id="kotlinClass"
|
||||||
|
implementation="org.jetbrains.kotlin.idea.refactoring.copy.CopyKotlinClassHandler"
|
||||||
|
order="first" />
|
||||||
|
<refactoring.copyHandler
|
||||||
|
id="kotlinFile"
|
||||||
|
implementation="org.jetbrains.kotlin.idea.refactoring.copy.CopyKotlinFileHandler"
|
||||||
|
order="after kotlinClass" />
|
||||||
<refactoring.changeSignatureUsageProcessor
|
<refactoring.changeSignatureUsageProcessor
|
||||||
implementation="org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinChangeSignatureUsageProcessor"
|
implementation="org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinChangeSignatureUsageProcessor"
|
||||||
order="after javaProcessor" />
|
order="after javaProcessor" />
|
||||||
|
|||||||
@@ -0,0 +1,178 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 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.copy
|
||||||
|
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.roots.JavaProjectRootsUtil
|
||||||
|
import com.intellij.openapi.ui.DialogWrapper
|
||||||
|
import com.intellij.openapi.ui.Messages
|
||||||
|
import com.intellij.psi.PsiDirectory
|
||||||
|
import com.intellij.psi.PsiManager
|
||||||
|
import com.intellij.refactoring.HelpID
|
||||||
|
import com.intellij.refactoring.MoveDestination
|
||||||
|
import com.intellij.refactoring.PackageWrapper
|
||||||
|
import com.intellij.refactoring.RefactoringBundle
|
||||||
|
import com.intellij.refactoring.copy.CopyFilesOrDirectoriesDialog
|
||||||
|
import com.intellij.refactoring.move.moveClassesOrPackages.DestinationFolderComboBox
|
||||||
|
import com.intellij.refactoring.ui.PackageNameReferenceEditorCombo
|
||||||
|
import com.intellij.ui.EditorTextField
|
||||||
|
import com.intellij.ui.RecentsManager
|
||||||
|
import com.intellij.ui.ReferenceEditorComboWithBrowseButton
|
||||||
|
import com.intellij.usageView.UsageViewUtil
|
||||||
|
import com.intellij.util.IncorrectOperationException
|
||||||
|
import com.intellij.util.ui.FormBuilder
|
||||||
|
import com.intellij.util.ui.UIUtil
|
||||||
|
import org.jetbrains.annotations.NonNls
|
||||||
|
import org.jetbrains.kotlin.idea.core.getPackage
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.Pass
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.hasIdentifiersOnly
|
||||||
|
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||||
|
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||||
|
import java.awt.BorderLayout
|
||||||
|
import java.awt.Font
|
||||||
|
import javax.swing.JComponent
|
||||||
|
import javax.swing.JLabel
|
||||||
|
import javax.swing.JPanel
|
||||||
|
|
||||||
|
// Based on com.intellij.refactoring.copy.CopyClassDialog
|
||||||
|
class CopyKotlinClassDialog(
|
||||||
|
klass: KtClassOrObject,
|
||||||
|
private val defaultTargetDirectory: PsiDirectory?,
|
||||||
|
private val project: Project
|
||||||
|
) : DialogWrapper(project, true) {
|
||||||
|
private val informationLabel = JLabel()
|
||||||
|
private val classNameField = EditorTextField("")
|
||||||
|
private val packageLabel = JLabel()
|
||||||
|
private lateinit var packageNameField: ReferenceEditorComboWithBrowseButton
|
||||||
|
private val openInEditorCheckBox = CopyFilesOrDirectoriesDialog.createOpenInEditorCB()
|
||||||
|
private val destinationComboBox = object : DestinationFolderComboBox() {
|
||||||
|
override fun getTargetPackage() = packageNameField.text.trim()
|
||||||
|
override fun reportBaseInTestSelectionInSource() = true
|
||||||
|
}
|
||||||
|
|
||||||
|
private val originalFile = klass.containingFile
|
||||||
|
|
||||||
|
var targetDirectory: MoveDestination? = null
|
||||||
|
private set
|
||||||
|
|
||||||
|
init {
|
||||||
|
informationLabel.text = RefactoringBundle.message("copy.class.copy.0.1", UsageViewUtil.getType(klass), UsageViewUtil.getLongName(klass))
|
||||||
|
informationLabel.font = informationLabel.font.deriveFont(Font.BOLD)
|
||||||
|
|
||||||
|
init()
|
||||||
|
|
||||||
|
destinationComboBox.setData(
|
||||||
|
project,
|
||||||
|
defaultTargetDirectory,
|
||||||
|
Pass { setErrorText(it, destinationComboBox) },
|
||||||
|
packageNameField.childComponent
|
||||||
|
)
|
||||||
|
classNameField.text = UsageViewUtil.getShortName(klass)
|
||||||
|
classNameField.selectAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getPreferredFocusedComponent() = classNameField
|
||||||
|
|
||||||
|
override fun createCenterPanel() = JPanel(BorderLayout())
|
||||||
|
|
||||||
|
override fun createNorthPanel(): JComponent? {
|
||||||
|
val qualifiedName = qualifiedName
|
||||||
|
packageNameField = PackageNameReferenceEditorCombo(qualifiedName, project, RECENTS_KEY, RefactoringBundle.message("choose.destination.package"))
|
||||||
|
packageNameField.setTextFieldPreferredWidth(Math.max(qualifiedName.length + 5, 40))
|
||||||
|
packageLabel.text = RefactoringBundle.message("destination.package")
|
||||||
|
packageLabel.labelFor = packageNameField
|
||||||
|
|
||||||
|
val label = JLabel(RefactoringBundle.message("target.destination.folder"))
|
||||||
|
val isMultipleSourceRoots = JavaProjectRootsUtil.getSuitableDestinationSourceRoots(project).size > 1
|
||||||
|
destinationComboBox.isVisible = isMultipleSourceRoots
|
||||||
|
label.isVisible = isMultipleSourceRoots
|
||||||
|
label.labelFor = destinationComboBox
|
||||||
|
|
||||||
|
val panel = JPanel(BorderLayout())
|
||||||
|
panel.add(openInEditorCheckBox, BorderLayout.EAST)
|
||||||
|
return FormBuilder.createFormBuilder()
|
||||||
|
.addComponent(informationLabel)
|
||||||
|
.addLabeledComponent(RefactoringBundle.message("copy.files.new.name.label"), classNameField, UIUtil.LARGE_VGAP)
|
||||||
|
.addLabeledComponent(packageLabel, packageNameField)
|
||||||
|
.addLabeledComponent(label, destinationComboBox)
|
||||||
|
.addComponent(panel)
|
||||||
|
.panel
|
||||||
|
}
|
||||||
|
|
||||||
|
private val qualifiedName: String
|
||||||
|
get() = defaultTargetDirectory?.getPackage()?.qualifiedName ?: ""
|
||||||
|
|
||||||
|
val className: String?
|
||||||
|
get() = classNameField.text
|
||||||
|
|
||||||
|
val openInEditor: Boolean
|
||||||
|
get() = openInEditorCheckBox.isSelected
|
||||||
|
|
||||||
|
private fun checkForErrors(): String? {
|
||||||
|
val packageName = packageNameField.text
|
||||||
|
val className = className
|
||||||
|
|
||||||
|
val manager = PsiManager.getInstance(project)
|
||||||
|
|
||||||
|
if (packageName.isNotEmpty() && !FqNameUnsafe(packageName).hasIdentifiersOnly()) {
|
||||||
|
return RefactoringBundle.message("invalid.target.package.name.specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (className.isNullOrEmpty()) {
|
||||||
|
return RefactoringBundle.message("no.class.name.specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
targetDirectory = destinationComboBox.selectDirectory(PackageWrapper(manager, packageName), false)
|
||||||
|
}
|
||||||
|
catch (e: IncorrectOperationException) {
|
||||||
|
return e.message
|
||||||
|
}
|
||||||
|
|
||||||
|
targetDirectory?.getTargetIfExists(defaultTargetDirectory)?.let {
|
||||||
|
val targetFileName = className + "." + originalFile.virtualFile.extension
|
||||||
|
if (it.findFile(targetFileName) == originalFile) {
|
||||||
|
return "Can't copy class to the containing file"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun doOKAction() {
|
||||||
|
val packageName = packageNameField.text
|
||||||
|
|
||||||
|
val errorString = checkForErrors()
|
||||||
|
if (errorString != null) {
|
||||||
|
if (errorString.isNotEmpty()) {
|
||||||
|
Messages.showMessageDialog(project, errorString, RefactoringBundle.message("error.title"), Messages.getErrorIcon())
|
||||||
|
}
|
||||||
|
classNameField.requestFocusInWindow()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
RecentsManager.getInstance(project).registerRecentEntry(RECENTS_KEY, packageName)
|
||||||
|
CopyFilesOrDirectoriesDialog.saveOpenInEditorState(openInEditorCheckBox.isSelected)
|
||||||
|
|
||||||
|
super.doOKAction()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getHelpId() = HelpID.COPY_CLASS
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@NonNls private val RECENTS_KEY = "CopyKotlinClassDialog.RECENTS_KEY"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,222 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 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.copy
|
||||||
|
|
||||||
|
import com.intellij.ide.util.EditorHelper
|
||||||
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.roots.ProjectRootManager
|
||||||
|
import com.intellij.openapi.ui.Messages
|
||||||
|
import com.intellij.openapi.util.Key
|
||||||
|
import com.intellij.psi.PsiDirectory
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
|
import com.intellij.refactoring.MoveDestination
|
||||||
|
import com.intellij.refactoring.RefactoringBundle
|
||||||
|
import com.intellij.refactoring.copy.CopyHandlerDelegateBase
|
||||||
|
import com.intellij.refactoring.rename.RenameProcessor
|
||||||
|
import com.intellij.refactoring.util.MoveRenameUsageInfo
|
||||||
|
import com.intellij.usageView.UsageInfo
|
||||||
|
import com.intellij.util.IncorrectOperationException
|
||||||
|
import org.jetbrains.annotations.TestOnly
|
||||||
|
import org.jetbrains.kotlin.idea.codeInsight.shorten.performDelayedRefactoringRequests
|
||||||
|
import org.jetbrains.kotlin.idea.core.getPackage
|
||||||
|
import org.jetbrains.kotlin.idea.core.quoteIfNeeded
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.createKotlinFile
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.move.*
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.executeCommand
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.psi.UserDataProperty
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||||
|
|
||||||
|
class CopyKotlinClassHandler : CopyHandlerDelegateBase() {
|
||||||
|
companion object {
|
||||||
|
@set:TestOnly
|
||||||
|
var Project.newName: String? by UserDataProperty(Key.create("NEW_NAME"))
|
||||||
|
|
||||||
|
private fun PsiElement.getTopLevelClass(): KtClassOrObject? {
|
||||||
|
val classOrFile = parentsWithSelf.firstOrNull { it is KtFile || (it is KtClassOrObject && it.isTopLevel()) }
|
||||||
|
return when (classOrFile) {
|
||||||
|
is KtFile -> classOrFile.declarations.singleOrNull() as? KtClassOrObject
|
||||||
|
is KtClassOrObject -> classOrFile
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getClassToCopy(elements: Array<out PsiElement>) = elements.singleOrNull()?.getTopLevelClass()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun canCopy(elements: Array<out PsiElement>, fromUpdate: Boolean) = getClassToCopy(elements) != null
|
||||||
|
|
||||||
|
enum class ExistingFilePolicy {
|
||||||
|
APPEND, OVERWRITE, SKIP
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getOrCreateTargetFile(
|
||||||
|
originalFile: KtFile,
|
||||||
|
targetDirectory: PsiDirectory,
|
||||||
|
targetFileName: String,
|
||||||
|
commandName: String
|
||||||
|
): KtFile? {
|
||||||
|
val existingFile = targetDirectory.findFile(targetFileName)
|
||||||
|
if (existingFile == originalFile) return null
|
||||||
|
if (existingFile != null) {
|
||||||
|
val policy = getFilePolicy(existingFile, targetFileName, targetDirectory, commandName)
|
||||||
|
when (policy) {
|
||||||
|
ExistingFilePolicy.APPEND -> {}
|
||||||
|
ExistingFilePolicy.OVERWRITE -> runWriteAction { existingFile.delete() }
|
||||||
|
ExistingFilePolicy.SKIP -> return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return runWriteAction {
|
||||||
|
if (existingFile != null && existingFile.isValid) {
|
||||||
|
existingFile as KtFile
|
||||||
|
} else {
|
||||||
|
createKotlinFile(targetFileName, targetDirectory)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getFilePolicy(
|
||||||
|
existingFile: PsiFile?,
|
||||||
|
targetFileName: String,
|
||||||
|
targetDirectory: PsiDirectory,
|
||||||
|
commandName: String
|
||||||
|
): ExistingFilePolicy {
|
||||||
|
val isUnitTestMode = ApplicationManager.getApplication().isUnitTestMode
|
||||||
|
return if (existingFile !is KtFile) {
|
||||||
|
if (isUnitTestMode) return ExistingFilePolicy.OVERWRITE
|
||||||
|
|
||||||
|
val answer = Messages.showOkCancelDialog(
|
||||||
|
"File $targetFileName already exists in ${targetDirectory.virtualFile.path}",
|
||||||
|
commandName,
|
||||||
|
"Overwrite",
|
||||||
|
"Cancel",
|
||||||
|
Messages.getQuestionIcon()
|
||||||
|
)
|
||||||
|
if (answer == Messages.OK) ExistingFilePolicy.OVERWRITE else ExistingFilePolicy.SKIP
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (isUnitTestMode) return ExistingFilePolicy.APPEND
|
||||||
|
|
||||||
|
val answer = Messages.showYesNoCancelDialog(
|
||||||
|
"File $targetFileName already exists in ${targetDirectory.virtualFile.path}",
|
||||||
|
commandName,
|
||||||
|
"Append",
|
||||||
|
"Overwrite",
|
||||||
|
"Cancel",
|
||||||
|
Messages.getQuestionIcon()
|
||||||
|
)
|
||||||
|
when (answer) {
|
||||||
|
Messages.YES -> ExistingFilePolicy.APPEND
|
||||||
|
Messages.NO -> ExistingFilePolicy.OVERWRITE
|
||||||
|
else -> ExistingFilePolicy.SKIP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun doCopy(elements: Array<out PsiElement>, defaultTargetDirectory: PsiDirectory?) {
|
||||||
|
val classToCopy = getClassToCopy(elements) ?: return
|
||||||
|
|
||||||
|
val originalFile = classToCopy.containingKtFile
|
||||||
|
val initialTargetDirectory = defaultTargetDirectory ?: originalFile.containingDirectory ?: return
|
||||||
|
|
||||||
|
val project = initialTargetDirectory.project
|
||||||
|
|
||||||
|
if (ProjectRootManager.getInstance(project).fileIndex.getSourceRootForFile(initialTargetDirectory.virtualFile) == null) return
|
||||||
|
|
||||||
|
val commandName = RefactoringBundle.message("copy.handler.copy.class")
|
||||||
|
|
||||||
|
var openInEditor = false
|
||||||
|
var newClassName: String? = classToCopy.name
|
||||||
|
var moveDestination: MoveDestination? = null
|
||||||
|
|
||||||
|
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||||
|
val dialog = CopyKotlinClassDialog(classToCopy, initialTargetDirectory, project)
|
||||||
|
dialog.title = commandName
|
||||||
|
if (!dialog.showAndGet()) return
|
||||||
|
|
||||||
|
openInEditor = dialog.openInEditor
|
||||||
|
newClassName = dialog.className
|
||||||
|
moveDestination = dialog.targetDirectory ?: return
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
project.newName?.let { newClassName = it }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newClassName.isNullOrEmpty()) return
|
||||||
|
|
||||||
|
val internalUsages = runReadAction {
|
||||||
|
val targetPackageName = moveDestination?.targetPackage?.qualifiedName
|
||||||
|
?: initialTargetDirectory.getPackage()?.qualifiedName
|
||||||
|
?: ""
|
||||||
|
val changeInfo = ContainerChangeInfo(
|
||||||
|
ContainerInfo.Package(originalFile.packageFqName),
|
||||||
|
ContainerInfo.Package(FqName(targetPackageName))
|
||||||
|
)
|
||||||
|
classToCopy
|
||||||
|
.getInternalReferencesToUpdateOnPackageNameChange(changeInfo)
|
||||||
|
.filter {
|
||||||
|
val referencedElement = (it as? MoveRenameUsageInfo)?.referencedElement
|
||||||
|
referencedElement == null || !classToCopy.isAncestor(referencedElement)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
markInternalUsages(internalUsages)
|
||||||
|
|
||||||
|
val restoredInternalUsages = ArrayList<UsageInfo>()
|
||||||
|
|
||||||
|
project.executeCommand(commandName) {
|
||||||
|
try {
|
||||||
|
val targetDirectory = runWriteAction { moveDestination?.getTargetDirectory(initialTargetDirectory) ?: initialTargetDirectory }
|
||||||
|
val targetFileName = newClassName + "." + originalFile.virtualFile.extension
|
||||||
|
|
||||||
|
val targetFile = getOrCreateTargetFile(originalFile, targetDirectory, targetFileName, commandName) ?: return@executeCommand
|
||||||
|
|
||||||
|
val newClass = runWriteAction {
|
||||||
|
val newClass = targetFile.add(classToCopy.copy()) as KtClassOrObject
|
||||||
|
val oldToNewElementsMapping: Map<PsiElement, PsiElement> = mapOf(classToCopy to newClass)
|
||||||
|
restoredInternalUsages += restoreInternalUsages(newClass, oldToNewElementsMapping, true)
|
||||||
|
postProcessMoveUsages(restoredInternalUsages, oldToNewElementsMapping)
|
||||||
|
performDelayedRefactoringRequests(project)
|
||||||
|
|
||||||
|
newClass
|
||||||
|
}
|
||||||
|
|
||||||
|
RenameProcessor(project, newClass, newClassName!!.quoteIfNeeded(), false, false).run()
|
||||||
|
|
||||||
|
if (openInEditor) {
|
||||||
|
EditorHelper.openFilesInEditor(arrayOf(targetFile))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e: IncorrectOperationException) {
|
||||||
|
Messages.showMessageDialog(project, e.message, RefactoringBundle.message("error.title"), Messages.getErrorIcon())
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
cleanUpInternalUsages(internalUsages + restoredInternalUsages)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun doClone(element: PsiElement) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-19
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations
|
|||||||
|
|
||||||
import com.intellij.ide.util.EditorHelper
|
import com.intellij.ide.util.EditorHelper
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.util.Key
|
|
||||||
import com.intellij.openapi.util.Ref
|
import com.intellij.openapi.util.Ref
|
||||||
import com.intellij.openapi.util.text.StringUtil
|
import com.intellij.openapi.util.text.StringUtil
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
@@ -30,7 +29,6 @@ import com.intellij.refactoring.move.MoveCallback
|
|||||||
import com.intellij.refactoring.move.MoveMultipleElementsViewDescriptor
|
import com.intellij.refactoring.move.MoveMultipleElementsViewDescriptor
|
||||||
import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassHandler
|
import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassHandler
|
||||||
import com.intellij.refactoring.rename.RenameUtil
|
import com.intellij.refactoring.rename.RenameUtil
|
||||||
import com.intellij.refactoring.util.MoveRenameUsageInfo
|
|
||||||
import com.intellij.refactoring.util.NonCodeUsageInfo
|
import com.intellij.refactoring.util.NonCodeUsageInfo
|
||||||
import com.intellij.refactoring.util.RefactoringUIUtil
|
import com.intellij.refactoring.util.RefactoringUIUtil
|
||||||
import com.intellij.refactoring.util.TextOccurrencesUtil
|
import com.intellij.refactoring.util.TextOccurrencesUtil
|
||||||
@@ -51,7 +49,6 @@ import org.jetbrains.kotlin.idea.refactoring.move.*
|
|||||||
import org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories.MoveKotlinClassHandler
|
import org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories.MoveKotlinClassHandler
|
||||||
import org.jetbrains.kotlin.idea.search.projectScope
|
import org.jetbrains.kotlin.idea.search.projectScope
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
|
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
|
||||||
@@ -83,8 +80,6 @@ interface Mover : (KtNamedDeclaration, KtElement) -> KtNamedDeclaration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal var KtSimpleNameExpression.internalUsageInfo: UsageInfo? by CopyableUserDataProperty(Key.create("INTERNAL_USAGE_INFO"))
|
|
||||||
|
|
||||||
class MoveDeclarationsDescriptor @JvmOverloads constructor(
|
class MoveDeclarationsDescriptor @JvmOverloads constructor(
|
||||||
val project: Project,
|
val project: Project,
|
||||||
val elementsToMove: Collection<KtNamedDeclaration>,
|
val elementsToMove: Collection<KtNamedDeclaration>,
|
||||||
@@ -245,7 +240,7 @@ class MoveKotlinDeclarationsProcessor(
|
|||||||
val (oldInternalUsages, externalUsages) = usages.partition { it is KotlinMoveUsage && it.isInternal }
|
val (oldInternalUsages, externalUsages) = usages.partition { it is KotlinMoveUsage && it.isInternal }
|
||||||
val newInternalUsages = ArrayList<UsageInfo>()
|
val newInternalUsages = ArrayList<UsageInfo>()
|
||||||
|
|
||||||
oldInternalUsages.forEach { (it.element as? KtSimpleNameExpression)?.internalUsageInfo = it }
|
markInternalUsages(oldInternalUsages)
|
||||||
|
|
||||||
val usagesToProcess = ArrayList(externalUsages)
|
val usagesToProcess = ArrayList(externalUsages)
|
||||||
|
|
||||||
@@ -281,16 +276,7 @@ class MoveKotlinDeclarationsProcessor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newDeclarations.forEach { newDeclaration ->
|
newDeclarations.forEach { newInternalUsages += restoreInternalUsages(it, oldToNewElementsMapping) }
|
||||||
newInternalUsages += newDeclaration.collectDescendantsOfType<KtSimpleNameExpression>().mapNotNull {
|
|
||||||
val usageInfo = it.internalUsageInfo
|
|
||||||
if (usageInfo?.element != null) return@mapNotNull usageInfo
|
|
||||||
val referencedElement = (usageInfo as? MoveRenameUsageInfo)?.referencedElement ?: return@mapNotNull null
|
|
||||||
val newReferencedElement = mapToNewOrThis(referencedElement, oldToNewElementsMapping)
|
|
||||||
if (!newReferencedElement.isValid) return@mapNotNull null
|
|
||||||
(usageInfo as? KotlinMoveUsage)?.refresh(it, newReferencedElement)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
usagesToProcess += newInternalUsages
|
usagesToProcess += newInternalUsages
|
||||||
|
|
||||||
@@ -301,9 +287,7 @@ class MoveKotlinDeclarationsProcessor(
|
|||||||
RefactoringUIUtil.processIncorrectOperation(myProject, e)
|
RefactoringUIUtil.processIncorrectOperation(myProject, e)
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
(newInternalUsages + oldInternalUsages).forEach {
|
cleanUpInternalUsages(newInternalUsages + oldInternalUsages)
|
||||||
(it.element as? KtSimpleNameExpression)?.internalUsageInfo = null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -181,6 +181,31 @@ fun KtElement.processInternalReferencesToUpdateOnPackageNameChange(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal var KtSimpleNameExpression.internalUsageInfo: UsageInfo? by CopyableUserDataProperty(Key.create("INTERNAL_USAGE_INFO"))
|
||||||
|
|
||||||
|
internal fun markInternalUsages(usages: Collection<UsageInfo>) {
|
||||||
|
usages.forEach { (it.element as? KtSimpleNameExpression)?.internalUsageInfo = it }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun restoreInternalUsages(
|
||||||
|
scope: KtElement,
|
||||||
|
oldToNewElementsMapping: Map<PsiElement, PsiElement>,
|
||||||
|
forcedRestore: Boolean = false
|
||||||
|
): List<UsageInfo> {
|
||||||
|
return scope.collectDescendantsOfType<KtSimpleNameExpression>().mapNotNull {
|
||||||
|
val usageInfo = it.internalUsageInfo
|
||||||
|
if (!forcedRestore && usageInfo?.element != null) return@mapNotNull usageInfo
|
||||||
|
val referencedElement = (usageInfo as? MoveRenameUsageInfo)?.referencedElement ?: return@mapNotNull null
|
||||||
|
val newReferencedElement = mapToNewOrThis(referencedElement, oldToNewElementsMapping)
|
||||||
|
if (!newReferencedElement.isValid) return@mapNotNull null
|
||||||
|
(usageInfo as? KotlinMoveUsage)?.refresh(it, newReferencedElement)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun cleanUpInternalUsages(usages: Collection<UsageInfo>) {
|
||||||
|
usages.forEach { (it.element as? KtSimpleNameExpression)?.internalUsageInfo = null }
|
||||||
|
}
|
||||||
|
|
||||||
class ImplicitCompanionAsDispatchReceiverUsageInfo(
|
class ImplicitCompanionAsDispatchReceiverUsageInfo(
|
||||||
callee: KtSimpleNameExpression,
|
callee: KtSimpleNameExpression,
|
||||||
val companionDescriptor: ClassDescriptor
|
val companionDescriptor: ClassDescriptor
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package bar
|
||||||
|
|
||||||
|
import foo.B
|
||||||
|
|
||||||
|
class A {
|
||||||
|
val a: A = A()
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
class A {
|
||||||
|
val a: A = A()
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
|
|
||||||
|
class B {
|
||||||
|
val a: A = A()
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
class A {
|
||||||
|
val a: <caret>A = A()
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
|
|
||||||
|
class B {
|
||||||
|
val a: A = A()
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"mainFile": "foo/test.kt",
|
||||||
|
"targetPackage": "bar"
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package bar
|
||||||
|
|
||||||
|
import foo.B
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class A {
|
||||||
|
val a: A = A()
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
class A {
|
||||||
|
val a: A = A()
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
|
|
||||||
|
class B {
|
||||||
|
val a: A = A()
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package bar
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
class <caret>A {
|
||||||
|
val a: A = A()
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
|
|
||||||
|
class B {
|
||||||
|
val a: A = A()
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"mainFile": "foo/test.kt",
|
||||||
|
"targetPackage": "bar"
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package bar
|
||||||
|
|
||||||
|
import foo.B
|
||||||
|
|
||||||
|
class A {
|
||||||
|
val a: A = A()
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
class A {
|
||||||
|
val a: A = A()
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
|
|
||||||
|
class B {
|
||||||
|
val a: A = A()
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
class <caret>A {
|
||||||
|
val a: A = A()
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
|
|
||||||
|
class B {
|
||||||
|
val a: A = A()
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"mainFile": "foo/test.kt",
|
||||||
|
"targetPackage": "bar"
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package bar
|
||||||
|
|
||||||
|
class A {
|
||||||
|
fun test() {
|
||||||
|
A.testCompanion()
|
||||||
|
testCompanion()
|
||||||
|
Companion.testCompanion()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun testCompanion() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
class A {
|
||||||
|
fun test() {
|
||||||
|
A.testCompanion()
|
||||||
|
testCompanion()
|
||||||
|
Companion.testCompanion()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun testCompanion() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
class <caret>A {
|
||||||
|
fun test() {
|
||||||
|
A.testCompanion()
|
||||||
|
testCompanion()
|
||||||
|
Companion.testCompanion()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun testCompanion() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"mainFile": "foo/test.kt",
|
||||||
|
"targetPackage": "bar"
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package bar
|
||||||
|
|
||||||
|
import foo.B
|
||||||
|
|
||||||
|
class X {
|
||||||
|
val a: X = X()
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
class A {
|
||||||
|
val a: A = A()
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
|
|
||||||
|
class B {
|
||||||
|
val a: A = A()
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
class <caret>A {
|
||||||
|
val a: A = A()
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
|
|
||||||
|
class B {
|
||||||
|
val a: A = A()
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"mainFile": "foo/test.kt",
|
||||||
|
"targetPackage": "bar",
|
||||||
|
"newName": "X"
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package bar
|
||||||
|
|
||||||
|
class A {
|
||||||
|
class B
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
class A {
|
||||||
|
class B
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
class A {
|
||||||
|
class <caret>B
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"mainFile": "foo/test.kt",
|
||||||
|
"targetPackage": "bar"
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package bar
|
||||||
|
|
||||||
|
import foo.B
|
||||||
|
|
||||||
|
object A {
|
||||||
|
val a: A = A
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
object A {
|
||||||
|
val a: A = A
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
|
|
||||||
|
class B {
|
||||||
|
val a: A = A
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
object <caret>A {
|
||||||
|
val a: A = A
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
|
|
||||||
|
class B {
|
||||||
|
val a: A = A
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"mainFile": "foo/test.kt",
|
||||||
|
"targetPackage": "bar"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package bar
|
||||||
|
|
||||||
|
class A
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
class A
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
class A
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"mainFile": "foo/test.kt",
|
||||||
|
"targetPackage": "bar"
|
||||||
|
}
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.refactoring
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
import com.google.gson.JsonParser
|
||||||
|
import com.intellij.codeInsight.TargetElementUtil
|
||||||
|
import com.intellij.openapi.editor.EditorFactory
|
||||||
|
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
|
import com.intellij.openapi.vfs.LocalFileSystem
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
import com.intellij.psi.PsiDocumentManager
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
|
import com.intellij.psi.PsiManager
|
||||||
|
import com.intellij.refactoring.BaseRefactoringProcessor
|
||||||
|
import com.intellij.testFramework.LightProjectDescriptor
|
||||||
|
import com.intellij.testFramework.PlatformTestUtil
|
||||||
|
import com.intellij.testFramework.UsefulTestCase
|
||||||
|
import org.jetbrains.kotlin.idea.jsonUtils.getNullableString
|
||||||
|
import org.jetbrains.kotlin.idea.jsonUtils.getString
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.move.MoveAction
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.move.runMoveRefactoring
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.rename.loadTestConfiguration
|
||||||
|
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||||
|
import org.jetbrains.kotlin.idea.test.KotlinLightProjectDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.test.extractMultipleMarkerOffsets
|
||||||
|
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
abstract class AbstractMultifileRefactoringTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||||
|
interface RefactoringAction {
|
||||||
|
fun runRefactoring(rootDir: VirtualFile, mainFile: PsiFile, elementsAtCaret: List<PsiElement>, config: JsonObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getProjectDescriptor(): LightProjectDescriptor {
|
||||||
|
if (KotlinTestUtils.isAllFilesPresentTest(getTestName(false))) return super.getProjectDescriptor()
|
||||||
|
|
||||||
|
val testConfigurationFile = File(super.getTestDataPath(), fileName())
|
||||||
|
val config = loadTestConfiguration(testConfigurationFile)
|
||||||
|
val withRuntime = config["withRuntime"]?.asBoolean ?: false
|
||||||
|
if (withRuntime) {
|
||||||
|
return KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
|
||||||
|
}
|
||||||
|
return KotlinLightProjectDescriptor.INSTANCE
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract fun runRefactoring(path: String, config: JsonObject, rootDir: VirtualFile, project: Project)
|
||||||
|
|
||||||
|
protected fun doTest(path: String) {
|
||||||
|
val testFile = File(path)
|
||||||
|
val config = JsonParser().parse(FileUtil.loadFile(testFile, true)) as JsonObject
|
||||||
|
|
||||||
|
doTestCommittingDocuments(testFile) { rootDir ->
|
||||||
|
runRefactoring(path, config, rootDir, project)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun getTestDirName(lowercaseFirstLetter : Boolean) : String {
|
||||||
|
val testName = getTestName(lowercaseFirstLetter)
|
||||||
|
val endIndex = testName.lastIndexOf('_')
|
||||||
|
if (endIndex < 0) return testName
|
||||||
|
return testName.substring(0, endIndex).replace('_', '/')
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getTestDataPath() = super.getTestDataPath() + "/" + getTestDirName(true)
|
||||||
|
|
||||||
|
protected fun doTestCommittingDocuments(testFile: File, action: (VirtualFile) -> Unit) {
|
||||||
|
val beforeVFile = myFixture.copyDirectoryToProject("before", "")
|
||||||
|
PsiDocumentManager.getInstance(myFixture.project).commitAllDocuments()
|
||||||
|
|
||||||
|
val afterDir = File(testFile.parentFile, "after")
|
||||||
|
val afterVFile = LocalFileSystem.getInstance().findFileByIoFile(afterDir)?.apply {
|
||||||
|
UsefulTestCase.refreshRecursively(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
action(beforeVFile)
|
||||||
|
|
||||||
|
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
||||||
|
FileDocumentManager.getInstance().saveAllDocuments()
|
||||||
|
PlatformTestUtil.assertDirectoriesEqual(afterVFile, beforeVFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun runRefactoringTest(
|
||||||
|
path: String,
|
||||||
|
config: JsonObject,
|
||||||
|
rootDir: VirtualFile,
|
||||||
|
project: Project,
|
||||||
|
action: AbstractMultifileRefactoringTest.RefactoringAction
|
||||||
|
) {
|
||||||
|
val testDir = path.substring(0, path.lastIndexOf("/"))
|
||||||
|
val mainFilePath = config.getNullableString("mainFile") ?: config.getAsJsonArray("filesToMove").first().asString
|
||||||
|
|
||||||
|
val conflictFile = File(testDir + "/conflicts.txt")
|
||||||
|
|
||||||
|
val mainFile = rootDir.findFileByRelativePath(mainFilePath)!!
|
||||||
|
val mainPsiFile = PsiManager.getInstance(project).findFile(mainFile)!!
|
||||||
|
val document = FileDocumentManager.getInstance().getDocument(mainFile)!!
|
||||||
|
val editor = EditorFactory.getInstance()!!.createEditor(document, project)!!
|
||||||
|
|
||||||
|
val caretOffsets = document.extractMultipleMarkerOffsets(project)
|
||||||
|
val elementsAtCaret = caretOffsets.map {
|
||||||
|
TargetElementUtil.getInstance().findTargetElement(
|
||||||
|
editor,
|
||||||
|
TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED or TargetElementUtil.ELEMENT_NAME_ACCEPTED,
|
||||||
|
it
|
||||||
|
)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
action.runRefactoring(rootDir, mainPsiFile, elementsAtCaret, config)
|
||||||
|
|
||||||
|
assert(!conflictFile.exists())
|
||||||
|
}
|
||||||
|
catch(e: BaseRefactoringProcessor.ConflictsInTestsException) {
|
||||||
|
KotlinTestUtils.assertEqualsToFile(conflictFile, e.messages.distinct().sorted().joinToString("\n"))
|
||||||
|
|
||||||
|
BaseRefactoringProcessor.ConflictsInTestsException.setTestIgnore(true)
|
||||||
|
|
||||||
|
// Run refactoring again with ConflictsInTestsException suppressed
|
||||||
|
action.runRefactoring(rootDir, mainPsiFile, elementsAtCaret, config)
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
BaseRefactoringProcessor.ConflictsInTestsException.setTestIgnore(false)
|
||||||
|
|
||||||
|
EditorFactory.getInstance()!!.releaseEditor(editor)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 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.copy
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
|
import com.intellij.refactoring.PackageWrapper
|
||||||
|
import com.intellij.refactoring.copy.CopyHandler
|
||||||
|
import com.intellij.refactoring.move.moveClassesOrPackages.MultipleRootsMoveDestination
|
||||||
|
import org.jetbrains.kotlin.idea.jsonUtils.getNullableString
|
||||||
|
import org.jetbrains.kotlin.idea.jsonUtils.getString
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.AbstractMultifileRefactoringTest
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.copy.CopyKotlinClassHandler.Companion.newName
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.runRefactoringTest
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||||
|
import org.jetbrains.kotlin.utils.ifEmpty
|
||||||
|
|
||||||
|
abstract class AbstractCopyTest : AbstractMultifileRefactoringTest(), AbstractMultifileRefactoringTest.RefactoringAction {
|
||||||
|
override fun runRefactoring(rootDir: VirtualFile, mainFile: PsiFile, elementsAtCaret: List<PsiElement>, config: JsonObject) {
|
||||||
|
val elementsToCopy = elementsAtCaret.ifEmpty { listOf(mainFile) }.toTypedArray()
|
||||||
|
assert(CopyHandler.canCopy(elementsToCopy))
|
||||||
|
|
||||||
|
val packageWrapper = PackageWrapper(mainFile.manager, config.getString("targetPackage"))
|
||||||
|
project.newName = config.getNullableString("newName")
|
||||||
|
val targetDirectory = runWriteAction { MultipleRootsMoveDestination(packageWrapper).getTargetDirectory(mainFile) }
|
||||||
|
CopyHandler.doCopy(elementsToCopy, targetDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun runRefactoring(path: String, config: JsonObject, rootDir: VirtualFile, project: Project) {
|
||||||
|
runRefactoringTest(path, config, rootDir, project, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 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.copy;
|
||||||
|
|
||||||
|
import com.intellij.testFramework.TestDataPath;
|
||||||
|
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||||
|
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||||
|
import org.jetbrains.kotlin.test.TargetBackend;
|
||||||
|
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/copy")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public class CopyTestGenerated extends AbstractCopyTest {
|
||||||
|
public void testAllFilesPresentInCopy() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentInSingleGeneratedClass(this.getClass(), new File("idea/testData/refactoring/copy"), Pattern.compile("^(.+)\\.test$"), TargetBackend.ANY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("copyClassCaretInside/copyClassCaretInside.test")
|
||||||
|
public void testCopyClassCaretInside_CopyClassCaretInside() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/copy/copyClassCaretInside/copyClassCaretInside.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("copyClassToExistingFile/copyClassToExistingFile.test")
|
||||||
|
public void testCopyClassToExistingFile_CopyClassToExistingFile() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/copy/copyClassToExistingFile/copyClassToExistingFile.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("copyClassToNewFile/copyClassToNewFile.test")
|
||||||
|
public void testCopyClassToNewFile_CopyClassToNewFile() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/copy/copyClassToNewFile/copyClassToNewFile.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("copyClassWithCompanionRefs/copyClassWithCompanionRefs.test")
|
||||||
|
public void testCopyClassWithCompanionRefs_CopyClassWithCompanionRefs() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/copy/copyClassWithCompanionRefs/copyClassWithCompanionRefs.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("copyClassWithRename/copyClassWithRename.test")
|
||||||
|
public void testCopyClassWithRename_CopyClassWithRename() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/copy/copyClassWithRename/copyClassWithRename.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("copyNestedClass/copyNestedClass.test")
|
||||||
|
public void testCopyNestedClass_CopyNestedClass() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/copy/copyNestedClass/copyNestedClass.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("copyObject/copyObject.test")
|
||||||
|
public void testCopyObject_CopyObject() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/copy/copyObject/copyObject.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("copySingleClassFile/copySingleClassFile.test")
|
||||||
|
public void testCopySingleClassFile_CopySingleClassFile() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/copy/copySingleClassFile/copySingleClassFile.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,18 +17,11 @@
|
|||||||
package org.jetbrains.kotlin.idea.refactoring.move
|
package org.jetbrains.kotlin.idea.refactoring.move
|
||||||
|
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import com.google.gson.JsonParser
|
|
||||||
import com.intellij.codeInsight.TargetElementUtil
|
|
||||||
import com.intellij.openapi.editor.EditorFactory
|
|
||||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.roots.ProjectRootManager
|
import com.intellij.openapi.roots.ProjectRootManager
|
||||||
import com.intellij.openapi.util.io.FileUtil
|
|
||||||
import com.intellij.openapi.vfs.LocalFileSystem
|
|
||||||
import com.intellij.openapi.vfs.VfsUtil
|
import com.intellij.openapi.vfs.VfsUtil
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
import com.intellij.psi.*
|
import com.intellij.psi.*
|
||||||
import com.intellij.refactoring.BaseRefactoringProcessor.ConflictsInTestsException
|
|
||||||
import com.intellij.refactoring.MoveDestination
|
import com.intellij.refactoring.MoveDestination
|
||||||
import com.intellij.refactoring.PackageWrapper
|
import com.intellij.refactoring.PackageWrapper
|
||||||
import com.intellij.refactoring.move.MoveHandler
|
import com.intellij.refactoring.move.MoveHandler
|
||||||
@@ -37,122 +30,33 @@ import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectori
|
|||||||
import com.intellij.refactoring.move.moveInner.MoveInnerProcessor
|
import com.intellij.refactoring.move.moveInner.MoveInnerProcessor
|
||||||
import com.intellij.refactoring.move.moveMembers.MockMoveMembersOptions
|
import com.intellij.refactoring.move.moveMembers.MockMoveMembersOptions
|
||||||
import com.intellij.refactoring.move.moveMembers.MoveMembersProcessor
|
import com.intellij.refactoring.move.moveMembers.MoveMembersProcessor
|
||||||
import com.intellij.testFramework.LightProjectDescriptor
|
|
||||||
import com.intellij.testFramework.PlatformTestUtil
|
|
||||||
import com.intellij.testFramework.UsefulTestCase
|
|
||||||
import com.intellij.util.ActionRunner
|
import com.intellij.util.ActionRunner
|
||||||
import org.jetbrains.kotlin.idea.jsonUtils.getNullableString
|
import org.jetbrains.kotlin.idea.jsonUtils.getNullableString
|
||||||
import org.jetbrains.kotlin.idea.jsonUtils.getString
|
import org.jetbrains.kotlin.idea.jsonUtils.getString
|
||||||
import org.jetbrains.kotlin.idea.refactoring.createKotlinFile
|
import org.jetbrains.kotlin.idea.refactoring.*
|
||||||
import org.jetbrains.kotlin.idea.refactoring.move.changePackage.KotlinChangePackageRefactoring
|
import org.jetbrains.kotlin.idea.refactoring.move.changePackage.KotlinChangePackageRefactoring
|
||||||
import org.jetbrains.kotlin.idea.refactoring.move.moveClassesOrPackages.KotlinAwareDelegatingMoveDestination
|
import org.jetbrains.kotlin.idea.refactoring.move.moveClassesOrPackages.KotlinAwareDelegatingMoveDestination
|
||||||
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.*
|
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.*
|
||||||
import org.jetbrains.kotlin.idea.refactoring.rename.loadTestConfiguration
|
|
||||||
import org.jetbrains.kotlin.idea.refactoring.toPsiDirectory
|
|
||||||
import org.jetbrains.kotlin.idea.refactoring.toPsiFile
|
|
||||||
import org.jetbrains.kotlin.idea.search.allScope
|
import org.jetbrains.kotlin.idea.search.allScope
|
||||||
import org.jetbrains.kotlin.idea.search.projectScope
|
import org.jetbrains.kotlin.idea.search.projectScope
|
||||||
import org.jetbrains.kotlin.idea.stubindex.KotlinFullClassNameIndex
|
import org.jetbrains.kotlin.idea.stubindex.KotlinFullClassNameIndex
|
||||||
import org.jetbrains.kotlin.idea.test.*
|
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
abstract class AbstractMoveTest : KotlinLightCodeInsightFixtureTestCase() {
|
abstract class AbstractMoveTest : AbstractMultifileRefactoringTest() {
|
||||||
override fun getProjectDescriptor(): LightProjectDescriptor {
|
override fun runRefactoring(path: String, config: JsonObject, rootDir: VirtualFile, project: Project) {
|
||||||
if (KotlinTestUtils.isAllFilesPresentTest(getTestName(false))) return super.getProjectDescriptor()
|
runMoveRefactoring(path, config, rootDir, project)
|
||||||
|
|
||||||
val testConfigurationFile = File(super.getTestDataPath(), fileName())
|
|
||||||
val config = loadTestConfiguration(testConfigurationFile)
|
|
||||||
val withRuntime = config["withRuntime"]?.asBoolean ?: false
|
|
||||||
if (withRuntime) {
|
|
||||||
return KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
|
|
||||||
}
|
|
||||||
return KotlinLightProjectDescriptor.INSTANCE
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun doTest(path: String) {
|
|
||||||
val testFile = File(path)
|
|
||||||
val config = JsonParser().parse(FileUtil.loadFile(testFile, true)) as JsonObject
|
|
||||||
|
|
||||||
doTestCommittingDocuments(testFile) { rootDir ->
|
|
||||||
runMoveRefactoring(path, config, rootDir, project)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun getTestDirName(lowercaseFirstLetter : Boolean) : String {
|
|
||||||
val testName = getTestName(lowercaseFirstLetter)
|
|
||||||
val endIndex = testName.lastIndexOf('_')
|
|
||||||
if (endIndex < 0) return testName
|
|
||||||
return testName.substring(0, endIndex).replace('_', '/')
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getTestDataPath() = super.getTestDataPath() + "/" + getTestDirName(true)
|
|
||||||
|
|
||||||
protected fun doTestCommittingDocuments(testFile: File, action: (VirtualFile) -> Unit) {
|
|
||||||
val beforeVFile = myFixture.copyDirectoryToProject("before", "")
|
|
||||||
PsiDocumentManager.getInstance(myFixture.project).commitAllDocuments()
|
|
||||||
|
|
||||||
val afterDir = File(testFile.parentFile, "after")
|
|
||||||
val afterVFile = LocalFileSystem.getInstance().findFileByIoFile(afterDir)?.apply {
|
|
||||||
UsefulTestCase.refreshRecursively(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
action(beforeVFile)
|
|
||||||
|
|
||||||
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
|
||||||
FileDocumentManager.getInstance().saveAllDocuments()
|
|
||||||
PlatformTestUtil.assertDirectoriesEqual(afterVFile, beforeVFile)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun runMoveRefactoring(path: String, config: JsonObject, rootDir: VirtualFile, project: Project) {
|
fun runMoveRefactoring(path: String, config: JsonObject, rootDir: VirtualFile, project: Project) {
|
||||||
val action = MoveAction.valueOf(config.getString("type"))
|
runRefactoringTest(path, config, rootDir, project, MoveAction.valueOf(config.getString("type")))
|
||||||
|
|
||||||
val testDir = path.substring(0, path.lastIndexOf("/"))
|
|
||||||
val mainFilePath = config.getNullableString("mainFile") ?: config.getAsJsonArray("filesToMove").first().asString
|
|
||||||
|
|
||||||
val conflictFile = File(testDir + "/conflicts.txt")
|
|
||||||
|
|
||||||
val mainFile = rootDir.findFileByRelativePath(mainFilePath)!!
|
|
||||||
val mainPsiFile = PsiManager.getInstance(project).findFile(mainFile)!!
|
|
||||||
val document = FileDocumentManager.getInstance().getDocument(mainFile)!!
|
|
||||||
val editor = EditorFactory.getInstance()!!.createEditor(document, project)!!
|
|
||||||
|
|
||||||
val caretOffsets = document.extractMultipleMarkerOffsets(project)
|
|
||||||
val elementsAtCaret = caretOffsets.map {
|
|
||||||
TargetElementUtil.getInstance().findTargetElement(
|
|
||||||
editor,
|
|
||||||
TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED or TargetElementUtil.ELEMENT_NAME_ACCEPTED,
|
|
||||||
it
|
|
||||||
)!!
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
action.runRefactoring(rootDir, mainPsiFile, elementsAtCaret, config)
|
|
||||||
|
|
||||||
assert(!conflictFile.exists())
|
|
||||||
}
|
|
||||||
catch(e: ConflictsInTestsException) {
|
|
||||||
KotlinTestUtils.assertEqualsToFile(conflictFile, e.messages.distinct().sorted().joinToString("\n"))
|
|
||||||
|
|
||||||
ConflictsInTestsException.setTestIgnore(true)
|
|
||||||
|
|
||||||
// Run refactoring again with ConflictsInTestsException suppressed
|
|
||||||
action.runRefactoring(rootDir, mainPsiFile, elementsAtCaret, config)
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
ConflictsInTestsException.setTestIgnore(false)
|
|
||||||
|
|
||||||
EditorFactory.getInstance()!!.releaseEditor(editor)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class MoveAction {
|
enum class MoveAction : AbstractMultifileRefactoringTest.RefactoringAction {
|
||||||
MOVE_MEMBERS {
|
MOVE_MEMBERS {
|
||||||
override fun runRefactoring(rootDir: VirtualFile, mainFile: PsiFile, elementsAtCaret: List<PsiElement>, config: JsonObject) {
|
override fun runRefactoring(rootDir: VirtualFile, mainFile: PsiFile, elementsAtCaret: List<PsiElement>, config: JsonObject) {
|
||||||
val members = elementsAtCaret.map { it.getNonStrictParentOfType<PsiMember>()!! }
|
val members = elementsAtCaret.map { it.getNonStrictParentOfType<PsiMember>()!! }
|
||||||
@@ -387,6 +291,4 @@ enum class MoveAction {
|
|||||||
MoveKotlinDeclarationsProcessor(descriptor).run()
|
MoveKotlinDeclarationsProcessor(descriptor).run()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
abstract fun runRefactoring(rootDir: VirtualFile, mainFile: PsiFile, elementsAtCaret: List<PsiElement>, config: JsonObject)
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user