Move: Implement "Update package directive" option

This commit is contained in:
Alexey Sedunov
2015-05-18 14:13:57 +03:00
parent d88e01e770
commit fa03e4e76c
6 changed files with 200 additions and 8 deletions
+2 -1
View File
@@ -251,7 +251,8 @@
<refactoring.helper implementation="org.jetbrains.kotlin.idea.codeInsight.KotlinShortenReferencesRefactoringHelper"/>
<refactoring.moveHandler
id="kotlin.moveFilesOrDirectories"
implementation="org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories.KotlinMoveFilesOrDirectoriesHandler"/>
implementation="org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories.KotlinMoveFilesOrDirectoriesHandler"
order="before moveJavaFileOrDir"/>
<refactoring.moveHandler
id="kotlin.moveTopLevelDeclarations"
implementation="org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.MoveKotlinTopLevelDeclarationsHandler"
@@ -18,9 +18,12 @@ package org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiDirectory
import com.intellij.psi.PsiDirectoryContainer
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.refactoring.move.MoveCallback
import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesHandler
import org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories
import org.jetbrains.kotlin.psi.JetClassOrObject
import org.jetbrains.kotlin.psi.JetFile
@@ -36,10 +39,18 @@ public class KotlinMoveFilesOrDirectoriesHandler : MoveFilesOrDirectoriesHandler
return super.canMove(adjustedElements.toTypedArray(), targetContainer)
}
override fun adjustForMove(project: Project, sourceElements: Array<PsiElement>, targetElement: PsiElement?): Array<PsiElement> {
override fun adjustForMove(project: Project, sourceElements: Array<out PsiElement>, targetElement: PsiElement?): Array<PsiElement> {
return sourceElements.map { if (it is JetClassOrObject) it.getContainingFile() else it }.toTypedArray()
}
override fun doMove(project: Project, elements: Array<out PsiElement>, targetContainer: PsiElement?, callback: MoveCallback?) {
if (!(targetContainer == null || targetContainer is PsiDirectory || targetContainer is PsiDirectoryContainer)) return
moveFilesOrDirectories(project, adjustForMove(project, elements, targetContainer), targetContainer) {
callback?.refactoringCompleted()
}
}
companion object {
public fun isMovableClass(clazz: JetClassOrObject): Boolean {
return clazz.isTopLevel() && clazz.getContainingJetFile().getDeclarations().all { it !is JetClassOrObject || it == clazz }
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.MoveK
import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.MoveKotlinTopLevelDeclarationsProcessor
import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.Mover
import org.jetbrains.kotlin.idea.refactoring.move.postProcessMoveUsages
import org.jetbrains.kotlin.idea.refactoring.move.updatePackageDirective
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.psi.JetNamedDeclaration
@@ -60,11 +61,12 @@ public class MoveKotlinFileHandler : MoveFileHandler() {
): List<UsageInfo>? {
clearState()
if (psiFile !is JetFile || !psiFile.packageMatchesDirectory()) return null
if (psiFile !is JetFile || !(psiFile.updatePackageDirective ?: psiFile.packageMatchesDirectory())) return null
val newPackage = newParent.getPackage() ?: return null
val packageNameInfo = PackageNameInfo(psiFile.getPackageFqName(), FqName(newPackage.getQualifiedName()))
if (packageNameInfo.oldPackageName == packageNameInfo.newPackageName) return null
val project = psiFile.getProject()
val declarationMoveProcessor = MoveKotlinTopLevelDeclarationsProcessor(
@@ -93,6 +95,8 @@ public class MoveKotlinFileHandler : MoveFileHandler() {
override fun updateMovedFile(file: PsiFile) {
if (file !is JetFile) return
file.updatePackageDirective = null
val packageNameInfo = packageNameInfo ?: return
val internalUsages = file.getInternalReferencesToUpdateOnPackageNameChange(packageNameInfo)
@@ -32,6 +32,7 @@ import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackages
import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesUtil
import com.intellij.refactoring.util.CommonRefactoringUtil
import org.jetbrains.kotlin.idea.core.refactoring.isInJavaSourceRoot
import org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories
import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.ui.MoveKotlinTopLevelDeclarationsDialog
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getPackage
@@ -51,11 +52,7 @@ public class MoveKotlinTopLevelDeclarationsHandler : MoveHandlerDelegate() {
val sourceFile = sourceFiles.singleOrNull()
if (sourceFile == null) {
if (sourceFiles.all { it.getDeclarations().size() == 1 }) {
MoveFilesOrDirectoriesUtil.doMove(project,
sourceFiles.toTypedArray(),
arrayOf(targetContainer),
null)
moveFilesOrDirectories(project, sourceFiles.toTypedArray(), targetContainer)
return true
}
@@ -0,0 +1,90 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.ui
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiDirectory
import com.intellij.psi.PsiElement
import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesDialog
import com.intellij.ui.NonFocusableCheckBox
import com.intellij.util.ui.UIUtil
import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.psi.psiUtil.packageMatchesDirectory
import java.awt.GridBagConstraints
import java.awt.Insets
import javax.swing.JCheckBox
import javax.swing.JComponent
import javax.swing.JLabel
import kotlin.properties.Delegates
public class MoveFilesOrDirectoriesDialogWithKotlinOptions(
project: Project,
callback: (MoveFilesOrDirectoriesDialog?) -> Unit): MoveFilesOrDirectoriesDialog(project, callback) {
private var cbUpdatePackageDirective: JCheckBox? = null
public val updatePackageDirective: Boolean
get() = cbUpdatePackageDirective!!.isSelected()
override fun createNorthPanel(): JComponent {
val panel = super.createNorthPanel()
val gbc = GridBagConstraints()
gbc.gridwidth = 1
gbc.gridx = 0
gbc.gridy = GridBagConstraints.RELATIVE
gbc.weightx = 0.0
gbc.weighty = 0.0
gbc.fill = GridBagConstraints.NONE
gbc.anchor = GridBagConstraints.WEST
gbc.insets = Insets(UIUtil.LARGE_VGAP, 0, 0, UIUtil.DEFAULT_HGAP)
panel.add(JLabel(), gbc)
cbUpdatePackageDirective = NonFocusableCheckBox()
gbc.gridx = 1
gbc.weightx = 1.0
gbc.weighty = 0.0
gbc.fill = GridBagConstraints.HORIZONTAL
gbc.anchor = GridBagConstraints.WEST
gbc.insets = Insets(UIUtil.LARGE_VGAP, 0, 0, 0)
panel.add(cbUpdatePackageDirective, gbc)
return panel
}
override fun setData(psiElements: Array<out PsiElement>, initialTargetDirectory: PsiDirectory?, helpID: String) {
super.setData(psiElements, initialTargetDirectory, helpID)
with (cbUpdatePackageDirective!!) {
val jetFiles = psiElements.filterIsInstance<JetFile>()
if (jetFiles.isEmpty()) {
getParent().remove(cbUpdatePackageDirective)
return
}
val singleFile = jetFiles.singleOrNull()
if (singleFile != null) {
setSelected(singleFile.packageMatchesDirectory())
setText("Update package directive")
}
else {
setSelected(true)
setText("Update package directive for Kotlin files")
}
}
}
}
@@ -16,15 +16,31 @@
package org.jetbrains.kotlin.idea.refactoring.move
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.DialogWrapper
import com.intellij.openapi.util.Comparing
import com.intellij.openapi.util.Computable
import com.intellij.openapi.util.Key
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.*
import com.intellij.refactoring.RefactoringBundle
import com.intellij.refactoring.RefactoringSettings
import com.intellij.refactoring.copy.CopyFilesOrDirectoriesHandler
import com.intellij.refactoring.move.MoveCallback
import com.intellij.refactoring.move.MoveHandler
import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesDialog
import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesProcessor
import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesUtil
import com.intellij.refactoring.move.moveMembers.MockMoveMembersOptions
import com.intellij.refactoring.move.moveMembers.MoveMemberHandler
import com.intellij.refactoring.move.moveMembers.MoveMembersProcessor
import com.intellij.refactoring.util.CommonRefactoringUtil
import com.intellij.refactoring.util.MoveRenameUsageInfo
import com.intellij.refactoring.util.NonCodeUsageInfo
import com.intellij.usageView.UsageInfo
import com.intellij.util.Function
import com.intellij.util.IncorrectOperationException
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
import org.jetbrains.kotlin.asJava.unwrapped
@@ -35,10 +51,13 @@ import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.codeInsight.JetFileReferencesResolver
import org.jetbrains.kotlin.idea.refactoring.fqName.isImported
import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.ui.MoveFilesOrDirectoriesDialogWithKotlinOptions
import org.jetbrains.kotlin.idea.references.JetReference
import org.jetbrains.kotlin.idea.references.JetSimpleNameReference
import org.jetbrains.kotlin.idea.references.JetSimpleNameReference.ShorteningMode
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
import org.jetbrains.kotlin.idea.util.application.executeCommand
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
@@ -261,3 +280,73 @@ fun postProcessMoveUsages(usages: List<UsageInfo>,
return nonCodeUsages
}
var JetFile.updatePackageDirective: Boolean? by UserDataProperty(Key.create("UPDATE_PACKAGE_DIRECTIVE"))
// Mostly copied from MoveFilesOrDirectoriesUtil.doMove()
public fun moveFilesOrDirectories(
project: Project,
elements: Array<PsiElement>,
targetElement: PsiElement?,
moveCallback: (() -> Unit)? = null
) {
elements.forEach { if (it !is PsiFile && it !is PsiDirectory) throw IllegalArgumentException("unexpected element type: " + it) }
val targetDirectory = MoveFilesOrDirectoriesUtil.resolveToDirectory(project, targetElement)
if (targetElement != null && targetDirectory == null) return
val initialTargetDirectory = MoveFilesOrDirectoriesUtil.getInitialTargetDirectory(targetDirectory, elements)
fun doRun(moveDialog: MoveFilesOrDirectoriesDialog?) {
fun closeDialog() {
moveDialog?.close(DialogWrapper.CANCEL_EXIT_CODE)
}
project.executeCommand(MoveHandler.REFACTORING_NAME) {
val selectedDir = if (moveDialog != null) moveDialog.getTargetDirectory() else initialTargetDirectory
val updatePackageDirective = (moveDialog as? MoveFilesOrDirectoriesDialogWithKotlinOptions)?.updatePackageDirective
try {
val choice = if (elements.size() > 1 || elements[0] is PsiDirectory) intArrayOf(-1) else null
val elementsToMove = elements.filterNot {
it is PsiFile
&& runWriteAction { CopyFilesOrDirectoriesHandler.checkFileExist(selectedDir, choice, it, it.getName(), "Move") }
}
elementsToMove.forEach {
MoveFilesOrDirectoriesUtil.checkMove(it, selectedDir)
(it as? JetFile)?.let { it.updatePackageDirective = updatePackageDirective }
}
if (elementsToMove.isNotEmpty()) {
MoveFilesOrDirectoriesProcessor(
project,
elementsToMove.toTypedArray(),
selectedDir,
RefactoringSettings.getInstance().MOVE_SEARCH_FOR_REFERENCES_FOR_FILE,
false,
false,
moveCallback,
::closeDialog
).run()
}
else {
closeDialog()
}
}
catch (e: IncorrectOperationException) {
CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("error.title"), e.getMessage(), "refactoring.moveFile", project)
}
}
}
if (ApplicationManager.getApplication().isUnitTestMode()) {
doRun(null)
return
}
with(MoveFilesOrDirectoriesDialogWithKotlinOptions(project, ::doRun)) {
setData(elements, initialTargetDirectory, "refactoring.moveFile")
show()
}
}