From 5cfb07072c589ecf0361edb0449e58c1ef90c57b Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Mon, 20 Jan 2014 15:24:55 +0400 Subject: [PATCH] Reuse Java safe delete for Kotlin classes and objects --- .../jetbrains/jet/lang/psi/JetPsiUtil.java | 9 ++-- .../jet/lang/psi/psiUtil/jetPsiUtil.kt | 7 ++- .../safeDelete/KotlinSafeDeleteProcessor.java | 45 ++++--------------- .../plugin/references/JetPsiReference.java | 13 ++++-- .../deleteClass/kotlinClass/localClass1.kt | 7 +++ .../kotlinClass/localClass1.kt.messages | 1 + .../deleteClass/kotlinClass/localClass2.kt | 5 +++ .../kotlinClass/localClass2.kt.after | 2 + .../deleteObject/kotlinObject/localObject1.kt | 7 +++ .../kotlinObject/localObject1.kt.messages | 1 + .../deleteObject/kotlinObject/localObject2.kt | 5 +++ .../kotlinObject/localObject2.kt.after | 2 + .../JetSafeDeleteTestGenerated.java | 20 +++++++++ 13 files changed, 78 insertions(+), 46 deletions(-) create mode 100644 idea/testData/safeDelete/deleteClass/kotlinClass/localClass1.kt create mode 100644 idea/testData/safeDelete/deleteClass/kotlinClass/localClass1.kt.messages create mode 100644 idea/testData/safeDelete/deleteClass/kotlinClass/localClass2.kt create mode 100644 idea/testData/safeDelete/deleteClass/kotlinClass/localClass2.kt.after create mode 100644 idea/testData/safeDelete/deleteObject/kotlinObject/localObject1.kt create mode 100644 idea/testData/safeDelete/deleteObject/kotlinObject/localObject1.kt.messages create mode 100644 idea/testData/safeDelete/deleteObject/kotlinObject/localObject2.kt create mode 100644 idea/testData/safeDelete/deleteObject/kotlinObject/localObject2.kt.after diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiUtil.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiUtil.java index 6ce6b4dfb36..e307ca503ac 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiUtil.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiUtil.java @@ -372,14 +372,13 @@ public class JetPsiUtil { public static void deleteClass(@NotNull JetClassOrObject clazz) { CheckUtil.checkWritable(clazz); JetFile file = (JetFile) clazz.getContainingFile(); - List declarations = file.getDeclarations(); - if (declarations.size() == 1) { - file.delete(); - } - else { + if (isLocal(clazz) || file.getDeclarations().size() > 1) { PsiElement parent = clazz.getParent(); CodeEditUtil.removeChild(parent.getNode(), clazz.getNode()); } + else { + file.delete(); + } } @Nullable diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/psiUtil/jetPsiUtil.kt b/compiler/frontend/src/org/jetbrains/jet/lang/psi/psiUtil/jetPsiUtil.kt index bd04e0c8a2b..e80c13d7201 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/psiUtil/jetPsiUtil.kt +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/psiUtil/jetPsiUtil.kt @@ -39,6 +39,8 @@ import com.intellij.psi.search.SearchScope import com.intellij.psi.search.PsiSearchScopeUtil import org.jetbrains.jet.lang.psi.JetClassBody import org.jetbrains.jet.lang.psi.JetParameterList +import org.jetbrains.jet.lang.psi.JetNamedDeclaration +import com.intellij.psi.PsiNamedElement fun PsiElement.getParentByTypesAndPredicate( strict : Boolean = false, vararg parentClasses : Class, predicate: (T) -> Boolean @@ -170,4 +172,7 @@ fun JetDeclaration.isOverridable(): Boolean { return klass.isTrait() || hasModifier(JetTokens.ABSTRACT_KEYWORD) || hasModifier(JetTokens.OPEN_KEYWORD) || hasModifier(JetTokens.OVERRIDE_KEYWORD) -} \ No newline at end of file +} + +val PsiElement.namedNavigationElement: PsiNamedElement? + get() = getNavigationElement()?.getParentByType(javaClass()) \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/safeDelete/KotlinSafeDeleteProcessor.java b/idea/src/org/jetbrains/jet/plugin/refactoring/safeDelete/KotlinSafeDeleteProcessor.java index c80493ae6e2..b8bd4ccd30a 100644 --- a/idea/src/org/jetbrains/jet/plugin/refactoring/safeDelete/KotlinSafeDeleteProcessor.java +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/safeDelete/KotlinSafeDeleteProcessor.java @@ -99,7 +99,7 @@ public class KotlinSafeDeleteProcessor extends JavaSafeDeleteProcessor { @NotNull PsiElement element, @NotNull PsiElement[] allElementsToDelete, @NotNull List result ) { if (element instanceof JetClassOrObject) { - return findClassOrObjectUsages(element, (JetClassOrObject) element, allElementsToDelete, result); + return delegateToJavaProcessor(LightClassUtil.getPsiClass((JetClassOrObject) element), allElementsToDelete, result); } if (element instanceof JetNamedFunction) { JetNamedFunction function = (JetNamedFunction) element; @@ -107,12 +107,12 @@ public class KotlinSafeDeleteProcessor extends JavaSafeDeleteProcessor { return findLocalDeclarationUsages(function, allElementsToDelete, result); } PsiMethod method = LightClassUtil.getLightClassMethod((JetNamedFunction) element); - if (method != null) return findPsiMethodUsages(method, allElementsToDelete, result); + if (method != null) return delegateToJavaProcessor(method, allElementsToDelete, result); return getSearchInfo(element, allElementsToDelete); } if (element instanceof PsiMethod) { - return findPsiMethodUsages((PsiMethod) element, allElementsToDelete, result); + return delegateToJavaProcessor(element, allElementsToDelete, result); } if (element instanceof JetProperty) { JetProperty property = (JetProperty) element; @@ -160,42 +160,16 @@ public class KotlinSafeDeleteProcessor extends JavaSafeDeleteProcessor { return JavaSafeDeleteProcessor.isInside(place, ancestor); } - @NotNull - protected static NonCodeUsageSearchInfo findClassOrObjectUsages( - @NotNull PsiElement referencedElement, - @NotNull final JetClassOrObject classOrObject, - @NotNull final PsiElement[] allElementsToDelete, - @NotNull final List result - ) { - ReferencesSearch.search(referencedElement).forEach(new Processor() { - @Override - public boolean process(PsiReference reference) { - PsiElement element = reference.getElement(); - - if (!isInside(element, allElementsToDelete)) { - JetImportDirective importDirective = PsiTreeUtil.getParentOfType(element, JetImportDirective.class, false); - if (importDirective != null) { - result.add(new SafeDeleteImportDirectiveUsageInfo(importDirective, classOrObject)); - return true; - } - - result.add(new SafeDeleteReferenceSimpleDeleteUsageInfo(element, classOrObject, false)); - } - return true; - } - }); - - return getSearchInfo(referencedElement, allElementsToDelete); - } - @Nullable - protected NonCodeUsageSearchInfo findPsiMethodUsages( - @NotNull PsiMethod method, + protected NonCodeUsageSearchInfo delegateToJavaProcessor( + @Nullable PsiElement element, @NotNull PsiElement[] allElementsToDelete, @NotNull List result ) { + if (element == null) return null; + List javaUsages = new ArrayList(); - NonCodeUsageSearchInfo searchInfo = super.findUsages(method, allElementsToDelete, javaUsages); + NonCodeUsageSearchInfo searchInfo = super.findUsages(element, allElementsToDelete, javaUsages); for (UsageInfo usageInfo : javaUsages) { if (usageInfo instanceof SafeDeleteOverridingMethodUsageInfo) { @@ -220,8 +194,7 @@ public class KotlinSafeDeleteProcessor extends JavaSafeDeleteProcessor { PsiElement usageElement = javaDeleteUsageInfo.getElement(); JetImportDirective importDirective = PsiTreeUtil.getParentOfType(usageElement, JetImportDirective.class, false); if (importDirective != null) { - usageInfo = SafeDeleteImportDirectiveUsageInfo.object$.create(importDirective, - (JetDeclaration) method.getNavigationElement()); + usageInfo = new SafeDeleteImportDirectiveUsageInfo(importDirective, (JetDeclaration) element.getNavigationElement()); } } if (usageInfo != null) { diff --git a/idea/src/org/jetbrains/jet/plugin/references/JetPsiReference.java b/idea/src/org/jetbrains/jet/plugin/references/JetPsiReference.java index ad778aa9aa1..1a6f0516c1a 100644 --- a/idea/src/org/jetbrains/jet/plugin/references/JetPsiReference.java +++ b/idea/src/org/jetbrains/jet/plugin/references/JetPsiReference.java @@ -27,10 +27,11 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.descriptors.PackageViewDescriptor; +import org.jetbrains.jet.lang.psi.JetNamedDeclaration; import org.jetbrains.jet.lang.psi.JetReferenceExpression; +import org.jetbrains.jet.lang.psi.psiUtil.PsiUtilPackage; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingContextUtils; -import org.jetbrains.jet.lang.resolve.java.jetAsJava.KotlinLightMethod; import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache; import java.util.Collection; @@ -90,9 +91,13 @@ public abstract class JetPsiReference implements PsiPolyVariantReference { @Override public boolean isReferenceTo(PsiElement element) { - PsiElement target = resolve(); - PsiElement mirrorElement = element instanceof KotlinLightMethod ? ((KotlinLightMethod) element).getOrigin() : null; - return target == element || (mirrorElement != null && target == mirrorElement) || (target != null && target.getNavigationElement() == element); + PsiElement resolvedElement = resolve(); + if (resolvedElement == null) return false; + + PsiNamedElement namedDeclaration = PsiUtilPackage.getNamedNavigationElement(element); + PsiNamedElement namedResolvedDeclaration = PsiUtilPackage.getNamedNavigationElement(resolvedElement); + + return namedDeclaration != null && namedDeclaration.equals(namedResolvedDeclaration); } @NotNull diff --git a/idea/testData/safeDelete/deleteClass/kotlinClass/localClass1.kt b/idea/testData/safeDelete/deleteClass/kotlinClass/localClass1.kt new file mode 100644 index 00000000000..5b609223623 --- /dev/null +++ b/idea/testData/safeDelete/deleteClass/kotlinClass/localClass1.kt @@ -0,0 +1,7 @@ +fun foo() { + class A { + + } + + A() +} \ No newline at end of file diff --git a/idea/testData/safeDelete/deleteClass/kotlinClass/localClass1.kt.messages b/idea/testData/safeDelete/deleteClass/kotlinClass/localClass1.kt.messages new file mode 100644 index 00000000000..abe6f5fd434 --- /dev/null +++ b/idea/testData/safeDelete/deleteClass/kotlinClass/localClass1.kt.messages @@ -0,0 +1 @@ +class foo.A has 1 usage that is not safe to delete. \ No newline at end of file diff --git a/idea/testData/safeDelete/deleteClass/kotlinClass/localClass2.kt b/idea/testData/safeDelete/deleteClass/kotlinClass/localClass2.kt new file mode 100644 index 00000000000..64e89b3ed79 --- /dev/null +++ b/idea/testData/safeDelete/deleteClass/kotlinClass/localClass2.kt @@ -0,0 +1,5 @@ +fun foo() { + class A { + + } +} \ No newline at end of file diff --git a/idea/testData/safeDelete/deleteClass/kotlinClass/localClass2.kt.after b/idea/testData/safeDelete/deleteClass/kotlinClass/localClass2.kt.after new file mode 100644 index 00000000000..f23d7f69d2f --- /dev/null +++ b/idea/testData/safeDelete/deleteClass/kotlinClass/localClass2.kt.after @@ -0,0 +1,2 @@ +fun foo() { +} \ No newline at end of file diff --git a/idea/testData/safeDelete/deleteObject/kotlinObject/localObject1.kt b/idea/testData/safeDelete/deleteObject/kotlinObject/localObject1.kt new file mode 100644 index 00000000000..541448d2d2c --- /dev/null +++ b/idea/testData/safeDelete/deleteObject/kotlinObject/localObject1.kt @@ -0,0 +1,7 @@ +fun foo() { + object A { + + } + + A +} \ No newline at end of file diff --git a/idea/testData/safeDelete/deleteObject/kotlinObject/localObject1.kt.messages b/idea/testData/safeDelete/deleteObject/kotlinObject/localObject1.kt.messages new file mode 100644 index 00000000000..83db955ca2d --- /dev/null +++ b/idea/testData/safeDelete/deleteObject/kotlinObject/localObject1.kt.messages @@ -0,0 +1 @@ +object foo.A has 1 usage that is not safe to delete. \ No newline at end of file diff --git a/idea/testData/safeDelete/deleteObject/kotlinObject/localObject2.kt b/idea/testData/safeDelete/deleteObject/kotlinObject/localObject2.kt new file mode 100644 index 00000000000..92996a7dd80 --- /dev/null +++ b/idea/testData/safeDelete/deleteObject/kotlinObject/localObject2.kt @@ -0,0 +1,5 @@ +fun foo() { + object A { + + } +} \ No newline at end of file diff --git a/idea/testData/safeDelete/deleteObject/kotlinObject/localObject2.kt.after b/idea/testData/safeDelete/deleteObject/kotlinObject/localObject2.kt.after new file mode 100644 index 00000000000..f23d7f69d2f --- /dev/null +++ b/idea/testData/safeDelete/deleteObject/kotlinObject/localObject2.kt.after @@ -0,0 +1,2 @@ +fun foo() { +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/safeDelete/JetSafeDeleteTestGenerated.java b/idea/tests/org/jetbrains/jet/safeDelete/JetSafeDeleteTestGenerated.java index 82881662ae2..9f376619030 100644 --- a/idea/tests/org/jetbrains/jet/safeDelete/JetSafeDeleteTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/safeDelete/JetSafeDeleteTestGenerated.java @@ -53,6 +53,16 @@ public class JetSafeDeleteTestGenerated extends AbstractJetSafeDeleteTest { doClassTest("idea/testData/safeDelete/deleteClass/kotlinClass/classInString.kt"); } + @TestMetadata("localClass1.kt") + public void testLocalClass1() throws Exception { + doClassTest("idea/testData/safeDelete/deleteClass/kotlinClass/localClass1.kt"); + } + + @TestMetadata("localClass2.kt") + public void testLocalClass2() throws Exception { + doClassTest("idea/testData/safeDelete/deleteClass/kotlinClass/localClass2.kt"); + } + @TestMetadata("nestedClass1.kt") public void testNestedClass1() throws Exception { doClassTest("idea/testData/safeDelete/deleteClass/kotlinClass/nestedClass1.kt"); @@ -91,6 +101,16 @@ public class JetSafeDeleteTestGenerated extends AbstractJetSafeDeleteTest { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("idea/testData/safeDelete/deleteObject/kotlinObject"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("localObject1.kt") + public void testLocalObject1() throws Exception { + doObjectTest("idea/testData/safeDelete/deleteObject/kotlinObject/localObject1.kt"); + } + + @TestMetadata("localObject2.kt") + public void testLocalObject2() throws Exception { + doObjectTest("idea/testData/safeDelete/deleteObject/kotlinObject/localObject2.kt"); + } + @TestMetadata("nestedObject1.kt") public void testNestedObject1() throws Exception { doObjectTest("idea/testData/safeDelete/deleteObject/kotlinObject/nestedObject1.kt");