Copy: Report module accessibility conflicts
This commit is contained in:
@@ -116,6 +116,7 @@ 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.copy.AbstractCopyTest
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.copy.AbstractMultiModuleCopyTest
|
||||||
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
|
||||||
@@ -759,6 +760,10 @@ fun main(args: Array<String>) {
|
|||||||
model("refactoring/moveMultiModule", extension = "test", singleClass = true)
|
model("refactoring/moveMultiModule", extension = "test", singleClass = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testClass<AbstractMultiModuleCopyTest> {
|
||||||
|
model("refactoring/copyMultiModule", extension = "test", singleClass = true)
|
||||||
|
}
|
||||||
|
|
||||||
testClass<AbstractMultiFileIntentionTest> {
|
testClass<AbstractMultiFileIntentionTest> {
|
||||||
model("multiFileIntentions", extension = "test", singleClass = true, filenameStartsLowerCase = true)
|
model("multiFileIntentions", extension = "test", singleClass = true, filenameStartsLowerCase = true)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,10 +22,12 @@ import com.intellij.injected.editor.VirtualFileWindow
|
|||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.roots.FileIndex
|
import com.intellij.openapi.roots.FileIndex
|
||||||
import com.intellij.openapi.roots.ProjectFileIndex
|
import com.intellij.openapi.roots.ProjectFileIndex
|
||||||
|
import com.intellij.openapi.roots.ProjectRootManager
|
||||||
import com.intellij.openapi.util.Ref
|
import com.intellij.openapi.util.Ref
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
import com.intellij.psi.PsiDirectory
|
import com.intellij.psi.PsiDirectory
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiFileSystemItem
|
||||||
import org.jetbrains.kotlin.idea.KotlinModuleFileType
|
import org.jetbrains.kotlin.idea.KotlinModuleFileType
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.JsProjectDetector
|
import org.jetbrains.kotlin.idea.caches.resolve.JsProjectDetector
|
||||||
import org.jetbrains.kotlin.idea.core.script.KotlinScriptConfigurationManager
|
import org.jetbrains.kotlin.idea.core.script.KotlinScriptConfigurationManager
|
||||||
@@ -40,6 +42,11 @@ fun FileIndex.isInSourceContentWithoutInjected(file: VirtualFile): Boolean {
|
|||||||
return file !is VirtualFileWindow && isInSourceContent(file)
|
return file !is VirtualFileWindow && isInSourceContent(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun VirtualFile.getSourceRoot(project: Project): VirtualFile? = ProjectRootManager.getInstance(project).fileIndex.getSourceRootForFile(this)
|
||||||
|
|
||||||
|
val PsiFileSystemItem.sourceRoot: VirtualFile?
|
||||||
|
get() = virtualFile?.getSourceRoot(project)
|
||||||
|
|
||||||
object ProjectRootsUtil {
|
object ProjectRootsUtil {
|
||||||
@JvmStatic fun isInContent(project: Project, file: VirtualFile, includeProjectSource: Boolean,
|
@JvmStatic fun isInContent(project: Project, file: VirtualFile, includeProjectSource: Boolean,
|
||||||
includeLibrarySource: Boolean, includeLibraryClasses: Boolean,
|
includeLibrarySource: Boolean, includeLibraryClasses: Boolean,
|
||||||
|
|||||||
@@ -15,10 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.jetbrains.kotlin.idea.refactoring.copy
|
package org.jetbrains.kotlin.idea.refactoring.copy
|
||||||
|
|
||||||
|
import com.intellij.ide.util.DirectoryChooser
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.roots.JavaProjectRootsUtil
|
import com.intellij.openapi.roots.JavaProjectRootsUtil
|
||||||
import com.intellij.openapi.ui.DialogWrapper
|
import com.intellij.openapi.ui.DialogWrapper
|
||||||
import com.intellij.openapi.ui.Messages
|
import com.intellij.openapi.ui.Messages
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
import com.intellij.psi.PsiDirectory
|
import com.intellij.psi.PsiDirectory
|
||||||
import com.intellij.psi.PsiManager
|
import com.intellij.psi.PsiManager
|
||||||
import com.intellij.refactoring.HelpID
|
import com.intellij.refactoring.HelpID
|
||||||
@@ -39,6 +41,7 @@ import org.jetbrains.annotations.NonNls
|
|||||||
import org.jetbrains.kotlin.idea.core.getPackage
|
import org.jetbrains.kotlin.idea.core.getPackage
|
||||||
import org.jetbrains.kotlin.idea.refactoring.Pass
|
import org.jetbrains.kotlin.idea.refactoring.Pass
|
||||||
import org.jetbrains.kotlin.idea.refactoring.hasIdentifiersOnly
|
import org.jetbrains.kotlin.idea.refactoring.hasIdentifiersOnly
|
||||||
|
import org.jetbrains.kotlin.idea.util.sourceRoot
|
||||||
import org.jetbrains.kotlin.name.FqNameUnsafe
|
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||||
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
||||||
import java.awt.BorderLayout
|
import java.awt.BorderLayout
|
||||||
@@ -68,6 +71,9 @@ class CopyKotlinDeclarationDialog(
|
|||||||
var targetDirectory: MoveDestination? = null
|
var targetDirectory: MoveDestination? = null
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
val targetSourceRoot: VirtualFile?
|
||||||
|
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)
|
||||||
|
|||||||
+70
-39
@@ -22,10 +22,12 @@ import com.intellij.openapi.project.Project
|
|||||||
import com.intellij.openapi.roots.ProjectRootManager
|
import com.intellij.openapi.roots.ProjectRootManager
|
||||||
import com.intellij.openapi.ui.Messages
|
import com.intellij.openapi.ui.Messages
|
||||||
import com.intellij.openapi.util.Key
|
import com.intellij.openapi.util.Key
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
import com.intellij.psi.PsiDirectory
|
import com.intellij.psi.PsiDirectory
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.PsiFile
|
import com.intellij.psi.PsiFile
|
||||||
import com.intellij.psi.PsiNamedElement
|
import com.intellij.psi.PsiNamedElement
|
||||||
|
import com.intellij.refactoring.BaseRefactoringProcessor
|
||||||
import com.intellij.refactoring.RefactoringBundle
|
import com.intellij.refactoring.RefactoringBundle
|
||||||
import com.intellij.refactoring.copy.CopyFilesOrDirectoriesDialog
|
import com.intellij.refactoring.copy.CopyFilesOrDirectoriesDialog
|
||||||
import com.intellij.refactoring.copy.CopyHandlerDelegateBase
|
import com.intellij.refactoring.copy.CopyHandlerDelegateBase
|
||||||
@@ -33,14 +35,20 @@ import com.intellij.refactoring.rename.RenameProcessor
|
|||||||
import com.intellij.refactoring.util.MoveRenameUsageInfo
|
import com.intellij.refactoring.util.MoveRenameUsageInfo
|
||||||
import com.intellij.usageView.UsageInfo
|
import com.intellij.usageView.UsageInfo
|
||||||
import com.intellij.util.IncorrectOperationException
|
import com.intellij.util.IncorrectOperationException
|
||||||
|
import com.intellij.util.containers.MultiMap
|
||||||
import org.jetbrains.annotations.TestOnly
|
import org.jetbrains.annotations.TestOnly
|
||||||
import org.jetbrains.kotlin.idea.codeInsight.shorten.performDelayedRefactoringRequests
|
import org.jetbrains.kotlin.idea.codeInsight.shorten.performDelayedRefactoringRequests
|
||||||
import org.jetbrains.kotlin.idea.core.quoteIfNeeded
|
import org.jetbrains.kotlin.idea.core.quoteIfNeeded
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively
|
||||||
import org.jetbrains.kotlin.idea.refactoring.createKotlinFile
|
import org.jetbrains.kotlin.idea.refactoring.createKotlinFile
|
||||||
import org.jetbrains.kotlin.idea.refactoring.move.*
|
import org.jetbrains.kotlin.idea.refactoring.move.*
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.KotlinDirectoryMoveTarget
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveConflictChecker
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.toPsiDirectory
|
||||||
import org.jetbrains.kotlin.idea.util.application.executeCommand
|
import org.jetbrains.kotlin.idea.util.application.executeCommand
|
||||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||||
|
import org.jetbrains.kotlin.idea.util.sourceRoot
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.psi.KtElement
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
@@ -55,7 +63,7 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
|
|||||||
@set:TestOnly
|
@set:TestOnly
|
||||||
var Project.newName: String? by UserDataProperty(Key.create("NEW_NAME"))
|
var Project.newName: String? by UserDataProperty(Key.create("NEW_NAME"))
|
||||||
|
|
||||||
private fun PsiElement.getElementsToCopy(): List<PsiNamedElement> {
|
private fun PsiElement.getElementsToCopy(): List<KtElement> {
|
||||||
val declarationOrFile = parentsWithSelf.firstOrNull { it is KtFile || (it is KtNamedDeclaration && it.parent is KtFile) }
|
val declarationOrFile = parentsWithSelf.firstOrNull { it is KtFile || (it is KtNamedDeclaration && it.parent is KtFile) }
|
||||||
return when (declarationOrFile) {
|
return when (declarationOrFile) {
|
||||||
is KtFile -> declarationOrFile.declarations.filterIsInstance<KtNamedDeclaration>().ifEmpty { listOf(declarationOrFile) }
|
is KtFile -> declarationOrFile.declarations.filterIsInstance<KtNamedDeclaration>().ifEmpty { listOf(declarationOrFile) }
|
||||||
@@ -147,15 +155,16 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
|
|||||||
|
|
||||||
val project = initialTargetDirectory.project
|
val project = initialTargetDirectory.project
|
||||||
|
|
||||||
if (ProjectRootManager.getInstance(project).fileIndex.getSourceRootForFile(initialTargetDirectory.virtualFile) == null) return
|
|
||||||
|
|
||||||
val commandName = "Copy Declarations"
|
val commandName = "Copy Declarations"
|
||||||
|
|
||||||
var openInEditor = false
|
var openInEditor = false
|
||||||
var newName: String? = singleElementToCopy?.name ?: originalFile.name
|
var newName: String? = singleElementToCopy?.name ?: originalFile.name
|
||||||
var targetDirWrapper: AutocreatingPsiDirectoryWrapper = initialTargetDirectory.toDirectoryWrapper()
|
var targetDirWrapper: AutocreatingPsiDirectoryWrapper = initialTargetDirectory.toDirectoryWrapper()
|
||||||
|
var targetSourceRoot: VirtualFile? = initialTargetDirectory.sourceRoot ?: return
|
||||||
|
|
||||||
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
val isUnitTestMode = ApplicationManager.getApplication().isUnitTestMode
|
||||||
|
|
||||||
|
if (!isUnitTestMode) {
|
||||||
if (singleElementToCopy != null && singleElementToCopy is KtNamedDeclaration) {
|
if (singleElementToCopy != null && singleElementToCopy is KtNamedDeclaration) {
|
||||||
val dialog = CopyKotlinDeclarationDialog(singleElementToCopy, initialTargetDirectory, project)
|
val dialog = CopyKotlinDeclarationDialog(singleElementToCopy, initialTargetDirectory, project)
|
||||||
dialog.title = commandName
|
dialog.title = commandName
|
||||||
@@ -164,6 +173,7 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
|
|||||||
openInEditor = dialog.openInEditor
|
openInEditor = dialog.openInEditor
|
||||||
newName = dialog.newName ?: singleElementToCopy.name
|
newName = dialog.newName ?: singleElementToCopy.name
|
||||||
targetDirWrapper = dialog.targetDirectory?.toDirectoryWrapper() ?: return
|
targetDirWrapper = dialog.targetDirectory?.toDirectoryWrapper() ?: return
|
||||||
|
targetSourceRoot = dialog.targetSourceRoot
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val dialog = CopyFilesOrDirectoriesDialog(arrayOf(originalFile), initialTargetDirectory, project, false)
|
val dialog = CopyFilesOrDirectoriesDialog(arrayOf(originalFile), initialTargetDirectory, project, false)
|
||||||
@@ -171,6 +181,7 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
|
|||||||
openInEditor = dialog.openInEditor()
|
openInEditor = dialog.openInEditor()
|
||||||
newName = dialog.newName
|
newName = dialog.newName
|
||||||
targetDirWrapper = dialog.targetDirectory?.toDirectoryWrapper() ?: return
|
targetDirWrapper = dialog.targetDirectory?.toDirectoryWrapper() ?: return
|
||||||
|
targetSourceRoot = dialog.targetDirectory?.sourceRoot
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -185,8 +196,8 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
|
|||||||
ContainerInfo.Package(originalFile.packageFqName),
|
ContainerInfo.Package(originalFile.packageFqName),
|
||||||
ContainerInfo.Package(FqName(targetPackageName))
|
ContainerInfo.Package(FqName(targetPackageName))
|
||||||
)
|
)
|
||||||
elementsToCopy.flatMap { elementToCopy ->
|
elementsToCopy.flatMapTo(LinkedHashSet()) { elementToCopy ->
|
||||||
(elementToCopy as KtElement).getInternalReferencesToUpdateOnPackageNameChange(changeInfo).filter {
|
elementToCopy.getInternalReferencesToUpdateOnPackageNameChange(changeInfo).filter {
|
||||||
val referencedElement = (it as? MoveRenameUsageInfo)?.referencedElement
|
val referencedElement = (it as? MoveRenameUsageInfo)?.referencedElement
|
||||||
referencedElement == null || !elementToCopy.isAncestor(referencedElement)
|
referencedElement == null || !elementToCopy.isAncestor(referencedElement)
|
||||||
}
|
}
|
||||||
@@ -194,51 +205,71 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
|
|||||||
}
|
}
|
||||||
markInternalUsages(internalUsages)
|
markInternalUsages(internalUsages)
|
||||||
|
|
||||||
val restoredInternalUsages = ArrayList<UsageInfo>()
|
fun doRefactor() {
|
||||||
|
val restoredInternalUsages = ArrayList<UsageInfo>()
|
||||||
|
|
||||||
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(".") ?: false) newName!! else newName + "." + originalFile.virtualFile.extension
|
||||||
|
|
||||||
val oldToNewElementsMapping = HashMap<PsiElement, PsiElement>()
|
val oldToNewElementsMapping = HashMap<PsiElement, PsiElement>()
|
||||||
|
|
||||||
|
val targetFile: KtFile
|
||||||
|
if (singleElementToCopy is KtFile) {
|
||||||
|
targetFile = runWriteAction { targetDirectory.copyFileFrom(targetFileName, singleElementToCopy) as KtFile }
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
targetFile = getOrCreateTargetFile(originalFile, targetDirectory, targetFileName, commandName) ?: return@executeCommand
|
||||||
|
runWriteAction {
|
||||||
|
val newElements = elementsToCopy.map { targetFile.add(it.copy()) as KtNamedDeclaration }
|
||||||
|
elementsToCopy.zip(newElements).toMap(oldToNewElementsMapping)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val targetFile: KtFile
|
|
||||||
if (singleElementToCopy is KtFile) {
|
|
||||||
targetFile = runWriteAction { targetDirectory.copyFileFrom(targetFileName, singleElementToCopy) as KtFile }
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
targetFile = getOrCreateTargetFile(originalFile, targetDirectory, targetFileName, commandName) ?: return@executeCommand
|
|
||||||
runWriteAction {
|
runWriteAction {
|
||||||
val newElements = elementsToCopy.map { targetFile.add(it.copy()) as KtNamedDeclaration }
|
for (newElement in oldToNewElementsMapping.values) {
|
||||||
elementsToCopy.zip(newElements).toMap(oldToNewElementsMapping)
|
restoredInternalUsages += restoreInternalUsages(newElement as KtElement, oldToNewElementsMapping, true)
|
||||||
}
|
postProcessMoveUsages(restoredInternalUsages, oldToNewElementsMapping)
|
||||||
}
|
}
|
||||||
|
|
||||||
runWriteAction {
|
performDelayedRefactoringRequests(project)
|
||||||
for (newElement in oldToNewElementsMapping.values) {
|
|
||||||
restoredInternalUsages += restoreInternalUsages(newElement as KtElement, oldToNewElementsMapping, true)
|
|
||||||
postProcessMoveUsages(restoredInternalUsages, oldToNewElementsMapping)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
performDelayedRefactoringRequests(project)
|
oldToNewElementsMapping.values.singleOrNull()?.let {
|
||||||
}
|
RenameProcessor(project, it, newName!!.quoteIfNeeded(), false, false).run()
|
||||||
|
}
|
||||||
|
|
||||||
oldToNewElementsMapping.values.singleOrNull()?.let {
|
if (openInEditor) {
|
||||||
RenameProcessor(project, it, newName!!.quoteIfNeeded(), false, false).run()
|
EditorHelper.openFilesInEditor(arrayOf(targetFile))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
catch (e: IncorrectOperationException) {
|
||||||
if (openInEditor) {
|
Messages.showMessageDialog(project, e.message, RefactoringBundle.message("error.title"), Messages.getErrorIcon())
|
||||||
EditorHelper.openFilesInEditor(arrayOf(targetFile))
|
}
|
||||||
|
finally {
|
||||||
|
cleanUpInternalUsages(internalUsages + restoredInternalUsages)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch (e: IncorrectOperationException) {
|
|
||||||
Messages.showMessageDialog(project, e.message, RefactoringBundle.message("error.title"), Messages.getErrorIcon())
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
cleanUpInternalUsages(internalUsages + restoredInternalUsages)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val conflicts = MultiMap<PsiElement, String>()
|
||||||
|
|
||||||
|
if (!(isUnitTestMode && BaseRefactoringProcessor.ConflictsInTestsException.isTestIgnore())) {
|
||||||
|
val targetSourceRootPsi = targetSourceRoot?.toPsiDirectory(project)
|
||||||
|
if (targetSourceRootPsi != null) {
|
||||||
|
val conflictChecker = MoveConflictChecker(
|
||||||
|
project,
|
||||||
|
elementsToCopy,
|
||||||
|
KotlinDirectoryMoveTarget(FqName.ROOT, targetSourceRootPsi),
|
||||||
|
originalFile
|
||||||
|
)
|
||||||
|
conflictChecker.checkModuleConflictsInDeclarations(internalUsages, conflicts)
|
||||||
|
conflictChecker.checkVisibilityInDeclarations(conflicts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
project.checkConflictsInteractively(conflicts, onAccept = ::doRefactor)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun doClone(element: PsiElement) {
|
override fun doClone(element: PsiElement) {
|
||||||
|
|||||||
+2
-2
@@ -219,7 +219,7 @@ class MoveConflictChecker(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkModuleConflictsInDeclarations(
|
fun checkModuleConflictsInDeclarations(
|
||||||
internalUsages: MutableSet<UsageInfo>,
|
internalUsages: MutableSet<UsageInfo>,
|
||||||
conflicts: MultiMap<PsiElement, String>
|
conflicts: MultiMap<PsiElement, String>
|
||||||
) {
|
) {
|
||||||
@@ -327,7 +327,7 @@ class MoveConflictChecker(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkVisibilityInDeclarations(conflicts: MultiMap<PsiElement, String>) {
|
fun checkVisibilityInDeclarations(conflicts: MultiMap<PsiElement, String>) {
|
||||||
val targetContainer = moveTarget.getContainerDescriptor() ?: return
|
val targetContainer = moveTarget.getContainerDescriptor() ?: return
|
||||||
|
|
||||||
fun DeclarationDescriptor.targetAwareContainingDescriptor(): DeclarationDescriptor? {
|
fun DeclarationDescriptor.targetAwareContainingDescriptor(): DeclarationDescriptor? {
|
||||||
|
|||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
idea/testData/refactoring/copyMultiModule/internalReferencesToAnotherModule2/after/A/src/foo/test.kt
Vendored
+11
@@ -0,0 +1,11 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
internal class A {
|
||||||
|
val a: A = A()
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class B {
|
||||||
|
val a: A = A()
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="module" module-name="A" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
Vendored
+8
@@ -0,0 +1,8 @@
|
|||||||
|
package bar
|
||||||
|
|
||||||
|
import foo.B
|
||||||
|
|
||||||
|
internal class A {
|
||||||
|
val a: A = A()
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
package bar
|
||||||
Vendored
+11
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
internal class <caret>A {
|
||||||
|
val a: A = A()
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class B {
|
||||||
|
val a: A = A()
|
||||||
|
val b: B = B()
|
||||||
|
}
|
||||||
Vendored
+12
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="module" module-name="A" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
package bar
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
Class A uses class B which will be inaccessible after move
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"mainFile": "A/src/foo/test.kt",
|
||||||
|
"targetDirectory": "B/src/bar"
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
Vendored
+11
@@ -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()
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
package bar
|
||||||
|
|
||||||
|
class A {
|
||||||
|
val a: A = A()
|
||||||
|
val b: foo.B = foo.B()
|
||||||
|
}
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
package bar
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
Vendored
+11
@@ -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()
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
package bar
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
Class foo.B, referenced in property A.b, will not be accessible in module B
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"mainFile": "A/src/foo/test.kt",
|
||||||
|
"targetDirectory": "B/src/bar"
|
||||||
|
}
|
||||||
@@ -29,21 +29,37 @@ import org.jetbrains.kotlin.idea.jsonUtils.getString
|
|||||||
import org.jetbrains.kotlin.idea.refactoring.AbstractMultifileRefactoringTest
|
import org.jetbrains.kotlin.idea.refactoring.AbstractMultifileRefactoringTest
|
||||||
import org.jetbrains.kotlin.idea.refactoring.copy.CopyKotlinDeclarationsHandler.Companion.newName
|
import org.jetbrains.kotlin.idea.refactoring.copy.CopyKotlinDeclarationsHandler.Companion.newName
|
||||||
import org.jetbrains.kotlin.idea.refactoring.runRefactoringTest
|
import org.jetbrains.kotlin.idea.refactoring.runRefactoringTest
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.toPsiDirectory
|
||||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||||
import org.jetbrains.kotlin.utils.ifEmpty
|
import org.jetbrains.kotlin.utils.ifEmpty
|
||||||
|
|
||||||
abstract class AbstractCopyTest : AbstractMultifileRefactoringTest(), AbstractMultifileRefactoringTest.RefactoringAction {
|
abstract class AbstractCopyTest : AbstractMultifileRefactoringTest() {
|
||||||
override fun runRefactoring(rootDir: VirtualFile, mainFile: PsiFile, elementsAtCaret: List<PsiElement>, config: JsonObject) {
|
companion object : RefactoringAction {
|
||||||
val elementsToCopy = elementsAtCaret.ifEmpty { listOf(mainFile) }.toTypedArray()
|
fun runCopyRefactoring(path: String, config: JsonObject, rootDir: VirtualFile, project: Project) {
|
||||||
assert(CopyHandler.canCopy(elementsToCopy))
|
runRefactoringTest(path, config, rootDir, project, this)
|
||||||
|
}
|
||||||
|
|
||||||
val packageWrapper = PackageWrapper(mainFile.manager, config.getString("targetPackage"))
|
override fun runRefactoring(rootDir: VirtualFile, mainFile: PsiFile, elementsAtCaret: List<PsiElement>, config: JsonObject) {
|
||||||
project.newName = config.getNullableString("newName")
|
val project = mainFile.project
|
||||||
val targetDirectory = runWriteAction { MultipleRootsMoveDestination(packageWrapper).getTargetDirectory(mainFile) }
|
|
||||||
CopyHandler.doCopy(elementsToCopy, targetDirectory)
|
val elementsToCopy = elementsAtCaret.ifEmpty { listOf(mainFile) }.toTypedArray()
|
||||||
|
assert(CopyHandler.canCopy(elementsToCopy))
|
||||||
|
|
||||||
|
val targetDirectory = config.getNullableString("targetDirectory")?.let {
|
||||||
|
rootDir.findFileByRelativePath(it)?.toPsiDirectory(project)
|
||||||
|
}
|
||||||
|
?: run {
|
||||||
|
val packageWrapper = PackageWrapper(mainFile.manager, config.getString("targetPackage"))
|
||||||
|
runWriteAction { MultipleRootsMoveDestination(packageWrapper).getTargetDirectory(mainFile) }
|
||||||
|
}
|
||||||
|
|
||||||
|
project.newName = config.getNullableString("newName")
|
||||||
|
|
||||||
|
CopyHandler.doCopy(elementsToCopy, targetDirectory)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun runRefactoring(path: String, config: JsonObject, rootDir: VirtualFile, project: Project) {
|
override fun runRefactoring(path: String, config: JsonObject, rootDir: VirtualFile, project: Project) {
|
||||||
runRefactoringTest(path, config, rootDir, project, this)
|
runCopyRefactoring(path, config, rootDir, project)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* 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 org.jetbrains.kotlin.idea.refactoring.rename.loadTestConfiguration
|
||||||
|
import org.jetbrains.kotlin.idea.test.KotlinMultiFileTestCase
|
||||||
|
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
abstract class AbstractMultiModuleCopyTest : KotlinMultiFileTestCase() {
|
||||||
|
override fun getTestRoot(): String = "/refactoring/copyMultiModule/"
|
||||||
|
|
||||||
|
override fun getTestDataPath(): String = PluginTestCaseBase.getTestDataPathBase()
|
||||||
|
|
||||||
|
fun doTest(path: String) {
|
||||||
|
val config = loadTestConfiguration(File(path))
|
||||||
|
|
||||||
|
isMultiModule = true
|
||||||
|
|
||||||
|
doTestCommittingDocuments { rootDir, _ ->
|
||||||
|
AbstractCopyTest.runCopyRefactoring(path, config, rootDir, project)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* 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/copyMultiModule")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public class MultiModuleCopyTestGenerated extends AbstractMultiModuleCopyTest {
|
||||||
|
public void testAllFilesPresentInCopyMultiModule() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentInSingleGeneratedClass(this.getClass(), new File("idea/testData/refactoring/copyMultiModule"), Pattern.compile("^(.+)\\.test$"), TargetBackend.ANY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("internalReferencesToAnotherModule2/internalReferencesToAnotherModule.test")
|
||||||
|
public void testInternalReferencesToAnotherModule2_InternalReferencesToAnotherModule() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/copyMultiModule/internalReferencesToAnotherModule2/internalReferencesToAnotherModule.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("referencesToUnrelatedModule/referencesToUnrelatedModule.test")
|
||||||
|
public void testReferencesToUnrelatedModule_ReferencesToUnrelatedModule() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/copyMultiModule/referencesToUnrelatedModule/referencesToUnrelatedModule.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user