Minor: clean up copy refactoring

This commit is contained in:
Dmitry Gridin
2019-07-02 21:09:11 +07:00
parent 1a9abaeb2a
commit 7c547d8c5a
2 changed files with 78 additions and 106 deletions
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
* 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 package org.jetbrains.kotlin.idea.refactoring.copy
@@ -53,9 +42,9 @@ import kotlin.math.max
// Based on com.intellij.refactoring.copy.CopyClassDialog // Based on com.intellij.refactoring.copy.CopyClassDialog
class CopyKotlinDeclarationDialog( class CopyKotlinDeclarationDialog(
declaration: KtNamedDeclaration, declaration: KtNamedDeclaration,
private val defaultTargetDirectory: PsiDirectory?, private val defaultTargetDirectory: PsiDirectory?,
private val project: Project private val project: Project
) : DialogWrapper(project, true) { ) : DialogWrapper(project, true) {
private val informationLabel = JLabel() private val informationLabel = JLabel()
private val classNameField = EditorTextField("") private val classNameField = EditorTextField("")
@@ -76,18 +65,19 @@ class CopyKotlinDeclarationDialog(
get() = ((destinationComboBox.comboBox.selectedItem as? DirectoryChooser.ItemWrapper)?.directory ?: originalFile).sourceRoot get() = ((destinationComboBox.comboBox.selectedItem as? DirectoryChooser.ItemWrapper)?.directory ?: originalFile).sourceRoot
init { init {
informationLabel.text = RefactoringBundle.message("copy.class.copy.0.1", UsageViewUtil.getType(declaration), UsageViewUtil.getLongName(declaration)) informationLabel.text =
RefactoringBundle.message("copy.class.copy.0.1", UsageViewUtil.getType(declaration), UsageViewUtil.getLongName(declaration))
informationLabel.font = informationLabel.font.deriveFont(Font.BOLD) informationLabel.font = informationLabel.font.deriveFont(Font.BOLD)
init() init()
destinationComboBox.setData( destinationComboBox.setData(
project, project,
defaultTargetDirectory, defaultTargetDirectory,
Pass { setErrorText(it, destinationComboBox) }, Pass { setErrorText(it, destinationComboBox) },
packageNameField.childComponent packageNameField.childComponent
) )
classNameField.setText(UsageViewUtil.getShortName(declaration)) classNameField.text = UsageViewUtil.getShortName(declaration)
classNameField.selectAll() classNameField.selectAll()
} }
@@ -97,7 +87,8 @@ class CopyKotlinDeclarationDialog(
override fun createNorthPanel(): JComponent? { override fun createNorthPanel(): JComponent? {
val qualifiedName = qualifiedName val qualifiedName = qualifiedName
packageNameField = PackageNameReferenceEditorCombo(qualifiedName, project, RECENTS_KEY, RefactoringBundle.message("choose.destination.package")) packageNameField =
PackageNameReferenceEditorCombo(qualifiedName, project, RECENTS_KEY, RefactoringBundle.message("choose.destination.package"))
packageNameField.setTextFieldPreferredWidth(max(qualifiedName.length + 5, 40)) packageNameField.setTextFieldPreferredWidth(max(qualifiedName.length + 5, 40))
packageLabel.text = RefactoringBundle.message("destination.package") packageLabel.text = RefactoringBundle.message("destination.package")
packageLabel.labelFor = packageNameField packageLabel.labelFor = packageNameField
@@ -111,12 +102,12 @@ class CopyKotlinDeclarationDialog(
val panel = JPanel(BorderLayout()) val panel = JPanel(BorderLayout())
panel.add(openInEditorCheckBox, BorderLayout.EAST) panel.add(openInEditorCheckBox, BorderLayout.EAST)
return FormBuilder.createFormBuilder() return FormBuilder.createFormBuilder()
.addComponent(informationLabel) .addComponent(informationLabel)
.addLabeledComponent(RefactoringBundle.message("copy.files.new.name.label"), classNameField, UIUtil.LARGE_VGAP) .addLabeledComponent(RefactoringBundle.message("copy.files.new.name.label"), classNameField, UIUtil.LARGE_VGAP)
.addLabeledComponent(packageLabel, packageNameField) .addLabeledComponent(packageLabel, packageNameField)
.addLabeledComponent(label, destinationComboBox) .addLabeledComponent(label, destinationComboBox)
.addComponent(panel) .addComponent(panel)
.panel .panel
} }
private val qualifiedName: String private val qualifiedName: String
@@ -144,8 +135,7 @@ class CopyKotlinDeclarationDialog(
try { try {
targetDirectory = destinationComboBox.selectDirectory(PackageWrapper(manager, packageName), false) targetDirectory = destinationComboBox.selectDirectory(PackageWrapper(manager, packageName), false)
} } catch (e: IncorrectOperationException) {
catch (e: IncorrectOperationException) {
return e.message return e.message
} }
@@ -180,6 +170,7 @@ class CopyKotlinDeclarationDialog(
override fun getHelpId() = HelpID.COPY_CLASS override fun getHelpId() = HelpID.COPY_CLASS
companion object { companion object {
@NonNls private val RECENTS_KEY = "CopyKotlinDeclarationDialog.RECENTS_KEY" @NonNls
private val RECENTS_KEY = "CopyKotlinDeclarationDialog.RECENTS_KEY"
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
* 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 package org.jetbrains.kotlin.idea.refactoring.copy
@@ -66,15 +55,12 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
var Project.newName: String? by UserDataProperty(Key.create("NEW_NAME")) var Project.newName: String? by UserDataProperty(Key.create("NEW_NAME"))
private fun PsiElement.getCopyableElement() = private fun PsiElement.getCopyableElement() =
parentsWithSelf.firstOrNull { it is KtFile || (it is KtNamedDeclaration && it.parent is KtFile) } as? KtElement parentsWithSelf.firstOrNull { it is KtFile || (it is KtNamedDeclaration && it.parent is KtFile) } as? KtElement
private fun PsiElement.getDeclarationsToCopy(): List<KtElement> { private fun PsiElement.getDeclarationsToCopy(): List<KtElement> = when (val declarationOrFile = getCopyableElement()) {
val declarationOrFile = getCopyableElement() is KtFile -> declarationOrFile.declarations.filterIsInstance<KtNamedDeclaration>().ifEmpty { listOf(declarationOrFile) }
return when (declarationOrFile) { is KtNamedDeclaration -> listOf(declarationOrFile)
is KtFile -> declarationOrFile.declarations.filterIsInstance<KtNamedDeclaration>().ifEmpty { listOf(declarationOrFile) } else -> emptyList()
is KtNamedDeclaration -> listOf(declarationOrFile)
else -> emptyList()
}
} }
} }
@@ -82,8 +68,8 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
private fun getSourceFiles(elements: Array<out PsiElement>): Array<PsiElement>? { private fun getSourceFiles(elements: Array<out PsiElement>): Array<PsiElement>? {
return elements return elements
.map { it.containingFile ?: it as? PsiFileSystemItem ?: return null } .map { it.containingFile ?: it as? PsiFileSystemItem ?: return null }
.toTypedArray() .toTypedArray()
} }
private fun canCopyFiles(elements: Array<out PsiElement>, fromUpdate: Boolean): Boolean { private fun canCopyFiles(elements: Array<out PsiElement>, fromUpdate: Boolean): Boolean {
@@ -94,11 +80,11 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
private fun canCopyDeclarations(elements: Array<out PsiElement>): Boolean { private fun canCopyDeclarations(elements: Array<out PsiElement>): Boolean {
val containingFile = val containingFile =
elements elements
.flatMap { it.getDeclarationsToCopy().ifEmpty { return false } } .flatMap { it.getDeclarationsToCopy().ifEmpty { return false } }
.distinctBy { it.containingFile } .distinctBy { it.containingFile }
.singleOrNull() .singleOrNull()
?.containingFile ?: return false ?.containingFile ?: return false
return containingFile.sourceRoot != null return containingFile.sourceRoot != null
} }
@@ -111,20 +97,18 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
} }
private fun getOrCreateTargetFile( private fun getOrCreateTargetFile(
originalFile: KtFile, originalFile: KtFile,
targetDirectory: PsiDirectory, targetDirectory: PsiDirectory,
targetFileName: String, targetFileName: String,
commandName: String commandName: String
): KtFile? { ): KtFile? {
val existingFile = targetDirectory.findFile(targetFileName) val existingFile = targetDirectory.findFile(targetFileName)
if (existingFile == originalFile) return null if (existingFile == originalFile) return null
if (existingFile != null) { if (existingFile != null) when (getFilePolicy(existingFile, targetFileName, targetDirectory, commandName)) {
val policy = getFilePolicy(existingFile, targetFileName, targetDirectory, commandName) ExistingFilePolicy.APPEND -> {
when (policy) {
ExistingFilePolicy.APPEND -> {}
ExistingFilePolicy.OVERWRITE -> runWriteAction { existingFile.delete() }
ExistingFilePolicy.SKIP -> return null
} }
ExistingFilePolicy.OVERWRITE -> runWriteAction { existingFile.delete() }
ExistingFilePolicy.SKIP -> return null
} }
return runWriteAction { return runWriteAction {
if (existingFile != null && existingFile.isValid) { if (existingFile != null && existingFile.isValid) {
@@ -136,34 +120,33 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
} }
private fun getFilePolicy( private fun getFilePolicy(
existingFile: PsiFile?, existingFile: PsiFile?,
targetFileName: String, targetFileName: String,
targetDirectory: PsiDirectory, targetDirectory: PsiDirectory,
commandName: String commandName: String
): ExistingFilePolicy { ): ExistingFilePolicy {
val isUnitTestMode = ApplicationManager.getApplication().isUnitTestMode val isUnitTestMode = ApplicationManager.getApplication().isUnitTestMode
return if (existingFile !is KtFile) { return if (existingFile !is KtFile) {
if (isUnitTestMode) return ExistingFilePolicy.OVERWRITE if (isUnitTestMode) return ExistingFilePolicy.OVERWRITE
val answer = Messages.showOkCancelDialog( val answer = Messages.showOkCancelDialog(
"File $targetFileName already exists in ${targetDirectory.virtualFile.path}", "File $targetFileName already exists in ${targetDirectory.virtualFile.path}",
commandName, commandName,
"Overwrite", "Overwrite",
"Cancel", "Cancel",
Messages.getQuestionIcon() Messages.getQuestionIcon()
) )
if (answer == Messages.OK) ExistingFilePolicy.OVERWRITE else ExistingFilePolicy.SKIP if (answer == Messages.OK) ExistingFilePolicy.OVERWRITE else ExistingFilePolicy.SKIP
} } else {
else {
if (isUnitTestMode) return ExistingFilePolicy.APPEND if (isUnitTestMode) return ExistingFilePolicy.APPEND
val answer = Messages.showYesNoCancelDialog( val answer = Messages.showYesNoCancelDialog(
"File $targetFileName already exists in ${targetDirectory.virtualFile.path}", "File $targetFileName already exists in ${targetDirectory.virtualFile.path}",
commandName, commandName,
"Append", "Append",
"Overwrite", "Overwrite",
"Cancel", "Cancel",
Messages.getQuestionIcon() Messages.getQuestionIcon()
) )
when (answer) { when (answer) {
Messages.YES -> ExistingFilePolicy.APPEND Messages.YES -> ExistingFilePolicy.APPEND
@@ -187,7 +170,8 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
val originalFile = elementsToCopy.first().containingFile as KtFile val originalFile = elementsToCopy.first().containingFile as KtFile
val initialTargetDirectory = defaultTargetDirectory ?: originalFile.containingDirectory ?: return val initialTargetDirectory = defaultTargetDirectory ?: originalFile.containingDirectory ?: return
val isSingleDeclarationInFile = singleElementToCopy is KtNamedDeclaration && originalFile.declarations.singleOrNull() == singleElementToCopy val isSingleDeclarationInFile =
singleElementToCopy is KtNamedDeclaration && originalFile.declarations.singleOrNull() == singleElementToCopy
val project = initialTargetDirectory.project val project = initialTargetDirectory.project
@@ -210,8 +194,7 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
newName = dialog.newName ?: singleElementToCopy.name newName = dialog.newName ?: singleElementToCopy.name
targetDirWrapper = dialog.targetDirectory?.toDirectoryWrapper() ?: return targetDirWrapper = dialog.targetDirectory?.toDirectoryWrapper() ?: return
targetSourceRoot = dialog.targetSourceRoot targetSourceRoot = dialog.targetSourceRoot
} } else {
else {
val dialog = CopyFilesOrDirectoriesDialog(arrayOf(originalFile), initialTargetDirectory, project, false) val dialog = CopyFilesOrDirectoriesDialog(arrayOf(originalFile), initialTargetDirectory, project, false)
if (!dialog.showAndGet()) return if (!dialog.showAndGet()) return
openInEditor = dialog.openInEditor() openInEditor = dialog.openInEditor()
@@ -219,8 +202,7 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
targetDirWrapper = dialog.targetDirectory?.toDirectoryWrapper() ?: return targetDirWrapper = dialog.targetDirectory?.toDirectoryWrapper() ?: return
targetSourceRoot = dialog.targetDirectory?.sourceRoot targetSourceRoot = dialog.targetDirectory?.sourceRoot
} }
} } else {
else {
project.newName?.let { newName = it } project.newName?.let { newName = it }
} }
@@ -229,8 +211,8 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
val internalUsages = runReadAction { val internalUsages = runReadAction {
val targetPackageName = targetDirWrapper.getPackageName() val targetPackageName = targetDirWrapper.getPackageName()
val changeInfo = ContainerChangeInfo( val changeInfo = ContainerChangeInfo(
ContainerInfo.Package(originalFile.packageFqName), ContainerInfo.Package(originalFile.packageFqName),
ContainerInfo.Package(FqName(targetPackageName)) ContainerInfo.Package(FqName(targetPackageName))
) )
elementsToCopy.flatMapTo(LinkedHashSet()) { elementToCopy -> elementsToCopy.flatMapTo(LinkedHashSet()) { elementToCopy ->
elementToCopy.getInternalReferencesToUpdateOnPackageNameChange(changeInfo).filter { elementToCopy.getInternalReferencesToUpdateOnPackageNameChange(changeInfo).filter {
@@ -247,7 +229,8 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
project.executeCommand(commandName) { project.executeCommand(commandName) {
try { try {
val targetDirectory = runWriteAction { targetDirWrapper.getOrCreateDirectory(initialTargetDirectory) } val targetDirectory = runWriteAction { targetDirWrapper.getOrCreateDirectory(initialTargetDirectory) }
val targetFileName = if (newName?.contains(".") ?: false) newName!! else newName + "." + originalFile.virtualFile.extension val targetFileName =
if (newName?.contains(".") == true) newName!! else newName + "." + originalFile.virtualFile.extension
val oldToNewElementsMapping = HashMap<PsiElement, PsiElement>() val oldToNewElementsMapping = HashMap<PsiElement, PsiElement>()
@@ -271,9 +254,9 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
copiedDeclaration = if (isSingleDeclarationInFile && targetFile is KtFile) { copiedDeclaration = if (isSingleDeclarationInFile && targetFile is KtFile) {
targetFile.declarations.singleOrNull() as? KtNamedDeclaration targetFile.declarations.singleOrNull() as? KtNamedDeclaration
} else null } else null
} } else {
else { targetFile =
targetFile = getOrCreateTargetFile(originalFile, targetDirectory, targetFileName, commandName) ?: return@executeCommand getOrCreateTargetFile(originalFile, targetDirectory, targetFileName, commandName) ?: return@executeCommand
runWriteAction { runWriteAction {
val newElements = elementsToCopy.map { targetFile.add(it.copy()) as KtNamedDeclaration } val newElements = elementsToCopy.map { targetFile.add(it.copy()) as KtNamedDeclaration }
elementsToCopy.zip(newElements).toMap(oldToNewElementsMapping) elementsToCopy.zip(newElements).toMap(oldToNewElementsMapping)
@@ -301,11 +284,9 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
if (openInEditor) { if (openInEditor) {
EditorHelper.openFilesInEditor(arrayOf(targetFile)) EditorHelper.openFilesInEditor(arrayOf(targetFile))
} }
} } catch (e: IncorrectOperationException) {
catch (e: IncorrectOperationException) {
Messages.showMessageDialog(project, e.message, RefactoringBundle.message("error.title"), Messages.getErrorIcon()) Messages.showMessageDialog(project, e.message, RefactoringBundle.message("error.title"), Messages.getErrorIcon())
} } finally {
finally {
cleanUpInternalUsages(internalUsages + restoredInternalUsages) cleanUpInternalUsages(internalUsages + restoredInternalUsages)
} }
} }
@@ -317,10 +298,10 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
val targetSourceRootPsi = targetSourceRoot?.toPsiDirectory(project) val targetSourceRootPsi = targetSourceRoot?.toPsiDirectory(project)
if (targetSourceRootPsi != null && project == originalFile.project) { if (targetSourceRootPsi != null && project == originalFile.project) {
val conflictChecker = MoveConflictChecker( val conflictChecker = MoveConflictChecker(
project, project,
elementsToCopy, elementsToCopy,
KotlinDirectoryMoveTarget(FqName.ROOT, targetSourceRootPsi), KotlinDirectoryMoveTarget(FqName.ROOT, targetSourceRootPsi),
originalFile originalFile
) )
conflictChecker.checkModuleConflictsInDeclarations(internalUsages, conflicts) conflictChecker.checkModuleConflictsInDeclarations(internalUsages, conflicts)
conflictChecker.checkVisibilityInDeclarations(conflicts) conflictChecker.checkVisibilityInDeclarations(conflicts)