Move: Fix exception on moving top-level declaration from/to script file

#KT-20260 Fixed
This commit is contained in:
Alexey Sedunov
2018-05-31 14:48:21 +03:00
parent c63854948b
commit 3c267b206b
21 changed files with 72 additions and 5 deletions
@@ -578,4 +578,13 @@ fun KtNamedDeclaration.safeFqNameForLazyResolve(): FqName? {
//NOTE: should only create special names for package level declarations, so we can safely rely on real fq name for parent
val parentFqName = KtNamedDeclarationUtil.getParentFqName(this)
return parentFqName?.child(safeNameForLazyResolve())
}
fun isTopLevelInFileOrScript(element: PsiElement): Boolean {
val parent = element.parent
return when (parent) {
is KtFile -> true
is KtBlockExpression -> parent.parent is KtScript
else -> false
}
}
@@ -43,12 +43,13 @@ import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.psi.psiUtil.isTopLevelInFileOrScript
import java.util.*
class MoveKotlinDeclarationsHandler : MoveHandlerDelegate() {
private fun getUniqueContainer(elements: Array<out PsiElement>): PsiElement? {
val getContainer: (PsiElement) -> PsiElement? =
if (elements.any { it.parent !is KtFile }) { e ->
if (elements.any { !isTopLevelInFileOrScript(it) }) { e ->
when (e) {
is KtNamedDeclaration -> e.containingClassOrObject ?: e.parent
is KtFile -> e.parent
@@ -43,12 +43,13 @@ import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.psi.psiUtil.isTopLevelInFileOrScript
import java.util.*
class MoveKotlinDeclarationsHandler : MoveHandlerDelegate() {
private fun getUniqueContainer(elements: Array<out PsiElement>): PsiElement? {
val getContainer: (PsiElement) -> PsiElement? =
if (elements.any { it.parent !is KtFile }) { e ->
if (elements.any { !isTopLevelInFileOrScript(it) }) { e ->
when (e) {
is KtNamedDeclaration -> e.containingClassOrObject ?: e.parent
is KtFile -> e.parent
@@ -71,7 +71,11 @@ interface Mover : (KtNamedDeclaration, KtElement) -> KtNamedDeclaration {
object Default : Mover {
override fun invoke(originalElement: KtNamedDeclaration, targetContainer: KtElement): KtNamedDeclaration {
return when (targetContainer) {
is KtFile -> targetContainer.add(originalElement) as KtNamedDeclaration
is KtFile -> {
val declarationContainer: KtElement =
if (targetContainer.isScript()) targetContainer.script!!.blockExpression else targetContainer
declarationContainer.add(originalElement) as KtNamedDeclaration
}
is KtClassOrObject -> targetContainer.addDeclaration(originalElement)
else -> error("Unexpected element: ${targetContainer.getElementTextWithContext()}")
}.apply {
@@ -66,6 +66,7 @@ import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.*;
import org.jetbrains.kotlin.idea.refactoring.ui.KotlinFileChooserDialog;
import org.jetbrains.kotlin.idea.util.application.ApplicationUtilsKt;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.KtDeclarationContainer;
import org.jetbrains.kotlin.psi.KtFile;
import org.jetbrains.kotlin.psi.KtNamedDeclaration;
@@ -77,6 +78,8 @@ import java.io.File;
import java.util.*;
import java.util.List;
import static java.util.Collections.emptyList;
public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
private static final String RECENTS_KEY = "MoveKotlinTopLevelDeclarationsDialog.RECENTS_KEY";
private final MoveCallback moveCallback;
@@ -169,7 +172,8 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
new Function1<KtFile, Iterable<?>>() {
@Override
public Iterable<?> invoke(KtFile jetFile) {
return jetFile.getDeclarations();
KtDeclarationContainer container = jetFile.isScript() ? jetFile.getScript() : jetFile;
return container != null ? container.getDeclarations() : emptyList();
}
}
),
@@ -190,7 +194,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
@Nullable String targetFileName,
@Nullable final PsiDirectory targetDirectory
) {
if (targetDirectory == null) return Collections.emptyList();
if (targetDirectory == null) return emptyList();
List<String> fileNames =
targetFileName != null
@@ -0,0 +1,3 @@
class Foo
class <caret>Bar
@@ -0,0 +1,5 @@
{
"mainFile": "source/test.kt",
"type": "MOVE_KOTLIN_TOP_LEVEL_DECLARATIONS",
"targetFile": "source/testNew.kts"
}
@@ -0,0 +1,3 @@
class Foo
class <caret>Bar
@@ -0,0 +1,5 @@
{
"mainFile": "source/test.kts",
"type": "MOVE_KOTLIN_TOP_LEVEL_DECLARATIONS",
"targetFile": "source/testNew.kt"
}
@@ -0,0 +1,3 @@
class Foo
class <caret>Bar
@@ -0,0 +1,5 @@
{
"mainFile": "source/test.kts",
"type": "MOVE_KOTLIN_TOP_LEVEL_DECLARATIONS",
"targetFile": "source/testNew.kts"
}
@@ -479,6 +479,21 @@ public class MoveTestGenerated extends AbstractMoveTest {
runTest("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/implicitInvokeCalls/differentTarget/differentTarget.test");
}
@TestMetadata("kotlin/moveTopLevelDeclarations/misc/classFromKtsToKt/classFromKtsToKt.test")
public void testKotlin_moveTopLevelDeclarations_misc_classFromKtsToKt_ClassFromKtsToKt() throws Exception {
runTest("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/classFromKtsToKt/classFromKtsToKt.test");
}
@TestMetadata("kotlin/moveTopLevelDeclarations/misc/classFromKtsToKts/classFromKtsToKts.test")
public void testKotlin_moveTopLevelDeclarations_misc_classFromKtsToKts_ClassFromKtsToKts() throws Exception {
runTest("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/classFromKtsToKts/classFromKtsToKts.test");
}
@TestMetadata("kotlin/moveTopLevelDeclarations/misc/classFromKtToKts/classFromKtToKts.test")
public void testKotlin_moveTopLevelDeclarations_misc_classFromKtToKts_ClassFromKtToKts() throws Exception {
runTest("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/classFromKtToKts/classFromKtToKts.test");
}
@TestMetadata("kotlin/moveTopLevelDeclarations/misc/classWithInitializer/delegateInObject.test")
public void testKotlin_moveTopLevelDeclarations_misc_classWithInitializer_DelegateInObject() throws Exception {
runTest("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/classWithInitializer/delegateInObject.test");