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
|
||||
}
|
||||
|
||||
val packageFqName: FqName
|
||||
var packageFqName: FqName
|
||||
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
|
||||
get() = packageDirectiveByTree?.fqName ?: FqName.ROOT
|
||||
|
||||
+12
-4
@@ -39,6 +39,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.refactoring.checkConflictsInteractively
|
||||
import org.jetbrains.kotlin.idea.refactoring.createKotlinFile
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.*
|
||||
@@ -63,8 +65,11 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
|
||||
@set:TestOnly
|
||||
var Project.newName: String? by UserDataProperty(Key.create("NEW_NAME"))
|
||||
|
||||
private fun PsiElement.getElementsToCopy(): List<KtElement> {
|
||||
val declarationOrFile = parentsWithSelf.firstOrNull { it is KtFile || (it is KtNamedDeclaration && it.parent is KtFile) }
|
||||
private fun PsiElement.getCopyableElement() =
|
||||
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) {
|
||||
is KtFile -> declarationOrFile.declarations.filterIsInstance<KtNamedDeclaration>().ifEmpty { listOf(declarationOrFile) }
|
||||
is KtNamedDeclaration -> listOf(declarationOrFile)
|
||||
@@ -90,7 +95,7 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
|
||||
private fun canCopyDeclarations(elements: Array<out PsiElement>): Boolean {
|
||||
val containingFile =
|
||||
elements
|
||||
.flatMap { it.getElementsToCopy().ifEmpty { return false } }
|
||||
.flatMap { it.getDeclarationsToCopy().ifEmpty { return false } }
|
||||
.distinctBy { it.containingFile }
|
||||
.singleOrNull()
|
||||
?.containingFile ?: return false
|
||||
@@ -174,7 +179,7 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
|
||||
return copyFilesHandler.doCopy(sourceFiles, defaultTargetDirectory)
|
||||
}
|
||||
|
||||
val elementsToCopy = elements.flatMap { it.getElementsToCopy() }
|
||||
val elementsToCopy = elements.mapNotNull { it.getCopyableElement() }
|
||||
if (elementsToCopy.isEmpty()) return
|
||||
|
||||
val singleElementToCopy = elementsToCopy.singleOrNull()
|
||||
@@ -248,6 +253,9 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
|
||||
if (singleElementToCopy is KtFile) {
|
||||
targetFile = runWriteAction {
|
||||
val copiedFile = targetDirectory.copyFileFrom(targetFileName, singleElementToCopy) as KtFile
|
||||
if (singleElementToCopy.packageMatchesDirectory()) {
|
||||
targetDirectory.getPackage()?.qualifiedName?.let { copiedFile.packageFqName = FqName(it) }
|
||||
}
|
||||
performDelayedRefactoringRequests(project)
|
||||
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 {
|
||||
val a: A = A()
|
||||
val b: B = B()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
package refactor.copy2
|
||||
|
||||
import refactor.ParentJava
|
||||
import refactor.copy.Company
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
enum class Possible {
|
||||
NO, YES
|
||||
}
|
||||
|
||||
data class Potable(val p1: String)
|
||||
class Insider(val peace: String)
|
||||
|
||||
class Init {
|
||||
fun referred() = 0
|
||||
fun moved() = referred()
|
||||
@@ -28,6 +27,7 @@ class Simple {
|
||||
}
|
||||
|
||||
annotation class MemAnn
|
||||
|
||||
class Variety<C> {
|
||||
// object
|
||||
object ExtractedObject {}
|
||||
@@ -42,8 +42,7 @@ class Variety<C> {
|
||||
private fun privateFun() = 0
|
||||
fun <T> genFunB(p: T): T = p
|
||||
fun <T> genFunC(p: T): C where T : C = p
|
||||
@MemAnn
|
||||
fun annotatedFun() = 0
|
||||
@MemAnn fun annotatedFun() = 0
|
||||
final fun finalFun() = 0
|
||||
// property
|
||||
var publicProp = 0
|
||||
@@ -56,8 +55,7 @@ class Variety<C> {
|
||||
var <T> List<T>.genVarL: T where T : C
|
||||
get() = last()
|
||||
set(p) {}
|
||||
@MemAnn
|
||||
val annotatedVal = 0
|
||||
@MemAnn val annotatedVal = 0
|
||||
var byVar by Delegates.notNull<Int>()
|
||||
lateinit var lateVal: String
|
||||
final val finalVal = 0
|
||||
@@ -146,9 +144,9 @@ class CtorParameterChild(val pvc: String, var prc: String) : CtorParameter(pvc,
|
||||
class CtorParameterChild2: CtorParameter {
|
||||
constructor() : super("", "", "")
|
||||
}
|
||||
|
||||
class CtorParameterChild3(override val pv: String, override var pr: String) : CtorParameter(pv, pv, pr)
|
||||
data class CtorData(val pv: String, var pr: String) {}
|
||||
|
||||
class Company {
|
||||
companion object {
|
||||
val companyVal = 0
|
||||
|
||||
@@ -72,6 +72,30 @@ public class CopyTestGenerated extends AbstractCopyTest {
|
||||
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")
|
||||
public void testCopyFunCallQualificationWithParentheses_CopyFunCallQualificationWithParentheses() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/copy/copyFunCallQualificationWithParentheses/copyFunCallQualificationWithParentheses.test");
|
||||
|
||||
Reference in New Issue
Block a user