Move: Force package name change when moving Java files

Package statement in Java file is updated too late
when moving the entire containing directory, so we force it
with intercepting MoveFileHandler implementation
This commit is contained in:
Alexey Sedunov
2017-04-07 17:35:43 +03:00
parent 3007b11973
commit 358b5fe548
19 changed files with 180 additions and 8 deletions
+3
View File
@@ -368,6 +368,9 @@
implementation="org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveKotlinDeclarationsHandler"
order="first,before kotlin.moveFilesOrDirectories"/>
<moveFileHandler implementation="org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories.MoveKotlinFileHandler" />
<moveFileHandler
implementation="org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories.FqNameFixingMoveJavaFileHandler"
order="before java" />
<refactoring.moveDirectoryWithClassesHelper
implementation="org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories.KotlinMoveDirectoryWithClassesHelper"
order="first" />
@@ -22,6 +22,7 @@ import com.intellij.openapi.util.Ref
import com.intellij.psi.PsiDirectory
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiJavaFile
import com.intellij.refactoring.move.MoveCallback
import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesProcessor
import com.intellij.usageView.UsageInfo
@@ -82,6 +83,17 @@ class MoveFilesWithDeclarationsProcessor @JvmOverloads constructor (
sourceFile.name = temporaryName
}
private fun markShouldFixFqName(value: Boolean) {
fun PsiElement.doMark(value: Boolean) {
when (this) {
is PsiJavaFile -> shouldFixFqName = value
is PsiDirectory -> children.forEach { it.doMark(value) }
}
}
elementsToMove.forEach { it.doMark(value) }
}
override fun performRefactoring(usages: Array<UsageInfo>) {
val needTemporaryRename = targetFileName != null && targetDirectory.findFile(targetFileName) != null
if (needTemporaryRename) {
@@ -89,9 +101,11 @@ class MoveFilesWithDeclarationsProcessor @JvmOverloads constructor (
}
try {
markShouldFixFqName(true)
super.performRefactoring(usages)
}
finally {
markShouldFixFqName(false)
if (needTemporaryRename) {
(elementsToMove.single() as PsiFile).name = targetFileName!!
}
@@ -0,0 +1,54 @@
/*
* 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.move.moveFilesOrDirectories
import com.intellij.openapi.util.Key
import com.intellij.psi.*
import com.intellij.psi.util.FileTypeUtils
import com.intellij.psi.util.PsiUtil
import com.intellij.refactoring.move.moveClassesOrPackages.MoveJavaFileHandler
import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFileHandler
import com.intellij.usageView.UsageInfo
import org.jetbrains.kotlin.psi.NotNullableUserDataProperty
internal var PsiJavaFile.shouldFixFqName: Boolean by NotNullableUserDataProperty(Key.create("SHOULD_FIX_FQ_NAME"), false)
class FqNameFixingMoveJavaFileHandler : MoveFileHandler() {
private val delegate = MoveJavaFileHandler()
override fun canProcessElement(element: PsiFile) =
delegate.canProcessElement(element)
override fun findUsages(psiFile: PsiFile, newParent: PsiDirectory?, searchInComments: Boolean, searchInNonJavaFiles: Boolean) =
delegate.findUsages(psiFile, newParent, searchInComments, searchInNonJavaFiles)
override fun prepareMovedFile(file: PsiFile, moveDestination: PsiDirectory, oldToNewMap: MutableMap<PsiElement, PsiElement>) {
delegate.prepareMovedFile(file, moveDestination, oldToNewMap)
if (file is PsiJavaFile && file.shouldFixFqName) {
val newPackage = JavaDirectoryService.getInstance().getPackage(moveDestination) ?: return
if (!FileTypeUtils.isInServerPageFile(file) && !PsiUtil.isModuleFile(file)) {
file.packageName = newPackage.qualifiedName
}
}
}
override fun updateMovedFile(file: PsiFile) =
delegate.updateMovedFile(file)
override fun retargetUsages(usageInfos: MutableList<UsageInfo>, oldToNewMap: MutableMap<PsiElement, PsiElement>) =
delegate.retargetUsages(usageInfos, oldToNewMap)
}
@@ -123,16 +123,16 @@ class MoveKotlinFileHandler : MoveFileHandler() {
}
override fun prepareMovedFile(file: PsiFile, moveDestination: PsiDirectory, oldToNewMap: MutableMap<PsiElement, PsiElement>) {
if (file !is KtFile) return
val moveProcessor = initMoveProcessor(file, moveDestination) ?: return
val moveContext = MoveContext(file, moveProcessor)
oldToNewMap[moveContext] = moveContext
val packageNameInfo = file.getPackageNameInfo(moveDestination, true) ?: return
file.packageDirective?.fqName = packageNameInfo.newContainer.fqName!!.quoteIfNeeded()
}
override fun updateMovedFile(file: PsiFile) {
if (file !is KtFile) return
val newDirectory = file.parent ?: return
val packageNameInfo = file.getPackageNameInfo(newDirectory, true) ?: return
file.packageDirective?.fqName = packageNameInfo.newContainer.fqName!!.quoteIfNeeded()
}
override fun retargetUsages(usageInfos: List<UsageInfo>?, oldToNewMap: Map<PsiElement, PsiElement>) {
@@ -0,0 +1,9 @@
package test.pack
import test2.pack.A
import test2.pack.J
class Foo {
val a = A()
val j = J()
}
@@ -0,0 +1,5 @@
package test2
import test.pack.Foo
val foo = Foo()
@@ -0,0 +1,12 @@
package test2.pack;
import test.*;
import test.pack.Foo;
public class J {
A a = new A();
B b = new B();
C c = new C();
Foo foo = new Foo();
Bar bar = new Bar();
}
@@ -0,0 +1,17 @@
package test2.pack
import test.Bar
import test.pack.Foo
class A {
val b = B()
}
class B {
val a = A()
}
class C {
internal val foo = Foo()
internal val bar = Bar()
}
@@ -0,0 +1,6 @@
package test.pack
class Foo {
val a = A()
val j = J()
}
@@ -0,0 +1,5 @@
package test
import test.pack.Foo
val foo = Foo()
@@ -0,0 +1,11 @@
package test.pack;
import test.*;
public class J {
A a = new A();
B b = new B();
C c = new C();
Foo foo = new Foo();
Bar bar = new Bar();
}
@@ -0,0 +1,16 @@
package test.pack
import test.Bar
class A {
val b = B()
}
class B {
val a = A()
}
class C {
internal val foo = Foo()
internal val bar = Bar()
}
@@ -0,0 +1,5 @@
{
"filesToMove": ["test/FooUsage.kt", "test/pack"],
"type": "MOVE_FILES_WITH_DECLARATIONS",
"targetDirectory": "test2"
}
@@ -271,16 +271,17 @@ enum class MoveAction {
MOVE_FILES_WITH_DECLARATIONS {
override fun runRefactoring(rootDir: VirtualFile, mainFile: PsiFile, elementsAtCaret: List<PsiElement>, config: JsonObject) {
val project = mainFile.project
val psiFilesToMove = config.getAsJsonArray("filesToMove").map {
rootDir.findFileByRelativePath(it.asString)!!.toPsiFile(project) as KtFile
val elementsToMove = config.getAsJsonArray("filesToMove").map {
val virtualFile = rootDir.findFileByRelativePath(it.asString)!!
if (virtualFile.isDirectory) virtualFile.toPsiDirectory(project)!! else virtualFile.toPsiFile(project)!!
}
val targetDirPath = config.getString("targetDirectory")
val targetDir = rootDir.findFileByRelativePath(targetDirPath)!!.toPsiDirectory(project)!!
MoveFilesWithDeclarationsProcessor(
project,
psiFilesToMove,
elementsToMove,
targetDir,
psiFilesToMove.singleOrNull()?.name,
(elementsToMove.singleOrNull() as? KtFile)?.name,
searchInComments = true,
searchInNonJavaFiles = true,
moveCallback = null
@@ -258,6 +258,12 @@ public class MoveTestGenerated extends AbstractMoveTest {
doTest(fileName);
}
@TestMetadata("kotlin/moveFile/moveFileAndDirWithJavaFileReferringToPackageFragementWithUnmatchedDir/moveFileAndDirWithJavaFileReferringToPackageFragementWithUnmatchedDir.test")
public void testKotlin_moveFile_moveFileAndDirWithJavaFileReferringToPackageFragementWithUnmatchedDir_MoveFileAndDirWithJavaFileReferringToPackageFragementWithUnmatchedDir() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveFile/moveFileAndDirWithJavaFileReferringToPackageFragementWithUnmatchedDir/moveFileAndDirWithJavaFileReferringToPackageFragementWithUnmatchedDir.test");
doTest(fileName);
}
@TestMetadata("kotlin/moveFile/moveFileToFile/moveFileToFile.test")
public void testKotlin_moveFile_moveFileToFile_MoveFileToFile() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveFile/moveFileToFile/moveFileToFile.test");