Reuse Java safe delete for Kotlin classes and objects

This commit is contained in:
Alexey Sedunov
2014-01-20 15:24:55 +04:00
parent d432c10251
commit 5cfb07072c
13 changed files with 78 additions and 46 deletions
@@ -372,14 +372,13 @@ public class JetPsiUtil {
public static void deleteClass(@NotNull JetClassOrObject clazz) {
CheckUtil.checkWritable(clazz);
JetFile file = (JetFile) clazz.getContainingFile();
List<JetDeclaration> 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
@@ -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<T: PsiElement>(
strict : Boolean = false, vararg parentClasses : Class<T>, 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)
}
}
val PsiElement.namedNavigationElement: PsiNamedElement?
get() = getNavigationElement()?.getParentByType(javaClass<PsiNamedElement>())
@@ -99,7 +99,7 @@ public class KotlinSafeDeleteProcessor extends JavaSafeDeleteProcessor {
@NotNull PsiElement element, @NotNull PsiElement[] allElementsToDelete, @NotNull List<UsageInfo> 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<UsageInfo> result
) {
ReferencesSearch.search(referencedElement).forEach(new Processor<PsiReference>() {
@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<UsageInfo> result
) {
if (element == null) return null;
List<UsageInfo> javaUsages = new ArrayList<UsageInfo>();
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) {
@@ -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
@@ -0,0 +1,7 @@
fun foo() {
class <caret>A {
}
A()
}
@@ -0,0 +1 @@
class foo.A has 1 usage that is not safe to delete.
@@ -0,0 +1,5 @@
fun foo() {
class <caret>A {
}
}
@@ -0,0 +1,2 @@
fun foo() {
}
@@ -0,0 +1,7 @@
fun foo() {
object <caret>A {
}
A
}
@@ -0,0 +1 @@
object foo.A has 1 usage that is not safe to delete.
@@ -0,0 +1,5 @@
fun foo() {
object <caret>A {
}
}
@@ -0,0 +1,2 @@
fun foo() {
}
@@ -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");