Copy: Improve file copying
- Do not copy individual declarations if entire file is selected (keeping original content with comments, formatting, etc.) - Update package directive when original file packages matches its directory #KT-20092 Fixed #KT-18196 Fixed
This commit is contained in:
@@ -76,8 +76,18 @@ open class KtFile(viewProvider: FileViewProvider, val isCompiled: Boolean) :
|
|||||||
return if (ast != null) ast.psi as KtPackageDirective else null
|
return if (ast != null) ast.psi as KtPackageDirective else null
|
||||||
}
|
}
|
||||||
|
|
||||||
val packageFqName: FqName
|
var packageFqName: FqName
|
||||||
get() = stub?.getPackageFqName() ?: packageFqNameByTree
|
get() = stub?.getPackageFqName() ?: packageFqNameByTree
|
||||||
|
set(value) {
|
||||||
|
val packageDirective = packageDirective
|
||||||
|
if (packageDirective != null) {
|
||||||
|
packageDirective.fqName = value
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val newPackageDirective = KtPsiFactory(this).createPackageDirectiveIfNeeded(value) ?: return
|
||||||
|
addAfter(newPackageDirective, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val packageFqNameByTree: FqName
|
val packageFqNameByTree: FqName
|
||||||
get() = packageDirectiveByTree?.fqName ?: FqName.ROOT
|
get() = packageDirectiveByTree?.fqName ?: FqName.ROOT
|
||||||
|
|||||||
+12
-4
@@ -39,6 +39,8 @@ import com.intellij.util.IncorrectOperationException
|
|||||||
import com.intellij.util.containers.MultiMap
|
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.getPackage
|
||||||
|
import org.jetbrains.kotlin.idea.core.packageMatchesDirectory
|
||||||
import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively
|
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.*
|
||||||
@@ -63,8 +65,11 @@ 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<KtElement> {
|
private fun PsiElement.getCopyableElement() =
|
||||||
val declarationOrFile = parentsWithSelf.firstOrNull { it is KtFile || (it is KtNamedDeclaration && it.parent is KtFile) }
|
parentsWithSelf.firstOrNull { it is KtFile || (it is KtNamedDeclaration && it.parent is KtFile) } as? KtElement
|
||||||
|
|
||||||
|
private fun PsiElement.getDeclarationsToCopy(): List<KtElement> {
|
||||||
|
val declarationOrFile = getCopyableElement()
|
||||||
return when (declarationOrFile) {
|
return when (declarationOrFile) {
|
||||||
is KtFile -> declarationOrFile.declarations.filterIsInstance<KtNamedDeclaration>().ifEmpty { listOf(declarationOrFile) }
|
is KtFile -> declarationOrFile.declarations.filterIsInstance<KtNamedDeclaration>().ifEmpty { listOf(declarationOrFile) }
|
||||||
is KtNamedDeclaration -> listOf(declarationOrFile)
|
is KtNamedDeclaration -> listOf(declarationOrFile)
|
||||||
@@ -90,7 +95,7 @@ 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.getElementsToCopy().ifEmpty { return false } }
|
.flatMap { it.getDeclarationsToCopy().ifEmpty { return false } }
|
||||||
.distinctBy { it.containingFile }
|
.distinctBy { it.containingFile }
|
||||||
.singleOrNull()
|
.singleOrNull()
|
||||||
?.containingFile ?: return false
|
?.containingFile ?: return false
|
||||||
@@ -174,7 +179,7 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
|
|||||||
return copyFilesHandler.doCopy(sourceFiles, defaultTargetDirectory)
|
return copyFilesHandler.doCopy(sourceFiles, defaultTargetDirectory)
|
||||||
}
|
}
|
||||||
|
|
||||||
val elementsToCopy = elements.flatMap { it.getElementsToCopy() }
|
val elementsToCopy = elements.mapNotNull { it.getCopyableElement() }
|
||||||
if (elementsToCopy.isEmpty()) return
|
if (elementsToCopy.isEmpty()) return
|
||||||
|
|
||||||
val singleElementToCopy = elementsToCopy.singleOrNull()
|
val singleElementToCopy = elementsToCopy.singleOrNull()
|
||||||
@@ -248,6 +253,9 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
|
|||||||
if (singleElementToCopy is KtFile) {
|
if (singleElementToCopy is KtFile) {
|
||||||
targetFile = runWriteAction {
|
targetFile = runWriteAction {
|
||||||
val copiedFile = targetDirectory.copyFileFrom(targetFileName, singleElementToCopy) as KtFile
|
val copiedFile = targetDirectory.copyFileFrom(targetFileName, singleElementToCopy) as KtFile
|
||||||
|
if (singleElementToCopy.packageMatchesDirectory()) {
|
||||||
|
targetDirectory.getPackage()?.qualifiedName?.let { copiedFile.packageFqName = FqName(it) }
|
||||||
|
}
|
||||||
performDelayedRefactoringRequests(project)
|
performDelayedRefactoringRequests(project)
|
||||||
copiedFile
|
copiedFile
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package bar
|
||||||
|
|
||||||
|
val a = 42
|
||||||
|
|
||||||
|
// comment
|
||||||
|
|
||||||
|
// some other comment
|
||||||
|
val s = ""
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
val a = 42
|
||||||
|
|
||||||
|
// comment
|
||||||
|
|
||||||
|
// some other comment
|
||||||
|
val s = ""
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
val a = 42
|
||||||
|
|
||||||
|
// comment
|
||||||
|
|
||||||
|
// some other comment
|
||||||
|
val s = ""
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"mainFile": "test.kt",
|
||||||
|
"targetPackage": "bar"
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package bar
|
||||||
|
|
||||||
|
val a = 42
|
||||||
|
|
||||||
|
// comment
|
||||||
|
|
||||||
|
// some other comment
|
||||||
|
val s = ""
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
val a = 42
|
||||||
|
|
||||||
|
// comment
|
||||||
|
|
||||||
|
// some other comment
|
||||||
|
val s = ""
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
val a = 42
|
||||||
|
|
||||||
|
// comment
|
||||||
|
|
||||||
|
// some other comment
|
||||||
|
val s = ""
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"mainFile": "foo/test.kt",
|
||||||
|
"targetPackage": "bar"
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
val a = 42
|
||||||
|
|
||||||
|
// comment
|
||||||
|
|
||||||
|
// some other comment
|
||||||
|
val s = ""
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
val a = 42
|
||||||
|
|
||||||
|
// comment
|
||||||
|
|
||||||
|
// some other comment
|
||||||
|
val s = ""
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
val a = 42
|
||||||
|
|
||||||
|
// comment
|
||||||
|
|
||||||
|
// some other comment
|
||||||
|
val s = ""
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"mainFile": "foo/test.kt",
|
||||||
|
"targetPackage": ""
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
package baz
|
||||||
|
|
||||||
|
val a = 42
|
||||||
|
|
||||||
|
// comment
|
||||||
|
|
||||||
|
// some other comment
|
||||||
|
val s = ""
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
package baz
|
||||||
|
|
||||||
|
val a = 42
|
||||||
|
|
||||||
|
// comment
|
||||||
|
|
||||||
|
// some other comment
|
||||||
|
val s = ""
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
package baz
|
||||||
|
|
||||||
|
val a = 42
|
||||||
|
|
||||||
|
// comment
|
||||||
|
|
||||||
|
// some other comment
|
||||||
|
val s = ""
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"mainFile": "foo/test.kt",
|
||||||
|
"targetPackage": "bar"
|
||||||
|
}
|
||||||
@@ -8,4 +8,4 @@ class A {
|
|||||||
class B {
|
class B {
|
||||||
val a: A = A()
|
val a: A = A()
|
||||||
val b: B = B()
|
val b: B = B()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
package refactor.copy2
|
package refactor.copy2
|
||||||
|
|
||||||
import refactor.ParentJava
|
import refactor.ParentJava
|
||||||
import refactor.copy.Company
|
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
enum class Possible {
|
enum class Possible {
|
||||||
NO, YES
|
NO, YES
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Potable(val p1: String)
|
data class Potable(val p1: String)
|
||||||
class Insider(val peace: String)
|
class Insider(val peace: String)
|
||||||
|
|
||||||
class Init {
|
class Init {
|
||||||
fun referred() = 0
|
fun referred() = 0
|
||||||
fun moved() = referred()
|
fun moved() = referred()
|
||||||
@@ -28,6 +27,7 @@ class Simple {
|
|||||||
}
|
}
|
||||||
|
|
||||||
annotation class MemAnn
|
annotation class MemAnn
|
||||||
|
|
||||||
class Variety<C> {
|
class Variety<C> {
|
||||||
// object
|
// object
|
||||||
object ExtractedObject {}
|
object ExtractedObject {}
|
||||||
@@ -42,8 +42,7 @@ class Variety<C> {
|
|||||||
private fun privateFun() = 0
|
private fun privateFun() = 0
|
||||||
fun <T> genFunB(p: T): T = p
|
fun <T> genFunB(p: T): T = p
|
||||||
fun <T> genFunC(p: T): C where T : C = p
|
fun <T> genFunC(p: T): C where T : C = p
|
||||||
@MemAnn
|
@MemAnn fun annotatedFun() = 0
|
||||||
fun annotatedFun() = 0
|
|
||||||
final fun finalFun() = 0
|
final fun finalFun() = 0
|
||||||
// property
|
// property
|
||||||
var publicProp = 0
|
var publicProp = 0
|
||||||
@@ -56,8 +55,7 @@ class Variety<C> {
|
|||||||
var <T> List<T>.genVarL: T where T : C
|
var <T> List<T>.genVarL: T where T : C
|
||||||
get() = last()
|
get() = last()
|
||||||
set(p) {}
|
set(p) {}
|
||||||
@MemAnn
|
@MemAnn val annotatedVal = 0
|
||||||
val annotatedVal = 0
|
|
||||||
var byVar by Delegates.notNull<Int>()
|
var byVar by Delegates.notNull<Int>()
|
||||||
lateinit var lateVal: String
|
lateinit var lateVal: String
|
||||||
final val finalVal = 0
|
final val finalVal = 0
|
||||||
@@ -146,9 +144,9 @@ class CtorParameterChild(val pvc: String, var prc: String) : CtorParameter(pvc,
|
|||||||
class CtorParameterChild2: CtorParameter {
|
class CtorParameterChild2: CtorParameter {
|
||||||
constructor() : super("", "", "")
|
constructor() : super("", "", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
class CtorParameterChild3(override val pv: String, override var pr: String) : CtorParameter(pv, pv, pr)
|
class CtorParameterChild3(override val pv: String, override var pr: String) : CtorParameter(pv, pv, pr)
|
||||||
data class CtorData(val pv: String, var pr: String) {}
|
data class CtorData(val pv: String, var pr: String) {}
|
||||||
|
|
||||||
class Company {
|
class Company {
|
||||||
companion object {
|
companion object {
|
||||||
val companyVal = 0
|
val companyVal = 0
|
||||||
|
|||||||
@@ -72,6 +72,30 @@ public class CopyTestGenerated extends AbstractCopyTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("copyFIleFromDefaultPackage/copyFIleFromDefaultPackage.test")
|
||||||
|
public void testCopyFIleFromDefaultPackage_CopyFIleFromDefaultPackage() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/copy/copyFIleFromDefaultPackage/copyFIleFromDefaultPackage.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("copyFIleRetainContent/copyFIleRetainContent.test")
|
||||||
|
public void testCopyFIleRetainContent_CopyFIleRetainContent() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/copy/copyFIleRetainContent/copyFIleRetainContent.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("copyFIleToDefaultPackage/copyFIleToDefaultPackage.test")
|
||||||
|
public void testCopyFIleToDefaultPackage_CopyFIleToDefaultPackage() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/copy/copyFIleToDefaultPackage/copyFIleToDefaultPackage.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("copyFIleWithPackageAndDirUnmatched/copyFIleWithPackageAndDirUnmatched.test")
|
||||||
|
public void testCopyFIleWithPackageAndDirUnmatched_CopyFIleWithPackageAndDirUnmatched() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/copy/copyFIleWithPackageAndDirUnmatched/copyFIleWithPackageAndDirUnmatched.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("copyFunCallQualificationWithParentheses/copyFunCallQualificationWithParentheses.test")
|
@TestMetadata("copyFunCallQualificationWithParentheses/copyFunCallQualificationWithParentheses.test")
|
||||||
public void testCopyFunCallQualificationWithParentheses_CopyFunCallQualificationWithParentheses() throws Exception {
|
public void testCopyFunCallQualificationWithParentheses_CopyFunCallQualificationWithParentheses() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/copy/copyFunCallQualificationWithParentheses/copyFunCallQualificationWithParentheses.test");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/copy/copyFunCallQualificationWithParentheses/copyFunCallQualificationWithParentheses.test");
|
||||||
|
|||||||
Reference in New Issue
Block a user