Move/copy refactoring: add base support of implicit package prefix

#KT-29720 Fixed
This commit is contained in:
Dmitry Gridin
2019-07-03 12:46:49 +07:00
parent 7c547d8c5a
commit d2b8535548
29 changed files with 98 additions and 40 deletions
@@ -39,8 +39,8 @@ fun PsiFile.getFqNameByDirectory(): FqName {
return qualifiedNameByDirectory?.let(::FqName) ?: FqName.ROOT
}
fun PsiDirectory.getFqNameWithImplicitPrefix(): FqName {
val packageFqName = getPackage()?.qualifiedName?.let(::FqName) ?: FqName.ROOT
fun PsiDirectory.getFqNameWithImplicitPrefix(): FqName? {
val packageFqName = getPackage()?.qualifiedName?.let(::FqName) ?: return null
sourceRoot?.takeIf { !it.hasExplicitPackagePrefix(project) }?.let { sourceRoot ->
val implicitPrefix = PerModulePackageCacheService.getInstance(project).getImplicitPackagePrefix(sourceRoot)
return FqName.fromSegments((implicitPrefix.pathSegments() + packageFqName.pathSegments()).map { it.asString() })
@@ -48,6 +48,8 @@ fun PsiDirectory.getFqNameWithImplicitPrefix(): FqName {
return packageFqName
}
fun PsiDirectory.getFqNameWithImplicitPrefixOrRoot(): FqName = getFqNameWithImplicitPrefix() ?: FqName.ROOT
private fun VirtualFile.hasExplicitPackagePrefix(project: Project): Boolean =
toPsiDirectory(project)?.getPackage()?.qualifiedName?.isNotEmpty() == true
@@ -8,14 +8,14 @@ package org.jetbrains.kotlin.idea.actions
import com.intellij.ide.fileTemplates.DefaultTemplatePropertiesProvider
import com.intellij.ide.fileTemplates.FileTemplate
import com.intellij.psi.PsiDirectory
import org.jetbrains.kotlin.idea.core.getFqNameWithImplicitPrefix
import org.jetbrains.kotlin.idea.core.getFqNameWithImplicitPrefixOrRoot
import java.util.*
class KotlinDefaultTemplatePropertiesProvider : DefaultTemplatePropertiesProvider {
override fun fillProperties(directory: PsiDirectory, props: Properties) {
props.setProperty(
FileTemplate.ATTRIBUTE_PACKAGE_NAME,
directory.getFqNameWithImplicitPrefix().asString()
directory.getFqNameWithImplicitPrefixOrRoot().asString()
)
}
}
@@ -26,7 +26,7 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.DialogWrapper
import com.intellij.psi.*
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.core.getPackage
import org.jetbrains.kotlin.idea.core.getFqNameWithImplicitPrefix
import org.jetbrains.kotlin.idea.core.quoteIfNeeded
import org.jetbrains.kotlin.idea.core.util.CodeInsightUtils
import org.jetbrains.kotlin.idea.quickfix.IntentionActionPriority
@@ -44,7 +44,6 @@ import org.jetbrains.kotlin.idea.util.application.executeCommand
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.idea.util.projectStructure.module
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.allChildren
@@ -201,7 +200,7 @@ open class CreateClassFromUsageFix<E : KtElement> protected constructor(
val targetDirectory = dialog.targetDirectory ?: return
val fileName = "$className.${KotlinFileType.EXTENSION}"
val packageFqName = targetDirectory.getPackage()?.qualifiedName?.let { FqName(it).quoteIfNeeded() }
val packageFqName = targetDirectory.getFqNameWithImplicitPrefix()?.quoteIfNeeded()
file.project.executeWriteCommand(text) {
val targetFile = getOrCreateKotlinFile(fileName, targetDirectory, (packageFqName ?: defaultPackageFqName).asString())
@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.idea.caches.project.implementedDescriptors
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithContent
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
import org.jetbrains.kotlin.idea.core.findOrCreateDirectoryForPackage
import org.jetbrains.kotlin.idea.core.getPackage
import org.jetbrains.kotlin.idea.core.getFqNameWithImplicitPrefix
import org.jetbrains.kotlin.idea.core.overrideImplement.MemberGenerateMode
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject.BodyType.EMPTY_OR_TEMPLATE
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject.BodyType.NO_BODY
@@ -31,7 +31,7 @@ import org.jetbrains.kotlin.idea.util.module
import org.jetbrains.kotlin.js.descriptorUtils.getJetTypeFqName
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.platform.impl.isCommon
import org.jetbrains.kotlin.platform.isCommon
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier
import org.jetbrains.kotlin.resolve.BindingContext
@@ -40,7 +40,6 @@ import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker
import org.jetbrains.kotlin.resolve.checkers.ExperimentalUsageChecker
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.platform.isCommon
import org.jetbrains.kotlin.types.AbbreviatedType
import org.jetbrains.kotlin.types.KotlinType
@@ -57,7 +56,7 @@ fun createFileForDeclaration(module: Module, declaration: KtNamedDeclaration): K
val fileNameWithExtension = "$fileName.kt"
val existingFile = directory.findFile(fileNameWithExtension)
val packageName =
if (packageDirective?.packageNameExpression == null) directory.getPackage()?.qualifiedName
if (packageDirective?.packageNameExpression == null) directory.getFqNameWithImplicitPrefix()?.asString()
else packageDirective.fqName.asString()
if (existingFile is KtFile) {
val existingPackageDirective = existingFile.packageDirective
@@ -28,8 +28,8 @@ import com.intellij.util.IncorrectOperationException
import com.intellij.util.containers.MultiMap
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.packageMatchesDirectory
import org.jetbrains.kotlin.idea.core.getFqNameWithImplicitPrefix
import org.jetbrains.kotlin.idea.core.packageMatchesDirectoryOrImplicit
import org.jetbrains.kotlin.idea.core.util.toPsiDirectory
import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively
import org.jetbrains.kotlin.idea.refactoring.createKotlinFile
@@ -244,9 +244,11 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
val copiedDeclaration: KtNamedDeclaration?
if (fileToCopy != null) {
targetFile = runWriteAction {
// implicit package prefix may change after copy
val targetDirectoryFqName = targetDirectory.getFqNameWithImplicitPrefix()
val copiedFile = targetDirectory.copyFileFrom(targetFileName, fileToCopy)
if (copiedFile is KtFile && fileToCopy.packageMatchesDirectory()) {
targetDirectory.getPackage()?.qualifiedName?.let { copiedFile.packageFqName = FqName(it) }
if (copiedFile is KtFile && fileToCopy.packageMatchesDirectoryOrImplicit()) {
targetDirectoryFqName?.let { copiedFile.packageFqName = it }
}
performDelayedRefactoringRequests(project)
copiedFile
@@ -38,10 +38,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.codeInsight.shorten.performDelayedRefactoringRequests
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.idea.core.copied
import org.jetbrains.kotlin.idea.core.getPackage
import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.idea.core.*
import org.jetbrains.kotlin.idea.core.util.runSynchronouslyWithProgress
import org.jetbrains.kotlin.idea.refactoring.introduce.insertDeclaration
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
@@ -242,7 +239,7 @@ class ExtractSuperRefactoring(
newClass.addAfter(psiFactory.createTypeParameterList(typeParameterListText), newClass.nameIdentifier)
}
val targetPackageFqName = (targetParent as? PsiDirectory)?.getPackage()?.qualifiedName
val targetPackageFqName = (targetParent as? PsiDirectory)?.getFqNameWithImplicitPrefix()?.asString()
val superTypeText = buildString {
if (!targetPackageFqName.isNullOrEmpty()) {
@@ -32,7 +32,6 @@ import com.intellij.openapi.ui.popup.*
import com.intellij.openapi.util.Pass
import com.intellij.openapi.util.TextRange
import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.*
import com.intellij.psi.impl.file.PsiPackageBase
@@ -71,8 +70,6 @@ import org.jetbrains.kotlin.idea.caches.resolve.util.getJavaMemberDescriptor
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.core.*
import org.jetbrains.kotlin.idea.core.util.showYesNoCancelDialog
import org.jetbrains.kotlin.idea.j2k.IdeaJavaToKotlinServices
import org.jetbrains.kotlin.idea.j2k.JavaToKotlinConverterFactory
import org.jetbrains.kotlin.idea.project.languageVersionSettings
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinValVar
import org.jetbrains.kotlin.idea.refactoring.changeSignature.toValVar
@@ -83,7 +80,6 @@ import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.idea.util.actualsForExpected
import org.jetbrains.kotlin.idea.util.liftToExpected
import org.jetbrains.kotlin.idea.util.string.collapseSpaces
import org.jetbrains.kotlin.j2k.ConverterSettings
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.FqNameUnsafe
@@ -95,7 +91,6 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getCallWithAssert
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
import org.jetbrains.kotlin.resolve.source.getPsi
import java.io.File
import java.lang.annotation.Retention
import java.util.*
import javax.swing.Icon
@@ -111,14 +106,14 @@ const val CHECK_SUPER_METHODS_YES_NO_DIALOG = "CHECK_SUPER_METHODS_YES_NO_DIALOG
fun getOrCreateKotlinFile(
fileName: String,
targetDir: PsiDirectory,
packageName: String? = targetDir.getPackage()?.qualifiedName
packageName: String? = targetDir.getFqNameWithImplicitPrefix()?.asString()
): KtFile? =
(targetDir.findFile(fileName) ?: createKotlinFile(fileName, targetDir, packageName)) as? KtFile
fun createKotlinFile(
fileName: String,
targetDir: PsiDirectory,
packageName: String? = targetDir.getPackage()?.qualifiedName
packageName: String? = targetDir.getFqNameWithImplicitPrefix()?.asString()
): KtFile {
targetDir.checkCreateFile(fileName)
val packageFqName = packageName?.let(::FqName) ?: FqName.ROOT
@@ -31,7 +31,7 @@ import com.intellij.ui.components.JBLabelDecorator
import com.intellij.util.IncorrectOperationException
import com.intellij.util.ui.FormBuilder
import com.intellij.util.ui.UIUtil
import org.jetbrains.kotlin.idea.core.packageMatchesDirectory
import org.jetbrains.kotlin.idea.core.packageMatchesDirectoryOrImplicit
import org.jetbrains.kotlin.idea.core.util.onTextChange
import org.jetbrains.kotlin.idea.refactoring.isInJavaSourceRoot
import org.jetbrains.kotlin.idea.util.application.executeCommand
@@ -138,7 +138,7 @@ class KotlinAwareMoveFilesOrDirectoriesDialog(
}
val singleFile = jetFiles.singleOrNull()
isSelected = singleFile == null || singleFile.packageMatchesDirectory()
isSelected = singleFile == null || singleFile.packageMatchesDirectoryOrImplicit()
text = "Update package directive (Kotlin files)"
}
}
@@ -158,7 +158,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
private static boolean arePackagesAndDirectoryMatched(List<KtFile> sourceFiles) {
for (KtFile sourceFile : sourceFiles) {
if (!PackageUtilsKt.packageMatchesDirectory(sourceFile)) return false;
if (!PackageUtilsKt.packageMatchesDirectoryOrImplicit(sourceFile)) return false;
}
return true;
}
@@ -16,6 +16,7 @@ import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectori
import com.intellij.usageView.UsageInfo
import com.intellij.util.Function
import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.idea.core.getFqNameWithImplicitPrefix
import org.jetbrains.kotlin.idea.core.getPackage
import org.jetbrains.kotlin.idea.core.quoteIfNeeded
import org.jetbrains.kotlin.idea.refactoring.invokeOnceOnCommandFinish
@@ -103,8 +104,8 @@ class KotlinMoveDirectoryWithClassesHelper : MoveDirectoryWithClassesHelper() {
val moveContextMap = getOrCreateMoveContextMap()
moveContextMap[file] = MoveContext(moveDestination, moveDeclarationsProcessor)
if (moveDeclarationsProcessor != null) {
moveDestination.getPackage()?.let { newPackage ->
file.packageDirective?.fqName = FqName(newPackage.qualifiedName).quoteIfNeeded()
moveDestination.getFqNameWithImplicitPrefix()?.quoteIfNeeded()?.let {
file.packageDirective?.fqName = it
}
}
return true
@@ -17,15 +17,14 @@ import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFileHandler
import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesUtil
import com.intellij.usageView.UsageInfo
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.idea.core.getPackage
import org.jetbrains.kotlin.idea.core.packageMatchesDirectory
import org.jetbrains.kotlin.idea.core.getFqNameWithImplicitPrefix
import org.jetbrains.kotlin.idea.core.packageMatchesDirectoryOrImplicit
import org.jetbrains.kotlin.idea.core.quoteIfNeeded
import org.jetbrains.kotlin.idea.refactoring.hasIdentifiersOnly
import org.jetbrains.kotlin.idea.refactoring.move.ContainerChangeInfo
import org.jetbrains.kotlin.idea.refactoring.move.ContainerInfo
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.*
import org.jetbrains.kotlin.idea.refactoring.move.updatePackageDirective
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.UserDataProperty
@@ -44,24 +43,23 @@ class MoveKotlinFileHandler : MoveFileHandler() {
}
private fun KtFile.getPackageNameInfo(newParent: PsiDirectory?, clearUserData: Boolean): ContainerChangeInfo? {
val shouldUpdatePackageDirective = updatePackageDirective ?: packageMatchesDirectory()
val shouldUpdatePackageDirective = updatePackageDirective ?: packageMatchesDirectoryOrImplicit()
updatePackageDirective = if (clearUserData) null else shouldUpdatePackageDirective
if (!shouldUpdatePackageDirective) return null
val oldPackageName = packageFqName
val newPackage = newParent?.getPackage() ?: return ContainerChangeInfo(
val newPackageName = newParent?.getFqNameWithImplicitPrefix() ?: return ContainerChangeInfo(
ContainerInfo.Package(oldPackageName),
ContainerInfo.UnknownPackage
)
val newPackageName = FqNameUnsafe(newPackage.qualifiedName)
if (oldPackageName.asString() == newPackageName.asString()
&& ModuleUtilCore.findModuleForPsiElement(this) == ModuleUtilCore.findModuleForPsiElement(newParent)
) return null
if (!newPackageName.hasIdentifiersOnly()) return null
return ContainerChangeInfo(ContainerInfo.Package(oldPackageName), ContainerInfo.Package(newPackageName.toSafe()))
return ContainerChangeInfo(ContainerInfo.Package(oldPackageName), ContainerInfo.Package(newPackageName))
}
fun initMoveProcessor(psiFile: PsiFile, newParent: PsiDirectory?, withConflicts: Boolean): MoveKotlinDeclarationsProcessor? {
@@ -0,0 +1,8 @@
package implicit.prefix.bar
val a = 42
// comment
// some other comment
val s = ""
@@ -0,0 +1,8 @@
package implicit.prefix
val a = 42
// comment
// some other comment
val s = ""
@@ -0,0 +1,8 @@
package implicit.prefix
val a = 42
// comment
// some other comment
val s = ""
@@ -0,0 +1,4 @@
{
"mainFile": "test.kt",
"targetPackage": "bar"
}
@@ -0,0 +1 @@
package implicit.prefix
@@ -0,0 +1,5 @@
package implicit.prefix.target
interface NamedFaceO
class NamedClassO : NamedFaceO
@@ -0,0 +1,5 @@
package implicit.prefix.target
interface NamedFaceN
class NamedClassN : NamedFaceN
@@ -0,0 +1 @@
package implicit.prefix
@@ -0,0 +1,5 @@
package implicit.prefix.source
interface NamedFaceO
class NamedClassO : NamedFaceO
@@ -0,0 +1,5 @@
package implicit.prefix.source
interface NamedFaceN
class NamedClassN : NamedFaceN
@@ -0,0 +1,5 @@
{
"filesToMove": ["source/foo.kt", "source/bar.kt"],
"type": "MOVE_FILES_WITH_DECLARATIONS",
"targetDirectory": "target"
}
@@ -1,3 +1,3 @@
package code.`in`.comments2
package code.`in`.comments.code.`in`.comments2
class MyTest
@@ -69,6 +69,11 @@ public class CopyTestGenerated extends AbstractCopyTest {
runTest("idea/testData/refactoring/copy/copyFileFromDefaultPackageToDefaultPackage/copyFIleToDefaultPackage.test");
}
@TestMetadata("copyFileFromDefaultPackageWithImpicitPrefix/copyFileFromDefaultPackageWithImplicitPrefix.test")
public void testCopyFileFromDefaultPackageWithImpicitPrefix_CopyFileFromDefaultPackageWithImplicitPrefix() throws Exception {
runTest("idea/testData/refactoring/copy/copyFileFromDefaultPackageWithImpicitPrefix/copyFileFromDefaultPackageWithImplicitPrefix.test");
}
@TestMetadata("copyFIleRetainContent/copyFIleRetainContent.test")
public void testCopyFIleRetainContent_CopyFIleRetainContent() throws Exception {
runTest("idea/testData/refactoring/copy/copyFIleRetainContent/copyFIleRetainContent.test");
@@ -289,6 +289,11 @@ public class MoveTestGenerated extends AbstractMoveTest {
runTest("idea/testData/refactoring/move/kotlin/moveFile/moveMultipleFIles/moveMultipleFiles.test");
}
@TestMetadata("kotlin/moveFile/moveMultipleFIlesWithImplicitPrefix/moveMultipleFilesWithImplicitPrefix.test")
public void testKotlin_moveFile_moveMultipleFIlesWithImplicitPrefix_MoveMultipleFilesWithImplicitPrefix() throws Exception {
runTest("idea/testData/refactoring/move/kotlin/moveFile/moveMultipleFIlesWithImplicitPrefix/moveMultipleFilesWithImplicitPrefix.test");
}
@TestMetadata("kotlin/moveFile/packageWithQuotation/moveFileToPackageWithQuotation.test")
public void testKotlin_moveFile_packageWithQuotation_MoveFileToPackageWithQuotation() throws Exception {
runTest("idea/testData/refactoring/move/kotlin/moveFile/packageWithQuotation/moveFileToPackageWithQuotation.test");